agentic-browser 2.6.1 → 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/AGENTS.md +25 -19
- package/README.md +16 -1
- package/dist/cli/index.mjs +300 -54
- package/dist/index.d.mts +158 -99
- package/dist/index.mjs +1 -2
- package/dist/mcp/index.d.mts +1 -4
- package/dist/mcp/index.mjs +47 -18
- package/dist/{runtime-BzdbtFF4.mjs → runtime-CfQ0xGAm.mjs} +1055 -207
- package/dist/{setup-DUAjdbQ3.mjs → setup-DwFaZOPf.mjs} +13 -6
- package/package.json +21 -19
package/dist/cli/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as createCliRuntime } from "../runtime-
|
|
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";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
|
|
7
|
+
// src/cli/commands/agent.ts
|
|
8
8
|
const STATE_FILE_NAME = "agent-state.json";
|
|
9
9
|
function stateFilePath(runtime) {
|
|
10
10
|
return path.join(runtime.context.config.dataDir, STATE_FILE_NAME);
|
|
@@ -20,8 +20,8 @@ function loadState(runtime) {
|
|
|
20
20
|
}
|
|
21
21
|
function saveState(runtime, state) {
|
|
22
22
|
const dir = path.dirname(stateFilePath(runtime));
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
ensurePrivateDir(dir);
|
|
24
|
+
writePrivateFileSync(stateFilePath(runtime), JSON.stringify(state, null, 2));
|
|
25
25
|
}
|
|
26
26
|
function requireSessionId(runtime) {
|
|
27
27
|
const state = loadState(runtime);
|
|
@@ -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) {
|
|
@@ -63,6 +75,12 @@ async function agentStop(runtime) {
|
|
|
63
75
|
sessionId
|
|
64
76
|
};
|
|
65
77
|
}
|
|
78
|
+
/** Only connection-level failures justify a restart + re-run. */
|
|
79
|
+
function isRetryableFailure(message) {
|
|
80
|
+
if (isConnectionError(message)) return true;
|
|
81
|
+
const lower = message.toLowerCase();
|
|
82
|
+
return lower.includes("session") && (lower.includes("dead") || lower.includes("terminated"));
|
|
83
|
+
}
|
|
66
84
|
async function executeAgentCommand(runtime, input) {
|
|
67
85
|
const sessionId = requireSessionId(runtime);
|
|
68
86
|
await ensureReady(runtime, sessionId);
|
|
@@ -71,16 +89,24 @@ async function executeAgentCommand(runtime, input) {
|
|
|
71
89
|
type: input.type,
|
|
72
90
|
payload: input.payload
|
|
73
91
|
});
|
|
74
|
-
|
|
75
|
-
const first = await attempt();
|
|
76
|
-
if (first.resultStatus === "failed") throw new Error(first.resultMessage ?? "Command failed");
|
|
77
|
-
return first;
|
|
78
|
-
} catch {
|
|
92
|
+
const retryAfterRestart = async () => {
|
|
79
93
|
await runtime.api.restartSession(sessionId);
|
|
80
94
|
const second = await attempt();
|
|
81
95
|
if (second.resultStatus === "failed") throw new Error(second.resultMessage ?? "Command failed after retry");
|
|
82
96
|
return second;
|
|
97
|
+
};
|
|
98
|
+
const noRetry = input.type === "restart" || input.type === "terminate";
|
|
99
|
+
let first;
|
|
100
|
+
try {
|
|
101
|
+
first = await attempt();
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (noRetry || !isRetryableFailure(error.message)) throw error;
|
|
104
|
+
return await retryAfterRestart();
|
|
83
105
|
}
|
|
106
|
+
if (first.resultStatus !== "failed") return first;
|
|
107
|
+
const message = first.resultMessage ?? "Command failed";
|
|
108
|
+
if (noRetry || !isRetryableFailure(message)) throw new Error(message);
|
|
109
|
+
return await retryAfterRestart();
|
|
84
110
|
}
|
|
85
111
|
async function agentNavigate(runtime, input) {
|
|
86
112
|
const result = await executeAgentCommand(runtime, {
|
|
@@ -93,10 +119,12 @@ async function agentNavigate(runtime, input) {
|
|
|
93
119
|
const dismissed = await runtime.api.dismissCookieBanner(sessionId);
|
|
94
120
|
if (dismissed.dismissed) cookieBanner = dismissed;
|
|
95
121
|
} catch {}
|
|
122
|
+
const page = await tryPageState(runtime, result.sessionId);
|
|
96
123
|
return {
|
|
97
124
|
ok: true,
|
|
98
125
|
action: "navigate",
|
|
99
126
|
...result,
|
|
127
|
+
url: page?.url,
|
|
100
128
|
cookieBanner
|
|
101
129
|
};
|
|
102
130
|
}
|
|
@@ -105,10 +133,155 @@ async function agentInteract(runtime, input) {
|
|
|
105
133
|
type: "interact",
|
|
106
134
|
payload: input
|
|
107
135
|
});
|
|
136
|
+
const page = await tryPageState(runtime, result.sessionId);
|
|
108
137
|
return {
|
|
109
138
|
ok: true,
|
|
110
139
|
action: input.action,
|
|
111
|
-
...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
|
+
}
|
|
112
285
|
};
|
|
113
286
|
}
|
|
114
287
|
async function agentRestart(runtime) {
|
|
@@ -156,6 +329,36 @@ async function agentContent(runtime, input) {
|
|
|
156
329
|
})
|
|
157
330
|
};
|
|
158
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
|
+
}
|
|
159
362
|
async function agentElements(runtime, input) {
|
|
160
363
|
const sessionId = requireSessionId(runtime);
|
|
161
364
|
await ensureReady(runtime, sessionId);
|
|
@@ -166,7 +369,10 @@ async function agentElements(runtime, input) {
|
|
|
166
369
|
roles: input.roles,
|
|
167
370
|
visibleOnly: input.visibleOnly,
|
|
168
371
|
limit: input.limit,
|
|
169
|
-
selector: input.selector
|
|
372
|
+
selector: input.selector,
|
|
373
|
+
href: input.href,
|
|
374
|
+
text: input.text,
|
|
375
|
+
frame: input.frame
|
|
170
376
|
})
|
|
171
377
|
};
|
|
172
378
|
}
|
|
@@ -184,16 +390,14 @@ async function agentCleanup(runtime, input) {
|
|
|
184
390
|
...runtime.api.cleanupSessions(input)
|
|
185
391
|
};
|
|
186
392
|
}
|
|
187
|
-
|
|
188
|
-
//#region src/cli/output/result-formatter.ts
|
|
393
|
+
// src/cli/output/result-formatter.ts
|
|
189
394
|
function formatResult(resultStatus, message) {
|
|
190
395
|
return {
|
|
191
396
|
resultStatus,
|
|
192
397
|
message
|
|
193
398
|
};
|
|
194
399
|
}
|
|
195
|
-
|
|
196
|
-
//#region src/cli/commands/session-start.ts
|
|
400
|
+
// src/cli/commands/session-start.ts
|
|
197
401
|
async function runSessionStart(runtime, input) {
|
|
198
402
|
const session = await runtime.api.createSession(input);
|
|
199
403
|
return {
|
|
@@ -201,29 +405,24 @@ async function runSessionStart(runtime, input) {
|
|
|
201
405
|
sessionId: session.sessionId
|
|
202
406
|
};
|
|
203
407
|
}
|
|
204
|
-
|
|
205
|
-
//#region src/cli/commands/session-status.ts
|
|
408
|
+
// src/cli/commands/session-status.ts
|
|
206
409
|
async function runSessionStatus(runtime, input) {
|
|
207
410
|
return runtime.api.getSession(input.sessionId);
|
|
208
411
|
}
|
|
209
|
-
|
|
210
|
-
//#region src/cli/commands/session-stop.ts
|
|
412
|
+
// src/cli/commands/session-stop.ts
|
|
211
413
|
async function runSessionStop(runtime, input) {
|
|
212
414
|
await runtime.api.terminateSession(input.sessionId);
|
|
213
415
|
return { terminated: true };
|
|
214
416
|
}
|
|
215
|
-
|
|
216
|
-
//#region src/cli/commands/session-cleanup.ts
|
|
417
|
+
// src/cli/commands/session-cleanup.ts
|
|
217
418
|
async function runSessionCleanup(runtime, input) {
|
|
218
419
|
return runtime.api.cleanupSessions(input);
|
|
219
420
|
}
|
|
220
|
-
|
|
221
|
-
//#region src/cli/commands/session-restart.ts
|
|
421
|
+
// src/cli/commands/session-restart.ts
|
|
222
422
|
async function runSessionRestart(runtime, input) {
|
|
223
423
|
return await runtime.api.restartSession(input.sessionId);
|
|
224
424
|
}
|
|
225
|
-
|
|
226
|
-
//#region src/cli/commands/session-auth.ts
|
|
425
|
+
// src/cli/commands/session-auth.ts
|
|
227
426
|
async function runSessionAuth(runtime, input) {
|
|
228
427
|
const token = runtime.api.rotateSessionToken(input.sessionId);
|
|
229
428
|
return {
|
|
@@ -231,8 +430,7 @@ async function runSessionAuth(runtime, input) {
|
|
|
231
430
|
valid: runtime.context.tokenService.validate(input.sessionId, token)
|
|
232
431
|
};
|
|
233
432
|
}
|
|
234
|
-
|
|
235
|
-
//#region src/cli/commands/command-run.ts
|
|
433
|
+
// src/cli/commands/command-run.ts
|
|
236
434
|
async function runCommand(runtime, input) {
|
|
237
435
|
if (!input.sessionId) throw new Error("No active session. Start or restart a session first.");
|
|
238
436
|
try {
|
|
@@ -245,8 +443,7 @@ async function runCommand(runtime, input) {
|
|
|
245
443
|
throw new Error(`Command rejected: ${error.message}`, { cause: error });
|
|
246
444
|
}
|
|
247
445
|
}
|
|
248
|
-
|
|
249
|
-
//#region src/cli/commands/page-content.ts
|
|
446
|
+
// src/cli/commands/page-content.ts
|
|
250
447
|
async function runPageContent(runtime, input) {
|
|
251
448
|
return await runtime.api.getContent(input.sessionId, {
|
|
252
449
|
mode: input.mode,
|
|
@@ -254,32 +451,35 @@ async function runPageContent(runtime, input) {
|
|
|
254
451
|
maxChars: input.maxChars
|
|
255
452
|
});
|
|
256
453
|
}
|
|
257
|
-
|
|
258
|
-
//#region src/cli/commands/memory-search.ts
|
|
454
|
+
// src/cli/commands/memory-search.ts
|
|
259
455
|
async function runMemorySearch(runtime, input) {
|
|
260
456
|
if (!input.taskIntent || !input.taskIntent.trim()) throw new Error("taskIntent is required");
|
|
261
457
|
return runtime.api.searchMemory(input);
|
|
262
458
|
}
|
|
263
|
-
|
|
264
|
-
//#region src/cli/commands/memory-inspect.ts
|
|
459
|
+
// src/cli/commands/memory-inspect.ts
|
|
265
460
|
async function runMemoryInspect(runtime, input) {
|
|
266
461
|
if (!input.insightId || !input.insightId.trim()) throw new Error("insightId is required");
|
|
267
462
|
return runtime.api.inspectMemory(input.insightId);
|
|
268
463
|
}
|
|
269
|
-
|
|
270
|
-
//#region src/cli/commands/memory-verify.ts
|
|
464
|
+
// src/cli/commands/memory-verify.ts
|
|
271
465
|
async function runMemoryVerify(runtime, input) {
|
|
272
466
|
if (!input.insightId || !input.insightId.trim()) throw new Error("insightId is required");
|
|
273
467
|
return runtime.api.verifyMemory(input.insightId);
|
|
274
468
|
}
|
|
275
|
-
|
|
276
|
-
//#region src/cli/commands/memory-stats.ts
|
|
469
|
+
// src/cli/commands/memory-stats.ts
|
|
277
470
|
async function runMemoryStats(runtime) {
|
|
278
471
|
return runtime.api.memoryStats();
|
|
279
472
|
}
|
|
280
|
-
|
|
281
|
-
//#region src/cli/index.ts
|
|
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
|
});
|
|
@@ -513,7 +760,7 @@ async function main() {
|
|
|
513
760
|
await startMcpServer();
|
|
514
761
|
});
|
|
515
762
|
program.command("setup").description("Configure agentic-browser as MCP server for your AI tool").action(async () => {
|
|
516
|
-
const { runSetup } = await import("../setup-
|
|
763
|
+
const { runSetup } = await import("../setup-DwFaZOPf.mjs");
|
|
517
764
|
await runSetup();
|
|
518
765
|
});
|
|
519
766
|
await program.parseAsync(process.argv);
|
|
@@ -526,5 +773,4 @@ main().then((keepAlive) => {
|
|
|
526
773
|
console.error(JSON.stringify({ error: message }));
|
|
527
774
|
process.exit(1);
|
|
528
775
|
});
|
|
529
|
-
//#endregion
|
|
530
776
|
export {};
|