@runcofactor/cli 0.16.0 → 0.18.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.
Files changed (129) hide show
  1. package/dist/{account-ZMV7NEFM.js → account-ZSM2Q3RB.js} +4 -4
  2. package/dist/{api-catalog-6HNOGVPA.js → api-catalog-MMPFWS52.js} +4 -4
  3. package/dist/{attachment-QAHR54HQ.js → attachment-NSYL5X3S.js} +4 -4
  4. package/dist/{augmentor-7T3L6ZR4.js → augmentor-LSJLMJYP.js} +4 -4
  5. package/dist/{auth-DURHUZDW.js → auth-OYF5A4BY.js} +4 -2
  6. package/dist/{chunk-RGDIVW3O.js → chunk-2R6544Z3.js} +2 -2
  7. package/dist/{chunk-AI3Y3AZH.js → chunk-7OO3RJYR.js} +1396 -185
  8. package/dist/chunk-7OO3RJYR.js.map +1 -0
  9. package/dist/{chunk-4NRDONSC.js → chunk-A6TH5VUI.js} +209 -79
  10. package/dist/chunk-A6TH5VUI.js.map +1 -0
  11. package/dist/{chunk-CEXV3MOC.js → chunk-E2FUQJVO.js} +4 -4
  12. package/dist/{chunk-D6AVOPOS.js → chunk-GFP4UEUT.js} +3 -3
  13. package/dist/{chunk-G5SXUXSU.js → chunk-H7ZML5QL.js} +2 -2
  14. package/dist/{chunk-LFFWD5JZ.js → chunk-IHCPMYKU.js} +3 -3
  15. package/dist/{chunk-UAJUZLDE.js → chunk-PVWG3V62.js} +3 -3
  16. package/dist/{chunk-3CZU7XCS.js → chunk-PWZ4ULCB.js} +20 -14
  17. package/dist/chunk-PWZ4ULCB.js.map +1 -0
  18. package/dist/{chunk-A4ISG3VX.js → chunk-U7OMQJ7V.js} +52 -4
  19. package/dist/chunk-U7OMQJ7V.js.map +1 -0
  20. package/dist/{chunk-DT2CEH2V.js → chunk-VHVOTGBQ.js} +3 -3
  21. package/dist/{chunk-IGHDIWZU.js → chunk-VYTIJ6HJ.js} +17 -2
  22. package/dist/{chunk-IGHDIWZU.js.map → chunk-VYTIJ6HJ.js.map} +1 -1
  23. package/dist/{credential-X6QKX473.js → credential-WOATCWOU.js} +4 -4
  24. package/dist/{credential-template-GD3D4MBI.js → credential-template-Q3DQE2AH.js} +4 -4
  25. package/dist/{debug-workflow-AAEOAME2.js → debug-workflow-ZNP5XZQF.js} +4 -4
  26. package/dist/{dist-QLTVBUMD.js → dist-4COOAKY3.js} +22 -2
  27. package/dist/{docs-BVUEXBY3.js → docs-XVU4LA7G.js} +4 -4
  28. package/dist/{doctor-RHMKD4W5.js → doctor-PGNHD3TI.js} +3 -3
  29. package/dist/{environment-WE7VVE6I.js → environment-MRDNWU47.js} +4 -4
  30. package/dist/{feature-flag-VJO2TJFT.js → feature-flag-D3DQZ6DX.js} +4 -4
  31. package/dist/index.js +51 -48
  32. package/dist/index.js.map +1 -1
  33. package/dist/{init-RQHHKJJD.js → init-DHWKUYIR.js} +4 -4
  34. package/dist/{install-CQLHNZ6U.js → install-DMEFGQHD.js} +4 -4
  35. package/dist/{issues-HK2JA5JS.js → issues-E4YQ4DVC.js} +4 -4
  36. package/dist/{knobs-ETJAV6YM.js → knobs-NA6SPLHX.js} +4 -4
  37. package/dist/{list-F6DGGW3Z.js → list-GJNGFQMC.js} +4 -4
  38. package/dist/{local-CMAY54I5.js → local-NSKOZHHA.js} +6 -6
  39. package/dist/{login-6CKG664C.js → login-AMSRVPXG.js} +4 -4
  40. package/dist/{mcp-GC2WOOZO.js → mcp-57VVZTXM.js} +5 -5
  41. package/dist/{metrics-IXICURAA.js → metrics-DM4YQQZN.js} +4 -4
  42. package/dist/{product-BUJWPYKB.js → product-WGOLTBRI.js} +4 -4
  43. package/dist/{property-4EXPWRNT.js → property-B7QMGLVW.js} +4 -4
  44. package/dist/{psql-XGLBI6T7.js → psql-4JHPJ2CO.js} +3 -3
  45. package/dist/{record-HL4QA7OX.js → record-RWVGZZSW.js} +5 -5
  46. package/dist/{redis-VJGCUJPV.js → redis-N44ZZ6VB.js} +3 -3
  47. package/dist/{release-2SMK5Y3V.js → release-R7GKLEAI.js} +4 -4
  48. package/dist/{sandbox-ORAPKOH3.js → sandbox-6D54ZQCG.js} +5 -5
  49. package/dist/{scenarios-DM5FDCAF.js → scenarios-WUUUKP2S.js} +4 -4
  50. package/dist/{session-X6IYGWYG.js → session-GDCMTNB3.js} +6 -6
  51. package/dist/{skill-ZU5V2EE5.js → skill-3SP7MAA4.js} +153 -2
  52. package/dist/skill-3SP7MAA4.js.map +1 -0
  53. package/dist/skills/optimize-cache/SKILL.md +25 -0
  54. package/dist/{spec-KO5QD6AY.js → spec-ESPA3H26.js} +3 -3
  55. package/dist/{src-DSD34JIX.js → src-5YXGM2KB.js} +18 -2
  56. package/dist/src-5YXGM2KB.js.map +1 -0
  57. package/dist/{src-PDYXACRP.js → src-B7CPYFLS.js} +4 -2
  58. package/dist/{src-HTGNKBUF.js → src-GRO2QR7U.js} +61 -23
  59. package/dist/{src-HTGNKBUF.js.map → src-GRO2QR7U.js.map} +1 -1
  60. package/dist/{start-2ECN4LX5.js → start-KDDRLUFN.js} +4 -4
  61. package/dist/{tag-R5I72HJ6.js → tag-DAGHQZAR.js} +5 -5
  62. package/dist/{task-V3JMSJIM.js → task-PRR75HI4.js} +4 -4
  63. package/dist/{test-suite-NOYVK6YX.js → test-suite-O3UY7EER.js} +6 -6
  64. package/dist/{trace-NOYEKNO4.js → trace-7LRHSPWN.js} +4 -4
  65. package/dist/{triage-4RFATHBE.js → triage-2BKUBMMX.js} +5 -5
  66. package/dist/{utilities-3WMXE743.js → utilities-O3OO64T5.js} +4 -4
  67. package/dist/{workflow-DC7WOTTW.js → workflow-5BFRS4CE.js} +10 -11
  68. package/dist/workflow-5BFRS4CE.js.map +1 -0
  69. package/dist/{workflow-cache-SDSI6SMF.js → workflow-cache-VVP3SPFM.js} +7 -7
  70. package/dist/{workflow-debug-6UQYP67Q.js → workflow-debug-XGOHNZ4E.js} +7 -7
  71. package/dist/{workflow-proposals-67AVGMHX.js → workflow-proposals-PGURJ6ZI.js} +4 -4
  72. package/package.json +5 -5
  73. package/dist/chunk-3CZU7XCS.js.map +0 -1
  74. package/dist/chunk-4NRDONSC.js.map +0 -1
  75. package/dist/chunk-A4ISG3VX.js.map +0 -1
  76. package/dist/chunk-AI3Y3AZH.js.map +0 -1
  77. package/dist/skill-ZU5V2EE5.js.map +0 -1
  78. package/dist/src-DSD34JIX.js.map +0 -1
  79. package/dist/workflow-DC7WOTTW.js.map +0 -1
  80. /package/dist/{account-ZMV7NEFM.js.map → account-ZSM2Q3RB.js.map} +0 -0
  81. /package/dist/{api-catalog-6HNOGVPA.js.map → api-catalog-MMPFWS52.js.map} +0 -0
  82. /package/dist/{attachment-QAHR54HQ.js.map → attachment-NSYL5X3S.js.map} +0 -0
  83. /package/dist/{augmentor-7T3L6ZR4.js.map → augmentor-LSJLMJYP.js.map} +0 -0
  84. /package/dist/{auth-DURHUZDW.js.map → auth-OYF5A4BY.js.map} +0 -0
  85. /package/dist/{chunk-RGDIVW3O.js.map → chunk-2R6544Z3.js.map} +0 -0
  86. /package/dist/{chunk-CEXV3MOC.js.map → chunk-E2FUQJVO.js.map} +0 -0
  87. /package/dist/{chunk-D6AVOPOS.js.map → chunk-GFP4UEUT.js.map} +0 -0
  88. /package/dist/{chunk-G5SXUXSU.js.map → chunk-H7ZML5QL.js.map} +0 -0
  89. /package/dist/{chunk-LFFWD5JZ.js.map → chunk-IHCPMYKU.js.map} +0 -0
  90. /package/dist/{chunk-UAJUZLDE.js.map → chunk-PVWG3V62.js.map} +0 -0
  91. /package/dist/{chunk-DT2CEH2V.js.map → chunk-VHVOTGBQ.js.map} +0 -0
  92. /package/dist/{credential-X6QKX473.js.map → credential-WOATCWOU.js.map} +0 -0
  93. /package/dist/{credential-template-GD3D4MBI.js.map → credential-template-Q3DQE2AH.js.map} +0 -0
  94. /package/dist/{debug-workflow-AAEOAME2.js.map → debug-workflow-ZNP5XZQF.js.map} +0 -0
  95. /package/dist/{dist-QLTVBUMD.js.map → dist-4COOAKY3.js.map} +0 -0
  96. /package/dist/{docs-BVUEXBY3.js.map → docs-XVU4LA7G.js.map} +0 -0
  97. /package/dist/{doctor-RHMKD4W5.js.map → doctor-PGNHD3TI.js.map} +0 -0
  98. /package/dist/{environment-WE7VVE6I.js.map → environment-MRDNWU47.js.map} +0 -0
  99. /package/dist/{feature-flag-VJO2TJFT.js.map → feature-flag-D3DQZ6DX.js.map} +0 -0
  100. /package/dist/{init-RQHHKJJD.js.map → init-DHWKUYIR.js.map} +0 -0
  101. /package/dist/{install-CQLHNZ6U.js.map → install-DMEFGQHD.js.map} +0 -0
  102. /package/dist/{issues-HK2JA5JS.js.map → issues-E4YQ4DVC.js.map} +0 -0
  103. /package/dist/{knobs-ETJAV6YM.js.map → knobs-NA6SPLHX.js.map} +0 -0
  104. /package/dist/{list-F6DGGW3Z.js.map → list-GJNGFQMC.js.map} +0 -0
  105. /package/dist/{local-CMAY54I5.js.map → local-NSKOZHHA.js.map} +0 -0
  106. /package/dist/{login-6CKG664C.js.map → login-AMSRVPXG.js.map} +0 -0
  107. /package/dist/{mcp-GC2WOOZO.js.map → mcp-57VVZTXM.js.map} +0 -0
  108. /package/dist/{metrics-IXICURAA.js.map → metrics-DM4YQQZN.js.map} +0 -0
  109. /package/dist/{product-BUJWPYKB.js.map → product-WGOLTBRI.js.map} +0 -0
  110. /package/dist/{property-4EXPWRNT.js.map → property-B7QMGLVW.js.map} +0 -0
  111. /package/dist/{psql-XGLBI6T7.js.map → psql-4JHPJ2CO.js.map} +0 -0
  112. /package/dist/{record-HL4QA7OX.js.map → record-RWVGZZSW.js.map} +0 -0
  113. /package/dist/{redis-VJGCUJPV.js.map → redis-N44ZZ6VB.js.map} +0 -0
  114. /package/dist/{release-2SMK5Y3V.js.map → release-R7GKLEAI.js.map} +0 -0
  115. /package/dist/{sandbox-ORAPKOH3.js.map → sandbox-6D54ZQCG.js.map} +0 -0
  116. /package/dist/{scenarios-DM5FDCAF.js.map → scenarios-WUUUKP2S.js.map} +0 -0
  117. /package/dist/{session-X6IYGWYG.js.map → session-GDCMTNB3.js.map} +0 -0
  118. /package/dist/{spec-KO5QD6AY.js.map → spec-ESPA3H26.js.map} +0 -0
  119. /package/dist/{src-PDYXACRP.js.map → src-B7CPYFLS.js.map} +0 -0
  120. /package/dist/{start-2ECN4LX5.js.map → start-KDDRLUFN.js.map} +0 -0
  121. /package/dist/{tag-R5I72HJ6.js.map → tag-DAGHQZAR.js.map} +0 -0
  122. /package/dist/{task-V3JMSJIM.js.map → task-PRR75HI4.js.map} +0 -0
  123. /package/dist/{test-suite-NOYVK6YX.js.map → test-suite-O3UY7EER.js.map} +0 -0
  124. /package/dist/{trace-NOYEKNO4.js.map → trace-7LRHSPWN.js.map} +0 -0
  125. /package/dist/{triage-4RFATHBE.js.map → triage-2BKUBMMX.js.map} +0 -0
  126. /package/dist/{utilities-3WMXE743.js.map → utilities-O3OO64T5.js.map} +0 -0
  127. /package/dist/{workflow-cache-SDSI6SMF.js.map → workflow-cache-VVP3SPFM.js.map} +0 -0
  128. /package/dist/{workflow-debug-6UQYP67Q.js.map → workflow-debug-XGOHNZ4E.js.map} +0 -0
  129. /package/dist/{workflow-proposals-67AVGMHX.js.map → workflow-proposals-PGURJ6ZI.js.map} +0 -0
@@ -3877,13 +3877,13 @@ var require_gifutil = __commonJS({
3877
3877
  }
3878
3878
  _quantize(imageOrImages, "WuQuant", maxColorIndexes, significantBits, dither);
3879
3879
  };
