<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sz-chrome-remote-cdp.git/src/daemon/protocol.ts, branch master</title>
<subtitle>pi-browser-harness, patched to drive a Chrome on another host over CDP, with castle_cdp_search web search
</subtitle>
<id>https://git.soarez.net/sz/sz-chrome-remote-cdp.git/atom?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/'/>
<updated>2026-08-02T13:42:46+00:00</updated>
<entry>
<title>refactor: act on the fallow audit</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T16:59:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=b484c017807acb902eaaaa4e242f8b79f8e8eba6'/>
<id>urn:sha1:b484c017807acb902eaaaa4e242f8b79f8e8eba6</id>
<content type='text'>
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) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: delete exports and knobs that nothing calls</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T16:17:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=aedbb88a8025851067be9f77ec406b29d65599a8'/>
<id>urn:sha1:aedbb88a8025851067be9f77ec406b29d65599a8</id>
<content type='text'>
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) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: tidy the whitespace the comment removal left behind</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T15:04:49+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=a9bdbd8590801cd9c2fa0cda6203948ac19a779c'/>
<id>urn:sha1:a9bdbd8590801cd9c2fa0cda6203948ac19a779c</id>
<content type='text'>
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) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor(daemon): strip comment noise</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-28T14:57:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=a2588bb315a56a8dfcd3aa8a261cdc19b160d079'/>
<id>urn:sha1:a2588bb315a56a8dfcd3aa8a261cdc19b160d079</id>
<content type='text'>
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) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>refactor: close the remaining unguarded type assertions</title>
<updated>2026-08-02T13:42:46+00:00</updated>
<author>
<name>amankumarsingh77</name>
<email>amankumarsingh7702@gmail.com</email>
</author>
<published>2026-07-27T19:49:58+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=f9dcdf7f6dcdf2979693a9f179eda2b9995d57ae'/>
<id>urn:sha1:f9dcdf7f6dcdf2979693a9f179eda2b9995d57ae</id>
<content type='text'>
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&lt;string, unknown&gt;` 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-&gt;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) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>fix: make browser daemon setup work on Windows (#7) (#12)</title>
<updated>2026-07-12T16:33:43+00:00</updated>
<author>
<name>Aman Kumar</name>
<email>145339995+amankumarsingh77@users.noreply.github.com</email>
</author>
<published>2026-07-12T16:33:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=cf92c10eda651cdad882aa050439c80d2f40b4b1'/>
<id>urn:sha1:cf92c10eda651cdad882aa050439c80d2f40b4b1</id>
<content type='text'>
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\&lt;name&gt;); 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</content>
</entry>
<entry>
<title>release: v0.6.0 — per-tab isolation, Unix socket daemon, on-demand init (#6)</title>
<updated>2026-06-20T19:16:44+00:00</updated>
<author>
<name>Aman Kumar</name>
<email>145339995+amankumarsingh77@users.noreply.github.com</email>
</author>
<published>2026-06-20T19:16:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/sz-chrome-remote-cdp.git/commit/?id=da5d31af999b728c31ab0169373531cab4543f0c'/>
<id>urn:sha1:da5d31af999b728c31ab0169373531cab4543f0c</id>
<content type='text'>
* 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</content>
</entry>
</feed>
