mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
3f1a723b5c
* fix(daemon): SIGKILL fallback when stale daemon refuses graceful shutdown When the CLI detects a stale daemon (`daemonVersion !== PKG_VERSION` after `npm install -g @jackwener/opencli@latest`), it currently asks the daemon to exit via `POST /shutdown` and waits up to 3 seconds for the port to release. If the old daemon hangs, refuses /shutdown, or the endpoint is missing entirely (pre-shutdown-endpoint version), the port stays held and the user sees `Stale daemon could not be replaced` with a `opencli daemon stop` hint. 99% of "I just upgraded and have to run `opencli doctor` every time" reports land here: the user upgraded the CLI but the persistent daemon survived from a previous install, and graceful shutdown is unreliable across versions. This patch reads the stale daemon's pid from its existing `/status` response (daemon.ts:252 already exposes `pid: process.pid`) and falls back to `process.kill(pid, 'SIGKILL')` after graceful shutdown fails, then waits another 2s for the port to release. Cross-platform: Node's `process.kill(_, 'SIGKILL')` maps to `TerminateProcess` on Windows, so no `taskkill` shell-out is needed. The user-visible "Stale daemon could not be replaced" error only fires when both graceful shutdown AND SIGKILL fail (cross-user owner / cross-machine PID — neither is reachable from a normal CLI invocation anyway). The hint message is updated to reflect that. Adds 2 tests: - SIGKILL succeeds → bridge proceeds past the stale block (and eventually fails the no-extension wait, proving the stale branch was passed cleanly). - SIGKILL throws EPERM AND waitForDaemonStop still returns false → falls through to the existing stale-daemon error. Troubleshooting docs note the new auto-fallback. * address opus review nits - bridge.ts: move `await waitForDaemonStop(2000)` out of the try/catch so the port poll always runs after `process.kill`, even when the kill itself throws ESRCH (target already dead) or EPERM (cross-user owner). - browser.test.ts: bump the existing 3 stale-daemon test fixtures from `pid: 1` to `pid: 999999` so the new SIGKILL fallback no longer fires a signal at init when the older tests reach the fallback path. - browser.test.ts: mock `waitForDaemonStop` in those 3 tests too, since the real implementation now polls for 2s in the fallback path (test runtime was up to ~6s before; back to ~400ms).