diff --git a/browse/src/browser-manager.ts b/browse/src/browser-manager.ts index 60c478842..5a3800718 100644 --- a/browse/src/browser-manager.ts +++ b/browse/src/browser-manager.ts @@ -186,12 +186,32 @@ export async function resolveDisconnectCause(browser: Browser | null): Promise<' } /** - * Headless `launch()` disconnect handler. Exits 0 on clean user-quit, 1 on - * crash. Inlined into the launch() body via a one-line dispatch so + * Exit-on-disconnect is DAEMON-ONLY semantics. The standalone server + * entrypoint opts in via markDaemonProcess() (under its import.meta.main + * gate, same contract as its signal handlers); embedders — gbrowser + * phoenix, and every test that launches a BrowserManager in-process — + * must never have a Chromium crash process.exit() their HOST. Observed + * live before this flag: a test-launched browser died mid-suite and the + * exit(1) killed the whole bun shard with no terminal summary (the + * truncation class the strict runner exists to catch). + */ +let daemonProcess = false; +export function markDaemonProcess(): void { + daemonProcess = true; +} + +/** + * Headless `launch()` disconnect handler. In the standalone daemon: exits 0 + * on clean user-quit, 1 on crash. Embedded contexts get the log line only. + * Inlined into the launch() body via a one-line dispatch so * browser-manager's flow stays grep-friendly. */ export async function handleChromiumDisconnect(browser: Browser | null): Promise { const cause = await resolveDisconnectCause(browser); + if (!daemonProcess) { + console.error(`[browse] Chromium disconnected (${cause}) in an embedded context — host process continues.`); + return; + } if (cause === 'clean') { console.error('[browse] Chromium closed cleanly (user-initiated quit). Server exiting (0).'); process.exit(0); diff --git a/browse/src/server.ts b/browse/src/server.ts index a31d3c83a..f0823cc97 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -13,7 +13,7 @@ * Port: random 10000-60000 (or BROWSE_PORT env for debug override) */ -import { BrowserManager } from './browser-manager'; +import { BrowserManager, markDaemonProcess } from './browser-manager'; import { handleReadCommand, hasOutArg } from './read-commands'; import { handleWriteCommand } from './write-commands'; import { handleMetaCommand } from './meta-commands'; @@ -1394,6 +1394,10 @@ async function handleCommand(body: any, tokenInfo?: TokenInfo | null): Promise activeShutdown?.()); // SIGHUP (terminal hangup): with handleSIGHUP:false at the three launch