diff options
| author | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-13 07:29:41 -0400 |
|---|---|---|
| committer | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-13 07:29:41 -0400 |
| commit | 4c54b05dc15b81e449dc8ac53da8bae536fa2798 (patch) | |
| tree | f81c5e627dcc4dd81d2fe2fd3b2b584e72a73eca /README.md | |
| parent | 5951b3fb04aaf53b875afe265a57a944d6fce641 (diff) | |
pi-chrome: snapshot pre-migration state
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 301 |
1 files changed, 200 insertions, 101 deletions
@@ -1,63 +1,82 @@ # pi-chrome [](https://www.npmjs.com/package/pi-chrome) +[](https://www.npmjs.com/package/pi-chrome) +[](./LICENSE) -Control the Chrome profile you already use from Pi. +> **The fastest way to give a [Pi](https://pi.dev) agent your real Chrome.** +> No CDP. No throwaway profile. No re-login. Watch it work — or run silent. -`pi-chrome` gives Pi agents browser tools for your **real Chrome windows, tabs, and authenticated sessions**. It uses a companion Chrome extension instead of the Chrome DevTools Protocol (CDP), so it does not launch a throwaway debug browser profile and does not require re-signing into the apps you already have open. +```text +You: "Find my open GitHub PR tab, summarize review state, and screenshot the failing CI." +Agent: chrome_tab(list) → chrome_snapshot(uid:…) → chrome_screenshot(...) + ✓ 3 reviewers, 1 change requested, CI red on iOS. Saved → .pi/chrome-screenshots/ci.png +You: [keeps coding — agent never asked you to log in] +``` -Multiple Pi sessions can use Chrome at the same time. The first Pi session starts the local bridge; later sessions automatically detect that bridge and submit their Chrome commands through it. +`pi-chrome` ships **20+ browser tools** for Pi agents, backed by a small MIT-licensed Chrome extension that runs inside the Chrome profile **you already use** — including every site you're already signed into. -## Why try it? +--- -- **Uses your existing Chrome profile** — works with the Chrome windows/tabs you are already using, including logged-in GitHub, admin dashboards, local apps, and internal tools. -- **Watch your authenticated Chrome work** — by default, `chrome_*` tool calls focus Chrome and activate the target tab so you can see the agent inspect, navigate, click, and type in real time. Switch to silent/background mode for the whole session with `/chrome quiet`, or pass `background: true` on a single tool call when you want quiet. -- **Full browser automation toolkit for Pi** — list/create/activate/close tabs, snapshot pages with usable CSS selectors, navigate, evaluate JavaScript, click, type, press keys, wait for page state, and capture screenshots. -- **Built-in setup and agent guidance** — `/chrome onboard` walks users through installing the companion extension, `/chrome doctor` checks connectivity and version drift, screenshots save to disk, and the prompt primer tells agents to inspect with `chrome_snapshot` before acting and avoid destructive actions unless explicitly requested. +## Why pi-chrome vs. everything else -## Install +> Short version: **pi-chrome is primitives — "Playwright for the Chrome you're already signed into."** Not an agent loop. Plug it under any agent framework (Browser Use, Stagehand, LangGraph) or call its tools directly from a Pi agent. See [docs/COMPARISON.md](./docs/COMPARISON.md) for the full three-axis landscape (drivers, agents, cloud providers). -```bash -pi install npm:pi-chrome -``` +| | **pi-chrome** | Playwright / Puppeteer | CDP-based agents | Selenium / WebDriver | +| ------------------------------ | --------------------------------- | ----------------------------- | ----------------------------- | ----------------------------- | +| Uses your real signed-in Chrome | ✅ yes (extension in your profile) | ❌ throwaway profile | ⚠️ requires `--remote-debug` | ❌ throwaway profile | +| Re-login required | **Never** | Every run | Sometimes | Every run | +| Watch agent work, live | ✅ default; toggle quiet | ❌ headless or new window | ⚠️ debugger banner always | ❌ new window | +| Works on strict-CSP pages | ✅ `new Function` MAIN-world | ✅ | ✅ | ✅ | +| Real browser-trusted clicks | ✅ opt-in (`chrome clicks on`) | ✅ | ✅ | ✅ | +| Multi-session safe | ✅ shared local bridge | ❌ port collisions | ❌ | ❌ | +| Network/console capture | ✅ built-in | ✅ | ✅ | ⚠️ via extensions | +| Honest result envelopes¹ | ✅ | ⚠️ | ❌ | ❌ | +| Built-in benchmark suite² | ✅ 38 primitives + 4 long-horizon | n/a | n/a | n/a | -For local development from a checkout: +¹ Every action returns `pageMutated`, `defaultPrevented`, `elementVisible`, `occludedBy`, and `valueMatches` so the agent knows when a click didn't take effect — instead of looping blindly. +² See [`test-suite/`](./test-suite) — 38 primitive challenges plus 4 hermetic BrowserGym-style tasks. Scoring is expected-outcome-by-mode (`synthetic` / `trusted` / `manual`), not raw PASS count. Pages grade any browser-control tool on trusted clicks, pointer humanization, keyboard fidelity, drag/drop, clipboard, Shadow DOM, iframes, file uploads, network capture, and fingerprint leaks. -```bash -pi install ./pi-chrome -``` +--- -### Why an unpacked Chrome extension? +## What an agent gets -`pi-chrome` cannot ship through the Chrome Web Store: a Web Store extension is not allowed to talk to a local bridge controlled by another tool. Instead it ships as a small, MIT-licensed unpacked extension in `extensions/chrome-profile-bridge/browser-extension/` — read the source before loading. `/chrome doctor` reports the loaded extension version and warns when it drifts from the installed `pi-chrome`. +**20 tools**, grouped by job. Every one runs against your already-open tabs. -## First-time setup +| Category | Tools | +| --------------- | ---------------------------------------------------------------------------------------------- | +| **Tabs** | `chrome_tab` (list/new/activate/close/version), `chrome_launch` | +| **Inspect** | `chrome_snapshot` (uids + selectors + text + viewport), `chrome_screenshot`, `chrome_evaluate` | +| **Navigate** | `chrome_navigate` (with optional `initScript` at `document_start`), `chrome_wait_for` | +| **Interact** | `chrome_click`, `chrome_type`, `chrome_fill`, `chrome_key`, `chrome_hover` | +| **Gesture** | `chrome_drag` (HTML5 DataTransfer), `chrome_scroll` (wheel + momentum), `chrome_tap` (touch) | +| **Files** | `chrome_upload_file` (no native picker; works with React/Vue/Angular file inputs) | +| **Observe** | `chrome_list_console_messages`, `chrome_list_network_requests`, `chrome_get_network_request` (with response body) | -In Pi, run: +Each tool is documented inline in Pi — agents see the parameters and the gotchas (synthetic vs. trusted, autoplay gates, file picker limits) without trial-and-error. -```text -/chrome onboard +--- + +## 60-second install + +```bash +pi install npm:pi-chrome ``` -Pi first shows setup instructions and waits for confirmation. Press Enter to continue. On macOS it will: +Then in Pi: -- open `chrome://extensions` -- reveal the bundled `browser-extension` folder in Finder -- copy the extension folder path to your clipboard +```text +/chrome onboard +``` -Then in Chrome: +On macOS this opens `chrome://extensions`, reveals the bundled `browser-extension/` folder in Finder, and copies its path to your clipboard. In Chrome: **Developer mode** → **Load unpacked** → paste the path. Done. -1. Enable **Developer mode**. -2. Click **Load unpacked**. -3. Select the revealed/copied `browser-extension` folder. -4. Return to Pi and run: +Verify: ```text /chrome doctor ``` -Expected output: - ```text Performing Chrome bridge health check pi-chrome v<version> @@ -65,122 +84,202 @@ pi-chrome v<version> ✓ Companion Chrome extension responding (ID: <chrome-extension-id>, ext v<version>) ``` -## Click modes +--- -pi-chrome can drive Chrome two ways: +## Try this in 30 seconds after install -- **Quiet clicks** — fast and unobtrusive. They work on most sites, but some pages (sign-in flows, copy-to-clipboard buttons, file pickers, autoplay videos, fullscreen, paywalls) ignore them because they don't look like a real human action. -- **Real-looking clicks** — indistinguishable from a person clicking. They unlock the cases above, but Chrome shows a *"Pi Chrome Connector started debugging this browser"* banner at the top of every tab pi-chrome touches while it's working. +```text +Use chrome_tab list to find my GitHub notifications tab. +chrome_snapshot it, then write a 5-bullet triage: +which PRs need my review today, sorted by staleness. +Do not click anything yet. +``` -Pick a mode with `/chrome clicks`: +You'll watch the agent jump to your GitHub tab and read the page — using **your** session, **your** filters, **your** orgs. -```text -/chrome clicks auto # default; quiet by default, real-looking only when needed -/chrome clicks off # always quiet, no banner ever -/chrome clicks on # always real-looking, banner stays up the whole session -/chrome clicks status # show the current mode +--- + +## Killer recipes (copy-paste into Pi) + +Each one assumes tabs you already have open + accounts you're already signed into. + +**PR triage** +> Use chrome_tab list to find my GitHub notifications tab, snapshot it, summarize PRs needing my review. + +**Linear standup** +> Open my Linear current cycle in the active tab, snapshot it, write a 5-bullet standup. + +**Bug repro with evidence** +> Open the staging app I'm already signed into, reproduce \<bug>, save a screenshot of each step under `./repro/`. + +**Form auto-fill (no submit)** +> Open the vendor portal, fill the new-vendor form from this JSON, stop before submit. + +**Admin cross-check** +> Across my Stripe / Postmark / our admin tabs, find any user where state disagrees. + +**Local dev visual diff** +> Snapshot `localhost:3000` and the staging URL of the same page; tell me what's visually different. + +**Auth-only data pull** +> Open my analytics dashboard tab and `chrome_evaluate` to extract today's KPIs from page state. + +**Network forensics** +> Reproduce the checkout bug, then use `chrome_list_network_requests` to find the failing call and dump its response body. + +**File upload through React** +> Open the photo uploader, `chrome_upload_file` with `./fixtures/sample.png`, confirm preview rendered. + +--- + +## Architecture + +``` + ┌──────────────────────┐ ┌──────────────────────────┐ + │ Pi agent (terminal) │ ─── http://127.0.0.1:17318 ─→ │ Chrome extension │ + │ chrome_* tools │ │ (your real profile) │ + └──────────┬───────────┘ └─────────┬────────────────┘ + │ same machine │ + ▼ ▼ + Other Pi sessions Tabs you already have open + share the same bridge (signed in to GitHub, + automatically Linear, Stripe, etc.) ``` -For a one-off call, pass `trusted: true` (or `false`) on `chrome_click`, `chrome_type`, `chrome_fill`, `chrome_key`, `chrome_hover`, `chrome_drag`, or `chrome_scroll`. The per-call value wins over the global mode. +Multiple Pi sessions (planner / worker / audit) can all drive the same Chrome at once. The first session opens the local bridge; later sessions detect it and pipe their commands through. -First time you update pi-chrome to a version that supports real-looking clicks, Chrome will ask you to re-approve the extension. Open `chrome://extensions` and accept the new permission once. +--- -## Background mode +## Click & input modes -By default, `chrome_*` tools focus Chrome and activate the target tab so you can watch the agent work — great for demos, pair-driving, debugging, and first-time confidence that things are happening. +`pi-chrome` can drive Chrome two ways: -When you want quiet (planner / audit / worker sessions running alongside your editor), turn background mode on for the whole Pi session: +- **Quiet** — synthetic DOM events. Fast, no UI banners. Drives React/Vue/Angular state. Won't satisfy autoplay, clipboard, file picker, fullscreen, or user-activation gates. +- **Trusted** — `chrome.debugger` / CDP under the hood. Indistinguishable from a person clicking. Shows Chrome's *"Pi Chrome Connector started debugging this browser"* banner while active. ```text -/chrome quiet # toggle -/chrome quiet on # explicit -/chrome quiet off # explicit +/chrome clicks auto # default: quiet, upgrade to trusted only when needed +/chrome clicks off # always quiet, never banner +/chrome clicks on # always trusted, banner stays up +/chrome clicks status ``` -For a single tool call, the agent can pass `background: true` directly. The per-call value always wins over the session toggle. +Per-call `trusted: true / false` on any input tool wins over the global mode. -## Quick demo prompts +--- -After setup, try one of these in Pi: +## Background / watch modes -Silent inspection (no Chrome interruption): +By default, every `chrome_*` call focuses Chrome and activates the target tab so you can **watch the agent work** — invaluable for demos, debugging, and first-time confidence. ```text -Inspect my active GitHub tab with chrome_snapshot using background:true and summarize the PR state without focusing Chrome. +/chrome quiet # toggle for the whole session +/chrome quiet on # explicit +/chrome quiet off # explicit ``` -Existing authenticated tab: +Per-call `background: true` wins over the session toggle. + +--- + +## Honest results + +Most browser-automation libraries return `void` or a generic ack. `pi-chrome` returns a structured envelope on every interaction: ```text -Use chrome_tab list to find my existing GitHub tab, chrome_snapshot it, then summarize the visible PR state. Do not click anything yet. +chrome_click(occluded-button) → + "Clicked el-3 — pageMutated=false; occluded by <div#overlay>" ``` -Local web app repro with screenshot: - ```text -Use chrome_tab list to find my localhost app, inspect it with chrome_snapshot, navigate through the bug repro flow, and save a screenshot when you reach the broken state. +chrome_type(react-input, "hello") → + "Typed into el-7 — valueMatches=true; pageMutated=true" ``` -## Recipes +This is why agents using pi-chrome don't get stuck in retry loops on broken sites. They get the **reason** the action didn't land and can fix course in one turn. -Copy-paste these into Pi after setup. Each one uses tabs you already have open and accounts you are already signed into. +--- -- **PR triage:** "Use chrome_tab list to find my GitHub notifications tab, snapshot it, and summarize PRs needing my review." -- **Linear standup:** "Open my Linear current cycle in the active tab, snapshot it, and write me a 5-bullet standup." -- **Bug repro with evidence:** "Open the staging app I'm already signed into, reproduce <bug>, and save a screenshot of each step under ./repro/." -- **Form auto-fill (no submit):** "Open <vendor> portal, fill the new-vendor form from this JSON, but stop before submit." -- **Admin cross-check:** "Across my Stripe / Postmark / our admin tabs, find any user where state disagrees." -- **Local dev visual diff:** "Snapshot localhost:3000 and the staging URL of the same page; tell me what's visually different." -- **Auth-only data pull:** "Open my analytics dashboard tab and chrome_evaluate to extract today's KPIs from the page state." +## Diagnostics -Screenshots save under `.pi/chrome-screenshots/` by default, which composes nicely with PR demo workflows. +- `/chrome doctor` — single command: connectivity, extension version, bridge owner, version drift, MAIN-world helper injection, `chrome_evaluate("1+1") === 2`, fingerprint flags. +- `/chrome onboard` — guided first-time setup. +- `/chrome quiet status`, `/chrome clicks status` — current modes. -## Diagnostics +If the loaded Chrome extension is older than `pi-chrome` on disk, `/chrome doctor` tells you to reload it from `chrome://extensions`. -- `/chrome doctor` — single command that checks connectivity and reports the loaded Chrome extension ID + version, plus a one-line fix for common setup failures (extension not loaded, bridge owner stale after `pi update`, version mismatch between pi-chrome and the loaded Chrome extension). +--- -If the Chrome extension you have loaded is older than `pi-chrome` on disk, `/chrome doctor` will tell you to reload it from `chrome://extensions`. +## Composes with -## Compose with +- **[pi-qq](https://www.npmjs.com/package/pi-qq)** — `/qq summarize what the active GitHub tab shows` without polluting the main transcript. +- **[pi-bar](https://www.npmjs.com/package/pi-bar)** — when the agent scrapes large pages, watch the context-usage segment turn yellow → red as a signal to `/qq` for a recap. +- **PR demo skills** — screenshots write to `.pi/chrome-screenshots/` so you can attach them to PR descriptions or demo bundles. -- **pi-qq** — ask side questions about what the agent saw in Chrome without polluting the main transcript: `/qq summarize what the active GitHub tab shows`. -- **pi-bar** — watch context pressure as the agent scrapes large pages; the footer's red threshold is a clean signal to `/qq` for a recap before context overflows. -- **PR demo skills** (such as `ios-pr-agent` / `ios-demo-record` workflows) — `chrome_screenshot` writes to `.pi/chrome-screenshots/` so you can attach images to PR descriptions or demo bundles. +--- -## Tools +## Why an unpacked Chrome extension? -The package registers these Pi tools: +`pi-chrome` cannot ship through the Chrome Web Store — a Web Store extension cannot talk to a local bridge controlled by another tool on the same machine. So it ships as a small MIT-licensed unpacked extension in [`extensions/chrome-profile-bridge/browser-extension/`](./extensions/chrome-profile-bridge/browser-extension). **Read the source before loading.** `/chrome doctor` reports the extension version and warns when it drifts from your installed `pi-chrome`. -- `chrome_launch` — setup/help entry point; opens a URL if the bridge is connected -- `chrome_tab` — list, create, activate, close, or inspect tabs -- `chrome_snapshot` — inspect title, URL, visible text, viewport, and clickable/focusable selectors -- `chrome_navigate` — navigate an existing tab -- `chrome_evaluate` — evaluate JavaScript in a tab -- `chrome_click` — click by selector or coordinates -- `chrome_type` — type text, optionally focusing a selector first -- `chrome_key` — send keyboard keys -- `chrome_wait_for` — wait for a selector or expression -- `chrome_screenshot` — capture viewport screenshots to disk +--- -These tools are especially useful for authenticated web app debugging, repro flows, admin workflows, visual checks, and inspecting local development pages without rebuilding login state. +## Security model -<details> -<summary><strong>How it works (technical details)</strong></summary> +The companion extension runs in the Chrome profile where you install it and has broad tab/scripting permissions. Only install it from a package source you trust. -Pi starts a local bridge on `127.0.0.1:17318`. The companion Chrome extension, installed in your normal Chrome profile, polls that local bridge for commands and executes them using Chrome extension APIs. +The Pi side listens on `127.0.0.1:17318` by default. Override before starting Pi: -If another Pi session is already running the bridge, additional Pi sessions automatically act as clients of that shared bridge. This lets planner/audit/worker sessions all use the same authenticated Chrome profile concurrently without fighting over the port. +```bash +PI_CHROME_BRIDGE_PORT=17319 pi +``` -This is intentionally different from CDP-based tools: the browser extension lives inside the profile you already use, so Pi can interact with existing tabs and authenticated page state. +There is no network exposure; the bridge binds to loopback only. -</details> +--- -## Security model +## Built-in benchmark suite -The companion Chrome extension runs in the Chrome profile where you install it and has broad tab/scripting permissions. Only install it from a package source you trust. +[`test-suite/`](./test-suite) is a benchmark for **any** browser-control agent (not just pi-chrome). It includes **38 primitive challenges** plus **4 hermetic BrowserGym-style long-horizon tasks**. -The Pi side listens on `127.0.0.1:17318` by default. Override before starting Pi with: +Scoring is **expected-outcome-by-mode**, not raw PASS count: each challenge has an expected verdict per mode (`synthetic`, `trusted`, `manual`) and a tool grades itself by whether its actual outcome matches the expected one. This avoids false equivalence between modes — a synthetic-events tool isn't supposed to satisfy a clipboard user-activation gate; matching that expectation is the pass. + +Each challenge exposes `window.__verdict` / `window.__reason` / `window.__events` and a manifest entry with expected results per mode. ```bash -PI_CHROME_BRIDGE_PORT=17319 pi +cd test-suite && python3 -m http.server 8765 +# open http://127.0.0.1:8765/ in the Chrome window pi-chrome controls ``` + +Categories: `trusted-input`, `pointer-humanization`, `keyboard`, `activation-gates`, `scroll`, `drag-drop`, `clipboard`, `native-controls`, `frameworks`, `editing`, `dom-complexity`, `frames`, `files`, `observability`, `fingerprint`, `agent-safety`. + +If you build a competing tool, please open a PR with your scores. We benchmark in public. + +--- + +## Roadmap signals + +`pi-chrome` is actively shipped. Things on the near roadmap: + +- More observability tools (DOM mutation streams, performance traces) +- First-class iframe + Shadow-DOM uid stability across snapshots +- Web Push & service worker introspection +- Recorder mode that emits agent prompts from your own clicks + +If you want one of those next, open an issue. + +--- + +## Contributing + +PRs welcome. The bar: + +1. Add a benchmark page in `test-suite/` that fails before your change and passes after. +2. Keep `chrome_*` tool results honest — surface `pageMutated`, `valueMatches`, `defaultPrevented`, etc. +3. Don't break the "no re-login" guarantee. Anything that requires a fresh profile is out of scope. + +--- + +## License + +MIT. See [LICENSE](./LICENSE). |
