mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 14:38:59 +02:00
fix(browse): Chromium-crash exit is daemon-only — embedded launches never kill their host
handleChromiumDisconnect unconditionally process.exit()ed. Correct for
the standalone daemon (its supervisor/user must notice); suicidal when
a TEST launches BrowserManager in-process: a mid-suite Chromium death
exited the whole bun shard with no terminal summary — the exact
truncation class the strict runner flags (observed live: CI shard 1 on
eb233299 died at cache-concurrent-refresh right after a daemon-spawning
gate test; with this fix the same pairing runs to completion and
REPORTS instead of dying).
The standalone entrypoint opts in via markDaemonProcess() under
server.ts's import.meta.main gate — the same embedder contract its
signal handlers already use (gbrowser phoenix keeps its own handlers).
Embedded contexts now get the disconnect log line and continue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<void> {
|
||||
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);
|
||||
|
||||
@@ -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<R
|
||||
// server.ts as a submodule can register their own signal handlers without
|
||||
// fighting with gstack's. CLI path is unchanged.
|
||||
if (import.meta.main) {
|
||||
// Standalone daemon: a Chromium crash must exit THIS process (its
|
||||
// supervisor/user notices); embedders and in-process test launches must
|
||||
// never be exited by browser-manager's disconnect handler.
|
||||
markDaemonProcess();
|
||||
// SIGINT (Ctrl+C): user intentionally stopping → shutdown.
|
||||
process.on('SIGINT', () => activeShutdown?.());
|
||||
// SIGHUP (terminal hangup): with handleSIGHUP:false at the three launch
|
||||
|
||||
Reference in New Issue
Block a user