aboutsummaryrefslogtreecommitdiff
path: root/src/daemon
AgeCommit message (Collapse)Author
2026-08-02refactor: merge nine files whose reason to exist was goneamankumarsingh77
src/ goes from 84 files to 75 at the same line count — every merge below had exactly one consumer. cdp/event-queue.ts and cdp/pending-requests.ts become daemon/queue.ts. I split these in the daemon-internals commit on the grounds that they held two unrelated concerns, but the transport commit had already deleted the second transport, so both halves had one consumer and the split bought nothing. They also sat in cdp/ while only daemon/ used them. cdp/transport.ts held only the CdpTransport interface after the second transport went; five of its six consumers import it type-only, so it moves to cdp/types.ts. cdp/evaluate.ts moves into cdp/session.ts rather than into its lone consumer isolated-tab.ts — it reaches session.call directly, so under src/domains/ it would trip the raw-cdp-call rule. util/keycodes.ts and util/time.ts fold into their only callers (keyboard.ts, page.ts); util/bytes.ts into util/truncate.ts, which already owns output-size formatting; readpage/capture.ts and search/extract.ts into the tools beside them; profile/picker.ts into profile/command.ts. Kept deliberately: mutex.ts and domains/target.ts each have their own test suite, and folding target.ts into click.ts would make a tool file export a helper. sharp-shim.ts is an isolation boundary carrying two allowlisted casts. result.ts, guards.ts, js-template.ts and paths.ts are small but have 44, 12, 8 and 3 consumers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02refactor: act on the fallow auditamankumarsingh77
Unused exports it found that my own sweep missed, because I had grepped by name and these names occur as local variables elsewhere: deadline and the Deadline type in util/time.ts were genuinely unreferenced and are gone; isWireMessage, resolveUserDataDir, CLEAR_SELECTION_LABEL and the ExpandableText type are used only inside their own file and no longer export. isWireMessage became internal when server.ts moved to the shared serialize/deserialize codec. Duplication: console-buffer and network-buffer were the same capacity-bounded map with an overflow flag, sharing eviction, the drain tail and clear() across three clone groups. That is now cdp/record-store.ts, with tests — neither buffer had any. The three Network follow-up events also repeated the same decode-and-find preamble; one `update` helper covers them, and drops the redundant optional chaining that survived a `!id` guard. formatBytes existed verbatim in js.ts and network.ts; it is now util/bytes.ts. Duplication overall: 2.7% to 2.0%, and none of the remainder is in src. .fallowrc.json records the two entry points the tool cannot infer — the pi extension (declared under pi.extensions) and the daemon (spawned by path) — without which five reachable files read as dead. sharp is ignored because it is loaded through a variable specifier in a dynamic import so it can stay optional. agentDir is exported solely for a test's dynamic import, so it is suppressed by name rather than globally enabling ignoreExportsUsedInFile, which would have hidden four of the five findings above. Not acted on: the "split high-impact file" suggestions for session.ts, client.ts and ref-resolve.ts (119 LOC) are size and fan-in heuristics with no defect behind them, and splitting them would add the indirection this refactor set out to remove. The claim that state.ts is 100% dead is an artifact of the missing entry point. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02refactor: daemon internals, and fix a leak the merge exposedamankumarsingh77
IdMultiplexer and EventRouter were the only classes in src/; they are now createIdMultiplexer/createEventRouter closures like every other factory here. The multiplexer kept {clientId, localId} per daemon id while the bridge kept {send, clientId, localId, timer, isAttach} under the same key — two maps of overlapping data, and mux.resolve()'s return value was discarded at three of four call sites. One map now holds the union. That merge fixes a real leak: removeClient cleared the multiplexer but never the callbacks map, so a disconnected client's timers stayed armed and later fired a response at a socket that was gone. clearClient now cancels them, and a test pins it. handleRequest polled every 250ms for up to 15s per request when Chrome was down; N queued requests now await one connect signal. server.ts uses its own protocol's serialize/deserialize instead of hand-rolled JSON, and broadcast no longer routes through `this` — the only `this` in the codebase, and the one thing that breaks when the server object is destructured. cdp/event-queue.ts held two unrelated concerns; the request/timeout half moves to cdp/pending-requests.ts. cdp/daemon-transport.ts is the client half of the daemon protocol, so it moves to daemon/transport.ts. test/daemon/ needs adding to the test glob to run at all — there was no daemon test directory before because none of this was reachable without live Chrome. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02refactor: delete exports and knobs that nothing callsamankumarsingh77
isInternalUrl, SPECIAL_KEYS, andThen and mapErr had no callers in src or test. BrowserState.remoteBrowserId was parsed on restore and never written — the field that is actually read lives on DaemonStatus. IpcServer.disconnectClient and IpcServer.clients() were unreachable, as was EventRouter.allClients and a write-only lineCount counter. DAEMON_STALE_SOCKET_CLEANUP was a hardcoded true with an if around it, so the unlink now runs unconditionally. CdpBridge.isAlive and getSessionOwner look equally dead from src/ alone, but test/manual/bridge-test.ts drives both — they are the only seams that suite has, so they stay. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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-08-02refactor: close the remaining unguarded type assertionsamankumarsingh77
Types the event-queue iterator's TReturn as undefined so the terminal result needs no cast; declares the isError field the pi runtime reads by duck-typing; binds socket to a local const in the daemon transport; replaces the @ts-ignore on the optional sharp import with a non-literal specifier so src/ still typechecks where sharp was not installed. Adds src/util/guards.ts as the single home for narrowing over unknown — isRecord plus scalar/array readers — and converts the two private isObject copies in cdp/types.ts and daemon/protocol.ts along with every inline `as Record<string, unknown>` in domains/js.ts and domains/render.ts. Gives Accessibility.getFullAXTree a real result schema, closing the last unvalidated CDP boundary: only nodeId is required, every object carries additionalProperties, and each AX value stays Unknown, so the tree decodes as a whole or fails as a whole rather than being filtered. Guards the five tool-result `details` readers, the daemon attach response, the persisted session state, the browser-namespace flag, and the fs errno narrowing, each feeding an already-present failure path. The daemon-transport socket binding is not a bug fix: send() runs synchronously inside sendWithTimeout's promise executor, so socket cannot be reassigned between the check and the write. It removes the assertion and makes the write immune to a later refactor that adds an await. Two assertions survive by design and are documented in the task report: the never->unknown re-widening in util/tool.ts, which the type-erased tool registry makes unavoidable, and the AsyncFunction constructor lookup in domains/js.ts, which JavaScript exposes no typed binding for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02refactor: validate JSON at every parse boundaryamankumarsingh77
Ten sites cast or implicitly typed JSON.parse output; JSON.parse("null") would have satisfied all of them. parseJson returns a Result and validates against a typebox schema. js.ts keeps unknown — user script output has no fixed shape. daemon/bridge.ts assigned JSON.parse straight to CdpRawMessage with no guard at all; it now uses isCdpRawMessage like the transport does. 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