fix(browse): kill old server before restart to prevent orphaned chromium processes

When the health check fails or the server connection drops, `ensureServer()`
and `sendCommand()` would call `startServer()` without first killing the
previous server process. This left orphaned `chrome-headless-shell` renderer
processes running at ~120% CPU each.

After several reconnect cycles (e.g. pages that crash during hydration or
trigger hard navigations via `window.location.href`), dozens of zombie
chromium processes accumulate and exhaust system resources.

Fix: call `killServer()` on the stale PID before spawning a new server in
both the `ensureServer()` unhealthy path and the `sendCommand()` connection-
lost retry path.

Fixes #294
This commit is contained in:
Claude
2026-03-21 14:58:47 -03:00
parent 709bed9f4d
commit 919f1a7131
+9 -1
View File
@@ -234,7 +234,10 @@ async function ensureServer(): Promise<ServerState> {
}
}
// Need to (re)start
// Need to (re)start — kill the old server first to avoid orphaned chromium processes
if (state && state.pid) {
await killServer(state.pid);
}
console.error('[browse] Starting server...');
return startServer();
}
@@ -289,6 +292,11 @@ async function sendCommand(state: ServerState, command: string, args: string[],
if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET' || err.message?.includes('fetch failed')) {
if (retries >= 1) throw new Error('[browse] Server crashed twice in a row — aborting');
console.error('[browse] Server connection lost. Restarting...');
// Kill the old server to avoid orphaned chromium processes
const oldState = readState();
if (oldState && oldState.pid) {
await killServer(oldState.pid);
}
const newState = await startServer();
return sendCommand(newState, command, args, retries + 1);
}