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/skills/iframer.md
CHANGED
|
@@ -10,9 +10,12 @@ You are an expert at using the iframer browser automation toolkit. The user want
|
|
|
10
10
|
- `iframer.credentials` — list or store login credentials (NEVER re-store as recovery)
|
|
11
11
|
- `iframer.execute` — run a browser pipeline (navigate, login, screenshot, snapshot, etc.)
|
|
12
12
|
- `iframer.browse` — fast headless page fetch (no pipeline needed)
|
|
13
|
-
- `iframer.session` — stop (save state)
|
|
13
|
+
- `iframer.session` — stop (save state, close the browser), clear (wipe session data), restart (recover from a freeze or crash), capture-start / capture-stop (record XHR/fetch), get-cookies, get-auth
|
|
14
14
|
- `iframer.status` — system health, modes, stored credentials
|
|
15
15
|
- `iframer.reverse-engineer` — capture API calls a site makes
|
|
16
|
+
- `iframer.tabs` — list/open tabs in the user's REAL Chrome (needs the extension)
|
|
17
|
+
- `iframer.remember` — per-domain element anchors, targeted as `@a:<name>` in any selector
|
|
18
|
+
- `iframer.clipboard` — read/write the machine's clipboard (same one the user's Chrome uses)
|
|
16
19
|
|
|
17
20
|
## Core rules
|
|
18
21
|
|
|
@@ -22,6 +25,8 @@ You are an expert at using the iframer browser automation toolkit. The user want
|
|
|
22
25
|
4. **NEVER pass explicit selectors to the login step** unless the auto-detect failed AND you took a snapshot to find the right ones. The login step auto-detects email, password, submit, and OTP fields.
|
|
23
26
|
5. If headless detects Imperva or Akamai, immediately stop headless and pivot to headed in the same run; do not wait for a block, retry headless, or defer the switch to a future request. Use headed from the outset when the provider is already known in the current task. On detection, switch to headed and use respectful pacing (at least 3 seconds between top-level actions; no parallel bursts or repeated blocked retries). Do not announce the provider or explain the switch unless the user asks why. Only when asked, explain that the site is covered by Imperva or Akamai and iframer adjusts its browser mode and pacing to respect it.
|
|
24
27
|
6. **Let iframer handle mode escalation.** Don't hardcode modes. If you must pick one, prefer no mode (auto-select) or `binary-headful` for sites known to block headless.
|
|
28
|
+
7. **"My browser" / "my tabs" / "I'm already logged in there" means extension mode, never a launched browser.** See the section below. A launched browser has none of the user's logins and opening one is a visible, unwanted window on their desktop.
|
|
29
|
+
8. **Every browser you open is yours to close.** Call `session stop` when the work is done, and if a call opened a browser by mistake, close it immediately instead of leaving it. `status` lists live windows.
|
|
25
30
|
|
|
26
31
|
## How to build pipelines
|
|
27
32
|
|
|
@@ -33,27 +38,34 @@ The `execute` tool takes a `steps` array. Each step is an object with a `type` f
|
|
|
33
38
|
|------|--------|-------------|
|
|
34
39
|
| `navigate` | `url`, `waitUntil?` | Go to URL |
|
|
35
40
|
| `login` | `domain` | Auto-detect form, fill stored credentials, handle 2FA. Handles email-first flows (Slack, Microsoft, Google) and standard email+password flows. |
|
|
36
|
-
| `snapshot` | — | Get interactive elements as structured list with @e refs. **Do this BEFORE interacting.** |
|
|
41
|
+
| `snapshot` | — | Get interactive elements as structured list with @e refs. **Do this BEFORE interacting.** Fields no human can reach (honeypots pushed offscreen/clipped/transparent by an ancestor) are omitted; bait-looking ones (`website`, `fax`, tabindex=-1) are marked `[honeypot?]` — leave those empty unless the task clearly needs them. |
|
|
37
42
|
| `find` | `role?`, `name?`, `text?`, `placeholder?`, `label?` | Find a specific element, returns a ref |
|
|
38
43
|
| `screenshot` | `annotate?` | Take a screenshot. `annotate: true` overlays numbered badges. |
|
|
39
|
-
| `click` | `selector` | Click element (use @e refs from snapshot) |
|
|
40
|
-
| `
|
|
44
|
+
| `click` | `selector` | Click element (use @e refs from snapshot/find). Text selectors (`text=`, `:has-text()`, `:text-is()`, `>>`) are rejected: `find` by text, then click the ref. |
|
|
45
|
+
| `right-click` | `selector` or `x`, `y` | Right-click an element or coordinates |
|
|
46
|
+
| `fill` | `selector`, `value`, `force?` | Fill input (use @e refs). Refuses honeypot fields (unreachable by a human) with an explanatory error — skip the field. `force: true` bypasses, only when you are certain it is real. |
|
|
41
47
|
| `human-click` | `selector` or `x`, `y` | Human-like click with random offset |
|
|
42
|
-
| `human-type` | `selector`, `value` | Human-like typing with variable delays |
|
|
48
|
+
| `human-type` | `selector`, `value`, `clear?` | Human-like typing with variable delays. Appends by default; `clear: true` replaces the existing text. |
|
|
43
49
|
| `scroll` | `deltaY?` | Scroll the page |
|
|
44
50
|
| `wait` | `ms` | Wait N milliseconds |
|
|
45
51
|
| `wait-for` | `selector`, `timeout?` | Wait for element to appear |
|
|
46
52
|
| `evaluate` | `expression` | Run JS, return result |
|
|
47
53
|
| `extract` | `expression` | Run JS, include result in response |
|
|
48
|
-
| `keyboard` | `key` | Press a key (Enter, Escape, Tab, etc.) |
|
|
49
|
-
| `
|
|
54
|
+
| `keyboard` | `key`, `meta?`, `ctrl?` | Press a key (Enter, Escape, Tab, etc.). Cmd/Ctrl+A selects all everywhere; other Cmd/Ctrl shortcuts are refused in extension mode. |
|
|
55
|
+
| `select` | `selector`, `value` | Pick an option in a native `<select>`, with real change events. Never set `.value` via `evaluate`. |
|
|
56
|
+
| `type-code` | `value`, `selector?` | Type a verification code digit by digit. Default target: `input[type="tel"]`. |
|
|
57
|
+
| `upload` | `selector`, `files` | Set absolute local file paths on an `<input type=file>` |
|
|
58
|
+
| `paste` | `selector?` | Paste the OS clipboard into a field, or the focused element if omitted. More reliable than a ⌘V `keyboard` step. |
|
|
59
|
+
| `download` | `url`, `path?` | Fetch a file with the browser's cookies and save it server-side, no Save-As dialog. Default path: `~/.iframer/downloads/<name>`. |
|
|
60
|
+
| `solve-captcha` | — | Best-effort reCAPTCHA or hCaptcha solve with vision AI. Works in any mode; needs `ANTHROPIC_API_KEY`. Fails the run with `captcha-unsolvable` if it can't solve it. |
|
|
61
|
+
| `recaptcha` | `action`, `tiles?` | Manual captcha control: `info`, `click`, `answer`, `select`, `verify`, `solve`. The full workflow arrives in the result when a captcha blocks a run. |
|
|
50
62
|
|
|
51
63
|
### Pipeline options
|
|
52
64
|
|
|
53
65
|
| Option | Default | Description |
|
|
54
66
|
|--------|---------|-------------|
|
|
55
|
-
| `mode` | auto | `headless`, `binary-headful
|
|
56
|
-
| `captureApi` | false | Record all XHR/fetch requests. Use for reverse-engineering. |
|
|
67
|
+
| `mode` | auto | `headless`, `binary-headful`. Omit for auto-select + auto-escalation. |
|
|
68
|
+
| `captureApi` | false | Record all XHR/fetch requests. Use for reverse-engineering, and when a form submit fails with only a vague on-page message: every HTTP >= 400 response is reported per step, so you read the server's real answer (e.g. `POST /api/apply → 422: {"error":"..."}`) instead of guessing. |
|
|
57
69
|
| `staleTimeoutMs` | 20000 | Per-step stale-state timeout |
|
|
58
70
|
| `continueOnError` | false | Don't abort pipeline on step failure |
|
|
59
71
|
| `screenshotAfterEach` | false | Screenshot after every step (expensive) |
|
|
@@ -154,6 +166,24 @@ execute: steps: [{ type: "click", selector: "@e3" }, { type: "screenshot" }]
|
|
|
154
166
|
|
|
155
167
|
The more interactions (scroll, click tabs, navigate sub-pages), the more API calls get captured. Results feed into the knowledge cache automatically.
|
|
156
168
|
|
|
169
|
+
### Reverse-engineer inside a browser iframer doesn't control (T3 Code, Playwright MCP, ...)
|
|
170
|
+
|
|
171
|
+
When you already drive a browser through your own tools (a T3 Code preview tab, Playwright MCP, Chrome DevTools MCP), don't launch iframer's browser: load the in-page kit into that tab. It records the page's own calls on the user's real session and exports shapes, never values. Use the CLI for this, not the MCP tools.
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
1. iframer kit --for t3 # or --for playwright-mcp / --for any. Prints the exact tool calls.
|
|
175
|
+
2. Follow the printed install steps (upload the kit file, stash it, top-level eval). Never paste the kit's source.
|
|
176
|
+
3. Drive the site with your normal tools. After EVERY full page load, re-run the printed eval step (step 4).
|
|
177
|
+
4. window.__ifk.dump(), then: iframer ingest --latest --typed
|
|
178
|
+
-> ./<host>-api/api.json, client.js, client.d.ts, plus the knowledge cache. The capture file is deleted.
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- Replays run IN the page: `window.__ifk.call('GET','/path')`, or load the generated `client.js` (`iframer kit --for t3 --client ./<host>-api/client.js` prints the steps). HttpOnly cookies never leave the browser, so curl replays fail.
|
|
182
|
+
- Writes need `{approved:true}`: ask the user first. Replays stop for good on 403, 429 or a captcha page. Do not retry or work around it.
|
|
183
|
+
- Sites that detect tampering: `iframer kit --mode passive` (no patching; URLs and form posts only).
|
|
184
|
+
- Report coverage as the kit states it. It is never complete: workers, redirect hops and cross-origin iframes are invisible.
|
|
185
|
+
- `ingest` refuses (exit 2) if anything value-like slipped into the capture. Don't edit the file to force it through; tell the user what was found.
|
|
186
|
+
|
|
157
187
|
### Handle captchas
|
|
158
188
|
|
|
159
189
|
Captcha handling depends on the browser mode:
|
|
@@ -168,16 +198,13 @@ Captcha handling depends on the browser mode:
|
|
|
168
198
|
execute: steps: [{ type: "screenshot" }] // verify captcha is gone, then continue
|
|
169
199
|
```
|
|
170
200
|
|
|
171
|
-
**
|
|
172
|
-
-
|
|
201
|
+
**Headless, or nobody at the screen**:
|
|
202
|
+
- If you hit a captcha in headless, iframer auto-escalates to binary-headful.
|
|
203
|
+
- If nobody can solve it by hand, try the `solve-captcha` step. It's best effort: it uses vision AI, needs `ANTHROPIC_API_KEY`, and fails on many challenges.
|
|
173
204
|
```
|
|
174
205
|
execute: steps: [{ type: "solve-captcha" }, { type: "screenshot" }]
|
|
175
206
|
```
|
|
176
207
|
|
|
177
|
-
**Headless**:
|
|
178
|
-
- Captchas can't be solved in headless. If you hit one, iframer auto-escalates to binary-headful.
|
|
179
|
-
- If it still fails, switch to docker-headful and use `solve-captcha`.
|
|
180
|
-
|
|
181
208
|
### Handle OTP / 2FA codes
|
|
182
209
|
|
|
183
210
|
If the login step encounters a code field and no TOTP secret is stored:
|
|
@@ -198,7 +225,7 @@ When the browser crashes, freezes, or you get connection errors:
|
|
|
198
225
|
```
|
|
199
226
|
session: { action: "restart" }
|
|
200
227
|
```
|
|
201
|
-
This kills ALL running browser instances
|
|
228
|
+
This kills ALL running browser instances and resets state. Credentials and knowledge cache are NOT affected. The next `execute` call launches a fresh browser automatically.
|
|
202
229
|
|
|
203
230
|
**The execute tool also auto-recovers:** if it detects a *connection-level* crash (connection closed, ECONNREFUSED/ECONNRESET/EPIPE, socket hang up), it restarts the browser server and retries once before reporting failure. Timeouts are deliberately NOT auto-retried — a slow-but-healthy pipeline that hit the client timeout must not be re-run, or non-idempotent steps (form submits, purchases) would double-execute. If auto-recovery fails, or a step timed out, call `session restart` manually and continue from where you left off (don't blindly re-run the whole pipeline).
|
|
204
231
|
|
|
@@ -229,11 +256,11 @@ knowledge: { action: "clear", domain: "example.com" }
|
|
|
229
256
|
|
|
230
257
|
If an `execute` call takes a very long time (30s+), it's probably stuck on one of:
|
|
231
258
|
|
|
232
|
-
1. **Captcha blocking progress** — the login/navigate step can't proceed because a captcha appeared. In binary-headful mode, tell the user to solve it in the visible window.
|
|
259
|
+
1. **Captcha blocking progress** — the login/navigate step can't proceed because a captcha appeared. In binary-headful mode, tell the user to solve it in the visible window. If nobody can, add a `solve-captcha` step.
|
|
233
260
|
|
|
234
261
|
2. **Waiting for a page transition that never happens** — the site didn't redirect after form submission. Take a screenshot to see the current state, then decide what to do (wrong button clicked, error message on page, etc.).
|
|
235
262
|
|
|
236
|
-
3. **Stale-state timeout** — iframer's stale monitor will abort after `staleTimeoutMs` (default 20s). The error will say "stale-state" and include a screenshot. Read the screenshot to understand what the page looks like.
|
|
263
|
+
3. **Stale-state timeout** — iframer's stale monitor will abort after `staleTimeoutMs` (default 20s). The error will say "stale-state" and include a screenshot. Read the screenshot to understand what the page looks like. If the step targeted a selector that no longer exists, the error is reported as `element-not-found` instead ("No element matches …"): the page re-rendered it away. Re-snapshot right before acting and retry — do not change styles or theorise about visibility.
|
|
237
264
|
|
|
238
265
|
**Recovery pattern for all stalls:**
|
|
239
266
|
```
|
|
@@ -242,7 +269,7 @@ If an `execute` call takes a very long time (30s+), it's probably stuck on one o
|
|
|
242
269
|
|
|
243
270
|
2. If captcha visible:
|
|
244
271
|
- binary-headful → ask user to solve it in the visible window
|
|
245
|
-
-
|
|
272
|
+
- nobody at the screen → execute: steps: [{ type: "solve-captcha" }]
|
|
246
273
|
|
|
247
274
|
3. If the page shows an error or unexpected state:
|
|
248
275
|
- Take a snapshot to get element refs
|
|
@@ -253,11 +280,9 @@ If an `execute` call takes a very long time (30s+), it's probably stuck on one o
|
|
|
253
280
|
|
|
254
281
|
**CRITICAL: Don't restart from scratch.** The browser session persists between execute calls (same daemon, same page context). If you were mid-login and hit a captcha, you don't need to navigate back to the login page — you're still there. Just solve the captcha and continue.
|
|
255
282
|
|
|
256
|
-
**Timeouts:**
|
|
283
|
+
**Timeouts:** Heavy SPAs (Slack, Figma) can take 30+ seconds to load. If an execute call times out:
|
|
257
284
|
1. Use `staleTimeoutMs: 60000` or higher for heavy sites
|
|
258
|
-
2.
|
|
259
|
-
3. Take a screenshot to see where the browser is — the session is likely still alive
|
|
260
|
-
4. If the container crashed, `docker compose up -d` restarts it; stored sessions/credentials survive since they're in the host's SQLite
|
|
285
|
+
2. Take a screenshot to see where the browser is — the session is likely still alive
|
|
261
286
|
|
|
262
287
|
## Multi-step flows (email → code, OAuth, etc.)
|
|
263
288
|
|
|
@@ -292,7 +317,6 @@ Instead:
|
|
|
292
317
|
**Your progress is NOT lost.** Credentials, sessions, and knowledge cache are in the SQLite database on disk — they survive MCP server restarts. You don't need to re-login or re-store anything.
|
|
293
318
|
|
|
294
319
|
If the MCP disconnects repeatedly on the same operation, the site is probably too heavy for the current mode. Switch to a lighter approach:
|
|
295
|
-
- If using `docker-headful` → switch to `binary-headful` (less overhead)
|
|
296
320
|
- If doing a complex multi-step pipeline → break it into smaller execute calls
|
|
297
321
|
- If just trying to capture APIs → you probably already have enough from previous runs. Check `knowledge get <domain>` before retrying.
|
|
298
322
|
|
|
@@ -302,7 +326,7 @@ If the MCP disconnects repeatedly on the same operation, the site is probably to
|
|
|
302
326
|
|---------|-----------------|
|
|
303
327
|
| Guessing CSS selectors like `button[data-qa='submit']` | Use `snapshot` → read refs → use `@e` refs |
|
|
304
328
|
| Passing `usernameSelector`/`passwordSelector` to login step | Omit them — auto-detect handles it |
|
|
305
|
-
| Re-storing credentials when login fails | Retry with stronger mode (`binary-headful`
|
|
329
|
+
| Re-storing credentials when login fails | Retry with stronger mode (`binary-headful`) |
|
|
306
330
|
| Launching browser without checking knowledge cache | Always call `knowledge get <domain>` first |
|
|
307
331
|
| Using `browse` for a page that needs interaction | Use `execute` with a pipeline |
|
|
308
332
|
| Running a huge pipeline without snapshots | Split into navigate+snapshot, then interact based on refs |
|
|
@@ -314,6 +338,56 @@ If the MCP disconnects repeatedly on the same operation, the site is probably to
|
|
|
314
338
|
| Not passing `mode` consistently across execute calls in the same flow | Always pass the same `options.mode` for all steps in a multi-call flow |
|
|
315
339
|
| Panicking when MCP disconnects (running `claude mcp`, inspecting source, giving up) | Just wait 2-3 seconds, call `status` to verify reconnect, then `session restart` and retry |
|
|
316
340
|
|
|
341
|
+
## Driving the user's real Chrome (extension mode)
|
|
342
|
+
|
|
343
|
+
Use this whenever the user says "my browser", "my tabs", "the page I have open", or the task
|
|
344
|
+
needs accounts they are already signed into. A launched browser has a fresh, logged-out profile.
|
|
345
|
+
|
|
346
|
+
1. `tabs` with `action: "list"` — returns open tabs as `[id <n>] title / url`. If it reports the
|
|
347
|
+
extension is not connected, tell the user to open Chrome / check the extension; do NOT fall
|
|
348
|
+
back to launching a browser.
|
|
349
|
+
2. `execute` with `options.mode: "extension"` AND `options.tabId: <n>` from that list. Both are
|
|
350
|
+
required: `mode` alone is refused and launches nothing.
|
|
351
|
+
3. Need a different page? `tabs` with `action: "open"` returns the new tab id. Do NOT use a
|
|
352
|
+
navigate-only pipeline in a launched browser to "get there".
|
|
353
|
+
|
|
354
|
+
Rules while in extension mode:
|
|
355
|
+
|
|
356
|
+
- **Do not call `session get-cookies` / `get-auth` / `capture-start` without a mode.** They default
|
|
357
|
+
to launching a separate window. The tab you are driving already has the auth; read it there.
|
|
358
|
+
- **Do not call `session stop`.** There is nothing to stop, and it is the user's own browser.
|
|
359
|
+
- `execute` never escalates in extension mode. If it fails, report why (extension disconnected,
|
|
360
|
+
tab closed) instead of retrying in a launched mode, which would silently use a logged-out profile.
|
|
361
|
+
|
|
362
|
+
Driving a live single-page app (most dashboards) in extension mode:
|
|
363
|
+
|
|
364
|
+
- **Act only on `@e` refs** from a `find`/`snapshot` in the same execute call. Never CSS you
|
|
365
|
+
guessed, never text selectors.
|
|
366
|
+
- **Give steps that can wedge the tab their own execute call:** `navigate`, anything that opens or
|
|
367
|
+
closes a modal, and `evaluate`. If one hangs, the steps around it are not lost.
|
|
368
|
+
- **If a step reports the tab stopped responding,** run a lone `read` or `snapshot` to confirm it
|
|
369
|
+
recovered, then continue. Don't resend the same step unchanged.
|
|
370
|
+
- **Route changes:** `navigate` first. If the page never leaves, use `evaluate` with
|
|
371
|
+
`location.assign("<url>")`, or click the site's own link.
|
|
372
|
+
- **Custom dropdowns (MUI, react-select, comboboxes):** click the field or its arrow button, `find`
|
|
373
|
+
the option (`role: "option", name: "Texas"`), click that `@e` ref. To filter first, `fill` the
|
|
374
|
+
field (it stays focused so the list stays open), then `find` and click the option. Don't type
|
|
375
|
+
with Cmd+A/Backspace tricks.
|
|
376
|
+
- **Don't scroll with `evaluate` (`scrollIntoView`).** `click` scrolls its target into view itself.
|
|
377
|
+
|
|
378
|
+
## Cleaning up after yourself
|
|
379
|
+
|
|
380
|
+
A browser opened by mistake is a real, visible window sitting on the user's desktop. It is your
|
|
381
|
+
job to close it, not theirs.
|
|
382
|
+
|
|
383
|
+
- Any pipeline that opens a browser must end with `session stop` once the task is done.
|
|
384
|
+
- If you opened a browser you did not need (wrong mode, wrong call, task turned out not to need
|
|
385
|
+
one), call `session stop` right away, in the same turn you noticed.
|
|
386
|
+
- `status` lists live windows with their `instanceId`. If you see one you do not recognise from
|
|
387
|
+
your own work in this conversation, say so rather than silently leaving it.
|
|
388
|
+
- A pipeline with no `navigate` step is refused unless a live browser already holds that
|
|
389
|
+
`instanceId`. If you get that error, you probably meant extension mode — go get a `tabId`.
|
|
390
|
+
|
|
317
391
|
## Mode selection guide
|
|
318
392
|
|
|
319
393
|
| Scenario | Recommended mode |
|
|
@@ -321,15 +395,13 @@ If the MCP disconnects repeatedly on the same operation, the site is probably to
|
|
|
321
395
|
| Simple data extraction, no login | Omit mode (auto-select, starts headless) |
|
|
322
396
|
| Site with login | Omit mode (auto-escalation if headless is blocked) |
|
|
323
397
|
| Known bot-detection (Cloudflare, etc.) | `binary-headful` |
|
|
324
|
-
| Heavy SPA (Slack, Figma, Linear, etc.) | `binary-headful`
|
|
398
|
+
| Heavy SPA (Slack, Figma, Linear, etc.) | `binary-headful` |
|
|
325
399
|
| Interactive session (browsing, clicking around) | `binary-headful` — most stable for long flows |
|
|
326
|
-
|
|
|
327
|
-
| Need to watch the browser remotely / headless server | `docker-headful` |
|
|
400
|
+
| Nobody available to solve a captcha | Any mode, with a best-effort `solve-captcha` step (needs `ANTHROPIC_API_KEY`) |
|
|
328
401
|
| Reverse-engineering after login | Same mode that login succeeded in |
|
|
402
|
+
| "Use my browser" / user's own logins / their open tab | `extension` + `tabId` from the `tabs` tool |
|
|
329
403
|
|
|
330
|
-
**Prefer `binary-headful`
|
|
331
|
-
|
|
332
|
-
Use `docker-headful` only when: (a) automated captcha solving is needed, (b) the user asks for it explicitly, or (c) it's a remote/headless server with no display.
|
|
404
|
+
**Prefer `binary-headful` for sites that block headless.** It runs Chrome directly on the user's machine, is stable for long sessions, and the user can solve captchas manually.
|
|
333
405
|
|
|
334
406
|
## Response handling
|
|
335
407
|
|