@agentium/browser 2.0.7 → 2.0.8
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/index.cjs +107 -4
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +107 -4
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -150,7 +150,11 @@ function buildStealthContextOpts(config, viewport) {
|
|
|
150
150
|
locale: config.locale ?? "en-US",
|
|
151
151
|
timezoneId: config.timezone ?? "America/New_York",
|
|
152
152
|
colorScheme: "light",
|
|
153
|
-
|
|
153
|
+
// Default to 1 — matches the most common real-user setup and avoids
|
|
154
|
+
// visual zoom/stretch in headed mode on non-Retina displays (the host
|
|
155
|
+
// OS would downsample a 2× rendered surface). Users on Retina-only
|
|
156
|
+
// deployments can opt back into DPR=2 for sharper screenshots.
|
|
157
|
+
deviceScaleFactor: config.deviceScaleFactor ?? 1,
|
|
154
158
|
hasTouch: false,
|
|
155
159
|
javaScriptEnabled: true,
|
|
156
160
|
ignoreHTTPSErrors: config.ignoreHTTPSErrors ?? false
|
|
@@ -342,7 +346,24 @@ var BrowserProvider = class {
|
|
|
342
346
|
"[contenteditable='true']",
|
|
343
347
|
"[tabindex]:not([tabindex='-1'])"
|
|
344
348
|
];
|
|
345
|
-
const
|
|
349
|
+
const semantic = Array.from(doc.querySelectorAll(selectors.join(",")));
|
|
350
|
+
const POINTER_SCAN_CAP = 2e3;
|
|
351
|
+
const pointerCandidates = [];
|
|
352
|
+
const allEls = doc.querySelectorAll("*");
|
|
353
|
+
for (let i = 0; i < allEls.length && pointerCandidates.length < POINTER_SCAN_CAP; i++) {
|
|
354
|
+
const el = allEls[i];
|
|
355
|
+
if (!el) continue;
|
|
356
|
+
const tag = el.tagName.toLowerCase();
|
|
357
|
+
if (tag === "html" || tag === "body" || tag === "head" || tag === "script" || tag === "style") continue;
|
|
358
|
+
const rect = el.getBoundingClientRect();
|
|
359
|
+
if (rect.width < 4 || rect.height < 4) continue;
|
|
360
|
+
if (rect.bottom < 0 || rect.top > win.innerHeight) continue;
|
|
361
|
+
if (rect.right < 0 || rect.left > win.innerWidth) continue;
|
|
362
|
+
const style = win.getComputedStyle(el);
|
|
363
|
+
if (style.cursor !== "pointer") continue;
|
|
364
|
+
pointerCandidates.push(el);
|
|
365
|
+
}
|
|
366
|
+
const all = semantic.concat(pointerCandidates);
|
|
346
367
|
const vh = win.innerHeight;
|
|
347
368
|
const vw = win.innerWidth;
|
|
348
369
|
const entries = [];
|
|
@@ -364,6 +385,7 @@ var BrowserProvider = class {
|
|
|
364
385
|
if (hit && hit !== el && !el.contains(hit) && !hit.contains(el)) {
|
|
365
386
|
continue;
|
|
366
387
|
}
|
|
388
|
+
if (hit && el.contains(hit)) seen.add(hit);
|
|
367
389
|
} catch {
|
|
368
390
|
}
|
|
369
391
|
seen.add(el);
|
|
@@ -392,6 +414,29 @@ var BrowserProvider = class {
|
|
|
392
414
|
}, max);
|
|
393
415
|
return elements;
|
|
394
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Deterministic, DOM-based click using Playwright's text locator.
|
|
419
|
+
*
|
|
420
|
+
* Returns `true` if a matching, visible, clickable element was found and
|
|
421
|
+
* clicked within `timeout` ms; `false` otherwise (so the caller can fall
|
|
422
|
+
* back to coordinate clicking). Substring-matches by default — e.g.
|
|
423
|
+
* `clickByText("Cheapest")` matches "Cheapest · 23-28 days · $2,550".
|
|
424
|
+
*
|
|
425
|
+
* This is the same pattern browser-use uses in DOM-only mode and is far
|
|
426
|
+
* more reliable than coordinate clicks for text-bearing targets.
|
|
427
|
+
*/
|
|
428
|
+
async clickByText(keyword, opts) {
|
|
429
|
+
this.ensurePage();
|
|
430
|
+
const timeout = opts?.timeout ?? 3e3;
|
|
431
|
+
try {
|
|
432
|
+
const locator = this.page.locator(`text=${keyword}`).first();
|
|
433
|
+
await locator.click({ timeout });
|
|
434
|
+
await this.humanPause();
|
|
435
|
+
return true;
|
|
436
|
+
} catch {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
395
440
|
// ── Page Info ────────────────────────────────────────────────────────
|
|
396
441
|
async getPageInfo() {
|
|
397
442
|
this.ensurePage();
|
|
@@ -564,6 +609,7 @@ function buildSystemPrompt(viewport, extraInstructions, credentialKeys) {
|
|
|
564
609
|
`### click`,
|
|
565
610
|
`Click at a specific coordinate. Use for buttons, links, inputs, checkboxes, etc.`,
|
|
566
611
|
`\`{ "action": "click", "x": <number>, "y": <number>, "description": "<what you are clicking>" }\``,
|
|
612
|
+
`IMPORTANT: when the target has visible text, ALWAYS put that text in single or double quotes inside the description, e.g. \`"description": "Click on 'Cheapest' tab"\` or \`"description": "Press the \\"Sign in\\" button"\`. The runtime uses the quoted phrase to do a deterministic DOM text-locator click and only falls back to (x,y) if no match is found, which dramatically improves accuracy on dynamic pages.`,
|
|
567
613
|
``,
|
|
568
614
|
`### type`,
|
|
569
615
|
`Type text. If x/y are provided, click that position first (to focus the input), then type. If omitted, types into the currently focused element. To press Enter after typing (e.g., to submit a search), append "\\n" to the text.`,
|
|
@@ -968,9 +1014,20 @@ ${memoryContext}` : memoryContext;
|
|
|
968
1014
|
async executeAction(browser, action) {
|
|
969
1015
|
try {
|
|
970
1016
|
switch (action.action) {
|
|
971
|
-
case "click":
|
|
972
|
-
|
|
1017
|
+
case "click": {
|
|
1018
|
+
const keyword = this.extractClickKeyword(action.description);
|
|
1019
|
+
let clicked = false;
|
|
1020
|
+
if (keyword) {
|
|
1021
|
+
clicked = await browser.clickByText(keyword);
|
|
1022
|
+
if (clicked) {
|
|
1023
|
+
this.logger.debug("Clicked by text", { keyword });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (!clicked) {
|
|
1027
|
+
await browser.click(action.x, action.y);
|
|
1028
|
+
}
|
|
973
1029
|
break;
|
|
1030
|
+
}
|
|
974
1031
|
case "type": {
|
|
975
1032
|
const resolvedText = this.credentials ? this.credentials.resolve(action.text) : action.text;
|
|
976
1033
|
if (action.x != null && action.y != null) {
|
|
@@ -1007,6 +1064,52 @@ ${memoryContext}` : memoryContext;
|
|
|
1007
1064
|
sleep(ms) {
|
|
1008
1065
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1009
1066
|
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Parse a quoted target keyword from a click action's `description`.
|
|
1069
|
+
*
|
|
1070
|
+
* The system prompt asks the model to include the visible label of its
|
|
1071
|
+
* click target in quotes (e.g. `"Click on 'Cheapest' tab"`). When a usable
|
|
1072
|
+
* quoted phrase is present we return it so `executeAction` can try a
|
|
1073
|
+
* deterministic Playwright text locator before falling back to
|
|
1074
|
+
* coordinate clicking.
|
|
1075
|
+
*
|
|
1076
|
+
* Returns `undefined` for generic / ambiguous labels (login buttons,
|
|
1077
|
+
* close, OK, etc.) where a substring text match could trivially fire on
|
|
1078
|
+
* the wrong element.
|
|
1079
|
+
*/
|
|
1080
|
+
extractClickKeyword(description) {
|
|
1081
|
+
if (!description) return void 0;
|
|
1082
|
+
const match = description.match(/['"\u2018\u2019\u201C\u201D]([^'"\u2018\u2019\u201C\u201D]{1,80})['"\u2018\u2019\u201C\u201D]/);
|
|
1083
|
+
if (!match) return void 0;
|
|
1084
|
+
const keyword = match[1].trim();
|
|
1085
|
+
if (!keyword || keyword.length < 2) return void 0;
|
|
1086
|
+
const skip = /* @__PURE__ */ new Set([
|
|
1087
|
+
"log in",
|
|
1088
|
+
"login",
|
|
1089
|
+
"sign in",
|
|
1090
|
+
"sign up",
|
|
1091
|
+
"submit",
|
|
1092
|
+
"close",
|
|
1093
|
+
"ok",
|
|
1094
|
+
"okay",
|
|
1095
|
+
"cancel",
|
|
1096
|
+
"yes",
|
|
1097
|
+
"no",
|
|
1098
|
+
"x",
|
|
1099
|
+
"continue",
|
|
1100
|
+
"next",
|
|
1101
|
+
"back",
|
|
1102
|
+
"accept",
|
|
1103
|
+
"dismiss",
|
|
1104
|
+
"got it",
|
|
1105
|
+
"agree",
|
|
1106
|
+
"i agree",
|
|
1107
|
+
"allow",
|
|
1108
|
+
"deny"
|
|
1109
|
+
]);
|
|
1110
|
+
if (skip.has(keyword.toLowerCase())) return void 0;
|
|
1111
|
+
return keyword;
|
|
1112
|
+
}
|
|
1010
1113
|
};
|
|
1011
1114
|
|
|
1012
1115
|
// src/credential-vault.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -198,6 +198,13 @@ interface StealthConfig {
|
|
|
198
198
|
};
|
|
199
199
|
/** Ignore HTTPS certificate errors. Default: false (secure). Only enable for local testing. */
|
|
200
200
|
ignoreHTTPSErrors?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* `window.devicePixelRatio` to emulate. Default: `1`. Set to `2` to mimic
|
|
203
|
+
* a Retina display (sharper screenshots at 2× cost). Setting to 2 on a
|
|
204
|
+
* non-Retina host display can cause the headed window to look zoomed-out
|
|
205
|
+
* or stretched because the OS compositor downsamples a 2× surface.
|
|
206
|
+
*/
|
|
207
|
+
deviceScaleFactor?: number;
|
|
201
208
|
/** HTTP/SOCKS proxy. Format: "http://user:pass@host:port" */
|
|
202
209
|
proxy?: {
|
|
203
210
|
server: string;
|
|
@@ -259,6 +266,20 @@ declare class BrowserAgent {
|
|
|
259
266
|
private finalize;
|
|
260
267
|
private executeAction;
|
|
261
268
|
private sleep;
|
|
269
|
+
/**
|
|
270
|
+
* Parse a quoted target keyword from a click action's `description`.
|
|
271
|
+
*
|
|
272
|
+
* The system prompt asks the model to include the visible label of its
|
|
273
|
+
* click target in quotes (e.g. `"Click on 'Cheapest' tab"`). When a usable
|
|
274
|
+
* quoted phrase is present we return it so `executeAction` can try a
|
|
275
|
+
* deterministic Playwright text locator before falling back to
|
|
276
|
+
* coordinate clicking.
|
|
277
|
+
*
|
|
278
|
+
* Returns `undefined` for generic / ambiguous labels (login buttons,
|
|
279
|
+
* close, OK, etc.) where a substring text match could trivially fire on
|
|
280
|
+
* the wrong element.
|
|
281
|
+
*/
|
|
282
|
+
private extractClickKeyword;
|
|
262
283
|
}
|
|
263
284
|
|
|
264
285
|
/**
|
|
@@ -305,6 +326,20 @@ declare class BrowserProvider {
|
|
|
305
326
|
extractDOM(opts?: {
|
|
306
327
|
maxElements?: number;
|
|
307
328
|
}): Promise<string>;
|
|
329
|
+
/**
|
|
330
|
+
* Deterministic, DOM-based click using Playwright's text locator.
|
|
331
|
+
*
|
|
332
|
+
* Returns `true` if a matching, visible, clickable element was found and
|
|
333
|
+
* clicked within `timeout` ms; `false` otherwise (so the caller can fall
|
|
334
|
+
* back to coordinate clicking). Substring-matches by default — e.g.
|
|
335
|
+
* `clickByText("Cheapest")` matches "Cheapest · 23-28 days · $2,550".
|
|
336
|
+
*
|
|
337
|
+
* This is the same pattern browser-use uses in DOM-only mode and is far
|
|
338
|
+
* more reliable than coordinate clicks for text-bearing targets.
|
|
339
|
+
*/
|
|
340
|
+
clickByText(keyword: string, opts?: {
|
|
341
|
+
timeout?: number;
|
|
342
|
+
}): Promise<boolean>;
|
|
308
343
|
getPageInfo(): Promise<PageInfo>;
|
|
309
344
|
waitForStable(minWait?: number): Promise<void>;
|
|
310
345
|
newTab(url?: string): Promise<string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -198,6 +198,13 @@ interface StealthConfig {
|
|
|
198
198
|
};
|
|
199
199
|
/** Ignore HTTPS certificate errors. Default: false (secure). Only enable for local testing. */
|
|
200
200
|
ignoreHTTPSErrors?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* `window.devicePixelRatio` to emulate. Default: `1`. Set to `2` to mimic
|
|
203
|
+
* a Retina display (sharper screenshots at 2× cost). Setting to 2 on a
|
|
204
|
+
* non-Retina host display can cause the headed window to look zoomed-out
|
|
205
|
+
* or stretched because the OS compositor downsamples a 2× surface.
|
|
206
|
+
*/
|
|
207
|
+
deviceScaleFactor?: number;
|
|
201
208
|
/** HTTP/SOCKS proxy. Format: "http://user:pass@host:port" */
|
|
202
209
|
proxy?: {
|
|
203
210
|
server: string;
|
|
@@ -259,6 +266,20 @@ declare class BrowserAgent {
|
|
|
259
266
|
private finalize;
|
|
260
267
|
private executeAction;
|
|
261
268
|
private sleep;
|
|
269
|
+
/**
|
|
270
|
+
* Parse a quoted target keyword from a click action's `description`.
|
|
271
|
+
*
|
|
272
|
+
* The system prompt asks the model to include the visible label of its
|
|
273
|
+
* click target in quotes (e.g. `"Click on 'Cheapest' tab"`). When a usable
|
|
274
|
+
* quoted phrase is present we return it so `executeAction` can try a
|
|
275
|
+
* deterministic Playwright text locator before falling back to
|
|
276
|
+
* coordinate clicking.
|
|
277
|
+
*
|
|
278
|
+
* Returns `undefined` for generic / ambiguous labels (login buttons,
|
|
279
|
+
* close, OK, etc.) where a substring text match could trivially fire on
|
|
280
|
+
* the wrong element.
|
|
281
|
+
*/
|
|
282
|
+
private extractClickKeyword;
|
|
262
283
|
}
|
|
263
284
|
|
|
264
285
|
/**
|
|
@@ -305,6 +326,20 @@ declare class BrowserProvider {
|
|
|
305
326
|
extractDOM(opts?: {
|
|
306
327
|
maxElements?: number;
|
|
307
328
|
}): Promise<string>;
|
|
329
|
+
/**
|
|
330
|
+
* Deterministic, DOM-based click using Playwright's text locator.
|
|
331
|
+
*
|
|
332
|
+
* Returns `true` if a matching, visible, clickable element was found and
|
|
333
|
+
* clicked within `timeout` ms; `false` otherwise (so the caller can fall
|
|
334
|
+
* back to coordinate clicking). Substring-matches by default — e.g.
|
|
335
|
+
* `clickByText("Cheapest")` matches "Cheapest · 23-28 days · $2,550".
|
|
336
|
+
*
|
|
337
|
+
* This is the same pattern browser-use uses in DOM-only mode and is far
|
|
338
|
+
* more reliable than coordinate clicks for text-bearing targets.
|
|
339
|
+
*/
|
|
340
|
+
clickByText(keyword: string, opts?: {
|
|
341
|
+
timeout?: number;
|
|
342
|
+
}): Promise<boolean>;
|
|
308
343
|
getPageInfo(): Promise<PageInfo>;
|
|
309
344
|
waitForStable(minWait?: number): Promise<void>;
|
|
310
345
|
newTab(url?: string): Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -112,7 +112,11 @@ function buildStealthContextOpts(config, viewport) {
|
|
|
112
112
|
locale: config.locale ?? "en-US",
|
|
113
113
|
timezoneId: config.timezone ?? "America/New_York",
|
|
114
114
|
colorScheme: "light",
|
|
115
|
-
|
|
115
|
+
// Default to 1 — matches the most common real-user setup and avoids
|
|
116
|
+
// visual zoom/stretch in headed mode on non-Retina displays (the host
|
|
117
|
+
// OS would downsample a 2× rendered surface). Users on Retina-only
|
|
118
|
+
// deployments can opt back into DPR=2 for sharper screenshots.
|
|
119
|
+
deviceScaleFactor: config.deviceScaleFactor ?? 1,
|
|
116
120
|
hasTouch: false,
|
|
117
121
|
javaScriptEnabled: true,
|
|
118
122
|
ignoreHTTPSErrors: config.ignoreHTTPSErrors ?? false
|
|
@@ -304,7 +308,24 @@ var BrowserProvider = class {
|
|
|
304
308
|
"[contenteditable='true']",
|
|
305
309
|
"[tabindex]:not([tabindex='-1'])"
|
|
306
310
|
];
|
|
307
|
-
const
|
|
311
|
+
const semantic = Array.from(doc.querySelectorAll(selectors.join(",")));
|
|
312
|
+
const POINTER_SCAN_CAP = 2e3;
|
|
313
|
+
const pointerCandidates = [];
|
|
314
|
+
const allEls = doc.querySelectorAll("*");
|
|
315
|
+
for (let i = 0; i < allEls.length && pointerCandidates.length < POINTER_SCAN_CAP; i++) {
|
|
316
|
+
const el = allEls[i];
|
|
317
|
+
if (!el) continue;
|
|
318
|
+
const tag = el.tagName.toLowerCase();
|
|
319
|
+
if (tag === "html" || tag === "body" || tag === "head" || tag === "script" || tag === "style") continue;
|
|
320
|
+
const rect = el.getBoundingClientRect();
|
|
321
|
+
if (rect.width < 4 || rect.height < 4) continue;
|
|
322
|
+
if (rect.bottom < 0 || rect.top > win.innerHeight) continue;
|
|
323
|
+
if (rect.right < 0 || rect.left > win.innerWidth) continue;
|
|
324
|
+
const style = win.getComputedStyle(el);
|
|
325
|
+
if (style.cursor !== "pointer") continue;
|
|
326
|
+
pointerCandidates.push(el);
|
|
327
|
+
}
|
|
328
|
+
const all = semantic.concat(pointerCandidates);
|
|
308
329
|
const vh = win.innerHeight;
|
|
309
330
|
const vw = win.innerWidth;
|
|
310
331
|
const entries = [];
|
|
@@ -326,6 +347,7 @@ var BrowserProvider = class {
|
|
|
326
347
|
if (hit && hit !== el && !el.contains(hit) && !hit.contains(el)) {
|
|
327
348
|
continue;
|
|
328
349
|
}
|
|
350
|
+
if (hit && el.contains(hit)) seen.add(hit);
|
|
329
351
|
} catch {
|
|
330
352
|
}
|
|
331
353
|
seen.add(el);
|
|
@@ -354,6 +376,29 @@ var BrowserProvider = class {
|
|
|
354
376
|
}, max);
|
|
355
377
|
return elements;
|
|
356
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Deterministic, DOM-based click using Playwright's text locator.
|
|
381
|
+
*
|
|
382
|
+
* Returns `true` if a matching, visible, clickable element was found and
|
|
383
|
+
* clicked within `timeout` ms; `false` otherwise (so the caller can fall
|
|
384
|
+
* back to coordinate clicking). Substring-matches by default — e.g.
|
|
385
|
+
* `clickByText("Cheapest")` matches "Cheapest · 23-28 days · $2,550".
|
|
386
|
+
*
|
|
387
|
+
* This is the same pattern browser-use uses in DOM-only mode and is far
|
|
388
|
+
* more reliable than coordinate clicks for text-bearing targets.
|
|
389
|
+
*/
|
|
390
|
+
async clickByText(keyword, opts) {
|
|
391
|
+
this.ensurePage();
|
|
392
|
+
const timeout = opts?.timeout ?? 3e3;
|
|
393
|
+
try {
|
|
394
|
+
const locator = this.page.locator(`text=${keyword}`).first();
|
|
395
|
+
await locator.click({ timeout });
|
|
396
|
+
await this.humanPause();
|
|
397
|
+
return true;
|
|
398
|
+
} catch {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
357
402
|
// ── Page Info ────────────────────────────────────────────────────────
|
|
358
403
|
async getPageInfo() {
|
|
359
404
|
this.ensurePage();
|
|
@@ -526,6 +571,7 @@ function buildSystemPrompt(viewport, extraInstructions, credentialKeys) {
|
|
|
526
571
|
`### click`,
|
|
527
572
|
`Click at a specific coordinate. Use for buttons, links, inputs, checkboxes, etc.`,
|
|
528
573
|
`\`{ "action": "click", "x": <number>, "y": <number>, "description": "<what you are clicking>" }\``,
|
|
574
|
+
`IMPORTANT: when the target has visible text, ALWAYS put that text in single or double quotes inside the description, e.g. \`"description": "Click on 'Cheapest' tab"\` or \`"description": "Press the \\"Sign in\\" button"\`. The runtime uses the quoted phrase to do a deterministic DOM text-locator click and only falls back to (x,y) if no match is found, which dramatically improves accuracy on dynamic pages.`,
|
|
529
575
|
``,
|
|
530
576
|
`### type`,
|
|
531
577
|
`Type text. If x/y are provided, click that position first (to focus the input), then type. If omitted, types into the currently focused element. To press Enter after typing (e.g., to submit a search), append "\\n" to the text.`,
|
|
@@ -930,9 +976,20 @@ ${memoryContext}` : memoryContext;
|
|
|
930
976
|
async executeAction(browser, action) {
|
|
931
977
|
try {
|
|
932
978
|
switch (action.action) {
|
|
933
|
-
case "click":
|
|
934
|
-
|
|
979
|
+
case "click": {
|
|
980
|
+
const keyword = this.extractClickKeyword(action.description);
|
|
981
|
+
let clicked = false;
|
|
982
|
+
if (keyword) {
|
|
983
|
+
clicked = await browser.clickByText(keyword);
|
|
984
|
+
if (clicked) {
|
|
985
|
+
this.logger.debug("Clicked by text", { keyword });
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (!clicked) {
|
|
989
|
+
await browser.click(action.x, action.y);
|
|
990
|
+
}
|
|
935
991
|
break;
|
|
992
|
+
}
|
|
936
993
|
case "type": {
|
|
937
994
|
const resolvedText = this.credentials ? this.credentials.resolve(action.text) : action.text;
|
|
938
995
|
if (action.x != null && action.y != null) {
|
|
@@ -969,6 +1026,52 @@ ${memoryContext}` : memoryContext;
|
|
|
969
1026
|
sleep(ms) {
|
|
970
1027
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
971
1028
|
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Parse a quoted target keyword from a click action's `description`.
|
|
1031
|
+
*
|
|
1032
|
+
* The system prompt asks the model to include the visible label of its
|
|
1033
|
+
* click target in quotes (e.g. `"Click on 'Cheapest' tab"`). When a usable
|
|
1034
|
+
* quoted phrase is present we return it so `executeAction` can try a
|
|
1035
|
+
* deterministic Playwright text locator before falling back to
|
|
1036
|
+
* coordinate clicking.
|
|
1037
|
+
*
|
|
1038
|
+
* Returns `undefined` for generic / ambiguous labels (login buttons,
|
|
1039
|
+
* close, OK, etc.) where a substring text match could trivially fire on
|
|
1040
|
+
* the wrong element.
|
|
1041
|
+
*/
|
|
1042
|
+
extractClickKeyword(description) {
|
|
1043
|
+
if (!description) return void 0;
|
|
1044
|
+
const match = description.match(/['"\u2018\u2019\u201C\u201D]([^'"\u2018\u2019\u201C\u201D]{1,80})['"\u2018\u2019\u201C\u201D]/);
|
|
1045
|
+
if (!match) return void 0;
|
|
1046
|
+
const keyword = match[1].trim();
|
|
1047
|
+
if (!keyword || keyword.length < 2) return void 0;
|
|
1048
|
+
const skip = /* @__PURE__ */ new Set([
|
|
1049
|
+
"log in",
|
|
1050
|
+
"login",
|
|
1051
|
+
"sign in",
|
|
1052
|
+
"sign up",
|
|
1053
|
+
"submit",
|
|
1054
|
+
"close",
|
|
1055
|
+
"ok",
|
|
1056
|
+
"okay",
|
|
1057
|
+
"cancel",
|
|
1058
|
+
"yes",
|
|
1059
|
+
"no",
|
|
1060
|
+
"x",
|
|
1061
|
+
"continue",
|
|
1062
|
+
"next",
|
|
1063
|
+
"back",
|
|
1064
|
+
"accept",
|
|
1065
|
+
"dismiss",
|
|
1066
|
+
"got it",
|
|
1067
|
+
"agree",
|
|
1068
|
+
"i agree",
|
|
1069
|
+
"allow",
|
|
1070
|
+
"deny"
|
|
1071
|
+
]);
|
|
1072
|
+
if (skip.has(keyword.toLowerCase())) return void 0;
|
|
1073
|
+
return keyword;
|
|
1074
|
+
}
|
|
972
1075
|
};
|
|
973
1076
|
|
|
974
1077
|
// src/credential-vault.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentium/browser",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "Browser automation agent for Agentium using Playwright",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typescript": "^5.6.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@agentium/core": "^2.0.
|
|
49
|
+
"@agentium/core": "^2.0.8",
|
|
50
50
|
"playwright": ">=1.40.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|