@runcofactor/cli 0.17.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 (40) hide show
  1. package/dist/{chunk-JZWJFG4M.js → chunk-2R6544Z3.js} +2 -2
  2. package/dist/{chunk-MO5G5TP5.js → chunk-7OO3RJYR.js} +994 -91
  3. package/dist/chunk-7OO3RJYR.js.map +1 -0
  4. package/dist/{chunk-FGB6PS2T.js → chunk-A6TH5VUI.js} +206 -76
  5. package/dist/chunk-A6TH5VUI.js.map +1 -0
  6. package/dist/{chunk-HGPZ2QWV.js → chunk-E2FUQJVO.js} +3 -3
  7. package/dist/{chunk-A4ISG3VX.js → chunk-U7OMQJ7V.js} +52 -4
  8. package/dist/chunk-U7OMQJ7V.js.map +1 -0
  9. package/dist/{dist-WW2S733L.js → dist-4COOAKY3.js} +22 -2
  10. package/dist/index.js +6 -6
  11. package/dist/{mcp-V4GKEL2J.js → mcp-57VVZTXM.js} +3 -3
  12. package/dist/{record-LDNKS42Y.js → record-RWVGZZSW.js} +2 -2
  13. package/dist/{sandbox-PLYUNOFX.js → sandbox-6D54ZQCG.js} +2 -2
  14. package/dist/{session-Y6RO7GFC.js → session-GDCMTNB3.js} +3 -3
  15. package/dist/skills/optimize-cache/SKILL.md +25 -0
  16. package/dist/{src-ECFPDE5X.js → src-5YXGM2KB.js} +2 -2
  17. package/dist/{src-PDYXACRP.js → src-B7CPYFLS.js} +4 -2
  18. package/dist/{src-OWPS5DCK.js → src-GRO2QR7U.js} +3 -3
  19. package/dist/{trace-BCSP7PCX.js → trace-7LRHSPWN.js} +3 -3
  20. package/dist/{workflow-76EKT3DS.js → workflow-5BFRS4CE.js} +5 -5
  21. package/dist/{workflow-cache-UDTTNIHT.js → workflow-cache-VVP3SPFM.js} +4 -4
  22. package/dist/{workflow-debug-WO7D32EI.js → workflow-debug-XGOHNZ4E.js} +4 -4
  23. package/package.json +3 -3
  24. package/dist/chunk-A4ISG3VX.js.map +0 -1
  25. package/dist/chunk-FGB6PS2T.js.map +0 -1
  26. package/dist/chunk-MO5G5TP5.js.map +0 -1
  27. /package/dist/{chunk-JZWJFG4M.js.map → chunk-2R6544Z3.js.map} +0 -0
  28. /package/dist/{chunk-HGPZ2QWV.js.map → chunk-E2FUQJVO.js.map} +0 -0
  29. /package/dist/{dist-WW2S733L.js.map → dist-4COOAKY3.js.map} +0 -0
  30. /package/dist/{mcp-V4GKEL2J.js.map → mcp-57VVZTXM.js.map} +0 -0
  31. /package/dist/{record-LDNKS42Y.js.map → record-RWVGZZSW.js.map} +0 -0
  32. /package/dist/{sandbox-PLYUNOFX.js.map → sandbox-6D54ZQCG.js.map} +0 -0
  33. /package/dist/{session-Y6RO7GFC.js.map → session-GDCMTNB3.js.map} +0 -0
  34. /package/dist/{src-ECFPDE5X.js.map → src-5YXGM2KB.js.map} +0 -0
  35. /package/dist/{src-PDYXACRP.js.map → src-B7CPYFLS.js.map} +0 -0
  36. /package/dist/{src-OWPS5DCK.js.map → src-GRO2QR7U.js.map} +0 -0
  37. /package/dist/{trace-BCSP7PCX.js.map → trace-7LRHSPWN.js.map} +0 -0
  38. /package/dist/{workflow-76EKT3DS.js.map → workflow-5BFRS4CE.js.map} +0 -0
  39. /package/dist/{workflow-cache-UDTTNIHT.js.map → workflow-cache-VVP3SPFM.js.map} +0 -0
  40. /package/dist/{workflow-debug-WO7D32EI.js.map → workflow-debug-XGOHNZ4E.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,6 +50430,354 @@ 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}`);
@@ -51057,7 +51719,7 @@ ${effectiveShowCoordinateGrid ? "Coordinate grid enabled - read coordinates from
51057
51719
  elementHints,
51058
51720
  matchedHintIds: void 0
51059
51721
  };
51060
- let semanticText = formatSemanticSnapshot(yaml, snapshotOpts);
51722
+ let semanticText = await formatSnapshotMaybeOffloaded(yaml, snapshotOpts);
51061
51723
  const formattedRefs = extractFormattedSnapshotRefs(semanticText);
51062
51724
  if (formattedRefs.length > LARGE_PAGE_REF_THRESHOLD) {
51063
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.]
@@ -51881,7 +52543,7 @@ ${recoveryHint}`;
51881
52543
  await fs22.writeFile(snapshotFilePath, yaml, "utf-8");
