aboutsummaryrefslogtreecommitdiff
path: root/src/util
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-02fix: make --browser-debug-clicks and BU_CDP_WS actually workamankumarsingh77
Both were documented and inert. --browser-debug-clicks was registered as a flag and never read; the only real switch was the undocumented BH_DEBUG_CLICKS env var. The flag now feeds util/debug.ts, which the click tool consults, and the env var keeps working as an equivalent. BU_CDP_WS was read in the client and handed to transport.connect(), but the daemon transport discards that argument and always dials the daemon socket — and the daemon's own discovery never looked at the variable, so setting it did nothing. The override now lives in discoverEndpoint, which is what both processes go through, and the client's redundant read is gone. The variable must be set before the daemon spawns. 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(util): strip comment noiseamankumarsingh77
Removes file-header banners and comments that restate the code. Keeps one-line notes at the optional-sharp dynamic import, the sharp .default probe, the `never` contravariance trick, the sanctioned renderCall cast, and safeJs's interpolation rule. 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(tools): declare concurrency in the tool definitionamankumarsingh77
serialized: true was applied in registry.ts, away from the tools it describes. Now a required concurrency field on each definition, with a test pinning the serialized set so the classes cannot drift. The lock check defaults to taking the mutex for any class that is not explicitly "parallel", so a definition that bypasses defineBrowserTool at runtime fails safe rather than racing on shared CDP state. The test script gains --conditions=import: pi-coding-agent publishes no "require" condition, so importing any tool definition from a CJS-loaded test could not resolve it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
2026-05-04feat: parallel tool execution with automatic mutation serializationamankumarsingh77
- Add lightweight async mutex (src/util/mutex.ts) for FIFO serialization - Add serialized?: boolean to BrowserToolDefinition - Wire mutex acquisition into registerBrowserTool execute wrapper - Expose mutationMutex() on BrowserClient - Mark all mutation tools (click, type, scroll, navigate, switch_tab, etc.) as serialized - Leave observation tools (screenshot, page_info, execute_js, http_get, etc.) unguarded - Update prompt and skill docs to reflect parallel-safe behavior - Fix scroll tool: add Page.bringToFront, explicit button:none/buttons:0/pointerType:mouse - Update .gitignore to ignore scripts/ and data/ folders
2026-05-02fix: replace @ts-expect-error with @ts-ignore for dynamic sharp importamankumarsingh77
2026-05-02chore: final cleanup — drop unused keyCode exportamankumarsingh77
2026-05-02feat(domains): migrate screenshot, add typed sharp shim, restore click ↵amankumarsingh77
crosshair overlay
2026-05-02feat(domains): migrate browser_click to defineBrowserTool — vertical sliceamankumarsingh77
Implements Task 9: adds src/domains/click.ts with the new architecture, updates registry.ts to register clickTool, removes the legacy browser_click block from tools.ts, and wires BrowserClient alongside BrowserDaemon in index.ts so both systems coexist during migration. Also fixes AnyBrowserToolDefinition variance: uses never-arg renderCall in the erased type and a boundary cast in registerBrowserTool so concrete tool definitions (with specific Static<S> args) are assignable to the registry array without widening.
2026-05-02feat: add defineBrowserTool helper, truncation, schemas/common, empty registryamankumarsingh77
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02docs(util): clarify safeJs trust boundary and unsupported value typesamankumarsingh77
2026-05-02feat(util): add pure keycodes and safeJs template helperamankumarsingh77
2026-05-02fix(util): preserve AbortSignal.reason; validate namespace in temp pathsamankumarsingh77
2026-05-02feat(util): add result, time, and paths primitives for v0.3 rewriteamankumarsingh77