From ce1240fa9b3b74983cc22171469c24d0708b10ea Mon Sep 17 00:00:00 2001 From: amankumarsingh77 Date: Tue, 28 Jul 2026 19:46:57 +0530 Subject: fix(tool): stop the socket gate rejecting every tool call on Windows The tool gate ran access() on DAEMON_SOCKET_PATH, which on Windows is the named pipe \\.\pipe\pi-browser-daemon. Named pipes are not filesystem entries, so access() always fails there and every browser_* call returned "Browser harness not initialized" no matter how healthy the daemon was. spawn.ts already skipped the identical check for this reason; that knowledge is now one shared daemonSocketMayExist() used by both call sites, with platform and path as parameters so the branch is testable off-platform. Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f1 Co-Authored-By: Claude Opus 5 (1M context) --- src/daemon/spawn.ts | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'src/daemon/spawn.ts') diff --git a/src/daemon/spawn.ts b/src/daemon/spawn.ts index d051fcc..165abcd 100644 --- a/src/daemon/spawn.ts +++ b/src/daemon/spawn.ts @@ -32,6 +32,29 @@ const cleanupStaleSocket = (): void => { unlink(DAEMON_SOCKET_PATH).catch(() => {}); }; +/** + * Whether the daemon endpoint could plausibly exist, judged without connecting. + * + * On POSIX a missing socket file means the daemon definitely isn't running, so + * callers can short-circuit. Windows named pipes are NOT filesystem entries — + * access() always fails on `\\.\pipe\…` even while the daemon is serving it — so + * on Windows the answer is always "maybe" and the caller must probe instead. + * + * Platform and path are parameters so the branch is testable off-platform. + */ +export const daemonSocketMayExist = async ( + platform: NodeJS.Platform = process.platform, + socketPath: string = DAEMON_SOCKET_PATH, +): Promise => { + if (platform === "win32") return true; + try { + await access(socketPath); + return true; + } catch { + return false; + } +}; + /** * Check whether the daemon is running by probing its Unix socket. * A file-existence check is unreliable — a crashed daemon leaves a stale socket. @@ -39,16 +62,7 @@ const cleanupStaleSocket = (): void => { * On failure, clean up the stale socket so ensureDaemon can spawn a fresh one. */ export const isDaemonRunning = async (timeoutMs = 2_000): Promise => { - // On POSIX, a missing socket file means the daemon definitely isn't running, - // so we can short-circuit. Windows named pipes are not filesystem entries — - // access() always fails there — so skip the pre-check and probe directly. - if (!isWindows) { - try { - await access(DAEMON_SOCKET_PATH); - } catch { - return false; - } - } + if (!(await daemonSocketMayExist())) return false; return new Promise((resolve) => { let settled = false; -- cgit v1.2.3