opensteer 0.8.10 → 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,16 +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
- url: mainFrame.url
10811
- };
10815
+ return engine.getActionBoundarySnapshot({ pageRef });
10812
10816
  }
10813
10817
  function createActionBoundaryDiagnostics(input) {
10814
10818
  return {
@@ -23607,6 +23611,9 @@ var OpensteerSessionRuntime = class {
23607
23611
  const result = await this.requireDom().click({
23608
23612
  pageRef,
23609
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 },
23610
23617
  timeout
23611
23618
  });
23612
23619
  return {
@@ -32326,7 +32333,11 @@ function screenshotMediaType(format2) {
32326
32333
  var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
32327
32334
  /* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
32328
32335
  );
32329
- var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32336
+ function createFlowRecorderInstallScript(options = {}) {
32337
+ const normalizedOptions = {
32338
+ showStopButton: options.showStopButton ?? true
32339
+ };
32340
+ return String.raw`(() => {
32330
32341
  const TOP_LEVEL_ONLY = (() => {
32331
32342
  try {
32332
32343
  return window.top === window.self;
@@ -32351,6 +32362,7 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32351
32362
  const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
32352
32363
  const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
32353
32364
  const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
32365
+ const options = ${JSON.stringify(normalizedOptions)};
32354
32366
 
32355
32367
  const previous = globalScope[recorderKey];
32356
32368
  if (previous && typeof previous.dispose === "function") {
@@ -32361,6 +32373,7 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32361
32373
  const cleanup = [];
32362
32374
  const inputFlushTimers = new Map();
32363
32375
  const pendingInputs = new Map();
32376
+ let historyStateCache = undefined;
32364
32377
  let stopRequested = false;
32365
32378
  let pendingWheel = undefined;
32366
32379
 
@@ -32681,44 +32694,76 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32681
32694
  inputFlushTimers.set(selector, timerId);
32682
32695
  }
32683
32696
 
32684
- function updateHistoryState(mode, nextUrl) {
32685
- let state;
32686
- try {
32687
- const raw = sessionStorage.getItem(historyStateKey);
32688
- state = raw ? JSON.parse(raw) : undefined;
32689
- } catch {
32690
- state = undefined;
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;
32691
32712
  }
32692
- if (!state || !Array.isArray(state.entries) || typeof state.index !== "number") {
32693
- state = { entries: [location.href], index: 0 };
32713
+ const entries = state.entries.slice();
32714
+ if (entries.length === 0) {
32715
+ return createDefaultHistoryState(location.href);
32694
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
+ };
32695
32738
  if (mode === "replace") {
32696
- state.entries[state.index] = nextUrl;
32739
+ nextState.entries[nextState.index] = nextUrl;
32697
32740
  } else if (mode === "push") {
32698
- state.entries = state.entries.slice(0, state.index + 1);
32699
- state.entries.push(nextUrl);
32700
- state.index = state.entries.length - 1;
32741
+ nextState.entries = nextState.entries.slice(0, nextState.index + 1);
32742
+ nextState.entries.push(nextUrl);
32743
+ nextState.index = nextState.entries.length - 1;
32701
32744
  } else if (mode === "back") {
32702
- state.index = Math.max(0, state.index - 1);
32745
+ nextState.index = Math.max(0, nextState.index - 1);
32703
32746
  } else if (mode === "forward") {
32704
- state.index = Math.min(state.entries.length - 1, state.index + 1);
32747
+ nextState.index = Math.min(nextState.entries.length - 1, nextState.index + 1);
32705
32748
  }
32706
- try {
32707
- sessionStorage.setItem(historyStateKey, JSON.stringify(state));
32708
- } catch {}
32709
- return state;
32749
+ return nextState;
32750
+ }
32751
+
32752
+ function updateHistoryState(mode, nextUrl) {
32753
+ return writeHistoryState(applyHistoryState(readHistoryState(), mode, nextUrl));
32710
32754
  }
32711
32755
 
32712
32756
  function readHistoryState() {
32757
+ const cached = normalizeHistoryState(historyStateCache);
32758
+ if (cached) {
32759
+ historyStateCache = cached;
32760
+ return cached;
32761
+ }
32713
32762
  try {
32714
32763
  const raw = sessionStorage.getItem(historyStateKey);
32715
- const parsed = raw ? JSON.parse(raw) : undefined;
32716
- if (
32717
- parsed &&
32718
- Array.isArray(parsed.entries) &&
32719
- parsed.entries.every((entry) => typeof entry === "string") &&
32720
- typeof parsed.index === "number"
32721
- ) {
32764
+ const parsed = normalizeHistoryState(raw ? JSON.parse(raw) : undefined);
32765
+ if (parsed) {
32766
+ historyStateCache = parsed;
32722
32767
  return parsed;
32723
32768
  }
32724
32769
  } catch {}
@@ -32730,28 +32775,30 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32730
32775
  if (!state) {
32731
32776
  return undefined;
32732
32777
  }
32778
+ if (state.entries[state.index] === currentUrl) {
32779
+ return undefined;
32780
+ }
32733
32781
  if (state.entries[state.index - 1] === currentUrl) {
32734
- updateHistoryState("back", currentUrl);
32782
+ writeHistoryState({
32783
+ entries: state.entries,
32784
+ index: state.index - 1,
32785
+ });
32735
32786
  return "go-back";
32736
32787
  }
32737
32788
  if (state.entries[state.index + 1] === currentUrl) {
32738
- updateHistoryState("forward", currentUrl);
32789
+ writeHistoryState({
32790
+ entries: state.entries,
32791
+ index: state.index + 1,
32792
+ });
32739
32793
  return "go-forward";
32740
32794
  }
32741
32795
  const existingIndex = state.entries.lastIndexOf(currentUrl);
32742
- if (existingIndex !== -1) {
32743
- if (existingIndex < state.index) {
32744
- while (readHistoryState()?.index > existingIndex) {
32745
- updateHistoryState("back", currentUrl);
32746
- }
32747
- return "go-back";
32748
- }
32749
- if (existingIndex > state.index) {
32750
- while (readHistoryState()?.index < existingIndex) {
32751
- updateHistoryState("forward", currentUrl);
32752
- }
32753
- return "go-forward";
32754
- }
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";
32755
32802
  }
32756
32803
  return undefined;
32757
32804
  }
@@ -32878,10 +32925,6 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
32878
32925
  " outline: 2px solid #2563eb;",
32879
32926
  " outline-offset: 2px;",
32880
32927
  "}",
32881
- "button:disabled {",
32882
- " cursor: wait;",
32883
- " opacity: 0.8;",
32884
- "}",
32885
32928
  ].join("\n");
32886
32929
  const button = document.createElement("button");
32887
32930
  button.type = "button";
@@ -33114,7 +33157,8 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33114
33157
  });
33115
33158
  return;
33116
33159
  }
33117
- if (readHistoryState()?.entries[readHistoryState().index] !== currentUrl) {
33160
+ const state = readHistoryState();
33161
+ if (state?.entries[state.index] !== currentUrl) {
33118
33162
  updateHistoryState("push", currentUrl);
33119
33163
  enqueue({
33120
33164
  kind: "navigate",
@@ -33168,10 +33212,13 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33168
33212
  },
33169
33213
  };
33170
33214
 
33171
- ensureStopButtonMounted();
33215
+ if (options.showStopButton) {
33216
+ ensureStopButtonMounted();
33217
+ }
33172
33218
  onInstall();
33173
33219
  })();`;
33174
- var FLOW_RECORDER_INSTALL_SCRIPT = FLOW_RECORDER_INSTALL_SOURCE;
33220
+ }
33221
+ var FLOW_RECORDER_INSTALL_SCRIPT = createFlowRecorderInstallScript();
33175
33222
  var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
33176
33223
  const recorder = globalThis.__opensteerFlowRecorder;
33177
33224
  if (!recorder || typeof recorder.drain !== "function") {
@@ -33191,6 +33238,7 @@ var FlowRecorderCollector = class {
33191
33238
  runtime;
33192
33239
  pollIntervalMs;
33193
33240
  onAction;
33241
+ installScript;
33194
33242
  pages = /* @__PURE__ */ new Map();
33195
33243
  actions = [];
33196
33244
  nextPageOrdinal = 0;
@@ -33203,10 +33251,11 @@ var FlowRecorderCollector = class {
33203
33251
  this.runtime = runtime;
33204
33252
  this.pollIntervalMs = options.pollIntervalMs ?? 250;
33205
33253
  this.onAction = options.onAction;
33254
+ this.installScript = options.installScript ?? FLOW_RECORDER_INSTALL_SCRIPT;
33206
33255
  }
33207
33256
  async install() {
33208
33257
  await this.runtime.addInitScript({
33209
- script: FLOW_RECORDER_INSTALL_SCRIPT
33258
+ script: this.installScript
33210
33259
  });
33211
33260
  const { pages } = await this.runtime.listPages();
33212
33261
  for (const page of pages) {
@@ -33215,7 +33264,7 @@ var FlowRecorderCollector = class {
33215
33264
  await Promise.all(
33216
33265
  pages.map(
33217
33266
  (page) => this.runtime.evaluate({
33218
- script: FLOW_RECORDER_INSTALL_SCRIPT,
33267
+ script: this.installScript,
33219
33268
  pageRef: page.pageRef
33220
33269
  }).catch(() => void 0)
33221
33270
  )
@@ -33247,6 +33296,18 @@ var FlowRecorderCollector = class {
33247
33296
  getActions() {
33248
33297
  return this.actions.slice();
33249
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
+ }
33250
33311
  async waitForStop() {
33251
33312
  if (this.stopDetected) {
33252
33313
  return;
@@ -33343,10 +33404,17 @@ var FlowRecorderCollector = class {
33343
33404
  }
33344
33405
  for (const listedPage of input.listedPages) {
33345
33406
  if (!input.previousPageRefs.has(listedPage.pageRef)) {
33346
- const created = this.ensureKnownPage(listedPage.pageRef, listedPage.url, listedPage.openerPageRef);
33407
+ const created = this.ensureKnownPage(
33408
+ listedPage.pageRef,
33409
+ listedPage.url,
33410
+ listedPage.openerPageRef
33411
+ );
33347
33412
  actions.push({
33348
33413
  kind: "new-tab",
33349
- timestamp: Math.max(0, (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1),
33414
+ timestamp: Math.max(
33415
+ 0,
33416
+ (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1
33417
+ ),
33350
33418
  pageId: created.pageId,
33351
33419
  pageUrl: listedPage.url,
33352
33420
  detail: {
@@ -33400,7 +33468,11 @@ var FlowRecorderCollector = class {
33400
33468
  (event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
33401
33469
  )
33402
33470
  );
33403
- this.updateKnownPage(evaluatedPage.pageRef, evaluatedPage.currentUrl, evaluatedPage.openerPageRef);
33471
+ this.updateKnownPage(
33472
+ evaluatedPage.pageRef,
33473
+ evaluatedPage.currentUrl,
33474
+ evaluatedPage.openerPageRef
33475
+ );
33404
33476
  }
33405
33477
  const focusedPage = input.evaluatedPages.find((page) => page.focused);
33406
33478
  if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
@@ -33651,6 +33723,14 @@ function actionSortPriority(kind) {
33651
33723
  return 6;
33652
33724
  }
33653
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
+ }
33654
33734
  function delay(ms) {
33655
33735
  return new Promise((resolve4) => setTimeout(resolve4, ms));
33656
33736
  }
@@ -33700,17 +33780,16 @@ var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
33700
33780
  window.scrollBy(Number(deltaX), Number(deltaY));
33701
33781
  }`;
33702
33782
  function generateReplayScript(options) {
33703
- const pageIds = collectPageIds(options.actions);
33704
- const initialPageId = pageIds[0] ?? "page0";
33783
+ const replayTarget = resolveReplayTarget(options);
33784
+ const initialPages = orderInitialPages(resolveInitialPages(options));
33785
+ const activeInitialPageId = resolveActiveInitialPageId(options, initialPages);
33786
+ const initialPageId = initialPages[0]?.pageId ?? "page0";
33705
33787
  const lines = [
33706
33788
  `import { Opensteer } from "opensteer";`,
33707
33789
  ``,
33708
- `const opensteer = new Opensteer({`,
33709
- ` workspace: ${JSON.stringify(options.workspace)},`,
33710
- ` browser: "persistent",`,
33711
- `});`,
33790
+ ...renderOpensteerBootstrap(replayTarget),
33712
33791
  ``,
33713
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(options.startUrl)})).pageRef;`,
33792
+ `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
33714
33793
  `let activePageRef: string | undefined = ${initialPageId};`,
33715
33794
  ``,
33716
33795
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -33751,6 +33830,17 @@ function generateReplayScript(options) {
33751
33830
  `try {`
33752
33831
  ];
33753
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
+ }
33754
33844
  for (let index = 0; index < options.actions.length; index += 1) {
33755
33845
  const action = options.actions[index];
33756
33846
  const pageVar = action.pageId;
@@ -33783,16 +33873,11 @@ function generateReplayScript(options) {
33783
33873
  case "dblclick":
33784
33874
  lines.push(` await ensureActive(${pageVar});`);
33785
33875
  lines.push(
33786
- ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
33787
- );
33788
- lines.push(
33789
- ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
33876
+ ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))}, clickCount: 2 });`
33790
33877
  );
33791
33878
  break;
33792
33879
  case "keypress":
33793
- lines.push(
33794
- ` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`
33795
- );
33880
+ lines.push(` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`);
33796
33881
  break;
33797
33882
  case "scroll": {
33798
33883
  const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
@@ -33820,7 +33905,7 @@ function generateReplayScript(options) {
33820
33905
  case "new-tab": {
33821
33906
  const openerPageVar = action.detail.openerPageId;
33822
33907
  const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
33823
- const creationLine = shouldUseWaitForPage && openerPageVar !== void 0 ? ` const ${pageVar} = (await opensteer.waitForPage({ openerPageRef: ${openerPageVar}, timeoutMs: 30_000 })).pageRef;` : ` const ${pageVar} = (await opensteer.newPage({${renderNewPageArguments(action.detail.openerPageId, action.detail.initialUrl)}})).pageRef;`;
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;`;
33824
33909
  lines.push(creationLine);
33825
33910
  lines.push(` activePageRef = ${pageVar};`);
33826
33911
  declaredPages.add(pageVar);
@@ -33853,29 +33938,114 @@ function generateReplayScript(options) {
33853
33938
  return `${lines.join("\n")}
33854
33939
  `;
33855
33940
  }
33856
- function collectPageIds(actions) {
33857
- const orderedIds = /* @__PURE__ */ new Set(["page0"]);
33858
- for (const action of actions) {
33859
- orderedIds.add(action.pageId);
33860
- if (action.kind === "new-tab" && action.detail.openerPageId !== void 0) {
33861
- orderedIds.add(action.detail.openerPageId);
33862
- }
33863
- if (action.kind === "switch-tab" && action.detail.fromPageId !== void 0) {
33864
- orderedIds.add(action.detail.fromPageId);
33865
- }
33866
- if (action.kind === "switch-tab") {
33867
- orderedIds.add(action.detail.toPageId);
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
33868
33972
  }
33973
+ ];
33974
+ }
33975
+ function resolveActiveInitialPageId(options, initialPages) {
33976
+ if (options.activePageId !== void 0) {
33977
+ return options.activePageId;
33869
33978
  }
33870
- return [...orderedIds].sort(comparePageIds);
33979
+ return initialPages[0]?.pageId;
33871
33980
  }
33872
- function comparePageIds(left, right) {
33873
- const leftMatch = /^page(\d+)$/u.exec(left);
33874
- const rightMatch = /^page(\d+)$/u.exec(right);
33875
- if (leftMatch && rightMatch) {
33876
- return Number(leftMatch[1]) - Number(rightMatch[1]);
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
+ ];
33877
33989
  }
33878
- return left.localeCompare(right);
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;
33879
34049
  }
33880
34050
  function requireSelector(action) {
33881
34051
  if (action.selector === void 0) {
@@ -33909,18 +34079,18 @@ function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
33909
34079
  }
33910
34080
  return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
33911
34081
  }
33912
- function renderNewPageArguments(openerPageId, initialUrl) {
34082
+ function renderNewPageInput(openerPageId, initialUrl) {
33913
34083
  const argumentsList = [];
33914
34084
  if (openerPageId !== void 0) {
33915
- argumentsList.push(` openerPageRef: ${openerPageId}`);
34085
+ argumentsList.push(`openerPageRef: ${openerPageId}`);
33916
34086
  }
33917
34087
  if (initialUrl.length > 0 && initialUrl !== "about:blank") {
33918
- argumentsList.push(` url: ${JSON.stringify(initialUrl)}`);
34088
+ argumentsList.push(`url: ${JSON.stringify(initialUrl)}`);
33919
34089
  }
33920
34090
  if (argumentsList.length === 0) {
33921
- return ``;
34091
+ return `{}`;
33922
34092
  }
33923
- return ` ${argumentsList.join(",")}`.trimStart();
34093
+ return `{ ${argumentsList.join(", ")} }`;
33924
34094
  }
33925
34095
 
33926
34096
  // src/sdk/runtime.ts
@@ -34546,6 +34716,30 @@ var OpensteerCloudClient = class {
34546
34716
  });
34547
34717
  return await response.json();
34548
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
+ }
34549
34743
  async closeSession(sessionId) {
34550
34744
  const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
34551
34745
  method: "DELETE"
@@ -34736,9 +34930,11 @@ function resolveCloudConfig(input = {}) {
34736
34930
  if (!baseUrl || baseUrl.trim().length === 0) {
34737
34931
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
34738
34932
  }
34933
+ const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
34739
34934
  return {
34740
34935
  apiKey: apiKey.trim(),
34741
34936
  baseUrl: baseUrl.trim().replace(/\/+$/, ""),
34937
+ ...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
34742
34938
  ...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
34743
34939
  };
34744
34940
  }
@@ -35807,18 +36003,6 @@ function isLoopbackBaseUrl(baseUrl) {
35807
36003
  }
35808
36004
 
35809
36005
  // src/sdk/runtime-resolution.ts
35810
- var LOCAL_ONLY_RUNTIME_OPTION_KEYS = [
35811
- "launch",
35812
- "context",
35813
- "engine",
35814
- "engineFactory",
35815
- "policy",
35816
- "descriptorStore",
35817
- "extractionDescriptorStore",
35818
- "registryOverrides",
35819
- "observationSessionId",
35820
- "observationSink"
35821
- ];
35822
36006
  function resolveOpensteerRuntimeConfig(input = {}) {
35823
36007
  const environment = input.environment ?? process.env;
35824
36008
  const provider = resolveOpensteerProvider({
@@ -35843,15 +36027,8 @@ function createOpensteerSemanticRuntime(input = {}) {
35843
36027
  ...input.provider === void 0 ? {} : { provider: input.provider },
35844
36028
  ...input.environment === void 0 ? {} : { environment: input.environment }
35845
36029
  });
35846
- const localOnlyRuntimeOptions = listLocalOnlyRuntimeOptions(runtimeOptions);
35847
- const providerMode = config.provider.mode === "cloud" && config.provider.source !== "explicit" && localOnlyRuntimeOptions.length > 0 ? "local" : config.provider.mode;
35848
- if (config.provider.mode === "cloud" && config.provider.source === "explicit" && localOnlyRuntimeOptions.length > 0) {
35849
- throw new Error(
35850
- `provider=cloud does not support local runtime options: ${localOnlyRuntimeOptions.join(", ")}.`
35851
- );
35852
- }
35853
- assertProviderSupportsEngine(providerMode, engine);
35854
- if (providerMode === "cloud") {
36030
+ assertProviderSupportsEngine(config.provider.mode, engine);
36031
+ if (config.provider.mode === "cloud") {
35855
36032
  return new CloudSessionProxy(new OpensteerCloudClient(config.cloud), {
35856
36033
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
35857
36034
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
@@ -35865,9 +36042,6 @@ function createOpensteerSemanticRuntime(input = {}) {
35865
36042
  engineName: engine
35866
36043
  });
35867
36044
  }
35868
- function listLocalOnlyRuntimeOptions(runtimeOptions) {
35869
- return LOCAL_ONLY_RUNTIME_OPTION_KEYS.filter((key) => runtimeOptions[key] !== void 0);
35870
- }
35871
36045
  var ENV_FILENAMES = [".env", ".env.local"];
35872
36046
  var OPENSTEER_ENV_PREFIX = "OPENSTEER_";
35873
36047
  var opensteerEnvironmentCache = /* @__PURE__ */ new Map();
@@ -35972,5 +36146,5 @@ function isOpensteerEnvironmentKey(key) {
35972
36146
  }
35973
36147
 
35974
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 };
35975
- //# sourceMappingURL=chunk-F3X6UOEN.js.map
35976
- //# sourceMappingURL=chunk-F3X6UOEN.js.map
36149
+ //# sourceMappingURL=chunk-33FDEOQY.js.map
36150
+ //# sourceMappingURL=chunk-33FDEOQY.js.map