crawlio-browser 1.5.1 → 1.5.3
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/dist/mcp-server/{chunk-DY3E5LV6.js → chunk-OCCTLY6F.js} +1 -1
- package/dist/mcp-server/index.js +30 -5
- package/dist/mcp-server/{init-JRSGXDVD.js → init-NEXLUHXC.js} +2 -2
- package/package.json +1 -1
- package/skills/browser-automation/SKILL.md +90 -5
- package/skills/browser-automation/reference.md +3 -3
package/dist/mcp-server/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
WS_PORT_MAX,
|
|
10
10
|
WS_RECONNECT_GRACE,
|
|
11
11
|
WS_STALE_THRESHOLD
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OCCTLY6F.js";
|
|
13
13
|
|
|
14
14
|
// src/mcp-server/index.ts
|
|
15
15
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -3709,17 +3709,37 @@ function createTools(bridge2, crawlio2) {
|
|
|
3709
3709
|
}
|
|
3710
3710
|
];
|
|
3711
3711
|
}
|
|
3712
|
+
function parseSnapshotRef(selector) {
|
|
3713
|
+
const m = /^\[ref=([a-zA-Z0-9]+)\]$/.exec(selector.trim());
|
|
3714
|
+
return m ? m[1] : null;
|
|
3715
|
+
}
|
|
3712
3716
|
async function buildSmartObject(bridge2) {
|
|
3713
|
-
const evaluate = (expression) =>
|
|
3717
|
+
const evaluate = (expression) => {
|
|
3718
|
+
const hasReturn = /(?:^|[;\n{])\s*return\s/m.test(expression);
|
|
3719
|
+
const expr = hasReturn ? `(async () => { ${expression} })()` : expression;
|
|
3720
|
+
return bridge2.send({ type: "browser_evaluate", expression: expr }, 5e3);
|
|
3721
|
+
};
|
|
3714
3722
|
const smart = {
|
|
3715
3723
|
evaluate,
|
|
3716
3724
|
click: async (selector, opts) => {
|
|
3725
|
+
const ref = parseSnapshotRef(selector);
|
|
3726
|
+
if (ref) {
|
|
3727
|
+
const result2 = await bridge2.send({ type: "browser_click", ref, button: "left", modifiers: {} }, 1e4);
|
|
3728
|
+
await new Promise((r) => setTimeout(r, opts?.settle ?? 500));
|
|
3729
|
+
return result2;
|
|
3730
|
+
}
|
|
3717
3731
|
await pollActionability(bridge2, selector);
|
|
3718
3732
|
const result = await bridge2.send({ type: "browser_click", selector, button: "left", modifiers: {} }, 1e4);
|
|
3719
3733
|
await new Promise((r) => setTimeout(r, opts?.settle ?? 500));
|
|
3720
3734
|
return result;
|
|
3721
3735
|
},
|
|
3722
3736
|
type: async (selector, text, opts) => {
|
|
3737
|
+
const ref = parseSnapshotRef(selector);
|
|
3738
|
+
if (ref) {
|
|
3739
|
+
const result2 = await bridge2.send({ type: "browser_type", ref, text, clearFirst: opts?.clearFirst ?? false, modifiers: {} }, 1e4);
|
|
3740
|
+
await new Promise((r) => setTimeout(r, opts?.settle ?? 300));
|
|
3741
|
+
return result2;
|
|
3742
|
+
}
|
|
3723
3743
|
await pollActionability(bridge2, selector);
|
|
3724
3744
|
const result = await bridge2.send({ type: "browser_type", selector, text, clearFirst: opts?.clearFirst ?? false, modifiers: {} }, 1e4);
|
|
3725
3745
|
await new Promise((r) => setTimeout(r, opts?.settle ?? 300));
|
|
@@ -3732,6 +3752,11 @@ async function buildSmartObject(bridge2) {
|
|
|
3732
3752
|
return result;
|
|
3733
3753
|
},
|
|
3734
3754
|
waitFor: async (selector, timeout) => {
|
|
3755
|
+
const ref = parseSnapshotRef(selector);
|
|
3756
|
+
if (ref) {
|
|
3757
|
+
await bridge2.send({ type: "browser_hover", ref }, timeout ?? 5e3);
|
|
3758
|
+
return { found: true, selector };
|
|
3759
|
+
}
|
|
3735
3760
|
await pollActionability(bridge2, selector, timeout ?? 5e3);
|
|
3736
3761
|
return { found: true, selector };
|
|
3737
3762
|
},
|
|
@@ -4002,8 +4027,8 @@ function createCodeModeTools(bridge2, crawlio2) {
|
|
|
4002
4027
|
"- compileRecording(session, { name, description? }) \u2014 compile RecordingSession to SKILL.md",
|
|
4003
4028
|
" Returns { skillMarkdown, name, pageCount, interactionCount }",
|
|
4004
4029
|
"- smart \u2014 auto-waiting wrappers and framework-specific data accessors:",
|
|
4005
|
-
" smart.evaluate(expr) \u2014
|
|
4006
|
-
" smart.click(selector, opts?) \u2014 poll + click + 500ms settle",
|
|
4030
|
+
" smart.evaluate(expr) \u2014 JS eval via CDP (auto-wraps if expr contains `return`)",
|
|
4031
|
+
" smart.click(selector, opts?) \u2014 poll + click + 500ms settle (accepts CSS or snapshot [ref=X])",
|
|
4007
4032
|
" smart.type(selector, text, opts?) \u2014 poll + type + 300ms settle",
|
|
4008
4033
|
" smart.navigate(url, opts?) \u2014 navigate + 1000ms settle",
|
|
4009
4034
|
" smart.waitFor(selector, timeout?) \u2014 poll until actionable",
|
|
@@ -4112,7 +4137,7 @@ function createCodeModeTools(bridge2, crawlio2) {
|
|
|
4112
4137
|
process.title = "Crawlio Agent";
|
|
4113
4138
|
var initMode = process.argv.includes("init") || process.argv.includes("--setup") || process.argv.includes("setup");
|
|
4114
4139
|
if (initMode) {
|
|
4115
|
-
const { runInit } = await import("./init-
|
|
4140
|
+
const { runInit } = await import("./init-NEXLUHXC.js");
|
|
4116
4141
|
await runInit(process.argv.slice(2));
|
|
4117
4142
|
process.exit(0);
|
|
4118
4143
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PKG_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OCCTLY6F.js";
|
|
4
4
|
|
|
5
5
|
// src/mcp-server/init.ts
|
|
6
6
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -311,7 +311,7 @@ function isAlreadyConfigured(config) {
|
|
|
311
311
|
function buildCloudflareEntry(accountId, apiToken) {
|
|
312
312
|
return {
|
|
313
313
|
command: "npx",
|
|
314
|
-
args: ["-y", "@cloudflare/mcp-server-cloudflare
|
|
314
|
+
args: ["-y", "@cloudflare/mcp-server-cloudflare", "run", accountId],
|
|
315
315
|
env: { CLOUDFLARE_API_TOKEN: apiToken }
|
|
316
316
|
};
|
|
317
317
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crawlio-browser",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "MCP server with 96 CDP-backed tools for browser automation — screenshots, DOM, network capture, framework detection, cookies, storage, session recording, performance metrics via Chrome",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/mcp-server/index.js",
|
|
@@ -36,6 +36,13 @@ Check connection status anytime:
|
|
|
36
36
|
return await bridge.send({ type: "get_connection_status" })
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Critical Rules
|
|
40
|
+
|
|
41
|
+
1. **ALWAYS `search` before `execute`** when you're unsure of a command name or its parameters. Never guess command names — they will fail.
|
|
42
|
+
2. **Return values are objects**, not primitives. Always destructure or access properties (see Return Value Shapes below).
|
|
43
|
+
3. **`close_tab` requires `tabId`** — it will error without one. Get tabId from `list_tabs` or `connect_tab`.
|
|
44
|
+
4. **`connect_tab` before any interaction** — most commands require an active tab connection.
|
|
45
|
+
|
|
39
46
|
## Core Patterns via `execute`
|
|
40
47
|
|
|
41
48
|
All browser commands run inside the `execute` tool. The code has access to:
|
|
@@ -54,10 +61,10 @@ return await smart.navigate("https://example.com")
|
|
|
54
61
|
### Screenshots
|
|
55
62
|
|
|
56
63
|
```js
|
|
57
|
-
return await
|
|
64
|
+
return await bridge.send({ type: 'take_screenshot' })
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
Returns base64 PNG.
|
|
67
|
+
Returns `{ screenshot: string }` (base64 PNG). For full-page: `bridge.send({ type: 'take_screenshot', fullPage: true })`.
|
|
61
68
|
|
|
62
69
|
### OCR Text Extraction (macOS only)
|
|
63
70
|
|
|
@@ -105,7 +112,17 @@ return await smart.snapshot()
|
|
|
105
112
|
return await smart.snapshot()
|
|
106
113
|
```
|
|
107
114
|
|
|
108
|
-
Returns the a11y tree
|
|
115
|
+
Returns `{ snapshot: string }` — the a11y tree text with `[ref=X]` labels on interactive elements. Use this to discover available elements when selectors fail.
|
|
116
|
+
|
|
117
|
+
Snapshot refs like `[ref=e3]` work directly with `smart.click`, `smart.type`, and `smart.waitFor`:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
const snap = await smart.snapshot()
|
|
121
|
+
// snap.snapshot contains: [ref=e3] button "Platform" ...
|
|
122
|
+
await smart.click('[ref=e3]') // clicks via the snapshot ref system (not CSS)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
These refs are resolved from the cached snapshot — they are NOT CSS selectors. They bypass `document.querySelector` and use CDP node resolution instead.
|
|
109
126
|
|
|
110
127
|
### Evaluate JavaScript
|
|
111
128
|
|
|
@@ -113,6 +130,15 @@ Returns the a11y tree — use this to discover available elements when selectors
|
|
|
113
130
|
return await smart.evaluate("document.title")
|
|
114
131
|
```
|
|
115
132
|
|
|
133
|
+
For multi-statement evaluation, just use `return` — it auto-wraps in an IIFE:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
return await smart.evaluate(`
|
|
137
|
+
const links = Array.from(document.querySelectorAll('a[href]'));
|
|
138
|
+
return links.map(a => ({ text: a.textContent.trim(), href: a.href }));
|
|
139
|
+
`)
|
|
140
|
+
```
|
|
141
|
+
|
|
116
142
|
### Framework Detection
|
|
117
143
|
|
|
118
144
|
```js
|
|
@@ -284,6 +310,65 @@ return await crawlio.api("GET", "/settings")
|
|
|
284
310
|
return await crawlio.api("POST", "/export", { format: "zip", destinationPath: "/tmp/site.zip" })
|
|
285
311
|
```
|
|
286
312
|
|
|
313
|
+
## Return Value Shapes
|
|
314
|
+
|
|
315
|
+
Commands return **objects**, not primitives. Always access the correct property.
|
|
316
|
+
|
|
317
|
+
### smart methods
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
// smart.snapshot() → { snapshot: string }
|
|
321
|
+
const snap = await smart.snapshot();
|
|
322
|
+
const tree = snap.snapshot; // the a11y tree text
|
|
323
|
+
// WRONG: snap.split('\n') — snap is an object, not a string
|
|
324
|
+
|
|
325
|
+
// smart.evaluate(expr) → { result: any, type: string }
|
|
326
|
+
const res = await smart.evaluate("document.title");
|
|
327
|
+
const title = res.result; // the actual value
|
|
328
|
+
const type = res.type; // "string", "number", "object", etc.
|
|
329
|
+
// WRONG: res.substring() — res is an object, not the raw value
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### bridge.send commands
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
// list_tabs → { tabs: Tab[], connectedTabId: number|null }
|
|
336
|
+
const result = await bridge.send({ type: 'list_tabs' });
|
|
337
|
+
const tabs = result.tabs; // array of tab objects
|
|
338
|
+
const connected = result.connectedTabId;
|
|
339
|
+
|
|
340
|
+
// connect_tab → { action, tabId, url, title, windowId, capturing, domainState }
|
|
341
|
+
const tab = await bridge.send({ type: 'connect_tab', tabId: 123 });
|
|
342
|
+
|
|
343
|
+
// capture_page → { url, title, framework, network, console, cookies, dom, capturedAt }
|
|
344
|
+
const page = await bridge.send({ type: 'capture_page' });
|
|
345
|
+
|
|
346
|
+
// take_screenshot → { screenshot: string } (base64 PNG)
|
|
347
|
+
const ss = await bridge.send({ type: 'take_screenshot' });
|
|
348
|
+
|
|
349
|
+
// get_cookies → { cookies: Cookie[] }
|
|
350
|
+
const result = await bridge.send({ type: 'get_cookies' });
|
|
351
|
+
const cookies = result.cookies;
|
|
352
|
+
|
|
353
|
+
// get_console_logs → { logs: LogEntry[] }
|
|
354
|
+
const result = await bridge.send({ type: 'get_console_logs' });
|
|
355
|
+
const logs = result.logs;
|
|
356
|
+
|
|
357
|
+
// close_tab → requires tabId!
|
|
358
|
+
await bridge.send({ type: 'close_tab', tabId: 123 }); // REQUIRED
|
|
359
|
+
// WRONG: bridge.send({ type: 'close_tab' }) — will error
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Network capture (start/stop pattern — no `get_network_entries`)
|
|
363
|
+
|
|
364
|
+
```js
|
|
365
|
+
await bridge.send({ type: 'start_network_capture' });
|
|
366
|
+
// ... interactions ...
|
|
367
|
+
const result = await bridge.send({ type: 'stop_network_capture' });
|
|
368
|
+
const entries = result.entries; // array of network entries
|
|
369
|
+
// WRONG: bridge.send({ type: 'get_network_entries' }) — does NOT exist
|
|
370
|
+
```
|
|
371
|
+
|
|
287
372
|
## Error Handling
|
|
288
373
|
|
|
289
374
|
| Error | Solution |
|
|
@@ -309,8 +394,8 @@ await smart.click("button[type='submit']")
|
|
|
309
394
|
await sleep(2000)
|
|
310
395
|
|
|
311
396
|
// 4. Verify navigation
|
|
312
|
-
const title = await smart.evaluate("document.title")
|
|
313
|
-
return { title, url: await smart.evaluate("location.href") }
|
|
397
|
+
const title = (await smart.evaluate("document.title")).result
|
|
398
|
+
return { title, url: (await smart.evaluate("location.href")).result }
|
|
314
399
|
```
|
|
315
400
|
|
|
316
401
|
## Reference
|
|
@@ -232,9 +232,9 @@ The `smart` object provides auto-waiting wrappers and framework-specific data:
|
|
|
232
232
|
|
|
233
233
|
| Method | Description |
|
|
234
234
|
|--------|-------------|
|
|
235
|
-
| `smart.evaluate(expr)` |
|
|
236
|
-
| `smart.click(selector, opts?)` | Poll + click + 500ms settle |
|
|
237
|
-
| `smart.type(selector, text, opts?)` | Poll + type + 300ms settle |
|
|
235
|
+
| `smart.evaluate(expr)` | JS evaluation via CDP. Auto-wraps in IIFE if `return` is used. |
|
|
236
|
+
| `smart.click(selector, opts?)` | Poll + click + 500ms settle. Accepts CSS selectors or snapshot refs (`[ref=e3]`). |
|
|
237
|
+
| `smart.type(selector, text, opts?)` | Poll + type + 300ms settle. Accepts CSS selectors or snapshot refs. |
|
|
238
238
|
| `smart.navigate(url, opts?)` | Navigate + 1000ms settle |
|
|
239
239
|
| `smart.waitFor(selector, timeout?)` | Poll until element is actionable |
|
|
240
240
|
| `smart.snapshot()` | Capture accessibility snapshot |
|