@elizaos/plugin-browser 2.0.3-beta.2 → 2.0.3-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/browser.d.ts.map +1 -1
- package/dist/actions/browser.js +264 -7
- package/dist/actions/browser.js.map +1 -1
- package/dist/actions/wait-for-url-predicate.d.ts +34 -0
- package/dist/actions/wait-for-url-predicate.d.ts.map +1 -0
- package/dist/actions/wait-for-url-predicate.js +33 -0
- package/dist/actions/wait-for-url-predicate.js.map +1 -0
- package/dist/actions/wait-for-url.d.ts +64 -0
- package/dist/actions/wait-for-url.d.ts.map +1 -0
- package/dist/actions/wait-for-url.js +89 -0
- package/dist/actions/wait-for-url.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -8
- package/dist/index.js.map +1 -1
- package/dist/parity/browser-matrix.d.ts +45 -0
- package/dist/parity/browser-matrix.d.ts.map +1 -0
- package/dist/parity/browser-matrix.js +361 -0
- package/dist/parity/browser-matrix.js.map +1 -0
- package/dist/parity/index.d.ts +5 -0
- package/dist/parity/index.d.ts.map +1 -0
- package/dist/parity/index.js +13 -0
- package/dist/parity/index.js.map +1 -0
- package/dist/routes/workspace.d.ts.map +1 -1
- package/dist/routes/workspace.js +42 -2
- package/dist/routes/workspace.js.map +1 -1
- package/dist/workspace/browser-capture.d.ts.map +1 -1
- package/dist/workspace/browser-capture.js +33 -1
- package/dist/workspace/browser-capture.js.map +1 -1
- package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-desktop.js +19 -5
- package/dist/workspace/browser-workspace-desktop.js.map +1 -1
- package/dist/workspace/browser-workspace-errors.d.ts +62 -0
- package/dist/workspace/browser-workspace-errors.d.ts.map +1 -0
- package/dist/workspace/browser-workspace-errors.js +69 -0
- package/dist/workspace/browser-workspace-errors.js.map +1 -0
- package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-helpers.js +29 -8
- package/dist/workspace/browser-workspace-helpers.js.map +1 -1
- package/dist/workspace/browser-workspace-web.d.ts.map +1 -1
- package/dist/workspace/browser-workspace-web.js +20 -6
- package/dist/workspace/browser-workspace-web.js.map +1 -1
- package/dist/workspace/index.d.ts +1 -0
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +1 -0
- package/dist/workspace/index.js.map +1 -1
- package/package.json +5 -4
- package/registry-entry.json +75 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/actions/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/actions/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAMP,MAAM,eAAe,CAAC;AAkBvB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAqlBnC,eAAO,MAAM,aAAa,EAAE,MA4W3B,CAAC"}
|
package/dist/actions/browser.js
CHANGED
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
executeBrowserWorkspaceCommand,
|
|
7
7
|
getBrowserWorkspaceMode
|
|
8
8
|
} from "../workspace/browser-workspace.js";
|
|
9
|
+
import {
|
|
10
|
+
WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS,
|
|
11
|
+
WAIT_FOR_URL_DEFAULT_TIMEOUT_MS,
|
|
12
|
+
waitForUrl
|
|
13
|
+
} from "./wait-for-url.js";
|
|
9
14
|
function getMessageText(message) {
|
|
10
15
|
const content = message?.content;
|
|
11
16
|
if (typeof content === "string") {
|
|
@@ -22,6 +27,9 @@ function inferBrowserSubaction(params, messageText) {
|
|
|
22
27
|
if (normalizedAction === "autofill-login" || params?.subaction === "autofill-login") {
|
|
23
28
|
return "autofill-login";
|
|
24
29
|
}
|
|
30
|
+
if (normalizedAction === "wait-for-url" || params?.subaction === "wait-for-url") {
|
|
31
|
+
return "wait-for-url";
|
|
32
|
+
}
|
|
25
33
|
const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);
|
|
26
34
|
if (legacySubaction) {
|
|
27
35
|
return legacySubaction;
|
|
@@ -60,6 +68,9 @@ function normalizeBrowserAction(action) {
|
|
|
60
68
|
return "cursor-hide";
|
|
61
69
|
case "autofill_login":
|
|
62
70
|
return "autofill-login";
|
|
71
|
+
case "wait_for_url":
|
|
72
|
+
case "wait-for-url":
|
|
73
|
+
return "wait-for-url";
|
|
63
74
|
default:
|
|
64
75
|
return action;
|
|
65
76
|
}
|
|
@@ -77,6 +88,7 @@ function normalizeLegacyBrowserAction(action) {
|
|
|
77
88
|
case "switch_tab":
|
|
78
89
|
return "tab";
|
|
79
90
|
case "autofill-login":
|
|
91
|
+
case "wait-for-url":
|
|
80
92
|
return void 0;
|
|
81
93
|
case void 0:
|
|
82
94
|
return void 0;
|
|
@@ -129,6 +141,193 @@ ${serialized}`;
|
|
|
129
141
|
}
|
|
130
142
|
return `Browser ${command.subaction} completed in ${result.mode} mode.`;
|
|
131
143
|
}
|
|
144
|
+
function browserProgressRationale(command, params, messageText) {
|
|
145
|
+
const explicit = params?.rationale?.trim();
|
|
146
|
+
if (explicit) return explicit;
|
|
147
|
+
switch (command.subaction) {
|
|
148
|
+
case "open":
|
|
149
|
+
case "navigate":
|
|
150
|
+
return command.url ? `open ${command.url}` : "open requested page";
|
|
151
|
+
case "click":
|
|
152
|
+
case "realistic-click":
|
|
153
|
+
return command.selector ? `click ${command.selector}` : "click requested target";
|
|
154
|
+
case "type":
|
|
155
|
+
case "realistic-fill":
|
|
156
|
+
case "realistic-type":
|
|
157
|
+
return command.selector ? `fill ${command.selector}` : "type requested text";
|
|
158
|
+
case "press":
|
|
159
|
+
case "realistic-press":
|
|
160
|
+
return command.key ? `press ${command.key}` : "press requested key";
|
|
161
|
+
case "tab":
|
|
162
|
+
return command.tabAction ? `${command.tabAction} browser tab` : "manage browser tabs";
|
|
163
|
+
case "wait":
|
|
164
|
+
return command.selector ? `wait for ${command.selector}` : "wait for browser state";
|
|
165
|
+
case "state":
|
|
166
|
+
return messageText.trim() || "read browser state";
|
|
167
|
+
default:
|
|
168
|
+
return `run browser ${command.subaction}`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function buildBrowserStepProgressContent(command, params, messageText, success, error) {
|
|
172
|
+
const rationale = error ? `failed: ${error}` : browserProgressRationale(command, params, messageText);
|
|
173
|
+
return {
|
|
174
|
+
text: `Step 1: ${command.subaction} \u2014 ${rationale}`,
|
|
175
|
+
source: "action_progress",
|
|
176
|
+
merge: "replace",
|
|
177
|
+
metadata: {
|
|
178
|
+
transient: true,
|
|
179
|
+
compactProgress: true,
|
|
180
|
+
progress: {
|
|
181
|
+
source: "browser",
|
|
182
|
+
actionName: "BROWSER",
|
|
183
|
+
step: 1,
|
|
184
|
+
kind: command.subaction,
|
|
185
|
+
rationale,
|
|
186
|
+
success,
|
|
187
|
+
error
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
async function emitBrowserStepProgress(callback, command, params, messageText, success, error) {
|
|
193
|
+
if (params?.streamProgress !== true || !callback) return;
|
|
194
|
+
try {
|
|
195
|
+
await callback(
|
|
196
|
+
buildBrowserStepProgressContent(
|
|
197
|
+
command,
|
|
198
|
+
params,
|
|
199
|
+
messageText,
|
|
200
|
+
success,
|
|
201
|
+
error
|
|
202
|
+
),
|
|
203
|
+
"BROWSER"
|
|
204
|
+
);
|
|
205
|
+
} catch (callbackError) {
|
|
206
|
+
logger.warn(
|
|
207
|
+
{
|
|
208
|
+
src: "plugin:browser",
|
|
209
|
+
action: command.subaction,
|
|
210
|
+
error: callbackError instanceof Error ? callbackError.message : String(callbackError)
|
|
211
|
+
},
|
|
212
|
+
"Failed to emit browser progress callback"
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function currentUrlFromResult(result) {
|
|
217
|
+
if (result.tab?.url) {
|
|
218
|
+
return result.tab.url;
|
|
219
|
+
}
|
|
220
|
+
if (typeof result.value === "string" && result.value.trim()) {
|
|
221
|
+
return result.value.trim();
|
|
222
|
+
}
|
|
223
|
+
if (Array.isArray(result.tabs)) {
|
|
224
|
+
const visible = result.tabs.find((tab) => tab.visible) ?? result.tabs[0];
|
|
225
|
+
if (visible?.url) {
|
|
226
|
+
return visible.url;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
async function readCurrentBrowserUrl(browserService, target, tabId) {
|
|
232
|
+
const run = async (command) => browserService ? browserService.execute(command, target) : executeBrowserWorkspaceCommand(command);
|
|
233
|
+
try {
|
|
234
|
+
const got = await run({
|
|
235
|
+
subaction: "get",
|
|
236
|
+
getMode: "url",
|
|
237
|
+
id: tabId
|
|
238
|
+
});
|
|
239
|
+
const url = currentUrlFromResult(got);
|
|
240
|
+
if (url) {
|
|
241
|
+
return url;
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
logger.debug(
|
|
245
|
+
`[BROWSER] wait_for_url get-url poll failed, falling back to state: ${error instanceof Error ? error.message : String(error)}`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const state = await run({ subaction: "state", id: tabId });
|
|
250
|
+
return currentUrlFromResult(state);
|
|
251
|
+
} catch (error) {
|
|
252
|
+
logger.debug(
|
|
253
|
+
`[BROWSER] wait_for_url state poll could not read URL: ${error instanceof Error ? error.message : String(error)}`
|
|
254
|
+
);
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function executeBrowserWaitForUrl(runtime, params, messageText, callback) {
|
|
259
|
+
const pattern = (params?.pattern ?? "").trim();
|
|
260
|
+
if (!pattern) {
|
|
261
|
+
const text = "wait_for_url needs a `pattern` (substring or regex) to watch for.";
|
|
262
|
+
logger.warn(`[BROWSER] ${text}`);
|
|
263
|
+
return {
|
|
264
|
+
text,
|
|
265
|
+
success: false,
|
|
266
|
+
values: { success: false, error: "BROWSER_WAIT_FOR_URL_NO_PATTERN" },
|
|
267
|
+
data: { actionName: "BROWSER", subaction: "wait_for_url" }
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const browserService = runtime.getService(BROWSER_SERVICE_TYPE) ?? null;
|
|
271
|
+
const target = params?.target;
|
|
272
|
+
const startUrl = params?.url?.trim() || extractFirstUrl(messageText) || void 0;
|
|
273
|
+
const timeoutMs = params?.timeoutMs ?? WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;
|
|
274
|
+
const pollIntervalMs = params?.pollIntervalMs ?? WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;
|
|
275
|
+
let tabId = params?.id?.trim() || void 0;
|
|
276
|
+
if (startUrl) {
|
|
277
|
+
const openCommand = {
|
|
278
|
+
subaction: tabId ? "navigate" : "open",
|
|
279
|
+
url: startUrl,
|
|
280
|
+
id: tabId
|
|
281
|
+
};
|
|
282
|
+
try {
|
|
283
|
+
const opened = browserService ? await browserService.execute(openCommand, target) : await executeBrowserWorkspaceCommand(openCommand);
|
|
284
|
+
tabId = opened.tab?.id ?? tabId;
|
|
285
|
+
} catch (error) {
|
|
286
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
287
|
+
logger.warn(
|
|
288
|
+
`[BROWSER] wait_for_url could not open ${startUrl}: ${reason}`
|
|
289
|
+
);
|
|
290
|
+
return {
|
|
291
|
+
text: `Couldn't open ${startUrl} to watch for "${pattern}": ${reason}`,
|
|
292
|
+
success: false,
|
|
293
|
+
values: { success: false, error: "BROWSER_WAIT_FOR_URL_OPEN_FAILED" },
|
|
294
|
+
data: { actionName: "BROWSER", subaction: "wait_for_url" }
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
await callback?.({
|
|
299
|
+
text: startUrl ? `I opened ${startUrl} \u2014 watching for "${pattern}" and I'll resume when it's reached.` : `Watching the current tab for "${pattern}" \u2014 I'll resume when it's reached.`
|
|
300
|
+
});
|
|
301
|
+
logger.info(
|
|
302
|
+
`[BROWSER] wait_for_url pattern="${pattern}" timeoutMs=${timeoutMs} pollIntervalMs=${pollIntervalMs} target=${target ?? "auto"}`
|
|
303
|
+
);
|
|
304
|
+
const outcome = await waitForUrl(
|
|
305
|
+
{ pattern, timeoutMs, pollIntervalMs },
|
|
306
|
+
{
|
|
307
|
+
getCurrentUrl: () => readCurrentBrowserUrl(browserService, target, tabId),
|
|
308
|
+
emitStatus: async (text) => {
|
|
309
|
+
await callback?.({ text });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
return {
|
|
314
|
+
text: outcome.message,
|
|
315
|
+
success: outcome.matched,
|
|
316
|
+
userFacingText: outcome.message,
|
|
317
|
+
values: {
|
|
318
|
+
success: outcome.matched,
|
|
319
|
+
subaction: "wait_for_url",
|
|
320
|
+
status: outcome.status,
|
|
321
|
+
matched: outcome.matched,
|
|
322
|
+
polls: outcome.polls
|
|
323
|
+
},
|
|
324
|
+
data: {
|
|
325
|
+
actionName: "BROWSER",
|
|
326
|
+
subaction: "wait_for_url",
|
|
327
|
+
outcome
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
132
331
|
const browserAction = {
|
|
133
332
|
name: "BROWSER",
|
|
134
333
|
contexts: ["browser", "web", "automation", "secrets"],
|
|
@@ -151,10 +350,10 @@ const browserAction = {
|
|
|
151
350
|
"LOG_INTO_SITE",
|
|
152
351
|
"SIGN_IN_TO_SITE"
|
|
153
352
|
],
|
|
154
|
-
description: "BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab.",
|
|
155
|
-
descriptionCompressed: "Browser open|navigate|click|type|screenshot|state|autofill_login; bridge status elsewhere",
|
|
353
|
+
description: "BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab. action=wait_for_url + pattern opens an optional url then watches the tab and resumes when its URL matches (OAuth callback, deploy/CI done), streaming progress.",
|
|
354
|
+
descriptionCompressed: "Browser open|navigate|click|type|screenshot|state|autofill_login|wait_for_url; bridge status elsewhere",
|
|
156
355
|
validate: async () => true,
|
|
157
|
-
handler: async (runtime, message, _state, options) => {
|
|
356
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
158
357
|
const params = options?.parameters;
|
|
159
358
|
const messageText = getMessageText(message);
|
|
160
359
|
const subaction = inferBrowserSubaction(params, messageText);
|
|
@@ -162,6 +361,9 @@ const browserAction = {
|
|
|
162
361
|
const { executeBrowserAutofillLogin } = await import("./browser-autofill-login.js");
|
|
163
362
|
return executeBrowserAutofillLogin(runtime, message, options);
|
|
164
363
|
}
|
|
364
|
+
if (subaction === "wait-for-url") {
|
|
365
|
+
return executeBrowserWaitForUrl(runtime, params, messageText, callback);
|
|
366
|
+
}
|
|
165
367
|
const url = params?.url?.trim() || extractFirstUrl(messageText) || void 0;
|
|
166
368
|
const command = {
|
|
167
369
|
id: params?.id?.trim(),
|
|
@@ -187,6 +389,13 @@ const browserAction = {
|
|
|
187
389
|
`[BROWSER] ${command.subaction} via target=${params?.target ?? "auto"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`
|
|
188
390
|
);
|
|
189
391
|
const result = browserService ? await browserService.execute(command, params?.target) : await executeBrowserWorkspaceCommand(command);
|
|
392
|
+
await emitBrowserStepProgress(
|
|
393
|
+
callback,
|
|
394
|
+
command,
|
|
395
|
+
params,
|
|
396
|
+
messageText,
|
|
397
|
+
true
|
|
398
|
+
);
|
|
190
399
|
return {
|
|
191
400
|
text: formatBrowserSessionResult(command, result),
|
|
192
401
|
success: true,
|
|
@@ -202,10 +411,18 @@ const browserAction = {
|
|
|
202
411
|
}
|
|
203
412
|
};
|
|
204
413
|
} catch (error) {
|
|
205
|
-
const
|
|
206
|
-
logger.warn(`[BROWSER] Failed: ${
|
|
414
|
+
const errorText = error instanceof Error ? error.message : "Browser action failed";
|
|
415
|
+
logger.warn(`[BROWSER] Failed: ${errorText}`);
|
|
416
|
+
await emitBrowserStepProgress(
|
|
417
|
+
callback,
|
|
418
|
+
command,
|
|
419
|
+
params,
|
|
420
|
+
messageText,
|
|
421
|
+
false,
|
|
422
|
+
errorText
|
|
423
|
+
);
|
|
207
424
|
return {
|
|
208
|
-
text: `Browser action failed: ${
|
|
425
|
+
text: `Browser action failed: ${errorText}`,
|
|
209
426
|
success: false,
|
|
210
427
|
values: { success: false, error: "BROWSER_FAILED" },
|
|
211
428
|
data: {
|
|
@@ -222,6 +439,18 @@ const browserAction = {
|
|
|
222
439
|
required: false,
|
|
223
440
|
schema: { type: "string" }
|
|
224
441
|
},
|
|
442
|
+
{
|
|
443
|
+
name: "streamProgress",
|
|
444
|
+
description: "When true, emit a compact Step 1 progress callback after the browser command dispatches.",
|
|
445
|
+
required: false,
|
|
446
|
+
schema: { type: "boolean", default: false }
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: "rationale",
|
|
450
|
+
description: "Optional rationale shown in streamProgress callback text.",
|
|
451
|
+
required: false,
|
|
452
|
+
schema: { type: "string" }
|
|
453
|
+
},
|
|
225
454
|
{
|
|
226
455
|
name: "action",
|
|
227
456
|
description: "Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.",
|
|
@@ -259,10 +488,23 @@ const browserAction = {
|
|
|
259
488
|
"realistic_press",
|
|
260
489
|
"cursor_move",
|
|
261
490
|
"cursor_hide",
|
|
262
|
-
"autofill_login"
|
|
491
|
+
"autofill_login",
|
|
492
|
+
"wait_for_url"
|
|
263
493
|
]
|
|
264
494
|
}
|
|
265
495
|
},
|
|
496
|
+
{
|
|
497
|
+
name: "pattern",
|
|
498
|
+
description: "For action=wait_for_url: substring or /regex/ to match the tab URL (e.g. callback?code=, or /\\/done$/).",
|
|
499
|
+
required: false,
|
|
500
|
+
schema: { type: "string" }
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
name: "pollIntervalMs",
|
|
504
|
+
description: "For action=wait_for_url: poll cadence in ms. Default 2000.",
|
|
505
|
+
required: false,
|
|
506
|
+
schema: { type: "number" }
|
|
507
|
+
},
|
|
266
508
|
{
|
|
267
509
|
name: "tabAction",
|
|
268
510
|
description: "Tab operation when subaction is tab",
|
|
@@ -403,6 +645,21 @@ const browserAction = {
|
|
|
403
645
|
text: "click completed in desktop mode."
|
|
404
646
|
}
|
|
405
647
|
}
|
|
648
|
+
],
|
|
649
|
+
[
|
|
650
|
+
{
|
|
651
|
+
name: "{{name1}}",
|
|
652
|
+
content: {
|
|
653
|
+
text: "Open the GitHub OAuth page and let me know when it redirects back to our callback."
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
name: "{{agentName}}",
|
|
658
|
+
content: {
|
|
659
|
+
text: `I opened https://github.com/login/oauth/authorize \u2014 watching for "callback?code=" and I'll resume when it's reached.`,
|
|
660
|
+
actions: ["BROWSER"]
|
|
661
|
+
}
|
|
662
|
+
}
|
|
406
663
|
]
|
|
407
664
|
]
|
|
408
665
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/actions/browser.ts"],"sourcesContent":["import type {\n Action,\n ActionExample,\n HandlerOptions,\n Memory,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n BROWSER_SERVICE_TYPE,\n type BrowserService,\n} from \"../browser-service.js\";\nimport {\n type BrowserWorkspaceCommand,\n executeBrowserWorkspaceCommand,\n getBrowserWorkspaceMode,\n} from \"../workspace/browser-workspace.js\";\n\n/**\n * Targets are the registered browser backends. The agent uses what is\n * available; specifying a target overrides automatic routing. `workspace`\n * is the app-owned browser surface, `bridge` is the paired Chrome/Safari\n * companion, `stagehand` is the Playwright/Stagehand fallback, and other\n * plugins may register additional target ids.\n */\nexport type BrowserTarget = string;\n\ntype BrowserWorkspaceSubaction =\n | \"back\"\n | \"click\"\n | \"close\"\n | \"forward\"\n | \"get\"\n | \"hide\"\n | \"navigate\"\n | \"open\"\n | \"press\"\n | \"reload\"\n | \"screenshot\"\n | \"show\"\n | \"snapshot\"\n | \"state\"\n | \"tab\"\n | \"type\"\n | \"wait\"\n | \"realistic-click\"\n | \"realistic-fill\"\n | \"realistic-type\"\n | \"realistic-press\"\n | \"cursor-move\"\n | \"cursor-hide\";\n\ntype BrowserWorkspaceAction =\n | BrowserWorkspaceSubaction\n | \"realistic_click\"\n | \"realistic_fill\"\n | \"realistic_type\"\n | \"realistic_press\"\n | \"cursor_move\"\n | \"cursor_hide\";\n\ntype BrowserActionSubaction = BrowserWorkspaceSubaction | \"autofill-login\";\ntype BrowserActionValue =\n | BrowserWorkspaceAction\n | \"autofill_login\"\n | \"autofill-login\";\ntype NormalizedBrowserAction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n\ntype BrowserActionParameters = {\n /**\n * Optional target override. Default: the BrowserService active target\n * selected from registered targets. Forces a specific backend when set.\n */\n target?: BrowserTarget;\n id?: string;\n key?: string;\n pixels?: number;\n script?: string;\n selector?: string;\n /**\n * Canonical browser action. Legacy `subaction` remains accepted.\n */\n action?:\n | BrowserActionValue\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n subaction?: BrowserActionSubaction;\n /** Registrable hostname for `action: \"autofill_login\"`. */\n domain?: string;\n /** Saved login username for autofill-login (optional). */\n username?: string;\n /** When true with autofill-login, submit after filling. */\n submit?: boolean;\n tabAction?: \"close\" | \"list\" | \"new\" | \"switch\";\n text?: string;\n timeoutMs?: number;\n url?: string;\n /** Cursor animation duration (ms) for realistic-* + cursor-* subactions. */\n cursorDurationMs?: number;\n /** Per-character delay for realistic-type / realistic-fill (ms). */\n perCharDelayMs?: number;\n /** Replace existing input value when filling (vs append). */\n replace?: boolean;\n /** Cursor target X (CSS pixels) for cursor-move. */\n x?: number;\n /** Cursor target Y (CSS pixels) for cursor-move. */\n y?: number;\n /** Hint that the agent is operating in a watch-mode (page-browser) scope. */\n watchMode?: boolean;\n};\n\nfunction getMessageText(message: Memory | undefined): string {\n const content = message?.content;\n if (typeof content === \"string\") {\n return content;\n }\n return typeof content?.text === \"string\" ? content.text : \"\";\n}\n\nfunction extractFirstUrl(value: string): string | null {\n const match = value.match(/https?:\\/\\/[^\\s<>\"'`]+/i);\n return match?.[0] ?? null;\n}\n\nfunction inferBrowserSubaction(\n params: BrowserActionParameters | undefined,\n messageText: string,\n): BrowserWorkspaceCommand[\"subaction\"] | \"autofill-login\" {\n const normalizedAction = normalizeBrowserAction(params?.action);\n if (\n normalizedAction === \"autofill-login\" ||\n params?.subaction === \"autofill-login\"\n ) {\n return \"autofill-login\";\n }\n\n const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);\n if (legacySubaction) {\n return legacySubaction;\n }\n if (params?.subaction) {\n return params.subaction;\n }\n\n if (params?.tabAction) {\n return \"tab\";\n }\n\n // In watch mode the user is observing the agent drive the browser; prefer\n // the realistic-* subactions so the cursor moves and pointer events fire\n // faithfully. Default-mode (no watcher) keeps the leaner click()/value=\n // path for speed.\n const watchMode = params?.watchMode === true;\n\n if (params?.selector && params?.text) {\n return watchMode ? \"realistic-fill\" : \"type\";\n }\n\n if (params?.selector) {\n return watchMode ? \"realistic-click\" : \"click\";\n }\n\n if (params?.url?.trim() || extractFirstUrl(messageText)) {\n return params?.id ? \"navigate\" : \"open\";\n }\n\n return \"state\";\n}\n\nfunction normalizeBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): NormalizedBrowserAction | undefined {\n switch (action) {\n case \"realistic_click\":\n return \"realistic-click\";\n case \"realistic_fill\":\n return \"realistic-fill\";\n case \"realistic_type\":\n return \"realistic-type\";\n case \"realistic_press\":\n return \"realistic-press\";\n case \"cursor_move\":\n return \"cursor-move\";\n case \"cursor_hide\":\n return \"cursor-hide\";\n case \"autofill_login\":\n return \"autofill-login\";\n default:\n return action as NormalizedBrowserAction | undefined;\n }\n}\n\nfunction normalizeLegacyBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserWorkspaceCommand[\"subaction\"] | undefined {\n const normalizedAction = normalizeBrowserAction(action);\n switch (normalizedAction) {\n case \"info\":\n case \"context\":\n case \"get_context\":\n return \"state\";\n case \"list_tabs\":\n case \"open_tab\":\n case \"close_tab\":\n case \"switch_tab\":\n return \"tab\";\n case \"autofill-login\":\n return undefined;\n case undefined:\n return undefined;\n default:\n return isWorkspaceSubaction(normalizedAction)\n ? normalizedAction\n : undefined;\n }\n}\n\nfunction isWorkspaceSubaction(\n action: unknown,\n): action is BrowserWorkspaceCommand[\"subaction\"] {\n return (\n action === \"back\" ||\n action === \"click\" ||\n action === \"close\" ||\n action === \"forward\" ||\n action === \"get\" ||\n action === \"hide\" ||\n action === \"navigate\" ||\n action === \"open\" ||\n action === \"press\" ||\n action === \"reload\" ||\n action === \"screenshot\" ||\n action === \"show\" ||\n action === \"snapshot\" ||\n action === \"state\" ||\n action === \"tab\" ||\n action === \"type\" ||\n action === \"wait\" ||\n action === \"realistic-click\" ||\n action === \"realistic-fill\" ||\n action === \"realistic-type\" ||\n action === \"realistic-press\" ||\n action === \"cursor-move\" ||\n action === \"cursor-hide\"\n );\n}\n\nfunction normalizeLegacyTabAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserActionParameters[\"tabAction\"] | undefined {\n switch (normalizeBrowserAction(action)) {\n case \"list_tabs\":\n return \"list\";\n case \"open_tab\":\n return \"new\";\n case \"close_tab\":\n return \"close\";\n case \"switch_tab\":\n return \"switch\";\n default:\n return undefined;\n }\n}\n\nfunction formatBrowserSessionResult(\n command: BrowserWorkspaceCommand,\n result: Awaited<ReturnType<typeof executeBrowserWorkspaceCommand>>,\n): string {\n if (result.tabs) {\n const labels = result.tabs\n .map((tab) => `- ${tab.title} (${tab.url})`)\n .join(\"\\n\");\n return labels\n ? `Browser tabs (${result.mode}):\\n${labels}`\n : `No browser session tabs are open (${result.mode}).`;\n }\n\n if (result.closed) {\n return `Browser closed (${result.mode}).`;\n }\n\n if (result.tab) {\n return `${command.subaction} completed in ${result.mode} mode.\\n${result.tab.title}\\n${result.tab.url}`;\n }\n\n if (result.value !== undefined) {\n if (\n command.subaction === \"cursor-move\" &&\n result.value !== null &&\n typeof result.value === \"object\" &&\n \"x\" in result.value &&\n \"y\" in result.value\n ) {\n const cursor = result.value as { x: number; y: number };\n return `Cursor moved to (${Math.round(cursor.x)}, ${Math.round(cursor.y)}) in ${result.mode} mode.`;\n }\n const serialized =\n typeof result.value === \"string\"\n ? result.value\n : JSON.stringify(result.value, null, 2);\n return `Browser ${command.subaction} result (${result.mode}):\\n${serialized}`;\n }\n\n if (result.snapshot?.data) {\n return `Browser ${command.subaction} captured a preview in ${result.mode} mode.`;\n }\n\n return `Browser ${command.subaction} completed in ${result.mode} mode.`;\n}\n\nexport const browserAction: Action = {\n name: \"BROWSER\",\n contexts: [\"browser\", \"web\", \"automation\", \"secrets\"],\n roleGate: { minRole: \"OWNER\" },\n similes: [\n \"BROWSE_SITE\",\n \"BROWSER_SESSION\",\n \"CONTROL_BROWSER\",\n \"CONTROL_BROWSER_SESSION\",\n \"MANAGE_ELIZA_BROWSER_WORKSPACE\",\n \"NAVIGATE_SITE\",\n \"OPEN_SITE\",\n \"USE_BROWSER\",\n \"BROWSER_ACTION\",\n \"BROWSER_AUTOFILL_LOGIN\",\n \"AGENT_AUTOFILL\",\n \"AUTOFILL_BROWSER_LOGIN\",\n \"AUTOFILL_LOGIN\",\n \"FILL_BROWSER_CREDENTIALS\",\n \"LOG_INTO_SITE\",\n \"SIGN_IN_TO_SITE\",\n ],\n description:\n \"BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab.\",\n descriptionCompressed:\n \"Browser open|navigate|click|type|screenshot|state|autofill_login; bridge status elsewhere\",\n validate: async () => true,\n handler: async (runtime, message, _state, options) => {\n const params = (options as HandlerOptions | undefined)?.parameters as\n | BrowserActionParameters\n | undefined;\n const messageText = getMessageText(message);\n const subaction = inferBrowserSubaction(params, messageText);\n\n if (subaction === \"autofill-login\") {\n const { executeBrowserAutofillLogin } = await import(\n \"./browser-autofill-login.js\"\n );\n return executeBrowserAutofillLogin(runtime, message, options);\n }\n\n const url =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n\n const command: BrowserWorkspaceCommand = {\n id: params?.id?.trim(),\n key: params?.key?.trim(),\n pixels: params?.pixels,\n script: params?.script,\n selector: params?.selector?.trim(),\n subaction,\n tabAction: params?.tabAction ?? normalizeLegacyTabAction(params?.action),\n text: params?.text,\n value: params?.text,\n timeoutMs: params?.timeoutMs,\n url,\n cursorDurationMs: params?.cursorDurationMs,\n perCharDelayMs: params?.perCharDelayMs,\n replace: params?.replace,\n x: params?.x,\n y: params?.y,\n };\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE);\n\n try {\n logger.info(\n `[BROWSER] ${command.subaction} via target=${params?.target ?? \"auto\"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`,\n );\n const result = browserService\n ? await browserService.execute(command, params?.target)\n : await executeBrowserWorkspaceCommand(command);\n\n return {\n text: formatBrowserSessionResult(command, result),\n success: true,\n values: {\n success: true,\n mode: result.mode,\n subaction: result.subaction,\n },\n data: {\n actionName: \"BROWSER\",\n command,\n result,\n },\n };\n } catch (error) {\n const messageText =\n error instanceof Error ? error.message : \"Browser action failed\";\n logger.warn(`[BROWSER] Failed: ${messageText}`);\n return {\n text: `Browser action failed: ${messageText}`,\n success: false,\n values: { success: false, error: \"BROWSER_FAILED\" },\n data: {\n actionName: \"BROWSER\",\n command,\n },\n };\n }\n },\n parameters: [\n {\n name: \"target\",\n description:\n \"Optional browser target id. Common values: workspace, bridge, computeruse, stagehand.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"action\",\n description:\n \"Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\n \"back\",\n \"click\",\n \"close\",\n \"context\",\n \"forward\",\n \"get\",\n \"get_context\",\n \"hide\",\n \"info\",\n \"list_tabs\",\n \"navigate\",\n \"open\",\n \"open_tab\",\n \"press\",\n \"reload\",\n \"screenshot\",\n \"show\",\n \"snapshot\",\n \"state\",\n \"tab\",\n \"type\",\n \"wait\",\n \"close_tab\",\n \"switch_tab\",\n \"realistic_click\",\n \"realistic_fill\",\n \"realistic_type\",\n \"realistic_press\",\n \"cursor_move\",\n \"cursor_hide\",\n \"autofill_login\",\n ],\n },\n },\n {\n name: \"tabAction\",\n description: \"Tab operation when subaction is tab\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\"close\", \"list\", \"new\", \"switch\"],\n },\n },\n {\n name: \"domain\",\n description:\n \"Required for action=autofill_login: registrable hostname, e.g. github.com.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"username\",\n description: \"For autofill-login: saved login username; omit for latest.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"submit\",\n description: \"For autofill-login: submit after filling. Default false.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"id\",\n description: \"Session or tab id to target\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"url\",\n description: \"URL for open or navigate\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"selector\",\n description: \"Selector for click, type, or wait\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"text\",\n description: \"Text for type\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"key\",\n description: \"Keyboard key for press\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pixels\",\n description: \"Scroll distance in pixels\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"timeoutMs\",\n description: \"Command timeout in milliseconds\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"script\",\n description: \"Script for eval\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"watchMode\",\n description:\n \"User watching hint; prefer realistic-* click/fill, visible cursor, pointer events.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"cursorDurationMs\",\n description: \"Cursor animation duration (ms) for realistic-* subactions\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"perCharDelayMs\",\n description: \"Per-character delay for realistic-type/realistic-fill (ms)\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"replace\",\n description: \"For realistic-fill: replace existing input, not append.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"x\",\n description: \"Cursor target X (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"y\",\n description: \"Cursor target Y (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n ],\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open elizaos.ai in a new browser tab.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"open completed in desktop mode.\\nelizaOS\\nhttps://elizaos.ai\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Click the sign-in button on that page.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"click completed in desktop mode.\",\n },\n },\n ],\n ] as ActionExample[][],\n};\n"],"mappings":"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","names":["messageText"]}
|
|
1
|
+
{"version":3,"sources":["../../src/actions/browser.ts"],"sourcesContent":["import type {\n Action,\n ActionExample,\n Content,\n HandlerCallback,\n HandlerOptions,\n Memory,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n BROWSER_SERVICE_TYPE,\n type BrowserService,\n} from \"../browser-service.js\";\nimport {\n type BrowserWorkspaceCommand,\n type BrowserWorkspaceCommandResult,\n executeBrowserWorkspaceCommand,\n getBrowserWorkspaceMode,\n} from \"../workspace/browser-workspace.js\";\nimport {\n WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS,\n WAIT_FOR_URL_DEFAULT_TIMEOUT_MS,\n waitForUrl,\n} from \"./wait-for-url.js\";\n\n/**\n * Targets are the registered browser backends. The agent uses what is\n * available; specifying a target overrides automatic routing. `workspace`\n * is the app-owned browser surface, `bridge` is the paired Chrome/Safari\n * companion, `stagehand` is the Playwright/Stagehand fallback, and other\n * plugins may register additional target ids.\n */\nexport type BrowserTarget = string;\n\ntype BrowserWorkspaceSubaction =\n | \"back\"\n | \"click\"\n | \"close\"\n | \"forward\"\n | \"get\"\n | \"hide\"\n | \"navigate\"\n | \"open\"\n | \"press\"\n | \"reload\"\n | \"screenshot\"\n | \"show\"\n | \"snapshot\"\n | \"state\"\n | \"tab\"\n | \"type\"\n | \"wait\"\n | \"realistic-click\"\n | \"realistic-fill\"\n | \"realistic-type\"\n | \"realistic-press\"\n | \"cursor-move\"\n | \"cursor-hide\";\n\ntype BrowserWorkspaceAction =\n | BrowserWorkspaceSubaction\n | \"realistic_click\"\n | \"realistic_fill\"\n | \"realistic_type\"\n | \"realistic_press\"\n | \"cursor_move\"\n | \"cursor_hide\";\n\ntype BrowserActionSubaction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"wait-for-url\";\ntype BrowserActionValue =\n | BrowserWorkspaceAction\n | \"autofill_login\"\n | \"autofill-login\"\n | \"wait_for_url\"\n | \"wait-for-url\";\ntype NormalizedBrowserAction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"wait-for-url\"\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n\ntype BrowserActionParameters = {\n /**\n * Optional target override. Default: the BrowserService active target\n * selected from registered targets. Forces a specific backend when set.\n */\n target?: BrowserTarget;\n id?: string;\n key?: string;\n pixels?: number;\n script?: string;\n selector?: string;\n /**\n * Canonical browser action. Legacy `subaction` remains accepted.\n */\n action?:\n | BrowserActionValue\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n subaction?: BrowserActionSubaction;\n /** For action=wait_for_url: substring or regex to match the tab URL. */\n pattern?: string;\n /** For action=wait_for_url: poll cadence in ms (default ~2000). */\n pollIntervalMs?: number;\n /** Registrable hostname for `action: \"autofill_login\"`. */\n domain?: string;\n /** Saved login username for autofill-login (optional). */\n username?: string;\n /** When true with autofill-login, submit after filling. */\n submit?: boolean;\n tabAction?: \"close\" | \"list\" | \"new\" | \"switch\";\n text?: string;\n timeoutMs?: number;\n url?: string;\n /** Cursor animation duration (ms) for realistic-* + cursor-* subactions. */\n cursorDurationMs?: number;\n /** Per-character delay for realistic-type / realistic-fill (ms). */\n perCharDelayMs?: number;\n /** Replace existing input value when filling (vs append). */\n replace?: boolean;\n /** Cursor target X (CSS pixels) for cursor-move. */\n x?: number;\n /** Cursor target Y (CSS pixels) for cursor-move. */\n y?: number;\n /** Hint that the agent is operating in a watch-mode (page-browser) scope. */\n watchMode?: boolean;\n /** Emit one compact progress callback after the browser step dispatches. */\n streamProgress?: boolean;\n /** Optional rationale to show in the compact progress callback. */\n rationale?: string;\n};\n\nfunction getMessageText(message: Memory | undefined): string {\n const content = message?.content;\n if (typeof content === \"string\") {\n return content;\n }\n return typeof content?.text === \"string\" ? content.text : \"\";\n}\n\nfunction extractFirstUrl(value: string): string | null {\n const match = value.match(/https?:\\/\\/[^\\s<>\"'`]+/i);\n return match?.[0] ?? null;\n}\n\nfunction inferBrowserSubaction(\n params: BrowserActionParameters | undefined,\n messageText: string,\n): BrowserWorkspaceCommand[\"subaction\"] | \"autofill-login\" | \"wait-for-url\" {\n const normalizedAction = normalizeBrowserAction(params?.action);\n if (\n normalizedAction === \"autofill-login\" ||\n params?.subaction === \"autofill-login\"\n ) {\n return \"autofill-login\";\n }\n\n if (\n normalizedAction === \"wait-for-url\" ||\n params?.subaction === \"wait-for-url\"\n ) {\n return \"wait-for-url\";\n }\n\n const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);\n if (legacySubaction) {\n return legacySubaction;\n }\n if (params?.subaction) {\n return params.subaction;\n }\n\n if (params?.tabAction) {\n return \"tab\";\n }\n\n // In watch mode the user is observing the agent drive the browser; prefer\n // the realistic-* subactions so the cursor moves and pointer events fire\n // faithfully. Default-mode (no watcher) keeps the leaner click()/value=\n // path for speed.\n const watchMode = params?.watchMode === true;\n\n if (params?.selector && params?.text) {\n return watchMode ? \"realistic-fill\" : \"type\";\n }\n\n if (params?.selector) {\n return watchMode ? \"realistic-click\" : \"click\";\n }\n\n if (params?.url?.trim() || extractFirstUrl(messageText)) {\n return params?.id ? \"navigate\" : \"open\";\n }\n\n return \"state\";\n}\n\nfunction normalizeBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): NormalizedBrowserAction | undefined {\n switch (action) {\n case \"realistic_click\":\n return \"realistic-click\";\n case \"realistic_fill\":\n return \"realistic-fill\";\n case \"realistic_type\":\n return \"realistic-type\";\n case \"realistic_press\":\n return \"realistic-press\";\n case \"cursor_move\":\n return \"cursor-move\";\n case \"cursor_hide\":\n return \"cursor-hide\";\n case \"autofill_login\":\n return \"autofill-login\";\n case \"wait_for_url\":\n case \"wait-for-url\":\n return \"wait-for-url\";\n default:\n return action as NormalizedBrowserAction | undefined;\n }\n}\n\nfunction normalizeLegacyBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserWorkspaceCommand[\"subaction\"] | undefined {\n const normalizedAction = normalizeBrowserAction(action);\n switch (normalizedAction) {\n case \"info\":\n case \"context\":\n case \"get_context\":\n return \"state\";\n case \"list_tabs\":\n case \"open_tab\":\n case \"close_tab\":\n case \"switch_tab\":\n return \"tab\";\n case \"autofill-login\":\n case \"wait-for-url\":\n return undefined;\n case undefined:\n return undefined;\n default:\n return isWorkspaceSubaction(normalizedAction)\n ? normalizedAction\n : undefined;\n }\n}\n\nfunction isWorkspaceSubaction(\n action: unknown,\n): action is BrowserWorkspaceCommand[\"subaction\"] {\n return (\n action === \"back\" ||\n action === \"click\" ||\n action === \"close\" ||\n action === \"forward\" ||\n action === \"get\" ||\n action === \"hide\" ||\n action === \"navigate\" ||\n action === \"open\" ||\n action === \"press\" ||\n action === \"reload\" ||\n action === \"screenshot\" ||\n action === \"show\" ||\n action === \"snapshot\" ||\n action === \"state\" ||\n action === \"tab\" ||\n action === \"type\" ||\n action === \"wait\" ||\n action === \"realistic-click\" ||\n action === \"realistic-fill\" ||\n action === \"realistic-type\" ||\n action === \"realistic-press\" ||\n action === \"cursor-move\" ||\n action === \"cursor-hide\"\n );\n}\n\nfunction normalizeLegacyTabAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserActionParameters[\"tabAction\"] | undefined {\n switch (normalizeBrowserAction(action)) {\n case \"list_tabs\":\n return \"list\";\n case \"open_tab\":\n return \"new\";\n case \"close_tab\":\n return \"close\";\n case \"switch_tab\":\n return \"switch\";\n default:\n return undefined;\n }\n}\n\nfunction formatBrowserSessionResult(\n command: BrowserWorkspaceCommand,\n result: Awaited<ReturnType<typeof executeBrowserWorkspaceCommand>>,\n): string {\n if (result.tabs) {\n const labels = result.tabs\n .map((tab) => `- ${tab.title} (${tab.url})`)\n .join(\"\\n\");\n return labels\n ? `Browser tabs (${result.mode}):\\n${labels}`\n : `No browser session tabs are open (${result.mode}).`;\n }\n\n if (result.closed) {\n return `Browser closed (${result.mode}).`;\n }\n\n if (result.tab) {\n return `${command.subaction} completed in ${result.mode} mode.\\n${result.tab.title}\\n${result.tab.url}`;\n }\n\n if (result.value !== undefined) {\n if (\n command.subaction === \"cursor-move\" &&\n result.value !== null &&\n typeof result.value === \"object\" &&\n \"x\" in result.value &&\n \"y\" in result.value\n ) {\n const cursor = result.value as { x: number; y: number };\n return `Cursor moved to (${Math.round(cursor.x)}, ${Math.round(cursor.y)}) in ${result.mode} mode.`;\n }\n const serialized =\n typeof result.value === \"string\"\n ? result.value\n : JSON.stringify(result.value, null, 2);\n return `Browser ${command.subaction} result (${result.mode}):\\n${serialized}`;\n }\n\n if (result.snapshot?.data) {\n return `Browser ${command.subaction} captured a preview in ${result.mode} mode.`;\n }\n\n return `Browser ${command.subaction} completed in ${result.mode} mode.`;\n}\n\nfunction browserProgressRationale(\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n): string {\n const explicit = params?.rationale?.trim();\n if (explicit) return explicit;\n\n switch (command.subaction) {\n case \"open\":\n case \"navigate\":\n return command.url ? `open ${command.url}` : \"open requested page\";\n case \"click\":\n case \"realistic-click\":\n return command.selector\n ? `click ${command.selector}`\n : \"click requested target\";\n case \"type\":\n case \"realistic-fill\":\n case \"realistic-type\":\n return command.selector\n ? `fill ${command.selector}`\n : \"type requested text\";\n case \"press\":\n case \"realistic-press\":\n return command.key ? `press ${command.key}` : \"press requested key\";\n case \"tab\":\n return command.tabAction\n ? `${command.tabAction} browser tab`\n : \"manage browser tabs\";\n case \"wait\":\n return command.selector\n ? `wait for ${command.selector}`\n : \"wait for browser state\";\n case \"state\":\n return messageText.trim() || \"read browser state\";\n default:\n return `run browser ${command.subaction}`;\n }\n}\n\nfunction buildBrowserStepProgressContent(\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n success: boolean,\n error?: string,\n): Content {\n const rationale = error\n ? `failed: ${error}`\n : browserProgressRationale(command, params, messageText);\n return {\n text: `Step 1: ${command.subaction} — ${rationale}`,\n source: \"action_progress\",\n merge: \"replace\",\n metadata: {\n transient: true,\n compactProgress: true,\n progress: {\n source: \"browser\",\n actionName: \"BROWSER\",\n step: 1,\n kind: command.subaction,\n rationale,\n success,\n error,\n },\n },\n };\n}\n\nasync function emitBrowserStepProgress(\n callback: HandlerCallback | undefined,\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n success: boolean,\n error?: string,\n): Promise<void> {\n if (params?.streamProgress !== true || !callback) return;\n try {\n await callback(\n buildBrowserStepProgressContent(\n command,\n params,\n messageText,\n success,\n error,\n ),\n \"BROWSER\",\n );\n } catch (callbackError) {\n logger.warn(\n {\n src: \"plugin:browser\",\n action: command.subaction,\n error:\n callbackError instanceof Error\n ? callbackError.message\n : String(callbackError),\n },\n \"Failed to emit browser progress callback\",\n );\n }\n}\n\nfunction currentUrlFromResult(\n result: BrowserWorkspaceCommandResult,\n): string | null {\n if (result.tab?.url) {\n return result.tab.url;\n }\n if (typeof result.value === \"string\" && result.value.trim()) {\n return result.value.trim();\n }\n if (Array.isArray(result.tabs)) {\n const visible = result.tabs.find((tab) => tab.visible) ?? result.tabs[0];\n if (visible?.url) {\n return visible.url;\n }\n }\n return null;\n}\n\n/**\n * Reads the current tab URL via the active browser target. Prefers a targeted\n * `get url` (cheap), falling back to `state` for backends that don't implement\n * the get-url mode. Returns null when the URL is not yet readable.\n */\nasync function readCurrentBrowserUrl(\n browserService: BrowserService | null,\n target: BrowserTarget | undefined,\n tabId: string | undefined,\n): Promise<string | null> {\n const run = async (\n command: BrowserWorkspaceCommand,\n ): Promise<BrowserWorkspaceCommandResult> =>\n browserService\n ? browserService.execute(command, target)\n : executeBrowserWorkspaceCommand(command);\n\n try {\n const got = await run({\n subaction: \"get\",\n getMode: \"url\",\n id: tabId,\n });\n const url = currentUrlFromResult(got);\n if (url) {\n return url;\n }\n } catch (error) {\n logger.debug(\n `[BROWSER] wait_for_url get-url poll failed, falling back to state: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n try {\n const state = await run({ subaction: \"state\", id: tabId });\n return currentUrlFromResult(state);\n } catch (error) {\n logger.debug(\n `[BROWSER] wait_for_url state poll could not read URL: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\n/**\n * Handles `action: \"wait_for_url\"`. Optionally opens/navigates to a starting\n * URL, tells the user it is watching, then polls the tab URL against `pattern`,\n * streaming a status update each poll. Resolves with a typed success/timeout\n * result and never throws on timeout.\n */\nasync function executeBrowserWaitForUrl(\n runtime: Parameters<NonNullable<Action[\"handler\"]>>[0],\n params: BrowserActionParameters | undefined,\n messageText: string,\n callback: HandlerCallback | undefined,\n): Promise<ReturnType<NonNullable<Action[\"handler\"]>>> {\n const pattern = (params?.pattern ?? \"\").trim();\n if (!pattern) {\n const text =\n \"wait_for_url needs a `pattern` (substring or regex) to watch for.\";\n logger.warn(`[BROWSER] ${text}`);\n return {\n text,\n success: false,\n values: { success: false, error: \"BROWSER_WAIT_FOR_URL_NO_PATTERN\" },\n data: { actionName: \"BROWSER\", subaction: \"wait_for_url\" },\n };\n }\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE) ?? null;\n const target = params?.target;\n const startUrl =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n const timeoutMs = params?.timeoutMs ?? WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;\n const pollIntervalMs =\n params?.pollIntervalMs ?? WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;\n\n let tabId = params?.id?.trim() || undefined;\n\n // Optionally launch the starting URL so the user can act on it.\n if (startUrl) {\n const openCommand: BrowserWorkspaceCommand = {\n subaction: tabId ? \"navigate\" : \"open\",\n url: startUrl,\n id: tabId,\n };\n try {\n const opened = browserService\n ? await browserService.execute(openCommand, target)\n : await executeBrowserWorkspaceCommand(openCommand);\n tabId = opened.tab?.id ?? tabId;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n logger.warn(\n `[BROWSER] wait_for_url could not open ${startUrl}: ${reason}`,\n );\n return {\n text: `Couldn't open ${startUrl} to watch for \"${pattern}\": ${reason}`,\n success: false,\n values: { success: false, error: \"BROWSER_WAIT_FOR_URL_OPEN_FAILED\" },\n data: { actionName: \"BROWSER\", subaction: \"wait_for_url\" },\n };\n }\n }\n\n await callback?.({\n text: startUrl\n ? `I opened ${startUrl} — watching for \"${pattern}\" and I'll resume when it's reached.`\n : `Watching the current tab for \"${pattern}\" — I'll resume when it's reached.`,\n });\n\n logger.info(\n `[BROWSER] wait_for_url pattern=\"${pattern}\" timeoutMs=${timeoutMs} pollIntervalMs=${pollIntervalMs} target=${target ?? \"auto\"}`,\n );\n\n const outcome = await waitForUrl(\n { pattern, timeoutMs, pollIntervalMs },\n {\n getCurrentUrl: () => readCurrentBrowserUrl(browserService, target, tabId),\n emitStatus: async (text) => {\n await callback?.({ text });\n },\n },\n );\n\n return {\n text: outcome.message,\n success: outcome.matched,\n userFacingText: outcome.message,\n values: {\n success: outcome.matched,\n subaction: \"wait_for_url\",\n status: outcome.status,\n matched: outcome.matched,\n polls: outcome.polls,\n },\n data: {\n actionName: \"BROWSER\",\n subaction: \"wait_for_url\",\n outcome,\n },\n };\n}\n\nexport const browserAction: Action = {\n name: \"BROWSER\",\n contexts: [\"browser\", \"web\", \"automation\", \"secrets\"],\n roleGate: { minRole: \"OWNER\" },\n similes: [\n \"BROWSE_SITE\",\n \"BROWSER_SESSION\",\n \"CONTROL_BROWSER\",\n \"CONTROL_BROWSER_SESSION\",\n \"MANAGE_ELIZA_BROWSER_WORKSPACE\",\n \"NAVIGATE_SITE\",\n \"OPEN_SITE\",\n \"USE_BROWSER\",\n \"BROWSER_ACTION\",\n \"BROWSER_AUTOFILL_LOGIN\",\n \"AGENT_AUTOFILL\",\n \"AUTOFILL_BROWSER_LOGIN\",\n \"AUTOFILL_LOGIN\",\n \"FILL_BROWSER_CREDENTIALS\",\n \"LOG_INTO_SITE\",\n \"SIGN_IN_TO_SITE\",\n ],\n description:\n \"BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab. action=wait_for_url + pattern opens an optional url then watches the tab and resumes when its URL matches (OAuth callback, deploy/CI done), streaming progress.\",\n descriptionCompressed:\n \"Browser open|navigate|click|type|screenshot|state|autofill_login|wait_for_url; bridge status elsewhere\",\n validate: async () => true,\n handler: async (\n runtime,\n message,\n _state,\n options,\n callback?: HandlerCallback,\n ) => {\n const params = (options as HandlerOptions | undefined)?.parameters as\n | BrowserActionParameters\n | undefined;\n const messageText = getMessageText(message);\n const subaction = inferBrowserSubaction(params, messageText);\n\n if (subaction === \"autofill-login\") {\n const { executeBrowserAutofillLogin } = await import(\n \"./browser-autofill-login.js\"\n );\n return executeBrowserAutofillLogin(runtime, message, options);\n }\n\n if (subaction === \"wait-for-url\") {\n return executeBrowserWaitForUrl(runtime, params, messageText, callback);\n }\n\n const url =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n\n const command: BrowserWorkspaceCommand = {\n id: params?.id?.trim(),\n key: params?.key?.trim(),\n pixels: params?.pixels,\n script: params?.script,\n selector: params?.selector?.trim(),\n subaction,\n tabAction: params?.tabAction ?? normalizeLegacyTabAction(params?.action),\n text: params?.text,\n value: params?.text,\n timeoutMs: params?.timeoutMs,\n url,\n cursorDurationMs: params?.cursorDurationMs,\n perCharDelayMs: params?.perCharDelayMs,\n replace: params?.replace,\n x: params?.x,\n y: params?.y,\n };\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE);\n\n try {\n logger.info(\n `[BROWSER] ${command.subaction} via target=${params?.target ?? \"auto\"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`,\n );\n const result = browserService\n ? await browserService.execute(command, params?.target)\n : await executeBrowserWorkspaceCommand(command);\n await emitBrowserStepProgress(\n callback,\n command,\n params,\n messageText,\n true,\n );\n\n return {\n text: formatBrowserSessionResult(command, result),\n success: true,\n values: {\n success: true,\n mode: result.mode,\n subaction: result.subaction,\n },\n data: {\n actionName: \"BROWSER\",\n command,\n result,\n },\n };\n } catch (error) {\n const errorText =\n error instanceof Error ? error.message : \"Browser action failed\";\n logger.warn(`[BROWSER] Failed: ${errorText}`);\n await emitBrowserStepProgress(\n callback,\n command,\n params,\n messageText,\n false,\n errorText,\n );\n return {\n text: `Browser action failed: ${errorText}`,\n success: false,\n values: { success: false, error: \"BROWSER_FAILED\" },\n data: {\n actionName: \"BROWSER\",\n command,\n },\n };\n }\n },\n parameters: [\n {\n name: \"target\",\n description:\n \"Optional browser target id. Common values: workspace, bridge, computeruse, stagehand.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"streamProgress\",\n description:\n \"When true, emit a compact Step 1 progress callback after the browser command dispatches.\",\n required: false,\n schema: { type: \"boolean\" as const, default: false },\n },\n {\n name: \"rationale\",\n description: \"Optional rationale shown in streamProgress callback text.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"action\",\n description:\n \"Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\n \"back\",\n \"click\",\n \"close\",\n \"context\",\n \"forward\",\n \"get\",\n \"get_context\",\n \"hide\",\n \"info\",\n \"list_tabs\",\n \"navigate\",\n \"open\",\n \"open_tab\",\n \"press\",\n \"reload\",\n \"screenshot\",\n \"show\",\n \"snapshot\",\n \"state\",\n \"tab\",\n \"type\",\n \"wait\",\n \"close_tab\",\n \"switch_tab\",\n \"realistic_click\",\n \"realistic_fill\",\n \"realistic_type\",\n \"realistic_press\",\n \"cursor_move\",\n \"cursor_hide\",\n \"autofill_login\",\n \"wait_for_url\",\n ],\n },\n },\n {\n name: \"pattern\",\n description:\n \"For action=wait_for_url: substring or /regex/ to match the tab URL (e.g. callback?code=, or /\\\\/done$/).\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pollIntervalMs\",\n description: \"For action=wait_for_url: poll cadence in ms. Default 2000.\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"tabAction\",\n description: \"Tab operation when subaction is tab\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\"close\", \"list\", \"new\", \"switch\"],\n },\n },\n {\n name: \"domain\",\n description:\n \"Required for action=autofill_login: registrable hostname, e.g. github.com.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"username\",\n description: \"For autofill-login: saved login username; omit for latest.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"submit\",\n description: \"For autofill-login: submit after filling. Default false.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"id\",\n description: \"Session or tab id to target\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"url\",\n description: \"URL for open or navigate\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"selector\",\n description: \"Selector for click, type, or wait\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"text\",\n description: \"Text for type\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"key\",\n description: \"Keyboard key for press\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pixels\",\n description: \"Scroll distance in pixels\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"timeoutMs\",\n description: \"Command timeout in milliseconds\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"script\",\n description: \"Script for eval\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"watchMode\",\n description:\n \"User watching hint; prefer realistic-* click/fill, visible cursor, pointer events.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"cursorDurationMs\",\n description: \"Cursor animation duration (ms) for realistic-* subactions\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"perCharDelayMs\",\n description: \"Per-character delay for realistic-type/realistic-fill (ms)\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"replace\",\n description: \"For realistic-fill: replace existing input, not append.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"x\",\n description: \"Cursor target X (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"y\",\n description: \"Cursor target Y (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n ],\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open elizaos.ai in a new browser tab.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"open completed in desktop mode.\\nelizaOS\\nhttps://elizaos.ai\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Click the sign-in button on that page.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"click completed in desktop mode.\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open the GitHub OAuth page and let me know when it redirects back to our callback.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"I opened https://github.com/login/oauth/authorize — watching for \\\"callback?code=\\\" and I'll resume when it's reached.\",\n actions: [\"BROWSER\"],\n },\n },\n ],\n ] as ActionExample[][],\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure URL-matching predicate for the BROWSER `wait_for_url` subaction.
|
|
3
|
+
*
|
|
4
|
+
* A `pattern` is treated as a regular expression only when it is written as a
|
|
5
|
+
* `/.../` literal (with optional flags); any other pattern is a
|
|
6
|
+
* case-insensitive substring match. This keeps ordinary URL fragments like
|
|
7
|
+
* `callback?code=` (which contain regex metacharacters) predictable. An invalid
|
|
8
|
+
* regex literal always falls back to a substring match so the agent never
|
|
9
|
+
* crashes on user input.
|
|
10
|
+
*
|
|
11
|
+
* Kept free of any browser/runtime imports so it stays trivially unit-testable.
|
|
12
|
+
*/
|
|
13
|
+
/** How a given pattern was interpreted when building the predicate. */
|
|
14
|
+
export type WaitForUrlPatternKind = "regex" | "substring";
|
|
15
|
+
export interface WaitForUrlPredicate {
|
|
16
|
+
/** The original, untrimmed pattern the caller supplied. */
|
|
17
|
+
readonly pattern: string;
|
|
18
|
+
/** How the pattern was interpreted ("regex" or "substring"). */
|
|
19
|
+
readonly kind: WaitForUrlPatternKind;
|
|
20
|
+
/** Returns true when `url` satisfies the pattern. */
|
|
21
|
+
test(url: string): boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build a {@link WaitForUrlPredicate} from a caller-supplied pattern.
|
|
25
|
+
*
|
|
26
|
+
* - `"/foo\\d+/i"` → regex `/foo\d+/i`.
|
|
27
|
+
* - `"/\\/done$/"` → regex.
|
|
28
|
+
* - `"callback?code="` → substring (case-insensitive), even though it contains
|
|
29
|
+
* regex metacharacters.
|
|
30
|
+
* - An invalid `/.../ ` literal → falls back to a case-insensitive substring
|
|
31
|
+
* match on the original pattern text.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildWaitForUrlPredicate(pattern: string): WaitForUrlPredicate;
|
|
34
|
+
//# sourceMappingURL=wait-for-url-predicate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-url-predicate.d.ts","sourceRoot":"","sources":["../../src/actions/wait-for-url-predicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,qDAAqD;IACrD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAYD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAwB7E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const REGEX_LITERAL = /^\/(.+)\/([a-z]*)$/i;
|
|
2
|
+
function compileRegex(source, flags) {
|
|
3
|
+
try {
|
|
4
|
+
return new RegExp(source, flags);
|
|
5
|
+
} catch {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function buildWaitForUrlPredicate(pattern) {
|
|
10
|
+
const trimmed = pattern.trim();
|
|
11
|
+
const literalMatch = trimmed.match(REGEX_LITERAL);
|
|
12
|
+
if (literalMatch) {
|
|
13
|
+
const [, source, flags] = literalMatch;
|
|
14
|
+
const compiled = compileRegex(source, flags || "");
|
|
15
|
+
if (compiled) {
|
|
16
|
+
return {
|
|
17
|
+
pattern,
|
|
18
|
+
kind: "regex",
|
|
19
|
+
test: (url) => compiled.test(url)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const needle = trimmed.toLowerCase();
|
|
24
|
+
return {
|
|
25
|
+
pattern,
|
|
26
|
+
kind: "substring",
|
|
27
|
+
test: (url) => needle.length === 0 ? false : url.toLowerCase().includes(needle)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
buildWaitForUrlPredicate
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=wait-for-url-predicate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/actions/wait-for-url-predicate.ts"],"sourcesContent":["/**\n * Pure URL-matching predicate for the BROWSER `wait_for_url` subaction.\n *\n * A `pattern` is treated as a regular expression only when it is written as a\n * `/.../` literal (with optional flags); any other pattern is a\n * case-insensitive substring match. This keeps ordinary URL fragments like\n * `callback?code=` (which contain regex metacharacters) predictable. An invalid\n * regex literal always falls back to a substring match so the agent never\n * crashes on user input.\n *\n * Kept free of any browser/runtime imports so it stays trivially unit-testable.\n */\n\n/** How a given pattern was interpreted when building the predicate. */\nexport type WaitForUrlPatternKind = \"regex\" | \"substring\";\n\nexport interface WaitForUrlPredicate {\n /** The original, untrimmed pattern the caller supplied. */\n readonly pattern: string;\n /** How the pattern was interpreted (\"regex\" or \"substring\"). */\n readonly kind: WaitForUrlPatternKind;\n /** Returns true when `url` satisfies the pattern. */\n test(url: string): boolean;\n}\n\nconst REGEX_LITERAL = /^\\/(.+)\\/([a-z]*)$/i;\n\nfunction compileRegex(source: string, flags: string): RegExp | null {\n try {\n return new RegExp(source, flags);\n } catch {\n return null;\n }\n}\n\n/**\n * Build a {@link WaitForUrlPredicate} from a caller-supplied pattern.\n *\n * - `\"/foo\\\\d+/i\"` → regex `/foo\\d+/i`.\n * - `\"/\\\\/done$/\"` → regex.\n * - `\"callback?code=\"` → substring (case-insensitive), even though it contains\n * regex metacharacters.\n * - An invalid `/.../ ` literal → falls back to a case-insensitive substring\n * match on the original pattern text.\n */\nexport function buildWaitForUrlPredicate(pattern: string): WaitForUrlPredicate {\n const trimmed = pattern.trim();\n\n const literalMatch = trimmed.match(REGEX_LITERAL);\n if (literalMatch) {\n const [, source, flags] = literalMatch;\n const compiled = compileRegex(source, flags || \"\");\n if (compiled) {\n return {\n pattern,\n kind: \"regex\",\n test: (url: string) => compiled.test(url),\n };\n }\n // Invalid regex literal: fall through to substring on the raw pattern.\n }\n\n const needle = trimmed.toLowerCase();\n return {\n pattern,\n kind: \"substring\",\n test: (url: string) =>\n needle.length === 0 ? false : url.toLowerCase().includes(needle),\n };\n}\n"],"mappings":"AAyBA,MAAM,gBAAgB;AAEtB,SAAS,aAAa,QAAgB,OAA8B;AAClE,MAAI;AACF,WAAO,IAAI,OAAO,QAAQ,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,yBAAyB,SAAsC;AAC7E,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,eAAe,QAAQ,MAAM,aAAa;AAChD,MAAI,cAAc;AAChB,UAAM,CAAC,EAAE,QAAQ,KAAK,IAAI;AAC1B,UAAM,WAAW,aAAa,QAAQ,SAAS,EAAE;AACjD,QAAI,UAAU;AACZ,aAAO;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN,MAAM,CAAC,QAAgB,SAAS,KAAK,GAAG;AAAA,MAC1C;AAAA,IACF;AAAA,EAEF;AAEA,QAAM,SAAS,QAAQ,YAAY;AACnC,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,MAAM,CAAC,QACL,OAAO,WAAW,IAAI,QAAQ,IAAI,YAAY,EAAE,SAAS,MAAM;AAAA,EACnE;AACF;","names":[]}
|