@proagentstore/cli 0.3.6 → 0.3.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.
|
@@ -63,6 +63,11 @@ async function typeRobustly(loc, text) {
|
|
|
63
63
|
*/
|
|
64
64
|
export async function performBrowserAction(page, action, clickRobustly, resumeFile) {
|
|
65
65
|
const locate = () => {
|
|
66
|
+
// Prefer the stable snapshot ref (aria-ref) — points at the exact element, so two
|
|
67
|
+
// fields sharing a label (e.g. a phone "Country" code and an address "Country")
|
|
68
|
+
// are never confused. Fall back to role+name for older snapshots.
|
|
69
|
+
if (action.ref)
|
|
70
|
+
return page.locator(`aria-ref=${action.ref}`);
|
|
66
71
|
const role = action.role;
|
|
67
72
|
let loc = role
|
|
68
73
|
? page.getByRole(role, action.name ? { name: action.name } : undefined)
|
|
@@ -267,12 +272,17 @@ export async function performBrowserAction(page, action, clickRobustly, resumeFi
|
|
|
267
272
|
export async function inspectField(page, action) {
|
|
268
273
|
if (action.action !== "type" && action.action !== "select" && action.action !== "check")
|
|
269
274
|
return "";
|
|
270
|
-
if (!action.name)
|
|
275
|
+
if (!action.name && !action.ref)
|
|
271
276
|
return "";
|
|
272
277
|
try {
|
|
273
278
|
const role = action.role;
|
|
274
|
-
let loc =
|
|
275
|
-
|
|
279
|
+
let loc = action.ref
|
|
280
|
+
? page.locator(`aria-ref=${action.ref}`)
|
|
281
|
+
: role
|
|
282
|
+
? page.getByRole(role, action.name ? { name: action.name } : undefined)
|
|
283
|
+
: page.getByText(action.name ?? "", { exact: false });
|
|
284
|
+
if (!action.ref)
|
|
285
|
+
loc = typeof action.nth === "number" ? loc.nth(action.nth) : loc.first();
|
|
276
286
|
const el = await loc.elementHandle({ timeout: 1_500 }).catch(() => null);
|
|
277
287
|
if (!el)
|
|
278
288
|
return "";
|
|
@@ -302,7 +312,8 @@ export async function inspectField(page, action) {
|
|
|
302
312
|
if (t && rx.test(t) && t.length < 180)
|
|
303
313
|
err = t;
|
|
304
314
|
}
|
|
305
|
-
|
|
315
|
+
const disabled = e.hasAttribute("disabled") || e.getAttribute("aria-disabled") === "true" || e.hasAttribute("readonly");
|
|
316
|
+
return { value, invalid, err: err.replace(/\s+/g, " ").slice(0, 180), tag: e.tagName.toLowerCase(), html: (e.outerHTML || "").replace(/\s+/g, " ").slice(0, 500), disabled };
|
|
306
317
|
})
|
|
307
318
|
.catch(() => null);
|
|
308
319
|
if (!info)
|
|
@@ -321,15 +332,22 @@ export async function inspectField(page, action) {
|
|
|
321
332
|
// or belongs to another field — reporting it as REJECTED sent the brain into a
|
|
322
333
|
// false-retry loop on a value that was actually accepted.
|
|
323
334
|
if (info.invalid || (typed && !stuck)) {
|
|
324
|
-
if (info.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
parts.unshift(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
335
|
+
if (info.disabled) {
|
|
336
|
+
// A disabled/read-only control can't be changed — it's almost always already
|
|
337
|
+
// set correctly. Tell the brain to move on instead of fixating on it.
|
|
338
|
+
parts.unshift(`ℹ "${action.name}" is DISABLED / read-only — it is likely already set correctly; do NOT keep trying, move on to other fields.`);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
if (info.err)
|
|
342
|
+
parts.unshift(`⚠ "${action.name}" REJECTED: "${info.err}"`);
|
|
343
|
+
else if (info.invalid)
|
|
344
|
+
parts.unshift(`⚠ "${action.name}" is marked invalid`);
|
|
345
|
+
// Value didn't take / field invalid → show the widget's real DOM so the brain
|
|
346
|
+
// can pick the right interaction (custom dropdown → click to open + click the
|
|
347
|
+
// option by text; masked input → a different shape) instead of blindly retrying.
|
|
348
|
+
if (info.html)
|
|
349
|
+
parts.push(`DOM: <${info.tag}> ${info.html}`);
|
|
350
|
+
}
|
|
333
351
|
}
|
|
334
352
|
return parts.join("; ");
|
|
335
353
|
}
|
|
@@ -620,7 +620,15 @@ export class LocalRunner {
|
|
|
620
620
|
}
|
|
621
621
|
const page = await this.getActivePage();
|
|
622
622
|
await page.waitForLoadState("domcontentloaded", { timeout: 5_000 }).catch(() => undefined);
|
|
623
|
-
|
|
623
|
+
// Match what a direct Claude + Playwright-MCP agent sees: `ariaSnapshot({mode:"ai"})`
|
|
624
|
+
// carries element STATE (disabled/checked/expanded/selected/value) and a stable
|
|
625
|
+
// [ref=eNN] on each interactive element, so the brain targets them unambiguously via
|
|
626
|
+
// the aria-ref selector (instead of role+name, which collides when two fields share a
|
|
627
|
+
// label). This is exactly the call Playwright's own MCP uses. Falls back to the plain
|
|
628
|
+
// aria snapshot if the option is ever unavailable.
|
|
629
|
+
const aria = page;
|
|
630
|
+
const full = (await aria.ariaSnapshot({ mode: "ai" }).catch(() => "")) ||
|
|
631
|
+
(await page.locator("body").ariaSnapshot().catch(() => ""));
|
|
624
632
|
const MAX = 16_000;
|
|
625
633
|
const snapshot = full.length > MAX ? `${full.slice(0, MAX)}\n… [snapshot truncated]` : full;
|
|
626
634
|
return {
|