iframer-toolkit 0.10.6 → 0.11.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/README.md +66 -65
- package/dist/cli.js +2935 -1776
- package/dist/local-server.cjs +1503 -1407
- package/dist/mcp-server.cjs +59 -115
- package/extension/README.md +10 -8
- package/extension/background.js +135 -84
- package/extension/manifest.json +1 -1
- package/extension/native-host.cjs +105 -29
- package/extension/popup.js +13 -4
- package/package.json +3 -6
- package/skills/iframer.md +103 -31
package/README.md
CHANGED
|
@@ -84,6 +84,7 @@ To update later:
|
|
|
84
84
|
```sh
|
|
85
85
|
iframer-toolkit update # update via npm (also refreshes the extension if installed)
|
|
86
86
|
iframer-toolkit update --check # just report whether a newer version exists
|
|
87
|
+
iframer-toolkit --version # print the installed version
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
Your agent can also just run `iframer-toolkit install` for you; it figures out the rest.
|
|
@@ -92,7 +93,6 @@ Ships as:
|
|
|
92
93
|
- **CLI** (`iframer-toolkit` / `iframer`): browse, screenshot, credentials, sessions, reverse-engineer APIs
|
|
93
94
|
- **MCP server**: plugs into Claude Code or Codex so agents drive the browser themselves
|
|
94
95
|
- **Browser extension** (optional): lets the agent drive tabs in your real Chrome, on your real logged-in session
|
|
95
|
-
- **Self-hosted Docker server** (optional): adds live headful browsing over noVNC for remote or multi-user setups
|
|
96
96
|
|
|
97
97
|
## Quick start
|
|
98
98
|
|
|
@@ -127,20 +127,18 @@ Claude / Codex (MCP) ──▶ iframer MCP server ──▶ shared local server
|
|
|
127
127
|
and per-domain knowledge cache
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
Everything runs on your machine. Both the MCP clients and the `iframer execute` CLI talk to one shared local server (discovered via `~/.iframer/server.json`, loopback only) that keeps a stealth-patched Chromium warm between calls, tracks every browser it spawns in an on-disk PID registry so nothing leaks, and idle-exits when no one needs it. `install mcp` registers the server in both Claude Code (`~/.claude.json`) and Codex (`~/.codex/config.toml`).
|
|
131
131
|
|
|
132
132
|
**Warm CLI.** `iframer execute` routes to that shared daemon instead of launching a cold browser per invocation, so after the first call it's as fast as the MCP (pass `--in-process` to force a private throwaway browser). Each agent gets its own browser instance, keyed off `CLAUDE_CODE_SESSION_ID` / `TERM_SESSION_ID` or `IFRAMER_INSTANCE`, so concurrent agents never share a window, while all CLI calls share one login session.
|
|
133
133
|
|
|
134
134
|
**Resuming a task.** The browser and its page persist in the daemon between calls and survive an interrupt (Ctrl-C). Name a multi-step or human-in-the-loop task's browser with a stable `instanceId`, and to resume, run `execute` again with the same `instanceId` and act on the current page (`snapshot` / `read` / `find`). Don't navigate again; that reloads the page and loses state like an OTP screen. `iframer instances` lists the live windows and which page each is on.
|
|
135
135
|
|
|
136
|
-
**One credential store for every browser mode.** Stored credentials, session cookies/localStorage, and the knowledge cache all live in one SQLite file at `~/.iframer/iframer.db`. Store a password once, from the CLI or the MCP, and every mode (`headless`, `binary-headful
|
|
136
|
+
**One credential store for every browser mode.** Stored credentials, session cookies/localStorage, and the knowledge cache all live in one SQLite file at `~/.iframer/iframer.db`. Store a password once, from the CLI or the MCP, and every mode (`headless`, `binary-headful`) uses the same row.
|
|
137
137
|
|
|
138
|
-
**Auto-escalation.** When a pipeline is blocked in `headless` (bot detection, captcha), iframer retries in `binary-headful`
|
|
138
|
+
**Auto-escalation.** When a pipeline is blocked in `headless` (bot detection, captcha), iframer retries in `binary-headful` without a round trip to the agent. The mode that worked is recorded in `~/.iframer/domain-modes.json`, so the next run on that domain starts at the right mode.
|
|
139
139
|
|
|
140
140
|
**Knowledge cache.** After every successful run, iframer writes a per-domain markdown file at `~/.iframer/knowledge/<domain>.md` recording which cookies, localStorage keys, and headers the site uses for auth, plus any API endpoints seen along the way. The next time an agent needs data from that domain, it reads the cache first; if there's a direct-API path, it skips the browser entirely. See [Knowledge cache](#knowledge-cache) below.
|
|
141
141
|
|
|
142
|
-
For live remote viewing, multi-user, or Linux server deployments, see [Self-hosting with Docker](#self-hosting-with-docker) below.
|
|
143
|
-
|
|
144
142
|
## Extension mode: drive your real Chrome
|
|
145
143
|
|
|
146
144
|
The optional browser extension lets the agent drive a tab you already have open in your real Chrome. No relaunch, no remote-debugging port, your real logged-in session. The extension dials out to iframer's local server and relays the CDP protocol via `chrome.debugger`, so your live tab is driven by the exact same pipeline engine (find, click, snapshot, obstacle handling, API capture) as every other mode, with real trusted input. While a run is active, Chrome shows its yellow "started debugging" bar.
|
|
@@ -176,15 +174,15 @@ Loading it from that exact path matters: it's what lets `iframer update` refresh
|
|
|
176
174
|
|
|
177
175
|
### Pairing (automatic)
|
|
178
176
|
|
|
179
|
-
Once loaded, the extension pairs itself. The pairing host installed in step 1 hands it the same local token the CLI and MCP already use, straight from `~/.iframer/secret`. Nothing to copy, nothing to type.
|
|
177
|
+
Once loaded, the extension pairs itself on macOS, Linux and Windows. The pairing host installed in step 1 hands it the same local token the CLI and MCP already use, straight from `~/.iframer/secret`. Nothing to copy, nothing to type. The install command also tests the host and starts the local server, and it tells you if either one fails.
|
|
180
178
|
|
|
181
|
-
If
|
|
179
|
+
If auto-pairing ever fails, click the iframer icon in the toolbar and paste the output of this command, in any terminal:
|
|
182
180
|
```sh
|
|
183
|
-
|
|
181
|
+
iframer extension token
|
|
184
182
|
```
|
|
185
183
|
then **Save & connect**.
|
|
186
184
|
|
|
187
|
-
Either way, check the popup: the dot next to "iframer" turns green once it
|
|
185
|
+
Either way, check the popup: the dot next to "iframer" turns green only once the running server has accepted the token. If it can't connect, the popup says why and keeps retrying.
|
|
188
186
|
|
|
189
187
|
You can repeat this whole install on more than one Chrome profile, or in Brave, Edge, or another Chromium-based browser, at the same time. Each one pairs under its own profile name, and iframer routes each task to whichever profile actually has the tab. Once paired, the agent lists your open tabs with the `tabs` tool and drives the one you mean. See [`extension/README.md`](extension/README.md) for how the pipeline and multi-profile routing work under the hood.
|
|
190
188
|
|
|
@@ -195,7 +193,7 @@ iframer-toolkit <command> [args]
|
|
|
195
193
|
|
|
196
194
|
Pipeline:
|
|
197
195
|
execute <pipeline.json|json> Run a pipeline of browser steps
|
|
198
|
-
--mode <mode> Force browser mode (headless|binary-headful
|
|
196
|
+
--mode <mode> Force browser mode (headless|binary-headful)
|
|
199
197
|
--capture-api Record XHR/fetch requests during execution
|
|
200
198
|
--continue-on-error Don't stop on step failure
|
|
201
199
|
--timeout <ms> Stale-state timeout (default: 20000)
|
|
@@ -209,11 +207,27 @@ Quick actions:
|
|
|
209
207
|
--wait-for <selector> Wait for element before extracting
|
|
210
208
|
--sessionless Skip session persistence
|
|
211
209
|
screenshot <url> Take a screenshot of a URL
|
|
210
|
+
--mode <mode> Browser mode (default: headless)
|
|
212
211
|
--annotate Overlay element badges with refs
|
|
213
|
-
-o, --output <path> Output file path
|
|
214
212
|
reverse-engineer <url|file> Capture API calls a site makes
|
|
215
213
|
--output <dir> Save directory
|
|
216
214
|
--typed Generate TypeScript
|
|
215
|
+
--mode <mode> Browser mode
|
|
216
|
+
|
|
217
|
+
Capture in a browser iframer doesn't control (T3 Code, Playwright MCP, ...):
|
|
218
|
+
kit Write the in-page kit, print the agent's tool calls
|
|
219
|
+
--for <t3|playwright-mcp|any> Which browser tool's steps to print (default: t3)
|
|
220
|
+
--mode passive No patching: URLs + form posts only
|
|
221
|
+
--host-filter <regex> Only record matching hosts (e.g. 'discord\.com$')
|
|
222
|
+
--client <path> Also print steps to load a generated client.js
|
|
223
|
+
--print Print the kit source instead (tools without upload)
|
|
224
|
+
ingest <file|-|--latest> Capture -> api.json + client.js + knowledge cache
|
|
225
|
+
--latest Newest capture in T3 Code's downloads or ~/Downloads
|
|
226
|
+
--typed Also write client.d.ts
|
|
227
|
+
--out <dir> Output directory (default: ./<host>-api/)
|
|
228
|
+
--keep Keep the capture file (default: deleted)
|
|
229
|
+
--no-knowledge Don't update ~/.iframer/knowledge
|
|
230
|
+
--json One-line JSON summary
|
|
217
231
|
|
|
218
232
|
Browser windows:
|
|
219
233
|
instances List live browser windows (instanceId → current page)
|
|
@@ -251,6 +265,7 @@ Setup:
|
|
|
251
265
|
install mcp [--dev] Register MCP server in Claude Code and Codex
|
|
252
266
|
install extension chrome Install the optional browser extension pairing host
|
|
253
267
|
extension path Print the extension folder to load in chrome://extensions
|
|
268
|
+
extension token Print the pairing token (only needed to pair by hand)
|
|
254
269
|
update [--check] Update iframer via npm (--check: report only)
|
|
255
270
|
remove [chromium|mcp|extension] Remove everything, or one piece
|
|
256
271
|
|
|
@@ -267,15 +282,26 @@ Once the MCP is registered, the agent has access to:
|
|
|
267
282
|
|
|
268
283
|
- **`status`**: system health, session state, stored credentials, available browser modes
|
|
269
284
|
- **`knowledge`**: read/list/clear the per-domain knowledge cache. Agents are told to check this before every `execute` or `browse`; if the cache already has a direct-API path, the browser never launches.
|
|
270
|
-
- **`execute`**: run a pipeline of browser steps (navigate, click, right-click, fill, select, human-click, human-type, type-code, read, evaluate, extract, wait, wait-for, scroll, keyboard, upload, paste, download, login, solve-captcha, recaptcha, screenshot, snapshot, find). Each step has a 20s stale-state timeout. On failure, it returns the exact step, error type, and a screenshot of the page at the point of failure. Auto-escalates browser modes on bot-block.
|
|
285
|
+
- **`execute`**: run a pipeline of browser steps (navigate, click, right-click, fill, select, human-click, human-type, type-code, read, evaluate, extract, wait, wait-for, scroll, keyboard, upload, paste, download, login, solve-captcha, recaptcha, screenshot, snapshot, find). Each step has a 20s stale-state timeout, and `click` fails at 8s with Playwright's own reason (not visible, covered by another element). On failure, it returns the exact step, error type, and a screenshot of the page at the point of failure. Auto-escalates browser modes on bot-block. See [Pipeline guardrails](#pipeline-guardrails).
|
|
271
286
|
- **`browse`**: fast headless fetch with session persistence for pages that don't need a full pipeline
|
|
272
287
|
- **`reverse-engineer`**: capture the APIs a site calls (feeds the knowledge cache so future runs can skip the browser)
|
|
273
288
|
- **`remember`**: per-domain element anchors. Save a selector once under a name, then target it as `@a:<name>` in any later pipeline, no re-finding the element on every visit. Anchors self-heal (a failed anchor triggers re-discovery, not blind retries) and the tool also records per-site quirks.
|
|
274
289
|
- **`tabs`**: list and target tabs in your real Chrome (requires the [extension](#extension-mode-drive-your-real-chrome))
|
|
275
290
|
- **`clipboard`**: read/write the machine's clipboard, the same one your Chrome uses: read a code a site just copied, or stage text to paste into a field via the `paste` step
|
|
276
|
-
- **`session`**: `stop` (save state)
|
|
291
|
+
- **`session`**: `stop` (save state, close the browser), `clear` (wipe stored session data), `restart` (kill browsers and reset after a freeze or crash; credentials and knowledge are kept), `capture-start` / `capture-stop` (record XHR/fetch outside a pipeline), `get-cookies` (all cookies, including HttpOnly), `get-auth` (cookies, localStorage and sessionStorage together)
|
|
277
292
|
- **`credentials`**: `store` (secure form via MCP elicitation), `list`. `store` refuses to overwrite existing credentials unless told to (`force: true`), which blocks the common "login failed, re-ask for password" mistake when the real problem is browser mode or bot detection.
|
|
278
293
|
|
|
294
|
+
## Pipeline guardrails
|
|
295
|
+
|
|
296
|
+
Some calls are refused or cut short on purpose, so an agent gets a clear error instead of a hang, a stray window, or a tripped bot trap.
|
|
297
|
+
|
|
298
|
+
- **Honeypots.** `snapshot` leaves out fields no human can reach (pushed offscreen, clipped, or made transparent by an ancestor) and marks bait-looking ones (`website`, `fax`, `tabindex=-1`) as `[honeypot?]`. `fill` refuses an unreachable field with an explanatory error; pass `force: true` only when you're certain the field is real.
|
|
299
|
+
- **Selectors.** Use an `@e` ref from `snapshot`/`find`, an `@a:` anchor, or plain CSS. Playwright-only syntax (`text=`, `:has-text()`, `:text-is()`, `>>`) is rejected with a hint to `find` the element instead.
|
|
300
|
+
- **Frozen tabs.** Every page probe has a time budget, so a tab whose JS stops answering fails in about 30s. In extension mode, the watchdog names the step that hung and keeps the results of the steps that finished.
|
|
301
|
+
- **Stuck navigation.** If a page never commits to the new URL, `navigate` falls back to `location.assign`. `beforeunload` prompts are accepted.
|
|
302
|
+
- **No stray windows.** A pipeline with no `navigate` step and no live browser to reattach to is refused, as is `mode: "extension"` without a `tabId`. While the extension is connected, `session` `capture-start`, `capture-stop`, `get-cookies` and `get-auth` also refuse to open a new window unless you pass a `mode` or a matching browser is already open.
|
|
303
|
+
- **Keyboard in extension mode.** Cmd/Ctrl+A selects all in every mode. Other Cmd/Ctrl shortcuts are refused in extension mode.
|
|
304
|
+
|
|
279
305
|
## Session persistence
|
|
280
306
|
|
|
281
307
|
Session data (cookies + localStorage) and credentials are stored in SQLite at `~/.iframer/iframer.db` and encrypted with AES-256-GCM. Data is automatically re-injected on the next `execute` or `browse` so agents stay logged in across restarts, and across browser modes: a session captured in `binary-headful` will load into `headless` on the next run.
|
|
@@ -302,70 +328,46 @@ iframer-toolkit --clear-cache # wipe all cached knowledge
|
|
|
302
328
|
iframer-toolkit --clear-cache figma.com # wipe one domain
|
|
303
329
|
```
|
|
304
330
|
|
|
331
|
+
## Bring your own browser: T3 Code and other agent browsers
|
|
332
|
+
|
|
333
|
+
If your agent already has a browser (a T3 Code preview tab, Playwright MCP, Chrome DevTools MCP), iframer can reverse engineer the site inside it, on the session you're already logged into, without launching anything.
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
iframer kit --for t3 # writes ~/.iframer/kit/iframer-kit.js, prints the agent's tool calls
|
|
337
|
+
# agent: upload the kit into the tab, eval it, use the site, then window.__ifk.dump()
|
|
338
|
+
iframer ingest --latest --typed # finds the capture in T3's downloads -> ./<host>-api/
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
- **Small for the agent:** the kit and the capture move as files, so neither lands in the agent's context. On the 40-endpoint test site, the whole install-capture-export round was about 550 tokens of tool calls.
|
|
342
|
+
- **Shapes, not values:** paths are templated (`/users/{id}`), bodies and responses become type shapes, headers become names. `ingest` scans the result and refuses it if anything looks like a token, email or phone number.
|
|
343
|
+
- **Output:** `api.json` (endpoint map with coverage), `client.js` (in-page client over `window.__ifk.call`), `client.d.ts` with `--typed`, and the domain's knowledge file.
|
|
344
|
+
- **Replay stays in the page:** HttpOnly cookies are invisible to page JS, so generated calls run through the tab. Same-origin only, GET unless approved, 50 calls per session, and a hard stop on 403, 429 or a captcha page.
|
|
345
|
+
- **Not complete:** worker requests, redirect hops, cross-origin iframes and Set-Cookie are invisible to the kit, and every report says so.
|
|
346
|
+
- **Survives the page:** after a full page load the agent re-runs one short `eval` and the capture continues. Works on sites that delete `window.sessionStorage` (Discord).
|
|
347
|
+
- **Other tools:** `--for playwright-mcp` or `--for any`. Tools without file upload can evaluate `iframer kit --print` instead, which costs about 17 KB per page load.
|
|
348
|
+
|
|
305
349
|
## Captcha solving
|
|
306
350
|
|
|
307
|
-
iframer
|
|
351
|
+
iframer can try to solve reCAPTCHA and hCaptcha with Claude's vision API, in any browser mode. Treat it as best effort: it fails on many challenges, and when someone is at the screen it's more reliable to have them solve it in a `binary-headful` window. To try it, use the `solve-captcha` step in a pipeline:
|
|
308
352
|
|
|
309
353
|
```json
|
|
310
354
|
{ "type": "solve-captcha" }
|
|
311
355
|
```
|
|
312
356
|
|
|
313
|
-
Requires `ANTHROPIC_API_KEY` in your
|
|
357
|
+
Requires `ANTHROPIC_API_KEY` in the environment that starts the iframer server: your shell for the CLI, or the `env` block of your MCP config. If the server is already running, stop it first so it picks up the key. With a key set, `navigate` also tries automatically when it lands on a captcha, and each attempt uses API credit.
|
|
358
|
+
|
|
359
|
+
If it can't solve the captcha, the run fails with `captcha-unsolvable` and the reason.
|
|
314
360
|
|
|
315
361
|
## Environment variables
|
|
316
362
|
|
|
317
363
|
| Variable | Required | Description |
|
|
318
364
|
|---------------------|----------|-------------|
|
|
319
365
|
| `ANTHROPIC_API_KEY` | For captcha | Used for vision-based captcha solving |
|
|
366
|
+
| `IFRAMER_CAPTCHA_MODEL` | No | Claude model the solver uses (default: `claude-haiku-4-5-20251001`). |
|
|
320
367
|
| `IFRAMER_SECRET` | No | Encryption key for sessions & credentials. Defaults to the value at `~/.iframer/secret` (auto-generated on first `install mcp`). Override in the shell or in `.env` to pin a specific key. |
|
|
321
368
|
| `IFRAMER_TELEMETRY` | No | Set to `0` in the MCP env to disable the local token-telemetry log (`iframer telemetry` reads it). |
|
|
322
369
|
| `IFRAMER_LOCAL_PORT`| No | Base port for the shared local server (default: `3022`, loopback only). |
|
|
323
|
-
| `IFRAMER_DATA_DIR` | No | Override the data directory. Default: `~/.iframer`.
|
|
324
|
-
| `IFRAMER_MODE` | No | `local` (default) or `docker`. Force a mode regardless of what's running. |
|
|
325
|
-
| `IFRAMER_URL` | No | Docker API URL when self-hosting (default: `http://localhost:3021`). |
|
|
326
|
-
|
|
327
|
-
## Self-hosting with Docker
|
|
328
|
-
|
|
329
|
-
The Docker server adds a live headful browsing mode over noVNC (watch the agent drive the browser in real time) and lets multiple clients share one browser pool. The Docker path only runs when a pipeline explicitly requests `mode: "docker-headful"`; `headless` and `binary-headful` always run directly on the host even when Docker is up, so host-stored credentials stay visible.
|
|
330
|
-
|
|
331
|
-
`docker-compose.yml` bind-mounts `~/.iframer` from the host into the container as `/iframer-data` and sets `IFRAMER_DATA_DIR=/iframer-data`, so container and host share the same SQLite file. Credentials stored via the CLI or MCP on the host are immediately visible to `docker-headful`, no copy step.
|
|
332
|
-
|
|
333
|
-
**1. Clone and configure**
|
|
334
|
-
|
|
335
|
-
```sh
|
|
336
|
-
git clone https://github.com/EduardoFazolo/iframer-toolkit.git
|
|
337
|
-
cd iframer-toolkit
|
|
338
|
-
cp .env.example .env
|
|
339
|
-
# Edit .env: set ANTHROPIC_API_KEY (for captcha) and IFRAMER_SECRET (for auth)
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
**2. Start**
|
|
343
|
-
|
|
344
|
-
```sh
|
|
345
|
-
bun run start:docker # docker compose up --build -d
|
|
346
|
-
bun run logs:docker # tail container logs
|
|
347
|
-
bun run stop:docker # stop containers
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
**3. Point the MCP at it (remote host only)**
|
|
351
|
-
|
|
352
|
-
If Docker runs on a remote machine and the MCP on a different one, install the MCP with the remote URL:
|
|
353
|
-
|
|
354
|
-
```sh
|
|
355
|
-
IFRAMER_URL=https://your-host:3021 iframer-toolkit install mcp --dev
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
When Docker is on the same host as the MCP, no extra setup is needed; the local MCP server auto-detects the Docker API on `localhost:3021` and routes only `docker-headful` requests through it.
|
|
359
|
-
|
|
360
|
-
**4. Watch the browser live**
|
|
361
|
-
|
|
362
|
-
When a `docker-headful` session is active, open noVNC:
|
|
363
|
-
|
|
364
|
-
```
|
|
365
|
-
http://your-host:6080
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
Or run `iframer-toolkit watch` to auto-open it.
|
|
370
|
+
| `IFRAMER_DATA_DIR` | No | Override the data directory. Default: `~/.iframer`. |
|
|
369
371
|
|
|
370
372
|
## Architecture
|
|
371
373
|
|
|
@@ -380,7 +382,6 @@ Or run `iframer-toolkit watch` to auto-open it.
|
|
|
380
382
|
| Session persistence| Same SQLite file, cookies + localStorage re-injected across runs and modes |
|
|
381
383
|
| Knowledge cache | Plain markdown at `~/.iframer/knowledge/<domain>.md` |
|
|
382
384
|
| Captcha solving | Anthropic vision API (`@anthropic-ai/sdk`) |
|
|
383
|
-
| Live viewing | Xvfb + x11vnc + noVNC + websockify (Docker mode only) |
|
|
384
385
|
| MCP server | `@modelcontextprotocol/sdk` |
|
|
385
386
|
| Runtime | Node.js ≥18 (Bun for development) |
|
|
386
387
|
|
|
@@ -416,10 +417,10 @@ bun run bin/cli.js status
|
|
|
416
417
|
# Run the MCP server from source
|
|
417
418
|
bun run src/mcp/server.ts
|
|
418
419
|
|
|
419
|
-
# Run the
|
|
420
|
+
# Run the local server from source
|
|
420
421
|
bun run start # bun run index.ts
|
|
421
422
|
|
|
422
|
-
# Rebuild the distributable bundles (dist/cli.
|
|
423
|
+
# Rebuild the distributable bundles (dist/cli.js, dist/mcp-server.cjs, dist/local-server.cjs)
|
|
423
424
|
bun run build
|
|
424
425
|
|
|
425
426
|
# Install the locally-built package globally for testing
|