diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-14 12:08:17 +0100 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-14 12:16:02 +0100 |
| commit | fd06303bec47430f4644896d587f8f677245c9ac (patch) | |
| tree | 56a134bbe7b29ffb2bcbe65f7732746d447a2e5b | |
| parent | 7e2196bd1f046707d1930f8c3d9aa7e4cc390bd6 (diff) | |
FORK.md: what the fork changes, setup, upstream releases
| -rw-r--r-- | FORK.md | 141 |
1 files changed, 141 insertions, 0 deletions
@@ -0,0 +1,141 @@ +# Fork notes + +A fork of [tianrendong/pi-chrome](https://github.com/tianrendong/pi-chrome) that +lets pi on **other machines** drive one signed-in browser. It adds a remote mode +to the pi extension and a standalone broker for the browser's host. Without +`PI_CHROME_REMOTE` set, it behaves exactly like upstream. + +Not published to npm and not proposed upstream. + +``` +origin https://git.soarez.net/sz/pi-chrome.git +upstream https://github.com/tianrendong/pi-chrome.git +``` + +## Why the patch exists + +Upstream only works on the machine running the browser: + +- the companion extension long-polls a bridge at `127.0.0.1:17318`, hardcoded + in `browser-extension/service_worker.js`; +- that bridge lives inside whichever pi session started first on the machine, + and goes away when that session exits; +- a pi session on another host cannot join it. `PI_CHROME_BRIDGE_HOST` set to a + remote address makes `listen()` fail with `EADDRNOTAVAIL`, and only + `EADDRINUSE` switches a session into client mode, so the session fails at + startup. + +Upstream tried a standalone daemon in 0.16–0.17 (`docs/DESIGN-0.17-daemon.md` in +the v0.17.3 tag) and then dropped it; main is back on the 0.15 line. This fork +is based on main. + +## What the fork changes + +**`broker/broker.mjs`** runs on the browser's host in place of upstream's +in-session bridge. It speaks upstream's HTTP contract to the extension, so the +extension is loaded exactly as shipped. Node builtins only, no install step. It +has two listeners: + +| listener | address | routes | +|---|---|---| +| extension | `127.0.0.1:17318` | `/next`, `/result`, `/status`, `/command` | +| peer | `PI_CHROME_BROKER_PEER_HOST`, same port | `/command`, `/status` | + +Keeping `/next` and `/result` off the peer listener means nothing on the network +can drain the extension's queue or forge results. `/command` needs a bearer +token on both listeners, because the browser is signed into real accounts. Bind +the peer listener to an address on a network you trust (a VPN, say), never to +`0.0.0.0`. + +| Broker setting | Effect | +|---|---| +| `PI_CHROME_BROKER_PORT` | both listeners' port; default `17318`, which the extension expects | +| `PI_CHROME_BROKER_PEER_HOST` | the peer listener's address; unset, there is no peer listener | +| `PI_CHROME_TOKEN` | the bearer token | +| `PI_CHROME_TOKEN_COMMAND` | a command whose output is the token, for a secret store | + +**`extensions/chrome-profile-bridge/index.ts`**: every change is marked +`FORK:`. + +| Setting | Effect | +|---|---| +| `PI_CHROME_REMOTE` unset, or `local` (also `off`, `none`, `0`, `no`, `localhost`) | upstream behaviour | +| `PI_CHROME_REMOTE=host:port` | the broker there | +| `PI_CHROME_TOKEN`, `PI_CHROME_TOKEN_COMMAND` | the bearer token, as for the broker; read on first use | +| `PI_CHROME_AUTHORIZE=indefinite` (or `30m`, `45`…) | skip `/chrome authorize`: every session starts authorized | +| `PI_CHROME_REMOTE_LABEL` | how the prompt names the browser; default "the user's own browser on *host*" | +| `PI_CHROME_REMOTE_SSH` | the ssh target the prompt gives for file transfer; default the broker's host | +| `PI_CHROME_OTHER_BROWSER_TOOLS` | another browser's tools (e.g. `browser_*`), for the prompt to route between | + +In remote mode the session never binds a port and never promotes itself to +bridge. If the broker is unreachable the tools fail. They do not silently drive +some other browser on this machine. + +The system prompt gains a `pi-chrome-remote` section beside upstream's +`chrome-profile-bridge` primer, present while `chrome_*` is authorized. It is +added by a `before_agent_start` handler of its own, so upstream's handler stays +as shipped. It says `chrome_*` drives +a signed-in browser on another host, and, when another browser's tools are +named, to keep this one for tasks that need the user's sessions. It also covers +moving files: uploads are staged on the browser's host with scp, downloads land +in its `~/Downloads`, and screenshots are already local. `chrome_upload_file` +refuses relative paths in remote mode, because the browser opens them on its own +host. + +Pi sessions on different hosts each get their own automation window. The +extension keys windows by pi session id, which is globally unique. Background +mode, which is upstream's default, keeps them from focusing the browser or +switching its tabs. + +## MCP clients + +[pi-chrome-mcp](https://git.soarez.net/sz/pi-chrome-mcp.git) serves these +tools to Claude Code, Claude Desktop and other MCP clients. Point its +`PI_CHROME_PATH` at this fork's `index.ts` to use remote mode. + +## Setup + +On the browser's host: + +1. Clone this repository somewhere it will stay. The browser disables an + unpacked extension whose folder goes missing, so not a package manager's + cache that it may replace. +2. Load the extension once: `chrome://extensions` (or `brave://extensions`) -> + Developer mode -> Load unpacked -> + `extensions/chrome-profile-bridge/browser-extension`. +3. Run `node broker/broker.mjs` under a supervisor (launchd, systemd), with + `PI_CHROME_BROKER_PEER_HOST` and a token set. No pi session on that machine + may be running stock pi-chrome, which would take the port first. + +On every other machine, install the fork as a pi package and set +`PI_CHROME_REMOTE=<browser host>:17318` and the token. + +## Taking upstream releases + +``` +git fetch upstream +git rebase upstream/main +git push --force-with-lease origin master +``` + +Then update the broker's checkout, restart it, and reload the extension in the +browser. `/chrome doctor` reports a version mismatch until you do. + +Expect conflicts in the places marked `FORK:` in `index.ts`: the bridge class's +bind, forward and status code (including `connectionStatus`, whose `/status` +fetch carries the token), `session_start`, the remote prompt-section handler +just ahead of upstream's primer, doctor, and `chrome_upload_file`. If upstream changes the extension↔bridge HTTP contract +(`/next`, `/result`, the `x-pi-chrome-version` header), port the change to +`broker/broker.mjs`. Its request handling is a line-by-line port of the server +half of `ChromeProfileBridge`. + +## Verifying + +`/chrome doctor` in a pi session should report "using the pi-chrome broker at +http://*host*:17318" and a connected extension. From outside pi: + +``` +curl -s -H "Authorization: Bearer $PI_CHROME_TOKEN" http://<host>:17318/status +``` + +`connected: true` with a recent `lastSeenAt` means the extension is polling. |
