opensteer 0.8.9 → 0.8.11

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.
@@ -1560,6 +1560,9 @@ function isBrowserCoreError(value) {
1560
1560
  // ../browser-core/src/cdp-visual-stability.ts
1561
1561
  var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
1562
1562
 
1563
+ // ../browser-core/src/post-load-tracker.ts
1564
+ var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
1565
+
1563
1566
  // ../protocol/src/identity.ts
1564
1567
  var refPrefixes = [
1565
1568
  "session",
@@ -6749,9 +6752,23 @@ var opensteerInspectStorageInputSchema = objectSchema(
6749
6752
  title: "OpensteerInspectStorageInput"
6750
6753
  }
6751
6754
  );
6755
+ var opensteerComputerMouseButtonSchema = enumSchema(["left", "middle", "right"], {
6756
+ title: "OpensteerComputerMouseButton"
6757
+ });
6758
+ var opensteerComputerKeyModifierSchema = enumSchema(
6759
+ ["Shift", "Control", "Alt", "Meta"],
6760
+ {
6761
+ title: "OpensteerComputerKeyModifier"
6762
+ }
6763
+ );
6752
6764
  var opensteerDomClickInputSchema = objectSchema(
6753
6765
  {
6754
6766
  target: opensteerTargetInputSchema,
6767
+ button: opensteerComputerMouseButtonSchema,
6768
+ clickCount: integerSchema({ minimum: 1 }),
6769
+ modifiers: arraySchema(opensteerComputerKeyModifierSchema, {
6770
+ uniqueItems: true
6771
+ }),
6755
6772
  persistAsDescription: stringSchema(),
6756
6773
  captureNetwork: stringSchema({ minLength: 1 })
6757
6774
  },
@@ -6851,18 +6868,6 @@ var opensteerSessionCloseOutputSchema = objectSchema(
6851
6868
  required: ["closed"]
6852
6869
  }
6853
6870
  );
6854
- var opensteerComputerMouseButtonSchema = enumSchema(
6855
- ["left", "middle", "right"],
6856
- {
6857
- title: "OpensteerComputerMouseButton"
6858
- }
6859
- );
6860
- var opensteerComputerKeyModifierSchema = enumSchema(
6861
- ["Shift", "Control", "Alt", "Meta"],
6862
- {
6863
- title: "OpensteerComputerKeyModifier"
6864
- }
6865
- );
6866
6871
  var opensteerComputerAnnotationSchema = enumSchema(opensteerComputerAnnotationNames, {
6867
6872
  title: "OpensteerComputerAnnotation"
6868
6873
  });
@@ -9053,6 +9058,7 @@ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
9053
9058
  scope: "visible-frames",
9054
9059
  timeoutMs: 7e3
9055
9060
  };
