agentic-browser 2.6.2 → 2.7.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 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
  ````
@@ -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-Bo7FmRYk.mjs";
2
+ import { a as isConnectionError, o as ensurePrivateDir, r as createCliRuntime, s as writePrivateFileSync } from "../runtime-CfQ0xGAm.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
- ...runtime.api.getSession(sessionId)
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("<selector>").argument("<text>").option("--fallback <selector>", "backup CSS selector", collect, []).action(async (selector, text, options) => {
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", "2000").action(async (selector, options) => {
403
- const result = await agentInteract(runtime, {
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("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").action(async (options) => {
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-Bo7FmRYk.mjs";
2
+ export { t as AgenticBrowserCore, n as createAgenticBrowserCore, i as createMockAgenticBrowserCore } from "./runtime-CfQ0xGAm.mjs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createAgenticBrowserCore } from "../runtime-Bo7FmRYk.mjs";
2
+ import { n as createAgenticBrowserCore } from "../runtime-CfQ0xGAm.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";