* PPID watchdog mirroring the one in `MCPServer.start` — kills the proxy if * the MCP host (or its proxy of a host, see HOST_PPID_ENV) goes away without * closing stdin. Issue #277 documents why we can't rely on stdin EOF on * Linux: the parent may be SIGKILL'd and reparenting doesn't close pipes
(socket: net.Socket)
| 701 | * watchers to clean up, so this is cheap. |
| 702 | */ |
| 703 | function startPpidWatchdog(socket: net.Socket): void { |
| 704 | const pollMs = parsePollMs(process.env.CODEGRAPH_PPID_POLL_MS); |
| 705 | if (pollMs <= 0) return; |
| 706 | // Baseline from the CLI entry's earliest capture, not process.ppid here — |
| 707 | // a launcher killed during our first ~100ms would otherwise leave the |
| 708 | // baseline at 1 and blind the divergence check forever (#1185). |
| 709 | const originalPpid = EARLY_PPID; |
| 710 | const hostPpid = parseHostPpid(process.env[HOST_PPID_ENV]); |
| 711 | const timer = setInterval(() => { |
| 712 | const reason = supervisionLostReason({ |
| 713 | originalPpid, |
| 714 | currentPpid: process.ppid, |
| 715 | hostPpid, |
| 716 | isAlive: isProcessAliveLocal, |
| 717 | }); |
| 718 | if (reason) { |
| 719 | process.stderr.write(`[CodeGraph MCP] Parent process exited (${reason}); shutting down.\n`); |
| 720 | try { socket.destroy(); } catch { /* ignore */ } |
| 721 | process.exit(0); |
| 722 | } |
| 723 | }, pollMs); |
| 724 | timer.unref?.(); |
| 725 | } |
| 726 | |
| 727 | function parsePollMs(raw: string | undefined): number { |
| 728 | return parseDelayMs(raw, DEFAULT_PPID_POLL_MS); |
no test coverage detected