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.
package/dist/cli/bin.cjs CHANGED
@@ -59,7 +59,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
59
59
 
60
60
  // package.json
61
61
  var package_default = {
62
- version: "0.8.9"};
62
+ version: "0.8.10"};
63
63
  util.promisify(child_process.execFile);
64
64
  Math.floor(Date.now() - process.uptime() * 1e3);
65
65
  ({ ...process.env});
@@ -2283,6 +2283,9 @@ function isBrowserCoreError(value) {
2283
2283
  // ../browser-core/src/cdp-visual-stability.ts
2284
2284
  var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
2285
2285
 
2286
+ // ../browser-core/src/post-load-tracker.ts
2287
+ var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
2288
+
2286
2289
  // ../protocol/src/identity.ts
2287
2290
  var refPrefixes = [
2288
2291
  "session",
@@ -7472,9 +7475,23 @@ var opensteerInspectStorageInputSchema = objectSchema(
7472
7475
  title: "OpensteerInspectStorageInput"
7473
7476
  }
7474
7477
  );
7478
+ var opensteerComputerMouseButtonSchema = enumSchema(["left", "middle", "right"], {
7479
+ title: "OpensteerComputerMouseButton"
7480
+ });
7481
+ var opensteerComputerKeyModifierSchema = enumSchema(
7482
+ ["Shift", "Control", "Alt", "Meta"],
7483
+ {
7484
+ title: "OpensteerComputerKeyModifier"
7485
+ }
7486
+ );
7475
7487
  var opensteerDomClickInputSchema = objectSchema(
7476
7488
  {
7477
7489
  target: opensteerTargetInputSchema,
7490
+ button: opensteerComputerMouseButtonSchema,
7491
+ clickCount: integerSchema({ minimum: 1 }),
7492
+ modifiers: arraySchema(opensteerComputerKeyModifierSchema, {
7493
+ uniqueItems: true
7494
+ }),
7478
7495
  persistAsDescription: stringSchema(),
7479
7496
  captureNetwork: stringSchema({ minLength: 1 })
7480
7497
  },
@@ -7574,18 +7591,6 @@ var opensteerSessionCloseOutputSchema = objectSchema(
7574
7591
  required: ["closed"]
7575
7592
  }
7576
7593
  );
7577
- var opensteerComputerMouseButtonSchema = enumSchema(
7578
- ["left", "middle", "right"],
7579
- {
7580
- title: "OpensteerComputerMouseButton"
7581
- }
7582
- );
7583
- var opensteerComputerKeyModifierSchema = enumSchema(
7584
- ["Shift", "Control", "Alt", "Meta"],
7585
- {
7586
- title: "OpensteerComputerKeyModifier"
7587
- }
7588
- );
7589
7594
  var opensteerComputerAnnotationSchema = enumSchema(opensteerComputerAnnotationNames, {
7590
7595
  title: "OpensteerComputerAnnotation"
7591
7596
  });
@@ -12126,6 +12131,30 @@ var OpensteerCloudClient = class {
12126
12131
  });
12127
12132
  return await response.json();
12128
12133
  }
