aboutsummaryrefslogtreecommitdiff
path: root/src/daemon/spawn.ts
AgeCommit message (Collapse)Author
2026-10-11fix(daemon): resolve tsx from the filesystem when Node's resolver cannot see ↵PhysicistLeon
node_modules (#28) Inside a Bun-compiled pi binary (pi.exe), createRequire().resolve("tsx/cli") is rooted at the embedded virtual filesystem and cannot see the extension's real node_modules, so browser_setup failed instantly. The resolver result is now checked on disk and, when it fails, the tsx CLI is found by walking up the node_modules chain. Under Bun the daemon runs on `node` from PATH (process.execPath is the pi binary itself); PI_BROWSER_NODE overrides it. Fixes #27 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-08-06fix: accept Windows script paths and start the daemon from a real install (#23)Aman Kumar
Two independent Windows/production defects reported in #22. browser_run_script rejected every absolute path on Windows. The allowed-root check compared with a hardcoded `${root}/`, but path.resolve returns backslash-separated paths there, so a file inside os.tmpdir() was refused even though the rejection message listed tmpdir as allowed. Decide containment with path.relative instead: it is separator-agnostic and carries each platform's own case rules, so it also accepts a differing drive-letter case that a path.sep patch would still reject. Escapes via `..` and sibling directories sharing the root's name prefix stay rejected, as before. The daemon could not start under `npm omit=dev`: it is spawned through tsx, which was declared only as a dev dependency. Moving tsx to dependencies is not enough on its own — pi installs extensions into one flat hoisted tree, so tsx lands beside the package and the fixed <pkg>/node_modules/.bin/tsx lookup still misses, falling through to an `npx tsx` that resolves against the wrong cwd and dies against stdio: "ignore". Resolve the CLI entry with Node's own resolver, which walks up to the hoisted copy. That removes the shell entirely, and with it the Windows .cmd shim, windowsVerbatimArguments, and the manual quoting that paths containing spaces needed. Both fixes live in platform-branching code, so their tests are registered in the three-OS CI matrix that already exists for that purpose — a Linux-only run cannot exercise either one.
2026-08-02refactor: tidy the whitespace the comment removal left behindamankumarsingh77
Drops leading blank lines where a file header was removed, collapses double blank lines, and closes the empty catch/handler blocks whose only content had been a comment. Whitespace only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02refactor(daemon): strip comment noiseamankumarsingh77
Removes module banners, section dividers, reference lists, and comments restating the code. Keeps one-line notes for the Windows named-pipe endpoint, the Windows unlink no-op, the do-not-unlink-on-rejection rule, the `.cmd` shell requirement, shell-quoting of spaced paths, and the in-place client promotion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02style: drop the comments added by the bug-audit fixesamankumarsingh77
The audit fixes were required to add no comments; two doc blocks slipped through review. Removed, leaving only comments that predate this task. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02fix(tool): stop the socket gate rejecting every tool call on Windowsamankumarsingh77
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) <noreply@anthropic.com>
2026-07-12fix: make browser daemon setup work on Windows (#7) (#12)Aman Kumar
The Unix socket daemon introduced in v0.6.0 made several POSIX-only assumptions that break setup on Windows: - protocol.ts hardcoded a Unix socket path (/tmp/pi-browser-daemon.sock). Node IPC on Windows requires a named pipe (\\.\pipe\<name>); a /tmp path is not a valid net listen/connect target there. - spawn.ts spawned the bare tsx binary / `npx` and probed the non-Windows .bin/tsx shim. On Windows npm binaries are .cmd shims that must run via a shell, and spawning them directly fails with EINVAL / ENOENT. - isDaemonRunning() pre-checked the socket with fs.access(), which always fails for a named pipe and would report a live daemon as dead. - stale-socket unlink() would throw on a named pipe path. Changes: - DAEMON_SOCKET_PATH is now platform-aware: named pipe on win32, Unix socket elsewhere. - spawnDaemon() resolves tsx.cmd / npx.cmd on Windows, runs via shell with per-token quoting (paths with spaces) and windowsVerbatimArguments. - isDaemonRunning() skips the fs.access pre-check on Windows and probes the pipe directly. - stale-socket cleanup is a no-op on Windows (named pipes self-clean). - setup.ts daemon-failure message uses the actual DAEMON_SOCKET_PATH. Closes #7
2026-07-06fix: stale socket detection prevents daemon spawn after crashamankumarsingh77
isDaemonRunning() only checked file existence of the Unix socket. A dead daemon leaves a stale socket, causing ensureDaemon() to skip spawning and client.start() to fail with 'Chrome not connected'. Replace with a liveness probe: connect, register, verify ack, then disconnect. On failure, clean up the stale socket. Also makes bridge handleRequest async to poll for Chrome connection for up to 15s before rejecting.
2026-06-21release: v0.6.0 — per-tab isolation, Unix socket daemon, on-demand init (#6)Aman Kumar
* feat: Unix socket daemon with auto-reconnect for persistent Chrome CDP Eliminates the 'Allow Remote Debugging' Chrome prompt that appeared on every pi session by multiplexing pi instances through a single persistent daemon process. Key additions: - Unix socket IPC (net.createServer + readline JSON framing) - CdpBridge with ID multiplexer + event router - DaemonTransport implementing CdpTransport interface - Auto-spawn with ensureDaemon() (lazy, checks socket) Fixes since initial implementation: - Bridge auto-reconnects to Chrome with exponential backoff (1s-60s). Previously the bridge tried once at startup and permanently failed if Chrome wasn't ready — causing 'Chrome not connected' on every browser tool call. - Daemon spawn uses project-local tsx binary instead of global --import flag, fixing silent spawn failure (ERR_MODULE_NOT_FOUND). - Stale ctx crash: persistState in onOwnershipChange wrapped in try/catch to prevent CDP event consumer crashes. 49 test assertions across 4 test files. Zero TypeScript errors. * feat: on-demand browser initialization Browser daemon no longer auto-starts on session_start. Instead: - session_start creates the client lazily but does NOT connect or spawn the daemon. Zero Chrome prompts for non-browser pi sessions. - Browser tool wrapper checks for daemon socket before each call. Socket missing → clear error: 'Run /browser-setup first'. Socket exists → proceeds silently (daemon already running). - /browser-setup is the explicit, one-time init point. It spawns the daemon and connects to Chrome only when the user opts in. - SKILL.md updated with Connection Setup instructions telling agents to direct users to /browser-setup when socket is missing. * fix: checkChromeRunning on macOS — match by substring not exact ps -A -o comm= returns full executable paths on modern macOS (e.g. /Applications/Google Chrome.app/Contents/MacOS/Google Chrome), not the basename. The old exact-match check (browserProcesses.includes(l)) never matched, causing false 'No browser instance running' errors. Changed to substring includes() with helper/renderer/crashpad filtering. * feat: browser_setup as agent-callable tool The agent can now self-recover from 'Browser harness not initialized' errors by calling browser_setup directly — no user mediation needed. - Extracted shared performSetup() from the slash command into setup.ts - Created src/domains/setup.ts with ensureAlive:false to bypass the socket guard (this tool IS the setup, can't require itself) - Registered browser_setup in the tool registry - Updated SKILL.md: agent calls browser_setup directly, not ask user - Removed stale test/manual/spawn-test.ts * release: v0.6.0 — per-tab isolation, Unix socket daemon, on-demand init