agentic-browser 2.5.1 → 2.6.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/AGENTS.md +1 -1
- package/dist/cli/index.d.mts +1 -1
- package/dist/cli/index.mjs +4 -3
- package/dist/index.d.mts +8 -21
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.mjs +111 -23
- package/dist/{runtime-BeD8ZcjH.mjs → runtime-BzdbtFF4.mjs} +309 -150
- package/package.json +15 -15
- /package/dist/{setup-BP0YyerC.mjs → setup-DUAjdbQ3.mjs} +0 -0
package/AGENTS.md
CHANGED
|
@@ -37,7 +37,7 @@ src/
|
|
|
37
37
|
chrome-launcher.ts — Chrome executable discovery & launch
|
|
38
38
|
transport/
|
|
39
39
|
control-api.ts — ControlApi facade (delegates to SessionManager)
|
|
40
|
-
|
|
40
|
+
auth-middleware.ts — verifyBearerAuth() token check helper
|
|
41
41
|
memory/
|
|
42
42
|
memory-service.ts — Task memory coordination
|
|
43
43
|
memory-index.ts — Search/ranking (fuzzy match + freshness + domain)
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
|
|
2
|
-
export {
|
|
2
|
+
export {}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as createCliRuntime } from "../runtime-
|
|
2
|
+
import { r as createCliRuntime } from "../runtime-BzdbtFF4.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
@@ -323,6 +323,7 @@ async function main() {
|
|
|
323
323
|
payload: JSON.parse(payloadJson)
|
|
324
324
|
});
|
|
325
325
|
console.log(JSON.stringify(result));
|
|
326
|
+
if (result.resultStatus === "failed" || result.resultStatus === "timed_out") process.exitCode = 1;
|
|
326
327
|
});
|
|
327
328
|
program.command("page:content").argument("<sessionId>").option("--mode <mode>", "title|text|html|a11y|summary", "summary").option("--selector <selector>", "optional CSS selector").option("--max-chars <n>", "max characters for text/html/a11y modes").action(async (sessionId, options) => {
|
|
328
329
|
const result = await runPageContent(runtime, {
|
|
@@ -512,14 +513,14 @@ async function main() {
|
|
|
512
513
|
await startMcpServer();
|
|
513
514
|
});
|
|
514
515
|
program.command("setup").description("Configure agentic-browser as MCP server for your AI tool").action(async () => {
|
|
515
|
-
const { runSetup } = await import("../setup-
|
|
516
|
+
const { runSetup } = await import("../setup-DUAjdbQ3.mjs");
|
|
516
517
|
await runSetup();
|
|
517
518
|
});
|
|
518
519
|
await program.parseAsync(process.argv);
|
|
519
520
|
return keepAlive;
|
|
520
521
|
}
|
|
521
522
|
main().then((keepAlive) => {
|
|
522
|
-
if (!keepAlive) process.exit(0);
|
|
523
|
+
if (!keepAlive) process.exit(process.exitCode ?? 0);
|
|
523
524
|
}).catch((error) => {
|
|
524
525
|
const message = error instanceof Error ? error.message : String(error);
|
|
525
526
|
console.error(JSON.stringify({ error: message }));
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import WebSocket
|
|
2
|
+
import WebSocket from "ws";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
|
|
5
4
|
//#region src/session/browser-controller.d.ts
|
|
6
5
|
type InteractAction = "click" | "type" | "press" | "waitFor" | "evaluate" | "scroll" | "hover" | "select" | "toggle" | "goBack" | "goForward" | "refresh" | "dialog";
|
|
7
6
|
interface InteractPayload {
|
|
@@ -306,11 +305,8 @@ type SessionEvent = z.infer<typeof SessionEventSchema>;
|
|
|
306
305
|
//#endregion
|
|
307
306
|
//#region src/lib/config.d.ts
|
|
308
307
|
interface AppConfig {
|
|
309
|
-
host: string;
|
|
310
|
-
wsPort: number;
|
|
311
308
|
dataDir: string;
|
|
312
309
|
browserExecutablePath?: string;
|
|
313
|
-
commandTimeoutMs: number;
|
|
314
310
|
cdpUrl?: string;
|
|
315
311
|
userProfileDir?: string;
|
|
316
312
|
headless?: boolean;
|
|
@@ -337,8 +333,15 @@ declare class EventStore {
|
|
|
337
333
|
private readonly filePath;
|
|
338
334
|
private readonly bufferedLines;
|
|
339
335
|
private flushTimer;
|
|
336
|
+
private pendingFlushDelayMs;
|
|
340
337
|
private flushInFlight;
|
|
341
338
|
constructor(baseDir: string);
|
|
339
|
+
/**
|
|
340
|
+
* Remove the process exit handlers and flush any buffered lines.
|
|
341
|
+
* Call when discarding a store (repeated context creation in tests/embedders
|
|
342
|
+
* would otherwise leak a listener pair per instance).
|
|
343
|
+
*/
|
|
344
|
+
dispose(): void;
|
|
342
345
|
append(event: SessionEvent): void;
|
|
343
346
|
list(sessionId: string, limit?: number): SessionEvent[];
|
|
344
347
|
clear(sessionId: string): void;
|
|
@@ -357,21 +360,6 @@ declare class SessionTokenService {
|
|
|
357
360
|
seed(sessionId: string, token: string): void;
|
|
358
361
|
}
|
|
359
362
|
//#endregion
|
|
360
|
-
//#region src/transport/ws-server.d.ts
|
|
361
|
-
interface WsServerOptions {
|
|
362
|
-
host: string;
|
|
363
|
-
port: number;
|
|
364
|
-
tokenService: SessionTokenService;
|
|
365
|
-
}
|
|
366
|
-
declare class AuthenticatedWsServer {
|
|
367
|
-
private readonly options;
|
|
368
|
-
private readonly logger;
|
|
369
|
-
private server?;
|
|
370
|
-
constructor(options: WsServerOptions);
|
|
371
|
-
start(onMessage: (socket: WebSocket$1, data: unknown) => void): void;
|
|
372
|
-
stop(): void;
|
|
373
|
-
}
|
|
374
|
-
//#endregion
|
|
375
363
|
//#region src/memory/memory-service.d.ts
|
|
376
364
|
interface MemorySearchInput {
|
|
377
365
|
taskIntent: string;
|
|
@@ -445,7 +433,6 @@ interface AppContext {
|
|
|
445
433
|
logger: Logger;
|
|
446
434
|
eventStore: EventStore;
|
|
447
435
|
tokenService: SessionTokenService;
|
|
448
|
-
wsServer: AuthenticatedWsServer;
|
|
449
436
|
memoryService: MemoryService;
|
|
450
437
|
}
|
|
451
438
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-
|
|
2
|
+
import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-BzdbtFF4.mjs";
|
|
3
3
|
export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
|
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-BzdbtFF4.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -147,11 +147,10 @@ function summarizeElements(elements) {
|
|
|
147
147
|
for (const element of elements) {
|
|
148
148
|
const role = String(element.role ?? "unknown");
|
|
149
149
|
byRole.set(role, (byRole.get(role) ?? 0) + 1);
|
|
150
|
-
if (primaryActions.length < 12 && typeof element.
|
|
150
|
+
if (primaryActions.length < 12 && typeof element.ref === "string" && element.ref && typeof element.text === "string" && element.text) primaryActions.push({
|
|
151
|
+
ref: element.ref,
|
|
151
152
|
role,
|
|
152
|
-
text: element.text
|
|
153
|
-
selector: element.selector,
|
|
154
|
-
fallbackSelectors: element.fallbackSelectors
|
|
153
|
+
text: element.text
|
|
155
154
|
});
|
|
156
155
|
}
|
|
157
156
|
return {
|
|
@@ -164,19 +163,24 @@ function compactElement(element, visibleOnly) {
|
|
|
164
163
|
if (visibleOnly) delete compact.visible;
|
|
165
164
|
delete compact.actions;
|
|
166
165
|
delete compact.tagName;
|
|
166
|
+
if (compact.ref) {
|
|
167
|
+
delete compact.selector;
|
|
168
|
+
delete compact.fallbackSelectors;
|
|
169
|
+
}
|
|
170
|
+
if (!compact.ref) delete compact.ref;
|
|
167
171
|
if (compact.enabled === true) delete compact.enabled;
|
|
168
172
|
if (!compact.text) delete compact.text;
|
|
169
173
|
if (compact.ariaLabel && compact.ariaLabel === compact.text) delete compact.ariaLabel;
|
|
170
174
|
if (compact.placeholder && compact.placeholder === compact.text) delete compact.placeholder;
|
|
171
175
|
return compact;
|
|
172
176
|
}
|
|
173
|
-
function compactInteractiveElementsResult(
|
|
174
|
-
const
|
|
177
|
+
function compactInteractiveElementsResult(elements, meta, visibleOnly) {
|
|
178
|
+
const compacted = elements.map((el) => compactElement(el, visibleOnly));
|
|
175
179
|
return {
|
|
176
|
-
elements,
|
|
177
|
-
totalFound:
|
|
178
|
-
truncated:
|
|
179
|
-
summary: summarizeElements(
|
|
180
|
+
elements: compacted,
|
|
181
|
+
totalFound: meta.totalFound,
|
|
182
|
+
truncated: meta.truncated,
|
|
183
|
+
summary: summarizeElements(compacted)
|
|
180
184
|
};
|
|
181
185
|
}
|
|
182
186
|
function compactMemoryResults(results) {
|
|
@@ -201,6 +205,47 @@ function compactMemoryResults(results) {
|
|
|
201
205
|
}) };
|
|
202
206
|
}
|
|
203
207
|
//#endregion
|
|
208
|
+
//#region src/mcp/element-refs.ts
|
|
209
|
+
var ElementRefRegistry = class {
|
|
210
|
+
bySession = /* @__PURE__ */ new Map();
|
|
211
|
+
/**
|
|
212
|
+
* Assign fresh refs to a freshly-discovered element list, replacing any prior
|
|
213
|
+
* refs for the session. Elements without a selector (nothing to act on) are
|
|
214
|
+
* still returned but get no ref.
|
|
215
|
+
*/
|
|
216
|
+
assign(sessionId, elements) {
|
|
217
|
+
const map = /* @__PURE__ */ new Map();
|
|
218
|
+
let n = 0;
|
|
219
|
+
const annotated = elements.map((el) => {
|
|
220
|
+
if (!el.selector) return {
|
|
221
|
+
...el,
|
|
222
|
+
ref: ""
|
|
223
|
+
};
|
|
224
|
+
const ref = `e${++n}`;
|
|
225
|
+
map.set(ref, {
|
|
226
|
+
selector: el.selector,
|
|
227
|
+
fallbackSelectors: el.fallbackSelectors,
|
|
228
|
+
text: el.text,
|
|
229
|
+
role: el.role
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
...el,
|
|
233
|
+
ref
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
this.bySession.set(sessionId, map);
|
|
237
|
+
return { elements: annotated };
|
|
238
|
+
}
|
|
239
|
+
/** Resolve a ref to its selector target, or undefined if unknown/stale. */
|
|
240
|
+
resolve(sessionId, ref) {
|
|
241
|
+
return this.bySession.get(sessionId)?.get(ref);
|
|
242
|
+
}
|
|
243
|
+
/** Drop a session's refs (on stop/restart) so stale handles can't resolve. */
|
|
244
|
+
clear(sessionId) {
|
|
245
|
+
this.bySession.delete(sessionId);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
//#endregion
|
|
204
249
|
//#region src/mcp/index.ts
|
|
205
250
|
function readPackageVersion() {
|
|
206
251
|
try {
|
|
@@ -241,6 +286,28 @@ function getCore() {
|
|
|
241
286
|
function genId(prefix) {
|
|
242
287
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
243
288
|
}
|
|
289
|
+
const refs = new ElementRefRegistry();
|
|
290
|
+
/**
|
|
291
|
+
* Discover interactive elements, assign fresh refs for the session, and return
|
|
292
|
+
* the compact agent-facing view (refs + role/text, no raw selectors). This is
|
|
293
|
+
* the single "what can I see and do now" snapshot both browser_get_elements and
|
|
294
|
+
* the post-interact page state share.
|
|
295
|
+
*/
|
|
296
|
+
async function snapshotElements(sessionId, opts = {}) {
|
|
297
|
+
const visibleOnly = opts.visibleOnly ?? true;
|
|
298
|
+
const result = await getCore().getInteractiveElements({
|
|
299
|
+
sessionId,
|
|
300
|
+
visibleOnly,
|
|
301
|
+
limit: opts.limit ?? 50,
|
|
302
|
+
selector: opts.selector,
|
|
303
|
+
roles: opts.roles
|
|
304
|
+
});
|
|
305
|
+
const { elements } = refs.assign(sessionId, result.elements);
|
|
306
|
+
return compactInteractiveElementsResult(elements, {
|
|
307
|
+
totalFound: result.totalFound,
|
|
308
|
+
truncated: result.truncated
|
|
309
|
+
}, visibleOnly);
|
|
310
|
+
}
|
|
244
311
|
/**
|
|
245
312
|
* Resolve a session ID — auto-starts a session if none exists.
|
|
246
313
|
* This means the LLM never has to call browser_start_session explicitly.
|
|
@@ -296,7 +363,7 @@ server.tool("browser_navigate", "Navigate the browser to a URL. A session is aut
|
|
|
296
363
|
text: JSON.stringify(compact)
|
|
297
364
|
}] };
|
|
298
365
|
});
|
|
299
|
-
server.tool("browser_interact", "Interact with a page element or perform browser actions. Element actions: \"click\", \"type\", \"press\", \"waitFor\", \"scroll\", \"hover\", \"select\", \"toggle\". Navigation actions: \"goBack\"
|
|
366
|
+
server.tool("browser_interact", "Interact with a page element or perform browser actions. Point at a target with `ref` (from browser_get_elements, e.g. \"e3\") — like clicking what you see. `selector` (CSS) still works as an escape hatch. Element actions: \"click\", \"type\", \"press\", \"waitFor\", \"scroll\", \"hover\", \"select\", \"toggle\". Navigation actions: \"goBack\", \"goForward\", \"refresh\". Dialog action: \"dialog\" (text=\"dismiss\" to cancel, value=\"...\" for prompt input). After an action that can change the page, the response includes a fresh `pageState` (new refs + summary) so you can see the result and pick your next action without a separate browser_get_elements call. A session is auto-started if needed.", {
|
|
300
367
|
action: z.enum([
|
|
301
368
|
"click",
|
|
302
369
|
"type",
|
|
@@ -311,8 +378,9 @@ server.tool("browser_interact", "Interact with a page element or perform browser
|
|
|
311
378
|
"refresh",
|
|
312
379
|
"dialog"
|
|
313
380
|
]).describe("The interaction type"),
|
|
314
|
-
|
|
315
|
-
|
|
381
|
+
ref: z.string().optional().describe("Element ref from browser_get_elements (e.g. \"e3\") — preferred over selector"),
|
|
382
|
+
selector: z.string().optional().describe("CSS selector for the target element (escape hatch)"),
|
|
383
|
+
fallbackSelectors: z.array(z.string()).optional().describe("Backup CSS selectors tried if the primary selector fails"),
|
|
316
384
|
text: z.string().optional().describe("Text to type (for \"type\"), or \"dismiss\" to dismiss a dialog (for \"dialog\")"),
|
|
317
385
|
key: z.string().optional().describe("Key to press (required for \"press\" action, e.g. \"Enter\", \"Tab\")"),
|
|
318
386
|
value: z.string().optional().describe("Option value to select (for \"select\"), or prompt text (for \"dialog\")"),
|
|
@@ -320,11 +388,25 @@ server.tool("browser_interact", "Interact with a page element or perform browser
|
|
|
320
388
|
scrollY: z.number().optional().describe("Vertical scroll delta in pixels (for \"scroll\" action, positive = down)"),
|
|
321
389
|
timeoutMs: z.number().optional().describe("Timeout in milliseconds (for \"waitFor\" action, default 4000)"),
|
|
322
390
|
sessionId: z.string().optional().describe("Session ID (auto-resolved if omitted)")
|
|
323
|
-
}, async ({ action, selector, fallbackSelectors, text, key, value, scrollX, scrollY, timeoutMs, sessionId }) => {
|
|
391
|
+
}, async ({ action, ref, selector, fallbackSelectors, text, key, value, scrollX, scrollY, timeoutMs, sessionId }) => {
|
|
324
392
|
const sid = await resolveSession(sessionId);
|
|
325
393
|
const payload = { action };
|
|
326
|
-
if (selector
|
|
327
|
-
|
|
394
|
+
if (!selector && ref) {
|
|
395
|
+
const resolved = refs.resolve(sid, ref);
|
|
396
|
+
if (!resolved) return { content: [{
|
|
397
|
+
type: "text",
|
|
398
|
+
text: JSON.stringify({
|
|
399
|
+
resultStatus: "failed",
|
|
400
|
+
resultMessage: `Unknown or stale ref "${ref}". Call browser_get_elements to refresh element refs.`,
|
|
401
|
+
errorCode: "SELECTOR_NOT_FOUND"
|
|
402
|
+
})
|
|
403
|
+
}] };
|
|
404
|
+
payload.selector = resolved.selector;
|
|
405
|
+
if (resolved.fallbackSelectors) payload.fallbackSelectors = resolved.fallbackSelectors;
|
|
406
|
+
} else {
|
|
407
|
+
if (selector) payload.selector = selector;
|
|
408
|
+
if (fallbackSelectors) payload.fallbackSelectors = fallbackSelectors;
|
|
409
|
+
}
|
|
328
410
|
if (text) payload.text = text;
|
|
329
411
|
if (key) payload.key = key;
|
|
330
412
|
if (value) payload.value = value;
|
|
@@ -347,6 +429,13 @@ server.tool("browser_interact", "Interact with a page element or perform browser
|
|
|
347
429
|
currentUrl
|
|
348
430
|
};
|
|
349
431
|
if (result.resultStatus === "failed") compact.errorCode = classifyError(result.resultMessage ?? "");
|
|
432
|
+
const pageChanging = action !== "hover" && action !== "waitFor";
|
|
433
|
+
if (result.resultStatus === "success" && pageChanging) try {
|
|
434
|
+
compact.pageState = await snapshotElements(sid, {
|
|
435
|
+
visibleOnly: true,
|
|
436
|
+
limit: 50
|
|
437
|
+
});
|
|
438
|
+
} catch {}
|
|
350
439
|
return { content: [{
|
|
351
440
|
type: "text",
|
|
352
441
|
text: JSON.stringify(compact)
|
|
@@ -374,10 +463,10 @@ server.tool("browser_get_content", "Get the current page content. Modes: \"summa
|
|
|
374
463
|
});
|
|
375
464
|
return { content: [{
|
|
376
465
|
type: "text",
|
|
377
|
-
text: JSON.stringify(compactPageContent(result))
|
|
466
|
+
text: JSON.stringify(compactPageContent(result, effectiveMaxChars))
|
|
378
467
|
}] };
|
|
379
468
|
});
|
|
380
|
-
server.tool("browser_get_elements", "Discover
|
|
469
|
+
server.tool("browser_get_elements", "Discover interactive elements on the current page (buttons, links, inputs, etc.). Each element gets a short ref like \"e1\" — pass that ref to browser_interact to act on it, the way you would point at something you see rather than describing its markup. Refs stay valid until the next browser_get_elements call or the next page-changing interaction. Actions per role: link/button/custom→click, input/textarea/contenteditable→click+type+press, select→click+select, checkbox/radio→toggle. A session is auto-started if needed.", {
|
|
381
470
|
roles: z.array(z.enum([
|
|
382
471
|
"link",
|
|
383
472
|
"button",
|
|
@@ -394,9 +483,7 @@ server.tool("browser_get_elements", "Discover all interactive elements on the cu
|
|
|
394
483
|
selector: z.string().optional().describe("CSS selector to scope element discovery to a subtree"),
|
|
395
484
|
sessionId: z.string().optional().describe("Session ID (auto-resolved if omitted)")
|
|
396
485
|
}, async ({ roles, visibleOnly, limit, selector, sessionId }) => {
|
|
397
|
-
const
|
|
398
|
-
const result = await getCore().getInteractiveElements({
|
|
399
|
-
sessionId: sid,
|
|
486
|
+
const snapshot = await snapshotElements(await resolveSession(sessionId), {
|
|
400
487
|
roles,
|
|
401
488
|
visibleOnly,
|
|
402
489
|
limit,
|
|
@@ -404,7 +491,7 @@ server.tool("browser_get_elements", "Discover all interactive elements on the cu
|
|
|
404
491
|
});
|
|
405
492
|
return { content: [{
|
|
406
493
|
type: "text",
|
|
407
|
-
text: JSON.stringify(
|
|
494
|
+
text: JSON.stringify(snapshot)
|
|
408
495
|
}] };
|
|
409
496
|
});
|
|
410
497
|
server.tool("browser_search_memory", "Search task memory for previously learned selectors, selector aliases, and interaction patterns. Results include selectorHints (proven selectors) and selectorAliases (human-readable names mapped to selectors with fallbacks). Use this before interacting with a known site to reuse proven selectors instead of rediscovering them.", {
|
|
@@ -458,6 +545,7 @@ server.tool("browser_stop_session", "Stop the browser session and terminate Chro
|
|
|
458
545
|
})
|
|
459
546
|
}] };
|
|
460
547
|
await getCore().stopSession(sid);
|
|
548
|
+
refs.clear(sid);
|
|
461
549
|
if (activeSessionId === sid) activeSessionId = void 0;
|
|
462
550
|
return { content: [{
|
|
463
551
|
type: "text",
|