9061
+ var NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS = 1e3;
9056
9062
  var defaultDomActionSettleObserver = {
9057
9063
  async settle(input) {
9058
9064
  if (input.trigger !== "dom-action") {
@@ -9088,6 +9094,13 @@ var defaultNavigationSettleObserver = {
9088
9094
  return false;
9089
9095
  }
9090
9096
  try {
9097
+ await input.engine.waitForPostLoadQuiet({
9098
+ pageRef: input.pageRef,
9099
+ timeoutMs: effectiveTimeout,
9100
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
9101
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
9102
+ signal: input.signal
9103
+ });
9091
9104
  await input.engine.waitForVisualStability({
9092
9105
  pageRef: input.pageRef,
9093
9106
  timeoutMs: effectiveTimeout,
@@ -10799,15 +10812,7 @@ var MemoryDomDescriptorStore = class {
10799
10812
  // ../runtime-core/src/action-boundary.ts
10800
10813
  var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
10801
10814
  async function captureActionBoundarySnapshot(engine, pageRef) {
10802
- const frames = await engine.listFrames({ pageRef });
10803
- const mainFrame = frames.find((frame) => frame.isMainFrame);
10804
- if (!mainFrame) {
10805
- throw new Error(`page ${pageRef} does not expose a main frame`);
10806
- }
10807
- return {
10808
- pageRef,
10809
- documentRef: mainFrame.documentRef
10810
- };
10815
+ return engine.getActionBoundarySnapshot({ pageRef });
10811
10816
  }
10812
10817
  function createActionBoundaryDiagnostics(input) {
10813
10818
  return {
@@ -14749,7 +14754,7 @@ async function clearPersistedSessionRecord(rootPath, provider) {
14749
14754
  await rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
14750
14755
  }
14751
14756
  function isPersistedCloudSessionRecord(value) {
14752
- return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.baseUrl === "string" && value.baseUrl.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
14757
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
14753
14758
  }
14754
14759
  function isPersistedLocalBrowserSessionRecord(value) {
14755
14760
  return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
@@ -22408,11 +22413,11 @@ var SandboxClock = class {
22408
22413
  performanceNow() {
22409
22414
  return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
22410
22415
  }
22411
- setTimeout(callback, delay2 = 0, ...args) {
22412
- return this.registerTimer(false, callback, delay2, args);
22416
+ setTimeout(callback, delay3 = 0, ...args) {
22417
+ return this.registerTimer(false, callback, delay3, args);
22413
22418
  }
22414
- setInterval(callback, delay2 = 0, ...args) {
22415
- return this.registerTimer(true, callback, delay2, args);
22419
+ setInterval(callback, delay3 = 0, ...args) {
22420
+ return this.registerTimer(true, callback, delay3, args);
22416
22421
  }
22417
22422
  clearTimeout(timerId) {
22418
22423
  this.clearTimer(timerId);
@@ -22433,9 +22438,9 @@ var SandboxClock = class {
22433
22438
  this.clearTimer(timerId);
22434
22439
  }
22435
22440
  }
22436
- registerTimer(repeat, callback, delay2, args) {
22441
+ registerTimer(repeat, callback, delay3, args) {
22437
22442
  const timerId = this.nextTimerId++;
22438
- const normalizedDelay = Math.max(0, delay2);
22443
+ const normalizedDelay = Math.max(0, delay3);
22439
22444
  const record = {
22440
22445
  callback,
22441
22446
  args,
@@ -22942,6 +22947,7 @@ function diffInteractionTraces(left, right) {
22942
22947
  // ../runtime-core/src/sdk/runtime.ts
22943
22948
  var requireForAuthRecipeHook = createRequire(import.meta.url);
22944
22949
  var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
22950
+ var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
22945
22951
  var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
22946
22952
  var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
22947
22953
  var OpensteerSessionRuntime = class {
@@ -23605,6 +23611,9 @@ var OpensteerSessionRuntime = class {
23605
23611
  const result = await this.requireDom().click({
23606
23612
  pageRef,
23607
23613
  target,
23614
+ ...input.button === void 0 ? {} : { button: input.button },
23615
+ ...input.clickCount === void 0 ? {} : { clickCount: input.clickCount },
23616
+ ...input.modifiers === void 0 ? {} : { modifiers: input.modifiers },
23608
23617
  timeout
23609
23618
  });
23610
23619
  return {
@@ -29081,6 +29090,29 @@ var OpensteerSessionRuntime = class {
29081
29090
  return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
29082
29091
  }
29083
29092
  async flushPersistedNetworkHistory() {
29093
+ if (this.sessionRef === void 0) {
29094
+ return;
29095
+ }
29096
+ const root = await this.ensureRoot();
29097
+ try {
29098
+ await withDetachedTimeoutSignal(PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, async (signal) => {
29099
+ const browserRecords = await this.readBrowserNetworkRecords(
29100
+ {
29101
+ includeBodies: true,
29102
+ includeCurrentPageOnly: false
29103
+ },
29104
+ signal
29105
+ );
29106
+ await this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
29107
+ bodyWriteMode: "authoritative",
29108
+ redactSecretHeaders: false
29109
+ });
29110
+ });
29111
+ } catch (error) {
29112
+ if (!isIgnorableRuntimeBindingError(error)) {
29113
+ throw error;
29114
+ }
29115
+ }
29084
29116
  }
29085
29117
  toDomTargetRef(target) {
29086
29118
  if (target.kind === "description") {
@@ -29237,6 +29269,12 @@ var OpensteerSessionRuntime = class {
29237
29269
  return "live";
29238
29270
  } catch (error) {
29239
29271
  if (isIgnorableRuntimeBindingError(error)) {
29272
+ const remainingPages = await engine.listPages({ sessionRef }).catch(() => void 0);
29273
+ const replacementPageRef = remainingPages?.[0]?.pageRef;
29274
+ if (replacementPageRef !== void 0) {
29275
+ this.pageRef = replacementPageRef;
29276
+ return "live";
29277
+ }
29240
29278
  return "invalid";
29241
29279
  }
29242
29280
  throw error;
@@ -32291,6 +32329,1770 @@ function screenshotMediaType(format2) {
32291
32329
  }
32292
32330
  }
32293
32331
 
32332
+ // ../runtime-core/src/recorder/browser-scripts.ts
32333
+ var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
32334
+ /* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
32335
+ );
32336
+ function createFlowRecorderInstallScript(options = {}) {
32337
+ const normalizedOptions = {
32338
+ showStopButton: options.showStopButton ?? true
32339
+ };
32340
+ return String.raw`(() => {
32341
+ const TOP_LEVEL_ONLY = (() => {
32342
+ try {
32343
+ return window.top === window.self;
32344
+ } catch {
32345
+ return false;
32346
+ }
32347
+ })();
32348
+ if (!TOP_LEVEL_ONLY) {
32349
+ return;
32350
+ }
32351
+
32352
+ const globalScope = globalThis;
32353
+ const recorderKey = "__opensteerFlowRecorder";
32354
+ const historyStateKey = "__opensteerFlowRecorderHistory";
32355
+ const recorderUiAttribute = "data-opensteer-recorder-ui";
32356
+ const queueLimit = 1000;
32357
+ const singleAttributePriority = ${JSON.stringify(SINGLE_ATTRIBUTE_PRIORITY)};
32358
+ const stablePrimaryAttrKeys = new Set(${JSON.stringify([...STABLE_PRIMARY_ATTR_KEYS])});
32359
+ const matchAttributePriority = ${JSON.stringify([...MATCH_ATTRIBUTE_PRIORITY])};
32360
+ const attributeDenyKeys = new Set(${JSON.stringify([...ATTRIBUTE_DENY_KEYS])});
32361
+ const lazyLoadingMediaTags = new Set(${JSON.stringify([...LAZY_LOADING_MEDIA_TAGS])});
32362
+ const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
32363
+ const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
32364
+ const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
32365
+ const options = ${JSON.stringify(normalizedOptions)};
32366
+
32367
+ const previous = globalScope[recorderKey];
32368
+ if (previous && typeof previous.dispose === "function") {
32369
+ previous.dispose();
32370
+ }
32371
+
32372
+ const queue = [];
32373
+ const cleanup = [];
32374
+ const inputFlushTimers = new Map();
32375
+ const pendingInputs = new Map();
32376
+ let historyStateCache = undefined;
32377
+ let stopRequested = false;
32378
+ let pendingWheel = undefined;
32379
+
32380
+ const actionTargetTags = new Set([
32381
+ "a",
32382
+ "button",
32383
+ "input",
32384
+ "label",
32385
+ "option",
32386
+ "select",
32387
+ "summary",
32388
+ "textarea",
32389
+ ]);
32390
+
32391
+ function now() {
32392
+ return Date.now();
32393
+ }
32394
+
32395
+ function enqueue(entry) {
32396
+ queue.push({
32397
+ ...entry,
32398
+ timestamp: typeof entry.timestamp === "number" ? entry.timestamp : now(),
32399
+ });
32400
+ if (queue.length > queueLimit) {
32401
+ queue.splice(0, queue.length - queueLimit);
32402
+ }
32403
+ }
32404
+
32405
+ function isRecorderUiNode(node) {
32406
+ let current = node instanceof Node ? node : null;
32407
+ while (current) {
32408
+ if (current instanceof Element && current.hasAttribute(recorderUiAttribute)) {
32409
+ return true;
32410
+ }
32411
+ const root = typeof current.getRootNode === "function" ? current.getRootNode() : null;
32412
+ if (root instanceof ShadowRoot) {
32413
+ current = root.host;
32414
+ continue;
32415
+ }
32416
+ current = current instanceof Element ? current.parentElement : null;
32417
+ }
32418
+ return false;
32419
+ }
32420
+
32421
+ function isValidAttributeName(name) {
32422
+ if (typeof name !== "string") {
32423
+ return false;
32424
+ }
32425
+ const normalized = name.trim();
32426
+ if (normalized.length === 0) {
32427
+ return false;
32428
+ }
32429
+ if (/[\s"'<>/]/.test(normalized)) {
32430
+ return false;
32431
+ }
32432
+ return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(normalized);
32433
+ }
32434
+
32435
+ function escapeAttributeValue(value) {
32436
+ return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
32437
+ }
32438
+
32439
+ function escapeIdentifier(value) {
32440
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
32441
+ return CSS.escape(value);
32442
+ }
32443
+ return String(value).replace(/[^A-Za-z0-9_-]/g, (character) => {
32444
+ const codePoint = character.codePointAt(0);
32445
+ return "\\" + (codePoint == null ? "" : codePoint.toString(16)) + " ";
32446
+ });
32447
+ }
32448
+
32449
+ function normalizeClassValue(element, rawValue) {
32450
+ const tag = element.tagName.toLowerCase();
32451
+ const tokens = String(rawValue)
32452
+ .split(/\s+/u)
32453
+ .map((token) => token.trim())
32454
+ .filter(Boolean)
32455
+ .filter((token) => !volatileClassTokens.has(token))
32456
+ .filter((token) => !(lazyLoadingMediaTags.has(tag) && volatileLazyClassTokens.has(token)));
32457
+ return tokens.join(" ");
32458
+ }
32459
+
32460
+ function shouldKeepAttribute(element, name, value) {
32461
+ const key = String(name || "")
32462
+ .trim()
32463
+ .toLowerCase();
32464
+ if (!key || !String(value || "").trim()) {
32465
+ return false;
32466
+ }
32467
+ if (!isValidAttributeName(key)) {
32468
+ return false;
32469
+ }
32470
+ if (key === "c") {
32471
+ return false;
32472
+ }
32473
+ if (/^on[a-z]/i.test(key)) {
32474
+ return false;
32475
+ }
32476
+ if (attributeDenyKeys.has(key)) {
32477
+ return false;
32478
+ }
32479
+ if (key.startsWith("data-os-") || key.startsWith("data-opensteer-")) {
32480
+ return false;
32481
+ }
32482
+ if (lazyLoadingMediaTags.has(element.tagName.toLowerCase()) && volatileLazyLoadingAttrs.has(key)) {
32483
+ return false;
32484
+ }
32485
+ return true;
32486
+ }
32487
+
32488
+ function readAttributeValue(element, key) {
32489
+ if (key === "class") {
32490
+ const normalized = normalizeClassValue(element, element.getAttribute("class") || "");
32491
+ return normalized.length === 0 ? undefined : normalized;
32492
+ }
32493
+ const value = element.getAttribute(key);
32494
+ if (!shouldKeepAttribute(element, key, value || "")) {
32495
+ return undefined;
32496
+ }
32497
+ return value || undefined;
32498
+ }
32499
+
32500
+ function buildSingleAttributeSelector(element, key, value) {
32501
+ if (!value) {
32502
+ return undefined;
32503
+ }
32504
+ const tag = element.tagName.toLowerCase();
32505
+ if (key === "id") {
32506
+ const idSelector = "#" + escapeIdentifier(value);
32507
+ return element.matches(idSelector)
32508
+ ? idSelector
32509
+ : tag + '[id="' + escapeAttributeValue(value) + '"]';
32510
+ }
32511
+ return tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
32512
+ }
32513
+
32514
+ function isUniqueSelector(selector, element) {
32515
+ if (!selector) {
32516
+ return false;
32517
+ }
32518
+ let matches;
32519
+ try {
32520
+ matches = document.querySelectorAll(selector);
32521
+ } catch {
32522
+ return false;
32523
+ }
32524
+ return matches.length === 1 && matches[0] === element;
32525
+ }
32526
+
32527
+ function nearestRecordTarget(node) {
32528
+ if (isRecorderUiNode(node)) {
32529
+ return null;
32530
+ }
32531
+ let current = node instanceof Element ? node : null;
32532
+ while (current) {
32533
+ if (current.hasAttribute(recorderUiAttribute)) {
32534
+ return null;
32535
+ }
32536
+ const tag = current.tagName.toLowerCase();
32537
+ if (
32538
+ actionTargetTags.has(tag) ||
32539
+ current.hasAttribute("data-testid") ||
32540
+ current.hasAttribute("data-test") ||
32541
+ current.hasAttribute("data-qa") ||
32542
+ current.hasAttribute("data-cy") ||
32543
+ current.hasAttribute("role") ||
32544
+ current.hasAttribute("aria-label") ||
32545
+ current.hasAttribute("name")
32546
+ ) {
32547
+ return current;
32548
+ }
32549
+ current = current.parentElement;
32550
+ }
32551
+ return node instanceof Element ? node : null;
32552
+ }
32553
+
32554
+ function buildSegmentSelector(element) {
32555
+ const tag = element.tagName.toLowerCase();
32556
+ for (const key of matchAttributePriority) {
32557
+ const value = readAttributeValue(element, key);
32558
+ if (!value) {
32559
+ continue;
32560
+ }
32561
+ if (key === "class") {
32562
+ const tokens = value
32563
+ .split(/\s+/u)
32564
+ .map((token) => token.trim())
32565
+ .filter(Boolean)
32566
+ .slice(0, 2);
32567
+ if (tokens.length === 0) {
32568
+ continue;
32569
+ }
32570
+ return tag + tokens.map((token) => "." + escapeIdentifier(token)).join("");
32571
+ }
32572
+ return key === "id"
32573
+ ? tag + '[id="' + escapeAttributeValue(value) + '"]'
32574
+ : tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
32575
+ }
32576
+ return tag;
32577
+ }
32578
+
32579
+ function nthOfTypeSegment(element, baseSelector) {
32580
+ const parent = element.parentElement;
32581
+ if (!parent) {
32582
+ return baseSelector;
32583
+ }
32584
+ const sameType = Array.from(parent.children).filter(
32585
+ (child) => child.tagName === element.tagName,
32586
+ );
32587
+ if (sameType.length <= 1) {
32588
+ return baseSelector;
32589
+ }
32590
+ const index = sameType.indexOf(element) + 1;
32591
+ return baseSelector + ":nth-of-type(" + String(index) + ")";
32592
+ }
32593
+
32594
+ function buildSelector(node) {
32595
+ const element = nearestRecordTarget(node);
32596
+ if (!(element instanceof Element)) {
32597
+ return undefined;
32598
+ }
32599
+
32600
+ for (const key of singleAttributePriority) {
32601
+ const value = readAttributeValue(element, key);
32602
+ const selector = buildSingleAttributeSelector(element, key, value);
32603
+ if (selector && isUniqueSelector(selector, element)) {
32604
+ return selector;
32605
+ }
32606
+ if (value && !stablePrimaryAttrKeys.has(key) && key !== "id") {
32607
+ const tagQualified =
32608
+ element.tagName.toLowerCase() + "[" + key + '="' + escapeAttributeValue(value) + '"]';
32609
+ if (isUniqueSelector(tagQualified, element)) {
32610
+ return tagQualified;
32611
+ }
32612
+ }
32613
+ }
32614
+
32615
+ const segments = [];
32616
+ let current = element;
32617
+ let depth = 0;
32618
+ while (current && depth < 6) {
32619
+ const segment = nthOfTypeSegment(current, buildSegmentSelector(current));
32620
+ segments.unshift(segment);
32621
+ const selector = segments.join(" > ");
32622
+ if (isUniqueSelector(selector, element)) {
32623
+ return selector;
32624
+ }
32625
+ current = current.parentElement;
32626
+ depth += 1;
32627
+ }
32628
+
32629
+ const fallback = segments.join(" > ");
32630
+ return fallback.length > 0 ? fallback : element.tagName.toLowerCase();
32631
+ }
32632
+
32633
+ function readTargetValue(target) {
32634
+ if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
32635
+ return target.value;
32636
+ }
32637
+ if (target instanceof HTMLSelectElement) {
32638
+ return target.value;
32639
+ }
32640
+ if (target instanceof HTMLElement && target.isContentEditable) {
32641
+ return target.textContent || "";
32642
+ }
32643
+ return undefined;
32644
+ }
32645
+
32646
+ function flushPendingInput(selector) {
32647
+ const pending = pendingInputs.get(selector);
32648
+ if (!pending) {
32649
+ return;
32650
+ }
32651
+ pendingInputs.delete(selector);
32652
+ const timer = inputFlushTimers.get(selector);
32653
+ if (timer !== undefined) {
32654
+ clearTimeout(timer);
32655
+ inputFlushTimers.delete(selector);
32656
+ }
32657
+ enqueue({
32658
+ kind: "type",
32659
+ selector,
32660
+ text: pending.text,
32661
+ timestamp: pending.timestamp,
32662
+ });
32663
+ }
32664
+
32665
+ function flushAllInputs() {
32666
+ for (const selector of Array.from(pendingInputs.keys())) {
32667
+ flushPendingInput(selector);
32668
+ }
32669
+ }
32670
+
32671
+ function flushPendingWheel() {
32672
+ if (!pendingWheel) {
32673
+ return;
32674
+ }
32675
+ clearTimeout(pendingWheel.timerId);
32676
+ enqueue({
32677
+ kind: "scroll",
32678
+ selector: pendingWheel.selector,
32679
+ deltaX: pendingWheel.deltaX,
32680
+ deltaY: pendingWheel.deltaY,
32681
+ timestamp: pendingWheel.timestamp,
32682
+ });
32683
+ pendingWheel = undefined;
32684
+ }
32685
+
32686
+ function scheduleInputFlush(selector) {
32687
+ const existing = inputFlushTimers.get(selector);
32688
+ if (existing !== undefined) {
32689
+ clearTimeout(existing);
32690
+ }
32691
+ const timerId = setTimeout(() => {
32692
+ flushPendingInput(selector);
32693
+ }, 400);
32694
+ inputFlushTimers.set(selector, timerId);
32695
+ }
32696
+
32697
+ function createDefaultHistoryState(currentUrl) {
32698
+ return {
32699
+ entries: [currentUrl],
32700
+ index: 0,
32701
+ };
32702
+ }
32703
+
32704
+ function normalizeHistoryState(state) {
32705
+ if (
32706
+ !state ||
32707
+ !Array.isArray(state.entries) ||
32708
+ !state.entries.every((entry) => typeof entry === "string") ||
32709
+ !Number.isInteger(state.index)
32710
+ ) {
32711
+ return undefined;
32712
+ }
32713
+ const entries = state.entries.slice();
32714
+ if (entries.length === 0) {
32715
+ return createDefaultHistoryState(location.href);
32716
+ }
32717
+ return {
32718
+ entries,
32719
+ index: Math.min(entries.length - 1, Math.max(0, state.index)),
32720
+ };
32721
+ }
32722
+
32723
+ function writeHistoryState(state) {
32724
+ const normalized = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
32725
+ historyStateCache = normalized;
32726
+ try {
32727
+ sessionStorage.setItem(historyStateKey, JSON.stringify(normalized));
32728
+ } catch {}
32729
+ return normalized;
32730
+ }
32731
+
32732
+ function applyHistoryState(state, mode, nextUrl) {
32733
+ const current = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
32734
+ const nextState = {
32735
+ entries: current.entries.slice(),
32736
+ index: current.index,
32737
+ };
32738
+ if (mode === "replace") {
32739
+ nextState.entries[nextState.index] = nextUrl;
32740
+ } else if (mode === "push") {
32741
+ nextState.entries = nextState.entries.slice(0, nextState.index + 1);
32742
+ nextState.entries.push(nextUrl);
32743
+ nextState.index = nextState.entries.length - 1;
32744
+ } else if (mode === "back") {
32745
+ nextState.index = Math.max(0, nextState.index - 1);
32746
+ } else if (mode === "forward") {
32747
+ nextState.index = Math.min(nextState.entries.length - 1, nextState.index + 1);
32748
+ }
32749
+ return nextState;
32750
+ }
32751
+
32752
+ function updateHistoryState(mode, nextUrl) {
32753
+ return writeHistoryState(applyHistoryState(readHistoryState(), mode, nextUrl));
32754
+ }
32755
+
32756
+ function readHistoryState() {
32757
+ const cached = normalizeHistoryState(historyStateCache);
32758
+ if (cached) {
32759
+ historyStateCache = cached;
32760
+ return cached;
32761
+ }
32762
+ try {
32763
+ const raw = sessionStorage.getItem(historyStateKey);
32764
+ const parsed = normalizeHistoryState(raw ? JSON.parse(raw) : undefined);
32765
+ if (parsed) {
32766
+ historyStateCache = parsed;
32767
+ return parsed;
32768
+ }
32769
+ } catch {}
32770
+ return undefined;
32771
+ }
32772
+
32773
+ function classifyHistoryTraversal(currentUrl) {
32774
+ const state = readHistoryState();
32775
+ if (!state) {
32776
+ return undefined;
32777
+ }
32778
+ if (state.entries[state.index] === currentUrl) {
32779
+ return undefined;
32780
+ }
32781
+ if (state.entries[state.index - 1] === currentUrl) {
32782
+ writeHistoryState({
32783
+ entries: state.entries,
32784
+ index: state.index - 1,
32785
+ });
32786
+ return "go-back";
32787
+ }
32788
+ if (state.entries[state.index + 1] === currentUrl) {
32789
+ writeHistoryState({
32790
+ entries: state.entries,
32791
+ index: state.index + 1,
32792
+ });
32793
+ return "go-forward";
32794
+ }
32795
+ const existingIndex = state.entries.lastIndexOf(currentUrl);
32796
+ if (existingIndex !== -1 && existingIndex !== state.index) {
32797
+ writeHistoryState({
32798
+ entries: state.entries,
32799
+ index: existingIndex,
32800
+ });
32801
+ return existingIndex < state.index ? "go-back" : "go-forward";
32802
+ }
32803
+ return undefined;
32804
+ }
32805
+
32806
+ function onInstall() {
32807
+ const currentUrl = location.href;
32808
+ const navigationEntry =
32809
+ typeof performance.getEntriesByType === "function"
32810
+ ? performance.getEntriesByType("navigation")[0]
32811
+ : undefined;
32812
+ const navigationType =
32813
+ navigationEntry && typeof navigationEntry.type === "string" ? navigationEntry.type : undefined;
32814
+ const existingState = readHistoryState();
32815
+
32816
+ if (!existingState) {
32817
+ updateHistoryState("replace", currentUrl);
32818
+ return;
32819
+ }
32820
+
32821
+ if (navigationType === "reload") {
32822
+ updateHistoryState("replace", currentUrl);
32823
+ enqueue({
32824
+ kind: "reload",
32825
+ url: currentUrl,
32826
+ });
32827
+ return;
32828
+ }
32829
+
32830
+ if (navigationType === "back_forward") {
32831
+ const traversal = classifyHistoryTraversal(currentUrl);
32832
+ if (traversal === "go-back" || traversal === "go-forward") {
32833
+ enqueue({
32834
+ kind: traversal,
32835
+ url: currentUrl,
32836
+ });
32837
+ return;
32838
+ }
32839
+ }
32840
+
32841
+ if (existingState.entries[existingState.index] !== currentUrl) {
32842
+ updateHistoryState("push", currentUrl);
32843
+ enqueue({
32844
+ kind: "navigate",
32845
+ url: currentUrl,
32846
+ source: "full-navigation",
32847
+ });
32848
+ }
32849
+ }
32850
+
32851
+ function modifierKeys(event) {
32852
+ const modifiers = [];
32853
+ if (event.altKey) {
32854
+ modifiers.push("Alt");
32855
+ }
32856
+ if (event.ctrlKey) {
32857
+ modifiers.push("Control");
32858
+ }
32859
+ if (event.metaKey) {
32860
+ modifiers.push("Meta");
32861
+ }
32862
+ if (event.shiftKey) {
32863
+ modifiers.push("Shift");
32864
+ }
32865
+ return modifiers;
32866
+ }
32867
+
32868
+ function addListener(target, type, listener, options) {
32869
+ target.addEventListener(type, listener, options);
32870
+ cleanup.push(() => {
32871
+ target.removeEventListener(type, listener, options);
32872
+ });
32873
+ }
32874
+
32875
+ function requestStop() {
32876
+ stopRequested = true;
32877
+ }
32878
+
32879
+ function mountStopButton() {
32880
+ if (document.querySelector("[" + recorderUiAttribute + "]")) {
32881
+ return true;
32882
+ }
32883
+ if (stopRequested) {
32884
+ return true;
32885
+ }
32886
+ const parent = document.body || document.documentElement;
32887
+ if (!(parent instanceof HTMLElement)) {
32888
+ return false;
32889
+ }
32890
+
32891
+ const host = document.createElement("div");
32892
+ host.setAttribute(recorderUiAttribute, "true");
32893
+ const shadowRoot = host.attachShadow({ mode: "open" });
32894
+ const style = document.createElement("style");
32895
+ style.textContent = [
32896
+ ":host {",
32897
+ " all: initial;",
32898
+ " display: block;",
32899
+ " position: fixed;",
32900
+ " top: 16px;",
32901
+ " right: 16px;",
32902
+ " z-index: 2147483647;",
32903
+ " pointer-events: auto;",
32904
+ " color-scheme: only light;",
32905
+ " contain: content;",
32906
+ " isolation: isolate;",
32907
+ "}",
32908
+ "button {",
32909
+ " appearance: none;",
32910
+ " border: 1px solid rgba(15, 23, 42, 0.2);",
32911
+ " border-radius: 999px;",
32912
+ " background: rgba(15, 23, 42, 0.92);",
32913
+ " color: #ffffff;",
32914
+ " color-scheme: only light;",
32915
+ " cursor: pointer;",
32916
+ " font: 600 12px/1.2 ui-sans-serif, system-ui, sans-serif;",
32917
+ " letter-spacing: 0.01em;",
32918
+ " padding: 10px 14px;",
32919
+ " box-shadow: 0 10px 30px rgba(15, 23, 42, 0.22);",
32920
+ "}",
32921
+ "button:hover {",
32922
+ " background: rgba(15, 23, 42, 0.98);",
32923
+ "}",
32924
+ "button:focus-visible {",
32925
+ " outline: 2px solid #2563eb;",
32926
+ " outline-offset: 2px;",
32927
+ "}",
32928
+ ].join("\n");
32929
+ const button = document.createElement("button");
32930
+ button.type = "button";
32931
+ button.textContent = "Stop recording";
32932
+ button.setAttribute("aria-label", "Stop recording");
32933
+ const consumePointerEvent = (event) => {
32934
+ event.preventDefault();
32935
+ event.stopPropagation();
32936
+ };
32937
+ for (const type of ["pointerdown", "mousedown", "mouseup", "click"]) {
32938
+ button.addEventListener(type, consumePointerEvent);
32939
+ }
32940
+ button.addEventListener("click", () => {
32941
+ requestStop();
32942
+ host.remove();
32943
+ });
32944
+ shadowRoot.append(style, button);
32945
+ parent.appendChild(host);
32946
+ cleanup.push(() => {
32947
+ host.remove();
32948
+ });
32949
+ return true;
32950
+ }
32951
+
32952
+ function ensureStopButtonMounted() {
32953
+ if (mountStopButton()) {
32954
+ return;
32955
+ }
32956
+
32957
+ const observer = new MutationObserver(() => {
32958
+ if (!mountStopButton()) {
32959
+ return;
32960
+ }
32961
+ observer.disconnect();
32962
+ });
32963
+ observer.observe(document, {
32964
+ childList: true,
32965
+ subtree: true,
32966
+ });
32967
+ cleanup.push(() => {
32968
+ observer.disconnect();
32969
+ });
32970
+ }
32971
+
32972
+ addListener(document, "click", (event) => {
32973
+ if (!event.isTrusted) {
32974
+ return;
32975
+ }
32976
+ const target = nearestRecordTarget(event.target);
32977
+ if (!(target instanceof Element)) {
32978
+ return;
32979
+ }
32980
+ const tag = target.tagName.toLowerCase();
32981
+ if (tag === "select" || tag === "option") {
32982
+ return;
32983
+ }
32984
+ const selector = buildSelector(target);
32985
+ if (!selector) {
32986
+ return;
32987
+ }
32988
+ enqueue({
32989
+ kind: "click",
32990
+ selector,
32991
+ button: event.button,
32992
+ modifiers: modifierKeys(event),
32993
+ });
32994
+ }, true);
32995
+
32996
+ addListener(document, "dblclick", (event) => {
32997
+ if (!event.isTrusted) {
32998
+ return;
32999
+ }
33000
+ const target = nearestRecordTarget(event.target);
33001
+ if (!(target instanceof Element)) {
33002
+ return;
33003
+ }
33004
+ const selector = buildSelector(target);
33005
+ if (!selector) {
33006
+ return;
33007
+ }
33008
+ enqueue({
33009
+ kind: "dblclick",
33010
+ selector,
33011
+ });
33012
+ }, true);
33013
+
33014
+ addListener(document, "input", (event) => {
33015
+ if (!event.isTrusted) {
33016
+ return;
33017
+ }
33018
+ const target = nearestRecordTarget(event.target);
33019
+ if (!(target instanceof Element)) {
33020
+ return;
33021
+ }
33022
+ const selector = buildSelector(target);
33023
+ const text = readTargetValue(target);
33024
+ if (!selector || typeof text !== "string") {
33025
+ return;
33026
+ }
33027
+ pendingInputs.set(selector, {
33028
+ selector,
33029
+ text,
33030
+ timestamp: now(),
33031
+ });
33032
+ scheduleInputFlush(selector);
33033
+ }, true);
33034
+
33035
+ addListener(document, "change", (event) => {
33036
+ if (!event.isTrusted) {
33037
+ return;
33038
+ }
33039
+ const target = nearestRecordTarget(event.target);
33040
+ if (!(target instanceof HTMLSelectElement)) {
33041
+ return;
33042
+ }
33043
+ const selector = buildSelector(target);
33044
+ if (!selector) {
33045
+ return;
33046
+ }
33047
+ const selectedOption = target.selectedOptions && target.selectedOptions.length > 0
33048
+ ? target.selectedOptions[0]
33049
+ : undefined;
33050
+ enqueue({
33051
+ kind: "select-option",
33052
+ selector,
33053
+ value: target.value,
33054
+ ...(selectedOption === undefined ? {} : { label: selectedOption.label || selectedOption.textContent || "" }),
33055
+ });
33056
+ }, true);
33057
+
33058
+ addListener(document, "keydown", (event) => {
33059
+ if (!event.isTrusted) {
33060
+ return;
33061
+ }
33062
+ const allowedKeys = new Set([
33063
+ "ArrowDown",
33064
+ "ArrowLeft",
33065
+ "ArrowRight",
33066
+ "ArrowUp",
33067
+ "Backspace",
33068
+ "Delete",
33069
+ "Enter",
33070
+ "Escape",
33071
+ "Tab",
33072
+ ]);
33073
+ if (!allowedKeys.has(event.key)) {
33074
+ return;
33075
+ }
33076
+ const target = nearestRecordTarget(event.target);
33077
+ const selector = target instanceof Element ? buildSelector(target) : undefined;
33078
+ if (event.key === "Enter" && selector) {
33079
+ flushPendingInput(selector);
33080
+ }
33081
+ enqueue({
33082
+ kind: "keypress",
33083
+ key: event.key,
33084
+ modifiers: modifierKeys(event),
33085
+ ...(selector === undefined ? {} : { selector }),
33086
+ });
33087
+ }, true);
33088
+
33089
+ addListener(document, "wheel", (event) => {
33090
+ if (!event.isTrusted) {
33091
+ return;
33092
+ }
33093
+ const target = nearestRecordTarget(event.target);
33094
+ const selector = target instanceof Element ? buildSelector(target) : undefined;
33095
+ if (pendingWheel && pendingWheel.selector === selector) {
33096
+ pendingWheel.deltaX += event.deltaX;
33097
+ pendingWheel.deltaY += event.deltaY;
33098
+ clearTimeout(pendingWheel.timerId);
33099
+ pendingWheel.timerId = setTimeout(flushPendingWheel, 250);
33100
+ return;
33101
+ }
33102
+ flushPendingWheel();
33103
+ pendingWheel = {
33104
+ selector,
33105
+ deltaX: event.deltaX,
33106
+ deltaY: event.deltaY,
33107
+ timestamp: now(),
33108
+ timerId: setTimeout(flushPendingWheel, 250),
33109
+ };
33110
+ }, true);
33111
+
33112
+ const originalPushState = history.pushState.bind(history);
33113
+ history.pushState = function pushState(state, unused, url) {
33114
+ const beforeUrl = location.href;
33115
+ const output = originalPushState.apply(this, arguments);
33116
+ const nextUrl = location.href;
33117
+ if (nextUrl !== beforeUrl) {
33118
+ updateHistoryState("push", nextUrl);
33119
+ enqueue({
33120
+ kind: "navigate",
33121
+ url: nextUrl,
33122
+ source: "push-state",
33123
+ });
33124
+ }
33125
+ return output;
33126
+ };
33127
+ cleanup.push(() => {
33128
+ history.pushState = originalPushState;
33129
+ });
33130
+
33131
+ const originalReplaceState = history.replaceState.bind(history);
33132
+ history.replaceState = function replaceState(state, unused, url) {
33133
+ const beforeUrl = location.href;
33134
+ const output = originalReplaceState.apply(this, arguments);
33135
+ const nextUrl = location.href;
33136
+ if (nextUrl !== beforeUrl) {
33137
+ updateHistoryState("replace", nextUrl);
33138
+ enqueue({
33139
+ kind: "navigate",
33140
+ url: nextUrl,
33141
+ source: "replace-state",
33142
+ });
33143
+ }
33144
+ return output;
33145
+ };
33146
+ cleanup.push(() => {
33147
+ history.replaceState = originalReplaceState;
33148
+ });
33149
+
33150
+ addListener(globalScope, "popstate", () => {
33151
+ const currentUrl = location.href;
33152
+ const traversal = classifyHistoryTraversal(currentUrl);
33153
+ if (traversal === "go-back" || traversal === "go-forward") {
33154
+ enqueue({
33155
+ kind: traversal,
33156
+ url: currentUrl,
33157
+ });
33158
+ return;
33159
+ }
33160
+ const state = readHistoryState();
33161
+ if (state?.entries[state.index] !== currentUrl) {
33162
+ updateHistoryState("push", currentUrl);
33163
+ enqueue({
33164
+ kind: "navigate",
33165
+ url: currentUrl,
33166
+ source: "history-traversal",
33167
+ });
33168
+ }
33169
+ });
33170
+
33171
+ addListener(globalScope, "hashchange", () => {
33172
+ const currentUrl = location.href;
33173
+ updateHistoryState("replace", currentUrl);
33174
+ enqueue({
33175
+ kind: "navigate",
33176
+ url: currentUrl,
33177
+ source: "hashchange",
33178
+ });
33179
+ });
33180
+
33181
+ function drain() {
33182
+ flushAllInputs();
33183
+ flushPendingWheel();
33184
+ return {
33185
+ url: location.href,
33186
+ focused: document.hasFocus(),
33187
+ visibilityState: document.visibilityState,
33188
+ stopRequested,
33189
+ events: queue.splice(0, queue.length),
33190
+ };
33191
+ }
33192
+
33193
+ globalScope[recorderKey] = {
33194
+ installed: true,
33195
+ debugSelector(target) {
33196
+ return buildSelector(target);
33197
+ },
33198
+ drain,
33199
+ requestStop,
33200
+ dispose() {
33201
+ flushAllInputs();
33202
+ flushPendingWheel();
33203
+ for (const dispose of cleanup.splice(0, cleanup.length)) {
33204
+ dispose();
33205
+ }
33206
+ for (const timerId of inputFlushTimers.values()) {
33207
+ clearTimeout(timerId);
33208
+ }
33209
+ inputFlushTimers.clear();
33210
+ pendingInputs.clear();
33211
+ delete globalScope[recorderKey];
33212
+ },
33213
+ };
33214
+
33215
+ if (options.showStopButton) {
33216
+ ensureStopButtonMounted();
33217
+ }
33218
+ onInstall();
33219
+ })();`;
33220
+ }
33221
+ var FLOW_RECORDER_INSTALL_SCRIPT = createFlowRecorderInstallScript();
33222
+ var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
33223
+ const recorder = globalThis.__opensteerFlowRecorder;
33224
+ if (!recorder || typeof recorder.drain !== "function") {
33225
+ return {
33226
+ url: location.href,
33227
+ focused: document.hasFocus(),
33228
+ visibilityState: document.visibilityState,
33229
+ stopRequested: false,
33230
+ events: [],
33231
+ };
33232
+ }
33233
+ return recorder.drain();
33234
+ })();`;
33235
+
33236
+ // ../runtime-core/src/recorder/event-collector.ts
33237
+ var FlowRecorderCollector = class {
33238
+ runtime;
33239
+ pollIntervalMs;
33240
+ onAction;
33241
+ installScript;
33242
+ pages = /* @__PURE__ */ new Map();
33243
+ actions = [];
33244
+ nextPageOrdinal = 0;
33245
+ runningLoop;
33246
+ loopStopRequested = false;
33247
+ stopDetected = false;
33248
+ focusedPageId;
33249
+ stopWaiters = [];
33250
+ constructor(runtime, options = {}) {
33251
+ this.runtime = runtime;
33252
+ this.pollIntervalMs = options.pollIntervalMs ?? 250;
33253
+ this.onAction = options.onAction;
33254
+ this.installScript = options.installScript ?? FLOW_RECORDER_INSTALL_SCRIPT;
33255
+ }
33256
+ async install() {
33257
+ await this.runtime.addInitScript({
33258
+ script: this.installScript
33259
+ });
33260
+ const { pages } = await this.runtime.listPages();
33261
+ for (const page of pages) {
33262
+ this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
33263
+ }
33264
+ await Promise.all(
33265
+ pages.map(
33266
+ (page) => this.runtime.evaluate({
33267
+ script: this.installScript,
33268
+ pageRef: page.pageRef
33269
+ }).catch(() => void 0)
33270
+ )
33271
+ );
33272
+ const evaluatedPages = await this.readEvaluatedPages(pages);
33273
+ for (const page of evaluatedPages) {
33274
+ this.updateKnownPage(page.pageRef, page.currentUrl, page.openerPageRef);
33275
+ }
33276
+ this.focusedPageId = evaluatedPages.find((page) => page.focused)?.pageId ?? this.focusedPageId;
33277
+ }
33278
+ start() {
33279
+ if (this.runningLoop !== void 0) {
33280
+ return;
33281
+ }
33282
+ this.loopStopRequested = false;
33283
+ this.runningLoop = this.runLoop();
33284
+ }
33285
+ async stop() {
33286
+ this.loopStopRequested = true;
33287
+ if (this.runningLoop !== void 0) {
33288
+ await this.runningLoop;
33289
+ this.runningLoop = void 0;
33290
+ }
33291
+ if (!this.stopDetected) {
33292
+ await this.pollOnce().catch(() => void 0);
33293
+ }
33294
+ return this.actions.slice();
33295
+ }
33296
+ getActions() {
33297
+ return this.actions.slice();
33298
+ }
33299
+ getPages() {
33300
+ return [...this.pages.values()].map((page) => ({
33301
+ pageId: page.pageId,
33302
+ pageRef: page.pageRef,
33303
+ ...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
33304
+ ...page.openerPageId === void 0 ? {} : { openerPageId: page.openerPageId },
33305
+ currentUrl: page.currentUrl
33306
+ })).sort((left, right) => comparePageIds(left.pageId, right.pageId));
33307
+ }
33308
+ getFocusedPageId() {
33309
+ return this.focusedPageId;
33310
+ }
33311
+ async waitForStop() {
33312
+ if (this.stopDetected) {
33313
+ return;
33314
+ }
33315
+ await new Promise((resolve4) => {
33316
+ this.stopWaiters.push(resolve4);
33317
+ });
33318
+ }
33319
+ async pollOnce() {
33320
+ const pollTimestamp = Date.now();
33321
+ const { pages } = await this.runtime.listPages();
33322
+ const previousPageRefs = new Set(this.pages.keys());
33323
+ const evaluatedPages = await this.readEvaluatedPages(pages);
33324
+ if (!this.stopDetected && evaluatedPages.some((page) => page.stopRequested)) {
33325
+ this.stopDetected = true;
33326
+ this.loopStopRequested = true;
33327
+ for (const resolve4 of this.stopWaiters.splice(0, this.stopWaiters.length)) {
33328
+ resolve4();
33329
+ }
33330
+ }
33331
+ const actions = this.normalizePoll({
33332
+ pollTimestamp,
33333
+ previousPageRefs,
33334
+ listedPages: pages,
33335
+ evaluatedPages
33336
+ });
33337
+ if (actions.length === 0) {
33338
+ return [];
33339
+ }
33340
+ actions.sort((left, right) => {
33341
+ const timestampOrder = left.timestamp - right.timestamp;
33342
+ if (timestampOrder !== 0) {
33343
+ return timestampOrder;
33344
+ }
33345
+ return actionSortPriority(left.kind) - actionSortPriority(right.kind);
33346
+ });
33347
+ for (const action of actions) {
33348
+ this.actions.push(action);
33349
+ if (this.onAction !== void 0) {
33350
+ await this.onAction(action);
33351
+ }
33352
+ }
33353
+ return actions;
33354
+ }
33355
+ async runLoop() {
33356
+ while (!this.loopStopRequested) {
33357
+ try {
33358
+ await this.pollOnce();
33359
+ } catch {
33360
+ }
33361
+ if (this.loopStopRequested) {
33362
+ break;
33363
+ }
33364
+ await delay(this.pollIntervalMs);
33365
+ }
33366
+ }
33367
+ async readEvaluatedPages(listedPages) {
33368
+ const pages = await Promise.all(
33369
+ listedPages.map(async (page) => {
33370
+ const knownPage = this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
33371
+ const snapshot = await this.readSnapshot(page.pageRef, page.url);
33372
+ this.updateKnownPage(page.pageRef, snapshot.url, page.openerPageRef);
33373
+ return {
33374
+ pageRef: page.pageRef,
33375
+ pageId: knownPage.pageId,
33376
+ previousUrl: knownPage.currentUrl,
33377
+ currentUrl: snapshot.url,
33378
+ focused: snapshot.focused || snapshot.visibilityState === "visible",
33379
+ stopRequested: snapshot.stopRequested,
33380
+ events: snapshot.events,
33381
+ ...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
33382
+ ...knownPage.openerPageId === void 0 ? {} : { openerPageId: knownPage.openerPageId }
33383
+ };
33384
+ })
33385
+ );
33386
+ return pages;
33387
+ }
33388
+ async readSnapshot(pageRef, fallbackUrl) {
33389
+ const value = await this.runtime.evaluate({
33390
+ script: FLOW_RECORDER_DRAIN_SCRIPT,
33391
+ pageRef
33392
+ });
33393
+ return normalizeSnapshot(value, fallbackUrl);
33394
+ }
33395
+ normalizePoll(input) {
33396
+ const listedPageRefs = new Set(input.listedPages.map((page) => page.pageRef));
33397
+ const actions = [];
33398
+ const firstEventTimestampByPage = /* @__PURE__ */ new Map();
33399
+ for (const evaluatedPage of input.evaluatedPages) {
33400
+ const firstTimestamp = evaluatedPage.events[0]?.timestamp;
33401
+ if (firstTimestamp !== void 0) {
33402
+ firstEventTimestampByPage.set(evaluatedPage.pageRef, firstTimestamp);
33403
+ }
33404
+ }
33405
+ for (const listedPage of input.listedPages) {
33406
+ if (!input.previousPageRefs.has(listedPage.pageRef)) {
33407
+ const created = this.ensureKnownPage(
33408
+ listedPage.pageRef,
33409
+ listedPage.url,
33410
+ listedPage.openerPageRef
33411
+ );
33412
+ actions.push({
33413
+ kind: "new-tab",
33414
+ timestamp: Math.max(
33415
+ 0,
33416
+ (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1
33417
+ ),
33418
+ pageId: created.pageId,
33419
+ pageUrl: listedPage.url,
33420
+ detail: {
33421
+ kind: "new-tab",
33422
+ ...created.openerPageId === void 0 ? {} : { openerPageId: created.openerPageId },
33423
+ initialUrl: listedPage.url
33424
+ }
33425
+ });
33426
+ }
33427
+ }
33428
+ for (const [pageRef, knownPage] of [...this.pages.entries()]) {
33429
+ if (listedPageRefs.has(pageRef)) {
33430
+ continue;
33431
+ }
33432
+ actions.push({
33433
+ kind: "close-tab",
33434
+ timestamp: input.pollTimestamp,
33435
+ pageId: knownPage.pageId,
33436
+ pageUrl: knownPage.currentUrl,
33437
+ detail: {
33438
+ kind: "close-tab"
33439
+ }
33440
+ });
33441
+ this.pages.delete(pageRef);
33442
+ }
33443
+ for (const evaluatedPage of input.evaluatedPages) {
33444
+ const knownPage = this.pages.get(evaluatedPage.pageRef);
33445
+ if (!knownPage) {
33446
+ continue;
33447
+ }
33448
+ if (evaluatedPage.previousUrl !== evaluatedPage.currentUrl && !evaluatedPage.events.some(
33449
+ (event) => event.kind === "navigate" || event.kind === "reload" || event.kind === "go-back" || event.kind === "go-forward"
33450
+ )) {
33451
+ actions.push({
33452
+ kind: "navigate",
33453
+ timestamp: Math.max(
33454
+ 0,
33455
+ (firstEventTimestampByPage.get(evaluatedPage.pageRef) ?? input.pollTimestamp) - 1
33456
+ ),
33457
+ pageId: knownPage.pageId,
33458
+ pageUrl: evaluatedPage.currentUrl,
33459
+ detail: {
33460
+ kind: "navigate",
33461
+ url: evaluatedPage.currentUrl,
33462
+ source: "poll"
33463
+ }
33464
+ });
33465
+ }
33466
+ actions.push(
33467
+ ...evaluatedPage.events.flatMap(
33468
+ (event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
33469
+ )
33470
+ );
33471
+ this.updateKnownPage(
33472
+ evaluatedPage.pageRef,
33473
+ evaluatedPage.currentUrl,
33474
+ evaluatedPage.openerPageRef
33475
+ );
33476
+ }
33477
+ const focusedPage = input.evaluatedPages.find((page) => page.focused);
33478
+ if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
33479
+ actions.push({
33480
+ kind: "switch-tab",
33481
+ timestamp: Math.max(
33482
+ 0,
33483
+ (firstEventTimestampByPage.get(focusedPage.pageRef) ?? input.pollTimestamp) - 1
33484
+ ),
33485
+ pageId: focusedPage.pageId,
33486
+ pageUrl: focusedPage.currentUrl,
33487
+ detail: {
33488
+ kind: "switch-tab",
33489
+ ...this.focusedPageId === void 0 ? {} : { fromPageId: this.focusedPageId },
33490
+ toPageId: focusedPage.pageId
33491
+ }
33492
+ });
33493
+ this.focusedPageId = focusedPage.pageId;
33494
+ }
33495
+ return dedupeConsecutiveSwitchActions(actions);
33496
+ }
33497
+ normalizeRawEvent(event, page, currentUrl) {
33498
+ switch (event.kind) {
33499
+ case "navigate":
33500
+ return [
33501
+ {
33502
+ kind: "navigate",
33503
+ timestamp: event.timestamp,
33504
+ pageId: page.pageId,
33505
+ pageUrl: event.url,
33506
+ detail: {
33507
+ kind: "navigate",
33508
+ url: event.url,
33509
+ source: event.source
33510
+ }
33511
+ }
33512
+ ];
33513
+ case "click":
33514
+ return [
33515
+ {
33516
+ kind: "click",
33517
+ timestamp: event.timestamp,
33518
+ pageId: page.pageId,
33519
+ pageUrl: currentUrl,
33520
+ selector: event.selector,
33521
+ detail: {
33522
+ kind: "click",
33523
+ button: event.button,
33524
+ modifiers: event.modifiers
33525
+ }
33526
+ }
33527
+ ];
33528
+ case "dblclick":
33529
+ return [
33530
+ {
33531
+ kind: "dblclick",
33532
+ timestamp: event.timestamp,
33533
+ pageId: page.pageId,
33534
+ pageUrl: currentUrl,
33535
+ selector: event.selector,
33536
+ detail: {
33537
+ kind: "dblclick"
33538
+ }
33539
+ }
33540
+ ];
33541
+ case "type":
33542
+ return [
33543
+ {
33544
+ kind: "type",
33545
+ timestamp: event.timestamp,
33546
+ pageId: page.pageId,
33547
+ pageUrl: currentUrl,
33548
+ selector: event.selector,
33549
+ detail: {
33550
+ kind: "type",
33551
+ text: event.text
33552
+ }
33553
+ }
33554
+ ];
33555
+ case "keypress":
33556
+ return [
33557
+ {
33558
+ kind: "keypress",
33559
+ timestamp: event.timestamp,
33560
+ pageId: page.pageId,
33561
+ pageUrl: currentUrl,
33562
+ ...event.selector === void 0 ? {} : { selector: event.selector },
33563
+ detail: {
33564
+ kind: "keypress",
33565
+ key: event.key,
33566
+ modifiers: event.modifiers
33567
+ }
33568
+ }
33569
+ ];
33570
+ case "scroll":
33571
+ return [
33572
+ {
33573
+ kind: "scroll",
33574
+ timestamp: event.timestamp,
33575
+ pageId: page.pageId,
33576
+ pageUrl: currentUrl,
33577
+ ...event.selector === void 0 ? {} : { selector: event.selector },
33578
+ detail: {
33579
+ kind: "scroll",
33580
+ deltaX: event.deltaX,
33581
+ deltaY: event.deltaY
33582
+ }
33583
+ }
33584
+ ];
33585
+ case "select-option":
33586
+ return [
33587
+ {
33588
+ kind: "select-option",
33589
+ timestamp: event.timestamp,
33590
+ pageId: page.pageId,
33591
+ pageUrl: currentUrl,
33592
+ selector: event.selector,
33593
+ detail: {
33594
+ kind: "select-option",
33595
+ value: event.value,
33596
+ ...event.label === void 0 ? {} : { label: event.label }
33597
+ }
33598
+ }
33599
+ ];
33600
+ case "reload":
33601
+ return [
33602
+ {
33603
+ kind: "reload",
33604
+ timestamp: event.timestamp,
33605
+ pageId: page.pageId,
33606
+ pageUrl: event.url,
33607
+ detail: {
33608
+ kind: "reload",
33609
+ url: event.url
33610
+ }
33611
+ }
33612
+ ];
33613
+ case "go-back":
33614
+ return [
33615
+ {
33616
+ kind: "go-back",
33617
+ timestamp: event.timestamp,
33618
+ pageId: page.pageId,
33619
+ pageUrl: event.url,
33620
+ detail: {
33621
+ kind: "go-back",
33622
+ url: event.url
33623
+ }
33624
+ }
33625
+ ];
33626
+ case "go-forward":
33627
+ return [
33628
+ {
33629
+ kind: "go-forward",
33630
+ timestamp: event.timestamp,
33631
+ pageId: page.pageId,
33632
+ pageUrl: event.url,
33633
+ detail: {
33634
+ kind: "go-forward",
33635
+ url: event.url
33636
+ }
33637
+ }
33638
+ ];
33639
+ }
33640
+ }
33641
+ ensureKnownPage(pageRef, url, openerPageRef) {
33642
+ const existing = this.pages.get(pageRef);
33643
+ if (existing !== void 0) {
33644
+ return existing;
33645
+ }
33646
+ const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
33647
+ const page = {
33648
+ pageId: `page${String(this.nextPageOrdinal++)}`,
33649
+ pageRef,
33650
+ currentUrl: url,
33651
+ ...openerPageRef === void 0 ? {} : { openerPageRef },
33652
+ ...openerPageId === void 0 ? {} : { openerPageId }
33653
+ };
33654
+ this.pages.set(pageRef, page);
33655
+ return page;
33656
+ }
33657
+ updateKnownPage(pageRef, url, openerPageRef) {
33658
+ const current = this.pages.get(pageRef);
33659
+ if (current === void 0) {
33660
+ this.ensureKnownPage(pageRef, url, openerPageRef);
33661
+ return;
33662
+ }
33663
+ const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
33664
+ this.pages.set(pageRef, {
33665
+ ...current,
33666
+ currentUrl: url,
33667
+ ...openerPageRef === void 0 ? {} : { openerPageRef },
33668
+ ...openerPageId === void 0 ? {} : { openerPageId }
33669
+ });
33670
+ }
33671
+ };
33672
+ function normalizeSnapshot(value, fallbackUrl) {
33673
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
33674
+ return {
33675
+ url: fallbackUrl,
33676
+ focused: false,
33677
+ visibilityState: "hidden",
33678
+ stopRequested: false,
33679
+ events: []
33680
+ };
33681
+ }
33682
+ const snapshot = value;
33683
+ return {
33684
+ url: typeof snapshot.url === "string" ? snapshot.url : fallbackUrl,
33685
+ focused: snapshot.focused === true,
33686
+ visibilityState: snapshot.visibilityState === "visible" || snapshot.visibilityState === "prerender" || snapshot.visibilityState === "hidden" ? snapshot.visibilityState : "hidden",
33687
+ stopRequested: snapshot.stopRequested === true,
33688
+ events: Array.isArray(snapshot.events) ? snapshot.events : []
33689
+ };
33690
+ }
33691
+ function dedupeConsecutiveSwitchActions(actions) {
33692
+ const deduped = [];
33693
+ for (const action of actions) {
33694
+ const previous = deduped[deduped.length - 1];
33695
+ if (previous?.kind === "switch-tab" && action.kind === "switch-tab" && previous.pageId === action.pageId) {
33696
+ continue;
33697
+ }
33698
+ deduped.push(action);
33699
+ }
33700
+ return deduped;
33701
+ }
33702
+ function actionSortPriority(kind) {
33703
+ switch (kind) {
33704
+ case "new-tab":
33705
+ return 0;
33706
+ case "switch-tab":
33707
+ return 1;
33708
+ case "navigate":
33709
+ case "go-back":
33710
+ case "go-forward":
33711
+ case "reload":
33712
+ return 2;
33713
+ case "click":
33714
+ case "dblclick":
33715
+ return 3;
33716
+ case "type":
33717
+ case "keypress":
33718
+ case "select-option":
33719
+ return 4;
33720
+ case "scroll":
33721
+ return 5;
33722
+ case "close-tab":
33723
+ return 6;
33724
+ }
33725
+ }
33726
+ function comparePageIds(left, right) {
33727
+ const leftMatch = /^page(\d+)$/u.exec(left);
33728
+ const rightMatch = /^page(\d+)$/u.exec(right);
33729
+ if (leftMatch && rightMatch) {
33730
+ return Number(leftMatch[1]) - Number(rightMatch[1]);
33731
+ }
33732
+ return left.localeCompare(right);
33733
+ }
33734
+ function delay(ms) {
33735
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
33736
+ }
33737
+
33738
+ // ../runtime-core/src/recorder/codegen.ts
33739
+ var DISPATCH_KEY_SCRIPT = String.raw`(key) => {
33740
+ const target = document.activeElement;
33741
+ if (!(target instanceof Element)) {
33742
+ throw new Error("No active element is available for key replay.");
33743
+ }
33744
+ const normalizedKey = String(key);
33745
+ const eventInit = {
33746
+ key: normalizedKey,
33747
+ code: normalizedKey,
33748
+ bubbles: true,
33749
+ cancelable: true,
33750
+ };
33751
+ target.dispatchEvent(new KeyboardEvent("keydown", eventInit));
33752
+ target.dispatchEvent(new KeyboardEvent("keyup", eventInit));
33753
+ }`;
33754
+ var SELECT_OPTION_SCRIPT = String.raw`(selector, value) => {
33755
+ const target = document.querySelector(String(selector));
33756
+ if (!(target instanceof HTMLSelectElement)) {
33757
+ throw new Error("Unable to find a <select> element for option replay.");
33758
+ }
33759
+ target.value = String(value);
33760
+ target.dispatchEvent(new Event("input", { bubbles: true }));
33761
+ target.dispatchEvent(new Event("change", { bubbles: true }));
33762
+ }`;
33763
+ var HISTORY_ACTION_SCRIPT = String.raw`(action) => {
33764
+ const normalized = String(action);
33765
+ if (normalized === "back") {
33766
+ history.back();
33767
+ return;
33768
+ }
33769
+ if (normalized === "forward") {
33770
+ history.forward();
33771
+ return;
33772
+ }
33773
+ if (normalized === "reload") {
33774
+ location.reload();
33775
+ return;
33776
+ }
33777
+ throw new Error("Unsupported history action: " + normalized);
33778
+ }`;
33779
+ var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
33780
+ window.scrollBy(Number(deltaX), Number(deltaY));
33781
+ }`;
33782
+ function generateReplayScript(options) {
33783
+ const replayTarget = resolveReplayTarget(options);
33784
+ const initialPages = orderInitialPages(resolveInitialPages(options));
33785
+ const activeInitialPageId = resolveActiveInitialPageId(options, initialPages);
33786
+ const initialPageId = initialPages[0]?.pageId ?? "page0";
33787
+ const lines = [
33788
+ `import { Opensteer } from "opensteer";`,
33789
+ ``,
33790
+ ...renderOpensteerBootstrap(replayTarget),
33791
+ ``,
33792
+ `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
33793
+ `let activePageRef: string | undefined = ${initialPageId};`,
33794
+ ``,
33795
+ `async function ensureActive(pageRef: string): Promise<void> {`,
33796
+ ` if (activePageRef === pageRef) {`,
33797
+ ` return;`,
33798
+ ` }`,
33799
+ ` await opensteer.activatePage({ pageRef });`,
33800
+ ` activePageRef = pageRef;`,
33801
+ `}`,
33802
+ ``,
33803
+ `async function dispatchKey(pageRef: string, key: string): Promise<void> {`,
33804
+ ` await ensureActive(pageRef);`,
33805
+ ` await opensteer.evaluate({`,
33806
+ ` pageRef,`,
33807
+ ` script: ${JSON.stringify(DISPATCH_KEY_SCRIPT)},`,
33808
+ ` args: [key],`,
33809
+ ` });`,
33810
+ `}`,
33811
+ ``,
33812
+ `async function selectOption(pageRef: string, selector: string, value: string): Promise<void> {`,
33813
+ ` await ensureActive(pageRef);`,
33814
+ ` await opensteer.evaluate({`,
33815
+ ` pageRef,`,
33816
+ ` script: ${JSON.stringify(SELECT_OPTION_SCRIPT)},`,
33817
+ ` args: [selector, value],`,
33818
+ ` });`,
33819
+ `}`,
33820
+ ``,
33821
+ `async function runHistoryAction(pageRef: string, action: "back" | "forward" | "reload"): Promise<void> {`,
33822
+ ` await ensureActive(pageRef);`,
33823
+ ` await opensteer.evaluate({`,
33824
+ ` pageRef,`,
33825
+ ` script: ${JSON.stringify(HISTORY_ACTION_SCRIPT)},`,
33826
+ ` args: [action],`,
33827
+ ` });`,
33828
+ `}`,
33829
+ ``,
33830
+ `try {`
33831
+ ];
33832
+ const declaredPages = /* @__PURE__ */ new Set([initialPageId]);
33833
+ for (const page of initialPages.slice(1)) {
33834
+ const openerPageId = page.openerPageId !== void 0 && declaredPages.has(page.openerPageId) ? page.openerPageId : void 0;
33835
+ lines.push(
33836
+ ` const ${page.pageId} = (await opensteer.newPage(${renderNewPageInput(openerPageId, page.initialUrl)})).pageRef;`
33837
+ );
33838
+ lines.push(` activePageRef = ${page.pageId};`);
33839
+ declaredPages.add(page.pageId);
33840
+ }
33841
+ if (activeInitialPageId !== void 0 && activeInitialPageId !== initialPageId) {
33842
+ lines.push(` await ensureActive(${activeInitialPageId});`);
33843
+ }
33844
+ for (let index = 0; index < options.actions.length; index += 1) {
33845
+ const action = options.actions[index];
33846
+ const pageVar = action.pageId;
33847
+ if (action.kind === "type") {
33848
+ const nextAction = options.actions[index + 1];
33849
+ const mergedPressEnter = nextAction?.kind === "keypress" && nextAction.pageId === action.pageId && nextAction.selector === action.selector && nextAction.detail.kind === "keypress" && nextAction.detail.key === "Enter" && nextAction.detail.modifiers.length === 0;
33850
+ lines.push(` await ensureActive(${pageVar});`);
33851
+ lines.push(
33852
+ ` await opensteer.input({ selector: ${JSON.stringify(requireSelector(action))}, text: ${JSON.stringify(action.detail.text)}${mergedPressEnter ? `, pressEnter: true` : ``} });`
33853
+ );
33854
+ if (mergedPressEnter) {
33855
+ index += 1;
33856
+ }
33857
+ continue;
33858
+ }
33859
+ if (action.kind === "switch-tab" && options.actions[index - 1]?.kind === "new-tab" && options.actions[index - 1]?.pageId === action.pageId) {
33860
+ continue;
33861
+ }
33862
+ switch (action.kind) {
33863
+ case "navigate":
33864
+ lines.push(` await ensureActive(${pageVar});`);
33865
+ lines.push(` await opensteer.goto(${JSON.stringify(action.detail.url)});`);
33866
+ break;
33867
+ case "click":
33868
+ lines.push(` await ensureActive(${pageVar});`);
33869
+ lines.push(
33870
+ ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
33871
+ );
33872
+ break;
33873
+ case "dblclick":
33874
+ lines.push(` await ensureActive(${pageVar});`);
33875
+ lines.push(
33876
+ ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))}, clickCount: 2 });`
33877
+ );
33878
+ break;
33879
+ case "keypress":
33880
+ lines.push(` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`);
33881
+ break;
33882
+ case "scroll": {
33883
+ const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
33884
+ lines.push(` await ensureActive(${pageVar});`);
33885
+ if (isWindowScroll) {
33886
+ lines.push(` await opensteer.evaluate({`);
33887
+ lines.push(` pageRef: ${pageVar},`);
33888
+ lines.push(` script: ${JSON.stringify(WINDOW_SCROLL_SCRIPT)},`);
33889
+ lines.push(
33890
+ ` args: [${String(action.detail.deltaX)}, ${String(action.detail.deltaY)}],`
33891
+ );
33892
+ lines.push(` });`);
33893
+ } else {
33894
+ lines.push(
33895
+ ` await opensteer.scroll({ selector: ${JSON.stringify(requireSelector(action))}, direction: ${JSON.stringify(direction)}, amount: ${String(amount)} });`
33896
+ );
33897
+ }
33898
+ break;
33899
+ }
33900
+ case "select-option":
33901
+ lines.push(
33902
+ ` await selectOption(${pageVar}, ${JSON.stringify(requireSelector(action))}, ${JSON.stringify(action.detail.value)});`
33903
+ );
33904
+ break;
33905
+ case "new-tab": {
33906
+ const openerPageVar = action.detail.openerPageId;
33907
+ const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
33908
+ const creationLine = shouldUseWaitForPage && openerPageVar !== void 0 ? ` const ${pageVar} = (await opensteer.waitForPage({ openerPageRef: ${openerPageVar}, timeoutMs: 30_000 })).pageRef;` : ` const ${pageVar} = (await opensteer.newPage(${renderNewPageInput(action.detail.openerPageId, action.detail.initialUrl)})).pageRef;`;
33909
+ lines.push(creationLine);
33910
+ lines.push(` activePageRef = ${pageVar};`);
33911
+ declaredPages.add(pageVar);
33912
+ break;
33913
+ }
33914
+ case "close-tab":
33915
+ lines.push(` await opensteer.closePage({ pageRef: ${pageVar} });`);
33916
+ lines.push(` if (activePageRef === ${pageVar}) {`);
33917
+ lines.push(` activePageRef = undefined;`);
33918
+ lines.push(` }`);
33919
+ break;
33920
+ case "switch-tab":
33921
+ lines.push(` await ensureActive(${pageVar});`);
33922
+ break;
33923
+ case "go-back":
33924
+ lines.push(` await runHistoryAction(${pageVar}, "back");`);
33925
+ break;
33926
+ case "go-forward":
33927
+ lines.push(` await runHistoryAction(${pageVar}, "forward");`);
33928
+ break;
33929
+ case "reload":
33930
+ lines.push(` await runHistoryAction(${pageVar}, "reload");`);
33931
+ break;
33932
+ }
33933
+ }
33934
+ lines.push(`} finally {`);
33935
+ lines.push(` await opensteer.close();`);
33936
+ lines.push(`}`);
33937
+ lines.push(``);
33938
+ return `${lines.join("\n")}
33939
+ `;
33940
+ }
33941
+ function resolveReplayTarget(options) {
33942
+ if (options.replayTarget !== void 0) {
33943
+ return options.replayTarget;
33944
+ }
33945
+ if (options.workspace !== void 0) {
33946
+ return {
33947
+ kind: "local",
33948
+ workspace: options.workspace
33949
+ };
33950
+ }
33951
+ throw new Error("Replay codegen requires either replayTarget or workspace.");
33952
+ }
33953
+ function resolveInitialPages(options) {
33954
+ if (options.initialPages !== void 0 && options.initialPages.length > 0) {
33955
+ const unique = /* @__PURE__ */ new Set();
33956
+ return options.initialPages.map((page) => {
33957
+ if (unique.has(page.pageId)) {
33958
+ throw new Error(`Duplicate initial page id "${page.pageId}" in recording bootstrap.`);
33959
+ }
33960
+ unique.add(page.pageId);
33961
+ return page;
33962
+ });
33963
+ }
33964
+ const startUrl = options.startUrl;
33965
+ if (startUrl === void 0) {
33966
+ throw new Error("Replay codegen requires startUrl when initialPages is not provided.");
33967
+ }
33968
+ return [
33969
+ {
33970
+ pageId: "page0",
33971
+ initialUrl: startUrl
33972
+ }
33973
+ ];
33974
+ }
33975
+ function resolveActiveInitialPageId(options, initialPages) {
33976
+ if (options.activePageId !== void 0) {
33977
+ return options.activePageId;
33978
+ }
33979
+ return initialPages[0]?.pageId;
33980
+ }
33981
+ function renderOpensteerBootstrap(replayTarget) {
33982
+ if (replayTarget.kind === "local") {
33983
+ return [
33984
+ `const opensteer = new Opensteer({`,
33985
+ ` workspace: ${JSON.stringify(replayTarget.workspace)},`,
33986
+ ` browser: "persistent",`,
33987
+ `});`
33988
+ ];
33989
+ }
33990
+ return [
33991
+ renderRequireEnvHelper(replayTarget),
33992
+ ``,
33993
+ `const opensteer = new Opensteer({`,
33994
+ ` provider: {`,
33995
+ ` mode: "cloud",`,
33996
+ ` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
33997
+ ` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
33998
+ ...renderCloudBrowserProfile(replayTarget),
33999
+ ` },`,
34000
+ `});`
34001
+ ];
34002
+ }
34003
+ function renderRequireEnvHelper(replayTarget) {
34004
+ const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
34005
+ const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
34006
+ return [
34007
+ `function requireEnv(name: string): string {`,
34008
+ ` const value = process.env[name];`,
34009
+ ` if (typeof value === "string" && value.trim().length > 0) {`,
34010
+ ` return value;`,
34011
+ ` }`,
34012
+ ` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
34013
+ `}`
34014
+ ].join("\n");
34015
+ }
34016
+ function renderCloudBrowserProfile(replayTarget) {
34017
+ if (replayTarget.browserProfileId === void 0) {
34018
+ return [];
34019
+ }
34020
+ return [
34021
+ ` browserProfile: {`,
34022
+ ` profileId: ${JSON.stringify(replayTarget.browserProfileId)},`,
34023
+ ...replayTarget.reuseBrowserProfileIfActive ? [` reuseIfActive: true,`] : [],
34024
+ ` },`
34025
+ ];
34026
+ }
34027
+ function orderInitialPages(initialPages) {
34028
+ const ordered = [];
34029
+ const declared = /* @__PURE__ */ new Set();
34030
+ const remaining = [...initialPages];
34031
+ while (remaining.length > 0) {
34032
+ let advanced = false;
34033
+ for (let index = 0; index < remaining.length; index += 1) {
34034
+ const page = remaining[index];
34035
+ if (page.openerPageId !== void 0 && !declared.has(page.openerPageId)) {
34036
+ continue;
34037
+ }
34038
+ ordered.push(page);
34039
+ declared.add(page.pageId);
34040
+ remaining.splice(index, 1);
34041
+ advanced = true;
34042
+ break;
34043
+ }
34044
+ if (!advanced) {
34045
+ ordered.push(...remaining.splice(0, remaining.length));
34046
+ }
34047
+ }
34048
+ return ordered;
34049
+ }
34050
+ function requireSelector(action) {
34051
+ if (action.selector === void 0) {
34052
+ throw new Error(`Action "${action.kind}" on ${action.pageId} is missing a selector.`);
34053
+ }
34054
+ return action.selector;
34055
+ }
34056
+ function normalizeScrollAction(action) {
34057
+ const horizontal = Math.abs(action.detail.deltaX);
34058
+ const vertical = Math.abs(action.detail.deltaY);
34059
+ if (vertical >= horizontal) {
34060
+ return {
34061
+ direction: action.detail.deltaY < 0 ? "up" : "down",
34062
+ amount: Math.max(1, Math.round(vertical)),
34063
+ isWindowScroll: action.selector === void 0
34064
+ };
34065
+ }
34066
+ return {
34067
+ direction: action.detail.deltaX < 0 ? "left" : "right",
34068
+ amount: Math.max(1, Math.round(horizontal)),
34069
+ isWindowScroll: action.selector === void 0
34070
+ };
34071
+ }
34072
+ function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
34073
+ if (openerPageId === void 0 || newTabIndex === 0) {
34074
+ return false;
34075
+ }
34076
+ const previousAction = actions[newTabIndex - 1];
34077
+ if (previousAction === void 0 || previousAction.pageId !== openerPageId) {
34078
+ return false;
34079
+ }
34080
+ return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
34081
+ }
34082
+ function renderNewPageInput(openerPageId, initialUrl) {
34083
+ const argumentsList = [];
34084
+ if (openerPageId !== void 0) {
34085
+ argumentsList.push(`openerPageRef: ${openerPageId}`);
34086
+ }
34087
+ if (initialUrl.length > 0 && initialUrl !== "about:blank") {
34088
+ argumentsList.push(`url: ${JSON.stringify(initialUrl)}`);
34089
+ }
34090
+ if (argumentsList.length === 0) {
34091
+ return `{}`;
34092
+ }
34093
+ return `{ ${argumentsList.join(", ")} }`;
34094
+ }
34095
+
32294
34096
  // src/sdk/runtime.ts
32295
34097
  var OpensteerRuntime = class extends OpensteerSessionRuntime {
32296
34098
  constructor(options = {}) {
@@ -32515,11 +34317,25 @@ function queryAllCookies(dbPath) {
32515
34317
  FROM cookies`
32516
34318
  );
32517
34319
  stmt.setReadBigInts(true);
32518
- return stmt.all();
34320
+ return stmt.all().map(toRawCookieRow);
32519
34321
  } finally {
32520
34322
  database.close();
32521
34323
  }
32522
34324
  }
34325
+ function toRawCookieRow(row) {
34326
+ return {
34327
+ host_key: row.host_key,
34328
+ name: row.name,
34329
+ value: row.value,
34330
+ encrypted_value: row.encrypted_value,
34331
+ path: row.path,
34332
+ expires_utc: row.expires_utc,
34333
+ is_secure: row.is_secure,
34334
+ is_httponly: row.is_httponly,
34335
+ samesite: row.samesite,
34336
+ is_persistent: row.is_persistent
34337
+ };
34338
+ }
32523
34339
  async function resolveDecryptionKey(brandId, userDataDir) {
32524
34340
  if (process.platform === "darwin") {
32525
34341
  const password = await resolveKeychainPassword(brandId);
@@ -32870,7 +34686,11 @@ var OpensteerCloudClient = class {
32870
34686
  ...input.browser === void 0 ? {} : { browser: input.browser },
32871
34687
  ...input.context === void 0 ? {} : { context: input.context },
32872
34688
  ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
32873
- ...input.observability === void 0 ? {} : { observability: input.observability }
34689
+ ...input.observability === void 0 ? {} : { observability: input.observability },
34690
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
34691
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
34692
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
34693
+ ...input.locality === void 0 ? {} : { locality: input.locality }
32874
34694
  }
32875
34695
  });
32876
34696
  return await response.json();
@@ -32896,6 +34716,30 @@ var OpensteerCloudClient = class {
32896
34716
  });
32897
34717
  return await response.json();
32898
34718
  }
34719
+ async getSessionRecording(sessionId) {
34720
+ const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/recording`, {
34721
+ method: "GET"
34722
+ });
34723
+ return await response.json();
34724
+ }
34725
+ async startSessionRecording(sessionId) {
34726
+ const response = await this.request(
34727
+ `/v1/sessions/${encodeURIComponent(sessionId)}/recording/start`,
34728
+ {
34729
+ method: "POST"
34730
+ }
34731
+ );
34732
+ return await response.json();
34733
+ }
34734
+ async stopSessionRecording(sessionId) {
34735
+ const response = await this.request(
34736
+ `/v1/sessions/${encodeURIComponent(sessionId)}/recording/stop`,
34737
+ {
34738
+ method: "POST"
34739
+ }
34740
+ );
34741
+ return await response.json();
34742
+ }
32899
34743
  async closeSession(sessionId) {
32900
34744
  const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
32901
34745
  method: "DELETE"
@@ -33042,12 +34886,12 @@ var OpensteerCloudClient = class {
33042
34886
  `Unexpected cloud session status "${String(session.status)}" while waiting for close.`
33043
34887
  );
33044
34888
  }
33045
- await delay(CLOUD_CLOSE_POLL_INTERVAL_MS);
34889
+ await delay2(CLOUD_CLOSE_POLL_INTERVAL_MS);
33046
34890
  }
33047
34891
  throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
33048
34892
  }
33049
34893
  };
33050
- function delay(ms) {
34894
+ function delay2(ms) {
33051
34895
  return new Promise((resolve4) => {
33052
34896
  setTimeout(resolve4, ms);
33053
34897
  });
@@ -33086,9 +34930,11 @@ function resolveCloudConfig(input = {}) {
33086
34930
  if (!baseUrl || baseUrl.trim().length === 0) {
33087
34931
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
33088
34932
  }
34933
+ const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
33089
34934
  return {
33090
34935
  apiKey: apiKey.trim(),
33091
34936
  baseUrl: baseUrl.trim().replace(/\/+$/, ""),
34937
+ ...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
33092
34938
  ...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
33093
34939
  };
33094
34940
  }
@@ -33107,6 +34953,9 @@ var OpensteerSemanticRestClient = class {
33107
34953
  this.connection = connection;
33108
34954
  }
33109
34955
  async invoke(operation, input) {
34956
+ return this.invokeInternal(operation, input, false);
34957
+ }
34958
+ async invokeInternal(operation, input, hasRetried) {
33110
34959
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
33111
34960
  if (!endpoint) {
33112
34961
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -33116,7 +34965,7 @@ var OpensteerSemanticRestClient = class {
33116
34965
  });
33117
34966
  let response;
33118
34967
  try {
33119
- response = await fetch(`${this.connection.baseUrl}${endpoint.path}`, {
34968
+ response = await fetch(`${await this.connection.getBaseUrl()}${endpoint.path}`, {
33120
34969
  method: "POST",
33121
34970
  headers: {
33122
34971
  authorization: await this.connection.getAuthorizationHeader(),
@@ -33138,6 +34987,9 @@ var OpensteerSemanticRestClient = class {
33138
34987
  }
33139
34988
  return envelope.data;
33140
34989
  } catch (error) {
34990
+ if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
34991
+ return this.invokeInternal(operation, input, true);
34992
+ }
33141
34993
  if (operation === "session.close" && isFetchFailure(error)) {
33142
34994
  return { closed: true };
33143
34995
  }
@@ -33271,7 +35123,10 @@ var OpensteerCloudAutomationClient = class {
33271
35123
  }
33272
35124
  async connect() {
33273
35125
  const grant = await this.issueGrant("automation");
33274
- const wsUrl = new URL(grant.wsUrl);
35126
+ if (grant.transport !== "ws") {
35127
+ throw new Error(`cloud issued an invalid ${grant.kind} grant transport`);
35128
+ }
35129
+ const wsUrl = new URL(grant.url);
33275
35130
  wsUrl.searchParams.set("token", grant.token);
33276
35131
  const socket = new WebSocket2(wsUrl);
33277
35132
  this.socket = socket;
@@ -33636,7 +35491,7 @@ var CloudSessionProxy = class {
33636
35491
  cloud;
33637
35492
  observability;
33638
35493
  sessionId;
33639
- sessionBaseUrl;
35494
+ semanticGrant;
33640
35495
  client;
33641
35496
  automation;
33642
35497
  workspaceStore;
@@ -33687,7 +35542,7 @@ var CloudSessionProxy = class {
33687
35542
  reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
33688
35543
  capabilities: {
33689
35544
  semanticOperations: opensteerSemanticOperationNames,
33690
- sessionGrants: ["automation", "view", "cdp"],
35545
+ sessionGrants: ["semantic", "automation", "view", "cdp"],
33691
35546
  instrumentation: {
33692
35547
  route: true,
33693
35548
  interceptScript: true,
@@ -33929,13 +35784,12 @@ var CloudSessionProxy = class {
33929
35784
  return this.requireClient().invoke("computer.execute", input);
33930
35785
  }
33931
35786
  async close() {
33932
- const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 || this.sessionBaseUrl === void 0 ? void 0 : {
35787
+ const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
33933
35788
  layout: "opensteer-session",
33934
35789
  version: 1,
33935
35790
  provider: "cloud",
33936
35791
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
33937
35792
  sessionId: this.sessionId,
33938
- baseUrl: this.sessionBaseUrl,
33939
35793
  startedAt: Date.now(),
33940
35794
  updatedAt: Date.now()
33941
35795
  });
@@ -33954,7 +35808,7 @@ var CloudSessionProxy = class {
33954
35808
  this.automation = void 0;
33955
35809
  this.client = void 0;
33956
35810
  this.sessionId = void 0;
33957
- this.sessionBaseUrl = void 0;
35811
+ this.semanticGrant = void 0;
33958
35812
  if (this.cleanupRootOnClose) {
33959
35813
  await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
33960
35814
  }
@@ -33970,39 +35824,56 @@ var CloudSessionProxy = class {
33970
35824
  await this.automation?.close().catch(() => void 0);
33971
35825
  this.automation = void 0;
33972
35826
  this.sessionId = void 0;
33973
- this.sessionBaseUrl = void 0;
35827
+ this.semanticGrant = void 0;
33974
35828
  }
33975
35829
  async ensureSession(input = {}) {
33976
35830
  if (this.client) {
33977
35831
  return;
33978
35832
  }
33979
35833
  assertSupportedCloudBrowserMode(input.browser);
35834
+ const localCloud = this.shouldUseLocalCloudTransport();
35835
+ const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
33980
35836
  const persisted = await this.loadPersistedSession();
33981
35837
  if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
33982
- await this.syncRegistryToCloud();
35838
+ if (localCloud) {
35839
+ void this.syncRegistryToCloud();
35840
+ } else {
35841
+ await this.syncRegistryToCloud();
35842
+ }
33983
35843
  this.bindClient(persisted);
33984
35844
  return;
33985
35845
  }
33986
- await this.syncRegistryToCloud();
33987
- const session = await this.cloud.createSession({
35846
+ if (localCloud) {
35847
+ void this.syncRegistryToCloud();
35848
+ } else {
35849
+ await this.syncRegistryToCloud();
35850
+ }
35851
+ const baseCreateInput = {
33988
35852
  ...this.workspace === void 0 ? {} : { name: this.workspace },
33989
35853
  ...input.launch === void 0 ? {} : { browser: input.launch },
33990
35854
  ...input.context === void 0 ? {} : { context: input.context },
33991
35855
  ...this.observability === void 0 ? {} : { observability: this.observability },
33992
- ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
33993
- });
35856
+ ...browserProfile === void 0 ? {} : { browserProfile }
35857
+ };
35858
+ const createInput = localCloud && this.workspace !== void 0 ? {
35859
+ ...baseCreateInput,
35860
+ sourceType: "local-cloud",
35861
+ sourceRef: this.workspace,
35862
+ localWorkspaceRootPath: this.rootPath,
35863
+ locality: "auto"
35864
+ } : baseCreateInput;
35865
+ const session = await this.cloud.createSession(createInput);
33994
35866
  const record = {
33995
35867
  layout: "opensteer-session",
33996
35868
  version: 1,
33997
35869
  provider: "cloud",
33998
35870
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
33999
35871
  sessionId: session.sessionId,
34000
- baseUrl: session.baseUrl,
34001
35872
  startedAt: Date.now(),
34002
35873
  updatedAt: Date.now()
34003
35874
  };
34004
35875
  await this.writePersistedSession(record);
34005
- this.bindClient(record);
35876
+ this.bindClient(record, session.initialGrants?.semantic);
34006
35877
  }
34007
35878
  async syncRegistryToCloud() {
34008
35879
  if (this.workspace === void 0) {
@@ -34011,15 +35882,16 @@ var CloudSessionProxy = class {
34011
35882
  try {
34012
35883
  const workspaceStore = await this.ensureWorkspaceStore();
34013
35884
  await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
34014
- } catch (error) {
35885
+ } catch {
34015
35886
  }
34016
35887
  }
34017
- bindClient(record) {
35888
+ bindClient(record, initialSemanticGrant) {
34018
35889
  this.sessionId = record.sessionId;
34019
- this.sessionBaseUrl = record.baseUrl;
35890
+ this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
34020
35891
  this.client = new OpensteerSemanticRestClient({
34021
- baseUrl: record.baseUrl,
34022
- getAuthorizationHeader: async () => this.cloud.buildAuthorizationHeader()
35892
+ getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
35893
+ getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
35894
+ handleError: (error) => this.handleSemanticClientError(error)
34023
35895
  });
34024
35896
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
34025
35897
  }
@@ -34068,6 +35940,43 @@ var CloudSessionProxy = class {
34068
35940
  }
34069
35941
  return this.automation;
34070
35942
  }
35943
+ async ensureSemanticGrant(forceRefresh = false) {
35944
+ if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
35945
+ return this.semanticGrant;
35946
+ }
35947
+ if (!this.sessionId) {
35948
+ throw new Error("Cloud session has not been initialized.");
35949
+ }
35950
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
35951
+ const grant = issued.grants.semantic;
35952
+ if (!grant || grant.transport !== "http") {
35953
+ throw new Error("cloud did not issue a valid semantic grant");
35954
+ }
35955
+ this.semanticGrant = grant;
35956
+ return grant;
35957
+ }
35958
+ async handleSemanticClientError(error) {
35959
+ if (!(error instanceof OpensteerSemanticRestError)) {
35960
+ return false;
35961
+ }
35962
+ if (error.statusCode !== 401 && error.statusCode !== 404) {
35963
+ return false;
35964
+ }
35965
+ this.semanticGrant = void 0;
35966
+ try {
35967
+ await this.ensureSemanticGrant(true);
35968
+ return true;
35969
+ } catch {
35970
+ return false;
35971
+ }
35972
+ }
35973
+ shouldUseLocalCloudTransport() {
35974
+ if (this.workspace === void 0) {
35975
+ return false;
35976
+ }
35977
+ const config = this.cloud.getConfig();
35978
+ return isLoopbackBaseUrl(config.baseUrl);
35979
+ }
34071
35980
  };
34072
35981
  function resolveCloudBrowserProfile(cloud, input) {
34073
35982
  return input.browserProfile ?? cloud.getConfig().browserProfile;
@@ -34083,6 +35992,15 @@ function assertSupportedCloudBrowserMode(browser) {
34083
35992
  function isMissingCloudSessionError(error) {
34084
35993
  return error instanceof Error && /\b404\b/.test(error.message);
34085
35994
  }
35995
+ function isLoopbackBaseUrl(baseUrl) {
35996
+ let url;
35997
+ try {
35998
+ url = new URL(baseUrl);
35999
+ } catch {
36000
+ return false;
36001
+ }
36002
+ return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
36003
+ }
34086
36004
 
34087
36005
  // src/sdk/runtime-resolution.ts
34088
36006
  function resolveOpensteerRuntimeConfig(input = {}) {
@@ -34227,6 +36145,6 @@ function isOpensteerEnvironmentKey(key) {
34227
36145
  return key.startsWith(OPENSTEER_ENV_PREFIX);
34228
36146
  }
34229
36147
 
34230
- export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
34231
- //# sourceMappingURL=chunk-RO6WAWWG.js.map
34232
- //# sourceMappingURL=chunk-RO6WAWWG.js.map
36148
+ export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomDescriptorStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
36149
+ //# sourceMappingURL=chunk-33FDEOQY.js.map
36150
+ //# sourceMappingURL=chunk-33FDEOQY.js.map