12134
+ async getSessionRecording(sessionId) {
12135
+ const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/recording`, {
12136
+ method: "GET"
12137
+ });
12138
+ return await response.json();
12139
+ }
12140
+ async startSessionRecording(sessionId) {
12141
+ const response = await this.request(
12142
+ `/v1/sessions/${encodeURIComponent(sessionId)}/recording/start`,
12143
+ {
12144
+ method: "POST"
12145
+ }
12146
+ );
12147
+ return await response.json();
12148
+ }
12149
+ async stopSessionRecording(sessionId) {
12150
+ const response = await this.request(
12151
+ `/v1/sessions/${encodeURIComponent(sessionId)}/recording/stop`,
12152
+ {
12153
+ method: "POST"
12154
+ }
12155
+ );
12156
+ return await response.json();
12157
+ }
12129
12158
  async closeSession(sessionId) {
12130
12159
  const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
12131
12160
  method: "DELETE"
@@ -12316,9 +12345,11 @@ function resolveCloudConfig(input = {}) {
12316
12345
  if (!baseUrl || baseUrl.trim().length === 0) {
12317
12346
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
12318
12347
  }
12348
+ const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
12319
12349
  return {
12320
12350
  apiKey: apiKey.trim(),
12321
12351
  baseUrl: baseUrl.trim().replace(/\/+$/, ""),
12352
+ ...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
12322
12353
  ...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
12323
12354
  };
12324
12355
  }
@@ -12333,16 +12364,7 @@ var OPENSTEER_RUNTIME_CORE_VERSION = package_default2.version;
12333
12364
  // ../runtime-core/src/action-boundary.ts
12334
12365
  var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
12335
12366
  async function captureActionBoundarySnapshot(engine, pageRef) {
12336
- const frames = await engine.listFrames({ pageRef });
12337
- const mainFrame = frames.find((frame) => frame.isMainFrame);
12338
- if (!mainFrame) {
12339
- throw new Error(`page ${pageRef} does not expose a main frame`);
12340
- }
12341
- return {
12342
- pageRef,
12343
- documentRef: mainFrame.documentRef,
12344
- url: mainFrame.url
12345
- };
12367
+ return engine.getActionBoundarySnapshot({ pageRef });
12346
12368
  }
12347
12369
  function createActionBoundaryDiagnostics(input) {
12348
12370
  return {
@@ -12459,6 +12481,7 @@ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
12459
12481
  scope: "visible-frames",
12460
12482
  timeoutMs: 7e3
12461
12483
  };
12484
+ var NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS = 1e3;
12462
12485
  var defaultDomActionSettleObserver = {
12463
12486
  async settle(input) {
12464
12487
  if (input.trigger !== "dom-action") {
@@ -12494,6 +12517,13 @@ var defaultNavigationSettleObserver = {
12494
12517
  return false;
12495
12518
  }
12496
12519
  try {
12520
+ await input.engine.waitForPostLoadQuiet({
12521
+ pageRef: input.pageRef,
12522
+ timeoutMs: effectiveTimeout,
12523
+ quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
12524
+ captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
12525
+ signal: input.signal
12526
+ });
12497
12527
  await input.engine.waitForVisualStability({
12498
12528
  pageRef: input.pageRef,
12499
12529
  timeoutMs: effectiveTimeout,
@@ -23274,11 +23304,11 @@ var SandboxClock = class {
23274
23304
  performanceNow() {
23275
23305
  return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
23276
23306
  }
23277
- setTimeout(callback, delay3 = 0, ...args) {
23278
- return this.registerTimer(false, callback, delay3, args);
23307
+ setTimeout(callback, delay4 = 0, ...args) {
23308
+ return this.registerTimer(false, callback, delay4, args);
23279
23309
  }
23280
- setInterval(callback, delay3 = 0, ...args) {
23281
- return this.registerTimer(true, callback, delay3, args);
23310
+ setInterval(callback, delay4 = 0, ...args) {
23311
+ return this.registerTimer(true, callback, delay4, args);
23282
23312
  }
23283
23313
  clearTimeout(timerId) {
23284
23314
  this.clearTimer(timerId);
@@ -23299,9 +23329,9 @@ var SandboxClock = class {
23299
23329
  this.clearTimer(timerId);
23300
23330
  }
23301
23331
  }
23302
- registerTimer(repeat, callback, delay3, args) {
23332
+ registerTimer(repeat, callback, delay4, args) {
23303
23333
  const timerId = this.nextTimerId++;
23304
- const normalizedDelay = Math.max(0, delay3);
23334
+ const normalizedDelay = Math.max(0, delay4);
23305
23335
  const record = {
23306
23336
  callback,
23307
23337
  args,
@@ -24472,6 +24502,9 @@ var OpensteerSessionRuntime = class {
24472
24502
  const result = await this.requireDom().click({
24473
24503
  pageRef,
24474
24504
  target,
24505
+ ...input.button === void 0 ? {} : { button: input.button },
24506
+ ...input.clickCount === void 0 ? {} : { clickCount: input.clickCount },
24507
+ ...input.modifiers === void 0 ? {} : { modifiers: input.modifiers },
24475
24508
  timeout
24476
24509
  });
24477
24510
  return {
@@ -33191,7 +33224,11 @@ function screenshotMediaType(format2) {
33191
33224
  var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
33192
33225
  /* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
33193
33226
  );
33194
- var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33227
+ function createFlowRecorderInstallScript(options = {}) {
33228
+ const normalizedOptions = {
33229
+ showStopButton: options.showStopButton ?? true
33230
+ };
33231
+ return String.raw`(() => {
33195
33232
  const TOP_LEVEL_ONLY = (() => {
33196
33233
  try {
33197
33234
  return window.top === window.self;
@@ -33216,6 +33253,7 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33216
33253
  const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
33217
33254
  const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
33218
33255
  const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
33256
+ const options = ${JSON.stringify(normalizedOptions)};
33219
33257
 
33220
33258
  const previous = globalScope[recorderKey];
33221
33259
  if (previous && typeof previous.dispose === "function") {
@@ -33226,6 +33264,7 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33226
33264
  const cleanup = [];
33227
33265
  const inputFlushTimers = new Map();
33228
33266
  const pendingInputs = new Map();
33267
+ let historyStateCache = undefined;
33229
33268
  let stopRequested = false;
33230
33269
  let pendingWheel = undefined;
33231
33270
 
@@ -33546,44 +33585,76 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33546
33585
  inputFlushTimers.set(selector, timerId);
33547
33586
  }
33548
33587
 
33549
- function updateHistoryState(mode, nextUrl) {
33550
- let state;
33551
- try {
33552
- const raw = sessionStorage.getItem(historyStateKey);
33553
- state = raw ? JSON.parse(raw) : undefined;
33554
- } catch {
33555
- state = undefined;
33588
+ function createDefaultHistoryState(currentUrl) {
33589
+ return {
33590
+ entries: [currentUrl],
33591
+ index: 0,
33592
+ };
33593
+ }
33594
+
33595
+ function normalizeHistoryState(state) {
33596
+ if (
33597
+ !state ||
33598
+ !Array.isArray(state.entries) ||
33599
+ !state.entries.every((entry) => typeof entry === "string") ||
33600
+ !Number.isInteger(state.index)
33601
+ ) {
33602
+ return undefined;
33556
33603
  }
33557
- if (!state || !Array.isArray(state.entries) || typeof state.index !== "number") {
33558
- state = { entries: [location.href], index: 0 };
33604
+ const entries = state.entries.slice();
33605
+ if (entries.length === 0) {
33606
+ return createDefaultHistoryState(location.href);
33559
33607
  }
33608
+ return {
33609
+ entries,
33610
+ index: Math.min(entries.length - 1, Math.max(0, state.index)),
33611
+ };
33612
+ }
33613
+
33614
+ function writeHistoryState(state) {
33615
+ const normalized = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
33616
+ historyStateCache = normalized;
33617
+ try {
33618
+ sessionStorage.setItem(historyStateKey, JSON.stringify(normalized));
33619
+ } catch {}
33620
+ return normalized;
33621
+ }
33622
+
33623
+ function applyHistoryState(state, mode, nextUrl) {
33624
+ const current = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
33625
+ const nextState = {
33626
+ entries: current.entries.slice(),
33627
+ index: current.index,
33628
+ };
33560
33629
  if (mode === "replace") {
33561
- state.entries[state.index] = nextUrl;
33630
+ nextState.entries[nextState.index] = nextUrl;
33562
33631
  } else if (mode === "push") {
33563
- state.entries = state.entries.slice(0, state.index + 1);
33564
- state.entries.push(nextUrl);
33565
- state.index = state.entries.length - 1;
33632
+ nextState.entries = nextState.entries.slice(0, nextState.index + 1);
33633
+ nextState.entries.push(nextUrl);
33634
+ nextState.index = nextState.entries.length - 1;
33566
33635
  } else if (mode === "back") {
33567
- state.index = Math.max(0, state.index - 1);
33636
+ nextState.index = Math.max(0, nextState.index - 1);
33568
33637
  } else if (mode === "forward") {
33569
- state.index = Math.min(state.entries.length - 1, state.index + 1);
33638
+ nextState.index = Math.min(nextState.entries.length - 1, nextState.index + 1);
33570
33639
  }
33571
- try {
33572
- sessionStorage.setItem(historyStateKey, JSON.stringify(state));
33573
- } catch {}
33574
- return state;
33640
+ return nextState;
33641
+ }
33642
+
33643
+ function updateHistoryState(mode, nextUrl) {
33644
+ return writeHistoryState(applyHistoryState(readHistoryState(), mode, nextUrl));
33575
33645
  }
33576
33646
 
33577
33647
  function readHistoryState() {
33648
+ const cached = normalizeHistoryState(historyStateCache);
33649
+ if (cached) {
33650
+ historyStateCache = cached;
33651
+ return cached;
33652
+ }
33578
33653
  try {
33579
33654
  const raw = sessionStorage.getItem(historyStateKey);
33580
- const parsed = raw ? JSON.parse(raw) : undefined;
33581
- if (
33582
- parsed &&
33583
- Array.isArray(parsed.entries) &&
33584
- parsed.entries.every((entry) => typeof entry === "string") &&
33585
- typeof parsed.index === "number"
33586
- ) {
33655
+ const parsed = normalizeHistoryState(raw ? JSON.parse(raw) : undefined);
33656
+ if (parsed) {
33657
+ historyStateCache = parsed;
33587
33658
  return parsed;
33588
33659
  }
33589
33660
  } catch {}
@@ -33595,28 +33666,30 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33595
33666
  if (!state) {
33596
33667
  return undefined;
33597
33668
  }
33669
+ if (state.entries[state.index] === currentUrl) {
33670
+ return undefined;
33671
+ }
33598
33672
  if (state.entries[state.index - 1] === currentUrl) {
33599
- updateHistoryState("back", currentUrl);
33673
+ writeHistoryState({
33674
+ entries: state.entries,
33675
+ index: state.index - 1,
33676
+ });
33600
33677
  return "go-back";
33601
33678
  }
33602
33679
  if (state.entries[state.index + 1] === currentUrl) {
33603
- updateHistoryState("forward", currentUrl);
33680
+ writeHistoryState({
33681
+ entries: state.entries,
33682
+ index: state.index + 1,
33683
+ });
33604
33684
  return "go-forward";
33605
33685
  }
33606
33686
  const existingIndex = state.entries.lastIndexOf(currentUrl);
33607
- if (existingIndex !== -1) {
33608
- if (existingIndex < state.index) {
33609
- while (readHistoryState()?.index > existingIndex) {
33610
- updateHistoryState("back", currentUrl);
33611
- }
33612
- return "go-back";
33613
- }
33614
- if (existingIndex > state.index) {
33615
- while (readHistoryState()?.index < existingIndex) {
33616
- updateHistoryState("forward", currentUrl);
33617
- }
33618
- return "go-forward";
33619
- }
33687
+ if (existingIndex !== -1 && existingIndex !== state.index) {
33688
+ writeHistoryState({
33689
+ entries: state.entries,
33690
+ index: existingIndex,
33691
+ });
33692
+ return existingIndex < state.index ? "go-back" : "go-forward";
33620
33693
  }
33621
33694
  return undefined;
33622
33695
  }
@@ -33743,10 +33816,6 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33743
33816
  " outline: 2px solid #2563eb;",
33744
33817
  " outline-offset: 2px;",
33745
33818
  "}",
33746
- "button:disabled {",
33747
- " cursor: wait;",
33748
- " opacity: 0.8;",
33749
- "}",
33750
33819
  ].join("\n");
33751
33820
  const button = document.createElement("button");
33752
33821
  button.type = "button";
@@ -33979,7 +34048,8 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
33979
34048
  });
33980
34049
  return;
33981
34050
  }
33982
- if (readHistoryState()?.entries[readHistoryState().index] !== currentUrl) {
34051
+ const state = readHistoryState();
34052
+ if (state?.entries[state.index] !== currentUrl) {
33983
34053
  updateHistoryState("push", currentUrl);
33984
34054
  enqueue({
33985
34055
  kind: "navigate",
@@ -34033,10 +34103,13 @@ var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
34033
34103
  },
34034
34104
  };
34035
34105
 
34036
- ensureStopButtonMounted();
34106
+ if (options.showStopButton) {
34107
+ ensureStopButtonMounted();
34108
+ }
34037
34109
  onInstall();
34038
34110
  })();`;
34039
- var FLOW_RECORDER_INSTALL_SCRIPT = FLOW_RECORDER_INSTALL_SOURCE;
34111
+ }
34112
+ var FLOW_RECORDER_INSTALL_SCRIPT = createFlowRecorderInstallScript();
34040
34113
  var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
34041
34114
  const recorder = globalThis.__opensteerFlowRecorder;
34042
34115
  if (!recorder || typeof recorder.drain !== "function") {
@@ -34056,6 +34129,7 @@ var FlowRecorderCollector = class {
34056
34129
  runtime;
34057
34130
  pollIntervalMs;
34058
34131
  onAction;
34132
+ installScript;
34059
34133
  pages = /* @__PURE__ */ new Map();
34060
34134
  actions = [];
34061
34135
  nextPageOrdinal = 0;
@@ -34068,10 +34142,11 @@ var FlowRecorderCollector = class {
34068
34142
  this.runtime = runtime;
34069
34143
  this.pollIntervalMs = options.pollIntervalMs ?? 250;
34070
34144
  this.onAction = options.onAction;
34145
+ this.installScript = options.installScript ?? FLOW_RECORDER_INSTALL_SCRIPT;
34071
34146
  }
34072
34147
  async install() {
34073
34148
  await this.runtime.addInitScript({
34074
- script: FLOW_RECORDER_INSTALL_SCRIPT
34149
+ script: this.installScript
34075
34150
  });
34076
34151
  const { pages } = await this.runtime.listPages();
34077
34152
  for (const page of pages) {
@@ -34080,7 +34155,7 @@ var FlowRecorderCollector = class {
34080
34155
  await Promise.all(
34081
34156
  pages.map(
34082
34157
  (page) => this.runtime.evaluate({
34083
- script: FLOW_RECORDER_INSTALL_SCRIPT,
34158
+ script: this.installScript,
34084
34159
  pageRef: page.pageRef
34085
34160
  }).catch(() => void 0)
34086
34161
  )
@@ -34112,6 +34187,18 @@ var FlowRecorderCollector = class {
34112
34187
  getActions() {
34113
34188
  return this.actions.slice();
34114
34189
  }
34190
+ getPages() {
34191
+ return [...this.pages.values()].map((page) => ({
34192
+ pageId: page.pageId,
34193
+ pageRef: page.pageRef,
34194
+ ...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
34195
+ ...page.openerPageId === void 0 ? {} : { openerPageId: page.openerPageId },
34196
+ currentUrl: page.currentUrl
34197
+ })).sort((left, right) => comparePageIds(left.pageId, right.pageId));
34198
+ }
34199
+ getFocusedPageId() {
34200
+ return this.focusedPageId;
34201
+ }
34115
34202
  async waitForStop() {
34116
34203
  if (this.stopDetected) {
34117
34204
  return;
@@ -34208,10 +34295,17 @@ var FlowRecorderCollector = class {
34208
34295
  }
34209
34296
  for (const listedPage of input.listedPages) {
34210
34297
  if (!input.previousPageRefs.has(listedPage.pageRef)) {
34211
- const created = this.ensureKnownPage(listedPage.pageRef, listedPage.url, listedPage.openerPageRef);
34298
+ const created = this.ensureKnownPage(
34299
+ listedPage.pageRef,
34300
+ listedPage.url,
34301
+ listedPage.openerPageRef
34302
+ );
34212
34303
  actions.push({
34213
34304
  kind: "new-tab",
34214
- timestamp: Math.max(0, (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1),
34305
+ timestamp: Math.max(
34306
+ 0,
34307
+ (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1
34308
+ ),
34215
34309
  pageId: created.pageId,
34216
34310
  pageUrl: listedPage.url,
34217
34311
  detail: {
@@ -34265,7 +34359,11 @@ var FlowRecorderCollector = class {
34265
34359
  (event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
34266
34360
  )
34267
34361
  );
34268
- this.updateKnownPage(evaluatedPage.pageRef, evaluatedPage.currentUrl, evaluatedPage.openerPageRef);
34362
+ this.updateKnownPage(
34363
+ evaluatedPage.pageRef,
34364
+ evaluatedPage.currentUrl,
34365
+ evaluatedPage.openerPageRef
34366
+ );
34269
34367
  }
34270
34368
  const focusedPage = input.evaluatedPages.find((page) => page.focused);
34271
34369
  if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
@@ -34516,6 +34614,14 @@ function actionSortPriority(kind) {
34516
34614
  return 6;
34517
34615
  }
34518
34616
  }
34617
+ function comparePageIds(left, right) {
34618
+ const leftMatch = /^page(\d+)$/u.exec(left);
34619
+ const rightMatch = /^page(\d+)$/u.exec(right);
34620
+ if (leftMatch && rightMatch) {
34621
+ return Number(leftMatch[1]) - Number(rightMatch[1]);
34622
+ }
34623
+ return left.localeCompare(right);
34624
+ }
34519
34625
  function delay2(ms) {
34520
34626
  return new Promise((resolve4) => setTimeout(resolve4, ms));
34521
34627
  }
@@ -34565,17 +34671,16 @@ var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
34565
34671
  window.scrollBy(Number(deltaX), Number(deltaY));
34566
34672
  }`;
34567
34673
  function generateReplayScript(options) {
34568
- const pageIds = collectPageIds(options.actions);
34569
- const initialPageId = pageIds[0] ?? "page0";
34674
+ const replayTarget = resolveReplayTarget(options);
34675
+ const initialPages = orderInitialPages(resolveInitialPages(options));
34676
+ const activeInitialPageId = resolveActiveInitialPageId(options, initialPages);
34677
+ const initialPageId = initialPages[0]?.pageId ?? "page0";
34570
34678
  const lines = [
34571
34679
  `import { Opensteer } from "opensteer";`,
34572
34680
  ``,
34573
- `const opensteer = new Opensteer({`,
34574
- ` workspace: ${JSON.stringify(options.workspace)},`,
34575
- ` browser: "persistent",`,
34576
- `});`,
34681
+ ...renderOpensteerBootstrap(replayTarget),
34577
34682
  ``,
34578
- `const ${initialPageId} = (await opensteer.open(${JSON.stringify(options.startUrl)})).pageRef;`,
34683
+ `const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
34579
34684
  `let activePageRef: string | undefined = ${initialPageId};`,
34580
34685
  ``,
34581
34686
  `async function ensureActive(pageRef: string): Promise<void> {`,
@@ -34616,6 +34721,17 @@ function generateReplayScript(options) {
34616
34721
  `try {`
34617
34722
  ];
34618
34723
  const declaredPages = /* @__PURE__ */ new Set([initialPageId]);
34724
+ for (const page of initialPages.slice(1)) {
34725
+ const openerPageId = page.openerPageId !== void 0 && declaredPages.has(page.openerPageId) ? page.openerPageId : void 0;
34726
+ lines.push(
34727
+ ` const ${page.pageId} = (await opensteer.newPage(${renderNewPageInput(openerPageId, page.initialUrl)})).pageRef;`
34728
+ );
34729
+ lines.push(` activePageRef = ${page.pageId};`);
34730
+ declaredPages.add(page.pageId);
34731
+ }
34732
+ if (activeInitialPageId !== void 0 && activeInitialPageId !== initialPageId) {
34733
+ lines.push(` await ensureActive(${activeInitialPageId});`);
34734
+ }
34619
34735
  for (let index = 0; index < options.actions.length; index += 1) {
34620
34736
  const action = options.actions[index];
34621
34737
  const pageVar = action.pageId;
@@ -34648,16 +34764,11 @@ function generateReplayScript(options) {
34648
34764
  case "dblclick":
34649
34765
  lines.push(` await ensureActive(${pageVar});`);
34650
34766
  lines.push(
34651
- ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
34652
- );
34653
- lines.push(
34654
- ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
34767
+ ` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))}, clickCount: 2 });`
34655
34768
  );
34656
34769
  break;
34657
34770
  case "keypress":
34658
- lines.push(
34659
- ` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`
34660
- );
34771
+ lines.push(` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`);
34661
34772
  break;
34662
34773
  case "scroll": {
34663
34774
  const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
@@ -34685,7 +34796,7 @@ function generateReplayScript(options) {
34685
34796
  case "new-tab": {
34686
34797
  const openerPageVar = action.detail.openerPageId;
34687
34798
  const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
34688
- 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;`;
34799
+ 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;`;
34689
34800
  lines.push(creationLine);
34690
34801
  lines.push(` activePageRef = ${pageVar};`);
34691
34802
  declaredPages.add(pageVar);
@@ -34718,29 +34829,114 @@ function generateReplayScript(options) {
34718
34829
  return `${lines.join("\n")}
34719
34830
  `;
34720
34831
  }
34721
- function collectPageIds(actions) {
34722
- const orderedIds = /* @__PURE__ */ new Set(["page0"]);
34723
- for (const action of actions) {
34724
- orderedIds.add(action.pageId);
34725
- if (action.kind === "new-tab" && action.detail.openerPageId !== void 0) {
34726
- orderedIds.add(action.detail.openerPageId);
34727
- }
34728
- if (action.kind === "switch-tab" && action.detail.fromPageId !== void 0) {
34729
- orderedIds.add(action.detail.fromPageId);
34730
- }
34731
- if (action.kind === "switch-tab") {
34732
- orderedIds.add(action.detail.toPageId);
34832
+ function resolveReplayTarget(options) {
34833
+ if (options.replayTarget !== void 0) {
34834
+ return options.replayTarget;
34835
+ }
34836
+ if (options.workspace !== void 0) {
34837
+ return {
34838
+ kind: "local",
34839
+ workspace: options.workspace
34840
+ };
34841
+ }
34842
+ throw new Error("Replay codegen requires either replayTarget or workspace.");
34843
+ }
34844
+ function resolveInitialPages(options) {
34845
+ if (options.initialPages !== void 0 && options.initialPages.length > 0) {
34846
+ const unique = /* @__PURE__ */ new Set();
34847
+ return options.initialPages.map((page) => {
34848
+ if (unique.has(page.pageId)) {
34849
+ throw new Error(`Duplicate initial page id "${page.pageId}" in recording bootstrap.`);
34850
+ }
34851
+ unique.add(page.pageId);
34852
+ return page;
34853
+ });
34854
+ }
34855
+ const startUrl = options.startUrl;
34856
+ if (startUrl === void 0) {
34857
+ throw new Error("Replay codegen requires startUrl when initialPages is not provided.");
34858
+ }
34859
+ return [
34860
+ {
34861
+ pageId: "page0",
34862
+ initialUrl: startUrl
34733
34863
  }
34864
+ ];
34865
+ }
34866
+ function resolveActiveInitialPageId(options, initialPages) {
34867
+ if (options.activePageId !== void 0) {
34868
+ return options.activePageId;
34734
34869
  }
34735
- return [...orderedIds].sort(comparePageIds);
34870
+ return initialPages[0]?.pageId;
34736
34871
  }
34737
- function comparePageIds(left, right) {
34738
- const leftMatch = /^page(\d+)$/u.exec(left);
34739
- const rightMatch = /^page(\d+)$/u.exec(right);
34740
- if (leftMatch && rightMatch) {
34741
- return Number(leftMatch[1]) - Number(rightMatch[1]);
34872
+ function renderOpensteerBootstrap(replayTarget) {
34873
+ if (replayTarget.kind === "local") {
34874
+ return [
34875
+ `const opensteer = new Opensteer({`,
34876
+ ` workspace: ${JSON.stringify(replayTarget.workspace)},`,
34877
+ ` browser: "persistent",`,
34878
+ `});`
34879
+ ];
34742
34880
  }
34743
- return left.localeCompare(right);
34881
+ return [
34882
+ renderRequireEnvHelper(replayTarget),
34883
+ ``,
34884
+ `const opensteer = new Opensteer({`,
34885
+ ` provider: {`,
34886
+ ` mode: "cloud",`,
34887
+ ` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
34888
+ ` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
34889
+ ...renderCloudBrowserProfile(replayTarget),
34890
+ ` },`,
34891
+ `});`
34892
+ ];
34893
+ }
34894
+ function renderRequireEnvHelper(replayTarget) {
34895
+ const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
34896
+ const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
34897
+ return [
34898
+ `function requireEnv(name: string): string {`,
34899
+ ` const value = process.env[name];`,
34900
+ ` if (typeof value === "string" && value.trim().length > 0) {`,
34901
+ ` return value;`,
34902
+ ` }`,
34903
+ ` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
34904
+ `}`
34905
+ ].join("\n");
34906
+ }
34907
+ function renderCloudBrowserProfile(replayTarget) {
34908
+ if (replayTarget.browserProfileId === void 0) {
34909
+ return [];
34910
+ }
34911
+ return [
34912
+ ` browserProfile: {`,
34913
+ ` profileId: ${JSON.stringify(replayTarget.browserProfileId)},`,
34914
+ ...replayTarget.reuseBrowserProfileIfActive ? [` reuseIfActive: true,`] : [],
34915
+ ` },`
34916
+ ];
34917
+ }
34918
+ function orderInitialPages(initialPages) {
34919
+ const ordered = [];
34920
+ const declared = /* @__PURE__ */ new Set();
34921
+ const remaining = [...initialPages];
34922
+ while (remaining.length > 0) {
34923
+ let advanced = false;
34924
+ for (let index = 0; index < remaining.length; index += 1) {
34925
+ const page = remaining[index];
34926
+ if (page.openerPageId !== void 0 && !declared.has(page.openerPageId)) {
34927
+ continue;
34928
+ }
34929
+ ordered.push(page);
34930
+ declared.add(page.pageId);
34931
+ remaining.splice(index, 1);
34932
+ advanced = true;
34933
+ break;
34934
+ }
34935
+ if (!advanced) {
34936
+ ordered.push(...remaining.splice(0, remaining.length));
34937
+ }
34938
+ }
34939
+ return ordered;
34744
34940
  }
34745
34941
  function requireSelector(action) {
34746
34942
  if (action.selector === void 0) {
@@ -34774,18 +34970,18 @@ function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
34774
34970
  }
34775
34971
  return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
34776
34972
  }
34777
- function renderNewPageArguments(openerPageId, initialUrl) {
34973
+ function renderNewPageInput(openerPageId, initialUrl) {
34778
34974
  const argumentsList = [];
34779
34975
  if (openerPageId !== void 0) {
34780
- argumentsList.push(` openerPageRef: ${openerPageId}`);
34976
+ argumentsList.push(`openerPageRef: ${openerPageId}`);
34781
34977
  }
34782
34978
  if (initialUrl.length > 0 && initialUrl !== "about:blank") {
34783
- argumentsList.push(` url: ${JSON.stringify(initialUrl)}`);
34979
+ argumentsList.push(`url: ${JSON.stringify(initialUrl)}`);
34784
34980
  }
34785
34981
  if (argumentsList.length === 0) {
34786
- return ``;
34982
+ return `{}`;
34787
34983
  }
34788
- return ` ${argumentsList.join(",")}`.trimStart();
34984
+ return `{ ${argumentsList.join(", ")} }`;
34789
34985
  }
34790
34986
  var OpensteerSemanticRestError = class extends Error {
34791
34987
  opensteerError;
@@ -35934,18 +36130,6 @@ function resolveOwnership(browser) {
35934
36130
  }
35935
36131
 
35936
36132
  // src/sdk/runtime-resolution.ts
35937
- var LOCAL_ONLY_RUNTIME_OPTION_KEYS = [
35938
- "launch",
35939
- "context",
35940
- "engine",
35941
- "engineFactory",
35942
- "policy",
35943
- "descriptorStore",
35944
- "extractionDescriptorStore",
35945
- "registryOverrides",
35946
- "observationSessionId",
35947
- "observationSink"
35948
- ];
35949
36133
  function resolveOpensteerRuntimeConfig(input = {}) {
35950
36134
  const environment = input.environment ?? process.env;
35951
36135
  const provider = resolveOpensteerProvider({
@@ -35970,15 +36154,8 @@ function createOpensteerSemanticRuntime(input = {}) {
35970
36154
  ...input.provider === void 0 ? {} : { provider: input.provider },
35971
36155
  ...input.environment === void 0 ? {} : { environment: input.environment }
35972
36156
  });
35973
- const localOnlyRuntimeOptions = listLocalOnlyRuntimeOptions(runtimeOptions);
35974
- const providerMode = config.provider.mode === "cloud" && config.provider.source !== "explicit" && localOnlyRuntimeOptions.length > 0 ? "local" : config.provider.mode;
35975
- if (config.provider.mode === "cloud" && config.provider.source === "explicit" && localOnlyRuntimeOptions.length > 0) {
35976
- throw new Error(
35977
- `provider=cloud does not support local runtime options: ${localOnlyRuntimeOptions.join(", ")}.`
35978
- );
35979
- }
35980
- assertProviderSupportsEngine(providerMode, engine);
35981
- if (providerMode === "cloud") {
36157
+ assertProviderSupportsEngine(config.provider.mode, engine);
36158
+ if (config.provider.mode === "cloud") {
35982
36159
  return new CloudSessionProxy(new OpensteerCloudClient(config.cloud), {
35983
36160
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
35984
36161
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
@@ -35992,9 +36169,6 @@ function createOpensteerSemanticRuntime(input = {}) {
35992
36169
  engineName: engine
35993
36170
  });
35994
36171
  }
35995
- function listLocalOnlyRuntimeOptions(runtimeOptions) {
35996
- return LOCAL_ONLY_RUNTIME_OPTION_KEYS.filter((key) => runtimeOptions[key] !== void 0);
35997
- }
35998
36172
  async function collectOpensteerStatus(input) {
35999
36173
  const output = {
36000
36174
  provider: {
@@ -36202,6 +36376,65 @@ async function runOpensteerRecordCommand(input) {
36202
36376
  }
36203
36377
  }
36204
36378
  }
36379
+ async function runOpensteerCloudRecordCommand(input) {
36380
+ const stdout = input.stdout ?? process.stdout;
36381
+ const stderr = input.stderr ?? process.stderr;
36382
+ const outputPath = resolveRecordOutputPath({
36383
+ rootDir: input.rootDir,
36384
+ workspace: input.workspace,
36385
+ ...input.outputPath === void 0 ? {} : { outputPath: input.outputPath }
36386
+ });
36387
+ let cloud;
36388
+ const resolveCloud = () => {
36389
+ cloud ??= new OpensteerCloudClient(input.cloudConfig);
36390
+ return cloud;
36391
+ };
36392
+ const runtime = input.runtime ?? new CloudSessionProxy(resolveCloud(), {
36393
+ rootDir: input.rootDir,
36394
+ workspace: input.workspace
36395
+ });
36396
+ const client = input.client ?? resolveCloud();
36397
+ const sleep5 = input.sleep ?? delay3;
36398
+ let closed = false;
36399
+ try {
36400
+ await runtime.open({
36401
+ url: input.url,
36402
+ ...input.browser === void 0 ? {} : { browser: input.browser },
36403
+ ...input.launch === void 0 ? {} : { launch: input.launch },
36404
+ ...input.context === void 0 ? {} : { context: input.context }
36405
+ });
36406
+ const sessionId = await resolveCloudRecordingSessionId(runtime);
36407
+ const sessionUrl = buildCloudRecordingSessionUrl(input.cloudConfig, sessionId);
36408
+ await client.startSessionRecording(sessionId);
36409
+ stderr.write(
36410
+ `Recording browser actions for workspace "${input.workspace}". Open ${sessionUrl} and click "Stop recording" in the browser session toolbar when you're done.
36411
+ `
36412
+ );
36413
+ const completed = await waitForCloudRecordingCompletion({
36414
+ client,
36415
+ sessionId,
36416
+ ...input.pollIntervalMs === void 0 ? {} : { pollIntervalMs: input.pollIntervalMs },
36417
+ sleep: sleep5
36418
+ });
36419
+ if (completed.result === void 0) {
36420
+ throw new Error("Cloud recording completed without a replay script.");
36421
+ }
36422
+ await promises.mkdir(path7__default.default.dirname(outputPath), { recursive: true });
36423
+ await promises.writeFile(outputPath, completed.result.script, "utf8");
36424
+ await runtime.close();
36425
+ closed = true;
36426
+ stdout.write(`${outputPath}
36427
+ `);
36428
+ stderr.write(`Cloud browser session: ${sessionUrl}
36429
+ `);
36430
+ stderr.write(`Wrote replay script to ${outputPath}
36431
+ `);
36432
+ } finally {
36433
+ if (!closed) {
36434
+ await runtime.close().catch(() => void 0);
36435
+ }
36436
+ }
36437
+ }
36205
36438
  function resolveRecordOutputPath(input) {
36206
36439
  if (input.outputPath !== void 0) {
36207
36440
  return path7__default.default.resolve(input.rootDir, input.outputPath);
@@ -36236,6 +36469,35 @@ function createRecorderRuntimeAdapter(runtime) {
36236
36469
  }
36237
36470
  };
36238
36471
  }
36472
+ function buildCloudRecordingSessionUrl(cloudConfig, sessionId) {
36473
+ const baseUrl = cloudConfig.appBaseUrl;
36474
+ if (!baseUrl || baseUrl.trim().length === 0) {
36475
+ throw new Error(
36476
+ 'record with provider=cloud requires OPENSTEER_CLOUD_APP_BASE_URL or "--cloud-app-base-url".'
36477
+ );
36478
+ }
36479
+ return `${baseUrl.replace(/\/+$/, "")}/browsers/${encodeURIComponent(sessionId)}`;
36480
+ }
36481
+ async function resolveCloudRecordingSessionId(runtime) {
36482
+ const info = await runtime.info();
36483
+ if (typeof info.sessionId !== "string" || info.sessionId.length === 0) {
36484
+ throw new Error("Cloud recording could not resolve the created session id.");
36485
+ }
36486
+ return info.sessionId;
36487
+ }
36488
+ async function waitForCloudRecordingCompletion(input) {
36489
+ const pollIntervalMs = input.pollIntervalMs ?? 1e3;
36490
+ for (; ; ) {
36491
+ const state = await input.client.getSessionRecording(input.sessionId);
36492
+ if (state.status === "completed") {
36493
+ return state;
36494
+ }
36495
+ if (state.status === "failed") {
36496
+ throw new Error(state.error ?? "Cloud recording failed.");
36497
+ }
36498
+ await input.sleep(pollIntervalMs);
36499
+ }
36500
+ }
36239
36501
  function formatRecordedAction(action) {
36240
36502
  const time = new Date(action.timestamp).toISOString().slice(11, 19);
36241
36503
  switch (action.kind) {
@@ -36267,6 +36529,11 @@ function formatRecordedAction(action) {
36267
36529
  return `[${time}] reload ${action.pageId}`;
36268
36530
  }
36269
36531
  }
36532
+ function delay3(ms) {
36533
+ return new Promise((resolve4) => {
36534
+ setTimeout(resolve4, ms);
36535
+ });
36536
+ }
36270
36537
 
36271
36538
  // src/cli/bin.ts
36272
36539
  var OPERATION_ALIASES = /* @__PURE__ */ new Map([
@@ -36516,22 +36783,39 @@ async function handleRecordCommandEntry(parsed) {
36516
36783
  }
36517
36784
  const provider = resolveCliProvider(parsed);
36518
36785
  assertCloudCliOptionsMatchProvider(parsed, provider.mode);
36519
- if (provider.mode !== "local") {
36520
- throw new Error(
36521
- 'record requires provider=local. Set "--provider local" or clear OPENSTEER_PROVIDER.'
36522
- );
36523
- }
36524
36786
  const engineName = resolveCliEngineName(parsed);
36525
36787
  if (engineName !== "playwright") {
36526
36788
  throw new Error("record requires engine=playwright.");
36527
36789
  }
36528
- if (parsed.options.browser !== void 0 && parsed.options.browser !== "persistent") {
36529
- throw new Error('record only supports "--browser persistent".');
36790
+ const rootDir = process2__default.default.cwd();
36791
+ const recordBrowser = parsed.options.browser;
36792
+ if (provider.mode === "cloud") {
36793
+ if (typeof recordBrowser === "object") {
36794
+ throw new Error('record does not support browser.mode="attach".');
36795
+ }
36796
+ const runtimeProvider = buildCliRuntimeProvider(parsed, provider.mode);
36797
+ const runtimeConfig = resolveOpensteerRuntimeConfig({
36798
+ ...runtimeProvider === void 0 ? {} : { provider: runtimeProvider },
36799
+ environment: process2__default.default.env
36800
+ });
36801
+ await runOpensteerCloudRecordCommand({
36802
+ cloudConfig: runtimeConfig.cloud,
36803
+ workspace: parsed.options.workspace,
36804
+ url,
36805
+ rootDir,
36806
+ ...recordBrowser === void 0 ? {} : { browser: recordBrowser },
36807
+ ...parsed.options.launch === void 0 ? {} : { launch: parsed.options.launch },
36808
+ ...parsed.options.context === void 0 ? {} : { context: parsed.options.context },
36809
+ ...parsed.options.output === void 0 ? {} : { outputPath: parsed.options.output }
36810
+ });
36811
+ return;
36530
36812
  }
36531
36813
  if (parsed.options.launch?.headless === true) {
36532
36814
  throw new Error('record requires a headed browser. Remove "--headless true".');
36533
36815
  }
36534
- const rootDir = process2__default.default.cwd();
36816
+ if (recordBrowser !== void 0 && recordBrowser !== "persistent") {
36817
+ throw new Error('record only supports "--browser persistent".');
36818
+ }
36535
36819
  const launch = {
36536
36820
  ...parsed.options.launch ?? {},
36537
36821
  headless: false
@@ -36671,6 +36955,7 @@ var CLI_OPTION_SPECS = {
36671
36955
  provider: { kind: "value" },
36672
36956
  "cloud-base-url": { kind: "value" },
36673
36957
  "cloud-api-key": { kind: "value" },
36958
+ "cloud-app-base-url": { kind: "value" },
36674
36959
  "cloud-profile-id": { kind: "value" },
36675
36960
  "cloud-profile-reuse-if-active": { kind: "boolean" },
36676
36961
  json: { kind: "boolean" },
@@ -36800,6 +37085,7 @@ function parseCommandLine(argv) {
36800
37085
  const provider = providerValue === void 0 ? void 0 : normalizeOpensteerProviderMode(providerValue, "--provider");
36801
37086
  const cloudBaseUrl = readSingle(rawOptions, "cloud-base-url");
36802
37087
  const cloudApiKey = readSingle(rawOptions, "cloud-api-key");
37088
+ const cloudAppBaseUrl = readSingle(rawOptions, "cloud-app-base-url");
36803
37089
  const cloudProfileId = readSingle(rawOptions, "cloud-profile-id");
36804
37090
  const cloudProfileReuseIfActive = readOptionalBoolean(
36805
37091
  rawOptions,
@@ -36821,6 +37107,7 @@ function parseCommandLine(argv) {
36821
37107
  ...provider === void 0 ? {} : { provider },
36822
37108
  ...cloudBaseUrl === void 0 ? {} : { cloudBaseUrl },
36823
37109
  ...cloudApiKey === void 0 ? {} : { cloudApiKey },
37110
+ ...cloudAppBaseUrl === void 0 ? {} : { cloudAppBaseUrl },
36824
37111
  ...cloudProfileId === void 0 ? {} : { cloudProfileId },
36825
37112
  ...cloudProfileReuseIfActive === void 0 ? {} : { cloudProfileReuseIfActive },
36826
37113
  ...json === void 0 ? {} : { json },
@@ -36905,7 +37192,7 @@ function buildCliRuntimeProvider(parsed, providerMode) {
36905
37192
  return explicitProvider?.mode === "local" ? explicitProvider : void 0;
36906
37193
  }
36907
37194
  const browserProfile = buildCliBrowserProfile(parsed);
36908
- const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || browserProfile !== void 0;
37195
+ const hasCloudOverrides = parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || browserProfile !== void 0;
36909
37196
  if (!hasCloudOverrides && explicitProvider?.mode !== "cloud") {
36910
37197
  return void 0;
36911
37198
  }
@@ -36913,11 +37200,12 @@ function buildCliRuntimeProvider(parsed, providerMode) {
36913
37200
  mode: "cloud",
36914
37201
  ...parsed.options.cloudBaseUrl === void 0 ? {} : { baseUrl: parsed.options.cloudBaseUrl },
36915
37202
  ...parsed.options.cloudApiKey === void 0 ? {} : { apiKey: parsed.options.cloudApiKey },
37203
+ ...parsed.options.cloudAppBaseUrl === void 0 ? {} : { appBaseUrl: parsed.options.cloudAppBaseUrl },
36916
37204
  ...browserProfile === void 0 ? {} : { browserProfile }
36917
37205
  };
36918
37206
  }
36919
37207
  function assertCloudCliOptionsMatchProvider(parsed, providerMode) {
36920
- if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
37208
+ if (providerMode !== "cloud" && (parsed.options.cloudBaseUrl !== void 0 || parsed.options.cloudApiKey !== void 0 || parsed.options.cloudAppBaseUrl !== void 0 || parsed.options.cloudProfileId !== void 0 || parsed.options.cloudProfileReuseIfActive === true)) {
36921
37209
  throw new Error(
36922
37210
  'Cloud-specific options require provider=cloud. Set "--provider cloud" or OPENSTEER_PROVIDER=cloud.'
36923
37211
  );
@@ -37059,6 +37347,7 @@ Common options:
37059
37347
  --provider local|cloud
37060
37348
  --cloud-base-url <url>
37061
37349
  --cloud-api-key <key>
37350
+ --cloud-app-base-url <url>
37062
37351
  --cloud-profile-id <id>
37063
37352
  --cloud-profile-reuse-if-active <true|false>
37064
37353
  --json <true|false>