pi-browser-harness 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to pi-browser-harness will be documented in this file.
4
+
5
+ ## [0.1.0] - 2026-05-02
6
+
7
+ ### Added
8
+ - Initial release of pi-browser-harness.
9
+ - 20 browser control tools (`browser_navigate`, `browser_screenshot`, `browser_click`, `browser_type`, `browser_press_key`, `browser_scroll`, `browser_execute_js`, `browser_http_get`, `browser_new_tab`, `browser_open_urls`, `browser_switch_tab`, `browser_list_tabs`, `browser_current_tab`, `browser_page_info`, `browser_go_back`, `browser_go_forward`, `browser_reload`, `browser_wait`, `browser_wait_for_load`, `browser_handle_dialog`).
10
+ - Self-extending harness: `list_dynamic_tools`, `register_tool`, `remove_tool` — the agent can write new browser tools at runtime.
11
+ - Guided setup command (`/browser-setup`) with Chrome detection, automatic browser-harness installation via `uv` or `git clone`.
12
+ - `/browser-status` and `/browser-reload-daemon` commands for daemon health monitoring.
13
+ - `--browser-namespace` and `--browser-debug-clicks` CLI flags.
14
+ - Session persistence for tab history and daemon namespace across reloads and branch navigation.
15
+ - System prompt injection with browser usage guidance and common workflow patterns.
16
+ - Custom TUI renderers for screenshots and tab listings.
17
+ - Dialog detection and handling for JS `alert`/`confirm`/`prompt`/`beforeunload`.
18
+ - Parallel URL opening via `browser_open_urls` with live progress streaming.
19
+ - Output truncation with temp-file fallback for large JS evaluation and HTTP responses.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pi Browser Harness Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # pi-browser-harness
2
+
3
+ Full browser control for pi agents. Navigate, click, type, screenshot, and extract data from your real Chrome — driven by your coding agent.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # 1. Install
11
+ pi install npm:pi-browser-harness
12
+
13
+ # Or install from GitHub:
14
+ # pi install git:github.com/amankumarsingh77/pi-browser-harness
15
+
16
+ # 2. Enable Chrome remote debugging
17
+ # Open chrome://inspect/#remote-debugging in Chrome,
18
+ # tick the "Discover network targets" checkbox, click Allow.
19
+ # No Chrome relaunch needed.
20
+
21
+ # 3. Connect pi to Chrome
22
+ /browser-setup
23
+ ```
24
+
25
+ That's it. Try:
26
+
27
+ ```
28
+ > navigate to https://github.com and take a screenshot
29
+ ```
30
+
31
+ ### Requirements
32
+
33
+ - pi (latest)
34
+ - Node.js ≥ 22
35
+ - Chrome / Chromium / Edge (any recent version)
36
+
37
+ > If `chrome://inspect/#remote-debugging` isn't available, you can launch Chrome with `--remote-debugging-port=9222` instead. The harness will find it either way.
38
+
39
+ ---
40
+
41
+ ## What you can do
42
+
43
+ ### Navigation & tabs
44
+
45
+ | Tool | What it does |
46
+ |------|--------------|
47
+ | `browser_navigate` | Go to a URL (reuses the attached tab) |
48
+ | `browser_new_tab` | Open a new tab, optionally navigate |
49
+ | `browser_open_urls` | Open many URLs in parallel tabs |
50
+ | `browser_go_back` / `browser_go_forward` | History navigation |
51
+ | `browser_reload` | Reload the current page |
52
+ | `browser_list_tabs` / `browser_current_tab` | Discover tabs |
53
+ | `browser_switch_tab` | Switch the active tab |
54
+ | `browser_page_info` | URL, title, viewport, scroll, dialog state |
55
+
56
+ ### Interaction
57
+
58
+ | Tool | What it does |
59
+ |------|--------------|
60
+ | `browser_click` | Click at viewport coordinates (works through iframes / shadow DOM) |
61
+ | `browser_type` | Type into the focused element |
62
+ | `browser_press_key` | Send a key with optional modifiers |
63
+ | `browser_scroll` | Scroll by delta pixels |
64
+ | `browser_handle_dialog` | Accept or dismiss `alert` / `confirm` / `prompt` |
65
+
66
+ ### Visual & data
67
+
68
+ | Tool | What it does |
69
+ |------|--------------|
70
+ | `browser_screenshot` | Capture the current page as PNG |
71
+ | `browser_execute_js` | Run JS in the page and return the result |
72
+ | `browser_http_get` | Direct HTTP GET — bypasses the browser, much faster for APIs |
73
+ | `browser_wait` / `browser_wait_for_load` | Sleep, or wait for `readyState === 'complete'` |
74
+
75
+ ### Extending the harness at runtime
76
+
77
+ | Tool | What it does |
78
+ |------|--------------|
79
+ | `browser_run_script` | Execute a temporary script file with daemon access |
80
+
81
+ ---
82
+
83
+ ## Usage
84
+
85
+ The core pattern is **screenshot → act → screenshot → verify.** Don't assume an action worked — look.
86
+
87
+ ### Navigate and screenshot
88
+
89
+ ```
90
+ browser_navigate("https://github.com/browser-use/browser-harness")
91
+ browser_wait_for_load()
92
+ browser_screenshot()
93
+ ```
94
+
95
+ ### Fill a form
96
+
97
+ ```
98
+ browser_screenshot() # see the form
99
+ browser_click(400, 200) # click the search input
100
+ browser_type("pi browser harness") # type
101
+ browser_press_key("Enter") # submit
102
+ browser_wait_for_load()
103
+ browser_screenshot() # verify
104
+ ```
105
+
106
+ ### Extract data
107
+
108
+ ```js
109
+ // Fast: hit the API directly, no browser
110
+ browser_http_get("https://api.github.com/repos/browser-use/browser-harness")
111
+
112
+ // DOM scraping for rendered content
113
+ browser_execute_js("document.querySelector('.price').innerText")
114
+
115
+ // Structured arrays
116
+ browser_execute_js(`
117
+ JSON.stringify(
118
+ Array.from(document.querySelectorAll('.result')).map(el => ({
119
+ title: el.querySelector('h3')?.textContent,
120
+ link: el.querySelector('a')?.href,
121
+ }))
122
+ )
123
+ `)
124
+ ```
125
+
126
+ ### Open multiple results in parallel
127
+
128
+ ```
129
+ browser_navigate("https://google.com/search?q=chrome+devtools+protocol")
130
+ browser_open_urls({ urls: [
131
+ "https://chromedevtools.github.io/devtools-protocol/",
132
+ "https://developer.chrome.com/docs/devtools",
133
+ ]})
134
+ browser_list_tabs()
135
+ browser_switch_tab({ targetId: "..." })
136
+ ```
137
+
138
+ ### SPAs
139
+
140
+ `browser_wait_for_load` only checks `readyState`. For React/Vue/etc., poll for the actual element you want:
141
+
142
+ ```js
143
+ browser_execute_js("!!document.querySelector('.loaded-content')")
144
+ ```
145
+
146
+ ### Keyboard modifiers
147
+
148
+ `browser_press_key` takes a modifier bitfield: `Alt=1`, `Ctrl=2`, `Meta/Cmd=4`, `Shift=8`. Combine with `|`.
149
+
150
+ ```
151
+ browser_press_key("c", { modifiers: 2 }) // Ctrl+C
152
+ browser_press_key("v", { modifiers: 4 }) // Cmd+V
153
+ browser_press_key("T", { modifiers: 10 }) // Ctrl+Shift+T
154
+ ```
155
+
156
+ ### Writing a temporary script
157
+
158
+ When the built-ins aren't enough, write a script to disk and run it with `browser_run_script`.
159
+
160
+ ```js
161
+ // 1. Write the script to disk
162
+ write("/tmp/scrape-results.js", `
163
+ const data = await daemon.evaluateJS(\`
164
+ JSON.stringify(
165
+ Array.from(document.querySelectorAll('${params.selector}'))
166
+ .map(el => el.textContent.trim())
167
+ )
168
+ \`);
169
+ return {
170
+ content: [{ type: "text", text: data }],
171
+ details: { items: JSON.parse(data) },
172
+ };
173
+ `)
174
+
175
+ // 2. Execute it
176
+ browser_run_script("/tmp/scrape-results.js", { selector: ".result" })
177
+ ```
178
+
179
+ Scripts receive `params`, `daemon`, `require`, `signal`, `onUpdate`, `ctx`, plus the standard JS globals. The script is on disk — auditable and re-runnable.
180
+
181
+ ---
182
+
183
+ ## Commands
184
+
185
+ | Command | Description |
186
+ |---------|-------------|
187
+ | `/browser-setup` | Connect pi to Chrome (run once) |
188
+ | `/browser-status` | Show daemon health and current page |
189
+ | `/browser-reload-daemon` | Restart the connection |
190
+
191
+ ---
192
+
193
+ ## Troubleshooting
194
+
195
+ | Symptom | Fix |
196
+ |---------|-----|
197
+ | `DevToolsActivePort not found` | Open `chrome://inspect/#remote-debugging`, tick the checkbox, click Allow, then retry. |
198
+ | Connection fails after Chrome restart | Run `/browser-reload-daemon`. |
199
+ | Page seems loaded but content is missing | SPA — `browser_wait_for_load` isn't enough. Poll with `browser_execute_js` for the element you need. |
200
+ | A JS dialog is blocking actions | `browser_page_info` will report the dialog. Use `browser_handle_dialog` to accept or dismiss. |
201
+
202
+ ---
203
+
204
+ ## Security
205
+
206
+ This extension drives your real Chrome. The agent can see open tabs, read page content, submit forms, and act inside authenticated sessions. `browser_run_script` evaluates JavaScript in the pi process with full `require` access — review any temporary scripts the agent writes to `/tmp/` before executing them.
207
+
208
+ ---
209
+
210
+ ## License
211
+
212
+ MIT — see [LICENSE](LICENSE).
Binary file
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "pi-browser-harness",
3
+ "version": "0.1.0",
4
+ "description": "Browser control extension for pi — navigate, click, type, screenshot, and extract data from real Chrome via CDP",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi-extension",
8
+ "browser",
9
+ "cdp",
10
+ "chrome",
11
+ "web-automation",
12
+ "screenshot",
13
+ "scraping"
14
+ ],
15
+ "author": "Pi Browser Harness Contributors",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/amankumarsingh77/pi-browser-harness.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/amankumarsingh77/pi-browser-harness/issues"
23
+ },
24
+ "homepage": "https://github.com/amankumarsingh77/pi-browser-harness#readme",
25
+ "engines": {
26
+ "node": ">=22.0.0"
27
+ },
28
+ "files": [
29
+ "src/",
30
+ "skills/",
31
+ "assets/",
32
+ "tsconfig.json",
33
+ "LICENSE",
34
+ "README.md",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "pi": {
38
+ "extensions": [
39
+ "./src/index.ts"
40
+ ],
41
+ "skills": [
42
+ "./skills"
43
+ ],
44
+ "image": "https://raw.githubusercontent.com/amankumarsingh77/pi-browser-harness/main/assets/hero.png"
45
+ },
46
+ "peerDependencies": {
47
+ "@mariozechner/pi-coding-agent": "*",
48
+ "@mariozechner/pi-tui": "*",
49
+ "typebox": "*"
50
+ },
51
+ "devDependencies": {
52
+ "@mariozechner/pi-coding-agent": "*",
53
+ "@mariozechner/pi-tui": "*",
54
+ "@types/ws": "^8.18.1",
55
+ "typebox": "*",
56
+ "typescript": "^5.0.0"
57
+ },
58
+ "scripts": {
59
+ "typecheck": "tsc --noEmit",
60
+ "prepublishOnly": "tsc --noEmit"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "dependencies": {
66
+ "ws": "^8.20.0"
67
+ }
68
+ }
@@ -0,0 +1,213 @@
1
+ ---
2
+ name: pi-browser-harness
3
+ description: Direct browser control via CDP. Use when the user wants to automate, scrape, test, or interact with web pages. Connects to the user's already-running Chrome.
4
+ ---
5
+
6
+ # pi-browser-harness
7
+
8
+ Direct browser control via CDP. Gives pi agents full control of a real Chrome browser.
9
+ Browser tools coexist with all standard pi tools (read, bash, edit, write, etc.).
10
+
11
+ ## Setup
12
+
13
+ Run `/browser-setup` to install browser-harness and connect to Chrome. Chrome must be running
14
+ with remote debugging enabled. Start Chrome with:
15
+
16
+ ```
17
+ --remote-debugging-port=9222
18
+ ```
19
+
20
+ Check status with `/browser-status`. Restart the daemon with `/browser-reload-daemon`.
21
+
22
+ ## Core Workflow
23
+
24
+ ```
25
+ browser_screenshot() → browser_click(x, y) → browser_screenshot()
26
+ ```
27
+
28
+ 1. **Screenshot first** — see what's on screen before any interaction
29
+ 2. **Coordinate click** — read the target pixel from the screenshot, click at (x, y)
30
+ 3. **Screenshot again** — verify the action worked
31
+
32
+ Compositor-level clicks (`Input.dispatchMouseEvent`) work through iframes, shadow DOM,
33
+ and cross-origin content. No selectors needed.
34
+
35
+ ## Available Tools
36
+
37
+ ### Navigation
38
+ - `browser_navigate` — navigate to a URL (creates new tab on first call, reuses current tab after)
39
+ - `browser_new_tab` — open a new tab, optionally navigate (use for first navigation)
40
+ - `browser_open_urls` — open multiple URLs in parallel tabs (use after search to open result links)
41
+ - `browser_go_back` / `browser_go_forward` / `browser_reload` — history navigation
42
+ - `browser_page_info` — get URL, title, viewport, scroll position, or dialog info
43
+ - `browser_list_tabs` / `browser_current_tab` / `browser_switch_tab` — tab management
44
+
45
+ ### Interaction
46
+ - `browser_click` — click at viewport CSS-pixel coordinates
47
+ - `browser_type` — type text into focused element
48
+ - `browser_press_key` — press a key (Enter, Tab, Escape, arrows, etc.)
49
+ - `browser_scroll` — scroll the page at coordinates
50
+
51
+ ### Visual
52
+ - `browser_screenshot` — capture PNG screenshot
53
+
54
+ ### Data Extraction
55
+ - `browser_execute_js` — execute JavaScript and return result
56
+ - `browser_http_get` — direct HTTP GET outside browser (10-50x faster for APIs)
57
+
58
+ ### Utility
59
+ - `browser_wait` — wait N seconds
60
+ - `browser_wait_for_load` — wait for document.readyState === 'complete'
61
+ - `browser_handle_dialog` — accept or dismiss JS dialogs (alert/confirm/prompt)
62
+
63
+ ### Extending
64
+ - `browser_run_script` — execute a temporary script file with daemon access
65
+
66
+ ## Pattern Reference
67
+
68
+ ### Navigation
69
+ ```
70
+ browser_new_tab("https://example.com") → browser_wait_for_load() → browser_screenshot()
71
+ ```
72
+
73
+ ### Form Filling
74
+ ```
75
+ browser_screenshot() → find input coordinates
76
+ → browser_click(x, y) → browser_type("text")
77
+ → browser_press_key("Tab") → browser_screenshot()
78
+ ```
79
+
80
+ ### Data Extraction
81
+ ```
82
+ browser_execute_js("document.querySelector('.price').innerText")
83
+ // or for APIs directly:
84
+ browser_http_get("https://api.example.com/data")
85
+ ```
86
+
87
+ ### Scrolling
88
+ ```
89
+ browser_screenshot() → browser_scroll({ deltaY: -500 }) → browser_screenshot()
90
+ ```
91
+
92
+ ### Research (search engine + browser_open_urls)
93
+ ```
94
+ browser_new_tab("https://google.com/search?q=query") → search engine results
95
+ browser_open_urls({urls: ["url1", "url2"]}) → open result pages in parallel tabs
96
+ browser_list_tabs() → see all open tabs with targetIds
97
+ browser_switch_tab({targetId: "..."}) → switch to a specific tab
98
+ browser_wait_for_load() → wait for the page to render
99
+ browser_screenshot() → visually inspect the page
100
+ browser_execute_js("document.querySelector('.content').innerText") → extract text
101
+ ```
102
+
103
+ ## Temporary Scripts
104
+
105
+ When the built-in tools aren't enough, write a temporary script to disk and execute it
106
+ with `browser_run_script`. Scripts run in the harness process with direct access to
107
+ the browser daemon and full Node.js APIs — no dynamic tool registration needed.
108
+
109
+ ```
110
+ ● agent: needs to scrape 47 paginated search result pages
111
+ │
112
+ ● write("/tmp/scrape-pages.js", "...script using daemon.cdp() and daemon.evaluateJS()...")
113
+ │
114
+ ● browser_run_script("/tmp/scrape-pages.js", { urls: [...] })
115
+ │
116
+ ✓ script executes and returns results
117
+ ```
118
+
119
+ The script is written to disk, so it's auditable and re-runnable.
120
+
121
+ **When to use scripts:**
122
+ - You're about to do the same 3+ step sequence more than once
123
+ - You need a capability the built-in tools don't provide (e.g., bulk CSV export, structured extraction, pagination)
124
+ - You discover a domain-specific pattern that should be reusable
125
+ - The alternative is a fragile multi-turn loop of screenshots + clicks
126
+
127
+ **What NOT to do:**
128
+ - Don't write scripts for one-off actions — use the built-in browser_* tools directly.
129
+ - Don't try to call browser_* tools from within a script — sequence them as separate tool calls.
130
+
131
+ **Script bindings:**
132
+ - `params` — the arguments passed to browser_run_script
133
+ - `daemon` — browser daemon (daemon.cdp(), daemon.evaluateJS(), daemon.getPageInfo(), etc.)
134
+ - `require` — Node.js require() (use 'fs', 'path', 'crypto', etc.)
135
+ - `signal` — AbortSignal for cancellation
136
+ - `onUpdate` — progress callback: onUpdate({ content: [{ type: 'text', text: '...' }] })
137
+ - `ctx` — ExtensionContext (ctx.cwd, ctx.signal, etc.)
138
+ - `console`, `fetch`, `JSON`, `Buffer`, `setTimeout`, `clearTimeout`
139
+
140
+ **Example — a script that extracts structured data:**
141
+ ```javascript
142
+ // /tmp/extract-products.js
143
+ const info = await daemon.getPageInfo();
144
+ if ("dialog" in info) throw new Error("Dialog is blocking: " + info.dialog.message);
145
+ const data = await daemon.evaluateJS(`
146
+ JSON.stringify(Array.from(document.querySelectorAll('${params.rowSelector}')).map(el => ({
147
+ title: el.querySelector('${params.titleSelector}')?.textContent?.trim(),
148
+ price: el.querySelector('${params.priceSelector}')?.textContent?.trim(),
149
+ })))
150
+ `);
151
+ return { content: [{ type: 'text', text: data }], details: { raw: JSON.parse(data) } };
152
+ ```
153
+
154
+ ## What NOT To Do
155
+
156
+ - **Don't launch your own browser** — you're connected to the user's real Chrome
157
+ - **Don't type credentials** — if you hit an auth wall, stop and ask the user
158
+ - **Don't assume page state** — screenshot to confirm
159
+ - **Don't write pixel coordinates into documentation** — describe how to locate targets (selectors, text, aria-label)
160
+ - **Don't use browser_navigate for first navigation** — use browser_new_tab to explicitly preserve the user's active tab (browser_navigate auto-creates a new tab on first call, but explicit is clearer)
161
+ - **Don't ignore dialogs** — if browser_page_info returns a dialog, handle it first
162
+
163
+ ## Dialogs
164
+
165
+ JS dialogs (alert, confirm, prompt, beforeunload) freeze the page's JS thread.
166
+ If browser_page_info returns a dialog, handle it with browser_handle_dialog
167
+ before any other browser action.
168
+
169
+ ```
170
+ // Check page_info first — if it shows a dialog:
171
+ browser_handle_dialog({ action: "accept" }) // accept/confirm
172
+ browser_handle_dialog({ action: "dismiss" }) // dismiss/cancel
173
+ browser_handle_dialog({ action: "accept", promptText: "hello" }) // prompt with text
174
+ ```
175
+
176
+ ## SPAs (React, Vue, etc.)
177
+
178
+ `browser_wait_for_load()` only checks `document.readyState === "complete"`.
179
+ Single-page apps often paint content after this. After wait_for_load, use
180
+ `browser_execute_js` to check for specific elements:
181
+
182
+ ```
183
+ browser_execute_js("!!document.querySelector('.loaded-content')")
184
+ ```
185
+
186
+ ## Keyboard Modifiers
187
+
188
+ `browser_press_key` supports modifier bitfield:
189
+ - 1 = Alt, 2 = Ctrl, 4 = Meta/Cmd, 8 = Shift
190
+ - Combine: Ctrl+Shift = 2|8 = 10, Cmd+Shift = 4|8 = 12
191
+
192
+ ## Troubleshooting
193
+
194
+ - **Daemon not starting**: Run `/browser-setup` to re-run the guided setup
195
+ - **"DevToolsActivePort not found"**: Open chrome://inspect/#remote-debugging, tick checkbox, click Allow
196
+ - **Stale session**: Run `/browser-reload-daemon` to restart the daemon
197
+ - **Dialog blocking**: Run browser_page_info to detect it, then browser_handle_dialog
198
+ - **Status check**: Run `/browser-status` to see daemon health and current page
199
+
200
+ ## Architecture
201
+
202
+ ```
203
+ pi agent → pi-browser-harness (TypeScript)
204
+ │ CDP WebSocket
205
+ ▼
206
+ Chrome
207
+
208
+ Temporary scripts:
209
+ write("/tmp/script.js") → browser_run_script("/tmp/script.js")
210
+ │ │
211
+ ▼ ▼
212
+ script on disk (auditable) executed in harness process with daemon access
213
+ ```