opensteer 0.9.3 → 0.9.5

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 (38) hide show
  1. package/README.md +158 -165
  2. package/dist/{chunk-UM2Q4JD2.js → chunk-7D45QUZ3.js} +5 -7
  3. package/dist/chunk-7D45QUZ3.js.map +1 -0
  4. package/dist/{chunk-GREXSYNC.js → chunk-7LQL5YUR.js} +578 -224
  5. package/dist/chunk-7LQL5YUR.js.map +1 -0
  6. package/dist/{chunk-2TIVULZY.js → chunk-GSCQQKZZ.js} +53 -9
  7. package/dist/chunk-GSCQQKZZ.js.map +1 -0
  8. package/dist/{chunk-BMPUL66S.js → chunk-T5P2QGZ3.js} +58 -53
  9. package/dist/chunk-T5P2QGZ3.js.map +1 -0
  10. package/dist/{chunk-FIMNKEG5.js → chunk-ZRF7WMS3.js} +4 -4
  11. package/dist/{chunk-FIMNKEG5.js.map → chunk-ZRF7WMS3.js.map} +1 -1
  12. package/dist/cli/bin.cjs +707 -278
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +30 -34
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +733 -473
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +25 -460
  19. package/dist/index.d.ts +25 -460
  20. package/dist/index.js +4 -5
  21. package/dist/local-view/serve-entry.cjs +106 -57
  22. package/dist/local-view/serve-entry.cjs.map +1 -1
  23. package/dist/local-view/serve-entry.js +2 -2
  24. package/dist/opensteer-T2JENADR.js +6 -0
  25. package/dist/{opensteer-IBDPRIEX.js.map → opensteer-T2JENADR.js.map} +1 -1
  26. package/dist/{session-control-IFE3IPS3.js → session-control-M3JD7ZKA.js} +4 -4
  27. package/dist/{session-control-IFE3IPS3.js.map → session-control-M3JD7ZKA.js.map} +1 -1
  28. package/package.json +6 -6
  29. package/skills/opensteer/SKILL.md +134 -95
  30. package/skills/recorder/SKILL.md +43 -48
  31. package/dist/chunk-2TIVULZY.js.map +0 -1
  32. package/dist/chunk-BMPUL66S.js.map +0 -1
  33. package/dist/chunk-GREXSYNC.js.map +0 -1
  34. package/dist/chunk-KCINASQC.js +0 -3
  35. package/dist/chunk-KCINASQC.js.map +0 -1
  36. package/dist/chunk-UM2Q4JD2.js.map +0 -1
  37. package/dist/opensteer-IBDPRIEX.js +0 -6
  38. package/skills/recorder/references/recorder-reference.md +0 -71
package/dist/cli/bin.cjs CHANGED
@@ -398,32 +398,33 @@ var init_browser_brands = __esm({
398
398
  }
399
399
  },
400
400
  {
401
- id: "chromium",
402
- displayName: "Chromium",
401
+ id: "edge",
402
+ displayName: "Microsoft Edge",
403
403
  darwin: {
404
- executableCandidates: ["/Applications/Chromium.app/Contents/MacOS/Chromium"],
405
- userDataDir: "~/Library/Application Support/Chromium",
406
- bundleId: "org.chromium.Chromium",
407
- processNames: ["/Applications/Chromium.app/Contents/MacOS/Chromium"]
404
+ executableCandidates: ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"],
405
+ userDataDir: "~/Library/Application Support/Microsoft Edge",
406
+ bundleId: "com.microsoft.edgemac",
407
+ processNames: ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"]
408
408
  },
409
409
  win32: {
410
410
  executableCandidates: [
411
- path10.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
412
- path10.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
413
- path10.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
411
+ path10.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
412
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
413
+ path10.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
414
414
  ],
415
- userDataDir: "~/AppData/Local/Chromium/User Data",
416
- processNames: ["/chromium/application/chrome.exe"]
415
+ userDataDir: "~/AppData/Local/Microsoft/Edge/User Data",
416
+ processNames: ["/microsoft/edge/application/msedge.exe"]
417
417
  },
418
418
  linux: {
419
419
  executableCandidates: [
420
- "/usr/bin/chromium",
421
- "/usr/bin/chromium-browser",
422
- resolveBinaryFromPath("chromium"),
423
- resolveBinaryFromPath("chromium-browser")
420
+ "/usr/bin/microsoft-edge",
421
+ "/usr/bin/microsoft-edge-stable",
422
+ "/opt/microsoft/msedge/msedge",
423
+ resolveBinaryFromPath("microsoft-edge"),
424
+ resolveBinaryFromPath("microsoft-edge-stable")
424
425
  ],
425
- userDataDir: "~/.config/chromium",
426
- processNames: ["/chromium", "/chromium-browser"]
426
+ userDataDir: "~/.config/microsoft-edge",
427
+ processNames: ["/microsoft-edge", "/microsoft-edge-stable", "/opt/microsoft/msedge/msedge"]
427
428
  }
428
429
  },
429
430
  {
@@ -460,36 +461,6 @@ var init_browser_brands = __esm({
460
461
  processNames: ["/brave-browser", "/opt/brave.com/brave/brave-browser"]
461
462
  }
462
463
  },
463
- {
464
- id: "edge",
465
- displayName: "Microsoft Edge",
466
- darwin: {
467
- executableCandidates: ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"],
468
- userDataDir: "~/Library/Application Support/Microsoft Edge",
469
- bundleId: "com.microsoft.edgemac",
470
- processNames: ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"]
471
- },
472
- win32: {
473
- executableCandidates: [
474
- path10.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
475
- path10.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
476
- path10.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
477
- ],
478
- userDataDir: "~/AppData/Local/Microsoft/Edge/User Data",
479
- processNames: ["/microsoft/edge/application/msedge.exe"]
480
- },
481
- linux: {
482
- executableCandidates: [
483
- "/usr/bin/microsoft-edge",
484
- "/usr/bin/microsoft-edge-stable",
485
- "/opt/microsoft/msedge/msedge",
486
- resolveBinaryFromPath("microsoft-edge"),
487
- resolveBinaryFromPath("microsoft-edge-stable")
488
- ],
489
- userDataDir: "~/.config/microsoft-edge",
490
- processNames: ["/microsoft-edge", "/microsoft-edge-stable", "/opt/microsoft/msedge/msedge"]
491
- }
492
- },
493
464
  {
494
465
  id: "vivaldi",
495
466
  displayName: "Vivaldi",
@@ -528,6 +499,35 @@ var init_browser_brands = __esm({
528
499
  userDataDir: "~/Library/Application Support/net.imput.helium",
529
500
  processNames: ["/Applications/Helium.app/Contents/MacOS/Helium"]
530
501
  }
502
+ },
503
+ {
504
+ id: "chromium",
505
+ displayName: "Chromium",
506
+ darwin: {
507
+ executableCandidates: ["/Applications/Chromium.app/Contents/MacOS/Chromium"],
508
+ userDataDir: "~/Library/Application Support/Chromium",
509
+ bundleId: "org.chromium.Chromium",
510
+ processNames: ["/Applications/Chromium.app/Contents/MacOS/Chromium"]
511
+ },
512
+ win32: {
513
+ executableCandidates: [
514
+ path10.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
515
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
516
+ path10.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
517
+ ],
518
+ userDataDir: "~/AppData/Local/Chromium/User Data",
519
+ processNames: ["/chromium/application/chrome.exe"]
520
+ },
521
+ linux: {
522
+ executableCandidates: [
523
+ "/usr/bin/chromium",
524
+ "/usr/bin/chromium-browser",
525
+ resolveBinaryFromPath("chromium"),
526
+ resolveBinaryFromPath("chromium-browser")
527
+ ],
528
+ userDataDir: "~/.config/chromium",
529
+ processNames: ["/chromium", "/chromium-browser"]
530
+ }
531
531
  }
532
532
  ];
533
533
  }
@@ -546,10 +546,15 @@ function resolveChromeExecutablePath(executablePath) {
546
546
  }
547
547
  return resolvedPath;
548
548
  }
