agentic-browser 2.6.2 → 2.7.1
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 +15 -0
- package/dist/cli/index.mjs +263 -16
- package/dist/index.d.mts +73 -1
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.mjs +1 -1
- package/dist/{runtime-Bo7FmRYk.mjs → runtime-CnexYwrf.mjs} +700 -94
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,10 +113,21 @@ agentic-browser agent start --user-agent "MyBot/1.0"
|
|
|
113
113
|
agentic-browser agent status
|
|
114
114
|
agentic-browser agent navigate https://example.com
|
|
115
115
|
agentic-browser agent click "#login"
|
|
116
|
+
agentic-browser agent click --text "Accept all" --frame "#sp_message_iframe_123"
|
|
117
|
+
agentic-browser agent type --frame "iframe#checkout" "#card" "4242..."
|
|
118
|
+
agentic-browser agent wait --url "*/dashboard*" --load --timeout 10000
|
|
119
|
+
agentic-browser agent wait --text "Welcome back"
|
|
120
|
+
agentic-browser agent eval "document.querySelectorAll('article').length"
|
|
121
|
+
agentic-browser agent tabs
|
|
122
|
+
agentic-browser agent tab-new https://example.org
|
|
123
|
+
agentic-browser agent tab <id-or-prefix>
|
|
124
|
+
agentic-browser agent tab-close <id-or-prefix>
|
|
116
125
|
agentic-browser agent content --mode summary
|
|
117
126
|
agentic-browser agent content --mode html --selector main
|
|
118
127
|
agentic-browser agent elements
|
|
119
128
|
agentic-browser agent elements --roles button,link --limit 20
|
|
129
|
+
agentic-browser agent elements --href /news/ --text "gta"
|
|
130
|
+
agentic-browser agent screenshot --out ./page.png --full-page
|
|
120
131
|
agentic-browser agent memory-search "navigate:example.com" --domain example.com
|
|
121
132
|
agentic-browser agent stop
|
|
122
133
|
agentic-browser agent cleanup --dry-run --max-age-days 7
|
|
@@ -156,6 +167,10 @@ MCP responses are compact — `visible`, `actions`, and `tagName` are omitted to
|
|
|
156
167
|
|
|
157
168
|
When an element lives inside an open shadow root or a same-origin iframe, discovery returns a composed locator using `>>>` to cross boundaries, for example `iframe[name="checkout"] >>> button[aria-label="Pay"]`. The same locator string works with `agent click`, `agent type`, MCP `browser_interact`, and fallback selectors.
|
|
158
169
|
|
|
170
|
+
Cross-origin iframes (consent managers, payment forms) cannot be read from the page. `agent elements` lists them under `crossOriginFrames`; pass one of those selectors as `--frame` to `elements`, `click`, `type`, `hover`, `select`, `toggle`, `wait` and `eval` to run inside it, or compose it directly: `#sp_message_iframe_123 >>> text=Accept all`. A `text=Foo` segment matches by visible text, label or placeholder (`text="Foo"` for an exact match).
|
|
171
|
+
|
|
172
|
+
`agent status`, `navigate` and interaction commands report the current `url`. `agent cookies` also handles cross-origin consent iframes and, when nothing was dismissed, explains why in `detail`.
|
|
173
|
+
|
|
159
174
|
`agent content` and MCP `browser_get_content` now default to `summary`, which returns a low-token overview with `title`, `headings`, `landmarks`, `primaryActions`, `inputs`, `alerts`, and iframe metadata. Use `a11y` when you need the deeper accessibility structure, and `text` or `html` only when you need raw content.
|
|
160
175
|
|
|
161
176
|
````
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as isConnectionError, o as ensurePrivateDir, r as createCliRuntime, s as writePrivateFileSync } from "../runtime-
|
|
2
|
+
import { a as isConnectionError, o as ensurePrivateDir, r as createCliRuntime, s as writePrivateFileSync } from "../runtime-CnexYwrf.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
@@ -45,12 +45,24 @@ async function agentStart(runtime) {
|
|
|
45
45
|
sessionId: session.sessionId
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
/** Best-effort page state; never fails the command it decorates. */
|
|
49
|
+
async function tryPageState(runtime, sessionId) {
|
|
50
|
+
try {
|
|
51
|
+
return await runtime.api.getPageState(sessionId);
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
48
56
|
async function agentStatus(runtime) {
|
|
49
57
|
const sessionId = requireSessionId(runtime);
|
|
58
|
+
const session = runtime.api.getSession(sessionId);
|
|
59
|
+
const page = session.status === "ready" ? await tryPageState(runtime, sessionId) : void 0;
|
|
50
60
|
return {
|
|
51
61
|
ok: true,
|
|
52
62
|
action: "status",
|
|
53
|
-
...
|
|
63
|
+
...session,
|
|
64
|
+
url: page?.url,
|
|
65
|
+
title: page?.title
|
|
54
66
|
};
|
|
55
67
|
}
|
|
56
68
|
async function agentStop(runtime) {
|
|
@@ -107,10 +119,12 @@ async function agentNavigate(runtime, input) {
|
|
|
107
119
|
const dismissed = await runtime.api.dismissCookieBanner(sessionId);
|
|
108
120
|
if (dismissed.dismissed) cookieBanner = dismissed;
|
|
109
121
|
} catch {}
|
|
122
|
+
const page = await tryPageState(runtime, result.sessionId);
|
|
110
123
|
return {
|
|
111
124
|
ok: true,
|
|
112
125
|
action: "navigate",
|
|
113
126
|
...result,
|
|
127
|
+
url: page?.url,
|
|
114
128
|
cookieBanner
|
|
115
129
|
};
|
|
116
130
|
}
|
|
@@ -119,10 +133,155 @@ async function agentInteract(runtime, input) {
|
|
|
119
133
|
type: "interact",
|
|
120
134
|
payload: input
|
|
121
135
|
});
|
|
136
|
+
const page = await tryPageState(runtime, result.sessionId);
|
|
122
137
|
return {
|
|
123
138
|
ok: true,
|
|
124
139
|
action: input.action,
|
|
125
|
-
...result
|
|
140
|
+
...result,
|
|
141
|
+
url: page?.url
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const INTERACTION_RESULT_PREFIX = "Interaction result: ";
|
|
145
|
+
/** Evaluates a JavaScript expression in the page (or a frame) and returns its value. */
|
|
146
|
+
async function agentEval(runtime, input) {
|
|
147
|
+
const result = await agentInteract(runtime, {
|
|
148
|
+
action: "evaluate",
|
|
149
|
+
text: input.expression,
|
|
150
|
+
frame: input.frame
|
|
151
|
+
});
|
|
152
|
+
const raw = result.resultMessage?.startsWith(INTERACTION_RESULT_PREFIX) ? result.resultMessage.slice(20) : result.resultMessage ?? "";
|
|
153
|
+
let value = raw;
|
|
154
|
+
try {
|
|
155
|
+
value = raw === "" || raw === "undefined" ? null : JSON.parse(raw);
|
|
156
|
+
} catch {}
|
|
157
|
+
return {
|
|
158
|
+
ok: true,
|
|
159
|
+
action: "eval",
|
|
160
|
+
sessionId: result.sessionId,
|
|
161
|
+
commandId: result.commandId,
|
|
162
|
+
result: value,
|
|
163
|
+
url: result.url
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/** `/re/flags` = regex, `*` = glob, otherwise substring. */
|
|
167
|
+
function matchesUrlPattern(url, pattern) {
|
|
168
|
+
const regex = /^\/(.+)\/([a-z]*)$/.exec(pattern);
|
|
169
|
+
if (regex) return new RegExp(regex[1], regex[2]).test(url);
|
|
170
|
+
if (pattern.includes("*")) {
|
|
171
|
+
const escaped = pattern.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
172
|
+
return new RegExp(`^${escaped}$`).test(url);
|
|
173
|
+
}
|
|
174
|
+
return url.includes(pattern);
|
|
175
|
+
}
|
|
176
|
+
const WAIT_POLL_MS = 200;
|
|
177
|
+
async function agentWait(runtime, input) {
|
|
178
|
+
if (!input.selector && !input.text && !input.url && !input.load) throw new Error("Nothing to wait for: pass a selector, --text, --url or --load");
|
|
179
|
+
if (input.selector && input.text) throw new Error("Pass either a selector or --text, not both");
|
|
180
|
+
const sessionId = requireSessionId(runtime);
|
|
181
|
+
await ensureReady(runtime, sessionId);
|
|
182
|
+
const timeoutMs = input.timeoutMs ?? 1e4;
|
|
183
|
+
const started = Date.now();
|
|
184
|
+
const remaining = () => timeoutMs - (Date.now() - started);
|
|
185
|
+
const pollPage = async (done, describe) => {
|
|
186
|
+
let last;
|
|
187
|
+
for (;;) {
|
|
188
|
+
last = await tryPageState(runtime, sessionId) ?? last;
|
|
189
|
+
if (last && done(last)) return last;
|
|
190
|
+
if (remaining() <= 0) throw new Error(`wait timeout after ${timeoutMs}ms: ${describe} (current url: ${last?.url ?? "unknown"}, readyState: ${last?.readyState ?? "unknown"})`);
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(WAIT_POLL_MS, remaining())));
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
if (input.url) {
|
|
195
|
+
const pattern = input.url;
|
|
196
|
+
await pollPage((page) => matchesUrlPattern(page.url, pattern), `url did not match "${pattern}"`);
|
|
197
|
+
}
|
|
198
|
+
if (input.load) await pollPage((page) => page.readyState === "complete", "page did not finish loading");
|
|
199
|
+
if (input.selector || input.text) await agentInteract(runtime, {
|
|
200
|
+
action: "waitFor",
|
|
201
|
+
selector: input.selector ?? `text=${input.text}`,
|
|
202
|
+
frame: input.frame,
|
|
203
|
+
timeoutMs: Math.max(remaining(), 1)
|
|
204
|
+
});
|
|
205
|
+
const page = await tryPageState(runtime, sessionId);
|
|
206
|
+
return {
|
|
207
|
+
ok: true,
|
|
208
|
+
action: "wait",
|
|
209
|
+
sessionId,
|
|
210
|
+
elapsedMs: Date.now() - started,
|
|
211
|
+
url: page?.url,
|
|
212
|
+
title: page?.title,
|
|
213
|
+
readyState: page?.readyState
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** Accepts a full tab id or a unique prefix of one. */
|
|
217
|
+
async function resolveTabId(runtime, sessionId, idOrPrefix) {
|
|
218
|
+
const tabs = await runtime.api.listTabs(sessionId);
|
|
219
|
+
const exact = tabs.find((tab) => tab.id === idOrPrefix);
|
|
220
|
+
if (exact) return exact.id;
|
|
221
|
+
const matches = tabs.filter((tab) => tab.id.startsWith(idOrPrefix));
|
|
222
|
+
if (matches.length === 1) return matches[0].id;
|
|
223
|
+
if (matches.length > 1) throw new Error(`Ambiguous tab id prefix: ${idOrPrefix}`);
|
|
224
|
+
throw new Error(`Tab not found: ${idOrPrefix}`);
|
|
225
|
+
}
|
|
226
|
+
async function agentTabs(runtime) {
|
|
227
|
+
const sessionId = requireSessionId(runtime);
|
|
228
|
+
await ensureReady(runtime, sessionId);
|
|
229
|
+
return {
|
|
230
|
+
ok: true,
|
|
231
|
+
action: "tabs",
|
|
232
|
+
sessionId,
|
|
233
|
+
tabs: (await runtime.api.listTabs(sessionId)).map(({ id, url, title, active }) => ({
|
|
234
|
+
id,
|
|
235
|
+
url,
|
|
236
|
+
title,
|
|
237
|
+
active
|
|
238
|
+
}))
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function agentTabSwitch(runtime, input) {
|
|
242
|
+
const sessionId = requireSessionId(runtime);
|
|
243
|
+
await ensureReady(runtime, sessionId);
|
|
244
|
+
const tab = await runtime.api.switchTab(sessionId, await resolveTabId(runtime, sessionId, input.id));
|
|
245
|
+
return {
|
|
246
|
+
ok: true,
|
|
247
|
+
action: "tab",
|
|
248
|
+
sessionId,
|
|
249
|
+
id: tab.id,
|
|
250
|
+
url: tab.url,
|
|
251
|
+
title: tab.title
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
async function agentTabNew(runtime, input) {
|
|
255
|
+
const sessionId = requireSessionId(runtime);
|
|
256
|
+
await ensureReady(runtime, sessionId);
|
|
257
|
+
const tab = await runtime.api.newTab(sessionId);
|
|
258
|
+
if (!input.url) return {
|
|
259
|
+
ok: true,
|
|
260
|
+
action: "tab-new",
|
|
261
|
+
sessionId,
|
|
262
|
+
id: tab.id,
|
|
263
|
+
url: tab.url
|
|
264
|
+
};
|
|
265
|
+
return {
|
|
266
|
+
...await agentNavigate(runtime, { url: input.url }),
|
|
267
|
+
action: "tab-new",
|
|
268
|
+
id: tab.id
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
async function agentTabClose(runtime, input) {
|
|
272
|
+
const sessionId = requireSessionId(runtime);
|
|
273
|
+
await ensureReady(runtime, sessionId);
|
|
274
|
+
const result = await runtime.api.closeTab(sessionId, await resolveTabId(runtime, sessionId, input.id));
|
|
275
|
+
return {
|
|
276
|
+
ok: true,
|
|
277
|
+
action: "tab-close",
|
|
278
|
+
sessionId,
|
|
279
|
+
closed: result.closed,
|
|
280
|
+
active: {
|
|
281
|
+
id: result.active.id,
|
|
282
|
+
url: result.active.url,
|
|
283
|
+
title: result.active.title
|
|
284
|
+
}
|
|
126
285
|
};
|
|
127
286
|
}
|
|
128
287
|
async function agentRestart(runtime) {
|
|
@@ -170,6 +329,36 @@ async function agentContent(runtime, input) {
|
|
|
170
329
|
})
|
|
171
330
|
};
|
|
172
331
|
}
|
|
332
|
+
async function agentScreenshot(runtime, input) {
|
|
333
|
+
const sessionId = requireSessionId(runtime);
|
|
334
|
+
await ensureReady(runtime, sessionId);
|
|
335
|
+
const format = input.format ?? "png";
|
|
336
|
+
const result = await runtime.api.screenshot(sessionId, {
|
|
337
|
+
format,
|
|
338
|
+
quality: input.quality,
|
|
339
|
+
fullPage: input.fullPage
|
|
340
|
+
});
|
|
341
|
+
const ext = format === "png" ? "png" : "jpg";
|
|
342
|
+
let filePath;
|
|
343
|
+
if (input.out) {
|
|
344
|
+
filePath = path.resolve(input.out);
|
|
345
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
346
|
+
} else {
|
|
347
|
+
const dir = path.join(runtime.context.config.dataDir, "screenshots");
|
|
348
|
+
ensurePrivateDir(dir);
|
|
349
|
+
filePath = path.join(dir, `${sessionId}-${Date.now()}.${ext}`);
|
|
350
|
+
}
|
|
351
|
+
const bytes = Buffer.from(result.data, "base64");
|
|
352
|
+
fs.writeFileSync(filePath, bytes, { mode: 384 });
|
|
353
|
+
return {
|
|
354
|
+
ok: true,
|
|
355
|
+
action: "screenshot",
|
|
356
|
+
sessionId,
|
|
357
|
+
path: filePath,
|
|
358
|
+
mimeType: result.mimeType,
|
|
359
|
+
bytes: bytes.length
|
|
360
|
+
};
|
|
361
|
+
}
|
|
173
362
|
async function agentElements(runtime, input) {
|
|
174
363
|
const sessionId = requireSessionId(runtime);
|
|
175
364
|
await ensureReady(runtime, sessionId);
|
|
@@ -180,7 +369,10 @@ async function agentElements(runtime, input) {
|
|
|
180
369
|
roles: input.roles,
|
|
181
370
|
visibleOnly: input.visibleOnly,
|
|
182
371
|
limit: input.limit,
|
|
183
|
-
selector: input.selector
|
|
372
|
+
selector: input.selector,
|
|
373
|
+
href: input.href,
|
|
374
|
+
text: input.text,
|
|
375
|
+
frame: input.frame
|
|
184
376
|
})
|
|
185
377
|
};
|
|
186
378
|
}
|
|
@@ -280,6 +472,14 @@ async function runMemoryStats(runtime) {
|
|
|
280
472
|
}
|
|
281
473
|
// src/cli/index.ts
|
|
282
474
|
const collect = (value, previous = []) => [...previous, value];
|
|
475
|
+
const FRAME_HELP = "iframe selector to run in (cross-origin frames supported)";
|
|
476
|
+
/** Resolves `<selector>` or `--text` (substring; `--exact` for exact match) to one locator. */
|
|
477
|
+
function resolveTarget(selector, options) {
|
|
478
|
+
if (selector && options.text) throw new Error("Pass either a selector or --text, not both");
|
|
479
|
+
if (options.text) return options.exact ? `text=${JSON.stringify(options.text)}` : `text=${options.text}`;
|
|
480
|
+
if (!selector) throw new Error("Missing target: pass a selector or --text <text>");
|
|
481
|
+
return selector;
|
|
482
|
+
}
|
|
283
483
|
async function main() {
|
|
284
484
|
const runtime = createCliRuntime();
|
|
285
485
|
const program = new Command();
|
|
@@ -375,19 +575,24 @@ async function main() {
|
|
|
375
575
|
const result = await agentNavigate(runtime, { url });
|
|
376
576
|
console.log(JSON.stringify(result));
|
|
377
577
|
});
|
|
378
|
-
agent.command("click").argument("<selector>").option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
578
|
+
agent.command("click").argument("[selector]", "CSS selector or composed locator (a >>> b)").option("--text <text>", "click the element with this visible text instead of a selector").option("--exact", "with --text: require an exact text match").option("--frame <selector>", FRAME_HELP).option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
379
579
|
const result = await agentInteract(runtime, {
|
|
380
580
|
action: "click",
|
|
381
|
-
selector,
|
|
581
|
+
selector: resolveTarget(selector, options),
|
|
582
|
+
frame: options.frame,
|
|
382
583
|
fallbackSelectors: options.fallback
|
|
383
584
|
});
|
|
384
585
|
console.log(JSON.stringify(result));
|
|
385
586
|
});
|
|
386
|
-
agent.command("type").argument("<
|
|
587
|
+
agent.command("type").argument("<selectorOrText>", "CSS selector, or the text to type when --into-text is used").argument("[text]").option("--into-text <label>", "target the field by its visible text/label/placeholder").option("--frame <selector>", FRAME_HELP).option("--fallback <selector>", "backup CSS selector", collect, []).action(async (first, second, options) => {
|
|
588
|
+
const selector = options.intoText ? `text=${options.intoText}` : first;
|
|
589
|
+
const text = options.intoText ? first : second;
|
|
590
|
+
if (text === void 0) throw new Error("Missing text to type");
|
|
387
591
|
const result = await agentInteract(runtime, {
|
|
388
592
|
action: "type",
|
|
389
593
|
selector,
|
|
390
594
|
text,
|
|
595
|
+
frame: options.frame,
|
|
391
596
|
fallbackSelectors: options.fallback
|
|
392
597
|
});
|
|
393
598
|
console.log(JSON.stringify(result));
|
|
@@ -399,35 +604,41 @@ async function main() {
|
|
|
399
604
|
});
|
|
400
605
|
console.log(JSON.stringify(result));
|
|
401
606
|
});
|
|
402
|
-
agent.command("wait").argument("<selector>").option("--timeout <ms>", "timeout in milliseconds", "
|
|
403
|
-
const result = await
|
|
404
|
-
action: "waitFor",
|
|
607
|
+
agent.command("wait").description("Wait for an element, visible text, a URL and/or the page load").argument("[selector]").option("--text <text>", "wait until an element with this text exists").option("--url <pattern>", "wait until the URL matches (substring, * glob, or /regex/)").option("--load", "wait until document.readyState is complete").option("--frame <selector>", FRAME_HELP).option("--timeout <ms>", "timeout in milliseconds", "10000").action(async (selector, options) => {
|
|
608
|
+
const result = await agentWait(runtime, {
|
|
405
609
|
selector,
|
|
610
|
+
text: options.text,
|
|
611
|
+
url: options.url,
|
|
612
|
+
load: options.load,
|
|
613
|
+
frame: options.frame,
|
|
406
614
|
timeoutMs: Number.parseInt(options.timeout, 10)
|
|
407
615
|
});
|
|
408
616
|
console.log(JSON.stringify(result));
|
|
409
617
|
});
|
|
410
|
-
agent.command("hover").argument("<selector>").option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
618
|
+
agent.command("hover").argument("[selector]").option("--text <text>", "hover the element with this visible text").option("--exact", "with --text: require an exact text match").option("--frame <selector>", FRAME_HELP).option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
411
619
|
const result = await agentInteract(runtime, {
|
|
412
620
|
action: "hover",
|
|
413
|
-
selector,
|
|
621
|
+
selector: resolveTarget(selector, options),
|
|
622
|
+
frame: options.frame,
|
|
414
623
|
fallbackSelectors: options.fallback
|
|
415
624
|
});
|
|
416
625
|
console.log(JSON.stringify(result));
|
|
417
626
|
});
|
|
418
|
-
agent.command("select").argument("<selector>").argument("<value>").option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, value, options) => {
|
|
627
|
+
agent.command("select").argument("<selector>").argument("<value>").option("--frame <selector>", FRAME_HELP).option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, value, options) => {
|
|
419
628
|
const result = await agentInteract(runtime, {
|
|
420
629
|
action: "select",
|
|
421
630
|
selector,
|
|
422
631
|
value,
|
|
632
|
+
frame: options.frame,
|
|
423
633
|
fallbackSelectors: options.fallback
|
|
424
634
|
});
|
|
425
635
|
console.log(JSON.stringify(result));
|
|
426
636
|
});
|
|
427
|
-
agent.command("toggle").argument("<selector>").option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
637
|
+
agent.command("toggle").argument("<selector>").option("--frame <selector>", FRAME_HELP).option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, options) => {
|
|
428
638
|
const result = await agentInteract(runtime, {
|
|
429
639
|
action: "toggle",
|
|
430
640
|
selector,
|
|
641
|
+
frame: options.frame,
|
|
431
642
|
fallbackSelectors: options.fallback
|
|
432
643
|
});
|
|
433
644
|
console.log(JSON.stringify(result));
|
|
@@ -466,6 +677,29 @@ async function main() {
|
|
|
466
677
|
const result = await agentDismissCookies(runtime);
|
|
467
678
|
console.log(JSON.stringify(result));
|
|
468
679
|
});
|
|
680
|
+
agent.command("eval").description("Evaluate a JavaScript expression in the page and print its JSON value").argument("<expression>", "expression; promises are awaited").option("--frame <selector>", FRAME_HELP).action(async (expression, options) => {
|
|
681
|
+
const result = await agentEval(runtime, {
|
|
682
|
+
expression,
|
|
683
|
+
frame: options.frame
|
|
684
|
+
});
|
|
685
|
+
console.log(JSON.stringify(result));
|
|
686
|
+
});
|
|
687
|
+
agent.command("tabs").description("List open tabs").action(async () => {
|
|
688
|
+
const result = await agentTabs(runtime);
|
|
689
|
+
console.log(JSON.stringify(result));
|
|
690
|
+
});
|
|
691
|
+
agent.command("tab").description("Switch the session to another tab").argument("<id>", "tab id (or unique prefix) from agent tabs").action(async (id) => {
|
|
692
|
+
const result = await agentTabSwitch(runtime, { id });
|
|
693
|
+
console.log(JSON.stringify(result));
|
|
694
|
+
});
|
|
695
|
+
agent.command("tab-new").description("Open a new tab, switch to it and optionally navigate").argument("[url]").action(async (url) => {
|
|
696
|
+
const result = await agentTabNew(runtime, { url });
|
|
697
|
+
console.log(JSON.stringify(result));
|
|
698
|
+
});
|
|
699
|
+
agent.command("tab-close").description("Close a tab (switches to another tab if it was active)").argument("<id>", "tab id (or unique prefix) from agent tabs").action(async (id) => {
|
|
700
|
+
const result = await agentTabClose(runtime, { id });
|
|
701
|
+
console.log(JSON.stringify(result));
|
|
702
|
+
});
|
|
469
703
|
agent.command("restart").action(async () => {
|
|
470
704
|
const result = await agentRestart(runtime);
|
|
471
705
|
console.log(JSON.stringify(result));
|
|
@@ -482,12 +716,25 @@ async function main() {
|
|
|
482
716
|
});
|
|
483
717
|
console.log(JSON.stringify(result));
|
|
484
718
|
});
|
|
485
|
-
agent.command("
|
|
719
|
+
agent.command("screenshot").description("Save a screenshot of the current page to a file").option("--out <path>", "output file (default: <data dir>/screenshots/<session>-<ts>.<ext>)").option("--format <format>", "png|jpeg", "png").option("--quality <n>", "jpeg quality 0-100").option("--full-page", "capture the full scrollable page").action(async (options) => {
|
|
720
|
+
if (options.format !== "png" && options.format !== "jpeg") throw new Error(`Invalid --format: ${options.format} (expected png or jpeg)`);
|
|
721
|
+
const result = await agentScreenshot(runtime, {
|
|
722
|
+
out: options.out,
|
|
723
|
+
format: options.format,
|
|
724
|
+
quality: options.quality ? Number.parseInt(options.quality, 10) : void 0,
|
|
725
|
+
fullPage: Boolean(options.fullPage)
|
|
726
|
+
});
|
|
727
|
+
console.log(JSON.stringify(result));
|
|
728
|
+
});
|
|
729
|
+
agent.command("elements").description("List interactive elements on the current page").option("--roles <roles>", "comma-separated roles filter", (v) => v.split(",")).option("--visible-only", "only visible elements", true).option("--no-visible-only", "include hidden elements").option("--limit <n>", "max elements", "50").option("--selector <selector>", "scope to CSS selector subtree").option("--href <substring>", "only links whose href contains this").option("--text <substring>", "only elements whose text/label contains this (case-insensitive)").option("--frame <selector>", FRAME_HELP).action(async (options) => {
|
|
486
730
|
const result = await agentElements(runtime, {
|
|
487
731
|
roles: options.roles,
|
|
488
732
|
visibleOnly: options.visibleOnly,
|
|
489
733
|
limit: Number.parseInt(options.limit, 10),
|
|
490
|
-
selector: options.selector
|
|
734
|
+
selector: options.selector,
|
|
735
|
+
href: options.href,
|
|
736
|
+
text: options.text,
|
|
737
|
+
frame: options.frame
|
|
491
738
|
});
|
|
492
739
|
console.log(JSON.stringify(result));
|
|
493
740
|
});
|
package/dist/index.d.mts
CHANGED
|
@@ -12,6 +12,13 @@ interface InteractPayload {
|
|
|
12
12
|
scrollX?: number;
|
|
13
13
|
scrollY?: number;
|
|
14
14
|
timeoutMs?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Locator of an iframe to run the action in. Needed for cross-origin frames,
|
|
17
|
+
* which `>>>` cannot pierce from the page. Composed selectors like
|
|
18
|
+
* `iframe#consent >>> button` are split automatically when the frame is
|
|
19
|
+
* cross-origin, so this is only required for explicit control.
|
|
20
|
+
*/
|
|
21
|
+
frame?: string;
|
|
15
22
|
}
|
|
16
23
|
type PageContentMode = "title" | "text" | "html" | "a11y" | "summary";
|
|
17
24
|
interface PageContentOptions {
|
|
@@ -48,11 +55,36 @@ interface InteractiveElementsOptions {
|
|
|
48
55
|
visibleOnly?: boolean;
|
|
49
56
|
limit?: number;
|
|
50
57
|
selector?: string;
|
|
58
|
+
/** Case-insensitive substring filter on the element's href. */
|
|
59
|
+
href?: string;
|
|
60
|
+
/** Case-insensitive substring filter on text, aria-label, placeholder, value or title. */
|
|
61
|
+
text?: string;
|
|
62
|
+
/** Discover elements inside this iframe (works for cross-origin frames). */
|
|
63
|
+
frame?: string;
|
|
64
|
+
}
|
|
65
|
+
/** An iframe whose content discovery could not enter from the page context. */
|
|
66
|
+
interface CrossOriginFrameInfo {
|
|
67
|
+
selector: string;
|
|
68
|
+
title?: string;
|
|
69
|
+
src?: string;
|
|
51
70
|
}
|
|
52
71
|
interface InteractiveElementsResult {
|
|
53
72
|
elements: InteractiveElement[];
|
|
54
73
|
totalFound: number;
|
|
55
74
|
truncated: boolean;
|
|
75
|
+
/** Cross-origin iframes skipped by discovery — pass one as `frame` to look inside. */
|
|
76
|
+
crossOriginFrames?: CrossOriginFrameInfo[];
|
|
77
|
+
}
|
|
78
|
+
interface PageState {
|
|
79
|
+
url: string;
|
|
80
|
+
title: string;
|
|
81
|
+
readyState: string;
|
|
82
|
+
}
|
|
83
|
+
interface TabInfo {
|
|
84
|
+
id: string;
|
|
85
|
+
url: string;
|
|
86
|
+
title: string;
|
|
87
|
+
targetWsUrl: string;
|
|
56
88
|
}
|
|
57
89
|
interface LaunchOptions {
|
|
58
90
|
executablePath?: string;
|
|
@@ -62,7 +94,7 @@ interface LaunchOptions {
|
|
|
62
94
|
}
|
|
63
95
|
interface DismissCookieBannerResult {
|
|
64
96
|
dismissed: boolean;
|
|
65
|
-
method?: "a11y" | "selector" | "text";
|
|
97
|
+
method?: "a11y" | "selector" | "text" | "iframe" | "iframe-text";
|
|
66
98
|
detail?: string;
|
|
67
99
|
}
|
|
68
100
|
type ErrorCode = "SELECTOR_NOT_FOUND" | "ELEMENT_COVERED" | "ELEMENT_NOT_EDITABLE" | "NAVIGATION_TIMEOUT" | "NAVIGATION_FAILED" | "CDP_DISCONNECTED" | "SESSION_DEAD" | "DIALOG_BLOCKING" | "TIMEOUT" | "UNKNOWN";
|
|
@@ -95,6 +127,11 @@ interface BrowserController {
|
|
|
95
127
|
dismissCookieBanner(targetWsUrl: string): Promise<DismissCookieBannerResult>;
|
|
96
128
|
screenshot(targetWsUrl: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
97
129
|
getCurrentUrl(targetWsUrl: string): Promise<string>;
|
|
130
|
+
getPageState(targetWsUrl: string): Promise<PageState>;
|
|
131
|
+
listTabs(cdpUrl: string): Promise<TabInfo[]>;
|
|
132
|
+
openTab(cdpUrl: string): Promise<TabInfo>;
|
|
133
|
+
activateTab(cdpUrl: string, tabId: string): Promise<void>;
|
|
134
|
+
closeTab(cdpUrl: string, tabId: string): Promise<void>;
|
|
98
135
|
terminate(pid: number): void;
|
|
99
136
|
closeConnection?(targetWsUrl: string): void;
|
|
100
137
|
dispose?(): void;
|
|
@@ -462,6 +499,18 @@ declare class SessionManager {
|
|
|
462
499
|
getInteractiveElements(sessionId: string, options: InteractiveElementsOptions): Promise<InteractiveElementsResult>;
|
|
463
500
|
screenshot(sessionId: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
464
501
|
getCurrentUrl(sessionId: string): Promise<string>;
|
|
502
|
+
getPageState(sessionId: string): Promise<PageState>;
|
|
503
|
+
listTabs(sessionId: string): Promise<Array<TabInfo & {
|
|
504
|
+
active: boolean;
|
|
505
|
+
}>>;
|
|
506
|
+
/** Points the session at another tab; later commands run there. */
|
|
507
|
+
switchTab(sessionId: string, tabId: string): Promise<TabInfo>;
|
|
508
|
+
newTab(sessionId: string): Promise<TabInfo>;
|
|
509
|
+
/** Closes a tab; closing the active one moves the session to a remaining tab. */
|
|
510
|
+
closeTab(sessionId: string, tabId: string): Promise<{
|
|
511
|
+
closed: string;
|
|
512
|
+
active: TabInfo;
|
|
513
|
+
}>;
|
|
465
514
|
dismissCookieBanner(sessionId: string): Promise<DismissCookieBannerResult>;
|
|
466
515
|
setStatus(status: SessionStatus, reason: string): Session;
|
|
467
516
|
terminateSession(sessionId: string): Promise<Session>;
|
|
@@ -622,6 +671,16 @@ declare class ControlApi {
|
|
|
622
671
|
dismissCookieBanner(sessionId: string): Promise<DismissCookieBannerResult>;
|
|
623
672
|
screenshot(sessionId: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
624
673
|
getCurrentUrl(sessionId: string): Promise<string>;
|
|
674
|
+
getPageState(sessionId: string): Promise<PageState>;
|
|
675
|
+
listTabs(sessionId: string): Promise<(TabInfo & {
|
|
676
|
+
active: boolean;
|
|
677
|
+
})[]>;
|
|
678
|
+
switchTab(sessionId: string, tabId: string): Promise<TabInfo>;
|
|
679
|
+
newTab(sessionId: string): Promise<TabInfo>;
|
|
680
|
+
closeTab(sessionId: string, tabId: string): Promise<{
|
|
681
|
+
closed: string;
|
|
682
|
+
active: TabInfo;
|
|
683
|
+
}>;
|
|
625
684
|
listEvents(sessionId: string, limit?: number): {
|
|
626
685
|
events: {
|
|
627
686
|
eventId: string;
|
|
@@ -791,10 +850,23 @@ export declare class AgenticBrowserCore {
|
|
|
791
850
|
visibleOnly?: boolean;
|
|
792
851
|
limit?: number;
|
|
793
852
|
selector?: string;
|
|
853
|
+
href?: string;
|
|
854
|
+
text?: string;
|
|
855
|
+
frame?: string;
|
|
794
856
|
}): Promise<InteractiveElementsResult>;
|
|
795
857
|
dismissCookieBanner(sessionId: string): Promise<DismissCookieBannerResult>;
|
|
796
858
|
screenshot(sessionId: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
797
859
|
getCurrentUrl(sessionId: string): Promise<string>;
|
|
860
|
+
getPageState(sessionId: string): Promise<PageState>;
|
|
861
|
+
listTabs(sessionId: string): Promise<(TabInfo & {
|
|
862
|
+
active: boolean;
|
|
863
|
+
})[]>;
|
|
864
|
+
switchTab(sessionId: string, tabId: string): Promise<TabInfo>;
|
|
865
|
+
newTab(sessionId: string): Promise<TabInfo>;
|
|
866
|
+
closeTab(sessionId: string, tabId: string): Promise<{
|
|
867
|
+
closed: string;
|
|
868
|
+
active: TabInfo;
|
|
869
|
+
}>;
|
|
798
870
|
restartSession(sessionId: string): Promise<{
|
|
799
871
|
sessionId: string;
|
|
800
872
|
status: "disconnected" | "failed" | "ready" | "starting" | "terminated";
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { t as AgenticBrowserCore, n as createAgenticBrowserCore, i as createMockAgenticBrowserCore } from "./runtime-
|
|
2
|
+
export { t as AgenticBrowserCore, n as createAgenticBrowserCore, i as createMockAgenticBrowserCore } from "./runtime-CnexYwrf.mjs";
|
package/dist/mcp/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as createAgenticBrowserCore } from "../runtime-
|
|
2
|
+
import { n as createAgenticBrowserCore } from "../runtime-CnexYwrf.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|