mercury-agent 0.5.0 → 0.5.2

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.
Files changed (125) hide show
  1. package/README.md +452 -451
  2. package/container/Dockerfile +127 -127
  3. package/container/Dockerfile.base +109 -109
  4. package/container/Dockerfile.power +17 -17
  5. package/container/agent-package.json +8 -8
  6. package/container/build.sh +54 -54
  7. package/docs/ROADMAP.md +3 -4
  8. package/docs/archive/summarization/2026-07-02-recent-dev-summary.md +71 -0
  9. package/docs/auth/dashboard.md +28 -28
  10. package/docs/auth/overview.md +109 -109
  11. package/docs/auth/whatsapp.md +173 -173
  12. package/docs/authoring-profiles.md +174 -174
  13. package/docs/configuration.md +54 -54
  14. package/docs/container-lifecycle.md +349 -349
  15. package/docs/{bugs/bwrap-privileged-linux-docker.md → debug/major/2026-07-02-bwrap-privileged-linux-docker.md} +21 -1
  16. package/docs/{bugs/e2big-prompt-delivery-fix.md → debug/major/2026-07-02-e2big-prompt-delivery-fix.md} +27 -1
  17. package/docs/{bugs/registry-pull-no-local-fallback.md → debug/major/2026-07-02-registry-pull-no-local-fallback.md} +27 -1
  18. package/docs/debug/summarization/2026-07-02-common-bug-patterns.md +72 -0
  19. package/docs/deployment.md +199 -199
  20. package/docs/extensions.md +375 -375
  21. package/docs/graceful-shutdown.md +62 -62
  22. package/docs/kb-distillation.md +77 -77
  23. package/docs/media/overview.md +140 -140
  24. package/docs/media/whatsapp.md +171 -171
  25. package/docs/memory.md +137 -137
  26. package/docs/permissions.md +217 -217
  27. package/docs/pipeline.md +228 -228
  28. package/docs/prd-chat-memory.md +76 -76
  29. package/docs/prd-config-load.md +82 -82
  30. package/docs/rate-limiting.md +229 -229
  31. package/docs/runbooks/publish-checklist.md +9 -1
  32. package/docs/scheduler.md +288 -288
  33. package/docs/setup-discord.md +100 -100
  34. package/docs/setup-slack.md +119 -119
  35. package/docs/setup-whatsapp.md +94 -94
  36. package/docs/subagents.md +166 -166
  37. package/docs/web-search.md +62 -62
  38. package/examples/extensions/README.md +12 -12
  39. package/examples/extensions/charts/index.ts +13 -13
  40. package/examples/extensions/charts/skill/SKILL.md +98 -98
  41. package/examples/extensions/gws/README.md +52 -52
  42. package/examples/extensions/gws/skill/SKILL.md +57 -57
  43. package/examples/extensions/gws/skill/references/calendar.md +101 -101
  44. package/examples/extensions/gws/skill/references/docs.md +65 -65
  45. package/examples/extensions/gws/skill/references/drive.md +79 -79
  46. package/examples/extensions/gws/skill/references/gmail.md +85 -85
  47. package/examples/extensions/gws/skill/references/sheets.md +60 -60
  48. package/examples/extensions/napkin/skill/SKILL.md +728 -728
  49. package/examples/extensions/pdf/skill/LICENSE.txt +30 -30
  50. package/examples/extensions/pdf/skill/SKILL.md +314 -314
  51. package/examples/extensions/pdf/skill/forms.md +294 -294
  52. package/examples/extensions/pdf/skill/reference.md +611 -611
  53. package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -65
  54. package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -11
  55. package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -33
  56. package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -37
  57. package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -122
  58. package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -115
  59. package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -98
  60. package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -107
  61. package/examples/extensions/permission-guard/index.ts +65 -65
  62. package/examples/extensions/pinchtab/skill/SKILL.md +224 -224
  63. package/examples/extensions/pinchtab/skill/TRUST.md +69 -69
  64. package/examples/extensions/pinchtab/skill/references/api.md +297 -297
  65. package/examples/extensions/pinchtab/skill/references/env.md +45 -45
  66. package/examples/extensions/pinchtab/skill/references/profiles.md +107 -107
  67. package/examples/extensions/tradestation/host/refresh.ts +102 -102
  68. package/examples/extensions/tradestation/index.ts +153 -153
  69. package/examples/extensions/tradestation/skill/SKILL.md +67 -67
  70. package/examples/extensions/voice-synth/index.ts +94 -94
  71. package/examples/extensions/voice-synth/skill/SKILL.md +38 -38
  72. package/examples/extensions/voice-transcribe/requirements.txt +8 -8
  73. package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -179
  74. package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -53
  75. package/examples/extensions/yahoo-mail/cli/package.json +13 -13
  76. package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -78
  77. package/package.json +106 -106
  78. package/resources/agents/explore.md +50 -50
  79. package/resources/agents/worker.md +24 -24
  80. package/resources/connection-env-vars.json +25 -25
  81. package/resources/pi-extensions/subagent/agents.ts +126 -126
  82. package/resources/pi-extensions/subagent/index.ts +964 -964
  83. package/resources/profiles/coding/AGENTS.md +43 -43
  84. package/resources/profiles/coding/mercury-profile.yaml +15 -15
  85. package/resources/profiles/general/AGENTS.md +31 -31
  86. package/resources/profiles/general/mercury-profile.yaml +15 -15
  87. package/resources/profiles/research/AGENTS.md +40 -40
  88. package/resources/profiles/research/mercury-profile.yaml +15 -15
  89. package/resources/skills/config/SKILL.md +25 -25
  90. package/resources/skills/context/SKILL.md +33 -33
  91. package/resources/skills/conversation-recap/SKILL.md +19 -19
  92. package/resources/skills/mutes/SKILL.md +31 -31
  93. package/resources/skills/permissions/SKILL.md +19 -19
  94. package/resources/skills/preferences/SKILL.md +31 -31
  95. package/resources/skills/recall/SKILL.md +24 -24
  96. package/resources/skills/roles/SKILL.md +18 -18
  97. package/resources/skills/spaces/SKILL.md +18 -18
  98. package/resources/skills/tasks/SKILL.md +45 -45
  99. package/resources/templates/AGENTS.md +157 -157
  100. package/resources/templates/env.template +38 -38
  101. package/resources/templates/mercury.example.yaml +99 -99
  102. package/src/agent/container-entry.ts +1 -1
  103. package/src/agent/container-runner.ts +1345 -1346
  104. package/src/cli/mercury.ts +39 -7
  105. package/src/cli/mrctl.ts +636 -636
  106. package/src/config-file.ts +540 -540
  107. package/src/config.ts +339 -339
  108. package/src/core/api.ts +125 -125
  109. package/src/core/caller-token.ts +101 -101
  110. package/src/core/permissions.ts +228 -228
  111. package/src/core/profiles.ts +271 -271
  112. package/src/core/routes/capability.ts +70 -70
  113. package/src/core/routes/index.ts +15 -15
  114. package/src/core/runtime.ts +1530 -1530
  115. package/src/dashboard/index.html +729 -729
  116. package/src/extensions/api.ts +273 -273
  117. package/src/extensions/loader.ts +286 -286
  118. package/src/extensions/types.ts +517 -517
  119. package/src/main.ts +605 -605
  120. package/docs/pending-updates/applicative-profiles.md +0 -15
  121. package/docs/pending-updates/caller-bound-capability-token.md +0 -15
  122. package/docs/pending-updates/dm-auto-space.md +0 -15
  123. /package/docs/archive/{2026-07-01-applicative-profiles.md → applicative-profiles/2026-07-01-applicative-profiles.md} +0 -0
  124. /package/docs/archive/{2026-07-01-caller-bound-capability-token.md → applicative-profiles/2026-07-01-caller-bound-capability-token.md} +0 -0
  125. /package/docs/archive/{2026-07-01-dm-auto-space.md → applicative-profiles/2026-07-01-dm-auto-space.md} +0 -0