549
- for (const installation of detectLocalChromeInstallations()) {
550
- if (installation.executablePath) {
551
- return installation.executablePath;
552
- }
549
+ const chromeInstallation = detectLocalChromeInstallations().find(
550
+ (installation) => installation.brand === "chrome" && installation.executablePath !== null
551
+ );
552
+ if (chromeInstallation?.executablePath) {
553
+ return chromeInstallation.executablePath;
554
+ }
555
+ const brandedInstallation = detectInstalledBrowserBrands()[0];
556
+ if (brandedInstallation) {
557
+ return brandedInstallation.executablePath;
553
558
  }
554
559
  throw new Error(
555
560
  "Could not find a Chrome or Chromium executable. Pass browser.executablePath or --executable-path."
@@ -945,10 +950,11 @@ async function copyRootLevelEntries(input) {
945
950
  if (!entryStat.isDirectory()) {
946
951
  continue;
947
952
  }
948
- if (SKIPPED_ROOT_DIRECTORIES.has(entry) || isProfileDirectory(input.sourceUserDataDir, entry)) {
953
+ if (SKIPPED_ROOT_DIRECTORIES.has(entry)) {
949
954
  continue;
950
955
  }
951
- if (input.copyMode === "session") {
956
+ const profileDirectory = isProfileDirectory(input.sourceUserDataDir, entry);
957
+ if (input.copyMode === "session" && !profileDirectory) {
952
958
  continue;
953
959
  }
954
960
  await promises.cp(sourcePath, targetPath, {
@@ -6673,7 +6679,7 @@ function assertValidSemanticOperationInput(name, input) {
6673
6679
  }
6674
6680
  );
6675
6681
  }
6676
- var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema, opensteerResolvedTargetSchema, opensteerActionResultSchema, opensteerSnapshotCounterSchema, opensteerSessionStateSchema, opensteerOpenInputSchema, opensteerPageListInputSchema, opensteerPageListOutputSchema, opensteerPageNewInputSchema, opensteerPageActivateInputSchema, opensteerPageCloseInputSchema, opensteerPageCloseOutputSchema, opensteerPageGotoInputSchema, opensteerPageEvaluateInputSchema, opensteerPageEvaluateOutputSchema, opensteerAddInitScriptInputSchema, opensteerAddInitScriptOutputSchema, opensteerCapturedScriptSchema, opensteerCaptureScriptsInputSchema, opensteerCaptureScriptsOutputSchema, opensteerPageSnapshotInputSchema, opensteerPageSnapshotOutputSchema, opensteerComputerMouseButtonSchema, opensteerComputerKeyModifierSchema, opensteerDomClickInputSchema, opensteerDomHoverInputSchema, opensteerDomInputInputSchema, opensteerDomScrollInputSchema, opensteerExtractSchemaSchema, opensteerDomExtractInputSchema, jsonValueSchema2, opensteerDomExtractOutputSchema, opensteerSessionCloseInputSchema, opensteerSessionCloseOutputSchema, opensteerComputerAnnotationSchema, opensteerComputerClickActionSchema, opensteerComputerMoveActionSchema, opensteerComputerScrollActionSchema, opensteerComputerTypeActionSchema, opensteerComputerKeyActionSchema, opensteerComputerDragActionSchema, opensteerComputerScreenshotActionSchema, opensteerComputerWaitActionSchema, opensteerComputerActionSchema, opensteerComputerScreenshotOptionsSchema, opensteerComputerExecuteInputSchema, opensteerComputerTracePointSchema, opensteerComputerTraceEnrichmentSchema, opensteerComputerExecuteTimingSchema, opensteerComputerDisplayScaleSchema, opensteerComputerExecuteOutputSchema, opensteerSemanticOperationSpecificationsBase, exposedSemanticOperationNameSet, opensteerSemanticOperationSpecificationsInternal, opensteerSemanticOperationSpecifications, opensteerSemanticOperationSpecificationMap, semanticRestBasePath, opensteerSemanticRestEndpoints;
6682
+ var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema, opensteerResolvedTargetSchema, opensteerActionResultSchema, opensteerSnapshotCounterSchema, opensteerSessionStateSchema, opensteerOpenInputSchema, opensteerPageListInputSchema, opensteerPageListOutputSchema, opensteerPageNewInputSchema, opensteerPageActivateInputSchema, opensteerPageCloseInputSchema, opensteerPageCloseOutputSchema, opensteerPageGotoInputSchema, opensteerPageEvaluateInputSchema, opensteerPageEvaluateOutputSchema, opensteerAddInitScriptInputSchema, opensteerAddInitScriptOutputSchema, opensteerCapturedScriptSchema, opensteerCaptureScriptsInputSchema, opensteerCaptureScriptsOutputSchema, opensteerPageSnapshotInputSchema, opensteerPageSnapshotOutputSchema, opensteerComputerMouseButtonSchema, opensteerComputerKeyModifierSchema, opensteerDomClickInputSchema, opensteerDomHoverInputSchema, opensteerDomInputInputSchema, opensteerDomScrollInputSchema, opensteerExtractTemplateSchema, opensteerDomExtractInputSchema, jsonValueSchema2, opensteerDomExtractOutputSchema, opensteerSessionCloseInputSchema, opensteerSessionCloseOutputSchema, opensteerComputerAnnotationSchema, opensteerComputerClickActionSchema, opensteerComputerMoveActionSchema, opensteerComputerScrollActionSchema, opensteerComputerTypeActionSchema, opensteerComputerKeyActionSchema, opensteerComputerDragActionSchema, opensteerComputerScreenshotActionSchema, opensteerComputerWaitActionSchema, opensteerComputerActionSchema, opensteerComputerScreenshotOptionsSchema, opensteerComputerExecuteInputSchema, opensteerComputerTracePointSchema, opensteerComputerTraceEnrichmentSchema, opensteerComputerExecuteTimingSchema, opensteerComputerDisplayScaleSchema, opensteerComputerExecuteOutputSchema, opensteerSemanticOperationSpecificationsBase, exposedSemanticOperationNameSet, opensteerSemanticOperationSpecificationsInternal, opensteerSemanticOperationSpecifications, opensteerSemanticOperationSpecificationMap, semanticRestBasePath, opensteerSemanticRestEndpoints;
6677
6683
  var init_semantic = __esm({
6678
6684
  "../protocol/src/semantic.ts"() {
6679
6685
  init_json2();
@@ -6849,6 +6855,20 @@ var init_semantic = __esm({
6849
6855
  {
6850
6856
  title: "OpensteerStealthProfileInput"
6851
6857
  }
6858
+ ),
6859
+ humanize: oneOfSchema(
6860
+ [
6861
+ { type: "boolean" },
6862
+ objectSchema(
6863
+ {
6864
+ mouse: { type: "boolean" },
6865
+ keyboard: { type: "boolean" },
6866
+ scroll: { type: "boolean" }
6867
+ },
6868
+ { title: "OpensteerHumanizeOptions" }
6869
+ )
6870
+ ],
6871
+ { title: "OpensteerHumanize" }
6852
6872
  )
6853
6873
  },
6854
6874
  {
@@ -7241,10 +7261,10 @@ var init_semantic = __esm({
7241
7261
  required: ["target", "direction", "amount"]
7242
7262
  }
7243
7263
  );
7244
- opensteerExtractSchemaSchema = objectSchema(
7264
+ opensteerExtractTemplateSchema = objectSchema(
7245
7265
  {},
7246
7266
  {
7247
- title: "OpensteerExtractSchema",
7267
+ title: "OpensteerExtractTemplate",
7248
7268
  additionalProperties: true
7249
7269
  }
7250
7270
  );
@@ -7252,13 +7272,13 @@ var init_semantic = __esm({
7252
7272
  ...objectSchema(
7253
7273
  {
7254
7274
  persist: stringSchema(),
7255
- schema: opensteerExtractSchemaSchema
7275
+ template: opensteerExtractTemplateSchema
7256
7276
  },
7257
7277
  {
7258
7278
  title: "OpensteerDomExtractInput"
7259
7279
  }
7260
7280
  ),
7261
- anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["schema"] })]
7281
+ anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["template"] })]
7262
7282
  });
7263
7283
  jsonValueSchema2 = recordSchema({}, { title: "JsonValueRecord" });
7264
7284
  opensteerDomExtractOutputSchema = objectSchema(
@@ -10483,6 +10503,26 @@ function normalizeWorkspace(workspace) {
10483
10503
  const normalized = workspace?.trim();
10484
10504
  return normalized === void 0 || normalized.length === 0 ? void 0 : normalized;
10485
10505
  }
10506
+ function resolveLaunchOptions(launch, environment) {
10507
+ if (launch?.executablePath !== void 0) {
10508
+ return launch;
10509
+ }
10510
+ const executablePath = normalizeConfiguredExecutablePath(environment.OPENSTEER_EXECUTABLE_PATH);
10511
+ if (executablePath === void 0) {
10512
+ return launch;
10513
+ }
10514
+ return {
10515
+ ...launch ?? {},
10516
+ executablePath
10517
+ };
10518
+ }
10519
+ function normalizeConfiguredExecutablePath(value) {
10520
+ if (value === void 0) {
10521
+ return void 0;
10522
+ }
10523
+ const trimmed = value.trim();
10524
+ return trimmed.length === 0 ? void 0 : trimmed;
10525
+ }
10486
10526
  function toPersistedLocalBrowserSessionRecord(workspace, live) {
10487
10527
  return {
10488
10528
  layout: "opensteer-session",
@@ -10578,7 +10618,7 @@ async function launchOwnedBrowser(input) {
10578
10618
  }
10579
10619
  function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebuggingPort) {
10580
10620
  const isHeadless = launch?.headless ?? true;
10581
- const args = [
10621
+ const args = isHeadless ? [
10582
10622
  ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
10583
10623
  "--no-first-run",
10584
10624
  "--no-default-browser-check",
@@ -10597,6 +10637,12 @@ function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebugging
10597
10637
  "--password-store=basic",
10598
10638
  "--use-mock-keychain",
10599
10639
  `--user-data-dir=${userDataDir}`
10640
+ ] : [
10641
+ ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
10642
+ "--no-first-run",
10643
+ "--no-default-browser-check",
10644
+ "--disable-blink-features=AutomationControlled",
10645
+ `--user-data-dir=${userDataDir}`
10600
10646
  ];
10601
10647
  if (isHeadless) {
10602
10648
  args.push("--headless=new");
@@ -10820,23 +10866,36 @@ function isMissingPackageError(error, packageName) {
10820
10866
  }
10821
10867
  return error.message.includes(`Cannot find package '${packageName}'`) || error.message.includes(`Cannot find module '${packageName}'`) || error.message.includes(`Cannot find module "${packageName}"`);
10822
10868
  }
10823
- function normalizeBrowserContextOptions(context) {
10869
+ function normalizeBrowserContextOptions(context, environment, engineName = DEFAULT_OPENSTEER_ENGINE) {
10824
10870
  const stealthProfile = resolveStealthProfile(context?.stealthProfile);
10825
10871
  const locale = context?.locale ?? stealthProfile?.locale;
10826
10872
  const timezoneId = context?.timezoneId ?? stealthProfile?.timezoneId;
10827
10873
  const userAgent = context?.userAgent ?? stealthProfile?.userAgent;
10874
+ const humanize = engineName === "abp" && context?.humanize === void 0 ? void 0 : resolveHumanizeOption(context?.humanize, environment);
10828
10875
  return {
10829
10876
  ...context ?? {},
10830
10877
  ...stealthProfile === void 0 ? {} : { stealthProfile },
10831
10878
  ...locale === void 0 ? {} : { locale },
10832
10879
  ...timezoneId === void 0 ? {} : { timezoneId },
10833
10880
  ...userAgent === void 0 ? {} : { userAgent },
10881
+ ...humanize === void 0 ? {} : { humanize },
10834
10882
  viewport: context?.viewport ?? stealthProfile?.viewport ?? {
10835
10883
  width: 1440,
10836
10884
  height: 900
10837
10885
  }
10838
10886
  };
10839
10887
  }
10888
+ function resolveHumanizeOption(explicit, environment) {
10889
+ if (explicit !== void 0) {
10890
+ return explicit;
10891
+ }
10892
+ const envValue = environment?.OPENSTEER_HUMANIZE;
10893
+ if (envValue !== void 0) {
10894
+ const normalized = envValue.trim().toLowerCase();
10895
+ return normalized !== "false" && normalized !== "0";
10896
+ }
10897
+ return void 0;
10898
+ }
10840
10899
  function toEngineBrowserContextOptions(context) {
10841
10900
  const { stealthProfile: _stealthProfile, ...engineContext } = context;
10842
10901
  return engineContext;
@@ -10890,9 +10949,13 @@ var init_browser_manager = __esm({
10890
10949
  this.workspace = normalizeWorkspace(options.workspace);
10891
10950
  this.mode = resolveBrowserMode(this.workspace, options.browser);
10892
10951
  this.browserOptions = isAttachBrowserOptions(options.browser) ? options.browser : void 0;
10893
- this.launchOptions = options.launch;
10894
- this.contextOptions = normalizeBrowserContextOptions(options.context);
10952
+ this.launchOptions = resolveLaunchOptions(options.launch, options.environment ?? process.env);
10895
10953
  this.engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
10954
+ this.contextOptions = normalizeBrowserContextOptions(
10955
+ options.context,
10956
+ options.environment ?? process.env,
10957
+ this.engineName
10958
+ );
10896
10959
  assertSupportedEngineOptions({
10897
10960
  engineName: this.engineName,
10898
10961
  ...options.browser === void 0 ? {} : { browser: options.browser },
@@ -12157,7 +12220,7 @@ var init_package = __esm({
12157
12220
  "../runtime-core/package.json"() {
12158
12221
  package_default2 = {
12159
12222
  name: "@opensteer/runtime-core",
12160
- version: "0.2.3",
12223
+ version: "0.2.4",
12161
12224
  description: "Shared semantic runtime for Opensteer local and cloud execution.",
12162
12225
  license: "MIT",
12163
12226
  type: "module",
@@ -12405,6 +12468,7 @@ var init_defaults = __esm({
12405
12468
  return false;
12406
12469
  }
12407
12470
  try {
12471
+ const startedAt = Date.now();
12408
12472
  await input.engine.waitForPostLoadQuiet({
12409
12473
  pageRef: input.pageRef,
12410
12474
  timeoutMs: effectiveTimeout,
@@ -12412,9 +12476,13 @@ var init_defaults = __esm({
12412
12476
  captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
12413
12477
  signal: input.signal
12414
12478
  });
12479
+ const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
12480
+ if (visualTimeout <= 0) {
12481
+ return true;
12482
+ }
12415
12483
  await input.engine.waitForVisualStability({
12416
12484
  pageRef: input.pageRef,
12417
- timeoutMs: effectiveTimeout,
12485
+ timeoutMs: visualTimeout,
12418
12486
  settleMs: profile.settleMs,
12419
12487
  scope: profile.scope
12420
12488
  });
@@ -17746,9 +17814,9 @@ var init_extraction_consolidation = __esm({
17746
17814
  CLUSTER_FALLBACK_PREFIX = "variant";
17747
17815
  }
17748
17816
  });
17749
- function assertValidOpensteerExtractionSchemaRoot(schema) {
17750
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
17751
- throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
17817
+ function assertValidOpensteerExtractionTemplateRoot(template) {
17818
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
17819
+ throw new Error("Invalid extraction template: expected a JSON object at the top level.");
17752
17820
  }
17753
17821
  }
17754
17822
  function isPersistedOpensteerExtractionValueNode2(value) {
@@ -17770,12 +17838,12 @@ function isPersistedOpensteerExtractionArrayNode2(value) {
17770
17838
  return "$array" in value;
17771
17839
  }
17772
17840
  async function compileOpensteerExtractionFieldTargets(options) {
17773
- assertValidOpensteerExtractionSchemaRoot(options.schema);
17841
+ assertValidOpensteerExtractionTemplateRoot(options.template);
17774
17842
  const fields = [];
17775
- await collectFieldTargetsFromSchemaObject({
17843
+ await collectFieldTargetsFromTemplateObject({
17776
17844
  dom: options.dom,
17777
17845
  pageRef: options.pageRef,
17778
- value: options.schema,
17846
+ value: options.template,
17779
17847
  path: "",
17780
17848
  fields,
17781
17849
  insideArray: false
@@ -17827,13 +17895,13 @@ function createOpensteerExtractionDescriptorStore(options) {
17827
17895
  }
17828
17896
  return new MemoryOpensteerExtractionDescriptorStore(namespace);
17829
17897
  }
17830
- async function collectFieldTargetsFromSchemaObject(options) {
17898
+ async function collectFieldTargetsFromTemplateObject(options) {
17831
17899
  for (const [key, childValue] of Object.entries(options.value)) {
17832
17900
  const normalizedKey = normalizeKey(key);
17833
17901
  if (!normalizedKey) {
17834
17902
  continue;
17835
17903
  }
17836
- await collectFieldTargetsFromSchemaValue({
17904
+ await collectFieldTargetsFromTemplateValue({
17837
17905
  dom: options.dom,
17838
17906
  pageRef: options.pageRef,
17839
17907
  value: childValue,
@@ -17843,8 +17911,8 @@ async function collectFieldTargetsFromSchemaObject(options) {
17843
17911
  });
17844
17912
  }
17845
17913
  }
17846
- async function collectFieldTargetsFromSchemaValue(options) {
17847
- const normalizedField = normalizeSchemaField(options.value);
17914
+ async function collectFieldTargetsFromTemplateValue(options) {
17915
+ const normalizedField = normalizeTemplateField(options.value);
17848
17916
  if (normalizedField !== null) {
17849
17917
  options.fields.push(
17850
17918
  await compileFieldTarget({
@@ -17859,12 +17927,12 @@ async function collectFieldTargetsFromSchemaValue(options) {
17859
17927
  if (Array.isArray(options.value)) {
17860
17928
  if (options.insideArray) {
17861
17929
  throw new Error(
17862
- `Nested arrays are not supported in extraction schema at "${labelForPath(options.path)}".`
17930
+ `Nested arrays are not supported in extraction template at "${labelForPath(options.path)}".`
17863
17931
  );
17864
17932
  }
17865
17933
  if (options.value.length === 0) {
17866
17934
  throw new Error(
17867
- `Extraction array "${labelForPath(options.path)}" must include at least one representative item.`
17935
+ `Extraction array "${labelForPath(options.path)}" must include at least one representative template item.`
17868
17936
  );
17869
17937
  }
17870
17938
  for (let index = 0; index < options.value.length; index += 1) {
@@ -17875,7 +17943,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
17875
17943
  );
17876
17944
  }
17877
17945
  const fieldCountBeforeItem = options.fields.length;
17878
- await collectFieldTargetsFromSchemaObject({
17946
+ await collectFieldTargetsFromTemplateObject({
17879
17947
  dom: options.dom,
17880
17948
  pageRef: options.pageRef,
17881
17949
  value: itemValue,
@@ -17886,7 +17954,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
17886
17954
  const itemFields = options.fields.slice(fieldCountBeforeItem);
17887
17955
  if (!itemFields.some((field) => !("source" in field))) {
17888
17956
  throw new Error(
17889
- `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element- or selector-backed field.`
17957
+ `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element number or selector field.`
17890
17958
  );
17891
17959
  }
17892
17960
  }
@@ -17894,10 +17962,10 @@ async function collectFieldTargetsFromSchemaValue(options) {
17894
17962
  }
17895
17963
  if (!options.value || typeof options.value !== "object") {
17896
17964
  throw new Error(
17897
- `Invalid extraction schema value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
17965
+ `Invalid extraction template value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
17898
17966
  );
17899
17967
  }
17900
- await collectFieldTargetsFromSchemaObject({
17968
+ await collectFieldTargetsFromTemplateObject({
17901
17969
  dom: options.dom,
17902
17970
  pageRef: options.pageRef,
17903
17971
  value: options.value,
@@ -17929,7 +17997,7 @@ async function compileFieldTarget(options) {
17929
17997
  path: await resolveSelectorFieldPath({
17930
17998
  dom: options.dom,
17931
17999
  pageRef: options.pageRef,
17932
- selector: `[c="${String(options.field.element)}"]`
18000
+ selector: `[c="${String(options.field.c)}"]`
17933
18001
  }),
17934
18002
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
17935
18003
  };
@@ -18230,24 +18298,29 @@ function countNonNullLeaves(value) {
18230
18298
  }
18231
18299
  return Object.values(value).reduce((sum, item) => sum + countNonNullLeaves(item), 0);
18232
18300
  }
18233
- function normalizeSchemaField(value) {
18301
+ function normalizeTemplateField(value) {
18302
+ if (typeof value === "number") {
18303
+ return {
18304
+ c: normalizeExtractionCounter(value)
18305
+ };
18306
+ }
18234
18307
  if (!value || typeof value !== "object" || Array.isArray(value)) {
18235
18308
  return null;
18236
18309
  }
18237
18310
  const raw = value;
18238
- const hasElement = raw.element !== void 0;
18311
+ const hasCounter = raw.c !== void 0 || raw.element !== void 0;
18239
18312
  const hasSelector = raw.selector !== void 0;
18240
18313
  const hasSource = raw.source !== void 0;
18241
- const targetCount = Number(hasElement) + Number(hasSelector) + Number(hasSource);
18314
+ const targetCount = Number(hasCounter) + Number(hasSelector) + Number(hasSource);
18242
18315
  if (targetCount === 0) {
18243
18316
  return null;
18244
18317
  }
18245
18318
  if (targetCount !== 1) {
18246
18319
  throw new Error(
18247
- "Extraction field descriptors must specify exactly one of element, selector, or source."
18320
+ "Extraction field descriptors must specify exactly one of c/element, selector, or source."
18248
18321
  );
18249
18322
  }
18250
- const attribute = raw.attribute === void 0 ? void 0 : normalizeNonEmptyString2("attribute", raw.attribute);
18323
+ const attribute = raw.attr !== void 0 ? normalizeNonEmptyString2("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString2("attribute", raw.attribute);
18251
18324
  if (hasSource) {
18252
18325
  if (raw.source !== "current_url") {
18253
18326
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -18262,17 +18335,20 @@ function normalizeSchemaField(value) {
18262
18335
  ...attribute === void 0 ? {} : { attribute }
18263
18336
  };
18264
18337
  }
18265
- const element = Number(raw.element);
18266
- if (!Number.isInteger(element) || element < 1) {
18267
- throw new Error(
18268
- `Extraction field element must be a positive integer, received ${String(raw.element)}.`
18269
- );
18270
- }
18271
18338
  return {
18272
- element,
18339
+ c: normalizeExtractionCounter(raw.c ?? raw.element),
18273
18340
  ...attribute === void 0 ? {} : { attribute }
18274
18341
  };
18275
18342
  }
18343
+ function normalizeExtractionCounter(value) {
18344
+ const counter = Number(value);
18345
+ if (!Number.isInteger(counter) || counter < 1) {
18346
+ throw new Error(
18347
+ `Extraction element number must be a positive integer, received ${String(value)}.`
18348
+ );
18349
+ }
18350
+ return counter;
18351
+ }
18276
18352
  function normalizeNamespace(namespace) {
18277
18353
  const normalized = String(namespace ?? "default").trim();
18278
18354
  return normalized.length === 0 ? "default" : normalized;
@@ -18303,7 +18379,7 @@ function parseExtractionDescriptorRecord(record) {
18303
18379
  kind: "dom-extraction",
18304
18380
  persist: raw.persist,
18305
18381
  root,
18306
- ...typeof raw.schemaHash === "string" ? { schemaHash: raw.schemaHash } : {},
18382
+ ...typeof raw.templateHash === "string" ? { templateHash: raw.templateHash } : typeof raw.schemaHash === "string" ? { templateHash: raw.schemaHash } : {},
18307
18383
  ...typeof raw.sourceUrl === "string" ? { sourceUrl: raw.sourceUrl } : {}
18308
18384
  }
18309
18385
  };
@@ -18412,7 +18488,7 @@ var init_extraction2 = __esm({
18412
18488
  kind: "dom-extraction",
18413
18489
  persist: input.persist,
18414
18490
  root: input.root,
18415
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
18491
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
18416
18492
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
18417
18493
  };
18418
18494
  const key = persistKey(this.namespace, input.persist);
@@ -18461,7 +18537,7 @@ var init_extraction2 = __esm({
18461
18537
  kind: "dom-extraction",
18462
18538
  persist: input.persist,
18463
18539
  root: input.root,
18464
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
18540
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
18465
18541
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
18466
18542
  };
18467
18543
  const key = persistKey(this.namespace, input.persist);
@@ -18638,19 +18714,260 @@ function truncateValue(value, max) {
18638
18714
  }
18639
18715
  return `${head}${TRUNCATION_SUFFIX}`;
18640
18716
  }
18717
+ function takeValueWithinSerializedLengthFromEnd(value, max) {
18718
+ let serializedLength = 0;
18719
+ const chars = [];
18720
+ for (let index = value.length - 1; index >= 0; index -= 1) {
18721
+ const char = value[index];
18722
+ let nextLength = 1;
18723
+ if (char === "&") {
18724
+ nextLength = 5;
18725
+ } else if (char === "<" || char === ">") {
18726
+ nextLength = 4;
18727
+ } else if (char === '"') {
18728
+ nextLength = 6;
18729
+ }
18730
+ if (serializedLength + nextLength > max) {
18731
+ break;
18732
+ }
18733
+ chars.push(char);
18734
+ serializedLength += nextLength;
18735
+ }
18736
+ return chars.reverse().join("");
18737
+ }
18738
+ function truncateValueInMiddle(value, headMax, tailMax, marker = MIDDLE_TRUNCATION_MARKER) {
18739
+ const markerLength = getSerializedLength(marker);
18740
+ const max = headMax + markerLength + tailMax;
18741
+ if (getSerializedLength(value) <= max) {
18742
+ return value;
18743
+ }
18744
+ const head = takeValueWithinSerializedLength(value, headMax).replace(/\s+$/u, "");
18745
+ const tail = takeValueWithinSerializedLengthFromEnd(value, tailMax).replace(/^\s+/u, "");
18746
+ if (head.length === 0) {
18747
+ return tail.length === 0 ? marker : `${marker}${tail}`;
18748
+ }
18749
+ if (tail.length === 0) {
18750
+ return `${head}${marker}`;
18751
+ }
18752
+ return `${head}${marker}${tail}`;
18753
+ }
18641
18754
  function getAttrLimit(attr) {
18642
- if (URL_ATTRS.has(attr)) {
18643
- return URL_ATTR_MAX;
18755
+ if (attr === "srcset") {
18756
+ return SRCSET_ATTR_MAX;
18644
18757
  }
18645
18758
  if (TEXT_ATTRS.has(attr)) {
18646
18759
  return TEXT_ATTR_MAX;
18647
18760
  }
18648
18761
  return void 0;
18649
18762
  }
18763
+ function shouldBoundAttr(attr) {
18764
+ return MIDDLE_TRUNCATED_URL_ATTRS.has(attr) || getAttrLimit(attr) !== void 0;
18765
+ }
18650
18766
  function setBoundedAttr(el, attr, value) {
18767
+ if (MIDDLE_TRUNCATED_URL_ATTRS.has(attr)) {
18768
+ el.attr(
18769
+ attr,
18770
+ truncateValueInMiddle(value, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX)
18771
+ );
18772
+ return;
18773
+ }
18651
18774
  const limit = getAttrLimit(attr);
18775
+ if (attr === "srcset" && limit !== void 0) {
18776
+ el.attr(attr, truncateSrcsetValue(value, limit));
18777
+ return;
18778
+ }
18652
18779
  el.attr(attr, limit === void 0 ? value : truncateValue(value, limit));
18653
18780
  }
18781
+ function truncateSrcsetValue(value, max) {
18782
+ if (getSerializedLength(value) <= max) {
18783
+ return value;
18784
+ }
18785
+ const candidates = parseSrcsetCandidates2(value);
18786
+ if (candidates.length === 0) {
18787
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
18788
+ }
18789
+ for (const [headMax, tailMax, includeBest] of [
18790
+ [SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, true],
18791
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, true],
18792
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, false]
18793
+ ]) {
18794
+ const compact = buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest);
18795
+ if (getSerializedLength(compact) <= max) {
18796
+ return compact;
18797
+ }
18798
+ }
18799
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
18800
+ }
18801
+ function buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest) {
18802
+ const kept = getPreferredSrcsetCandidateIndices(candidates, includeBest);
18803
+ const parts = [];
18804
+ let previousIndex;
18805
+ for (const candidateIndex of kept) {
18806
+ if (previousIndex !== void 0 && candidateIndex - previousIndex > 1) {
18807
+ parts.push(MIDDLE_TRUNCATION_MARKER);
18808
+ }
18809
+ parts.push(formatSrcsetCandidate(candidates[candidateIndex], headMax, tailMax));
18810
+ previousIndex = candidateIndex;
18811
+ }
18812
+ return parts.join(", ");
18813
+ }
18814
+ function getPreferredSrcsetCandidateIndices(candidates, includeBest) {
18815
+ if (candidates.length === 0) {
18816
+ return [];
18817
+ }
18818
+ const kept = /* @__PURE__ */ new Set([0, candidates.length - 1]);
18819
+ if (includeBest) {
18820
+ kept.add(pickBestSrcsetCandidateIndex(candidates));
18821
+ }
18822
+ return [...kept].filter((index) => index >= 0 && index < candidates.length).sort((a, b) => a - b);
18823
+ }
18824
+ function pickBestSrcsetCandidateIndex(candidates) {
18825
+ let bestWidthIndex = -1;
18826
+ let bestWidth = -1;
18827
+ let bestDensityIndex = -1;
18828
+ let bestDensity = -1;
18829
+ for (let index = 0; index < candidates.length; index += 1) {
18830
+ const candidate = candidates[index];
18831
+ if (typeof candidate.width === "number" && Number.isFinite(candidate.width) && candidate.width > bestWidth) {
18832
+ bestWidth = candidate.width;
18833
+ bestWidthIndex = index;
18834
+ }
18835
+ if (typeof candidate.density === "number" && Number.isFinite(candidate.density) && candidate.density > bestDensity) {
18836
+ bestDensity = candidate.density;
18837
+ bestDensityIndex = index;
18838
+ }
18839
+ }
18840
+ if (bestWidthIndex >= 0) {
18841
+ return bestWidthIndex;
18842
+ }
18843
+ if (bestDensityIndex >= 0) {
18844
+ return bestDensityIndex;
18845
+ }
18846
+ return candidates.length - 1;
18847
+ }
18848
+ function formatSrcsetCandidate(candidate, headMax, tailMax) {
18849
+ const url = truncateValueInMiddle(candidate.url, headMax, tailMax);
18850
+ return candidate.descriptorText ? `${url} ${candidate.descriptorText}` : url;
18851
+ }
18852
+ function parseSrcsetCandidates2(raw) {
18853
+ const text = raw.trim();
18854
+ if (!text) {
18855
+ return [];
18856
+ }
18857
+ const out = [];
18858
+ let index = 0;
18859
+ while (index < text.length) {
18860
+ index = skipSrcsetSeparators(text, index);
18861
+ if (index >= text.length) {
18862
+ break;
18863
+ }
18864
+ const urlToken = readSrcsetUrlToken(text, index);
18865
+ index = urlToken.nextIndex;
18866
+ const url = urlToken.value.trim();
18867
+ if (!url) {
18868
+ continue;
18869
+ }
18870
+ index = skipSrcsetWhitespace(text, index);
18871
+ const descriptors = [];
18872
+ while (index < text.length && text[index] !== ",") {
18873
+ const descriptorToken = readSrcsetDescriptorToken(text, index);
18874
+ if (!descriptorToken.value) {
18875
+ index = descriptorToken.nextIndex;
18876
+ continue;
18877
+ }
18878
+ descriptors.push(descriptorToken.value);
18879
+ index = descriptorToken.nextIndex;
18880
+ index = skipSrcsetWhitespace(text, index);
18881
+ }
18882
+ if (index < text.length && text[index] === ",") {
18883
+ index += 1;
18884
+ }
18885
+ let width = null;
18886
+ let density = null;
18887
+ for (const descriptor of descriptors) {
18888
+ const token = descriptor.trim().toLowerCase();
18889
+ if (!token) {
18890
+ continue;
18891
+ }
18892
+ const widthMatch = token.match(/^(\d+)w$/);
18893
+ if (widthMatch) {
18894
+ const parsed = Number.parseInt(widthMatch[1], 10);
18895
+ if (Number.isFinite(parsed)) {
18896
+ width = parsed;
18897
+ }
18898
+ continue;
18899
+ }
18900
+ const densityMatch = token.match(/^(\d*\.?\d+)x$/);
18901
+ if (densityMatch) {
18902
+ const parsed = Number.parseFloat(densityMatch[1]);
18903
+ if (Number.isFinite(parsed)) {
18904
+ density = parsed;
18905
+ }
18906
+ }
18907
+ }
18908
+ out.push({
18909
+ url,
18910
+ descriptorText: descriptors.join(" "),
18911
+ width,
18912
+ density
18913
+ });
18914
+ }
18915
+ return out;
18916
+ }
18917
+ function skipSrcsetWhitespace(value, index) {
18918
+ let cursor = index;
18919
+ while (cursor < value.length && /\s/u.test(value[cursor])) {
18920
+ cursor += 1;
18921
+ }
18922
+ return cursor;
18923
+ }
18924
+ function skipSrcsetSeparators(value, index) {
18925
+ let cursor = skipSrcsetWhitespace(value, index);
18926
+ while (cursor < value.length && value[cursor] === ",") {
18927
+ cursor += 1;
18928
+ cursor = skipSrcsetWhitespace(value, cursor);
18929
+ }
18930
+ return cursor;
18931
+ }
18932
+ function readSrcsetUrlToken(value, index) {
18933
+ let cursor = index;
18934
+ let out = "";
18935
+ const isDataUrl = value.slice(index, index + 5).toLowerCase().startsWith("data:");
18936
+ while (cursor < value.length) {
18937
+ const char = value[cursor];
18938
+ if (/\s/u.test(char)) {
18939
+ break;
18940
+ }
18941
+ if (char === "," && !isDataUrl) {
18942
+ break;
18943
+ }
18944
+ out += char;
18945
+ cursor += 1;
18946
+ }
18947
+ if (isDataUrl && out.endsWith(",") && cursor < value.length) {
18948
+ out = out.slice(0, -1);
18949
+ }
18950
+ return {
18951
+ value: out,
18952
+ nextIndex: cursor
18953
+ };
18954
+ }
18955
+ function readSrcsetDescriptorToken(value, index) {
18956
+ let cursor = skipSrcsetWhitespace(value, index);
18957
+ let out = "";
18958
+ while (cursor < value.length) {
18959
+ const char = value[cursor];
18960
+ if (char === "," || /\s/u.test(char)) {
18961
+ break;
18962
+ }
18963
+ out += char;
18964
+ cursor += 1;
18965
+ }
18966
+ return {
18967
+ value: out.trim(),
18968
+ nextIndex: cursor
18969
+ };
18970
+ }
18654
18971
  function removeNoise($) {
18655
18972
  for (const tag of STRIP_TAGS) {
18656
18973
  $(tag).remove();
@@ -18675,38 +18992,68 @@ function markInlineSelfHiddenFallback($) {
18675
18992
  });
18676
18993
  }
18677
18994
  function pruneSelfHiddenNodes($) {
18678
- const nodes = [];
18679
- $(`[${OPENSTEER_SELF_HIDDEN_ATTR}]`).each(function collectSelfHiddenNodes() {
18680
- nodes.push($(this));
18681
- });
18682
- nodes.sort((left, right) => right.parents().length - left.parents().length);
18683
- for (const el of nodes) {
18684
- if (!el[0]) {
18995
+ for (const node of getElementsInReverseDocumentOrder($)) {
18996
+ if (node.attribs?.[OPENSTEER_SELF_HIDDEN_ATTR] === void 0) {
18685
18997
  continue;
18686
18998
  }
18999
+ const el = $(node);
18687
19000
  el.contents().each(function removeSelfHiddenText() {
18688
19001
  if (this.type === "text") {
18689
19002
  $(this).remove();
18690
19003
  }
18691
19004
  });
18692
- if (el.children().length === 0) {
19005
+ if (!hasElementChildren(node)) {
18693
19006
  el.remove();
18694
19007
  }
18695
19008
  }
18696
19009
  }
18697
- function hasDirectText($, el) {
18698
- return el.contents().filter(function hasDirectNodeText() {
18699
- return this.type === "text" && $(this).text().trim() !== "";
18700
- }).length > 0;
19010
+ function getChildNodes(node) {
19011
+ return node?.children ?? [];
18701
19012
  }
18702
- function hasTextDeep(el) {
18703
- return el.text().trim().length > 0;
19013
+ function isElementLikeNode(node) {
19014
+ return node?.type === "tag" || node?.type === "script" || node?.type === "style";
19015
+ }
19016
+ function hasDirectText(node) {
19017
+ if (!node) {
19018
+ return false;
19019
+ }
19020
+ for (const child of getChildNodes(node)) {
19021
+ if (child.type === "text" && (child.data || "").trim() !== "") {
19022
+ return true;
19023
+ }
19024
+ }
19025
+ return false;
19026
+ }
19027
+ function hasElementChildren(node) {
19028
+ if (!node) {
19029
+ return false;
19030
+ }
19031
+ for (const child of getChildNodes(node)) {
19032
+ if (isElementLikeNode(child)) {
19033
+ return true;
19034
+ }
19035
+ }
19036
+ return false;
19037
+ }
19038
+ function hasTextDeepNode(node) {
19039
+ if (!node) {
19040
+ return false;
19041
+ }
19042
+ if (node.type === "text") {
19043
+ return (node.data || "").trim() !== "";
19044
+ }
19045
+ for (const child of getChildNodes(node)) {
19046
+ if (hasTextDeepNode(child)) {
19047
+ return true;
19048
+ }
19049
+ }
19050
+ return false;
18704
19051
  }
18705
19052
  function hasActionLabel(attrs) {
18706
19053
  return typeof attrs["aria-label"] === "string" && attrs["aria-label"].trim() !== "" || typeof attrs["aria-labelledby"] === "string" && attrs["aria-labelledby"].trim() !== "" || typeof attrs["aria-describedby"] === "string" && attrs["aria-describedby"].trim() !== "" || typeof attrs.title === "string" && attrs.title.trim() !== "" || typeof attrs.placeholder === "string" && attrs.placeholder.trim() !== "" || typeof attrs.value === "string" && attrs.value.trim() !== "";
18707
19054
  }
18708
19055
  function unwrapActionNode($, el) {
18709
- if (hasTextDeep(el)) {
19056
+ if (hasTextDeepNode(el[0])) {
18710
19057
  if (el.prev().length > 0) {
18711
19058
  el.before(" ");
18712
19059
  }
@@ -18727,7 +19074,7 @@ function stripToAttrs(el, keep) {
18727
19074
  if (typeof value !== "string") {
18728
19075
  continue;
18729
19076
  }
18730
- if (getAttrLimit(attr) !== void 0) {
19077
+ if (shouldBoundAttr(attr)) {
18731
19078
  setBoundedAttr(el, attr, value);
18732
19079
  }
18733
19080
  }
@@ -18745,6 +19092,9 @@ function restoreBoundedAttr(el, attr, value) {
18745
19092
  function deduplicateImages(html) {
18746
19093
  const seen = /* @__PURE__ */ new Set();
18747
19094
  return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
19095
+ if (/\bc\s*=/.test(attrContent)) {
19096
+ return full;
19097
+ }
18748
19098
  const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
18749
19099
  const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
18750
19100
  let src = null;
@@ -18763,59 +19113,155 @@ function deduplicateImages(html) {
18763
19113
  return full;
18764
19114
  });
18765
19115
  }
18766
- function isPreservedImageElement($, el) {
18767
- const tag = (el[0]?.tagName || "").toLowerCase();
19116
+ function hasAttribute2(node, attr) {
19117
+ return node?.attribs?.[attr] !== void 0;
19118
+ }
19119
+ function hasPictureAncestor(node) {
19120
+ let current = node?.parent;
19121
+ while (current) {
19122
+ if (isElementLikeNode(current) && (current.tagName || "").toLowerCase() === "picture") {
19123
+ return true;
19124
+ }
19125
+ current = current.parent;
19126
+ }
19127
+ return false;
19128
+ }
19129
+ function pictureHasPreservedDescendant(node) {
19130
+ if (!node) {
19131
+ return false;
19132
+ }
19133
+ for (const child of getChildNodes(node)) {
19134
+ if (!isElementLikeNode(child)) {
19135
+ continue;
19136
+ }
19137
+ const tag = (child.tagName || "").toLowerCase();
19138
+ if (tag === "img") {
19139
+ return true;
19140
+ }
19141
+ if (tag === "source" && typeof child.attribs?.src === "string" && child.attribs.src.trim() !== "") {
19142
+ return true;
19143
+ }
19144
+ if (tag === "source" && typeof child.attribs?.srcset === "string" && child.attribs.srcset.trim() !== "") {
19145
+ return true;
19146
+ }
19147
+ if (pictureHasPreservedDescendant(child)) {
19148
+ return true;
19149
+ }
19150
+ }
19151
+ return false;
19152
+ }
19153
+ function isPreservedImageElement(node) {
19154
+ const tag = (node?.tagName || "").toLowerCase();
18768
19155
  if (tag === "img") {
18769
19156
  return true;
18770
19157
  }
18771
19158
  if (tag === "picture") {
18772
- const hasImg = el.find("img").length > 0;
18773
- const hasSource = el.find("source[src], source[srcset]").length > 0;
18774
- return hasImg || hasSource;
19159
+ return pictureHasPreservedDescendant(node);
18775
19160
  }
18776
19161
  if (tag === "source") {
18777
- const inPicture = el.parents("picture").length > 0;
18778
- const hasSrc = el.attr("src") != null && el.attr("src").trim() !== "" || el.attr("srcset") != null && el.attr("srcset").trim() !== "";
19162
+ const inPicture = hasPictureAncestor(node);
19163
+ const hasSrc = typeof node?.attribs?.src === "string" && node.attribs.src.trim() !== "" || typeof node?.attribs?.srcset === "string" && node.attribs.srcset.trim() !== "";
18779
19164
  return inPicture && hasSrc;
18780
19165
  }
18781
19166
  return false;
18782
19167
  }
19168
+ function getElementsInReverseDocumentOrder($) {
19169
+ return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
19170
+ }
19171
+ function getNodeDepth(node) {
19172
+ let depth = 0;
19173
+ let current = node.parent;
19174
+ while (current) {
19175
+ depth++;
19176
+ current = current.parent;
19177
+ }
19178
+ return depth;
19179
+ }
19180
+ function getElementsByDepthDescending($) {
19181
+ const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
19182
+ const depths = /* @__PURE__ */ new Map();
19183
+ for (const el of elements) {
19184
+ depths.set(el, getNodeDepth(el));
19185
+ }
19186
+ return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
19187
+ }
18783
19188
  function flattenExtractionTree($) {
18784
- const flatten = (root) => {
18785
- root.find("*").each(function flattenNode() {
18786
- const el = $(this);
18787
- const node = el[0];
18788
- if (!node) {
18789
- return;
18790
- }
18791
- const tag = (node.tagName || "").toLowerCase();
18792
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
18793
- return;
18794
- }
18795
- if (isPreservedImageElement($, el)) {
18796
- return;
18797
- }
18798
- if (tag === "a") {
18799
- el.children().each(function flattenAnchorChild() {
18800
- flatten($(this));
18801
- });
18802
- return;
18803
- }
18804
- const hasText = hasDirectText($, el);
18805
- if (hasText) {
18806
- return;
18807
- }
18808
- if (el.children().length === 0) {
18809
- el.remove();
18810
- return;
19189
+ for (const node of getElementsInReverseDocumentOrder($)) {
19190
+ const el = $(node);
19191
+ const tag = (node.tagName || "").toLowerCase();
19192
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag) || isPreservedImageElement(node)) {
19193
+ continue;
19194
+ }
19195
+ if (tag === "a" || hasDirectText(node)) {
19196
+ continue;
19197
+ }
19198
+ if (!hasElementChildren(node)) {
19199
+ el.remove();
19200
+ continue;
19201
+ }
19202
+ el.replaceWith(el.contents());
19203
+ }
19204
+ }
19205
+ function hasMarkedAncestor(el, attr) {
19206
+ let current = el[0]?.parent;
19207
+ while (current) {
19208
+ if (!isElementLikeNode(current)) {
19209
+ return false;
19210
+ }
19211
+ if (current.attribs?.[attr] !== void 0) {
19212
+ return true;
19213
+ }
19214
+ current = current.parent;
19215
+ }
19216
+ return false;
19217
+ }
19218
+ function isIndicatorImage(node) {
19219
+ return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
19220
+ }
19221
+ function isIndicatorPictureSource(node) {
19222
+ return (node?.tagName || "").toLowerCase() === "source" && hasPictureAncestor(node) && (hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
19223
+ }
19224
+ function isSemanticIndicator(node) {
19225
+ const tag = (node?.tagName || "").toLowerCase();
19226
+ if (tag === "svg") {
19227
+ return true;
19228
+ }
19229
+ return hasAttribute2(node, "aria-label") || hasAttribute2(node, "title") || hasAttribute2(node, "data-icon") || node?.attribs?.role === "img";
19230
+ }
19231
+ function findIndicatorDescendant(root) {
19232
+ if (!root) {
19233
+ return void 0;
19234
+ }
19235
+ let firstImage;
19236
+ let firstSource;
19237
+ let firstSemantic;
19238
+ const visit = (node) => {
19239
+ if (!isElementLikeNode(node)) {
19240
+ return false;
19241
+ }
19242
+ if (isIndicatorImage(node)) {
19243
+ firstImage = node;
19244
+ return true;
19245
+ }
19246
+ if (firstSource === void 0 && isIndicatorPictureSource(node)) {
19247
+ firstSource = node;
19248
+ }
19249
+ if (firstSemantic === void 0 && isSemanticIndicator(node)) {
19250
+ firstSemantic = node;
19251
+ }
19252
+ for (const child of getChildNodes(node)) {
19253
+ if (visit(child)) {
19254
+ return true;
18811
19255
  }
18812
- el.children().each(function flattenChild() {
18813
- flatten($(this));
18814
- });
18815
- el.replaceWith(el.contents());
18816
- });
19256
+ }
19257
+ return false;
18817
19258
  };
18818
- flatten($.root());
19259
+ for (const child of getChildNodes(root)) {
19260
+ if (visit(child)) {
19261
+ return firstImage;
19262
+ }
19263
+ }
19264
+ return firstImage ?? firstSource ?? firstSemantic;
18819
19265
  }
18820
19266
  function serializeForExtraction($, root) {
18821
19267
  const lines = [];
@@ -18884,7 +19330,7 @@ function serializeForExtraction($, root) {
18884
19330
  lines.push(`${" ".repeat(depth)}</${tagName}>`);
18885
19331
  }
18886
19332
  traverse(root, 0);
18887
- return lines.join("\n");
19333
+ return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
18888
19334
  }
18889
19335
  function isClickable($, el, context) {
18890
19336
  if (context.hasPreMarked) {
@@ -18976,7 +19422,11 @@ function cleanForExtraction(html) {
18976
19422
  }
18977
19423
  });
18978
19424
  flattenExtractionTree($clean);
18979
- return deduplicateImages(serializeForExtraction($clean, $clean.root()[0]));
19425
+ const root = $clean.root()[0];
19426
+ if (root === void 0) {
19427
+ return "";
19428
+ }
19429
+ return deduplicateImages(serializeForExtraction($clean, root));
18980
19430
  }
18981
19431
  function cleanForAction(html) {
18982
19432
  if (!html.trim()) {
@@ -19002,22 +19452,12 @@ function cleanForAction(html) {
19002
19452
  $(`[${clickableMark}]`).each(function markIndicators() {
19003
19453
  const el = $(this);
19004
19454
  const wrapperAttrs = el.attr() || {};
19005
- if (hasTextDeep(el) || hasActionLabel(wrapperAttrs)) {
19455
+ if (hasTextDeepNode(el[0]) || hasActionLabel(wrapperAttrs)) {
19006
19456
  return;
19007
19457
  }
19008
- const imageIndicator = el.find("img[alt], img[src], img[srcset]").first();
19009
- if (imageIndicator.length) {
19010
- imageIndicator.attr(indicatorMark, "1");
19011
- return;
19012
- }
19013
- const pictureSourceIndicator = el.find("picture source[src], picture source[srcset]").first();
19014
- if (pictureSourceIndicator.length) {
19015
- pictureSourceIndicator.attr(indicatorMark, "1");
19016
- return;
19017
- }
19018
- const semanticIndicator = el.find('[aria-label], [title], [data-icon], [role="img"], svg').first();
19019
- if (semanticIndicator.length) {
19020
- semanticIndicator.attr(indicatorMark, "1");
19458
+ const indicatorNode = findIndicatorDescendant(el[0]);
19459
+ if (indicatorNode !== void 0) {
19460
+ $(indicatorNode).attr(indicatorMark, "1");
19021
19461
  }
19022
19462
  });
19023
19463
  $(`[${clickableMark}]`).each(function removeEmptyClickable() {
@@ -19027,7 +19467,7 @@ function cleanForAction(html) {
19027
19467
  if (NATIVE_INTERACTIVE_TAGS.has(tag) || tag === "a") {
19028
19468
  return;
19029
19469
  }
19030
- if (el.children().length > 0 || hasDirectText($, el)) {
19470
+ if (hasElementChildren(node) || hasDirectText(node)) {
19031
19471
  return;
19032
19472
  }
19033
19473
  const wrapperAttrs = el.attr() || {};
@@ -19053,46 +19493,31 @@ function cleanForAction(html) {
19053
19493
  current = ancestor.parent();
19054
19494
  }
19055
19495
  });
19056
- let changed = true;
19057
- while (changed) {
19058
- changed = false;
19059
- const nodes = [];
19060
- $("*").each(function collectNodes() {
19061
- nodes.push($(this));
19062
- });
19063
- nodes.sort((left, right) => right.parents().length - left.parents().length);
19064
- for (const el of nodes) {
19065
- const node = el[0];
19066
- if (!node) {
19067
- continue;
19068
- }
19069
- const tag = (node.tagName || "").toLowerCase();
19070
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
19071
- continue;
19072
- }
19073
- if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
19074
- continue;
19075
- }
19076
- const insideClickable = el.parents(`[${clickableMark}]`).length > 0;
19077
- const preserveBranch = el.attr(branchMark) !== void 0;
19078
- const hasContent = el.children().length > 0 || hasDirectText($, el);
19079
- if (insideClickable || preserveBranch) {
19080
- if (!hasContent) {
19081
- el.remove();
19082
- } else {
19083
- unwrapActionNode($, el);
19084
- }
19085
- changed = true;
19086
- continue;
19087
- }
19496
+ for (const node of getElementsByDepthDescending($)) {
19497
+ const el = $(node);
19498
+ const tag = (node.tagName || "").toLowerCase();
19499
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
19500
+ continue;
19501
+ }
19502
+ if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
19503
+ continue;
19504
+ }
19505
+ const insideClickable = hasMarkedAncestor(el, clickableMark);
19506
+ const preserveBranch = el.attr(branchMark) !== void 0;
19507
+ const hasContent = hasElementChildren(node) || hasDirectText(node);
19508
+ if (insideClickable || preserveBranch) {
19088
19509
  if (!hasContent) {
19089
19510
  el.remove();
19090
- changed = true;
19091
- continue;
19511
+ } else {
19512
+ unwrapActionNode($, el);
19092
19513
  }
19093
- unwrapActionNode($, el);
19094
- changed = true;
19514
+ continue;
19095
19515
  }
19516
+ if (!hasContent) {
19517
+ el.remove();
19518
+ continue;
19519
+ }
19520
+ unwrapActionNode($, el);
19096
19521
  }
19097
19522
  $.root().find("*").contents().each(function normalizeActionTextNodes() {
19098
19523
  if (this.type !== "text") {
@@ -19172,21 +19597,7 @@ function cleanForAction(html) {
19172
19597
  OPENSTEER_SPARSE_COUNTER_ATTR
19173
19598
  ]);
19174
19599
  if (clickable) {
19175
- for (const attr of [
19176
- "href",
19177
- "role",
19178
- "type",
19179
- "title",
19180
- "placeholder",
19181
- "value",
19182
- "aria-label",
19183
- "aria-labelledby",
19184
- "aria-describedby",
19185
- "aria-expanded",
19186
- "aria-pressed",
19187
- "aria-selected",
19188
- "aria-haspopup"
19189
- ]) {
19600
+ for (const attr of ["href", "role", "type", "title", "placeholder", "value", "aria-label"]) {
19190
19601
  keep.add(attr);
19191
19602
  }
19192
19603
  }
@@ -19210,16 +19621,25 @@ function cleanForAction(html) {
19210
19621
  });
19211
19622
  return compactHtml(deduplicateImages($.html()));
19212
19623
  }
19213
- var STRIP_TAGS, TEXT_ATTR_MAX, URL_ATTR_MAX, URL_ATTRS, TEXT_ATTRS, TRUNCATION_SUFFIX, NOISE_SELECTORS, VOID_TAGS2;
19624
+ var STRIP_TAGS, TEXT_ATTR_MAX, SRCSET_ATTR_MAX, MIDDLE_TRUNCATED_URL_ATTRS, TEXT_ATTRS, TRUNCATION_SUFFIX, MIDDLE_TRUNCATION_MARKER, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX, SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX, NOISE_SELECTORS, VOID_TAGS2;
19214
19625
  var init_cleaner = __esm({
19215
19626
  "../runtime-core/src/sdk/snapshot/cleaner.ts"() {
19216
19627
  init_constants();
19217
19628
  STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
19218
19629
  TEXT_ATTR_MAX = 150;
19219
- URL_ATTR_MAX = 500;
19220
- URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "srcset"]);
19630
+ SRCSET_ATTR_MAX = 160;
19631
+ MIDDLE_TRUNCATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
19221
19632
  TEXT_ATTRS = /* @__PURE__ */ new Set(["alt", "title", "aria-label", "placeholder", "value"]);
19222
- TRUNCATION_SUFFIX = " [truncated]";
19633
+ TRUNCATION_SUFFIX = "...";
19634
+ MIDDLE_TRUNCATION_MARKER = "...";
19635
+ MIDDLE_TRUNCATION_HEAD_MAX = 40;
19636
+ MIDDLE_TRUNCATION_TAIL_MAX = 20;
19637
+ SRCSET_CANDIDATE_HEAD_MAX = 36;
19638
+ SRCSET_CANDIDATE_TAIL_MAX = 12;
19639
+ SRCSET_COMPACT_CANDIDATE_HEAD_MAX = 20;
19640
+ SRCSET_COMPACT_CANDIDATE_TAIL_MAX = 8;
19641
+ SRCSET_FALLBACK_HEAD_MAX = 56;
19642
+ SRCSET_FALLBACK_TAIL_MAX = 20;
19223
19643
  NOISE_SELECTORS = [
19224
19644
  `[${OPENSTEER_HIDDEN_ATTR}]`,
19225
19645
  "[hidden]",
@@ -19758,9 +20178,9 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
19758
20178
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
19759
20179
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
19760
20180
  const attributes = [...authoredAttributes];
19761
- const subtreeHidden = hasAttribute2(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
19762
- const selfHidden = !subtreeHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
19763
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
20181
+ const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
20182
+ const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
20183
+ const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
19764
20184
  if (interactive) {
19765
20185
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
19766
20186
  }
@@ -20078,7 +20498,7 @@ function parseOpacity(value) {
20078
20498
  const parsed = Number.parseFloat(value);
20079
20499
  return Number.isFinite(parsed) ? parsed : Number.NaN;
20080
20500
  }
20081
- function hasAttribute2(attributes, name) {
20501
+ function hasAttribute3(attributes, name) {
20082
20502
  const normalizedName = name.toLowerCase();
20083
20503
  return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
20084
20504
  }
@@ -22629,7 +23049,7 @@ function screenshotMediaType(format2) {
22629
23049
  return "image/webp";
22630
23050
  }
22631
23051
  }
22632
- var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, PENDING_OPERATION_EVENT_CAPTURE_LIMIT, PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS, REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS, OpensteerSessionRuntime, DEFAULT_STATE_GLOBAL_NAMES, REPLAY_TRANSPORT_LADDER, CAPTURE_PAGE_STATE_SCRIPT, INTERACTION_RECORDER_INSTALL_SCRIPT, INTERACTION_RECORDER_READ_SCRIPT, INTERACTION_REPLAY_SCRIPT, PAGE_HTTP_REQUEST_SCRIPT;
23052
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, PERSISTED_NETWORK_SETTLE_POLL_MS, PENDING_OPERATION_EVENT_CAPTURE_LIMIT, PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS, REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS, OpensteerSessionRuntime, DEFAULT_STATE_GLOBAL_NAMES, REPLAY_TRANSPORT_LADDER, CAPTURE_PAGE_STATE_SCRIPT, INTERACTION_RECORDER_INSTALL_SCRIPT, INTERACTION_RECORDER_READ_SCRIPT, INTERACTION_REPLAY_SCRIPT, PAGE_HTTP_REQUEST_SCRIPT;
22633
23053
  var init_runtime3 = __esm({
22634
23054
  "../runtime-core/src/sdk/runtime.ts"() {
22635
23055
  init_src();
@@ -22663,6 +23083,7 @@ var init_runtime3 = __esm({
22663
23083
  init_diff();
22664
23084
  MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
22665
23085
  PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
23086
+ PERSISTED_NETWORK_SETTLE_POLL_MS = 25;
22666
23087
  PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
22667
23088
  PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
22668
23089
  REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
@@ -23407,12 +23828,12 @@ var init_runtime3 = __esm({
23407
23828
  async (timeout) => {
23408
23829
  let descriptor2;
23409
23830
  let data;
23410
- if (input.schema !== void 0) {
23411
- assertValidOpensteerExtractionSchemaRoot(input.schema);
23831
+ if (input.template !== void 0) {
23832
+ assertValidOpensteerExtractionTemplateRoot(input.template);
23412
23833
  const fieldTargets = await timeout.runStep(
23413
23834
  () => compileOpensteerExtractionFieldTargets({
23414
23835
  pageRef,
23415
- schema: input.schema,
23836
+ template: input.template,
23416
23837
  dom: this.requireDom()
23417
23838
  })
23418
23839
  );
@@ -23444,7 +23865,7 @@ var init_runtime3 = __esm({
23444
23865
  () => descriptors.write({
23445
23866
  persist,
23446
23867
  root: payload,
23447
- schemaHash: canonicalJsonString(input.schema),
23868
+ templateHash: canonicalJsonString(input.template),
23448
23869
  sourceUrl: pageInfo.url
23449
23870
  })
23450
23871
  );
@@ -23504,7 +23925,7 @@ var init_runtime3 = __esm({
23504
23925
  artifacts,
23505
23926
  data: {
23506
23927
  ...input.persist === void 0 ? {} : { persist: input.persist },
23507
- ...descriptor?.payload.schemaHash === void 0 ? {} : { schemaHash: descriptor.payload.schemaHash },
23928
+ ...descriptor?.payload.templateHash === void 0 ? {} : { templateHash: descriptor.payload.templateHash },
23508
23929
  data: output.data
23509
23930
  },
23510
23931
  context: buildRuntimeTraceContext({
@@ -25437,15 +25858,22 @@ var init_runtime3 = __esm({
25437
25858
  return [];
25438
25859
  }
25439
25860
  const root = await this.ensureRoot();
25440
- const browserRecords = await this.readBrowserNetworkRecords(
25441
- {
25442
- includeBodies: true,
25443
- includeCurrentPageOnly: options.includeCurrentPageOnly,
25444
- ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
25445
- requestIds
25446
- },
25447
- signal
25448
- );
25861
+ let browserRecords = [];
25862
+ for (; ; ) {
25863
+ browserRecords = await this.readBrowserNetworkRecords(
25864
+ {
25865
+ includeBodies: true,
25866
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
25867
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
25868
+ requestIds
25869
+ },
25870
+ signal
25871
+ );
25872
+ if (browserRecords.length === requestIds.length && browserRecords.every((record) => record.captureState !== "pending")) {
25873
+ break;
25874
+ }
25875
+ await delayWithSignal(PERSISTED_NETWORK_SETTLE_POLL_MS, signal);
25876
+ }
25449
25877
  return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
25450
25878
  bodyWriteMode: "authoritative",
25451
25879
  redactSecretHeaders: false
@@ -29543,9 +29971,11 @@ var init_session_proxy = __esm({
29543
29971
  function buildSharedRuntimeOptions(input) {
29544
29972
  const ownership = resolveOwnership(input.browser);
29545
29973
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
29974
+ ...input.rootDir === void 0 ? {} : { rootDir: input.rootDir },
29546
29975
  rootPath: input.rootPath,
29547
29976
  ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
29548
29977
  engineName: input.engineName,
29978
+ ...input.environment === void 0 ? {} : { environment: input.environment },
29549
29979
  ...(factoryOptions.browser ?? input.browser) === void 0 ? {} : { browser: factoryOptions.browser ?? input.browser },
29550
29980
  ...(factoryOptions.launch ?? input.launch) === void 0 ? {} : { launch: factoryOptions.launch ?? input.launch },
29551
29981
  ...(factoryOptions.context ?? input.context) === void 0 ? {} : { context: factoryOptions.context ?? input.context }
@@ -29608,8 +30038,10 @@ var init_runtime4 = __esm({
29608
30038
  super(
29609
30039
  buildSharedRuntimeOptions({
29610
30040
  name: publicWorkspace ?? "default",
30041
+ ...options.rootDir === void 0 ? {} : { rootDir: options.rootDir },
29611
30042
  rootPath,
29612
30043
  ...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
30044
+ ...options.environment === void 0 ? {} : { environment: options.environment },
29613
30045
  ...options.browser === void 0 ? {} : { browser: options.browser },
29614
30046
  ...options.launch === void 0 ? {} : { launch: options.launch },
29615
30047
  ...options.context === void 0 ? {} : { context: options.context },
@@ -29651,9 +30083,10 @@ function resolveOpensteerRuntimeConfig(input = {}) {
29651
30083
  function createOpensteerSemanticRuntime(input = {}) {
29652
30084
  const runtimeOptions = input.runtimeOptions ?? {};
29653
30085
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
30086
+ const environment = input.environment ?? process.env;
29654
30087
  const config = resolveOpensteerRuntimeConfig({
29655
30088
  ...input.provider === void 0 ? {} : { provider: input.provider },
29656
- ...input.environment === void 0 ? {} : { environment: input.environment }
30089
+ environment
29657
30090
  });
29658
30091
  assertProviderSupportsEngine(config.provider.mode, engine);
29659
30092
  if (config.provider.mode === "cloud") {
@@ -29668,7 +30101,8 @@ function createOpensteerSemanticRuntime(input = {}) {
29668
30101
  }
29669
30102
  return new OpensteerRuntime({
29670
30103
  ...runtimeOptions,
29671
- engineName: engine
30104
+ engineName: engine,
30105
+ environment
29672
30106
  });
29673
30107
  }
29674
30108
  var init_runtime_resolution = __esm({
@@ -29993,6 +30427,7 @@ var init_opensteer = __esm({
29993
30427
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
29994
30428
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
29995
30429
  ...engineName === void 0 ? {} : { engineName },
30430
+ environment,
29996
30431
  ...runtimeOptions.browser === void 0 ? {} : { browser: runtimeOptions.browser },
29997
30432
  ...runtimeOptions.launch === void 0 ? {} : { launch: runtimeOptions.launch },
29998
30433
  ...runtimeOptions.context === void 0 ? {} : { context: runtimeOptions.context }
@@ -30117,9 +30552,6 @@ var init_opensteer = __esm({
30117
30552
  await delay5(pollIntervalMs);
30118
30553
  }
30119
30554
  }
30120
- async snapshot(mode = "action") {
30121
- return (await this.runtime.snapshot({ mode })).html;
30122
- }
30123
30555
  async cookies(domain) {
30124
30556
  return new SessionCookieJar(
30125
30557
  await this.runtime.getCookies(domain === void 0 ? {} : { domain })
@@ -30177,7 +30609,7 @@ var init_opensteer = __esm({
30177
30609
 
30178
30610
  // package.json
30179
30611
  var package_default = {
30180
- version: "0.9.3"};
30612
+ version: "0.9.5"};
30181
30613
 
30182
30614
  // src/cli/bin.ts
30183
30615
  init_browser_manager();
@@ -30210,11 +30642,11 @@ Navigation:
30210
30642
 
30211
30643
  DOM:
30212
30644
  snapshot [action|extraction]
30213
- click <element> [--button left|middle|right] [--persist <key>] [--capture-network <label>]
30214
- hover <element> [--persist <key>] [--capture-network <label>]
30215
- input <element> <text> [--press-enter] [--persist <key>] [--capture-network <label>]
30216
- scroll <direction> <amount> [--element <n>] [--persist <key>] [--capture-network <label>]
30217
- extract <schema> [--persist <key>]
30645
+ click <element> --persist <key> [--button left|middle|right] [--capture-network <label>]
30646
+ hover <element> --persist <key> [--capture-network <label>]
30647
+ input <element> <text> --persist <key> [--press-enter] [--capture-network <label>]
30648
+ scroll <direction> <amount> --persist <key> [--element <n>] [--capture-network <label>]
30649
+ extract <template> --persist <key>
30218
30650
  evaluate <script>
30219
30651
  init-script <script>
30220
30652
 
@@ -30764,18 +31196,18 @@ async function buildOperationInput(operation, parsed, runtime) {
30764
31196
  },
30765
31197
  direction,
30766
31198
  amount,
30767
- ...persist === void 0 ? {} : { persist },
31199
+ persist,
30768
31200
  ...captureNetwork === void 0 ? {} : { captureNetwork }
30769
31201
  };
30770
31202
  }
30771
31203
  case "dom.extract": {
30772
31204
  if (parsed.rest[0] === void 0) {
30773
- throw new Error("extract requires a schema.");
31205
+ throw new Error("extract requires a template.");
30774
31206
  }
30775
- const persist = readExtractPersistKey(parsed);
31207
+ const persist = readPersistKey(parsed, "extract");
30776
31208
  return {
30777
- schema: parseRequiredJsonObjectArgument(joinRest(parsed.rest, 0), "extract schema"),
30778
- ...persist === void 0 ? {} : { persist }
31209
+ persist,
31210
+ template: parseRequiredJsonObjectArgument(joinRest(parsed.rest, 0), "extract template")
30779
31211
  };
30780
31212
  }
30781
31213
  case "network.query": {
@@ -30985,7 +31417,7 @@ function buildElementTargetInput(parsed, verb) {
30985
31417
  kind: "element",
30986
31418
  element
30987
31419
  },
30988
- ...persist === void 0 ? {} : { persist },
31420
+ persist,
30989
31421
  ...captureNetwork === void 0 ? {} : { captureNetwork }
30990
31422
  };
30991
31423
  }
@@ -31190,23 +31622,10 @@ function readKeyModifiers(value) {
31190
31622
  function readPersistKey(parsed, verb) {
31191
31623
  const value = readSingle(parsed.rawOptions, "persist");
31192
31624
  if (value === void 0) {
31193
- return void 0;
31194
- }
31195
- if (value === "true" || value === "false") {
31196
- throw new Error(`${verb} requires "--persist <key>" when using --persist.`);
31197
- }
31198
- if (verb === "scroll" && readOptionalNumber(parsed.rawOptions, "element") === void 0) {
31199
- throw new Error('scroll requires "--element <n>" when using "--persist <key>".');
31200
- }
31201
- return value;
31202
- }
31203
- function readExtractPersistKey(parsed) {
31204
- const value = readSingle(parsed.rawOptions, "persist");
31205
- if (value === void 0) {
31206
- return void 0;
31625
+ throw new Error(`${verb} requires "--persist <key>".`);
31207
31626
  }
31208
31627
  if (value === "true" || value === "false") {
31209
- throw new Error('extract requires "--persist <key>" when using --persist.');
31628
+ throw new Error(`${verb} requires "--persist <key>".`);
31210
31629
  }
31211
31630
  return value;
31212
31631
  }
@@ -34177,7 +34596,7 @@ async function runLocalViewService() {
34177
34596
  init_service();
34178
34597
  init_session_manifest();
34179
34598
  init_root2();
34180
- async function handleViewCommand(parsed) {
34599
+ async function handleViewCommand(parsed, options = {}) {
34181
34600
  const subcommand = parsed.command[1];
34182
34601
  if (subcommand === "serve") {
34183
34602
  assertNoViewPreferenceFlag(parsed);
@@ -34211,6 +34630,16 @@ async function handleViewCommand(parsed) {
34211
34630
  url,
34212
34631
  ...sessionId === void 0 ? {} : { sessionId }
34213
34632
  });
34633
+ if (parsed.options.json !== true) {
34634
+ try {
34635
+ await (options.openUrl ?? openBrowserUrl)(url);
34636
+ } catch {
34637
+ process.stderr.write(
34638
+ `Could not automatically open the local view. Open it manually: ${url}
34639
+ `
34640
+ );
34641
+ }
34642
+ }
34214
34643
  }
34215
34644
  async function resolveWorkspaceSessionId(input) {
34216
34645
  const rootPath = resolveFilesystemWorkspacePath({