@runcofactor/cli 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-ZMV7NEFM.js → account-ZSM2Q3RB.js} +4 -4
- package/dist/{api-catalog-6HNOGVPA.js → api-catalog-MMPFWS52.js} +4 -4
- package/dist/{attachment-QAHR54HQ.js → attachment-NSYL5X3S.js} +4 -4
- package/dist/{augmentor-7T3L6ZR4.js → augmentor-LSJLMJYP.js} +4 -4
- package/dist/{auth-DURHUZDW.js → auth-OYF5A4BY.js} +4 -2
- package/dist/{chunk-4NRDONSC.js → chunk-FGB6PS2T.js} +4 -4
- package/dist/chunk-FGB6PS2T.js.map +1 -0
- package/dist/{chunk-D6AVOPOS.js → chunk-GFP4UEUT.js} +3 -3
- package/dist/{chunk-G5SXUXSU.js → chunk-H7ZML5QL.js} +2 -2
- package/dist/{chunk-CEXV3MOC.js → chunk-HGPZ2QWV.js} +3 -3
- package/dist/{chunk-LFFWD5JZ.js → chunk-IHCPMYKU.js} +3 -3
- package/dist/{chunk-RGDIVW3O.js → chunk-JZWJFG4M.js} +2 -2
- package/dist/{chunk-AI3Y3AZH.js → chunk-MO5G5TP5.js} +404 -96
- package/dist/chunk-MO5G5TP5.js.map +1 -0
- package/dist/{chunk-UAJUZLDE.js → chunk-PVWG3V62.js} +3 -3
- package/dist/{chunk-3CZU7XCS.js → chunk-PWZ4ULCB.js} +20 -14
- package/dist/chunk-PWZ4ULCB.js.map +1 -0
- package/dist/{chunk-DT2CEH2V.js → chunk-VHVOTGBQ.js} +3 -3
- package/dist/{chunk-IGHDIWZU.js → chunk-VYTIJ6HJ.js} +17 -2
- package/dist/{chunk-IGHDIWZU.js.map → chunk-VYTIJ6HJ.js.map} +1 -1
- package/dist/{credential-X6QKX473.js → credential-WOATCWOU.js} +4 -4
- package/dist/{credential-template-GD3D4MBI.js → credential-template-Q3DQE2AH.js} +4 -4
- package/dist/{debug-workflow-AAEOAME2.js → debug-workflow-ZNP5XZQF.js} +4 -4
- package/dist/{dist-QLTVBUMD.js → dist-WW2S733L.js} +2 -2
- package/dist/{docs-BVUEXBY3.js → docs-XVU4LA7G.js} +4 -4
- package/dist/{doctor-RHMKD4W5.js → doctor-PGNHD3TI.js} +3 -3
- package/dist/{environment-WE7VVE6I.js → environment-MRDNWU47.js} +4 -4
- package/dist/{feature-flag-VJO2TJFT.js → feature-flag-D3DQZ6DX.js} +4 -4
- package/dist/index.js +51 -48
- package/dist/index.js.map +1 -1
- package/dist/{init-RQHHKJJD.js → init-DHWKUYIR.js} +4 -4
- package/dist/{install-CQLHNZ6U.js → install-DMEFGQHD.js} +4 -4
- package/dist/{issues-HK2JA5JS.js → issues-E4YQ4DVC.js} +4 -4
- package/dist/{knobs-ETJAV6YM.js → knobs-NA6SPLHX.js} +4 -4
- package/dist/{list-F6DGGW3Z.js → list-GJNGFQMC.js} +4 -4
- package/dist/{local-CMAY54I5.js → local-NSKOZHHA.js} +6 -6
- package/dist/{login-6CKG664C.js → login-AMSRVPXG.js} +4 -4
- package/dist/{mcp-GC2WOOZO.js → mcp-V4GKEL2J.js} +5 -5
- package/dist/{metrics-IXICURAA.js → metrics-DM4YQQZN.js} +4 -4
- package/dist/{product-BUJWPYKB.js → product-WGOLTBRI.js} +4 -4
- package/dist/{property-4EXPWRNT.js → property-B7QMGLVW.js} +4 -4
- package/dist/{psql-XGLBI6T7.js → psql-4JHPJ2CO.js} +3 -3
- package/dist/{record-HL4QA7OX.js → record-LDNKS42Y.js} +5 -5
- package/dist/{redis-VJGCUJPV.js → redis-N44ZZ6VB.js} +3 -3
- package/dist/{release-2SMK5Y3V.js → release-R7GKLEAI.js} +4 -4
- package/dist/{sandbox-ORAPKOH3.js → sandbox-PLYUNOFX.js} +5 -5
- package/dist/{scenarios-DM5FDCAF.js → scenarios-WUUUKP2S.js} +4 -4
- package/dist/{session-X6IYGWYG.js → session-Y6RO7GFC.js} +6 -6
- package/dist/{skill-ZU5V2EE5.js → skill-3SP7MAA4.js} +153 -2
- package/dist/skill-3SP7MAA4.js.map +1 -0
- package/dist/{spec-KO5QD6AY.js → spec-ESPA3H26.js} +3 -3
- package/dist/{src-DSD34JIX.js → src-ECFPDE5X.js} +17 -1
- package/dist/src-ECFPDE5X.js.map +1 -0
- package/dist/{src-HTGNKBUF.js → src-OWPS5DCK.js} +60 -22
- package/dist/{src-HTGNKBUF.js.map → src-OWPS5DCK.js.map} +1 -1
- package/dist/{start-2ECN4LX5.js → start-KDDRLUFN.js} +4 -4
- package/dist/{tag-R5I72HJ6.js → tag-DAGHQZAR.js} +5 -5
- package/dist/{task-V3JMSJIM.js → task-PRR75HI4.js} +4 -4
- package/dist/{test-suite-NOYVK6YX.js → test-suite-O3UY7EER.js} +6 -6
- package/dist/{trace-NOYEKNO4.js → trace-BCSP7PCX.js} +4 -4
- package/dist/{triage-4RFATHBE.js → triage-2BKUBMMX.js} +5 -5
- package/dist/{utilities-3WMXE743.js → utilities-O3OO64T5.js} +4 -4
- package/dist/{workflow-DC7WOTTW.js → workflow-76EKT3DS.js} +10 -11
- package/dist/workflow-76EKT3DS.js.map +1 -0
- package/dist/{workflow-cache-SDSI6SMF.js → workflow-cache-UDTTNIHT.js} +7 -7
- package/dist/{workflow-debug-6UQYP67Q.js → workflow-debug-WO7D32EI.js} +7 -7
- package/dist/{workflow-proposals-67AVGMHX.js → workflow-proposals-PGURJ6ZI.js} +4 -4
- package/package.json +3 -3
- package/dist/chunk-3CZU7XCS.js.map +0 -1
- package/dist/chunk-4NRDONSC.js.map +0 -1
- package/dist/chunk-AI3Y3AZH.js.map +0 -1
- package/dist/skill-ZU5V2EE5.js.map +0 -1
- package/dist/src-DSD34JIX.js.map +0 -1
- package/dist/workflow-DC7WOTTW.js.map +0 -1
- /package/dist/{account-ZMV7NEFM.js.map → account-ZSM2Q3RB.js.map} +0 -0
- /package/dist/{api-catalog-6HNOGVPA.js.map → api-catalog-MMPFWS52.js.map} +0 -0
- /package/dist/{attachment-QAHR54HQ.js.map → attachment-NSYL5X3S.js.map} +0 -0
- /package/dist/{augmentor-7T3L6ZR4.js.map → augmentor-LSJLMJYP.js.map} +0 -0
- /package/dist/{auth-DURHUZDW.js.map → auth-OYF5A4BY.js.map} +0 -0
- /package/dist/{chunk-D6AVOPOS.js.map → chunk-GFP4UEUT.js.map} +0 -0
- /package/dist/{chunk-G5SXUXSU.js.map → chunk-H7ZML5QL.js.map} +0 -0
- /package/dist/{chunk-CEXV3MOC.js.map → chunk-HGPZ2QWV.js.map} +0 -0
- /package/dist/{chunk-LFFWD5JZ.js.map → chunk-IHCPMYKU.js.map} +0 -0
- /package/dist/{chunk-RGDIVW3O.js.map → chunk-JZWJFG4M.js.map} +0 -0
- /package/dist/{chunk-UAJUZLDE.js.map → chunk-PVWG3V62.js.map} +0 -0
- /package/dist/{chunk-DT2CEH2V.js.map → chunk-VHVOTGBQ.js.map} +0 -0
- /package/dist/{credential-X6QKX473.js.map → credential-WOATCWOU.js.map} +0 -0
- /package/dist/{credential-template-GD3D4MBI.js.map → credential-template-Q3DQE2AH.js.map} +0 -0
- /package/dist/{debug-workflow-AAEOAME2.js.map → debug-workflow-ZNP5XZQF.js.map} +0 -0
- /package/dist/{dist-QLTVBUMD.js.map → dist-WW2S733L.js.map} +0 -0
- /package/dist/{docs-BVUEXBY3.js.map → docs-XVU4LA7G.js.map} +0 -0
- /package/dist/{doctor-RHMKD4W5.js.map → doctor-PGNHD3TI.js.map} +0 -0
- /package/dist/{environment-WE7VVE6I.js.map → environment-MRDNWU47.js.map} +0 -0
- /package/dist/{feature-flag-VJO2TJFT.js.map → feature-flag-D3DQZ6DX.js.map} +0 -0
- /package/dist/{init-RQHHKJJD.js.map → init-DHWKUYIR.js.map} +0 -0
- /package/dist/{install-CQLHNZ6U.js.map → install-DMEFGQHD.js.map} +0 -0
- /package/dist/{issues-HK2JA5JS.js.map → issues-E4YQ4DVC.js.map} +0 -0
- /package/dist/{knobs-ETJAV6YM.js.map → knobs-NA6SPLHX.js.map} +0 -0
- /package/dist/{list-F6DGGW3Z.js.map → list-GJNGFQMC.js.map} +0 -0
- /package/dist/{local-CMAY54I5.js.map → local-NSKOZHHA.js.map} +0 -0
- /package/dist/{login-6CKG664C.js.map → login-AMSRVPXG.js.map} +0 -0
- /package/dist/{mcp-GC2WOOZO.js.map → mcp-V4GKEL2J.js.map} +0 -0
- /package/dist/{metrics-IXICURAA.js.map → metrics-DM4YQQZN.js.map} +0 -0
- /package/dist/{product-BUJWPYKB.js.map → product-WGOLTBRI.js.map} +0 -0
- /package/dist/{property-4EXPWRNT.js.map → property-B7QMGLVW.js.map} +0 -0
- /package/dist/{psql-XGLBI6T7.js.map → psql-4JHPJ2CO.js.map} +0 -0
- /package/dist/{record-HL4QA7OX.js.map → record-LDNKS42Y.js.map} +0 -0
- /package/dist/{redis-VJGCUJPV.js.map → redis-N44ZZ6VB.js.map} +0 -0
- /package/dist/{release-2SMK5Y3V.js.map → release-R7GKLEAI.js.map} +0 -0
- /package/dist/{sandbox-ORAPKOH3.js.map → sandbox-PLYUNOFX.js.map} +0 -0
- /package/dist/{scenarios-DM5FDCAF.js.map → scenarios-WUUUKP2S.js.map} +0 -0
- /package/dist/{session-X6IYGWYG.js.map → session-Y6RO7GFC.js.map} +0 -0
- /package/dist/{spec-KO5QD6AY.js.map → spec-ESPA3H26.js.map} +0 -0
- /package/dist/{start-2ECN4LX5.js.map → start-KDDRLUFN.js.map} +0 -0
- /package/dist/{tag-R5I72HJ6.js.map → tag-DAGHQZAR.js.map} +0 -0
- /package/dist/{task-V3JMSJIM.js.map → task-PRR75HI4.js.map} +0 -0
- /package/dist/{test-suite-NOYVK6YX.js.map → test-suite-O3UY7EER.js.map} +0 -0
- /package/dist/{trace-NOYEKNO4.js.map → trace-BCSP7PCX.js.map} +0 -0
- /package/dist/{triage-4RFATHBE.js.map → triage-2BKUBMMX.js.map} +0 -0
- /package/dist/{utilities-3WMXE743.js.map → utilities-O3OO64T5.js.map} +0 -0
- /package/dist/{workflow-cache-SDSI6SMF.js.map → workflow-cache-UDTTNIHT.js.map} +0 -0
- /package/dist/{workflow-debug-6UQYP67Q.js.map → workflow-debug-WO7D32EI.js.map} +0 -0
- /package/dist/{workflow-proposals-67AVGMHX.js.map → workflow-proposals-PGURJ6ZI.js.map} +0 -0
|
@@ -50122,6 +50122,52 @@ var ToolAbortedError = class extends Error {
|
|
|
50122
50122
|
this.name = "ToolAbortedError";
|
|
50123
50123
|
}
|
|
50124
50124
|
};
|
|
50125
|
+
function labelForContext(context) {
|
|
50126
|
+
return context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
|
|
50127
|
+
}
|
|
50128
|
+
function elementDescription(context) {
|
|
50129
|
+
const role = context.role ? ` role=${context.role}` : "";
|
|
50130
|
+
const type = context.inputType ? ` type=${context.inputType}` : "";
|
|
50131
|
+
return `${context.tagName}${type}${role}`;
|
|
50132
|
+
}
|
|
50133
|
+
function formatFillTargetNotEditableMessage(context) {
|
|
50134
|
+
if (context.state === "not-fillable") {
|
|
50135
|
+
return `Cannot fill target ${labelForContext(context)}: ${elementDescription(context)} cannot accept text.`;
|
|
50136
|
+
}
|
|
50137
|
+
return `Cannot fill ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`;
|
|
50138
|
+
}
|
|
50139
|
+
var FillTargetNotEditableError = class extends Error {
|
|
50140
|
+
fillTarget;
|
|
50141
|
+
constructor(context) {
|
|
50142
|
+
super(formatFillTargetNotEditableMessage(context));
|
|
50143
|
+
this.name = "FillTargetNotEditableError";
|
|
50144
|
+
this.fillTarget = context;
|
|
50145
|
+
}
|
|
50146
|
+
};
|
|
50147
|
+
function isRecord(value) {
|
|
50148
|
+
return typeof value === "object" && value !== null;
|
|
50149
|
+
}
|
|
50150
|
+
function isFillTargetNotEditableError(error) {
|
|
50151
|
+
if (error instanceof FillTargetNotEditableError) return true;
|
|
50152
|
+
if (!isRecord(error) || error.name !== "FillTargetNotEditableError") return false;
|
|
50153
|
+
if (!isRecord(error.fillTarget)) return false;
|
|
50154
|
+
const context = error.fillTarget;
|
|
50155
|
+
return typeof context.ref === "string" && typeof context.tagName === "string" && (context.state === "readonly" || context.state === "disabled" || context.state === "not-fillable");
|
|
50156
|
+
}
|
|
50157
|
+
function formatFillTargetNotEditableRecovery(error, toolName = "browser_fill_form") {
|
|
50158
|
+
const context = error.fillTarget;
|
|
50159
|
+
const action = toolName === "browser_type" ? "type into" : "fill";
|
|
50160
|
+
if (context.state === "not-fillable") {
|
|
50161
|
+
return [
|
|
50162
|
+
`Error: ${toolName} cannot ${action} target ${labelForContext(context)}: ${elementDescription(context)} cannot accept typed text.`,
|
|
50163
|
+
`Do not retry ${toolName} for this ref. Call browser_snapshot and choose the actual editable input or textarea. If the target is a dropdown, use browser_select_option or click the dropdown and select an option.`
|
|
50164
|
+
].join(" ");
|
|
50165
|
+
}
|
|
50166
|
+
return [
|
|
50167
|
+
`Error: ${toolName} cannot ${action} ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`,
|
|
50168
|
+
`Do not retry ${toolName} for this ref. Call browser_snapshot and choose an editable input or textarea for the same requirement, or complete the prerequisite that unlocks this field first.`
|
|
50169
|
+
].join(" ");
|
|
50170
|
+
}
|
|
50125
50171
|
function isMCPContentWithImages(result) {
|
|
50126
50172
|
return typeof result === "object" && result !== null && "text" in result && "images" in result && Array.isArray(result.images) && result.images.length > 0;
|
|
50127
50173
|
}
|
|
@@ -50212,6 +50258,31 @@ function formatEmptyPressKeyRecovery() {
|
|
|
50212
50258
|
'Do not retry with an empty string. Use a Playwright key such as "Enter", "Escape", "Tab", "ArrowDown", or use browser_type when you need to enter text.'
|
|
50213
50259
|
].join(" ");
|
|
50214
50260
|
}
|
|
50261
|
+
function normalizeRef(ref2) {
|
|
50262
|
+
const trimmed = ref2?.trim();
|
|
50263
|
+
return trimmed ? trimmed : void 0;
|
|
50264
|
+
}
|
|
50265
|
+
function formatEmptyRefRecovery(toolName) {
|
|
50266
|
+
return [
|
|
50267
|
+
`Error: ${toolName} requires a non-empty element ref from the latest browser_snapshot.`,
|
|
50268
|
+
'Do not retry with an empty string. Call browser_snapshot and use a concrete ref such as "e42", or use the coordinate path when that tool supports coordinates.'
|
|
50269
|
+
].join(" ");
|
|
50270
|
+
}
|
|
50271
|
+
function extractWaitingAriaRef(error) {
|
|
50272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50273
|
+
return message.match(/waiting for locator\('aria-ref=([^']+)'\)/)?.[1];
|
|
50274
|
+
}
|
|
50275
|
+
function isStaleAriaRefActionTimeout(error, action) {
|
|
50276
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50277
|
+
return message.includes(`${action}: Timeout`) && message.includes("waiting for locator('aria-ref=") && !message.includes("locator resolved to <");
|
|
50278
|
+
}
|
|
50279
|
+
function formatStaleRefActionRecovery(toolName, ref2, element) {
|
|
50280
|
+
const label = element ? ` for "${element}"` : "";
|
|
50281
|
+
return [
|
|
50282
|
+
`Error: ${toolName} could not find ref ${ref2}${label} in the current page snapshot.`,
|
|
50283
|
+
"Do not retry the same ref. Call browser_snapshot and choose a fresh ref, or use the coordinate path when that tool supports coordinates."
|
|
50284
|
+
].join(" ");
|
|
50285
|
+
}
|
|
50215
50286
|
function isSelectOptionNonSelectError(error) {
|
|
50216
50287
|
const message = error instanceof Error ? error.message : String(error);
|
|
50217
50288
|
return message.includes("selectOption:") && message.includes("Element is not a <select>");
|
|
@@ -50219,6 +50290,35 @@ function isSelectOptionNonSelectError(error) {
|
|
|
50219
50290
|
function isMissingDownloadTempFileError(message) {
|
|
50220
50291
|
return /\bENOENT:\s+no such file or directory,\s+open\b/.test(message) && message.includes("/playwright-downloads-");
|
|
50221
50292
|
}
|
|
50293
|
+
function isNavigateDownloadStartingError(error) {
|
|
50294
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50295
|
+
return /\bgoto:\s+Download is starting\b/.test(message);
|
|
50296
|
+
}
|
|
50297
|
+
function formatNavigateDownloadStartingRecovery() {
|
|
50298
|
+
return [
|
|
50299
|
+
"Error: browser_navigate started a file download instead of loading a page.",
|
|
50300
|
+
"Do not retry browser_navigate for the same URL. Call browser_list_downloads to find the captured file, then use browser_read_download or browser_pdf_read to inspect it."
|
|
50301
|
+
].join(" ");
|
|
50302
|
+
}
|
|
50303
|
+
function isRelativeNavigateTarget(url) {
|
|
50304
|
+
const trimmed = url.trim();
|
|
50305
|
+
return trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("?") || trimmed.startsWith("#");
|
|
50306
|
+
}
|
|
50307
|
+
function isHttpUrl(url) {
|
|
50308
|
+
try {
|
|
50309
|
+
const parsed = new URL(url);
|
|
50310
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
50311
|
+
} catch {
|
|
50312
|
+
return false;
|
|
50313
|
+
}
|
|
50314
|
+
}
|
|
50315
|
+
function formatRelativeNavigateRecovery(url, currentUrl) {
|
|
50316
|
+
const baseDescription = currentUrl ? `current page URL is "${currentUrl}"` : "current page URL is unavailable";
|
|
50317
|
+
return [
|
|
50318
|
+
`Error: browser_navigate received relative URL "${url}", but the ${baseDescription}.`,
|
|
50319
|
+
"Use an absolute http(s) URL, or call browser_snapshot first if the current page needs to be inspected before navigating."
|
|
50320
|
+
].join(" ");
|
|
50321
|
+
}
|
|
50222
50322
|
function formatSelectOptionNonSelectRecovery(ref2, value, element) {
|
|
50223
50323
|
return [
|
|
50224
50324
|
`Error: browser_select_option can only operate on native <select> elements; ref ${ref2} for "${element}" resolved to a custom dropdown or combobox.`,
|
|
@@ -50676,11 +50776,38 @@ var BrowserToolExecutor = class {
|
|
|
50676
50776
|
}
|
|
50677
50777
|
// ==================== Navigation ====================
|
|
50678
50778
|
async navigate(url) {
|
|
50679
|
-
|
|
50680
|
-
|
|
50779
|
+
const navigationUrl = await this.resolveNavigateUrl(url);
|
|
50780
|
+
if (navigationUrl.kind === "recovery") {
|
|
50781
|
+
return navigationUrl.message;
|
|
50782
|
+
}
|
|
50783
|
+
ensureAllowedHost(navigationUrl.url, this.allowedHosts);
|
|
50784
|
+
let rawResult;
|
|
50785
|
+
try {
|
|
50786
|
+
rawResult = await this.client.navigate(navigationUrl.url, { signal: this.currentSignal });
|
|
50787
|
+
} catch (error) {
|
|
50788
|
+
if (isNavigateDownloadStartingError(error)) {
|
|
50789
|
+
return formatNavigateDownloadStartingRecovery();
|
|
50790
|
+
}
|
|
50791
|
+
throw error;
|
|
50792
|
+
}
|
|
50681
50793
|
const result = withRecoveryNotice(this.client, rawResult, this.logger);
|
|
50682
50794
|
return this.formatNavigateResult(result);
|
|
50683
50795
|
}
|
|
50796
|
+
async resolveNavigateUrl(url) {
|
|
50797
|
+
const trimmedUrl = url.trim();
|
|
50798
|
+
if (!isRelativeNavigateTarget(trimmedUrl)) {
|
|
50799
|
+
return { kind: "url", url: trimmedUrl };
|
|
50800
|
+
}
|
|
50801
|
+
const currentUrl = await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0);
|
|
50802
|
+
if (!currentUrl || !isHttpUrl(currentUrl)) {
|
|
50803
|
+
return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
|
|
50804
|
+
}
|
|
50805
|
+
try {
|
|
50806
|
+
return { kind: "url", url: new URL(trimmedUrl, currentUrl).toString() };
|
|
50807
|
+
} catch {
|
|
50808
|
+
return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
|
|
50809
|
+
}
|
|
50810
|
+
}
|
|
50684
50811
|
async navigateBack() {
|
|
50685
50812
|
const result = await this.client.navigateBack({ signal: this.currentSignal });
|
|
50686
50813
|
return this.formatNavigateResult(result);
|
|
@@ -50985,6 +51112,10 @@ ${body}
|
|
|
50985
51112
|
* Returns a structured result with the message and optional created hint metadata.
|
|
50986
51113
|
*/
|
|
50987
51114
|
async hint(opts) {
|
|
51115
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51116
|
+
if (!ref2) {
|
|
51117
|
+
return { message: formatEmptyRefRecovery("browser_hint") };
|
|
51118
|
+
}
|
|
50988
51119
|
if (!this.hintProvider) {
|
|
50989
51120
|
return { message: "Element hints are not available in this environment." };
|
|
50990
51121
|
}
|
|
@@ -50998,9 +51129,9 @@ ${body}
|
|
|
50998
51129
|
return { message: "Cannot create hint: no recent snapshot available. Take a snapshot first." };
|
|
50999
51130
|
}
|
|
51000
51131
|
const elements = parseSnapshot(lastYaml);
|
|
51001
|
-
const fpResult = fingerprintForHint(elements,
|
|
51132
|
+
const fpResult = fingerprintForHint(elements, ref2);
|
|
51002
51133
|
if (!fpResult) {
|
|
51003
|
-
return { message: `Cannot create hint: element ref "${
|
|
51134
|
+
return { message: `Cannot create hint: element ref "${ref2}" not found in the current snapshot.` };
|
|
51004
51135
|
}
|
|
51005
51136
|
if (opts.invalidate) {
|
|
51006
51137
|
if (!opts.reason) {
|
|
@@ -51019,7 +51150,7 @@ ${body}
|
|
|
51019
51150
|
fingerprintHash: fpResult.hash,
|
|
51020
51151
|
reason: opts.reason
|
|
51021
51152
|
});
|
|
51022
|
-
return { message: `Hint on [${
|
|
51153
|
+
return { message: `Hint on [${ref2}] invalidated: ${opts.reason}` };
|
|
51023
51154
|
}
|
|
51024
51155
|
if (!opts.frontmatter) {
|
|
51025
51156
|
return { message: "Cannot create hint: frontmatter is required." };
|
|
@@ -51064,7 +51195,7 @@ ${body}
|
|
|
51064
51195
|
});
|
|
51065
51196
|
const anchorNote = fpResult.anchoredToParent ? ` (anchored to ${fpResult.anchor.role} "${fpResult.anchor.text ?? ""}")` : "";
|
|
51066
51197
|
return {
|
|
51067
|
-
message: `Hint created on [${
|
|
51198
|
+
message: `Hint created on [${ref2}]${anchorNote}: (${category}: ${opts.frontmatter})`,
|
|
51068
51199
|
created: {
|
|
51069
51200
|
id: created.id,
|
|
51070
51201
|
frontmatter: opts.frontmatter,
|
|
@@ -51075,10 +51206,11 @@ ${body}
|
|
|
51075
51206
|
};
|
|
51076
51207
|
}
|
|
51077
51208
|
async screenshot(opts) {
|
|
51209
|
+
const ref2 = normalizeRef(opts?.ref);
|
|
51078
51210
|
const img = await this.client.takeScreenshot({
|
|
51079
51211
|
fullPage: opts?.fullPage,
|
|
51080
51212
|
element: opts?.element,
|
|
51081
|
-
ref:
|
|
51213
|
+
ref: ref2,
|
|
51082
51214
|
type: opts?.type ?? "jpeg",
|
|
51083
51215
|
label: opts?.label,
|
|
51084
51216
|
signal: this.currentSignal
|
|
@@ -51099,9 +51231,10 @@ ${body}
|
|
|
51099
51231
|
return `Screenshot captured${opts?.label ? ` (${opts.label})` : ""}: ${img ? "success" : "no screenshot"}`;
|
|
51100
51232
|
}
|
|
51101
51233
|
async evaluate(opts) {
|
|
51234
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51102
51235
|
const result = await this.client.evaluate(opts.expression, {
|
|
51103
51236
|
label: opts.label,
|
|
51104
|
-
ref:
|
|
51237
|
+
ref: ref2,
|
|
51105
51238
|
signal: this.currentSignal
|
|
51106
51239
|
});
|
|
51107
51240
|
return typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
@@ -51119,7 +51252,7 @@ ${body}
|
|
|
51119
51252
|
}
|
|
51120
51253
|
// ==================== Interaction ====================
|
|
51121
51254
|
async click(opts) {
|
|
51122
|
-
const
|
|
51255
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51123
51256
|
let x2 = opts.x;
|
|
51124
51257
|
let y2 = opts.y;
|
|
51125
51258
|
if (x2 === 0 && y2 === 0) {
|
|
@@ -51135,29 +51268,40 @@ ${body}
|
|
|
51135
51268
|
const useCoordinates = !ref2 && x2 !== void 0 && y2 !== void 0;
|
|
51136
51269
|
const clickVerb = opts.doubleClick ? "Double-clicked" : "Clicked";
|
|
51137
51270
|
const actionDesc = useCoordinates ? `${clickVerb} "${opts.element}" at (${x2}, ${y2})` : `${clickVerb} "${opts.element}" via ref ${ref2}`;
|
|
51138
|
-
this.lastReplayableAction = {
|
|
51271
|
+
this.lastReplayableAction = {
|
|
51272
|
+
description: actionDesc,
|
|
51273
|
+
thunk: () => this.click({ ...opts, ref: ref2 })
|
|
51274
|
+
};
|
|
51139
51275
|
let clickedElement;
|
|
51140
51276
|
let fallbackUsed = false;
|
|
51141
51277
|
const preClickUrl = useCoordinates && this.augmentorProvider ? await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0) : void 0;
|
|
51142
|
-
|
|
51143
|
-
|
|
51144
|
-
|
|
51145
|
-
|
|
51146
|
-
|
|
51147
|
-
|
|
51148
|
-
|
|
51149
|
-
|
|
51150
|
-
|
|
51151
|
-
|
|
51152
|
-
|
|
51153
|
-
|
|
51154
|
-
|
|
51155
|
-
|
|
51156
|
-
|
|
51157
|
-
|
|
51158
|
-
|
|
51278
|
+
let diffText;
|
|
51279
|
+
try {
|
|
51280
|
+
({ diffText } = await this.withSemanticDiff(actionDesc, async () => {
|
|
51281
|
+
if (useCoordinates) {
|
|
51282
|
+
const coordResult = await this.client.clickAtCoordinates(x2, y2, opts.element, {
|
|
51283
|
+
doubleClick: opts.doubleClick,
|
|
51284
|
+
modifiers: opts.modifiers,
|
|
51285
|
+
signal: this.currentSignal
|
|
51286
|
+
});
|
|
51287
|
+
clickedElement = coordResult.clickedElement;
|
|
51288
|
+
} else {
|
|
51289
|
+
const refResult = await this.client.click(ref2, opts.element, {
|
|
51290
|
+
doubleClick: opts.doubleClick,
|
|
51291
|
+
button: opts.button,
|
|
51292
|
+
modifiers: opts.modifiers,
|
|
51293
|
+
signal: this.currentSignal
|
|
51294
|
+
});
|
|
51295
|
+
clickedElement = refResult.clickedElement;
|
|
51296
|
+
fallbackUsed = refResult.fallbackUsed;
|
|
51297
|
+
}
|
|
51298
|
+
}));
|
|
51299
|
+
} catch (error) {
|
|
51300
|
+
if (!useCoordinates && ref2 && isStaleAriaRefActionTimeout(error, "click")) {
|
|
51301
|
+
return formatStaleRefActionRecovery("browser_click", ref2, opts.element);
|
|
51159
51302
|
}
|
|
51160
|
-
|
|
51303
|
+
throw error;
|
|
51304
|
+
}
|
|
51161
51305
|
if (useCoordinates) {
|
|
51162
51306
|
await this.emitAugmentorSignal("coordinate_click", x2, y2, preClickUrl ?? "");
|
|
51163
51307
|
} else {
|
|
@@ -51173,36 +51317,56 @@ ${body}
|
|
|
51173
51317
|
return withRecoveryNoticeMCP(this.client, result, this.logger);
|
|
51174
51318
|
}
|
|
51175
51319
|
async type(opts) {
|
|
51320
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51321
|
+
if (!ref2) {
|
|
51322
|
+
return formatEmptyRefRecovery("browser_type");
|
|
51323
|
+
}
|
|
51176
51324
|
const submitNote = opts.submit ? " and submitted" : "";
|
|
51177
51325
|
const actionDesc = `Typed "${opts.text}" into "${opts.element}"${submitNote}`;
|
|
51178
|
-
this.lastReplayableAction = {
|
|
51179
|
-
|
|
51180
|
-
|
|
51181
|
-
|
|
51182
|
-
|
|
51183
|
-
|
|
51326
|
+
this.lastReplayableAction = {
|
|
51327
|
+
description: actionDesc,
|
|
51328
|
+
thunk: () => this.type({ ...opts, ref: ref2 })
|
|
51329
|
+
};
|
|
51330
|
+
try {
|
|
51331
|
+
if (opts.submit) {
|
|
51332
|
+
const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
|
|
51333
|
+
await this.client.type(ref2, opts.text, opts.element, opts.submit, {
|
|
51334
|
+
delay: opts.delay,
|
|
51335
|
+
signal: this.currentSignal
|
|
51336
|
+
});
|
|
51184
51337
|
});
|
|
51338
|
+
return withRecoveryNoticeMCP(this.client, diffText, this.logger);
|
|
51339
|
+
}
|
|
51340
|
+
await this.client.type(ref2, opts.text, opts.element, opts.submit, {
|
|
51341
|
+
delay: opts.delay,
|
|
51342
|
+
signal: this.currentSignal
|
|
51185
51343
|
});
|
|
51186
|
-
return
|
|
51344
|
+
return withRecoveryNotice(this.client, actionDesc, this.logger);
|
|
51345
|
+
} catch (error) {
|
|
51346
|
+
if (isFillTargetNotEditableError(error)) {
|
|
51347
|
+
return formatFillTargetNotEditableRecovery(error, "browser_type");
|
|
51348
|
+
}
|
|
51349
|
+
throw error;
|
|
51187
51350
|
}
|
|
51188
|
-
await this.client.type(opts.ref, opts.text, opts.element, opts.submit, {
|
|
51189
|
-
delay: opts.delay,
|
|
51190
|
-
signal: this.currentSignal
|
|
51191
|
-
});
|
|
51192
|
-
return withRecoveryNotice(this.client, actionDesc, this.logger);
|
|
51193
51351
|
}
|
|
51194
51352
|
async hover(opts) {
|
|
51353
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51354
|
+
if (!ref2) {
|
|
51355
|
+
return formatEmptyRefRecovery("browser_hover");
|
|
51356
|
+
}
|
|
51195
51357
|
const { diffText } = await this.withSemanticDiff(
|
|
51196
|
-
`Hovered over "${opts.element}" via ref ${
|
|
51358
|
+
`Hovered over "${opts.element}" via ref ${ref2}`,
|
|
51197
51359
|
async () => {
|
|
51198
|
-
await this.client.hover(
|
|
51360
|
+
await this.client.hover(ref2, opts.element, { signal: this.currentSignal });
|
|
51199
51361
|
}
|
|
51200
51362
|
);
|
|
51201
51363
|
return diffText;
|
|
51202
51364
|
}
|
|
51203
51365
|
async drag(opts) {
|
|
51366
|
+
const startRef = normalizeRef(opts.startRef);
|
|
51367
|
+
const endRef = normalizeRef(opts.endRef);
|
|
51204
51368
|
const useCoordinates = opts.startX !== void 0 && opts.startY !== void 0 && opts.endX !== void 0 && opts.endY !== void 0;
|
|
51205
|
-
const useRefs =
|
|
51369
|
+
const useRefs = startRef !== void 0 && endRef !== void 0;
|
|
51206
51370
|
if (!useCoordinates && !useRefs) {
|
|
51207
51371
|
return "Error: Provide either startRef + endRef, or all four coordinates (startX, startY, endX, endY)";
|
|
51208
51372
|
}
|
|
@@ -51221,7 +51385,7 @@ ${body}
|
|
|
51221
51385
|
{ signal: this.currentSignal }
|
|
51222
51386
|
);
|
|
51223
51387
|
} else {
|
|
51224
|
-
await this.client.drag(
|
|
51388
|
+
await this.client.drag(startRef, opts.startElement, endRef, opts.endElement, {
|
|
51225
51389
|
signal: this.currentSignal
|
|
51226
51390
|
});
|
|
51227
51391
|
}
|
|
@@ -51247,35 +51411,63 @@ ${body}
|
|
|
51247
51411
|
return withRecoveryNotice(this.client, `Pressed ${key}`, this.logger);
|
|
51248
51412
|
}
|
|
51249
51413
|
async fillForm(fields) {
|
|
51250
|
-
const
|
|
51251
|
-
|
|
51252
|
-
|
|
51253
|
-
|
|
51414
|
+
const normalizedFields = fields.map((field) => ({
|
|
51415
|
+
...field,
|
|
51416
|
+
ref: normalizeRef(field.ref) ?? ""
|
|
51417
|
+
}));
|
|
51418
|
+
if (normalizedFields.some((field) => field.ref.length === 0)) {
|
|
51419
|
+
return formatEmptyRefRecovery("browser_fill_form");
|
|
51420
|
+
}
|
|
51421
|
+
try {
|
|
51422
|
+
const { diffText } = await this.withSemanticDiff(
|
|
51423
|
+
`Filled ${fields.length} form field(s)`,
|
|
51424
|
+
async () => {
|
|
51425
|
+
await this.client.fillForm(normalizedFields, { signal: this.currentSignal });
|
|
51426
|
+
}
|
|
51427
|
+
);
|
|
51428
|
+
return diffText;
|
|
51429
|
+
} catch (error) {
|
|
51430
|
+
if (isFillTargetNotEditableError(error)) {
|
|
51431
|
+
return formatFillTargetNotEditableRecovery(error);
|
|
51432
|
+
}
|
|
51433
|
+
if (isStaleAriaRefActionTimeout(error, "fill")) {
|
|
51434
|
+
const staleRef = extractWaitingAriaRef(error);
|
|
51435
|
+
const field = normalizedFields.find((candidate) => candidate.ref === staleRef) ?? normalizedFields[0];
|
|
51436
|
+
return formatStaleRefActionRecovery(
|
|
51437
|
+
"browser_fill_form",
|
|
51438
|
+
staleRef ?? field?.ref ?? "unknown",
|
|
51439
|
+
field?.element
|
|
51440
|
+
);
|
|
51254
51441
|
}
|
|
51255
|
-
|
|
51256
|
-
|
|
51442
|
+
throw error;
|
|
51443
|
+
}
|
|
51257
51444
|
}
|
|
51258
51445
|
async selectOption(opts) {
|
|
51446
|
+
const ref2 = normalizeRef(opts.ref);
|
|
51447
|
+
if (!ref2) {
|
|
51448
|
+
return formatEmptyRefRecovery("browser_select_option");
|
|
51449
|
+
}
|
|
51259
51450
|
try {
|
|
51260
51451
|
const { diffText } = await this.withSemanticDiff(
|
|
51261
51452
|
`Selected "${opts.value}" in "${opts.element}"`,
|
|
51262
51453
|
async () => {
|
|
51263
|
-
await this.client.selectOption(
|
|
51454
|
+
await this.client.selectOption(ref2, opts.value, opts.element, { signal: this.currentSignal });
|
|
51264
51455
|
}
|
|
51265
51456
|
);
|
|
51266
51457
|
return diffText;
|
|
51267
51458
|
} catch (error) {
|
|
51268
51459
|
if (isSelectOptionNonSelectError(error)) {
|
|
51269
|
-
return formatSelectOptionNonSelectRecovery(
|
|
51460
|
+
return formatSelectOptionNonSelectRecovery(ref2, opts.value, opts.element);
|
|
51270
51461
|
}
|
|
51271
51462
|
throw error;
|
|
51272
51463
|
}
|
|
51273
51464
|
}
|
|
51274
51465
|
async fileUpload(paths, ref2) {
|
|
51466
|
+
const normalizedRef = normalizeRef(ref2);
|
|
51275
51467
|
const { diffText } = await this.withSemanticDiff(
|
|
51276
51468
|
`Uploaded ${paths.length} file(s)`,
|
|
51277
51469
|
async () => {
|
|
51278
|
-
await this.client.fileUpload(paths, { ref:
|
|
51470
|
+
await this.client.fileUpload(paths, { ref: normalizedRef, signal: this.currentSignal });
|
|
51279
51471
|
}
|
|
51280
51472
|
);
|
|
51281
51473
|
return diffText;
|
|
@@ -51284,15 +51476,17 @@ ${body}
|
|
|
51284
51476
|
async scroll(opts) {
|
|
51285
51477
|
let result;
|
|
51286
51478
|
let label;
|
|
51287
|
-
|
|
51479
|
+
const withinRef = normalizeRef(opts.withinRef);
|
|
51480
|
+
const toRef = normalizeRef(opts.toRef);
|
|
51481
|
+
if (toRef) {
|
|
51288
51482
|
result = await this.client.scroll(
|
|
51289
51483
|
opts.direction,
|
|
51290
51484
|
opts.amount,
|
|
51291
|
-
|
|
51292
|
-
|
|
51485
|
+
withinRef,
|
|
51486
|
+
toRef,
|
|
51293
51487
|
{ signal: this.currentSignal }
|
|
51294
51488
|
);
|
|
51295
|
-
label = `toRef=${
|
|
51489
|
+
label = `toRef=${toRef}`;
|
|
51296
51490
|
} else if (opts.x !== void 0 && opts.y !== void 0) {
|
|
51297
51491
|
if (opts.x === 0 && opts.y === 0) {
|
|
51298
51492
|
return "Error: Coordinate scroll at (0, 0) is invalid. The coordinates were likely defaulted instead of intentionally chosen. Use direction-based scroll, scroll to a ref, or provide real coordinates before retrying.";
|
|
@@ -51309,11 +51503,11 @@ ${body}
|
|
|
51309
51503
|
result = await this.client.scroll(
|
|
51310
51504
|
opts.direction,
|
|
51311
51505
|
opts.amount,
|
|
51312
|
-
|
|
51506
|
+
withinRef,
|
|
51313
51507
|
void 0,
|
|
51314
51508
|
{ signal: this.currentSignal }
|
|
51315
51509
|
);
|
|
51316
|
-
label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${
|
|
51510
|
+
label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${withinRef ? `, withinRef=${withinRef}` : ""}`;
|
|
51317
51511
|
}
|
|
51318
51512
|
const scrollInfo = typeof result === "object" && result !== null ? result.scrollInfo : void 0;
|
|
51319
51513
|
if (scrollInfo && !scrollInfo.moved) {
|
|
@@ -51467,6 +51661,9 @@ ${header}
|
|
|
51467
51661
|
if (message === "No downloads captured during this session.") {
|
|
51468
51662
|
return `${message} Use browser_list_downloads after triggering a download, or try browser_pdf_read if the file opened inline instead of downloading.`;
|
|
51469
51663
|
}
|
|
51664
|
+
if (downloadId === 0 && message === "Download #0 not found. Use browser_list_downloads to see available downloads.") {
|
|
51665
|
+
return "Download #0 was not found. Download ids start at 1; call browser_list_downloads and use the listed id, or omit downloadId to read the most recent download.";
|
|
51666
|
+
}
|
|
51470
51667
|
if (isMissingDownloadTempFileError(message)) {
|
|
51471
51668
|
const label = downloadId !== null && downloadId !== void 0 ? `Download #${downloadId}` : "The most recent download";
|
|
51472
51669
|
return `${label} was captured, but its temporary file is no longer available. Trigger the download again, then call browser_list_downloads and browser_read_download with the new id.`;
|
|
@@ -54653,6 +54850,42 @@ function resolveBrowserContextRequestUrl(url, pageUrl) {
|
|
|
54653
54850
|
return url;
|
|
54654
54851
|
}
|
|
54655
54852
|
}
|
|
54853
|
+
function isRouteAlreadyHandledError(error) {
|
|
54854
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
54855
|
+
return message.includes("Route is already handled");
|
|
54856
|
+
}
|
|
54857
|
+
function createSingleUseContextRouteHandle(route, logger2) {
|
|
54858
|
+
let handled = false;
|
|
54859
|
+
const runOnce = async (action, fn) => {
|
|
54860
|
+
if (handled) {
|
|
54861
|
+
logger2.debug("[ContextRoute] Ignoring duplicate route action", { action });
|
|
54862
|
+
return;
|
|
54863
|
+
}
|
|
54864
|
+
handled = true;
|
|
54865
|
+
try {
|
|
54866
|
+
await fn();
|
|
54867
|
+
} catch (error) {
|
|
54868
|
+
if (isRouteAlreadyHandledError(error)) {
|
|
54869
|
+
logger2.debug("[ContextRoute] Route was already handled by Playwright", {
|
|
54870
|
+
action,
|
|
54871
|
+
error: error instanceof Error ? error.message : String(error)
|
|
54872
|
+
});
|
|
54873
|
+
return;
|
|
54874
|
+
}
|
|
54875
|
+
throw error;
|
|
54876
|
+
}
|
|
54877
|
+
};
|
|
54878
|
+
return {
|
|
54879
|
+
fulfill: (opts) => runOnce("fulfill", () => route.fulfill(opts)),
|
|
54880
|
+
continue: (opts) => runOnce("continue", () => route.continue(opts)),
|
|
54881
|
+
request: () => ({
|
|
54882
|
+
url: () => route.request().url(),
|
|
54883
|
+
method: () => route.request().method(),
|
|
54884
|
+
postData: () => route.request().postData(),
|
|
54885
|
+
headers: () => route.request().headers()
|
|
54886
|
+
})
|
|
54887
|
+
};
|
|
54888
|
+
}
|
|
54656
54889
|
var TOAST_SNAPSHOT_TTL = 5;
|
|
54657
54890
|
function formatMcpResponse(input) {
|
|
54658
54891
|
const lines = [];
|
|
@@ -54868,6 +55101,17 @@ function logLateRunCodeWaitRejection(logger2, kind, promise) {
|
|
|
54868
55101
|
});
|
|
54869
55102
|
});
|
|
54870
55103
|
}
|
|
55104
|
+
function guardRunCodeFetch(logger2) {
|
|
55105
|
+
return (...args) => {
|
|
55106
|
+
const promise = globalThis.fetch(...args);
|
|
55107
|
+
promise.catch((err) => {
|
|
55108
|
+
logger2.debug("[DirectPlaywright] runCode fetch rejected after call", {
|
|
55109
|
+
error: err instanceof Error ? err.message : String(err)
|
|
55110
|
+
});
|
|
55111
|
+
});
|
|
55112
|
+
return promise;
|
|
55113
|
+
};
|
|
55114
|
+
}
|
|
54871
55115
|
function guardFileChooserWaitForEvent(page, logger2) {
|
|
54872
55116
|
const originalWaitForEvent = page.waitForEvent;
|
|
54873
55117
|
let guarded = 0;
|
|
@@ -56469,9 +56713,10 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
56469
56713
|
let threwError = null;
|
|
56470
56714
|
let aborted = runController.signal.aborted;
|
|
56471
56715
|
let guardedWaits = { fileChooser: 0, request: 0, response: 0 };
|
|
56716
|
+
const guardedFetch = guardRunCodeFetch(this.logger);
|
|
56472
56717
|
try {
|
|
56473
56718
|
if (!aborted) {
|
|
56474
|
-
const fn = new Function("page", `return (async () => { ${code} })()`);
|
|
56719
|
+
const fn = new Function("page", "fetch", `return (async () => { ${code} })()`);
|
|
56475
56720
|
const abortPromise = new Promise((_, reject) => {
|
|
56476
56721
|
runController.signal.addEventListener(
|
|
56477
56722
|
"abort",
|
|
@@ -56479,7 +56724,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
|
|
|
56479
56724
|
{ once: true }
|
|
56480
56725
|
);
|
|
56481
56726
|
});
|
|
56482
|
-
const inner = fn(page);
|
|
56727
|
+
const inner = fn(page, guardedFetch);
|
|
56483
56728
|
inner.catch((err) => {
|
|
56484
56729
|
this.logger.debug("[DirectPlaywright] runCode orphan rejected after race", {
|
|
56485
56730
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -56698,6 +56943,16 @@ ${warning}` : ""}`,
|
|
|
56698
56943
|
}
|
|
56699
56944
|
} catch (err) {
|
|
56700
56945
|
const message = err instanceof Error ? err.message : String(err);
|
|
56946
|
+
if (isPlaywrightTargetClosedError(err)) {
|
|
56947
|
+
throw toBrowserActionTargetClosedError(err, {
|
|
56948
|
+
operation: "click",
|
|
56949
|
+
ref: ref2,
|
|
56950
|
+
elementDesc,
|
|
56951
|
+
lastKnownUrl: this.lastKnownUrl,
|
|
56952
|
+
pendingCrashType: this._pendingCrashType,
|
|
56953
|
+
pendingCrashUrl: this._pendingCrashUrl
|
|
56954
|
+
});
|
|
56955
|
+
}
|
|
56701
56956
|
const isTimeout = message.includes("Timeout") || message.includes("timeout");
|
|
56702
56957
|
if (isTimeout && preClickBox) {
|
|
56703
56958
|
const centerX = Math.round(preClickBox.x + preClickBox.width / 2);
|
|
@@ -56849,6 +57104,7 @@ ${warning}` : ""}`,
|
|
|
56849
57104
|
const dialogMsg = this.getPendingDialogMessage();
|
|
56850
57105
|
if (dialogMsg) throw new Error(dialogMsg);
|
|
56851
57106
|
const locator = await this.resolveRef(ref2);
|
|
57107
|
+
await assertFillTargetEditable(locator, ref2, elementDesc);
|
|
56852
57108
|
const timeout = opts?.timeoutMs ?? TIMEOUTS.TYPE;
|
|
56853
57109
|
const inputType = await locator.evaluate((node) => node instanceof HTMLInputElement ? node.type : null).catch((err) => {
|
|
56854
57110
|
this.logger.debug("[DirectPlaywright] Failed to inspect input type before typing", {
|
|
@@ -59477,15 +59733,7 @@ Legacy two-step (only if you need to change the action between calls): browser_h
|
|
|
59477
59733
|
const context = await this.getContext();
|
|
59478
59734
|
for (const route of routes) {
|
|
59479
59735
|
await context.route(route.urlPattern, async (pwRoute) => {
|
|
59480
|
-
await route.handler(
|
|
59481
|
-
fulfill: (opts) => pwRoute.fulfill(opts),
|
|
59482
|
-
continue: () => pwRoute.continue(),
|
|
59483
|
-
request: () => ({
|
|
59484
|
-
url: () => pwRoute.request().url(),
|
|
59485
|
-
method: () => pwRoute.request().method(),
|
|
59486
|
-
postData: () => pwRoute.request().postData()
|
|
59487
|
-
})
|
|
59488
|
-
});
|
|
59736
|
+
await route.handler(createSingleUseContextRouteHandle(pwRoute, this.logger));
|
|
59489
59737
|
});
|
|
59490
59738
|
}
|
|
59491
59739
|
}
|
|
@@ -59497,33 +59745,93 @@ Legacy two-step (only if you need to change the action between calls): browser_h
|
|
|
59497
59745
|
return guessMimeType(filename);
|
|
59498
59746
|
}
|
|
59499
59747
|
};
|
|
59500
|
-
|
|
59501
|
-
const
|
|
59502
|
-
|
|
59503
|
-
|
|
59504
|
-
|
|
59505
|
-
|
|
59506
|
-
|
|
59507
|
-
|
|
59508
|
-
|
|
59509
|
-
const contentEditableFalse = element.getAttribute("contenteditable") === "false";
|
|
59510
|
-
return {
|
|
59511
|
-
readonly: Boolean(
|
|
59512
|
-
htmlInput?.readOnly || htmlTextArea?.readOnly || ariaReadonly || contentEditableFalse
|
|
59513
|
-
),
|
|
59514
|
-
disabled: Boolean(
|
|
59515
|
-
htmlInput?.disabled || htmlTextArea?.disabled || htmlSelect?.disabled || htmlButton?.disabled || ariaDisabled
|
|
59516
|
-
),
|
|
59517
|
-
tagName: element.tagName.toLowerCase(),
|
|
59518
|
-
role: element.getAttribute("role")
|
|
59519
|
-
};
|
|
59748
|
+
function isPlaywrightTargetClosedError(error) {
|
|
59749
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59750
|
+
return /Target page, context or browser has been closed/i.test(message);
|
|
59751
|
+
}
|
|
59752
|
+
function toBrowserActionTargetClosedError(error, context) {
|
|
59753
|
+
const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
|
|
59754
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59755
|
+
const wrapped = new Error(`Browser click target closed for ${label}: ${message}`, {
|
|
59756
|
+
cause: error instanceof Error ? error : void 0
|
|
59520
59757
|
});
|
|
59521
|
-
|
|
59522
|
-
|
|
59523
|
-
|
|
59758
|
+
wrapped.name = "BrowserActionTargetClosedError";
|
|
59759
|
+
Object.assign(wrapped, { browserActionTargetClosed: context });
|
|
59760
|
+
return wrapped;
|
|
59761
|
+
}
|
|
59762
|
+
function toFillTargetProbeError(error, context) {
|
|
59763
|
+
const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
|
|
59764
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59765
|
+
const wrapped = new Error(`Fill target editability probe failed for ${label}: ${message}`, {
|
|
59766
|
+
cause: error instanceof Error ? error : void 0
|
|
59767
|
+
});
|
|
59768
|
+
wrapped.name = "FillTargetProbeError";
|
|
59769
|
+
Object.assign(wrapped, { fillTargetProbe: context });
|
|
59770
|
+
return wrapped;
|
|
59771
|
+
}
|
|
59772
|
+
async function assertFillTargetEditable(locator, ref2, elementDesc) {
|
|
59773
|
+
let probe;
|
|
59774
|
+
try {
|
|
59775
|
+
probe = await locator.evaluate((el) => {
|
|
59776
|
+
const element = el;
|
|
59777
|
+
const htmlInput = element instanceof HTMLInputElement ? element : null;
|
|
59778
|
+
const htmlTextArea = element instanceof HTMLTextAreaElement ? element : null;
|
|
59779
|
+
const htmlSelect = element instanceof HTMLSelectElement ? element : null;
|
|
59780
|
+
const htmlButton = element instanceof HTMLButtonElement ? element : null;
|
|
59781
|
+
const ariaReadonly = element.getAttribute("aria-readonly") === "true";
|
|
59782
|
+
const ariaDisabled = element.getAttribute("aria-disabled") === "true";
|
|
59783
|
+
const contentEditableFalse = element.getAttribute("contenteditable") === "false";
|
|
59784
|
+
const inputType = htmlInput?.type.toLowerCase() ?? null;
|
|
59785
|
+
const fillableInputTypes = /* @__PURE__ */ new Set([
|
|
59786
|
+
"",
|
|
59787
|
+
"color",
|
|
59788
|
+
"date",
|
|
59789
|
+
"datetime-local",
|
|
59790
|
+
"email",
|
|
59791
|
+
"month",
|
|
59792
|
+
"number",
|
|
59793
|
+
"password",
|
|
59794
|
+
"range",
|
|
59795
|
+
"search",
|
|
59796
|
+
"tel",
|
|
59797
|
+
"text",
|
|
59798
|
+
"time",
|
|
59799
|
+
"url",
|
|
59800
|
+
"week"
|
|
59801
|
+
]);
|
|
59802
|
+
return {
|
|
59803
|
+
readonly: Boolean(
|
|
59804
|
+
htmlInput?.readOnly || htmlTextArea?.readOnly || ariaReadonly || contentEditableFalse
|
|
59805
|
+
),
|
|
59806
|
+
disabled: Boolean(
|
|
59807
|
+
htmlInput?.disabled || htmlTextArea?.disabled || htmlSelect?.disabled || htmlButton?.disabled || ariaDisabled
|
|
59808
|
+
),
|
|
59809
|
+
fillable: Boolean(
|
|
59810
|
+
htmlInput && fillableInputTypes.has(inputType ?? "") || htmlTextArea || element.isContentEditable
|
|
59811
|
+
),
|
|
59812
|
+
tagName: element.tagName.toLowerCase(),
|
|
59813
|
+
role: element.getAttribute("role"),
|
|
59814
|
+
inputType
|
|
59815
|
+
};
|
|
59816
|
+
});
|
|
59817
|
+
} catch (error) {
|
|
59818
|
+
throw toFillTargetProbeError(error, {
|
|
59819
|
+
ref: ref2,
|
|
59820
|
+
elementDesc,
|
|
59821
|
+
stage: "editability_probe"
|
|
59822
|
+
});
|
|
59823
|
+
}
|
|
59824
|
+
if (!probe.readonly && !probe.disabled && probe.fillable) return;
|
|
59825
|
+
const state = probe.disabled ? "disabled" : probe.readonly ? "readonly" : "not-fillable";
|
|
59524
59826
|
const tag = probe.tagName ?? "element";
|
|
59525
|
-
|
|
59526
|
-
|
|
59827
|
+
throw new FillTargetNotEditableError({
|
|
59828
|
+
ref: ref2,
|
|
59829
|
+
elementDesc,
|
|
59830
|
+
state,
|
|
59831
|
+
tagName: tag,
|
|
59832
|
+
role: probe.role,
|
|
59833
|
+
...probe.inputType ? { inputType: probe.inputType } : {}
|
|
59834
|
+
});
|
|
59527
59835
|
}
|
|
59528
59836
|
|
|
59529
59837
|
export {
|
|
@@ -59921,4 +60229,4 @@ playwright-extra/dist/index.esm.js:
|
|
|
59921
60229
|
* @license MIT
|
|
59922
60230
|
*)
|
|
59923
60231
|
*/
|
|
59924
|
-
//# sourceMappingURL=chunk-
|
|
60232
|
+
//# sourceMappingURL=chunk-MO5G5TP5.js.map
|