diff --git a/src/servers/api/chat/opencode/server-manager.ts b/src/servers/api/chat/opencode/server-manager.ts index d6d0f79f..61586e31 100644 --- a/src/servers/api/chat/opencode/server-manager.ts +++ b/src/servers/api/chat/opencode/server-manager.ts @@ -22,6 +22,30 @@ type OpenCodeServer = { baseUrl: string; proc: Subprocess }; const servers = new Map(); const starting = new Map>(); +// On startup, reap orphaned per-cwd serves left by a previous officer instance (each ~0.5GB) so they +// don't accumulate across restarts. The fixed server (OPENCODE_SERVER_URL) is preserved by port. +(function reapOrphanServers() { + const fixedPort = (() => { + try { + return new URL(OPENCODE_SERVER_URL).port || '4096'; + } catch { + return '4096'; + } + })(); + try { + Bun.spawnSync([ + 'bash', + '-c', + `ss -tlnp 2>/dev/null | grep -F opencode | grep -oP '127\\.0\\.0\\.1:\\K[0-9]+' | sort -u | while read -r p; do ` + + `[ "$p" = "${fixedPort}" ] && continue; ` + + `pid=$(ss -tlnp 2>/dev/null | grep "127.0.0.1:$p " | grep -oP 'pid=\\K[0-9]+' | head -1); ` + + `[ -n "$pid" ] && kill "$pid" 2>/dev/null; done`, + ]); + } catch { + /* best effort */ + } +})(); + async function isHealthy(baseUrl: string): Promise { try { const res = await fetch(`${baseUrl}/api/health`, { signal: AbortSignal.timeout(3000) });