@@ -1,65 +1,65 @@
1
- /**
2
- * Mercury Permission Guard — pi extension
3
- *
4
- * Prevents the agent from bypassing RBAC by calling extension CLIs
5
- * directly in bash. Forces all extension CLI calls through `mrctl`,
6
- * which checks permissions via the Mercury API.
7
- *
8
- * Environment variables:
9
- * MERCURY_EXT_CLIS — comma-separated list of extension CLI names
10
- * e.g. "pinchtab,napkin,charts,pdf"
11
- *
12
- * Without this extension, the agent can call `pinchtab search foo`
13
- * directly in bash, bypassing the permission check that `mrctl pinchtab
14
- * search foo` would enforce.
15
- *
16
- * Behavior: intercepts bash tool calls that invoke an extension CLI
17
- * directly and blocks them with a message to use `mrctl` instead.
18
- */
19
-
20
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
21
-
22
- export default function (pi: ExtensionAPI) {
23
- const extClisEnv = process.env.MERCURY_EXT_CLIS;
24
- if (!extClisEnv) return;
25
-
26
- const extClis = extClisEnv
27
- .split(",")
28
- .map((s) => s.trim())
29
- .filter(Boolean);
30
-
31
- if (extClis.length === 0) return;
32
-
33
- // Match CLI name as the first command word, or after shell operators
34
- // Covers: `pinchtab args`, `cd /tmp && pinchtab args`, `pinchtab &`
35
- const cliPatterns = extClis.map((name) => ({
36
- name,
37
- pattern: new RegExp(
38
- `(?:^|&&|\\|\\||;|\\|)\\s*${escapeRegex(name)}(?:\\s|$|&)`,
39
- ),
40
- }));
41
-
42
- pi.on("tool_call", async (event) => {
43
- if (event.toolName !== "bash") return undefined;
44
-
45
- const command = (event.input.command as string).trim();
46
-
47
- // Don't block if already going through mrctl
48
- if (/(?:^|&&|\|\||;|\|)\s*mrctl\s/.test(command)) return undefined;
49
-
50
- for (const { name, pattern } of cliPatterns) {
51
- if (pattern.test(command)) {
52
- return {
53
- block: true,
54
- reason: `Direct "${name}" calls are not allowed. Use "mrctl ${name} ..." instead, which enforces permissions.`,
55
- };
56
- }
57
- }
58
-
59
- return undefined;
60
- });
61
- }
62
-
63
- function escapeRegex(s: string): string {
64
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65
- }
1
+ /**
2
+ * Mercury Permission Guard — pi extension
3
+ *
4
+ * Prevents the agent from bypassing RBAC by calling extension CLIs
5
+ * directly in bash. Forces all extension CLI calls through `mrctl`,
6
+ * which checks permissions via the Mercury API.
7
+ *
8
+ * Environment variables:
9
+ * MERCURY_EXT_CLIS — comma-separated list of extension CLI names
10
+ * e.g. "pinchtab,napkin,charts,pdf"
11
+ *
12
+ * Without this extension, the agent can call `pinchtab search foo`
13
+ * directly in bash, bypassing the permission check that `mrctl pinchtab
14
+ * search foo` would enforce.
15
+ *
16
+ * Behavior: intercepts bash tool calls that invoke an extension CLI
17
+ * directly and blocks them with a message to use `mrctl` instead.
18
+ */
19
+
20
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
21
+
22
+ export default function (pi: ExtensionAPI) {
23
+ const extClisEnv = process.env.MERCURY_EXT_CLIS;
24
+ if (!extClisEnv) return;
25
+
26
+ const extClis = extClisEnv
27
+ .split(",")
28
+ .map((s) => s.trim())
29
+ .filter(Boolean);
30
+
31
+ if (extClis.length === 0) return;
32
+
33
+ // Match CLI name as the first command word, or after shell operators
34
+ // Covers: `pinchtab args`, `cd /tmp && pinchtab args`, `pinchtab &`
35
+ const cliPatterns = extClis.map((name) => ({
36
+ name,
37
+ pattern: new RegExp(
38
+ `(?:^|&&|\\|\\||;|\\|)\\s*${escapeRegex(name)}(?:\\s|$|&)`,
39
+ ),
40
+ }));
41
+
42
+ pi.on("tool_call", async (event) => {
43
+ if (event.toolName !== "bash") return undefined;
44
+
45
+ const command = (event.input.command as string).trim();
46
+
47
+ // Don't block if already going through mrctl
48
+ if (/(?:^|&&|\|\||;|\|)\s*mrctl\s/.test(command)) return undefined;
49
+
50
+ for (const { name, pattern } of cliPatterns) {
51
+ if (pattern.test(command)) {
52
+ return {
53
+ block: true,
54
+ reason: `Direct "${name}" calls are not allowed. Use "mrctl ${name} ..." instead, which enforces permissions.`,
55
+ };
56
+ }
57
+ }
58
+
59
+ return undefined;
60
+ });
61
+ }
62
+
63
+ function escapeRegex(s: string): string {
64
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65
+ }
@@ -1,224 +1,224 @@
1
- ---
2
- name: pinchtab
3
- description: Control a headless or headed Chrome browser via Pinchtab's HTTP API for web automation, scraping, form filling, navigation, screenshots, and extraction with stable accessibility refs.
4
- metadata:
5
- short-description: Browser automation via Pinchtab HTTP API
6
- ---
7
-
8
- # Pinchtab
9
-
10
- Fast, lightweight browser control for AI agents via HTTP + accessibility tree.
11
-
12
- **Security Note:** Pinchtab runs entirely locally. It does not contact external services, send telemetry, or exfiltrate data. However, it controls a real Chrome instance — if pointed at a profile with saved logins, agents can access authenticated sites. Always use a dedicated empty profile and set BRIDGE_TOKEN when exposing the API. See [TRUST.md](TRUST.md) for the full security model.
13
-
14
- ## Quick Start (Agent Workflow)
15
-
16
- The 30-second pattern for browser tasks:
17
-
18
- ```bash
19
- # 1. Start Pinchtab (runs forever, local on :9867)
20
- pinchtab &
21
-
22
- # 2. In your agent, follow this loop:
23
- # a) Navigate to a URL
24
- # b) Snapshot the page (get refs like e0, e5, e12)
25
- # c) Act on a ref (click e5, type e12 "search text")
26
- # d) Snapshot again to see the result
27
- # e) Repeat step c-d until done
28
- ```
29
-
30
- **That's it.** Refs are stable—you don't need to re-snapshot before every action. Only snapshot when the page changes significantly.
31
-
32
- ## Mercury / Docker (required)
33
-
34
- In the Mercury agent container, `pinchtab &` plus a short `sleep` often races the HTTP bridge: the CLI then hits `127.0.0.1:9867` before the daemon listens (`connection refused`). The host injects `CHROME_BINARY` and `CHROME_FLAGS` (`--no-sandbox` as root). **Always** wait until the port is open and capture daemon logs.
35
-
36
- ```bash
37
- pinchtab_ensure() {
38
- local bind="${BRIDGE_BIND:-127.0.0.1}"
39
- local port="${BRIDGE_PORT:-9867}"
40
- local log="${PINCHTAB_LOG:-/tmp/pinchtab.log}"
41
- local max_wait="${1:-120}"
42
- mkdir -p "$(dirname "$log")" 2>/dev/null || true
43
- : >"$log"
44
- if [ ! -x "${CHROME_BINARY:-}" ]; then
45
- for _c in /usr/local/bin/chromium /usr/bin/chromium; do
46
- if [ -x "$_c" ]; then export CHROME_BINARY="$_c"; break; fi
47
- done
48
- fi
49
- if [ ! -x "${CHROME_BINARY:-}" ]; then
50
- echo "No executable Chromium (CHROME_BINARY=${CHROME_BINARY:-}; tried /usr/local/bin/chromium, /usr/bin/chromium). Rebuild mercury-agent-ext (restart Mercury)." | tee -a "$log"
51
- return 1
52
- fi
53
- _pinchtab_port_open() { (echo >/dev/tcp/$bind/$port) 2>/dev/null; }
54
- if command -v pinchtab >/dev/null 2>&1 && _pinchtab_port_open; then
55
- return 0
56
- fi
57
- pkill -f '[p]inchtab' 2>/dev/null || true
58
- nohup pinchtab >>"$log" 2>&1 &
59
- local pid=$!
60
- sleep 2
61
- if ! kill -0 "$pid" 2>/dev/null; then
62
- echo "pinchtab exited immediately (pid $pid). Log:" >&2
63
- tail -120 "$log" >&2
64
- return 1
65
- fi
66
- local i=0
67
- while [ "$i" -lt "$max_wait" ]; do
68
- if _pinchtab_port_open; then
69
- return 0
70
- fi
71
- if ! kill -0 "$pid" 2>/dev/null; then
72
- echo "pinchtab died during startup. Log:" >&2
73
- tail -120 "$log" >&2
74
- return 1
75
- fi
76
- sleep 1
77
- i=$((i+1))
78
- done
79
- echo "pinchtab did not listen on $bind:$port within ${max_wait}s. Log:" >&2
80
- tail -120 "$log" >&2
81
- return 1
82
- }
83
- ```
84
-
85
- Use it before every navigation/snapshot/text workflow:
86
-
87
- ```bash
88
- pinchtab_ensure || { echo "pinchtab failed — see /tmp/pinchtab.log"; exit 1; }
89
- pinchtab nav "https://example.com"
90
- sleep 3
91
- pinchtab text
92
- ```
93
-
94
- If `pinchtab_ensure` fails, show the user the tail of `/tmp/pinchtab.log`; do not only increase `sleep` and retry blindly.
95
-
96
- ### Recommended Secure Setup
97
-
98
- ```bash
99
- # Best practice for AI agents
100
- BRIDGE_BIND=127.0.0.1 \
101
- BRIDGE_TOKEN="your-strong-secret" \
102
- BRIDGE_PROFILE=~/.pinchtab/automation-profile \
103
- pinchtab &
104
- ```
105
-
106
- **Never expose to 0.0.0.0 without a token. Never point at your daily Chrome profile.**
107
-
108
- ## Setup
109
-
110
- ```bash
111
- # Headless (default) — no visible window
112
- pinchtab &
113
-
114
- # Headed — visible Chrome window for human debugging
115
- BRIDGE_HEADLESS=false pinchtab &
116
-
117
- # With auth token
118
- BRIDGE_TOKEN="your-secret-token" pinchtab &
119
-
120
- # Custom port
121
- BRIDGE_PORT=8080 pinchtab &
122
- ```
123
-
124
- Default: **port 9867**, no auth required (local). Set `BRIDGE_TOKEN` for remote access.
125
-
126
- For advanced setup, see [references/profiles.md](references/profiles.md) and [references/env.md](references/env.md).
127
-
128
- ## What a Snapshot Looks Like
129
-
130
- After calling `/snapshot`, you get the page's accessibility tree as JSON—flat list of elements with refs:
131
-
132
- ```json
133
- {
134
- "refs": [
135
- {"id": "e0", "role": "link", "text": "Sign In", "selector": "a[href='/login']"},
136
- {"id": "e1", "role": "textbox", "label": "Email", "selector": "input[name='email']"},
137
- {"id": "e2", "role": "button", "text": "Submit", "selector": "button[type='submit']"}
138
- ],
139
- "text": "... readable text version of page ...",
140
- "title": "Login Page"
141
- }
142
- ```
143
-
144
- Then you act on refs: `click e0`, `type e1 "user@example.com"`, `press e2 Enter`.
145
-
146
- ## Core Workflow
147
-
148
- The typical agent loop:
149
-
150
- 1. **Navigate** to a URL
151
- 2. **Snapshot** the accessibility tree (get refs)
152
- 3. **Act** on refs (click, type, press)
153
- 4. **Snapshot** again to see results
154
-
155
- Refs (e.g. `e0`, `e5`, `e12`) are cached per tab after each snapshot — no need to re-snapshot before every action unless the page changed significantly.
156
-
157
- ### Quick examples
158
-
159
- ```bash
160
- pinchtab nav https://example.com
161
- pinchtab snap -i -c # interactive + compact
162
- pinchtab click e5
163
- pinchtab type e12 hello world
164
- pinchtab press Enter
165
- pinchtab text # readable text (~1K tokens)
166
- pinchtab text | jq .text # pipe to jq
167
- pinchtab ss -o page.jpg # screenshot
168
- pinchtab eval "document.title" # run JavaScript
169
- pinchtab pdf --tab TAB_ID -o page.pdf # export PDF
170
- ```
171
-
172
- For the full HTTP API (curl examples, download, upload, cookies, stealth, batch actions, PDF export with full parameter control), see [references/api.md](references/api.md).
173
-
174
- ## Token Cost Guide
175
-
176
- | Method | Typical tokens | When to use |
177
- |---|---|---|
178
- | `/text` | ~800 | Reading page content |
179
- | `/snapshot?filter=interactive` | ~3,600 | Finding buttons/links to click |
180
- | `/snapshot?diff=true` | varies | Multi-step workflows (only changes) |
181
- | `/snapshot?format=compact` | ~56-64% less | One-line-per-node, best efficiency |
182
- | `/snapshot` | ~10,500 | Full page understanding |
183
- | `/screenshot` | ~2K (vision) | Visual verification |
184
- | `/tabs/{id}/pdf` | 0 (binary) | Export page as PDF (no token cost) |
185
-
186
- **Strategy**: Start with `?filter=interactive&format=compact`. Use `?diff=true` on subsequent snapshots. Use `/text` when you only need readable content. Full `/snapshot` only when needed.
187
-
188
- ## Agent Optimization
189
-
190
- **Validated Feb 2026**: Testing with AI agents revealed a critical pattern for reliable, token-efficient scraping.
191
-
192
- **See the full guide:** [docs/agent-optimization.md](../../docs/agent-optimization.md)
193
-
194
- ### Quick Summary
195
-
196
- **The 3-second pattern** — wait after navigate before snapshot:
197
-
198
- ```bash
199
- curl -X POST http://localhost:9867/navigate \
200
- -H "Content-Type: application/json" \
201
- -d '{"url": "https://example.com"}' && \
202
- sleep 3 && \
203
- curl http://localhost:9867/snapshot | jq '.nodes[] | select(.name | length > 15) | .name'
204
- ```
205
-
206
- **Token savings:** 93% reduction (3,842 → 272 tokens) when using prescriptive instructions vs. exploratory agent approach.
207
-
208
- For detailed findings, system prompt templates, and site-specific notes, see [docs/agent-optimization.md](../../docs/agent-optimization.md).
209
-
210
- ## Tips
211
-
212
- - **Always pass `tabId` explicitly** when working with multiple tabs
213
- - Refs are stable between snapshot and actions — no need to re-snapshot before clicking
214
- - After navigation or major page changes, take a new snapshot for fresh refs
215
- - Pinchtab persists sessions — tabs survive restarts (disable with `BRIDGE_NO_RESTORE=true`)
216
- - Chrome profile is persistent — cookies/logins carry over between runs
217
- - Use `BRIDGE_BLOCK_IMAGES=true` or `"blockImages": true` on navigate for read-heavy tasks
218
- - **Wait 3+ seconds after navigate before snapshot** — Chrome needs time to render 2000+ accessibility tree nodes
219
-
220
- ## Authenticated Browser Sessions
221
-
222
- If the user has saved a browser session for a site (via the Browser Sessions page in the console), the agent will automatically use it when navigating to that domain. No special instructions are needed — just navigate to the URL normally. The session (cookies + localStorage) is pre-loaded into the container environment and injected transparently before the first page load on the matched domain.
223
-
224
- Sites behind login walls (banks, airlines, HR portals, niche SaaS) can be accessed this way without any copy-pasting or API key setup. If a session seems stale or the site still shows a login screen, the user can re-capture the session from the Browser Sessions page.
1
+ ---
2
+ name: pinchtab
3
+ description: Control a headless or headed Chrome browser via Pinchtab's HTTP API for web automation, scraping, form filling, navigation, screenshots, and extraction with stable accessibility refs.
4
+ metadata:
5
+ short-description: Browser automation via Pinchtab HTTP API
6
+ ---
7
+
8
+ # Pinchtab
9
+
10
+ Fast, lightweight browser control for AI agents via HTTP + accessibility tree.
11
+
12
+ **Security Note:** Pinchtab runs entirely locally. It does not contact external services, send telemetry, or exfiltrate data. However, it controls a real Chrome instance — if pointed at a profile with saved logins, agents can access authenticated sites. Always use a dedicated empty profile and set BRIDGE_TOKEN when exposing the API. See [TRUST.md](TRUST.md) for the full security model.
13
+
14
+ ## Quick Start (Agent Workflow)
15
+
16
+ The 30-second pattern for browser tasks:
17
+
18
+ ```bash
19
+ # 1. Start Pinchtab (runs forever, local on :9867)
20
+ pinchtab &
21
+
22
+ # 2. In your agent, follow this loop:
23
+ # a) Navigate to a URL
24
+ # b) Snapshot the page (get refs like e0, e5, e12)
25
+ # c) Act on a ref (click e5, type e12 "search text")
26
+ # d) Snapshot again to see the result
27
+ # e) Repeat step c-d until done
28
+ ```
29
+
30
+ **That's it.** Refs are stable—you don't need to re-snapshot before every action. Only snapshot when the page changes significantly.
31
+
32
+ ## Mercury / Docker (required)
33
+
34
+ In the Mercury agent container, `pinchtab &` plus a short `sleep` often races the HTTP bridge: the CLI then hits `127.0.0.1:9867` before the daemon listens (`connection refused`). The host injects `CHROME_BINARY` and `CHROME_FLAGS` (`--no-sandbox` as root). **Always** wait until the port is open and capture daemon logs.
35
+
36
+ ```bash
37
+ pinchtab_ensure() {
38
+ local bind="${BRIDGE_BIND:-127.0.0.1}"
39
+ local port="${BRIDGE_PORT:-9867}"
40
+ local log="${PINCHTAB_LOG:-/tmp/pinchtab.log}"
41
+ local max_wait="${1:-120}"
42
+ mkdir -p "$(dirname "$log")" 2>/dev/null || true
43
+ : >"$log"
44
+ if [ ! -x "${CHROME_BINARY:-}" ]; then
45
+ for _c in /usr/local/bin/chromium /usr/bin/chromium; do
46
+ if [ -x "$_c" ]; then export CHROME_BINARY="$_c"; break; fi
47
+ done
48
+ fi
49
+ if [ ! -x "${CHROME_BINARY:-}" ]; then
50
+ echo "No executable Chromium (CHROME_BINARY=${CHROME_BINARY:-}; tried /usr/local/bin/chromium, /usr/bin/chromium). Rebuild mercury-agent-ext (restart Mercury)." | tee -a "$log"
51
+ return 1
52
+ fi
53
+ _pinchtab_port_open() { (echo >/dev/tcp/$bind/$port) 2>/dev/null; }
54
+ if command -v pinchtab >/dev/null 2>&1 && _pinchtab_port_open; then
55
+ return 0
56
+ fi
57
+ pkill -f '[p]inchtab' 2>/dev/null || true
58
+ nohup pinchtab >>"$log" 2>&1 &
59
+ local pid=$!
60
+ sleep 2
61
+ if ! kill -0 "$pid" 2>/dev/null; then
62
+ echo "pinchtab exited immediately (pid $pid). Log:" >&2
63
+ tail -120 "$log" >&2
64
+ return 1
65
+ fi
66
+ local i=0
67
+ while [ "$i" -lt "$max_wait" ]; do
68
+ if _pinchtab_port_open; then
69
+ return 0
70
+ fi
71
+ if ! kill -0 "$pid" 2>/dev/null; then
72
+ echo "pinchtab died during startup. Log:" >&2
73
+ tail -120 "$log" >&2
74
+ return 1
75
+ fi
76
+ sleep 1
77
+ i=$((i+1))
78
+ done
79
+ echo "pinchtab did not listen on $bind:$port within ${max_wait}s. Log:" >&2
80
+ tail -120 "$log" >&2
81
+ return 1
82
+ }
83
+ ```
84
+
85
+ Use it before every navigation/snapshot/text workflow:
86
+
87
+ ```bash
88
+ pinchtab_ensure || { echo "pinchtab failed — see /tmp/pinchtab.log"; exit 1; }
89
+ pinchtab nav "https://example.com"
90
+ sleep 3
91
+ pinchtab text
92
+ ```
93
+
94
+ If `pinchtab_ensure` fails, show the user the tail of `/tmp/pinchtab.log`; do not only increase `sleep` and retry blindly.
95
+
96
+ ### Recommended Secure Setup
97
+
98
+ ```bash
99
+ # Best practice for AI agents
100
+ BRIDGE_BIND=127.0.0.1 \
101
+ BRIDGE_TOKEN="your-strong-secret" \
102
+ BRIDGE_PROFILE=~/.pinchtab/automation-profile \
103
+ pinchtab &
104
+ ```
105
+
106
+ **Never expose to 0.0.0.0 without a token. Never point at your daily Chrome profile.**
107
+
108
+ ## Setup
109
+
110
+ ```bash
111
+ # Headless (default) — no visible window
112
+ pinchtab &
113
+
114
+ # Headed — visible Chrome window for human debugging
115
+ BRIDGE_HEADLESS=false pinchtab &
116
+
117
+ # With auth token
118
+ BRIDGE_TOKEN="your-secret-token" pinchtab &
119
+
120
+ # Custom port
121
+ BRIDGE_PORT=8080 pinchtab &
122
+ ```
123
+
124
+ Default: **port 9867**, no auth required (local). Set `BRIDGE_TOKEN` for remote access.
125
+
126
+ For advanced setup, see [references/profiles.md](references/profiles.md) and [references/env.md](references/env.md).
127
+
128
+ ## What a Snapshot Looks Like
129
+
130
+ After calling `/snapshot`, you get the page's accessibility tree as JSON—flat list of elements with refs:
131
+
132
+ ```json
133
+ {
134
+ "refs": [
135
+ {"id": "e0", "role": "link", "text": "Sign In", "selector": "a[href='/login']"},
136
+ {"id": "e1", "role": "textbox", "label": "Email", "selector": "input[name='email']"},
137
+ {"id": "e2", "role": "button", "text": "Submit", "selector": "button[type='submit']"}
138
+ ],
139
+ "text": "... readable text version of page ...",
140
+ "title": "Login Page"
141
+ }
142
+ ```
143
+
144
+ Then you act on refs: `click e0`, `type e1 "user@example.com"`, `press e2 Enter`.
145
+
146
+ ## Core Workflow
147
+
148
+ The typical agent loop:
149
+
150
+ 1. **Navigate** to a URL
151
+ 2. **Snapshot** the accessibility tree (get refs)
152
+ 3. **Act** on refs (click, type, press)
153
+ 4. **Snapshot** again to see results
154
+
155
+ Refs (e.g. `e0`, `e5`, `e12`) are cached per tab after each snapshot — no need to re-snapshot before every action unless the page changed significantly.
156
+
157
+ ### Quick examples
158
+
159
+ ```bash
160
+ pinchtab nav https://example.com
161
+ pinchtab snap -i -c # interactive + compact
162
+ pinchtab click e5
163
+ pinchtab type e12 hello world
164
+ pinchtab press Enter
165
+ pinchtab text # readable text (~1K tokens)
166
+ pinchtab text | jq .text # pipe to jq
167
+ pinchtab ss -o page.jpg # screenshot
168
+ pinchtab eval "document.title" # run JavaScript
169
+ pinchtab pdf --tab TAB_ID -o page.pdf # export PDF
170
+ ```
171
+
172
+ For the full HTTP API (curl examples, download, upload, cookies, stealth, batch actions, PDF export with full parameter control), see [references/api.md](references/api.md).
173
+
174
+ ## Token Cost Guide
175
+
176
+ | Method | Typical tokens | When to use |
177
+ |---|---|---|
178
+ | `/text` | ~800 | Reading page content |
179
+ | `/snapshot?filter=interactive` | ~3,600 | Finding buttons/links to click |
180
+ | `/snapshot?diff=true` | varies | Multi-step workflows (only changes) |
181
+ | `/snapshot?format=compact` | ~56-64% less | One-line-per-node, best efficiency |
182
+ | `/snapshot` | ~10,500 | Full page understanding |
183
+ | `/screenshot` | ~2K (vision) | Visual verification |
184
+ | `/tabs/{id}/pdf` | 0 (binary) | Export page as PDF (no token cost) |
185
+
186
+ **Strategy**: Start with `?filter=interactive&format=compact`. Use `?diff=true` on subsequent snapshots. Use `/text` when you only need readable content. Full `/snapshot` only when needed.
187
+
188
+ ## Agent Optimization
189
+
190
+ **Validated Feb 2026**: Testing with AI agents revealed a critical pattern for reliable, token-efficient scraping.
191
+
192
+ **See the full guide:** [docs/agent-optimization.md](../../docs/agent-optimization.md)
193
+
194
+ ### Quick Summary
195
+
196
+ **The 3-second pattern** — wait after navigate before snapshot:
197
+
198
+ ```bash
199
+ curl -X POST http://localhost:9867/navigate \
200
+ -H "Content-Type: application/json" \
201
+ -d '{"url": "https://example.com"}' && \
202
+ sleep 3 && \
203
+ curl http://localhost:9867/snapshot | jq '.nodes[] | select(.name | length > 15) | .name'
204
+ ```
205
+
206
+ **Token savings:** 93% reduction (3,842 → 272 tokens) when using prescriptive instructions vs. exploratory agent approach.
207
+
208
+ For detailed findings, system prompt templates, and site-specific notes, see [docs/agent-optimization.md](../../docs/agent-optimization.md).
209
+
210
+ ## Tips
211
+
212
+ - **Always pass `tabId` explicitly** when working with multiple tabs
213
+ - Refs are stable between snapshot and actions — no need to re-snapshot before clicking
214
+ - After navigation or major page changes, take a new snapshot for fresh refs
215
+ - Pinchtab persists sessions — tabs survive restarts (disable with `BRIDGE_NO_RESTORE=true`)
216
+ - Chrome profile is persistent — cookies/logins carry over between runs
217
+ - Use `BRIDGE_BLOCK_IMAGES=true` or `"blockImages": true` on navigate for read-heavy tasks
218
+ - **Wait 3+ seconds after navigate before snapshot** — Chrome needs time to render 2000+ accessibility tree nodes
219
+
220
+ ## Authenticated Browser Sessions
221
+
222
+ If the user has saved a browser session for a site (via the Browser Sessions page in the console), the agent will automatically use it when navigating to that domain. No special instructions are needed — just navigate to the URL normally. The session (cookies + localStorage) is pre-loaded into the container environment and injected transparently before the first page load on the matched domain.
223
+
224
+ Sites behind login walls (banks, airlines, HR portals, niche SaaS) can be accessed this way without any copy-pasting or API key setup. If a session seems stale or the site still shows a login screen, the user can re-capture the session from the Browser Sessions page.