3880
- exports.read = function(source, decoder) {
3881
- decoder = decoder || defaultCodec;
3880
+ exports.read = function(source, decoder2) {
3881
+ decoder2 = decoder2 || defaultCodec;
3882
3882
  if (Buffer.isBuffer(source)) {
3883
- return decoder.decodeGif(source);
3883
+ return decoder2.decodeGif(source);
3884
3884
  }
3885
3885
  return _readBinary(source).then((buffer) => {
3886
- return decoder.decodeGif(buffer);
3886
+ return decoder2.decodeGif(buffer);
3887
3887
  });
3888
3888
  };
3889
3889
  exports.shareAsJimp = function(jimp, bitmapImageToShare) {
@@ -3895,13 +3895,13 @@ var require_gifutil = __commonJS({
3895
3895
  jimpImage.bitmap.data = bitmapImageToShare.bitmap.data;
3896
3896
  return jimpImage;
3897
3897
  };
3898
- exports.write = function(path6, frames, spec, encoder) {
3899
- encoder = encoder || defaultCodec;
3898
+ exports.write = function(path6, frames, spec, encoder2) {
3899
+ encoder2 = encoder2 || defaultCodec;
3900
3900
  const matches = path6.match(/\.[a-zA-Z]+$/);
3901
3901
  if (matches !== null && INVALID_SUFFIXES.includes(matches[0].toLowerCase())) {
3902
3902
  throw new Error(`GIF '${path6}' has an unexpected suffix`);
3903
3903
  }
3904
- return encoder.encodeGif(frames, spec).then((gif2) => {
3904
+ return encoder2.encodeGif(frames, spec).then((gif2) => {
3905
3905
  return _writeBinary(path6, gif2.buffer).then(() => {
3906
3906
  return gif2;
3907
3907
  });
@@ -5449,8 +5449,8 @@ var require_encoder = __commonJS({
5449
5449
  }
5450
5450
  function encode3(imgData, qu) {
5451
5451
  if (typeof qu === "undefined") qu = 50;
5452
- var encoder = new JPEGEncoder(qu);
5453
- var data2 = encoder.encode(imgData, qu);
5452
+ var encoder2 = new JPEGEncoder(qu);
5453
+ var data2 = encoder2.encode(imgData, qu);
5454
5454
  return {
5455
5455
  data: data2,
5456
5456
  width: imgData.width,
@@ -6489,22 +6489,22 @@ var require_decoder = __commonJS({
6489
6489
  };
6490
6490
  var opts = { ...defaultOpts, ...userOpts };
6491
6491
  var arr = new Uint8Array(jpegData);
6492
- var decoder = new JpegImage();
6493
- decoder.opts = opts;
6492
+ var decoder2 = new JpegImage();
6493
+ decoder2.opts = opts;
6494
6494
  JpegImage.resetMaxMemoryUsage(opts.maxMemoryUsageInMB * 1024 * 1024);
6495
- decoder.parse(arr);
6495
+ decoder2.parse(arr);
6496
6496
  var channels = opts.formatAsRGBA ? 4 : 3;
6497
- var bytesNeeded = decoder.width * decoder.height * channels;
6497
+ var bytesNeeded = decoder2.width * decoder2.height * channels;
6498
6498
  try {
6499
6499
  JpegImage.requestMemoryAllocation(bytesNeeded);
6500
6500
  var image2 = {
6501
- width: decoder.width,
6502
- height: decoder.height,
6503
- exifBuffer: decoder.exifBuffer,
6501
+ width: decoder2.width,
6502
+ height: decoder2.height,
6503
+ exifBuffer: decoder2.exifBuffer,
6504
6504
  data: opts.useTArray ? new Uint8Array(bytesNeeded) : Buffer.alloc(bytesNeeded)
6505
6505
  };
6506
- if (decoder.comments.length > 0) {
6507
- image2["comments"] = decoder.comments;
6506
+ if (decoder2.comments.length > 0) {
6507
+ image2["comments"] = decoder2.comments;
6508
6508
  }
6509
6509
  } catch (err) {
6510
6510
  if (err instanceof RangeError) {
@@ -6517,7 +6517,7 @@ var require_decoder = __commonJS({
6517
6517
  }
6518
6518
  throw err;
6519
6519
  }
6520
- decoder.copyToImageData(image2, opts.formatAsRGBA);
6520
+ decoder2.copyToImageData(image2, opts.formatAsRGBA);
6521
6521
  return image2;
6522
6522
  }
6523
6523
  }
@@ -24616,14 +24616,14 @@ var require_merge_deep = __commonJS({
24616
24616
  }
24617
24617
  });
24618
24618
 
24619
- // ../../node_modules/.bun/puppeteer-extra-plugin@3.2.3+e57d6a82292734a1/node_modules/puppeteer-extra-plugin/dist/index.esm.js
24619
+ // ../../node_modules/.bun/puppeteer-extra-plugin@3.2.3+174067defc7674c7/node_modules/puppeteer-extra-plugin/dist/index.esm.js
24620
24620
  var index_esm_exports = {};
24621
24621
  __export(index_esm_exports, {
24622
24622
  PuppeteerExtraPlugin: () => PuppeteerExtraPlugin
24623
24623
  });
24624
24624
  var import_debug2, merge, PuppeteerExtraPlugin;
24625
24625
  var init_index_esm = __esm({
24626
- "../../node_modules/.bun/puppeteer-extra-plugin@3.2.3+e57d6a82292734a1/node_modules/puppeteer-extra-plugin/dist/index.esm.js"() {
24626
+ "../../node_modules/.bun/puppeteer-extra-plugin@3.2.3+174067defc7674c7/node_modules/puppeteer-extra-plugin/dist/index.esm.js"() {
24627
24627
  "use strict";
24628
24628
  import_debug2 = __toESM(require_src());
24629
24629
  merge = require_merge_deep();
@@ -25086,9 +25086,9 @@ var init_index_esm = __esm({
25086
25086
  }
25087
25087
  });
25088
25088
 
25089
- // ../../node_modules/.bun/puppeteer-extra-plugin-stealth@2.11.2+e57d6a82292734a1/node_modules/puppeteer-extra-plugin-stealth/index.js
25089
+ // ../../node_modules/.bun/puppeteer-extra-plugin-stealth@2.11.2+174067defc7674c7/node_modules/puppeteer-extra-plugin-stealth/index.js
25090
25090
  var require_puppeteer_extra_plugin_stealth = __commonJS({
25091
- "../../node_modules/.bun/puppeteer-extra-plugin-stealth@2.11.2+e57d6a82292734a1/node_modules/puppeteer-extra-plugin-stealth/index.js"(exports, module) {
25091
+ "../../node_modules/.bun/puppeteer-extra-plugin-stealth@2.11.2+174067defc7674c7/node_modules/puppeteer-extra-plugin-stealth/index.js"(exports, module) {
25092
25092
  "use strict";
25093
25093
  var { PuppeteerExtraPlugin: PuppeteerExtraPlugin2 } = (init_index_esm(), __toCommonJS(index_esm_exports));
25094
25094
  var StealthPlugin2 = class extends PuppeteerExtraPlugin2 {
@@ -40257,11 +40257,17 @@ import * as path2 from "path";
40257
40257
  import * as path3 from "path";
40258
40258
  import * as fs22 from "fs/promises";
40259
40259
  import * as crypto3 from "crypto";
40260
+ import { cpus } from "os";
40261
+ import { spawn as spawn2 } from "child_process";
40262
+ import { existsSync as existsSync2 } from "fs";
40263
+ import { createRequire } from "module";
40264
+ import { dirname, join as join2 } from "path";
40265
+ import { fileURLToPath } from "url";
40260
40266
  import {
40261
40267
  chromium as playwrightChromium
40262
40268
  } from "playwright";
40263
40269
 
40264
- // ../../node_modules/.bun/playwright-extra@4.3.6+8de3b499c6b19053/node_modules/playwright-extra/dist/index.esm.js
40270
+ // ../../node_modules/.bun/playwright-extra@4.3.6+0a2747ebf12ccdf9/node_modules/playwright-extra/dist/index.esm.js
40265
40271
  var import_debug = __toESM(require_src());
40266
40272
  var Loader = class {
40267
40273
  constructor(moduleName, packageNames) {
@@ -41654,6 +41660,16 @@ var TIMEOUTS = {
41654
41660
  GET_TITLE: 2e4,
41655
41661
  GET_LINKS: 2e4,
41656
41662
  SNAPSHOT: 3e4,
41663
+ // Driver-side hard cap for a single `page.ariaSnapshot()` call. ariaSnapshot
41664
+ // has NO Playwright timeout and ignores AbortSignal: on a jammed renderer
41665
+ // (blocked main thread / native dialog) the op parks for minutes, and the
41666
+ // per-tool timeout's abort can't cancel it — the orphan kept running until
41667
+ // the 1800s job-stall watchdog tore down the whole job (prod, June 2026).
41668
+ // This cap makes the *driver* give up and fall back to an empty snapshot so
41669
+ // the agent keeps moving; the wedged page is then reaped by step-level
41670
+ // force-recovery. Kept below POST_ACTION_SNAPSHOT (15s) and SNAPSHOT (30s) so
41671
+ // the inner cap wins; a healthy ariaSnapshot returns in <1s.
41672
+ ARIA_SNAPSHOT_CAP: 12e3,
41657
41673
  CLICK: 1e4,
41658
41674
  TYPE: 1e4,
41659
41675
  FILL_FORM: 2e4,
@@ -42004,6 +42020,9 @@ function extractAndStripV2Fields(attributes) {
42004
42020
  out.visibility = value;
42005
42021
  }
42006
42022
  break;
42023
+ case "~dialog-inert":
42024
+ if (value === "true") out.dialogInert = true;
42025
+ break;
42007
42026
  case "~bbox": {
42008
42027
  const parts = value.split(":").map((s2) => parseFloat(s2));
42009
42028
  if (parts.length === 4 && parts.every((n2) => Number.isFinite(n2))) {
@@ -42348,6 +42367,192 @@ function appendAttrToLine(line, segment) {
42348
42367
  function errorMessage(error) {
42349
42368
  return error instanceof Error ? error.message : String(error);
42350
42369
  }
42370
+ var ICON_FAMILY_PREFIXES = [
42371
+ // FontAwesome family + style tokens
42372
+ "fa-solid-",
42373
+ "fa-regular-",
42374
+ "fa-light-",
42375
+ "fa-thin-",
42376
+ "fa-duotone-",
42377
+ "fa-brands-",
42378
+ "fa-sharp-",
42379
+ "fa-",
42380
+ "fas-",
42381
+ "far-",
42382
+ "fal-",
42383
+ "fad-",
42384
+ "fab-",
42385
+ // Telerik / Kendo
42386
+ "k-svg-i-",
42387
+ "k-i-",
42388
+ // Bootstrap Icons, Material Design Icons, Glyphicons, Ionicons
42389
+ "bi-",
42390
+ "mdi-",
42391
+ "glyphicon-",
42392
+ "ion-",
42393
+ "material-icons-",
42394
+ // Generic convention
42395
+ "icon-"
42396
+ ];
42397
+ var ICON_NOISE_TOKENS = /* @__PURE__ */ new Set([
42398
+ "fa",
42399
+ "fa-solid",
42400
+ "fa-regular",
42401
+ "fa-light",
42402
+ "fa-thin",
42403
+ "fa-duotone",
42404
+ "fa-brands",
42405
+ "fa-sharp",
42406
+ "fas",
42407
+ "far",
42408
+ "fal",
42409
+ "fad",
42410
+ "fab",
42411
+ "fa-lg",
42412
+ "fa-sm",
42413
+ "fa-xs",
42414
+ "fa-xl",
42415
+ "fa-fw",
42416
+ "fa-spin",
42417
+ "fa-pulse",
42418
+ "fa-border",
42419
+ "fa-pull-left",
42420
+ "fa-pull-right",
42421
+ "k-icon",
42422
+ "k-svg-icon",
42423
+ "k-button-icon"
42424
+ ]);
42425
+ var ICON_NAME_MAP = {
42426
+ // export / data movement
42427
+ "file-export": "Export",
42428
+ "file-arrow-down": "Export",
42429
+ "arrow-up-from-bracket": "Export",
42430
+ export: "Export",
42431
+ download: "Download",
42432
+ "file-download": "Download",
42433
+ "cloud-arrow-down": "Download",
42434
+ "arrow-down-to-line": "Download",
42435
+ upload: "Upload",
42436
+ "file-upload": "Upload",
42437
+ "cloud-arrow-up": "Upload",
42438
+ "file-import": "Import",
42439
+ "file-arrow-up": "Import",
42440
+ import: "Import",
42441
+ // filtering / search / columns
42442
+ filter: "Filter",
42443
+ "filter-circle-xmark": "Clear filter",
42444
+ funnel: "Filter",
42445
+ sliders: "Filter",
42446
+ "magnifying-glass": "Search",
42447
+ search: "Search",
42448
+ "table-columns": "Columns",
42449
+ columns: "Columns",
42450
+ "table-cells": "Columns",
42451
+ table: "Columns",
42452
+ // settings
42453
+ gear: "Settings",
42454
+ gears: "Settings",
42455
+ cog: "Settings",
42456
+ cogs: "Settings",
42457
+ wrench: "Settings",
42458
+ sliders2: "Settings",
42459
+ // CRUD
42460
+ plus: "Add",
42461
+ "plus-circle": "Add",
42462
+ "circle-plus": "Add",
42463
+ add: "Add",
42464
+ pen: "Edit",
42465
+ pencil: "Edit",
42466
+ "pen-to-square": "Edit",
42467
+ "pencil-alt": "Edit",
42468
+ edit: "Edit",
42469
+ trash: "Delete",
42470
+ "trash-can": "Delete",
42471
+ "trash-alt": "Delete",
42472
+ delete: "Delete",
42473
+ "floppy-disk": "Save",
42474
+ save: "Save",
42475
+ copy: "Copy",
42476
+ clone: "Copy",
42477
+ // navigation / disclosure
42478
+ xmark: "Close",
42479
+ times: "Close",
42480
+ x: "Close",
42481
+ "circle-xmark": "Close",
42482
+ close: "Close",
42483
+ "chevron-down": "Expand",
42484
+ "chevron-up": "Collapse",
42485
+ "chevron-left": "Previous",
42486
+ "chevron-right": "Next",
42487
+ "angle-down": "Expand",
42488
+ "angle-up": "Collapse",
42489
+ "angle-left": "Previous",
42490
+ "angle-right": "Next",
42491
+ "caret-down": "Expand",
42492
+ "caret-up": "Collapse",
42493
+ "caret-left": "Previous",
42494
+ "caret-right": "Next",
42495
+ "arrow-left": "Back",
42496
+ "arrow-right": "Forward",
42497
+ // misc actions
42498
+ ellipsis: "More",
42499
+ "ellipsis-vertical": "More",
42500
+ "ellipsis-v": "More",
42501
+ "ellipsis-h": "More",
42502
+ bars: "Menu",
42503
+ bell: "Notifications",
42504
+ "circle-info": "Info",
42505
+ "info-circle": "Info",
42506
+ info: "Info",
42507
+ "circle-question": "Help",
42508
+ "question-circle": "Help",
42509
+ question: "Help",
42510
+ check: "Confirm",
42511
+ "circle-check": "Confirm",
42512
+ eye: "Show",
42513
+ "eye-slash": "Hide",
42514
+ "arrows-rotate": "Refresh",
42515
+ "arrow-rotate-right": "Refresh",
42516
+ rotate: "Refresh",
42517
+ refresh: "Refresh",
42518
+ sync: "Refresh",
42519
+ print: "Print",
42520
+ printer: "Print",
42521
+ share: "Share",
42522
+ "share-nodes": "Share",
42523
+ calendar: "Calendar",
42524
+ "calendar-days": "Calendar",
42525
+ star: "Favorite",
42526
+ print2: "Print"
42527
+ };
42528
+ function stripIconFamilyPrefix(raw) {
42529
+ let name = raw.trim().toLowerCase();
42530
+ if (!name) return void 0;
42531
+ let changed = true;
42532
+ while (changed) {
42533
+ changed = false;
42534
+ for (const prefix of ICON_FAMILY_PREFIXES) {
42535
+ if (name.startsWith(prefix) && name.length > prefix.length) {
42536
+ name = name.slice(prefix.length);
42537
+ changed = true;
42538
+ break;
42539
+ }
42540
+ }
42541
+ }
42542
+ if (!name || ICON_NOISE_TOKENS.has(name) || ICON_NOISE_TOKENS.has(raw.trim().toLowerCase())) {
42543
+ return void 0;
42544
+ }
42545
+ return name;
42546
+ }
42547
+ function resolveIconClassName(raw) {
42548
+ const name = stripIconFamilyPrefix(raw);
42549
+ if (!name) return void 0;
42550
+ const mapped = ICON_NAME_MAP[name];
42551
+ if (mapped) return mapped;
42552
+ const base = name.replace(/-\d+$/, "");
42553
+ if (base !== name && ICON_NAME_MAP[base]) return ICON_NAME_MAP[base];
42554
+ return name.split("-").filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
42555
+ }
42351
42556
  async function resolveRefs(scope, refs, fn) {
42352
42557
  if (refs.length === 0) return {};
42353
42558
  const handles = await Promise.all(
@@ -42438,20 +42643,31 @@ async function resolveRefsWithArgs(scope, items, globals, fn) {
42438
42643
  );
42439
42644
  }
42440
42645
  }
42646
+ function isFrameworkMarkerClass(cls) {
42647
+ if (cls === "telerik-blazor") return true;
42648
+ return /^(k|mat|mdc|ng|chakra|ant|el|v|q)-/.test(cls) || /^mui[A-Z-]/.test(cls);
42649
+ }
42441
42650
  function extractSemanticHint(className) {
42442
42651
  if (!className) return void 0;
42443
42652
  const classes = className.split(/\s+/).filter(Boolean);
42653
+ const isNoise = (cls) => {
42654
+ if (/^(css|sc|emotion|styled|svelte)-/.test(cls)) return true;
42655
+ if (/^(clickable-element|bubble-element|bubble-r-container|bubble-r-vertical-center)/.test(cls)) return true;
42656
+ if (/^_[a-zA-Z0-9]{4,}$/.test(cls)) return true;
42657
+ if (/^(p|m|w|h|gap|text|bg|border|rounded|shadow|opacity|z|flex|grid|block|inline|hidden|relative|absolute|fixed|sticky|overflow|cursor|transition|ring|outline|sr|not|group|peer|dark|hover|focus|active|disabled|data|aria)-/.test(cls)) return true;
42658
+ if (/^(items|justify|self|place|space|divide|whitespace|break|font|leading|tracking|decoration|underline|overline|line|list|placeholder|align|content|object|max|min|inset|top|right|bottom|left|order|auto|grow|shrink|basis|table|caption|scale|rotate|translate|skew|origin|select|snap|scroll|touch|resize|appearance|columns|aspect|accent|caret|fill|stroke|will|animate|delay|duration|ease)-./.test(cls)) return true;
42659
+ if (/^(flex|grid|block|inline|hidden|table|contents)$/.test(cls)) return true;
42660
+ if (isFrameworkMarkerClass(cls)) return true;
42661
+ return false;
42662
+ };
42663
+ const KEYWORD_RE = /delete|remove|trash|edit|save|close|submit|cancel|add|create|copy|refresh|back|forward|next|prev|menu|nav|icon|btn|button|action|toggle|collapse|expand|upload|download|export|import|filter|search|settings|help|info|warning|error/i;
42444
42664
  for (const cls of classes) {
42445
- if (/^(css|sc|emotion|styled|svelte)-/.test(cls)) continue;
42446
- if (/^(clickable-element|bubble-element|bubble-r-container|bubble-r-vertical-center)/.test(cls)) continue;
42447
- if (/^_[a-zA-Z0-9]{4,}$/.test(cls)) continue;
42448
- if (/^(p|m|w|h|gap|text|bg|border|rounded|shadow|opacity|z|flex|grid|block|inline|hidden|relative|absolute|fixed|sticky|overflow|cursor|transition|ring|outline|sr|not|group|peer|dark|hover|focus|active|disabled|data|aria)-/.test(cls)) continue;
42449
- if (/^(items|justify|self|place|space|divide|whitespace|break|font|leading|tracking|decoration|underline|overline|line|list|placeholder|align|content|object|max|min|inset|top|right|bottom|left|order|auto|grow|shrink|basis|table|caption|scale|rotate|translate|skew|origin|select|snap|scroll|touch|resize|appearance|columns|aspect|accent|caret|fill|stroke|will|animate|delay|duration|ease)-./.test(cls)) continue;
42450
- if (/^(flex|grid|block|inline|hidden|table|contents)$/.test(cls)) continue;
42451
- if (/delete|remove|trash|edit|save|close|submit|cancel|add|create|copy|refresh|back|forward|next|prev|menu|nav|icon|btn|button|action|toggle|collapse|expand|upload|download|settings|help|info|warning|error/i.test(cls)) {
42452
- return cls;
42453
- }
42454
- if (/^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)+$/.test(cls) && cls.length >= 6) {
42665
+ if (isNoise(cls)) continue;
42666
+ if (KEYWORD_RE.test(cls)) return cls;
42667
+ }
42668
+ for (const cls of classes) {
42669
+ if (isNoise(cls)) continue;
42670
+ if (/^[a-z][a-z0-9]*((-{1,2}|__)[a-z][a-z0-9]*)+$/.test(cls) && cls.length >= 6) {
42455
42671
  return cls;
42456
42672
  }
42457
42673
  }
@@ -42547,6 +42763,9 @@ function planChecks(elements, opts) {
42547
42763
  if (opts.v2 && el.role === "generic" && hasInteractive(el)) {
42548
42764
  addCheck(el.ref, "modalV2");
42549
42765
  }
42766
+ if (el.role === "dialog" || el.role === "alertdialog") {
42767
+ addCheck(el.ref, "dialogModality");
42768
+ }
42550
42769
  if ((el.role === "button" || el.role === "link") && (!el.text || hasOnlyPrivateUseGlyphs(el.text))) {
42551
42770
  addCheck(el.ref, "iconButton");
42552
42771
  }
@@ -42789,6 +43008,46 @@ function pageSideFn(el, _ref, args, G) {
42789
43008
  }
42790
43009
  }
42791
43010
  }
43011
+ if (checks.has("dialogModality")) {
43012
+ out.dialogModality = { inert: false };
43013
+ try {
43014
+ const isVisibleStyle = (n2) => {
43015
+ const he = n2;
43016
+ if (he.offsetWidth === 0 && he.offsetHeight === 0 && he.getClientRects().length === 0) {
43017
+ return false;
43018
+ }
43019
+ const style = window.getComputedStyle(n2);
43020
+ if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") {
43021
+ return false;
43022
+ }
43023
+ if (parseFloat(style.opacity || "1") === 0) return false;
43024
+ return true;
43025
+ };
43026
+ let visible = true;
43027
+ let cur = el;
43028
+ for (let i2 = 0; i2 < 25 && cur; i2++) {
43029
+ if (!isVisibleStyle(cur)) {
43030
+ visible = false;
43031
+ break;
43032
+ }
43033
+ cur = cur.parentElement;
43034
+ }
43035
+ let onScreen = true;
43036
+ if (visible) {
43037
+ const rect = el.getBoundingClientRect();
43038
+ const vw = window.innerWidth || document.documentElement.clientWidth || 0;
43039
+ const vh = window.innerHeight || document.documentElement.clientHeight || 0;
43040
+ if (rect.width === 0 || rect.height === 0) {
43041
+ onScreen = false;
43042
+ } else if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= vw || rect.top >= vh) {
43043
+ onScreen = false;
43044
+ }
43045
+ }
43046
+ out.dialogModality = { inert: !(visible && onScreen) };
43047
+ } catch {
43048
+ out.dialogModality = null;
43049
+ }
43050
+ }
42792
43051
  const extractSvgHint = (root) => {
42793
43052
  const svg = root.querySelector("svg");
42794
43053
  if (!svg) return {};
@@ -42815,6 +43074,38 @@ function pageSideFn(el, _ref, args, G) {
42815
43074
  if (m) lucideIconClass = m[0].toLowerCase();
42816
43075
  return { svgHint, lucideIconClass };
42817
43076
  };
43077
+ const extractIconClass = (root) => {
43078
+ const FA_STYLE = /^fa-(solid|regular|light|thin|duotone|brands|sharp)$/;
43079
+ const FA_SIZE = /^fa-(2?xs|sm|lg|xl|2xl|fw|spin|pulse|border|inverse|stack(-[12]x)?|pull-left|pull-right|beat|fade|flip|shake|bounce|spin-pulse|rotate-[0-9a-z]+|flip-[a-z]+)$/;
43080
+ const classOf = (node) => typeof node.className === "string" ? node.className : node.getAttribute("class") || "";
43081
+ const pick = (raw, allowGeneric) => {
43082
+ const tokens = raw.split(/\s+/).filter(Boolean);
43083
+ for (const t2 of tokens) {
43084
+ if (/^fa-/.test(t2) && t2 !== "fa" && !FA_STYLE.test(t2) && !FA_SIZE.test(t2)) return t2;
43085
+ }
43086
+ for (const t2 of tokens) {
43087
+ if (/^k-svg-i-/.test(t2) || /^k-i-/.test(t2)) return t2;
43088
+ }
43089
+ if (allowGeneric) {
43090
+ for (const t2 of tokens) if (/^icon-[a-z]/.test(t2)) return t2;
43091
+ }
43092
+ return void 0;
43093
+ };
43094
+ const rootHit = pick(classOf(root), false);
43095
+ if (rootHit) return rootHit;
43096
+ let kids = [];
43097
+ try {
43098
+ kids = Array.from(root.querySelectorAll("i, svg, em, span"));
43099
+ } catch {
43100
+ kids = [];
43101
+ }
43102
+ for (const node of kids) {
43103
+ const tag = node.tagName;
43104
+ const hit = pick(classOf(node), tag === "I" || tag === "SVG" || tag === "EM");
43105
+ if (hit) return hit;
43106
+ }
43107
+ return void 0;
43108
+ };
42818
43109
  if (checks.has("iconButton")) {
42819
43110
  try {
42820
43111
  const ariaLabel = el.getAttribute("aria-label") || void 0;
@@ -42872,6 +43163,7 @@ function pageSideFn(el, _ref, args, G) {
42872
43163
  }
42873
43164
  }
42874
43165
  }
43166
+ const iconClass = extractIconClass(el);
42875
43167
  out.iconButton = {
42876
43168
  ariaLabel,
42877
43169
  title,
@@ -42889,7 +43181,8 @@ function pageSideFn(el, _ref, args, G) {
42889
43181
  reactComponentName,
42890
43182
  id: el.id || void 0,
42891
43183
  className: typeof el.className === "string" ? el.className : void 0,
42892
- lucideIconClass
43184
+ lucideIconClass,
43185
+ iconClass
42893
43186
  };
42894
43187
  } catch {
42895
43188
  out.iconButton = null;
@@ -42902,9 +43195,19 @@ function pageSideFn(el, _ref, args, G) {
42902
43195
  const id = el.id || void 0;
42903
43196
  const className = typeof el.className === "string" ? el.className : void 0;
42904
43197
  const { svgHint, lucideIconClass } = extractSvgHint(el);
43198
+ const iconClass = extractIconClass(el);
42905
43199
  const rawInnerText = el instanceof HTMLElement ? el.innerText : void 0;
42906
43200
  const innerText = rawInnerText?.replace(/\n+/g, " ").trim().slice(0, 80) || void 0;
42907
- out.genericClickable = { title, ariaLabel, svgHint, id, className, innerText, lucideIconClass };
43201
+ out.genericClickable = {
43202
+ title,
43203
+ ariaLabel,
43204
+ svgHint,
43205
+ id,
43206
+ className,
43207
+ innerText,
43208
+ lucideIconClass,
43209
+ iconClass
43210
+ };
42908
43211
  } catch {
42909
43212
  out.genericClickable = null;
42910
43213
  }
@@ -43192,6 +43495,9 @@ async function runPerRefAugmentations(scope, yaml, logger2, opts = {}) {
43192
43495
  if (r2.hclip) {
43193
43496
  lines[i2] = appendAttributeIfMissing(lines[i2], "~hclip", r2.hclip);
43194
43497
  }
43498
+ if (r2.dialogModality?.inert) {
43499
+ lines[i2] = appendAttributeIfMissing(lines[i2], "~dialog-inert", "true");
43500
+ }
43195
43501
  if (r2.gridCellValue) {
43196
43502
  const escaped = r2.gridCellValue.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
43197
43503
  lines[i2] = appendValueTail(lines[i2], escaped);
@@ -43240,7 +43546,7 @@ async function runPerRefAugmentations(scope, yaml, logger2, opts = {}) {
43240
43546
  const linePreLabel = lines[i2];
43241
43547
  const role = linePreLabel.includes("- link") ? "link" : "button";
43242
43548
  const pathnameLabel = role === "link" ? labelFromPathname(h.pathname) : void 0;
43243
- const hint = h.title || h.ariaLabel || h.svgHint || h.srText || h.imgAlt || pathnameLabel || h.reactComponentName || (h.id || void 0) || extractSemanticHint(h.className);
43549
+ const hint = h.title || h.ariaLabel || h.svgHint || h.srText || h.imgAlt || (h.iconClass ? resolveIconClassName(h.iconClass) : void 0) || pathnameLabel || h.reactComponentName || (h.id || void 0) || extractSemanticHint(h.className);
43244
43550
  if (hint) {
43245
43551
  lines[i2] = injectElementLabel(lines[i2], role, hint);
43246
43552
  tagAugmentedNameSource(i2);
@@ -43254,6 +43560,8 @@ async function runPerRefAugmentations(scope, yaml, logger2, opts = {}) {
43254
43560
  if (h.ariaCurrent) line = appendAttributeIfMissing(line, "aria-current", h.ariaCurrent);
43255
43561
  if (h.lucideIconClass) {
43256
43562
  line = appendAttributeIfMissing(line, "~icon-class", h.lucideIconClass);
43563
+ } else if (h.iconClass) {
43564
+ line = appendAttributeIfMissing(line, "~icon-class", h.iconClass);
43257
43565
  }
43258
43566
  const semanticToken = extractSemanticHint(h.className);
43259
43567
  if (semanticToken) {
@@ -43269,7 +43577,7 @@ async function runPerRefAugmentations(scope, yaml, logger2, opts = {}) {
43269
43577
  }
43270
43578
  if (r2.genericClickable) {
43271
43579
  const h = r2.genericClickable;
43272
- const hint = h.title || h.ariaLabel || h.innerText || h.svgHint || (h.id || void 0) || extractSemanticHint(h.className);
43580
+ const hint = h.title || h.ariaLabel || h.innerText || h.svgHint || (h.iconClass ? resolveIconClassName(h.iconClass) : void 0) || (h.id || void 0) || extractSemanticHint(h.className);
43273
43581
  if (hint) {
43274
43582
  lines[i2] = injectElementLabel(lines[i2], "generic", hint);
43275
43583
  tagAugmentedNameSource(i2);
@@ -43277,6 +43585,8 @@ async function runPerRefAugmentations(scope, yaml, logger2, opts = {}) {
43277
43585
  let line = lines[i2];
43278
43586
  if (h.lucideIconClass) {
43279
43587
  line = appendAttributeIfMissing(line, "~icon-class", h.lucideIconClass);
43588
+ } else if (h.iconClass) {
43589
+ line = appendAttributeIfMissing(line, "~icon-class", h.iconClass);
43280
43590
  }
43281
43591
  const semanticToken = extractSemanticHint(h.className);
43282
43592
  if (semanticToken) {
@@ -44805,7 +45115,7 @@ var CdpScreencastManager = class {
44805
45115
  return;
44806
45116
  }
44807
45117
  const framerate = Math.round(1e3 / this.legacyOptions.minIntervalMs);
44808
- const encoder = this.encoderFactory.create({
45118
+ const encoder2 = this.encoderFactory.create({
44809
45119
  framerate,
44810
45120
  onInitSegment: videoCallbacks.onInitSegment,
44811
45121
  onMediaSegment: videoCallbacks.onMediaSegment,
@@ -44816,11 +45126,11 @@ var CdpScreencastManager = class {
44816
45126
  videoCallbacks.onError?.(err);
44817
45127
  }
44818
45128
  });
44819
- encoder.start();
45129
+ encoder2.start();
44820
45130
  const encoderSub = await this.subscribe(page, {
44821
45131
  onJpeg: (jpeg2) => {
44822
45132
  try {
44823
- encoder.pushFrame(jpeg2);
45133
+ encoder2.pushFrame(jpeg2);
44824
45134
  } catch (err) {
44825
45135
  getLogger().warn("[CdpScreencast] Failed to push frame to encoder", {
44826
45136
  error: err instanceof Error ? err.message : String(err)
@@ -44834,7 +45144,7 @@ var CdpScreencastManager = class {
44834
45144
  maxHeight: this.legacyOptions.maxHeight,
44835
45145
  everyNthFrame: this.legacyOptions.everyNthFrame
44836
45146
  });
44837
- this.legacyEncoder = encoder;
45147
+ this.legacyEncoder = encoder2;
44838
45148
  this.legacyEncoderSubscription = encoderSub;
44839
45149
  this._videoEncodingActive = true;
44840
45150
  getLogger().info("[CdpScreencast] Video encoding enabled", { framerate });
@@ -48168,7 +48478,11 @@ function buildSection(element, depth) {
48168
48478
  if (dismissRef) {
48169
48479
  section.dismissActionRef = dismissRef;
48170
48480
  }
48171
- if (element.attributes["aria-modal"] === "true") {
48481
+ const inert = element.dialogInert === true || element.visibility === "hidden" || element.visibility === "off-screen";
48482
+ if (inert) {
48483
+ section.isInertDialog = true;
48484
+ }
48485
+ if (element.attributes["aria-modal"] === "true" && !inert) {
48172
48486
  section.isBlockingModal = true;
48173
48487
  }
48174
48488
  }
@@ -48305,7 +48619,7 @@ function sortSectionsByFocus(sections) {
48305
48619
  dialogStack.unshift(focusedDialog.heading);
48306
48620
  }
48307
48621
  for (const dialog of dialogs) {
48308
- if (!dialog.containsActive) {
48622
+ if (!dialog.containsActive && !dialog.isInertDialog) {
48309
48623
  dialogStack.push(dialog.heading);
48310
48624
  }
48311
48625
  }
@@ -50116,12 +50430,406 @@ async function extractElementMetadata(locator, ref2, _logger3) {
50116
50430
  childImgAlt: domProps.childImgAlt
50117
50431
  };
50118
50432
  }
50433
+ init_logger();
50434
+ init_logger();
50435
+ var READY_ID = 0;
50436
+ var HEADER_BYTES = 4;
50437
+ var MAX_FRAME_BYTES = 64 * 1024 * 1024;
50438
+ var encoder = new TextEncoder();
50439
+ var decoder = new TextDecoder();
50440
+ function encodeFrame(obj) {
50441
+ const body = encoder.encode(JSON.stringify(obj));
50442
+ const frame = new Uint8Array(HEADER_BYTES + body.length);
50443
+ new DataView(frame.buffer).setUint32(0, body.length, false);
50444
+ frame.set(body, HEADER_BYTES);
50445
+ return frame;
50446
+ }
50447
+ var FrameDecoder = class {
50448
+ buf = new Uint8Array(0);
50449
+ /** Append a chunk and return every frame that is now complete. */
50450
+ push(chunk) {
50451
+ const next = new Uint8Array(this.buf.length + chunk.length);
50452
+ next.set(this.buf, 0);
50453
+ next.set(chunk, this.buf.length);
50454
+ this.buf = next;
50455
+ const out = [];
50456
+ let offset2 = 0;
50457
+ while (this.buf.length - offset2 >= HEADER_BYTES) {
50458
+ const len = new DataView(this.buf.buffer, this.buf.byteOffset + offset2).getUint32(0, false);
50459
+ if (len > MAX_FRAME_BYTES) {
50460
+ throw new Error(`cpu-offload frame length ${len} exceeds ${MAX_FRAME_BYTES} (corrupt stream)`);
50461
+ }
50462
+ if (this.buf.length - offset2 - HEADER_BYTES < len) break;
50463
+ const start = offset2 + HEADER_BYTES;
50464
+ const body = this.buf.subarray(start, start + len);
50465
+ out.push(JSON.parse(decoder.decode(body)));
50466
+ offset2 = start + len;
50467
+ }
50468
+ if (offset2 > 0) this.buf = this.buf.subarray(offset2);
50469
+ return out;
50470
+ }
50471
+ };
50472
+ var CpuOffloadTimeoutError = class extends Error {
50473
+ _tag = "CpuOffloadTimeoutError";
50474
+ constructor(task, timeoutMs) {
50475
+ super(`cpu-offload task '${task}' exceeded ${timeoutMs}ms and was terminated`);
50476
+ this.name = "CpuOffloadTimeoutError";
50477
+ }
50478
+ };
50479
+ var CpuOffloadWorkerError = class extends Error {
50480
+ _tag = "CpuOffloadWorkerError";
50481
+ constructor(message) {
50482
+ super(message);
50483
+ this.name = "CpuOffloadWorkerError";
50484
+ }
50485
+ };
50486
+ var READY_HANDSHAKE_TIMEOUT_MS = 1e4;
50487
+ function resolveCpuOffloadWorkerPath() {
50488
+ const candidates = [
50489
+ new URL("./worker-process.ts", import.meta.url),
50490
+ new URL("./worker-process.js", import.meta.url),
50491
+ new URL("./cpu-offload/worker-process.js", import.meta.url)
50492
+ ].map((u2) => fileURLToPath(u2));
50493
+ const found = candidates.find((p2) => existsSync2(p2));
50494
+ if (found) return found;
50495
+ try {
50496
+ const pkgJson = createRequire(import.meta.url).resolve("@chatsdet/browser-core/package.json");
50497
+ const viaPkg = join2(dirname(pkgJson), "dist", "cpu-offload", "worker-process.js");
50498
+ if (existsSync2(viaPkg)) return viaPkg;
50499
+ candidates.push(viaPkg);
50500
+ } catch {
50501
+ }
50502
+ throw new CpuOffloadWorkerError(`worker entry not found; looked in: ${candidates.join(", ")}`);
50503
+ }
50504
+ var CpuOffloadPool = class {
50505
+ opts;
50506
+ logger;
50507
+ workers = /* @__PURE__ */ new Set();
50508
+ idle = [];
50509
+ waiters = [];
50510
+ nextId = 1;
50511
+ shuttingDown = false;
50512
+ constructor(options) {
50513
+ this.logger = options.logger ?? consoleLogger;
50514
+ this.opts = {
50515
+ size: Math.max(1, options.size),
50516
+ defaultTimeoutMs: options.defaultTimeoutMs,
50517
+ spawn: options.spawn ?? spawn2,
50518
+ workerPath: options.workerPath ?? resolveCpuOffloadWorkerPath(),
50519
+ execPath: options.execPath ?? process.execPath
50520
+ };
50521
+ }
50522
+ /** Run a registered task. Rejects with a typed error on timeout/crash. */
50523
+ async run(task, payload, runOpts) {
50524
+ if (this.shuttingDown) {
50525
+ throw new CpuOffloadWorkerError("pool is shutting down");
50526
+ }
50527
+ const worker = await this.acquire();
50528
+ const timeoutMs = runOpts?.timeoutMs ?? this.opts.defaultTimeoutMs;
50529
+ return new Promise((resolve, reject) => {
50530
+ const id = this.nextId++;
50531
+ const timer = setTimeout(() => this.onTimeout(worker), timeoutMs);
50532
+ worker.current = {
50533
+ id,
50534
+ task,
50535
+ timeoutMs,
50536
+ timer,
50537
+ resolve,
50538
+ reject
50539
+ };
50540
+ const req = { id, task, payload };
50541
+ const stdin = worker.child.stdin;
50542
+ if (!stdin) {
50543
+ this.retireWorker(worker, new CpuOffloadWorkerError("worker has no stdin"));
50544
+ return;
50545
+ }
50546
+ try {
50547
+ stdin.write(encodeFrame(req));
50548
+ } catch (err) {
50549
+ this.retireWorker(
50550
+ worker,
50551
+ new CpuOffloadWorkerError(
50552
+ `failed to write to worker: ${err instanceof Error ? err.message : String(err)}`
50553
+ )
50554
+ );
50555
+ }
50556
+ });
50557
+ }
50558
+ stats() {
50559
+ return {
50560
+ total: this.workers.size,
50561
+ idle: this.idle.length,
50562
+ busy: this.workers.size - this.idle.length,
50563
+ waiting: this.waiters.length
50564
+ };
50565
+ }
50566
+ async shutdown() {
50567
+ this.shuttingDown = true;
50568
+ for (const waiter of this.waiters) {
50569
+ waiter.reject(new CpuOffloadWorkerError("pool shutting down"));
50570
+ }
50571
+ this.waiters.length = 0;
50572
+ for (const w of this.workers) {
50573
+ w.retired = true;
50574
+ if (w.current) {
50575
+ clearTimeout(w.current.timer);
50576
+ w.current.reject(new CpuOffloadWorkerError("pool shutting down"));
50577
+ }
50578
+ w.child.kill("SIGKILL");
50579
+ }
50580
+ this.workers.clear();
50581
+ this.idle.length = 0;
50582
+ }
50583
+ // ── internals ──────────────────────────────────────────────────────────
50584
+ acquire() {
50585
+ const free = this.idle.pop();
50586
+ if (free) return Promise.resolve(free);
50587
+ if (this.workers.size < this.opts.size) return this.spawnWorker();
50588
+ return new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));
50589
+ }
50590
+ release(worker) {
50591
+ if (worker.retired || this.shuttingDown) return;
50592
+ const waiter = this.waiters.shift();
50593
+ if (waiter) waiter.resolve(worker);
50594
+ else this.idle.push(worker);
50595
+ }
50596
+ spawnWorker() {
50597
+ const child = this.opts.spawn(this.opts.execPath, [this.opts.workerPath], {
50598
+ stdio: ["pipe", "pipe", "inherit"]
50599
+ });
50600
+ const worker = { child, decoder: new FrameDecoder(), current: null, retired: false };
50601
+ this.workers.add(worker);
50602
+ return new Promise((resolve, reject) => {
50603
+ let ready = false;
50604
+ let spawnFailed = false;
50605
+ const failSpawn = (err) => {
50606
+ if (ready || spawnFailed) return;
50607
+ spawnFailed = true;
50608
+ clearTimeout(readyTimer);
50609
+ this.workers.delete(worker);
50610
+ worker.child.kill("SIGKILL");
50611
+ reject(err);
50612
+ };
50613
+ const readyTimer = setTimeout(
50614
+ () => failSpawn(new CpuOffloadWorkerError("worker did not become ready in time")),
50615
+ READY_HANDSHAKE_TIMEOUT_MS
50616
+ );
50617
+ child.stdout?.on("data", (chunk) => {
50618
+ let frames;
50619
+ try {
50620
+ frames = worker.decoder.push(chunk);
50621
+ } catch (err) {
50622
+ const wrapped = new CpuOffloadWorkerError(
50623
+ `worker protocol error: ${err instanceof Error ? err.message : String(err)}`
50624
+ );
50625
+ if (ready) this.retireWorker(worker, wrapped);
50626
+ else failSpawn(wrapped);
50627
+ return;
50628
+ }
50629
+ for (const msg of frames) {
50630
+ const resp = msg;
50631
+ if (resp.id === READY_ID) {
50632
+ ready = true;
50633
+ clearTimeout(readyTimer);
50634
+ resolve(worker);
50635
+ } else {
50636
+ this.onResponse(worker, resp);
50637
+ }
50638
+ }
50639
+ });
50640
+ child.on("exit", (code, signal) => {
50641
+ if (!ready) {
50642
+ failSpawn(
50643
+ new CpuOffloadWorkerError(`worker exited before ready (code=${code}, signal=${signal})`)
50644
+ );
50645
+ return;
50646
+ }
50647
+ if (!worker.retired) {
50648
+ this.retireWorker(
50649
+ worker,
50650
+ new CpuOffloadWorkerError(`worker exited unexpectedly (code=${code}, signal=${signal})`)
50651
+ );
50652
+ }
50653
+ });
50654
+ child.on("error", (err) => {
50655
+ if (!ready) {
50656
+ failSpawn(new CpuOffloadWorkerError(`worker spawn failed: ${err.message}`));
50657
+ } else if (!worker.retired) {
50658
+ this.retireWorker(worker, new CpuOffloadWorkerError(`worker error: ${err.message}`));
50659
+ }
50660
+ });
50661
+ });
50662
+ }
50663
+ onResponse(worker, resp) {
50664
+ const call = worker.current;
50665
+ if (!call || call.id !== resp.id) return;
50666
+ clearTimeout(call.timer);
50667
+ worker.current = null;
50668
+ if (resp.ok) call.resolve(resp.result);
50669
+ else call.reject(new CpuOffloadWorkerError(resp.error.message));
50670
+ this.release(worker);
50671
+ }
50672
+ onTimeout(worker) {
50673
+ const call = worker.current;
50674
+ if (!call) return;
50675
+ this.retireWorker(worker, new CpuOffloadTimeoutError(call.task, call.timeoutMs));
50676
+ }
50677
+ /**
50678
+ * Reject any in-flight call, kill the process, drop the worker, and — unless
50679
+ * shutting down — spawn a replacement to keep capacity up and drain waiters.
50680
+ * Idempotent: the SIGKILL's own `exit` event re-enters here as a no-op.
50681
+ */
50682
+ retireWorker(worker, error) {
50683
+ if (worker.retired) return;
50684
+ worker.retired = true;
50685
+ this.workers.delete(worker);
50686
+ const idx = this.idle.indexOf(worker);
50687
+ if (idx >= 0) this.idle.splice(idx, 1);
50688
+ this.logger.warn("[CpuOffloadPool] retiring worker", {
50689
+ pid: worker.child.pid,
50690
+ task: worker.current?.task,
50691
+ reason: error.message,
50692
+ timedOut: error instanceof CpuOffloadTimeoutError
50693
+ });
50694
+ if (worker.current) {
50695
+ clearTimeout(worker.current.timer);
50696
+ worker.current.reject(error);
50697
+ worker.current = null;
50698
+ }
50699
+ worker.child.kill("SIGKILL");
50700
+ if (this.shuttingDown) return;
50701
+ if (this.waiters.length > 0 || this.workers.size < this.opts.size) {
50702
+ this.spawnWorker().then((w) => this.release(w)).catch((err) => {
50703
+ this.logger.error("[CpuOffloadPool] failed to respawn worker; rejecting waiters", {
50704
+ error: err instanceof Error ? err.message : String(err),
50705
+ waiting: this.waiters.length
50706
+ });
50707
+ const drained = this.waiters.splice(0);
50708
+ for (const waiter of drained) {
50709
+ waiter.reject(
50710
+ new CpuOffloadWorkerError(
50711
+ `worker respawn failed: ${err instanceof Error ? err.message : String(err)}`
50712
+ )
50713
+ );
50714
+ }
50715
+ });
50716
+ }
50717
+ }
50718
+ };
50719
+ var DEFAULT_CPU_OFFLOAD_TIMEOUT_MS = 15e3;
50720
+ var defaultCpuOffloadSize = () => Math.min(8, Math.max(2, Math.floor(cpus().length / 2)));
50721
+ var config = { enabled: false };
50722
+ var pool = null;
50723
+ function configureCpuOffload(next) {
50724
+ config = next;
50725
+ if (typeof next.enabled === "boolean" && next.enabled === false) {
50726
+ void shutdownCpuOffload();
50727
+ }
50728
+ }
50729
+ function isCpuOffloadEnabled() {
50730
+ const { enabled } = config;
50731
+ return typeof enabled === "function" ? !!enabled() : enabled;
50732
+ }
50733
+ function getPool() {
50734
+ if (!pool) {
50735
+ pool = new CpuOffloadPool({
50736
+ size: config.size ?? defaultCpuOffloadSize(),
50737
+ defaultTimeoutMs: config.timeoutMs ?? DEFAULT_CPU_OFFLOAD_TIMEOUT_MS,
50738
+ logger: config.logger ?? consoleLogger
50739
+ });
50740
+ }
50741
+ return pool;
50742
+ }
50743
+ async function shutdownCpuOffload() {
50744
+ const p2 = pool;
50745
+ pool = null;
50746
+ if (p2) await p2.shutdown();
50747
+ }
50748
+ var TIMEOUT_MARKER = "---\nSEMANTIC SNAPSHOT - formatting timed out\nThe page was too large to format within the time budget. Narrow with browser_snapshot search=\u2026 or scroll to a region.\n---";
50749
+ async function formatSnapshotMaybeOffloaded(yaml, options) {
50750
+ if (!isCpuOffloadEnabled()) {
50751
+ return formatSemanticSnapshot(yaml, options);
50752
+ }
50753
+ const logger2 = config.logger ?? consoleLogger;
50754
+ try {
50755
+ const { elementHints, matchedHintIds: _drop, ...rest } = options ?? {};
50756
+ const payload = {
50757
+ yaml,
50758
+ options: rest,
50759
+ ...elementHints ? { elementHints: [...elementHints] } : {}
50760
+ };
50761
+ const result = await getPool().run("format-snapshot", payload);
50762
+ if (options && result.matchedHintIds) {
50763
+ options.matchedHintIds = result.matchedHintIds;
50764
+ }
50765
+ return result.text;
50766
+ } catch (err) {
50767
+ if (err instanceof CpuOffloadTimeoutError) {
50768
+ logger2.warn("[CpuOffload] format-snapshot timed out; returning marker (NOT inlining)", {
50769
+ task: "format-snapshot",
50770
+ error: err.message
50771
+ });
50772
+ return TIMEOUT_MARKER;
50773
+ }
50774
+ logger2.warn("[CpuOffload] format-snapshot offload failed; running inline", {
50775
+ task: "format-snapshot",
50776
+ error: err instanceof Error ? err.message : String(err)
50777
+ });
50778
+ return formatSemanticSnapshot(yaml, options);
50779
+ }
50780
+ }
50119
50781
  var ToolAbortedError = class extends Error {
50120
50782
  constructor(stage) {
50121
50783
  super(`Browser action aborted at stage: ${stage}`);
50122
50784
  this.name = "ToolAbortedError";
50123
50785
  }
50124
50786
  };
50787
+ function labelForContext(context) {
50788
+ return context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
50789
+ }
50790
+ function elementDescription(context) {
50791
+ const role = context.role ? ` role=${context.role}` : "";
50792
+ const type = context.inputType ? ` type=${context.inputType}` : "";
50793
+ return `${context.tagName}${type}${role}`;
50794
+ }
50795
+ function formatFillTargetNotEditableMessage(context) {
50796
+ if (context.state === "not-fillable") {
50797
+ return `Cannot fill target ${labelForContext(context)}: ${elementDescription(context)} cannot accept text.`;
50798
+ }
50799
+ return `Cannot fill ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`;
50800
+ }
50801
+ var FillTargetNotEditableError = class extends Error {
50802
+ fillTarget;
50803
+ constructor(context) {
50804
+ super(formatFillTargetNotEditableMessage(context));
50805
+ this.name = "FillTargetNotEditableError";
50806
+ this.fillTarget = context;
50807
+ }
50808
+ };
50809
+ function isRecord(value) {
50810
+ return typeof value === "object" && value !== null;
50811
+ }
50812
+ function isFillTargetNotEditableError(error) {
50813
+ if (error instanceof FillTargetNotEditableError) return true;
50814
+ if (!isRecord(error) || error.name !== "FillTargetNotEditableError") return false;
50815
+ if (!isRecord(error.fillTarget)) return false;
50816
+ const context = error.fillTarget;
50817
+ return typeof context.ref === "string" && typeof context.tagName === "string" && (context.state === "readonly" || context.state === "disabled" || context.state === "not-fillable");
50818
+ }
50819
+ function formatFillTargetNotEditableRecovery(error, toolName = "browser_fill_form") {
50820
+ const context = error.fillTarget;
50821
+ const action = toolName === "browser_type" ? "type into" : "fill";
50822
+ if (context.state === "not-fillable") {
50823
+ return [
50824
+ `Error: ${toolName} cannot ${action} target ${labelForContext(context)}: ${elementDescription(context)} cannot accept typed text.`,
50825
+ `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.`
50826
+ ].join(" ");
50827
+ }
50828
+ return [
50829
+ `Error: ${toolName} cannot ${action} ${context.state} field ${labelForContext(context)}: ${elementDescription(context)} is not editable.`,
50830
+ `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.`
50831
+ ].join(" ");
50832
+ }
50125
50833
  function isMCPContentWithImages(result) {
50126
50834
  return typeof result === "object" && result !== null && "text" in result && "images" in result && Array.isArray(result.images) && result.images.length > 0;
50127
50835
  }
@@ -50212,6 +50920,31 @@ function formatEmptyPressKeyRecovery() {
50212
50920
  '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
50921
  ].join(" ");
50214
50922
  }
50923
+ function normalizeRef(ref2) {
50924
+ const trimmed = ref2?.trim();
50925
+ return trimmed ? trimmed : void 0;
50926
+ }
50927
+ function formatEmptyRefRecovery(toolName) {
50928
+ return [
50929
+ `Error: ${toolName} requires a non-empty element ref from the latest browser_snapshot.`,
50930
+ '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.'
50931
+ ].join(" ");
50932
+ }
50933
+ function extractWaitingAriaRef(error) {
50934
+ const message = error instanceof Error ? error.message : String(error);
50935
+ return message.match(/waiting for locator\('aria-ref=([^']+)'\)/)?.[1];
50936
+ }
50937
+ function isStaleAriaRefActionTimeout(error, action) {
50938
+ const message = error instanceof Error ? error.message : String(error);
50939
+ return message.includes(`${action}: Timeout`) && message.includes("waiting for locator('aria-ref=") && !message.includes("locator resolved to <");
50940
+ }
50941
+ function formatStaleRefActionRecovery(toolName, ref2, element) {
50942
+ const label = element ? ` for "${element}"` : "";
50943
+ return [
50944
+ `Error: ${toolName} could not find ref ${ref2}${label} in the current page snapshot.`,
50945
+ "Do not retry the same ref. Call browser_snapshot and choose a fresh ref, or use the coordinate path when that tool supports coordinates."
50946
+ ].join(" ");
50947
+ }
50215
50948
  function isSelectOptionNonSelectError(error) {
50216
50949
  const message = error instanceof Error ? error.message : String(error);
50217
50950
  return message.includes("selectOption:") && message.includes("Element is not a <select>");
@@ -50219,6 +50952,35 @@ function isSelectOptionNonSelectError(error) {
50219
50952
  function isMissingDownloadTempFileError(message) {
50220
50953
  return /\bENOENT:\s+no such file or directory,\s+open\b/.test(message) && message.includes("/playwright-downloads-");
50221
50954
  }
50955
+ function isNavigateDownloadStartingError(error) {
50956
+ const message = error instanceof Error ? error.message : String(error);
50957
+ return /\bgoto:\s+Download is starting\b/.test(message);
50958
+ }
50959
+ function formatNavigateDownloadStartingRecovery() {
50960
+ return [
50961
+ "Error: browser_navigate started a file download instead of loading a page.",
50962
+ "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."
50963
+ ].join(" ");
50964
+ }
50965
+ function isRelativeNavigateTarget(url) {
50966
+ const trimmed = url.trim();
50967
+ return trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("?") || trimmed.startsWith("#");
50968
+ }
50969
+ function isHttpUrl(url) {
50970
+ try {
50971
+ const parsed = new URL(url);
50972
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
50973
+ } catch {
50974
+ return false;
50975
+ }
50976
+ }
50977
+ function formatRelativeNavigateRecovery(url, currentUrl) {
50978
+ const baseDescription = currentUrl ? `current page URL is "${currentUrl}"` : "current page URL is unavailable";
50979
+ return [
50980
+ `Error: browser_navigate received relative URL "${url}", but the ${baseDescription}.`,
50981
+ "Use an absolute http(s) URL, or call browser_snapshot first if the current page needs to be inspected before navigating."
50982
+ ].join(" ");
50983
+ }
50222
50984
  function formatSelectOptionNonSelectRecovery(ref2, value, element) {
50223
50985
  return [
50224
50986
  `Error: browser_select_option can only operate on native <select> elements; ref ${ref2} for "${element}" resolved to a custom dropdown or combobox.`,
@@ -50676,11 +51438,38 @@ var BrowserToolExecutor = class {
50676
51438
  }
50677
51439
  // ==================== Navigation ====================
50678
51440
  async navigate(url) {
50679
- ensureAllowedHost(url, this.allowedHosts);
50680
- const rawResult = await this.client.navigate(url, { signal: this.currentSignal });
51441
+ const navigationUrl = await this.resolveNavigateUrl(url);
51442
+ if (navigationUrl.kind === "recovery") {
51443
+ return navigationUrl.message;
51444
+ }
51445
+ ensureAllowedHost(navigationUrl.url, this.allowedHosts);
51446
+ let rawResult;
51447
+ try {
51448
+ rawResult = await this.client.navigate(navigationUrl.url, { signal: this.currentSignal });
51449
+ } catch (error) {
51450
+ if (isNavigateDownloadStartingError(error)) {
51451
+ return formatNavigateDownloadStartingRecovery();
51452
+ }
51453
+ throw error;
51454
+ }
50681
51455
  const result = withRecoveryNotice(this.client, rawResult, this.logger);
50682
51456
  return this.formatNavigateResult(result);
50683
51457
  }
51458
+ async resolveNavigateUrl(url) {
51459
+ const trimmedUrl = url.trim();
51460
+ if (!isRelativeNavigateTarget(trimmedUrl)) {
51461
+ return { kind: "url", url: trimmedUrl };
51462
+ }
51463
+ const currentUrl = await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0);
51464
+ if (!currentUrl || !isHttpUrl(currentUrl)) {
51465
+ return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
51466
+ }
51467
+ try {
51468
+ return { kind: "url", url: new URL(trimmedUrl, currentUrl).toString() };
51469
+ } catch {
51470
+ return { kind: "recovery", message: formatRelativeNavigateRecovery(trimmedUrl, currentUrl) };
51471
+ }
51472
+ }
50684
51473
  async navigateBack() {
50685
51474
  const result = await this.client.navigateBack({ signal: this.currentSignal });
50686
51475
  return this.formatNavigateResult(result);
@@ -50930,7 +51719,7 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
50930
51719
  elementHints,
50931
51720
  matchedHintIds: void 0
50932
51721
  };
50933
- let semanticText = formatSemanticSnapshot(yaml, snapshotOpts);
51722
+ let semanticText = await formatSnapshotMaybeOffloaded(yaml, snapshotOpts);
50934
51723
  const formattedRefs = extractFormattedSnapshotRefs(semanticText);
50935
51724
  if (formattedRefs.length > LARGE_PAGE_REF_THRESHOLD) {
50936
51725
  semanticText = `[Large page: ~${formattedRefs.length.toLocaleString()} interactive elements \u2014 scroll-position hints reduced for speed. Narrow with browser_snapshot search=\u2026 or scroll to a region.]
@@ -50985,6 +51774,10 @@ ${body}
50985
51774
  * Returns a structured result with the message and optional created hint metadata.
50986
51775
  */
50987
51776
  async hint(opts) {
51777
+ const ref2 = normalizeRef(opts.ref);
51778
+ if (!ref2) {
51779
+ return { message: formatEmptyRefRecovery("browser_hint") };
51780
+ }
50988
51781
  if (!this.hintProvider) {
50989
51782
  return { message: "Element hints are not available in this environment." };
50990
51783
  }
@@ -50998,9 +51791,9 @@ ${body}
50998
51791
  return { message: "Cannot create hint: no recent snapshot available. Take a snapshot first." };
50999
51792
  }
51000
51793
  const elements = parseSnapshot(lastYaml);
51001
- const fpResult = fingerprintForHint(elements, opts.ref);
51794
+ const fpResult = fingerprintForHint(elements, ref2);
51002
51795
  if (!fpResult) {
51003
- return { message: `Cannot create hint: element ref "${opts.ref}" not found in the current snapshot.` };
51796
+ return { message: `Cannot create hint: element ref "${ref2}" not found in the current snapshot.` };
51004
51797
  }
51005
51798
  if (opts.invalidate) {
51006
51799
  if (!opts.reason) {
@@ -51019,7 +51812,7 @@ ${body}
51019
51812
  fingerprintHash: fpResult.hash,
51020
51813
  reason: opts.reason
51021
51814
  });
51022
- return { message: `Hint on [${opts.ref}] invalidated: ${opts.reason}` };
51815
+ return { message: `Hint on [${ref2}] invalidated: ${opts.reason}` };
51023
51816
  }
51024
51817
  if (!opts.frontmatter) {
51025
51818
  return { message: "Cannot create hint: frontmatter is required." };
@@ -51064,7 +51857,7 @@ ${body}
51064
51857
  });
51065
51858
  const anchorNote = fpResult.anchoredToParent ? ` (anchored to ${fpResult.anchor.role} "${fpResult.anchor.text ?? ""}")` : "";
51066
51859
  return {
51067
- message: `Hint created on [${opts.ref}]${anchorNote}: (${category}: ${opts.frontmatter})`,
51860
+ message: `Hint created on [${ref2}]${anchorNote}: (${category}: ${opts.frontmatter})`,
51068
51861
  created: {
51069
51862
  id: created.id,
51070
51863
  frontmatter: opts.frontmatter,
@@ -51075,10 +51868,11 @@ ${body}
51075
51868
  };
51076
51869
  }
51077
51870
  async screenshot(opts) {
51871
+ const ref2 = normalizeRef(opts?.ref);
51078
51872
  const img = await this.client.takeScreenshot({
51079
51873
  fullPage: opts?.fullPage,
51080
51874
  element: opts?.element,
51081
- ref: opts?.ref,
51875
+ ref: ref2,
51082
51876
  type: opts?.type ?? "jpeg",
51083
51877
  label: opts?.label,
51084
51878
  signal: this.currentSignal
@@ -51099,9 +51893,10 @@ ${body}
51099
51893
  return `Screenshot captured${opts?.label ? ` (${opts.label})` : ""}: ${img ? "success" : "no screenshot"}`;
51100
51894
  }
51101
51895
  async evaluate(opts) {
51896
+ const ref2 = normalizeRef(opts.ref);
51102
51897
  const result = await this.client.evaluate(opts.expression, {
51103
51898
  label: opts.label,
51104
- ref: opts.ref,
51899
+ ref: ref2,
51105
51900
  signal: this.currentSignal
51106
51901
  });
51107
51902
  return typeof result === "string" ? result : JSON.stringify(result, null, 2);
@@ -51119,7 +51914,7 @@ ${body}
51119
51914
  }
51120
51915
  // ==================== Interaction ====================
51121
51916
  async click(opts) {
51122
- const { ref: ref2 } = opts;
51917
+ const ref2 = normalizeRef(opts.ref);
51123
51918
  let x2 = opts.x;
51124
51919
  let y2 = opts.y;
51125
51920
  if (x2 === 0 && y2 === 0) {
@@ -51135,29 +51930,40 @@ ${body}
51135
51930
  const useCoordinates = !ref2 && x2 !== void 0 && y2 !== void 0;
51136
51931
  const clickVerb = opts.doubleClick ? "Double-clicked" : "Clicked";
51137
51932
  const actionDesc = useCoordinates ? `${clickVerb} "${opts.element}" at (${x2}, ${y2})` : `${clickVerb} "${opts.element}" via ref ${ref2}`;
51138
- this.lastReplayableAction = { description: actionDesc, thunk: () => this.click(opts) };
51933
+ this.lastReplayableAction = {
51934
+ description: actionDesc,
51935
+ thunk: () => this.click({ ...opts, ref: ref2 })
51936
+ };
51139
51937
  let clickedElement;
51140
51938
  let fallbackUsed = false;
51141
51939
  const preClickUrl = useCoordinates && this.augmentorProvider ? await this.client.getCurrentUrl({ signal: this.currentSignal }).catch(() => void 0) : void 0;
51142
- const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51143
- if (useCoordinates) {
51144
- const coordResult = await this.client.clickAtCoordinates(x2, y2, opts.element, {
51145
- doubleClick: opts.doubleClick,
51146
- modifiers: opts.modifiers,
51147
- signal: this.currentSignal
51148
- });
51149
- clickedElement = coordResult.clickedElement;
51150
- } else {
51151
- const refResult = await this.client.click(ref2, opts.element, {
51152
- doubleClick: opts.doubleClick,
51153
- button: opts.button,
51154
- modifiers: opts.modifiers,
51155
- signal: this.currentSignal
51156
- });
51157
- clickedElement = refResult.clickedElement;
51158
- fallbackUsed = refResult.fallbackUsed;
51940
+ let diffText;
51941
+ try {
51942
+ ({ diffText } = await this.withSemanticDiff(actionDesc, async () => {
51943
+ if (useCoordinates) {
51944
+ const coordResult = await this.client.clickAtCoordinates(x2, y2, opts.element, {
51945
+ doubleClick: opts.doubleClick,
51946
+ modifiers: opts.modifiers,
51947
+ signal: this.currentSignal
51948
+ });
51949
+ clickedElement = coordResult.clickedElement;
51950
+ } else {
51951
+ const refResult = await this.client.click(ref2, opts.element, {
51952
+ doubleClick: opts.doubleClick,
51953
+ button: opts.button,
51954
+ modifiers: opts.modifiers,
51955
+ signal: this.currentSignal
51956
+ });
51957
+ clickedElement = refResult.clickedElement;
51958
+ fallbackUsed = refResult.fallbackUsed;
51959
+ }
51960
+ }));
51961
+ } catch (error) {
51962
+ if (!useCoordinates && ref2 && isStaleAriaRefActionTimeout(error, "click")) {
51963
+ return formatStaleRefActionRecovery("browser_click", ref2, opts.element);
51159
51964
  }
51160
- });
51965
+ throw error;
51966
+ }
51161
51967
  if (useCoordinates) {
51162
51968
  await this.emitAugmentorSignal("coordinate_click", x2, y2, preClickUrl ?? "");
51163
51969
  } else {
@@ -51173,36 +51979,56 @@ ${body}
51173
51979
  return withRecoveryNoticeMCP(this.client, result, this.logger);
51174
51980
  }
51175
51981
  async type(opts) {
51982
+ const ref2 = normalizeRef(opts.ref);
51983
+ if (!ref2) {
51984
+ return formatEmptyRefRecovery("browser_type");
51985
+ }
51176
51986
  const submitNote = opts.submit ? " and submitted" : "";
51177
51987
  const actionDesc = `Typed "${opts.text}" into "${opts.element}"${submitNote}`;
51178
- this.lastReplayableAction = { description: actionDesc, thunk: () => this.type(opts) };
51179
- if (opts.submit) {
51180
- const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51181
- await this.client.type(opts.ref, opts.text, opts.element, opts.submit, {
51182
- delay: opts.delay,
51183
- signal: this.currentSignal
51988
+ this.lastReplayableAction = {
51989
+ description: actionDesc,
51990
+ thunk: () => this.type({ ...opts, ref: ref2 })
51991
+ };
51992
+ try {
51993
+ if (opts.submit) {
51994
+ const { diffText } = await this.withSemanticDiff(actionDesc, async () => {
51995
+ await this.client.type(ref2, opts.text, opts.element, opts.submit, {
51996
+ delay: opts.delay,
51997
+ signal: this.currentSignal
51998
+ });
51184
51999
  });
52000
+ return withRecoveryNoticeMCP(this.client, diffText, this.logger);
52001
+ }
52002
+ await this.client.type(ref2, opts.text, opts.element, opts.submit, {
52003
+ delay: opts.delay,
52004
+ signal: this.currentSignal
51185
52005
  });
51186
- return withRecoveryNoticeMCP(this.client, diffText, this.logger);
52006
+ return withRecoveryNotice(this.client, actionDesc, this.logger);
52007
+ } catch (error) {
52008
+ if (isFillTargetNotEditableError(error)) {
52009
+ return formatFillTargetNotEditableRecovery(error, "browser_type");
52010
+ }
52011
+ throw error;
51187
52012
  }
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
52013
  }
51194
52014
  async hover(opts) {
52015
+ const ref2 = normalizeRef(opts.ref);
52016
+ if (!ref2) {
52017
+ return formatEmptyRefRecovery("browser_hover");
52018
+ }
51195
52019
  const { diffText } = await this.withSemanticDiff(
51196
- `Hovered over "${opts.element}" via ref ${opts.ref}`,
52020
+ `Hovered over "${opts.element}" via ref ${ref2}`,
51197
52021
  async () => {
51198
- await this.client.hover(opts.ref, opts.element, { signal: this.currentSignal });
52022
+ await this.client.hover(ref2, opts.element, { signal: this.currentSignal });
51199
52023
  }
51200
52024
  );
51201
52025
  return diffText;
51202
52026
  }
51203
52027
  async drag(opts) {
52028
+ const startRef = normalizeRef(opts.startRef);
52029
+ const endRef = normalizeRef(opts.endRef);
51204
52030
  const useCoordinates = opts.startX !== void 0 && opts.startY !== void 0 && opts.endX !== void 0 && opts.endY !== void 0;
51205
- const useRefs = opts.startRef !== void 0 && opts.endRef !== void 0;
52031
+ const useRefs = startRef !== void 0 && endRef !== void 0;
51206
52032
  if (!useCoordinates && !useRefs) {
51207
52033
  return "Error: Provide either startRef + endRef, or all four coordinates (startX, startY, endX, endY)";
51208
52034
  }
@@ -51221,7 +52047,7 @@ ${body}
51221
52047
  { signal: this.currentSignal }
51222
52048
  );
51223
52049
  } else {
51224
- await this.client.drag(opts.startRef, opts.startElement, opts.endRef, opts.endElement, {
52050
+ await this.client.drag(startRef, opts.startElement, endRef, opts.endElement, {
51225
52051
  signal: this.currentSignal
51226
52052
  });
51227
52053
  }
@@ -51247,35 +52073,63 @@ ${body}
51247
52073
  return withRecoveryNotice(this.client, `Pressed ${key}`, this.logger);
51248
52074
  }
51249
52075
  async fillForm(fields) {
51250
- const { diffText } = await this.withSemanticDiff(
51251
- `Filled ${fields.length} form field(s)`,
51252
- async () => {
51253
- await this.client.fillForm(fields, { signal: this.currentSignal });
52076
+ const normalizedFields = fields.map((field) => ({
52077
+ ...field,
52078
+ ref: normalizeRef(field.ref) ?? ""
52079
+ }));
52080
+ if (normalizedFields.some((field) => field.ref.length === 0)) {
52081
+ return formatEmptyRefRecovery("browser_fill_form");
52082
+ }
52083
+ try {
52084
+ const { diffText } = await this.withSemanticDiff(
52085
+ `Filled ${fields.length} form field(s)`,
52086
+ async () => {
52087
+ await this.client.fillForm(normalizedFields, { signal: this.currentSignal });
52088
+ }
52089
+ );
52090
+ return diffText;
52091
+ } catch (error) {
52092
+ if (isFillTargetNotEditableError(error)) {
52093
+ return formatFillTargetNotEditableRecovery(error);
52094
+ }
52095
+ if (isStaleAriaRefActionTimeout(error, "fill")) {
52096
+ const staleRef = extractWaitingAriaRef(error);
52097
+ const field = normalizedFields.find((candidate) => candidate.ref === staleRef) ?? normalizedFields[0];
52098
+ return formatStaleRefActionRecovery(
52099
+ "browser_fill_form",
52100
+ staleRef ?? field?.ref ?? "unknown",
52101
+ field?.element
52102
+ );
51254
52103
  }
51255
- );
51256
- return diffText;
52104
+ throw error;
52105
+ }
51257
52106
  }
51258
52107
  async selectOption(opts) {
52108
+ const ref2 = normalizeRef(opts.ref);
52109
+ if (!ref2) {
52110
+ return formatEmptyRefRecovery("browser_select_option");
52111
+ }
51259
52112
  try {
51260
52113
  const { diffText } = await this.withSemanticDiff(
51261
52114
  `Selected "${opts.value}" in "${opts.element}"`,
51262
52115
  async () => {
51263
- await this.client.selectOption(opts.ref, opts.value, opts.element, { signal: this.currentSignal });
52116
+ await this.client.selectOption(ref2, opts.value, opts.element, { signal: this.currentSignal });
51264
52117
  }
51265
52118
  );
51266
52119
  return diffText;
51267
52120
  } catch (error) {
51268
52121
  if (isSelectOptionNonSelectError(error)) {
51269
- return formatSelectOptionNonSelectRecovery(opts.ref, opts.value, opts.element);
52122
+ return formatSelectOptionNonSelectRecovery(ref2, opts.value, opts.element);
51270
52123
  }
51271
52124
  throw error;
51272
52125
  }
51273
52126
  }
51274
52127
  async fileUpload(paths, ref2) {
52128
+ const normalizedRef = normalizeRef(ref2);
51275
52129
  const { diffText } = await this.withSemanticDiff(
51276
52130
  `Uploaded ${paths.length} file(s)`,
51277
52131
  async () => {
51278
- await this.client.fileUpload(paths, { ref: ref2, signal: this.currentSignal });
52132
+ await this.client.fileUpload(paths, { ref: normalizedRef, signal: this.currentSignal });
51279
52133
  }
51280
52134
  );
51281
52135
  return diffText;
@@ -51284,15 +52138,17 @@ ${body}
51284
52138
  async scroll(opts) {
51285
52139
  let result;
51286
52140
  let label;
51287
- if (opts.toRef) {
52141
+ const withinRef = normalizeRef(opts.withinRef);
52142
+ const toRef = normalizeRef(opts.toRef);
52143
+ if (toRef) {
51288
52144
  result = await this.client.scroll(
51289
52145
  opts.direction,
51290
52146
  opts.amount,
51291
- opts.withinRef,
51292
- opts.toRef,
52147
+ withinRef,
52148
+ toRef,
51293
52149
  { signal: this.currentSignal }
51294
52150
  );
51295
- label = `toRef=${opts.toRef}`;
52151
+ label = `toRef=${toRef}`;
51296
52152
  } else if (opts.x !== void 0 && opts.y !== void 0) {
51297
52153
  if (opts.x === 0 && opts.y === 0) {
51298
52154
  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 +52165,11 @@ ${body}
51309
52165
  result = await this.client.scroll(
51310
52166
  opts.direction,
51311
52167
  opts.amount,
51312
- opts.withinRef,
52168
+ withinRef,
51313
52169
  void 0,
51314
52170
  { signal: this.currentSignal }
51315
52171
  );
51316
- label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${opts.withinRef ? `, withinRef=${opts.withinRef}` : ""}`;
52172
+ label = `direction=${opts.direction}${opts.amount ? `, amount=${opts.amount}` : ""}${withinRef ? `, withinRef=${withinRef}` : ""}`;
51317
52173
  }
51318
52174
  const scrollInfo = typeof result === "object" && result !== null ? result.scrollInfo : void 0;
51319
52175
  if (scrollInfo && !scrollInfo.moved) {
@@ -51467,6 +52323,9 @@ ${header}
51467
52323
  if (message === "No downloads captured during this session.") {
51468
52324
  return `${message} Use browser_list_downloads after triggering a download, or try browser_pdf_read if the file opened inline instead of downloading.`;
51469
52325
  }
52326
+ if (downloadId === 0 && message === "Download #0 not found. Use browser_list_downloads to see available downloads.") {
52327
+ 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.";
52328
+ }
51470
52329
  if (isMissingDownloadTempFileError(message)) {
51471
52330
  const label = downloadId !== null && downloadId !== void 0 ? `Download #${downloadId}` : "The most recent download";
51472
52331
  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.`;
@@ -51684,7 +52543,7 @@ ${recoveryHint}`;
51684
52543
  await fs22.writeFile(snapshotFilePath, yaml, "utf-8");
51685
52544
  } catch {
51686
52545
  }
51687
- const semanticText = formatSemanticSnapshot(yaml, {
52546
+ const semanticText = await formatSnapshotMaybeOffloaded(yaml, {
51688
52547
  snapshotFilePath,
51689
52548
  currentTime: (/* @__PURE__ */ new Date()).toISOString(),
51690
52549
  capturedToasts: this.client.getCapturedToasts?.() ?? [],
@@ -54653,6 +55512,42 @@ function resolveBrowserContextRequestUrl(url, pageUrl) {
54653
55512
  return url;
54654
55513
  }
54655
55514
  }
55515
+ function isRouteAlreadyHandledError(error) {
55516
+ const message = error instanceof Error ? error.message : String(error);
55517
+ return message.includes("Route is already handled");
55518
+ }
55519
+ function createSingleUseContextRouteHandle(route, logger2) {
55520
+ let handled = false;
55521
+ const runOnce = async (action, fn) => {
55522
+ if (handled) {
55523
+ logger2.debug("[ContextRoute] Ignoring duplicate route action", { action });
55524
+ return;
55525
+ }
55526
+ handled = true;
55527
+ try {
55528
+ await fn();
55529
+ } catch (error) {
55530
+ if (isRouteAlreadyHandledError(error)) {
55531
+ logger2.debug("[ContextRoute] Route was already handled by Playwright", {
55532
+ action,
55533
+ error: error instanceof Error ? error.message : String(error)
55534
+ });
55535
+ return;
55536
+ }
55537
+ throw error;
55538
+ }
55539
+ };
55540
+ return {
55541
+ fulfill: (opts) => runOnce("fulfill", () => route.fulfill(opts)),
55542
+ continue: (opts) => runOnce("continue", () => route.continue(opts)),
55543
+ request: () => ({
55544
+ url: () => route.request().url(),
55545
+ method: () => route.request().method(),
55546
+ postData: () => route.request().postData(),
55547
+ headers: () => route.request().headers()
55548
+ })
55549
+ };
55550
+ }
54656
55551
  var TOAST_SNAPSHOT_TTL = 5;
54657
55552
  function formatMcpResponse(input) {
54658
55553
  const lines = [];
@@ -54842,6 +55737,16 @@ function normalizeDateInputValue(value) {
54842
55737
  }
54843
55738
  return value;
54844
55739
  }
55740
+ var OperationTimeoutError = class extends Error {
55741
+ label;
55742
+ timeoutMs;
55743
+ constructor(label, timeoutMs) {
55744
+ super(`${label} timed out after ${timeoutMs}ms`);
55745
+ this.name = "OperationTimeoutError";
55746
+ this.label = label;
55747
+ this.timeoutMs = timeoutMs;
55748
+ }
55749
+ };
54845
55750
  function asEmitter(page) {
54846
55751
  return page;
54847
55752
  }
@@ -54861,6 +55766,15 @@ function stripNewListeners(page, event, baseline) {
54861
55766
  }
54862
55767
  return stripped;
54863
55768
  }
55769
+ var runCodePageRequestMethods = /* @__PURE__ */ new Set([
55770
+ "delete",
55771
+ "fetch",
55772
+ "get",
55773
+ "head",
55774
+ "patch",
55775
+ "post",
55776
+ "put"
55777
+ ]);
54864
55778
  function logLateRunCodeWaitRejection(logger2, kind, promise) {
54865
55779
  promise.catch((err) => {
54866
55780
  logger2.debug(`[DirectPlaywright] runCode ${kind} wait rejected after call`, {
@@ -54868,6 +55782,95 @@ function logLateRunCodeWaitRejection(logger2, kind, promise) {
54868
55782
  });
54869
55783
  });
54870
55784
  }
55785
+ function guardRunCodeFetch(logger2) {
55786
+ return (...args) => {
55787
+ const promise = globalThis.fetch(...args);
55788
+ promise.catch((err) => {
55789
+ logger2.debug("[DirectPlaywright] runCode fetch rejected after call", {
55790
+ error: err instanceof Error ? err.message : String(err)
55791
+ });
55792
+ });
55793
+ return promise;
55794
+ };
55795
+ }
55796
+ function isPromiseLike(value) {
55797
+ return typeof value === "object" && value !== null && typeof value.catch === "function";
55798
+ }
55799
+ function logLateRunCodePageRequestRejection(logger2, method, url, promise) {
55800
+ promise.catch((err) => {
55801
+ logger2.debug("[DirectPlaywright] runCode page.request rejected after call", {
55802
+ method,
55803
+ url: url.slice(0, 200),
55804
+ error: err instanceof Error ? err.message : String(err)
55805
+ });
55806
+ });
55807
+ }
55808
+ function guardRunCodePageRequestContext(request2, getPageUrl, logger2) {
55809
+ if (request2 === null || typeof request2 !== "object" && typeof request2 !== "function") {
55810
+ return request2;
55811
+ }
55812
+ const functionCache = /* @__PURE__ */ new Map();
55813
+ return new Proxy(request2, {
55814
+ get(target, property, receiver) {
55815
+ const value = Reflect.get(target, property, receiver);
55816
+ if (typeof value !== "function") {
55817
+ return value;
55818
+ }
55819
+ const cached = functionCache.get(property);
55820
+ if (cached) {
55821
+ return cached;
55822
+ }
55823
+ if (!runCodePageRequestMethods.has(property)) {
55824
+ const bound = value.bind(target);
55825
+ functionCache.set(property, bound);
55826
+ return bound;
55827
+ }
55828
+ const method = property;
55829
+ const guarded = (...args) => {
55830
+ const requestArgs = [...args];
55831
+ const originalUrl = requestArgs[0];
55832
+ let requestUrl = null;
55833
+ if (typeof originalUrl === "string") {
55834
+ requestUrl = resolveBrowserContextRequestUrl(originalUrl, getPageUrl());
55835
+ requestArgs[0] = requestUrl;
55836
+ }
55837
+ const result = value.apply(target, requestArgs);
55838
+ if (isPromiseLike(result) && requestUrl !== null) {
55839
+ logLateRunCodePageRequestRejection(logger2, method, requestUrl, result);
55840
+ }
55841
+ return result;
55842
+ };
55843
+ functionCache.set(property, guarded);
55844
+ return guarded;
55845
+ }
55846
+ });
55847
+ }
55848
+ function guardRunCodePageRequest(page, logger2) {
55849
+ const guardedRequest = guardRunCodePageRequestContext(
55850
+ page.request,
55851
+ () => page.url(),
55852
+ logger2
55853
+ );
55854
+ const functionCache = /* @__PURE__ */ new Map();
55855
+ return new Proxy(page, {
55856
+ get(target, property, receiver) {
55857
+ if (property === "request") {
55858
+ return guardedRequest;
55859
+ }
55860
+ const value = Reflect.get(target, property, receiver);
55861
+ if (typeof value !== "function") {
55862
+ return value;
55863
+ }
55864
+ const cached = functionCache.get(property);
55865
+ if (cached) {
55866
+ return cached;
55867
+ }
55868
+ const bound = value.bind(page);
55869
+ functionCache.set(property, bound);
55870
+ return bound;
55871
+ }
55872
+ });
55873
+ }
54871
55874
  function guardFileChooserWaitForEvent(page, logger2) {
54872
55875
  const originalWaitForEvent = page.waitForEvent;
54873
55876
  let guarded = 0;
@@ -55297,10 +56300,7 @@ var PlaywrightClient = class _PlaywrightClient {
55297
56300
  return await Promise.race([
55298
56301
  operation,
55299
56302
  new Promise((_, reject) => {
55300
- timer = setTimeout(
55301
- () => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
55302
- timeoutMs
55303
- );
56303
+ timer = setTimeout(() => reject(new OperationTimeoutError(label, timeoutMs)), timeoutMs);
55304
56304
  })
55305
56305
  ]);
55306
56306
  } finally {
@@ -55398,6 +56398,20 @@ var PlaywrightClient = class _PlaywrightClient {
55398
56398
  // Tracing state
55399
56399
  tracingActive = false;
55400
56400
  traceDir = null;
56401
+ // Flushed trace chunk paths (one per browser context). Tracing is bound to a
56402
+ // single Playwright context; when the context is swapped/recreated mid-run we
56403
+ // flush the in-progress trace to disk here and restart tracing on the new
56404
+ // context, so the run-level trace survives the swap instead of being discarded.
56405
+ traceChunks = [];
56406
+ // Every trace temp dir created this session (incl. chunks whose flush failed,
56407
+ // so they aren't in traceChunks) — swept on disconnect so none leak.
56408
+ allTraceDirs = /* @__PURE__ */ new Set();
56409
+ // Context routes (e.g. Cloudflare Access scoped-header injection) last applied
56410
+ // via setupContextRoutes(). Route interception is per-context and is dropped
56411
+ // when the context is recreated; we remember the routes so auto-recovery paths
56412
+ // (restoreBrowser) can re-arm them — otherwise the edge would bounce
56413
+ // post-recovery requests to a login page.
56414
+ storedContextRoutes = [];
55401
56415
  // Video recording state.
55402
56416
  // The screencast encoder is the single source of truth for the recorded
55403
56417
  // file: while it lives, `videoDir`/`videoPath` derive from it. Before the
@@ -55649,6 +56663,17 @@ var PlaywrightClient = class _PlaywrightClient {
55649
56663
  supportsContextSwap() {
55650
56664
  return !this.extensionsEnabled && (!!this.browser || !!this.browserLease);
55651
56665
  }
56666
+ /**
56667
+ * Replace the browser context (e.g. to apply fresh storage state on re-auth),
56668
+ * keeping the same browser. Contract for context-bound state:
56669
+ * - Tracing AUTO-resumes here: the in-progress trace is flushed to a chunk
56670
+ * first (recoverable), then restarted on the new context.
56671
+ * - Context routes (e.g. CF Access scoped headers) are NOT re-armed here —
56672
+ * the caller must re-apply them via setupContextRoutes after the swap,
56673
+ * because the swap may intentionally change the headers (new credential).
56674
+ * (Crash-recovery in restoreBrowser DOES auto re-arm from stored routes,
56675
+ * since the headers are unchanged there.)
56676
+ */
55652
56677
  async swapContext(options) {
55653
56678
  if (!this.supportsContextSwap()) {
55654
56679
  throw new Error(
@@ -55672,14 +56697,9 @@ var PlaywrightClient = class _PlaywrightClient {
55672
56697
  this.logger.info("[DirectPlaywright] Stopping screencast frame delivery for context swap");
55673
56698
  await this._stopScreencastFrameDelivery();
55674
56699
  }
55675
- if (this.tracingActive && this.context) {
55676
- try {
55677
- await this.context.tracing.stop();
55678
- } catch {
55679
- } finally {
55680
- this.tracingActive = false;
55681
- this.traceDir = null;
55682
- }
56700
+ const resumeTracingAfterSwap = this.tracingActive && !!this.context;
56701
+ if (resumeTracingAfterSwap) {
56702
+ await this.flushTraceChunk();
55683
56703
  }
55684
56704
  this.contextClosedIntentionally = true;
55685
56705
  this.pageClosedIntentionally = true;
@@ -55771,6 +56791,13 @@ var PlaywrightClient = class _PlaywrightClient {
55771
56791
  if (wasScreencasting && this.page && this.screencastHandler) {
55772
56792
  await this.startScreencast(this.screencastHandler);
55773
56793
  }
56794
+ if (resumeTracingAfterSwap) {
56795
+ await this.beginTracingOnContext().catch((err) => {
56796
+ this.logger.warn("[DirectPlaywright] Failed to resume tracing after context swap", {
56797
+ error: err instanceof Error ? err.message : String(err)
56798
+ });
56799
+ });
56800
+ }
55774
56801
  this.logger.info("[DirectPlaywright] Context swapped successfully");
55775
56802
  }
55776
56803
  /**
@@ -56103,20 +57130,22 @@ var PlaywrightClient = class _PlaywrightClient {
56103
57130
  }
56104
57131
  this._downloadDir = null;
56105
57132
  }
56106
- if (this.traceDir) {
56107
- try {
56108
- await fs4.rm(this.traceDir, { recursive: true, force: true });
56109
- this.logger.debug("[DirectPlaywright] Cleaned up trace directory", {
56110
- traceDir: this.traceDir
56111
- });
56112
- } catch (err) {
56113
- this.logger.warn("[DirectPlaywright] Failed to clean up trace directory", {
56114
- traceDir: this.traceDir,
56115
- error: err instanceof Error ? err.message : String(err)
56116
- });
56117
- }
56118
- this.traceDir = null;
56119
- }
57133
+ const traceDirsToClean = new Set(this.allTraceDirs);
57134
+ if (this.traceDir) traceDirsToClean.add(this.traceDir);
57135
+ await Promise.allSettled(
57136
+ [...traceDirsToClean].map(
57137
+ (dir) => fs4.rm(dir, { recursive: true, force: true }).catch(
57138
+ (err) => this.logger.warn("[DirectPlaywright] Failed to clean up trace directory", {
57139
+ traceDir: dir,
57140
+ error: err instanceof Error ? err.message : String(err)
57141
+ })
57142
+ )
57143
+ )
57144
+ );
57145
+ this.traceDir = null;
57146
+ this.traceChunks = [];
57147
+ this.allTraceDirs.clear();
57148
+ this.storedContextRoutes = [];
56120
57149
  this._capturedDownloads = [];
56121
57150
  this._downloadCounter = 0;
56122
57151
  this._lastReportedDownloadIndex = 0;
@@ -56469,9 +57498,11 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56469
57498
  let threwError = null;
56470
57499
  let aborted = runController.signal.aborted;
56471
57500
  let guardedWaits = { fileChooser: 0, request: 0, response: 0 };
57501
+ const guardedFetch = guardRunCodeFetch(this.logger);
57502
+ const guardedPage = guardRunCodePageRequest(page, this.logger);
56472
57503
  try {
56473
57504
  if (!aborted) {
56474
- const fn = new Function("page", `return (async () => { ${code} })()`);
57505
+ const fn = new Function("page", "fetch", `return (async () => { ${code} })()`);
56475
57506
  const abortPromise = new Promise((_, reject) => {
56476
57507
  runController.signal.addEventListener(
56477
57508
  "abort",
@@ -56479,7 +57510,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56479
57510
  { once: true }
56480
57511
  );
56481
57512
  });
56482
- const inner = fn(page);
57513
+ const inner = fn(guardedPage, guardedFetch);
56483
57514
  inner.catch((err) => {
56484
57515
  this.logger.debug("[DirectPlaywright] runCode orphan rejected after race", {
56485
57516
  error: err instanceof Error ? err.message : String(err)
@@ -56698,6 +57729,16 @@ ${warning}` : ""}`,
56698
57729
  }
56699
57730
  } catch (err) {
56700
57731
  const message = err instanceof Error ? err.message : String(err);
57732
+ if (isPlaywrightTargetClosedError(err)) {
57733
+ throw toBrowserActionTargetClosedError(err, {
57734
+ operation: "click",
57735
+ ref: ref2,
57736
+ elementDesc,
57737
+ lastKnownUrl: this.lastKnownUrl,
57738
+ pendingCrashType: this._pendingCrashType,
57739
+ pendingCrashUrl: this._pendingCrashUrl
57740
+ });
57741
+ }
56701
57742
  const isTimeout = message.includes("Timeout") || message.includes("timeout");
56702
57743
  if (isTimeout && preClickBox) {
56703
57744
  const centerX = Math.round(preClickBox.x + preClickBox.width / 2);
@@ -56849,6 +57890,7 @@ ${warning}` : ""}`,
56849
57890
  const dialogMsg = this.getPendingDialogMessage();
56850
57891
  if (dialogMsg) throw new Error(dialogMsg);
56851
57892
  const locator = await this.resolveRef(ref2);
57893
+ await assertFillTargetEditable(locator, ref2, elementDesc);
56852
57894
  const timeout = opts?.timeoutMs ?? TIMEOUTS.TYPE;
56853
57895
  const inputType = await locator.evaluate((node) => node instanceof HTMLInputElement ? node.type : null).catch((err) => {
56854
57896
  this.logger.debug("[DirectPlaywright] Failed to inspect input type before typing", {
@@ -57840,13 +58882,58 @@ ${warning}` : ""}`,
57840
58882
  }
57841
58883
  // ================ TRACING ================
57842
58884
  async startTracing(_opts) {
57843
- const context = await this.getContext();
57844
58885
  this.logger.info("[DirectPlaywright] Starting trace recording");
57845
- this.traceDir = path5.join(this.tmpBaseDir, `playwright-trace-${Date.now()}`);
57846
- await fs4.mkdir(this.traceDir, { recursive: true });
57847
- await context.tracing.start({ screenshots: true, snapshots: true });
58886
+ this.traceChunks = [];
58887
+ this.allTraceDirs.clear();
58888
+ await this.beginTracingOnContext();
58889
+ }
58890
+ /**
58891
+ * Start a Playwright trace on the current context. Used both for the initial
58892
+ * `startTracing()` and to resume tracing after a context swap. `traceDir` /
58893
+ * `tracingActive` are set ONLY after `tracing.start()` resolves — a thrown
58894
+ * start must not leave `traceDir` set, or a later `stopTracing()` would target
58895
+ * a context that never started tracing ("Must start tracing before stopping").
58896
+ */
58897
+ async beginTracingOnContext() {
58898
+ const context = await this.getContext();
58899
+ const traceDir = path5.join(
58900
+ this.tmpBaseDir,
58901
+ `playwright-trace-${Date.now()}-${this.traceChunks.length}`
58902
+ );
58903
+ await fs4.mkdir(traceDir, { recursive: true });
58904
+ try {
58905
+ await context.tracing.start({ screenshots: true, snapshots: true });
58906
+ } catch (err) {
58907
+ await fs4.rm(traceDir, { recursive: true, force: true }).catch(() => {
58908
+ });
58909
+ throw err;
58910
+ }
58911
+ this.traceDir = traceDir;
58912
+ this.allTraceDirs.add(traceDir);
57848
58913
  this.tracingActive = true;
57849
58914
  }
58915
+ /**
58916
+ * Flush the in-progress trace on the current context to disk and record the
58917
+ * chunk. Returns the chunk path, or null when tracing isn't active. Stopping
58918
+ * WITH a path preserves the trace; stopping without one discards it. Errors are
58919
+ * swallowed (best-effort) so a failed flush never blocks a context swap.
58920
+ */
58921
+ async flushTraceChunk() {
58922
+ if (!this.tracingActive || !this.context || !this.traceDir) return null;
58923
+ const chunkPath = path5.join(this.traceDir, "trace.zip");
58924
+ try {
58925
+ await this.context.tracing.stop({ path: chunkPath });
58926
+ this.traceChunks.push(chunkPath);
58927
+ return chunkPath;
58928
+ } catch (err) {
58929
+ this.logger.warn("[DirectPlaywright] Failed to flush trace chunk", {
58930
+ error: err instanceof Error ? err.message : String(err)
58931
+ });
58932
+ return null;
58933
+ } finally {
58934
+ this.tracingActive = false;
58935
+ }
58936
+ }
57850
58937
  async traceGroup(name) {
57851
58938
  if (!this.tracingActive) return;
57852
58939
  const context = await this.getContext();
@@ -57866,20 +58953,26 @@ ${warning}` : ""}`,
57866
58953
  * The trace.zip can be viewed at https://trace.playwright.dev/
57867
58954
  */
57868
58955
  async stopTracing(_opts) {
57869
- const context = await this.getContext();
57870
- if (!this.traceDir) throw new Error("Tracing not started");
57871
58956
  this.logger.info("[DirectPlaywright] Stopping trace recording");
57872
- const tracePath = path5.join(this.traceDir, "trace.zip");
57873
- await context.tracing.stop({ path: tracePath });
57874
- this.tracingActive = false;
58957
+ await this.flushTraceChunk();
58958
+ if (this.traceChunks.length === 0) {
58959
+ throw new Error("No trace chunks captured (tracing not started, or all flushes failed)");
58960
+ }
58961
+ const chunks = [...this.traceChunks];
58962
+ if (chunks.length > 1) {
58963
+ this.logger.info("[DirectPlaywright] Captured multiple trace chunks across context swaps", {
58964
+ chunkCount: chunks.length
58965
+ });
58966
+ }
57875
58967
  return {
57876
- trace: tracePath,
58968
+ trace: chunks[0],
58969
+ traces: chunks,
57877
58970
  network: "",
57878
58971
  // Playwright bundles network data in trace.zip (not separate file)
57879
58972
  resources: "",
57880
58973
  // Playwright bundles resources in trace.zip (not separate file)
57881
- directory: this.traceDir,
57882
- legend: `Trace saved to ${tracePath}`
58974
+ directory: path5.dirname(chunks[0]),
58975
+ legend: `Trace saved to ${chunks[0]}`
57883
58976
  };
57884
58977
  }
57885
58978
  // ================ PRIVATE: Accessibility Snapshot ================
@@ -57998,11 +59091,22 @@ ${warning}` : ""}`,
57998
59091
  result = await this.raceAgainstSignal(
57999
59092
  "snapshot:aria",
58000
59093
  signal,
58001
- () => page.ariaSnapshot({ mode: "ai" })
59094
+ () => this.withOperationTimeout(
59095
+ page.ariaSnapshot({ mode: "ai" }),
59096
+ TIMEOUTS.ARIA_SNAPSHOT_CAP,
59097
+ "snapshot:aria"
59098
+ )
58002
59099
  );
58003
59100
  break;
58004
59101
  } catch (err) {
58005
59102
  if (signal?.aborted) return void 0;
59103
+ if (err instanceof OperationTimeoutError && err.label === "snapshot:aria") {
59104
+ this.logger.warn(
59105
+ "[DirectPlaywright] ariaSnapshot exceeded driver cap, using empty fallback",
59106
+ { capMs: TIMEOUTS.ARIA_SNAPSHOT_CAP, attempt }
59107
+ );
59108
+ break;
59109
+ }
58006
59110
  if (attempt < maxRetries) {
58007
59111
  this.logger.debug("[DirectPlaywright] Snapshot attempt failed, retrying", {
58008
59112
  attempt,
@@ -59004,6 +60108,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59004
60108
  this.browser = null;
59005
60109
  this.attachContextListeners(this.context);
59006
60110
  await this.createPageWithListeners(this.context, reason);
60111
+ await this.reapplyStoredContextRoutes();
60112
+ await this.resumeTracingOnRecreatedContext();
59007
60113
  await this.restoreUrlAfterRecovery(urlToRestore, crashType);
59008
60114
  await restartScreencastIfNeeded();
59009
60115
  await restartScreencastRecordingIfNeeded();
@@ -59049,6 +60155,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59049
60155
  this.attachBrowserListeners(this.browser);
59050
60156
  }
59051
60157
  await this.createPageWithListeners(this.context, reason);
60158
+ await this.reapplyStoredContextRoutes();
60159
+ await this.resumeTracingOnRecreatedContext();
59052
60160
  await this.restoreUrlAfterRecovery(urlToRestore, crashType);
59053
60161
  await restartScreencastIfNeeded();
59054
60162
  await restartScreencastRecordingIfNeeded();
@@ -59222,8 +60330,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59222
60330
  });
59223
60331
  await Promise.allSettled(stopPromises);
59224
60332
  this._screencastDisposables.clear();
59225
- const encoder = this._screencastEncoder;
59226
- if (!encoder) {
60333
+ const encoder2 = this._screencastEncoder;
60334
+ if (!encoder2) {
59227
60335
  this._screencastEncoder = null;
59228
60336
  this.logger.warn("[DirectPlaywright] Screencast encoder was never started \u2014 no video produced", {
59229
60337
  videoEnabled: this.videoEnabled,
@@ -59231,9 +60339,9 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59231
60339
  });
59232
60340
  return null;
59233
60341
  }
59234
- this._savedVideoPath = encoder.outputFilePath;
60342
+ this._savedVideoPath = encoder2.outputFilePath;
59235
60343
  this._screencastEncoder = null;
59236
- const stopResult = await encoder.stop();
60344
+ const stopResult = await encoder2.stop();
59237
60345
  const {
59238
60346
  durationMs,
59239
60347
  receivedFrameCount,
@@ -59475,17 +60583,50 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59475
60583
  // ================ EMULATION ROUTES ================
59476
60584
  async setupContextRoutes(routes) {
59477
60585
  const context = await this.getContext();
60586
+ await this.registerContextRoutes(routes, context);
60587
+ this.storedContextRoutes = routes;
60588
+ }
60589
+ async registerContextRoutes(routes, context) {
59478
60590
  for (const route of routes) {
59479
60591
  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
- });
60592
+ await route.handler(createSingleUseContextRouteHandle(pwRoute, this.logger));
60593
+ });
60594
+ }
60595
+ }
60596
+ /**
60597
+ * Restart tracing on a freshly recreated (post-crash/recovery) context. The
60598
+ * pre-recovery trace died with the old context and cannot be flushed, so the
60599
+ * best we can do is capture post-recovery activity and ensure the final
60600
+ * stopTracing() targets a context that actually has tracing started.
60601
+ * Best-effort — no-op when tracing wasn't active.
60602
+ */
60603
+ async resumeTracingOnRecreatedContext() {
60604
+ if (!this.tracingActive) return;
60605
+ this.tracingActive = false;
60606
+ await this.beginTracingOnContext().catch((err) => {
60607
+ this.logger.warn("[DirectPlaywright] Failed to resume tracing after browser recovery", {
60608
+ error: err instanceof Error ? err.message : String(err),
60609
+ chunksCapturedSoFar: this.traceChunks.length
60610
+ });
60611
+ });
60612
+ }
60613
+ /**
60614
+ * Re-arm the stored context routes on the current (freshly recreated) context.
60615
+ * Called from recovery paths that build a new context — without this, scoped
60616
+ * header injection (e.g. Cloudflare Access) silently stops after a restore and
60617
+ * the edge redirects subsequent requests to a login page. Best-effort.
60618
+ */
60619
+ async reapplyStoredContextRoutes() {
60620
+ if (this.storedContextRoutes.length === 0 || !this.context) return;
60621
+ try {
60622
+ await this.registerContextRoutes(this.storedContextRoutes, this.context);
60623
+ this.logger.info("[DirectPlaywright] Re-armed context routes on recreated context", {
60624
+ routeCount: this.storedContextRoutes.length
60625
+ });
60626
+ } catch (err) {
60627
+ this.logger.warn("[DirectPlaywright] Failed to re-arm context routes after recreation", {
60628
+ error: err instanceof Error ? err.message : String(err),
60629
+ routeCount: this.storedContextRoutes.length
59489
60630
  });
59490
60631
  }
59491
60632
  }
@@ -59497,33 +60638,93 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59497
60638
  return guessMimeType(filename);
59498
60639
  }
59499
60640
  };
59500
- async function assertFillTargetEditable(locator, ref2, elementDesc) {
59501
- const probe = await locator.evaluate((el) => {
59502
- const element = el;
59503
- const htmlInput = element instanceof HTMLInputElement ? element : null;
59504
- const htmlTextArea = element instanceof HTMLTextAreaElement ? element : null;
59505
- const htmlSelect = element instanceof HTMLSelectElement ? element : null;
59506
- const htmlButton = element instanceof HTMLButtonElement ? element : null;
59507
- const ariaReadonly = element.getAttribute("aria-readonly") === "true";
59508
- const ariaDisabled = element.getAttribute("aria-disabled") === "true";
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
- };
60641
+ function isPlaywrightTargetClosedError(error) {
60642
+ const message = error instanceof Error ? error.message : String(error);
60643
+ return /Target page, context or browser has been closed/i.test(message);
60644
+ }
60645
+ function toBrowserActionTargetClosedError(error, context) {
60646
+ const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
60647
+ const message = error instanceof Error ? error.message : String(error);
60648
+ const wrapped = new Error(`Browser click target closed for ${label}: ${message}`, {
60649
+ cause: error instanceof Error ? error : void 0
60650
+ });
60651
+ wrapped.name = "BrowserActionTargetClosedError";
60652
+ Object.assign(wrapped, { browserActionTargetClosed: context });
60653
+ return wrapped;
60654
+ }
60655
+ function toFillTargetProbeError(error, context) {
60656
+ const label = context.elementDesc ? `${context.elementDesc} (${context.ref})` : context.ref;
60657
+ const message = error instanceof Error ? error.message : String(error);
60658
+ const wrapped = new Error(`Fill target editability probe failed for ${label}: ${message}`, {
60659
+ cause: error instanceof Error ? error : void 0
59520
60660
  });
59521
- if (!probe.readonly && !probe.disabled) return;
59522
- const label = elementDesc ? `${elementDesc} (${ref2})` : ref2;
59523
- const state = probe.disabled ? "disabled" : "readonly";
60661
+ wrapped.name = "FillTargetProbeError";
60662
+ Object.assign(wrapped, { fillTargetProbe: context });
60663
+ return wrapped;
60664
+ }
60665
+ async function assertFillTargetEditable(locator, ref2, elementDesc) {
60666
+ let probe;
60667
+ try {
60668
+ probe = await locator.evaluate((el) => {
60669
+ const element = el;
60670
+ const htmlInput = element instanceof HTMLInputElement ? element : null;
60671
+ const htmlTextArea = element instanceof HTMLTextAreaElement ? element : null;
60672
+ const htmlSelect = element instanceof HTMLSelectElement ? element : null;
60673
+ const htmlButton = element instanceof HTMLButtonElement ? element : null;
60674
+ const ariaReadonly = element.getAttribute("aria-readonly") === "true";
60675
+ const ariaDisabled = element.getAttribute("aria-disabled") === "true";
60676
+ const contentEditableFalse = element.getAttribute("contenteditable") === "false";
60677
+ const inputType = htmlInput?.type.toLowerCase() ?? null;
60678
+ const fillableInputTypes = /* @__PURE__ */ new Set([
60679
+ "",
60680
+ "color",
60681
+ "date",
60682
+ "datetime-local",
60683
+ "email",
60684
+ "month",
60685
+ "number",
60686
+ "password",
60687
+ "range",
60688
+ "search",
60689
+ "tel",
60690
+ "text",
60691
+ "time",
60692
+ "url",
60693
+ "week"
60694
+ ]);
60695
+ return {
60696
+ readonly: Boolean(
60697
+ htmlInput?.readOnly || htmlTextArea?.readOnly || ariaReadonly || contentEditableFalse
60698
+ ),
60699
+ disabled: Boolean(
60700
+ htmlInput?.disabled || htmlTextArea?.disabled || htmlSelect?.disabled || htmlButton?.disabled || ariaDisabled
60701
+ ),
60702
+ fillable: Boolean(
60703
+ htmlInput && fillableInputTypes.has(inputType ?? "") || htmlTextArea || element.isContentEditable
60704
+ ),
60705
+ tagName: element.tagName.toLowerCase(),
60706
+ role: element.getAttribute("role"),
60707
+ inputType
60708
+ };
60709
+ });
60710
+ } catch (error) {
60711
+ throw toFillTargetProbeError(error, {
60712
+ ref: ref2,
60713
+ elementDesc,
60714
+ stage: "editability_probe"
60715
+ });
60716
+ }
60717
+ if (!probe.readonly && !probe.disabled && probe.fillable) return;
60718
+ const state = probe.disabled ? "disabled" : probe.readonly ? "readonly" : "not-fillable";
59524
60719
  const tag = probe.tagName ?? "element";
59525
- const role = probe.role ? ` role=${probe.role}` : "";
59526
- throw new Error(`Cannot fill ${state} field ${label}: ${tag}${role} is not editable.`);
60720
+ throw new FillTargetNotEditableError({
60721
+ ref: ref2,
60722
+ elementDesc,
60723
+ state,
60724
+ tagName: tag,
60725
+ role: probe.role,
60726
+ ...probe.inputType ? { inputType: probe.inputType } : {}
60727
+ });
59527
60728
  }
59528
60729
 
59529
60730
  export {
@@ -59621,6 +60822,16 @@ export {
59621
60822
  guessMimeType,
59622
60823
  captureElementAtPoint,
59623
60824
  extractElementMetadata,
60825
+ CpuOffloadTimeoutError,
60826
+ CpuOffloadWorkerError,
60827
+ resolveCpuOffloadWorkerPath,
60828
+ CpuOffloadPool,
60829
+ DEFAULT_CPU_OFFLOAD_TIMEOUT_MS,
60830
+ defaultCpuOffloadSize,
60831
+ configureCpuOffload,
60832
+ isCpuOffloadEnabled,
60833
+ shutdownCpuOffload,
60834
+ formatSnapshotMaybeOffloaded,
59624
60835
  ToolAbortedError,
59625
60836
  isMCPContentWithImages,
59626
60837
  coerceResultText,
@@ -59921,4 +61132,4 @@ playwright-extra/dist/index.esm.js:
59921
61132
  * @license MIT
59922
61133
  *)
59923
61134
  */
59924
- //# sourceMappingURL=chunk-AI3Y3AZH.js.map
61135
+ //# sourceMappingURL=chunk-7OO3RJYR.js.map