feat(opencode): never leave orphaned OpenCode server processes
OpenChamber spawns the OpenCode server as an external child binary (detached on Unix), so a hard crash, SIGKILL, or Ctrl+C of the host before graceful teardown could leave it running. Orphaned servers then accumulate and contend on the shared SQLite DB, causing severe startup slowdowns. Add a per-process registry plus a startup reaper, mirroring the pattern OpenCode's own CLI daemon uses for its detached server: - One file per spawned process at ~/.config/openchamber/managed-opencode/<pid>.json. Per-process files avoid the read-modify-write clobber race between concurrent runtimes/windows that a single shared file would suffer. - On spawn, record the child (pid, owner pid, port, binary, host runtime). - On graceful close/restart, delete the record. - On startup, reap only our own, verified, genuinely-orphaned processes: recorded by us AND still a live `opencode serve` on the recorded port AND whose spawner is provably gone (reparented to pid 1, or recorded owner dead). It never touches a process a live instance is using, the user's standalone server, the official desktop app, or the TUI. Wire it into every runtime that spawns the server: - web/desktop via the OpenCode lifecycle (register on spawn, unregister on close/restart, reap at startup). The restart-for-config-change flow inherits this automatically through the same kill/spawn paths. - VS Code carries a parity implementation (it does not bundle the web package) that reads/writes the same registry directory and uses the same algorithm. - Tag the actual host runtime (desktop/web/ssh-remote/vscode) for observability. Also tighten teardown so the registry stays accurate and orphans die promptly instead of only on the next start: - The web server now also handles SIGHUP and SIGUSR2 (terminal close and the nodemon restart used by dev:server:watch / dev:web:hmr). - Electron now installs SIGINT/SIGTERM/SIGHUP handlers that run the same background teardown as a normal quit, covering Ctrl+C on electron:dev. External OpenCode servers (OPENCODE_SKIP_START) are intentionally excluded: we never manage or kill processes we did not spawn.
This commit is contained in:
@@ -131,9 +131,15 @@ export const createServerStartupRuntime = (dependencies) => {
|
||||
const handleSignal = async () => {
|
||||
await gracefulShutdown();
|
||||
};
|
||||
// Cover every signal a shell or dev harness may use to stop/restart us, so
|
||||
// the managed OpenCode child is always torn down gracefully instead of
|
||||
// orphaned: SIGINT/SIGQUIT (Ctrl+C/Ctrl+\), SIGTERM (kill/default), SIGHUP
|
||||
// (terminal close), SIGUSR2 (nodemon restart for `dev:server:watch`).
|
||||
process.on('SIGTERM', handleSignal);
|
||||
process.on('SIGINT', handleSignal);
|
||||
process.on('SIGQUIT', handleSignal);
|
||||
process.on('SIGHUP', handleSignal);
|
||||
process.on('SIGUSR2', handleSignal);
|
||||
setSignalsAttached(true);
|
||||
syncToHmrState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user