51882
52544
  } catch {
51883
52545
  }
51884
- const semanticText = formatSemanticSnapshot(yaml, {
52546
+ const semanticText = await formatSnapshotMaybeOffloaded(yaml, {
51885
52547
  snapshotFilePath,
51886
52548
  currentTime: (/* @__PURE__ */ new Date()).toISOString(),
51887
52549
  capturedToasts: this.client.getCapturedToasts?.() ?? [],
@@ -55075,6 +55737,16 @@ function normalizeDateInputValue(value) {
55075
55737
  }
55076
55738
  return value;
55077
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
+ };
55078
55750
  function asEmitter(page) {
55079
55751
  return page;
55080
55752
  }
@@ -55094,6 +55766,15 @@ function stripNewListeners(page, event, baseline) {
55094
55766
  }
55095
55767
  return stripped;
55096
55768
  }
55769
+ var runCodePageRequestMethods = /* @__PURE__ */ new Set([
55770
+ "delete",
55771
+ "fetch",
55772
+ "get",
55773
+ "head",
55774
+ "patch",
55775
+ "post",
55776
+ "put"
55777
+ ]);
55097
55778
  function logLateRunCodeWaitRejection(logger2, kind, promise) {
55098
55779
  promise.catch((err) => {
55099
55780
  logger2.debug(`[DirectPlaywright] runCode ${kind} wait rejected after call`, {
@@ -55112,6 +55793,84 @@ function guardRunCodeFetch(logger2) {
55112
55793
  return promise;
55113
55794
  };
55114
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
+ }
55115
55874
  function guardFileChooserWaitForEvent(page, logger2) {
55116
55875
  const originalWaitForEvent = page.waitForEvent;
55117
55876
  let guarded = 0;
@@ -55541,10 +56300,7 @@ var PlaywrightClient = class _PlaywrightClient {
55541
56300
  return await Promise.race([
55542
56301
  operation,
55543
56302
  new Promise((_, reject) => {
55544
- timer = setTimeout(
55545
- () => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
55546
- timeoutMs
55547
- );
56303
+ timer = setTimeout(() => reject(new OperationTimeoutError(label, timeoutMs)), timeoutMs);
55548
56304
  })
55549
56305
  ]);
55550
56306
  } finally {
@@ -55642,6 +56398,20 @@ var PlaywrightClient = class _PlaywrightClient {
55642
56398
  // Tracing state
55643
56399
  tracingActive = false;
55644
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 = [];
55645
56415
  // Video recording state.
55646
56416
  // The screencast encoder is the single source of truth for the recorded
55647
56417
  // file: while it lives, `videoDir`/`videoPath` derive from it. Before the
@@ -55893,6 +56663,17 @@ var PlaywrightClient = class _PlaywrightClient {
55893
56663
  supportsContextSwap() {
55894
56664
  return !this.extensionsEnabled && (!!this.browser || !!this.browserLease);
55895
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
+ */
55896
56677
  async swapContext(options) {
55897
56678
  if (!this.supportsContextSwap()) {
55898
56679
  throw new Error(
@@ -55916,14 +56697,9 @@ var PlaywrightClient = class _PlaywrightClient {
55916
56697
  this.logger.info("[DirectPlaywright] Stopping screencast frame delivery for context swap");
55917
56698
  await this._stopScreencastFrameDelivery();
55918
56699
  }
55919
- if (this.tracingActive && this.context) {
55920
- try {
55921
- await this.context.tracing.stop();
55922
- } catch {
55923
- } finally {
55924
- this.tracingActive = false;
55925
- this.traceDir = null;
55926
- }
56700
+ const resumeTracingAfterSwap = this.tracingActive && !!this.context;
56701
+ if (resumeTracingAfterSwap) {
56702
+ await this.flushTraceChunk();
55927
56703
  }
55928
56704
  this.contextClosedIntentionally = true;
55929
56705
  this.pageClosedIntentionally = true;
@@ -56015,6 +56791,13 @@ var PlaywrightClient = class _PlaywrightClient {
56015
56791
  if (wasScreencasting && this.page && this.screencastHandler) {
56016
56792
  await this.startScreencast(this.screencastHandler);
56017
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
+ }
56018
56801
  this.logger.info("[DirectPlaywright] Context swapped successfully");
56019
56802
  }
56020
56803
  /**
@@ -56347,20 +57130,22 @@ var PlaywrightClient = class _PlaywrightClient {
56347
57130
  }
56348
57131
  this._downloadDir = null;
56349
57132
  }
56350
- if (this.traceDir) {
56351
- try {
56352
- await fs4.rm(this.traceDir, { recursive: true, force: true });
56353
- this.logger.debug("[DirectPlaywright] Cleaned up trace directory", {
56354
- traceDir: this.traceDir
56355
- });
56356
- } catch (err) {
56357
- this.logger.warn("[DirectPlaywright] Failed to clean up trace directory", {
56358
- traceDir: this.traceDir,
56359
- error: err instanceof Error ? err.message : String(err)
56360
- });
56361
- }
56362
- this.traceDir = null;
56363
- }
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 = [];
56364
57149
  this._capturedDownloads = [];
56365
57150
  this._downloadCounter = 0;
56366
57151
  this._lastReportedDownloadIndex = 0;
@@ -56714,6 +57499,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56714
57499
  let aborted = runController.signal.aborted;
56715
57500
  let guardedWaits = { fileChooser: 0, request: 0, response: 0 };
56716
57501
  const guardedFetch = guardRunCodeFetch(this.logger);
57502
+ const guardedPage = guardRunCodePageRequest(page, this.logger);
56717
57503
  try {
56718
57504
  if (!aborted) {
56719
57505
  const fn = new Function("page", "fetch", `return (async () => { ${code} })()`);
@@ -56724,7 +57510,7 @@ Use coordinate-based clicking (x, y) to interact with elements visible in the sc
56724
57510
  { once: true }
56725
57511
  );
56726
57512
  });
56727
- const inner = fn(page, guardedFetch);
57513
+ const inner = fn(guardedPage, guardedFetch);
56728
57514
  inner.catch((err) => {
56729
57515
  this.logger.debug("[DirectPlaywright] runCode orphan rejected after race", {
56730
57516
  error: err instanceof Error ? err.message : String(err)
@@ -58096,13 +58882,58 @@ ${warning}` : ""}`,
58096
58882
  }
58097
58883
  // ================ TRACING ================
58098
58884
  async startTracing(_opts) {
58099
- const context = await this.getContext();
58100
58885
  this.logger.info("[DirectPlaywright] Starting trace recording");
58101
- this.traceDir = path5.join(this.tmpBaseDir, `playwright-trace-${Date.now()}`);
58102
- await fs4.mkdir(this.traceDir, { recursive: true });
58103
- 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);
58104
58913
  this.tracingActive = true;
58105
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
+ }
58106
58937
  async traceGroup(name) {
58107
58938
  if (!this.tracingActive) return;
58108
58939
  const context = await this.getContext();
@@ -58122,20 +58953,26 @@ ${warning}` : ""}`,
58122
58953
  * The trace.zip can be viewed at https://trace.playwright.dev/
58123
58954
  */
58124
58955
  async stopTracing(_opts) {
58125
- const context = await this.getContext();
58126
- if (!this.traceDir) throw new Error("Tracing not started");
58127
58956
  this.logger.info("[DirectPlaywright] Stopping trace recording");
58128
- const tracePath = path5.join(this.traceDir, "trace.zip");
58129
- await context.tracing.stop({ path: tracePath });
58130
- 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
+ }
58131
58967
  return {
58132
- trace: tracePath,
58968
+ trace: chunks[0],
58969
+ traces: chunks,
58133
58970
  network: "",
58134
58971
  // Playwright bundles network data in trace.zip (not separate file)
58135
58972
  resources: "",
58136
58973
  // Playwright bundles resources in trace.zip (not separate file)
58137
- directory: this.traceDir,
58138
- legend: `Trace saved to ${tracePath}`
58974
+ directory: path5.dirname(chunks[0]),
58975
+ legend: `Trace saved to ${chunks[0]}`
58139
58976
  };
58140
58977
  }
58141
58978
  // ================ PRIVATE: Accessibility Snapshot ================
@@ -58254,11 +59091,22 @@ ${warning}` : ""}`,
58254
59091
  result = await this.raceAgainstSignal(
58255
59092
  "snapshot:aria",
58256
59093
  signal,
58257
- () => page.ariaSnapshot({ mode: "ai" })
59094
+ () => this.withOperationTimeout(
59095
+ page.ariaSnapshot({ mode: "ai" }),
59096
+ TIMEOUTS.ARIA_SNAPSHOT_CAP,
59097
+ "snapshot:aria"
59098
+ )
58258
59099
  );
58259
59100
  break;
58260
59101
  } catch (err) {
58261
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
+ }
58262
59110
  if (attempt < maxRetries) {
58263
59111
  this.logger.debug("[DirectPlaywright] Snapshot attempt failed, retrying", {
58264
59112
  attempt,
@@ -59260,6 +60108,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59260
60108
  this.browser = null;
59261
60109
  this.attachContextListeners(this.context);
59262
60110
  await this.createPageWithListeners(this.context, reason);
60111
+ await this.reapplyStoredContextRoutes();
60112
+ await this.resumeTracingOnRecreatedContext();
59263
60113
  await this.restoreUrlAfterRecovery(urlToRestore, crashType);
59264
60114
  await restartScreencastIfNeeded();
59265
60115
  await restartScreencastRecordingIfNeeded();
@@ -59305,6 +60155,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59305
60155
  this.attachBrowserListeners(this.browser);
59306
60156
  }
59307
60157
  await this.createPageWithListeners(this.context, reason);
60158
+ await this.reapplyStoredContextRoutes();
60159
+ await this.resumeTracingOnRecreatedContext();
59308
60160
  await this.restoreUrlAfterRecovery(urlToRestore, crashType);
59309
60161
  await restartScreencastIfNeeded();
59310
60162
  await restartScreencastRecordingIfNeeded();
@@ -59478,8 +60330,8 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59478
60330
  });
59479
60331
  await Promise.allSettled(stopPromises);
59480
60332
  this._screencastDisposables.clear();
59481
- const encoder = this._screencastEncoder;
59482
- if (!encoder) {
60333
+ const encoder2 = this._screencastEncoder;
60334
+ if (!encoder2) {
59483
60335
  this._screencastEncoder = null;
59484
60336
  this.logger.warn("[DirectPlaywright] Screencast encoder was never started \u2014 no video produced", {
59485
60337
  videoEnabled: this.videoEnabled,
@@ -59487,9 +60339,9 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59487
60339
  });
59488
60340
  return null;
59489
60341
  }
59490
- this._savedVideoPath = encoder.outputFilePath;
60342
+ this._savedVideoPath = encoder2.outputFilePath;
59491
60343
  this._screencastEncoder = null;
59492
- const stopResult = await encoder.stop();
60344
+ const stopResult = await encoder2.stop();
59493
60345
  const {
59494
60346
  durationMs,
59495
60347
  receivedFrameCount,
@@ -59731,12 +60583,53 @@ Legacy two-step (only if you need to change the action between calls): browser_h
59731
60583
  // ================ EMULATION ROUTES ================
59732
60584
  async setupContextRoutes(routes) {
59733
60585
  const context = await this.getContext();
60586
+ await this.registerContextRoutes(routes, context);
60587
+ this.storedContextRoutes = routes;
60588
+ }
60589
+ async registerContextRoutes(routes, context) {
59734
60590
  for (const route of routes) {
59735
60591
  await context.route(route.urlPattern, async (pwRoute) => {
59736
60592
  await route.handler(createSingleUseContextRouteHandle(pwRoute, this.logger));
59737
60593
  });
59738
60594
  }
59739
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
60630
+ });
60631
+ }
60632
+ }
59740
60633
  // ================ PRIVATE: Download Helpers ================
59741
60634
  formatFileSize(bytes) {
59742
60635
  return formatFileSize(bytes);
@@ -59929,6 +60822,16 @@ export {
59929
60822
  guessMimeType,
59930
60823
  captureElementAtPoint,
59931
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,
59932
60835
  ToolAbortedError,
59933
60836
  isMCPContentWithImages,
59934
60837
  coerceResultText,
@@ -60229,4 +61132,4 @@ playwright-extra/dist/index.esm.js:
60229
61132
  * @license MIT
60230
61133
  *)
60231
61134
  */
60232
- //# sourceMappingURL=chunk-MO5G5TP5.js.map
61135
+ //# sourceMappingURL=chunk-7OO3RJYR.js.map