Skip to content

Commit 301d513

Browse files
Serhatcckclaude
andcommitted
feat(hackbrowser): enrich browser-lifecycle logs + log popups for partial-crawl diagnosis (#105)
The worker already listened for browser.disconnected / page.close / page.crash, but logged a terse one-line message with no context, and never logged a popup/new tab opening — so a crawl that stopped early left little to diagnose from. Add a guarded lifecycleContext(browser, page?) helper (url, open-page count, isConnected — every read wrapped so a crash/disconnect handler can never throw while gathering diagnostics) and attach its output to the existing terminal logs on BOTH the single- and multi-credential paths. Also add a context "page" listener that logs (debug) when a popup/tab opens, with the same context — no such listener existed. Scope kept proportionate: no dedicated per-session log file (worker logs are already persisted via the parent's log pipeline, and #104 removed the main mid-crawl crash, so the crash-window loss the file would guard is now rare), no verbosity flag, no crawl-progress plumbing. Verified: a real crawl that opens a popup logs "new page/popup opened" with {url, openPages, connected} and the terminal logs now carry the same context; the guarded reads don't throw even on a not-yet-committed popup URL or a disconnected browser; 99/99 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3f7e082 commit 301d513

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

packages/hackbrowser/src/agent.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,41 @@ function createBrowserHealth(): BrowserHealth {
117117
return { dead: false, reason: "" }
118118
}
119119

120+
// Best-effort context for a browser lifecycle log — every read is guarded so a lifecycle
121+
// handler (especially crash/disconnect) can never throw while gathering diagnostics.
122+
function lifecycleContext(browser: import("playwright").Browser, page?: Page): Record<string, unknown> {
123+
try {
124+
const ctx: Record<string, unknown> = { connected: browser.isConnected() }
125+
if (page) {
126+
ctx.url = page.url()
127+
ctx.openPages = page.context().pages().length
128+
}
129+
return ctx
130+
} catch {
131+
return {}
132+
}
133+
}
134+
120135
function attachLifecycleHandlers(browser: import("playwright").Browser, page: Page, health: BrowserHealth): void {
121136
browser.on("disconnected", () => {
122137
health.dead = true
123138
health.reason = "browser process disconnected"
124-
log.error("browser disconnected — crawl will terminate")
139+
log.error("browser disconnected — crawl will terminate", lifecycleContext(browser, page))
125140
})
126141
page.on("close", () => {
127142
health.dead = true
128143
health.reason = "page closed unexpectedly"
129-
log.error("page closed — crawl will terminate")
144+
log.error("page closed — crawl will terminate", lifecycleContext(browser, page))
130145
})
131146
page.on("crash", () => {
132147
health.dead = true
133148
health.reason = "page renderer crashed"
134-
log.error("page crashed — crawl will terminate")
149+
log.error("page crashed — crawl will terminate", lifecycleContext(browser, page))
150+
})
151+
// A new page/popup/tab opened in this context — logged (debug) so a partial crawl caused
152+
// by a popup can be traced. Additive; there is no other context "page" listener.
153+
page.context().on("page", (p) => {
154+
log.debug("new page/popup opened", lifecycleContext(browser, p))
135155
})
136156
}
137157

@@ -1864,7 +1884,7 @@ async function runMultiCredential(config: AgentConfig, credentials: CredentialCo
18641884
browser.on("disconnected", () => {
18651885
health.dead = true
18661886
health.reason = "browser process disconnected"
1867-
log.error("browser disconnected — multi-credential crawl will terminate")
1887+
log.error("browser disconnected — multi-credential crawl will terminate", lifecycleContext(browser))
18681888
})
18691889

18701890
// Single CyberStrike session for ALL credentials. Honor a host-provided
@@ -1897,12 +1917,21 @@ async function runMultiCredential(config: AgentConfig, credentials: CredentialCo
18971917
page.on("close", () => {
18981918
health.dead = true
18991919
health.reason = `page closed (credential: ${cred.id})`
1900-
log.error("page closed — multi-credential crawl will terminate", { credential: cred.id })
1920+
log.error("page closed — multi-credential crawl will terminate", {
1921+
credential: cred.id,
1922+
...lifecycleContext(browser, page),
1923+
})
19011924
})
19021925
page.on("crash", () => {
19031926
health.dead = true
19041927
health.reason = `page crashed (credential: ${cred.id})`
1905-
log.error("page crashed — multi-credential crawl will terminate", { credential: cred.id })
1928+
log.error("page crashed — multi-credential crawl will terminate", {
1929+
credential: cred.id,
1930+
...lifecycleContext(browser, page),
1931+
})
1932+
})
1933+
browserContext.on("page", (p) => {
1934+
log.debug("new page/popup opened", { credential: cred.id, ...lifecycleContext(browser, p) })
19061935
})
19071936
attachDialogAutoAccept(page)
19081937
attachFileChooserAutoFill(page)

0 commit comments

Comments
 (0)