Skip to content

fix(hackbrowser): browser stability + subprocess lifecycle hardening - #85

Merged
badchars merged 14 commits into
devfrom
fix/hackbrowser-stability
Aug 24, 2026
Merged

fix(hackbrowser): browser stability + subprocess lifecycle hardening#85
badchars merged 14 commits into
devfrom
fix/hackbrowser-stability

Conversation

@badchars

Copy link
Copy Markdown
Contributor

Summary

  • Browser crash on drag/resize — stability launch args + renderer throttle prevention
  • Overlay panel not repositioningposition: fixed + visualViewport resize listener + viewport: null in headed mode
  • Browser closes on stop-loading button — graceful worker shutdown (stdin.end instead of proc.kill), browser stays open in headed mode
  • Subprocess lifecycle hardening — safe disconnect-wait with race/timeout guards, abort on stdin close, kill timeout backstop, crash-path cleanup, drain interval leak fix, post-crawl capture continuity, worker watchdog

Files Changed

  • packages/hackbrowser/src/agent.tswaitForBrowserClose() helper, try-catch initial goto, post-crawl drain
  • packages/hackbrowser/src/stealth.ts — headed mode viewport + window-size args
  • packages/hackbrowser/src/panel/inject.ts — fixed overlay + resize listener
  • packages/cyberstrike/src/tool/hackbrowser-launcher.ts — graceful shutdown + kill timeout
  • packages/cyberstrike/src/hackbrowser-subprocess/hackbrowser-worker.ts — abort on stdin close + watchdog

Test Plan

  • Start hackbrowser in headed mode, press Chrome stop button during navigation — browser stays open
  • Drag/resize browser window during crawl — no crash
  • Overlay panel stays in bottom-right corner on window resize
  • Cancel crawl via CyberStrike stop — browser stays open for manual inspection
  • Kill CyberStrike process during crawl — worker + Chrome exit within 5s
  • Let crawl complete normally in headed mode — browser stays open until user closes

Closes #84

…nux-only

- Add 6 Chromium flags to prevent renderer throttling/death during
  window drag/resize/minimize: --disable-backgrounding-occluded-windows,
  --disable-renderer-backgrounding, --disable-background-timer-throttling,
  --disable-hang-monitor, --disable-ipc-flooding-protection,
  --disable-component-update
- Move --no-sandbox, --disable-setuid-sandbox, --disable-dev-shm-usage,
  --no-zygote to Linux-only (PLATFORM_ARGS) — these are Docker/Linux
  flags that can cause instability on macOS

Closes part of #84
The browser could die mid-crawl (crash, disconnect, page close) with
no detection — the agent continued issuing commands to a dead browser,
wasting tokens and eventually throwing an unhandled error that lost all
captured data.

Changes:
- Add BrowserHealth tracker with browser.on('disconnected'),
  page.on('close'), page.on('crash') event handlers
- Check isBrowserDead() at each BFS iteration boundary (both
  single-cred and multi-cred paths)
- Wrap explorePageWithAI in try-catch — on browser death, break
  the BFS loop instead of propagating the error
- Graceful shutdown: drain captured requests, skip panel events
  and waitForTimeout on dead browser, report partial results
  in errors[] instead of throwing
- browser.close() wrapped in .catch() to handle already-dead browser

Both run() and runMultiCredential() are covered.

Closes #84
…window resize

viewport: null when headless=false lets the CSS viewport track the actual
window dimensions. The overlay panel (position:fixed) now repositions
naturally on resize instead of staying pinned to the 1920x1080 virtual
viewport.  --window-size=1920,1080 launch arg keeps the initial size.
Card was position:absolute inside a fixed root — when the viewport
diverged from the window (Playwright viewport lock) the card drifted
off-screen.  Now position:fixed on the card itself, max-width clamp,
and a resize listener that keeps it within visible bounds.
When the user hits stop (AbortSignal), skip browser.close() so the
window stays open for manual inspection.  Both run() and
runMultiCredential() paths affected.
… killing

When user cancels the crawl (AbortSignal), the worker now waits for
browser.on('disconnected') — keeping the browser alive until the user
manually closes it.  The worker process stays alive during this wait,
so the launcher's IPC reader loop blocks naturally until the browser
closes and the result is sent.

Only applies in headed mode — headless crawls still close immediately.
…roc.kill

Replace proc.kill() with stdin.end() in backgroundRun's finally block.
The worker process exits naturally when stdin closes, allowing Chrome
to shut down gracefully instead of being force-killed mid-operation.
In headed mode, always wait for user to close the browser window
instead of calling browser.close(). Prevents Chrome from closing
when the stop-loading button cancels a navigation mid-crawl.
Also wraps initial page.goto in try-catch so navigation failure
keeps the browser open for manual interaction.
Add waitForBrowserClose() helper that handles three edge cases:
- Race condition: re-checks isConnected() after attaching listener
- Parent crash: races disconnect against abort signal
- Chrome hang: 30-minute max timeout as backstop

Also wraps runMultiCredential initial goto in try-catch (same
pattern as run()) to prevent browser closing on navigation failure.
When parent closes stdin (shutdown or crash), abort the crawl
controller so run()'s disconnect-wait resolves and the worker
exits cleanly instead of keeping Chrome alive indefinitely.
Add 10s kill timeout after stdin.end() as a backstop if the worker
hangs. On the crash detection path (worker exited without result),
force-kill to clean up orphaned Chrome child processes.
Wrap crawl body in try-finally to guarantee clearInterval on any exit
path (auth errors, navigation failures). Add second drain pass after
csEmit to catch late captures. In headed mode, keep draining captures
during manual browsing until user closes the browser.
If runWorker is still hanging 5s after stdin close (e.g. stuck
Playwright operation that ignores abort signal), force-exit the
worker process to prevent indefinite orphan.
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@badchars
badchars merged commit 3d45c64 into dev Aug 24, 2026
7 of 12 checks passed
@badchars
badchars deleted the fix/hackbrowser-stability branch August 24, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant