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.
- package/README.md +452 -451
- package/container/Dockerfile +127 -127
- package/container/Dockerfile.base +109 -109
- package/container/Dockerfile.power +17 -17
- package/container/agent-package.json +8 -8
- package/container/build.sh +54 -54
- package/docs/ROADMAP.md +3 -4
- package/docs/archive/summarization/2026-07-02-recent-dev-summary.md +71 -0
- package/docs/auth/dashboard.md +28 -28
- package/docs/auth/overview.md +109 -109
- package/docs/auth/whatsapp.md +173 -173
- package/docs/authoring-profiles.md +174 -174
- package/docs/configuration.md +54 -54
- package/docs/container-lifecycle.md +349 -349
- package/docs/{bugs/bwrap-privileged-linux-docker.md → debug/major/2026-07-02-bwrap-privileged-linux-docker.md} +21 -1
- package/docs/{bugs/e2big-prompt-delivery-fix.md → debug/major/2026-07-02-e2big-prompt-delivery-fix.md} +27 -1
- package/docs/{bugs/registry-pull-no-local-fallback.md → debug/major/2026-07-02-registry-pull-no-local-fallback.md} +27 -1
- package/docs/debug/summarization/2026-07-02-common-bug-patterns.md +72 -0
- package/docs/deployment.md +199 -199
- package/docs/extensions.md +375 -375
- package/docs/graceful-shutdown.md +62 -62
- package/docs/kb-distillation.md +77 -77
- package/docs/media/overview.md +140 -140
- package/docs/media/whatsapp.md +171 -171
- package/docs/memory.md +137 -137
- package/docs/permissions.md +217 -217
- package/docs/pipeline.md +228 -228
- package/docs/prd-chat-memory.md +76 -76
- package/docs/prd-config-load.md +82 -82
- package/docs/rate-limiting.md +229 -229
- package/docs/runbooks/publish-checklist.md +9 -1
- package/docs/scheduler.md +288 -288
- package/docs/setup-discord.md +100 -100
- package/docs/setup-slack.md +119 -119
- package/docs/setup-whatsapp.md +94 -94
- package/docs/subagents.md +166 -166
- package/docs/web-search.md +62 -62
- package/examples/extensions/README.md +12 -12
- package/examples/extensions/charts/index.ts +13 -13
- package/examples/extensions/charts/skill/SKILL.md +98 -98
- package/examples/extensions/gws/README.md +52 -52
- package/examples/extensions/gws/skill/SKILL.md +57 -57
- package/examples/extensions/gws/skill/references/calendar.md +101 -101
- package/examples/extensions/gws/skill/references/docs.md +65 -65
- package/examples/extensions/gws/skill/references/drive.md +79 -79
- package/examples/extensions/gws/skill/references/gmail.md +85 -85
- package/examples/extensions/gws/skill/references/sheets.md +60 -60
- package/examples/extensions/napkin/skill/SKILL.md +728 -728
- package/examples/extensions/pdf/skill/LICENSE.txt +30 -30
- package/examples/extensions/pdf/skill/SKILL.md +314 -314
- package/examples/extensions/pdf/skill/forms.md +294 -294
- package/examples/extensions/pdf/skill/reference.md +611 -611
- package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -65
- package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -11
- package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -33
- package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -37
- package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -122
- package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -115
- package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -98
- package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -107
- package/examples/extensions/permission-guard/index.ts +65 -65
- package/examples/extensions/pinchtab/skill/SKILL.md +224 -224
- package/examples/extensions/pinchtab/skill/TRUST.md +69 -69
- package/examples/extensions/pinchtab/skill/references/api.md +297 -297
- package/examples/extensions/pinchtab/skill/references/env.md +45 -45
- package/examples/extensions/pinchtab/skill/references/profiles.md +107 -107
- package/examples/extensions/tradestation/host/refresh.ts +102 -102
- package/examples/extensions/tradestation/index.ts +153 -153
- package/examples/extensions/tradestation/skill/SKILL.md +67 -67
- package/examples/extensions/voice-synth/index.ts +94 -94
- package/examples/extensions/voice-synth/skill/SKILL.md +38 -38
- package/examples/extensions/voice-transcribe/requirements.txt +8 -8
- package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -179
- package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -53
- package/examples/extensions/yahoo-mail/cli/package.json +13 -13
- package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -78
- package/package.json +106 -106
- package/resources/agents/explore.md +50 -50
- package/resources/agents/worker.md +24 -24
- package/resources/connection-env-vars.json +25 -25
- package/resources/pi-extensions/subagent/agents.ts +126 -126
- package/resources/pi-extensions/subagent/index.ts +964 -964
- package/resources/profiles/coding/AGENTS.md +43 -43
- package/resources/profiles/coding/mercury-profile.yaml +15 -15
- package/resources/profiles/general/AGENTS.md +31 -31
- package/resources/profiles/general/mercury-profile.yaml +15 -15
- package/resources/profiles/research/AGENTS.md +40 -40
- package/resources/profiles/research/mercury-profile.yaml +15 -15
- package/resources/skills/config/SKILL.md +25 -25
- package/resources/skills/context/SKILL.md +33 -33
- package/resources/skills/conversation-recap/SKILL.md +19 -19
- package/resources/skills/mutes/SKILL.md +31 -31
- package/resources/skills/permissions/SKILL.md +19 -19
- package/resources/skills/preferences/SKILL.md +31 -31
- package/resources/skills/recall/SKILL.md +24 -24
- package/resources/skills/roles/SKILL.md +18 -18
- package/resources/skills/spaces/SKILL.md +18 -18
- package/resources/skills/tasks/SKILL.md +45 -45
- package/resources/templates/AGENTS.md +157 -157
- package/resources/templates/env.template +38 -38
- package/resources/templates/mercury.example.yaml +99 -99
- package/src/agent/container-entry.ts +1 -1
- package/src/agent/container-runner.ts +1345 -1346
- package/src/cli/mercury.ts +39 -7
- package/src/cli/mrctl.ts +636 -636
- package/src/config-file.ts +540 -540
- package/src/config.ts +339 -339
- package/src/core/api.ts +125 -125
- package/src/core/caller-token.ts +101 -101
- package/src/core/permissions.ts +228 -228
- package/src/core/profiles.ts +271 -271
- package/src/core/routes/capability.ts +70 -70
- package/src/core/routes/index.ts +15 -15
- package/src/core/runtime.ts +1530 -1530
- package/src/dashboard/index.html +729 -729
- package/src/extensions/api.ts +273 -273
- package/src/extensions/loader.ts +286 -286
- package/src/extensions/types.ts +517 -517
- package/src/main.ts +605 -605
- package/docs/pending-updates/applicative-profiles.md +0 -15
- package/docs/pending-updates/caller-bound-capability-token.md +0 -15
- package/docs/pending-updates/dm-auto-space.md +0 -15
- /package/docs/archive/{2026-07-01-applicative-profiles.md → applicative-profiles/2026-07-01-applicative-profiles.md} +0 -0
- /package/docs/archive/{2026-07-01-caller-bound-capability-token.md → applicative-profiles/2026-07-01-caller-bound-capability-token.md} +0 -0
- /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.
|