opensteer 0.4.8 → 0.4.10

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/bin/opensteer.mjs CHANGED
@@ -781,7 +781,6 @@ Environment:
781
781
  OPENSTEER_MODE Runtime routing: "local" (default) or "cloud"
782
782
  OPENSTEER_API_KEY Required when cloud mode is selected
783
783
  OPENSTEER_BASE_URL Override cloud control-plane base URL
784
- OPENSTEER_APP_URL Cloud app base URL for emitting browser session links (default: https://opensteer.com)
785
784
  OPENSTEER_REMOTE_ANNOUNCE Cloud session announcement policy: always (default), off, tty
786
785
  `)
787
786
  }
@@ -38,6 +38,7 @@ function sanitizeNamespaceSegment(segment) {
38
38
  var DEFAULT_TIMEOUT = 3e4;
39
39
  var DEFAULT_SETTLE_MS = 750;
40
40
  var FRAME_EVALUATE_GRACE_MS = 200;
41
+ var TRANSIENT_CONTEXT_RETRY_DELAY_MS = 25;
41
42
  var STEALTH_WORLD_NAME = "__opensteer_wait__";
42
43
  var StealthWaitUnavailableError = class extends Error {
43
44
  constructor(cause) {
@@ -341,7 +342,12 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
341
342
  const frameRecords = await this.getFrameRecords();
342
343
  const mainFrame = frameRecords[0];
343
344
  if (!mainFrame) return;
344
- await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
345
+ await this.waitForFrameVisualStability(
346
+ mainFrame.frameId,
347
+ timeout,
348
+ settleMs,
349
+ true
350
+ );
345
351
  }
346
352
  async collectVisibleFrameIds() {
347
353
  const frameRecords = await this.getFrameRecords();
@@ -370,19 +376,44 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
370
376
  }
371
377
  return visibleFrameIds;
372
378
  }
373
- async waitForFrameVisualStability(frameId, timeout, settleMs) {
379
+ async waitForFrameVisualStability(frameId, timeout, settleMs, retryTransientContextErrors = true) {
374
380
  if (timeout <= 0) return;
375
381
  const script = buildStabilityScript(timeout, settleMs);
376
- let contextId = await this.ensureFrameContextId(frameId);
377
- try {
378
- await this.evaluateWithGuard(contextId, script, timeout);
379
- } catch (error) {
380
- if (!isMissingExecutionContextError(error)) {
381
- throw error;
382
+ if (!retryTransientContextErrors) {
383
+ let contextId = await this.ensureFrameContextId(frameId);
384
+ try {
385
+ await this.evaluateWithGuard(contextId, script, timeout);
386
+ } catch (error) {
387
+ if (!isMissingExecutionContextError(error)) {
388
+ throw error;
389
+ }
390
+ this.contextsByFrame.delete(frameId);
391
+ contextId = await this.ensureFrameContextId(frameId);
392
+ await this.evaluateWithGuard(contextId, script, timeout);
393
+ }
394
+ return;
395
+ }
396
+ const deadline = Date.now() + timeout;
397
+ while (true) {
398
+ const remaining = Math.max(0, deadline - Date.now());
399
+ if (remaining === 0) {
400
+ return;
401
+ }
402
+ const contextId = await this.ensureFrameContextId(frameId);
403
+ try {
404
+ await this.evaluateWithGuard(contextId, script, remaining);
405
+ return;
406
+ } catch (error) {
407
+ if (!isTransientExecutionContextError(error)) {
408
+ throw error;
409
+ }
410
+ this.contextsByFrame.delete(frameId);
411
+ const retryDelay = Math.min(
412
+ TRANSIENT_CONTEXT_RETRY_DELAY_MS,
413
+ Math.max(0, deadline - Date.now())
414
+ );
415
+ await sleep(retryDelay);
382
416
  }
383
- this.contextsByFrame.delete(frameId);
384
- contextId = await this.ensureFrameContextId(frameId);
385
- await this.evaluateWithGuard(contextId, script, timeout);
386
417
  }
387
418
  }
388
419
  async initialize() {
@@ -502,7 +533,8 @@ async function waitForVisualStabilityAcrossFrames(page, options = {}) {
502
533
  await runtime.waitForFrameVisualStability(
503
534
  frameId,
504
535
  remaining,
505
- settleMs
536
+ settleMs,
537
+ false
506
538
  );
507
539
  } catch (error) {
508
540
  if (isIgnorableFrameError(error)) return;
@@ -540,6 +572,11 @@ function sameFrameIds(before, after) {
540
572
  function formatCdpException(details) {
541
573
  return details.exception?.description || details.text || "CDP runtime evaluation failed.";
542
574
  }
575
+ function isTransientExecutionContextError(error) {
576
+ if (!(error instanceof Error)) return false;
577
+ const message = error.message;
578
+ return message.includes("Execution context was destroyed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
579
+ }
543
580
  function isMissingExecutionContextError(error) {
544
581
  if (!(error instanceof Error)) return false;
545
582
  const message = error.message;
@@ -548,7 +585,7 @@ function isMissingExecutionContextError(error) {
548
585
  function isIgnorableFrameError(error) {
549
586
  if (!(error instanceof Error)) return false;
550
587
  const message = error.message;
551
- return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context") || message.includes("No frame for given id found");
588
+ return message.includes("Frame was detached") || message.includes("Target page, context or browser has been closed") || isTransientExecutionContextError(error) || message.includes("No frame for given id found");
552
589
  }
553
590
  function sleep(ms) {
554
591
  return new Promise((resolve) => {
@@ -6177,6 +6214,11 @@ function resolveOpensteerApiKey(env) {
6177
6214
  if (!value) return void 0;
6178
6215
  return value;
6179
6216
  }
6217
+ function resolveOpensteerBaseUrl(env) {
6218
+ const value = env.OPENSTEER_BASE_URL?.trim();
6219
+ if (!value) return void 0;
6220
+ return value;
6221
+ }
6180
6222
  function resolveOpensteerAuthScheme(env) {
6181
6223
  return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
6182
6224
  }
@@ -6255,6 +6297,7 @@ function resolveConfig(input = {}) {
6255
6297
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
6256
6298
  const resolved = mergeDeep(mergedWithEnv, input);
6257
6299
  const envApiKey = resolveOpensteerApiKey(env);
6300
+ const envBaseUrl = resolveOpensteerBaseUrl(env);
6258
6301
  const envAuthScheme = resolveOpensteerAuthScheme(env);
6259
6302
  const envCloudAnnounce = parseCloudAnnounce(
6260
6303
  env.OPENSTEER_REMOTE_ANNOUNCE,
@@ -6272,6 +6315,9 @@ function resolveConfig(input = {}) {
6272
6315
  const inputHasCloudApiKey = Boolean(
6273
6316
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
6274
6317
  );
6318
+ const inputHasCloudBaseUrl = Boolean(
6319
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
6320
+ );
6275
6321
  const cloudSelection = resolveCloudSelection({
6276
6322
  cloud: resolved.cloud
6277
6323
  }, env);
@@ -6291,6 +6337,12 @@ function resolveConfig(input = {}) {
6291
6337
  apiKey: envApiKey
6292
6338
  };
6293
6339
  }
6340
+ if (envBaseUrl && cloudSelection.cloud && !inputHasCloudBaseUrl) {
6341
+ resolved.cloud = {
6342
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
6343
+ baseUrl: envBaseUrl
6344
+ };
6345
+ }
6294
6346
  return resolved;
6295
6347
  }
6296
6348
  function resolveNamespace(config, rootDir) {
@@ -7685,12 +7737,16 @@ function clonePersistedExtractNode(node) {
7685
7737
 
7686
7738
  // src/cloud/runtime.ts
7687
7739
  var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
7688
- var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
7689
- function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
7740
+ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
7741
+ const normalizedBaseUrl = normalizeCloudBaseUrl(baseUrl);
7690
7742
  return {
7691
- sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
7743
+ sessionClient: new CloudSessionClient(
7744
+ normalizedBaseUrl,
7745
+ key,
7746
+ authScheme
7747
+ ),
7692
7748
  cdpClient: new CloudCdpClient(),
7693
- appUrl: normalizeCloudAppUrl(appUrl),
7749
+ baseUrl: normalizedBaseUrl,
7694
7750
  actionClient: null,
7695
7751
  sessionId: null,
7696
7752
  localRunId: null,
@@ -7700,15 +7756,9 @@ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authSchem
7700
7756
  function resolveCloudBaseUrl() {
7701
7757
  const value = process.env.OPENSTEER_BASE_URL?.trim();
7702
7758
  if (!value) return DEFAULT_CLOUD_BASE_URL;
7703
- return value.replace(/\/+$/, "");
7759
+ return normalizeCloudBaseUrl(value);
7704
7760
  }
7705
- function resolveCloudAppUrl() {
7706
- const value = process.env.OPENSTEER_APP_URL?.trim();
7707
- if (!value) return DEFAULT_CLOUD_APP_URL;
7708
- return normalizeCloudAppUrl(value);
7709
- }
7710
- function normalizeCloudAppUrl(value) {
7711
- if (!value) return null;
7761
+ function normalizeCloudBaseUrl(value) {
7712
7762
  return value.replace(/\/+$/, "");
7713
7763
  }
7714
7764
  function readCloudActionDescription(payload) {
@@ -7766,8 +7816,7 @@ var Opensteer = class _Opensteer {
7766
7816
  this.cloud = createCloudRuntimeState(
7767
7817
  apiKey,
7768
7818
  cloudConfig?.baseUrl,
7769
- cloudConfig?.authScheme,
7770
- cloudConfig?.appUrl
7819
+ cloudConfig?.authScheme
7771
7820
  );
7772
7821
  } else {
7773
7822
  this.cloud = null;
@@ -7959,10 +8008,7 @@ var Opensteer = class _Opensteer {
7959
8008
  this.snapshotCache = null;
7960
8009
  this.cloud.actionClient = actionClient;
7961
8010
  this.cloud.sessionId = sessionId;
7962
- this.cloud.cloudSessionUrl = buildCloudSessionUrl(
7963
- this.cloud.appUrl,
7964
- session2.cloudSession.sessionId
7965
- );
8011
+ this.cloud.cloudSessionUrl = session2.cloudSessionUrl;
7966
8012
  this.announceCloudSession({
7967
8013
  sessionId: session2.sessionId,
7968
8014
  workspaceId: session2.cloudSession.workspaceId,
@@ -10019,12 +10065,6 @@ function buildLocalRunId(namespace) {
10019
10065
  const normalized = namespace.trim() || "default";
10020
10066
  return `${normalized}-${Date.now().toString(36)}-${randomUUID2().slice(0, 8)}`;
10021
10067
  }
10022
- function buildCloudSessionUrl(appUrl, sessionId) {
10023
- if (!appUrl) {
10024
- return null;
10025
- }
10026
- return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10027
- }
10028
10068
 
10029
10069
  export {
10030
10070
  normalizeNamespace,
@@ -1036,6 +1036,11 @@ function resolveOpensteerApiKey(env) {
1036
1036
  if (!value) return void 0;
1037
1037
  return value;
1038
1038
  }
1039
+ function resolveOpensteerBaseUrl(env) {
1040
+ const value = env.OPENSTEER_BASE_URL?.trim();
1041
+ if (!value) return void 0;
1042
+ return value;
1043
+ }
1039
1044
  function resolveOpensteerAuthScheme(env) {
1040
1045
  return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
1041
1046
  }
@@ -1114,6 +1119,7 @@ function resolveConfig(input = {}) {
1114
1119
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1115
1120
  const resolved = mergeDeep(mergedWithEnv, input);
1116
1121
  const envApiKey = resolveOpensteerApiKey(env);
1122
+ const envBaseUrl = resolveOpensteerBaseUrl(env);
1117
1123
  const envAuthScheme = resolveOpensteerAuthScheme(env);
1118
1124
  const envCloudAnnounce = parseCloudAnnounce(
1119
1125
  env.OPENSTEER_REMOTE_ANNOUNCE,
@@ -1131,6 +1137,9 @@ function resolveConfig(input = {}) {
1131
1137
  const inputHasCloudApiKey = Boolean(
1132
1138
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
1133
1139
  );
1140
+ const inputHasCloudBaseUrl = Boolean(
1141
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
1142
+ );
1134
1143
  const cloudSelection = resolveCloudSelection({
1135
1144
  cloud: resolved.cloud
1136
1145
  }, env);
@@ -1150,6 +1159,12 @@ function resolveConfig(input = {}) {
1150
1159
  apiKey: envApiKey
1151
1160
  };
1152
1161
  }
1162
+ if (envBaseUrl && cloudSelection.cloud && !inputHasCloudBaseUrl) {
1163
+ resolved.cloud = {
1164
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
1165
+ baseUrl: envBaseUrl
1166
+ };
1167
+ }
1153
1168
  return resolved;
1154
1169
  }
1155
1170
  function resolveNamespace(config, rootDir) {
@@ -1190,6 +1205,7 @@ function getCallerFilePath() {
1190
1205
  var DEFAULT_TIMEOUT = 3e4;
1191
1206
  var DEFAULT_SETTLE_MS = 750;
1192
1207
  var FRAME_EVALUATE_GRACE_MS = 200;
1208
+ var TRANSIENT_CONTEXT_RETRY_DELAY_MS = 25;
1193
1209
  var STEALTH_WORLD_NAME = "__opensteer_wait__";
1194
1210
  var StealthWaitUnavailableError = class extends Error {
1195
1211
  constructor(cause) {
@@ -1493,7 +1509,12 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
1493
1509
  const frameRecords = await this.getFrameRecords();
1494
1510
  const mainFrame = frameRecords[0];
1495
1511
  if (!mainFrame) return;
1496
- await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
1512
+ await this.waitForFrameVisualStability(
1513
+ mainFrame.frameId,
1514
+ timeout,
1515
+ settleMs,
1516
+ true
1517
+ );
1497
1518
  }
1498
1519
  async collectVisibleFrameIds() {
1499
1520
  const frameRecords = await this.getFrameRecords();
@@ -1522,19 +1543,44 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
1522
1543
  }
1523
1544
  return visibleFrameIds;
1524
1545
  }
1525
- async waitForFrameVisualStability(frameId, timeout, settleMs) {
1546
+ async waitForFrameVisualStability(frameId, timeout, settleMs, retryTransientContextErrors = true) {
1526
1547
  if (timeout <= 0) return;
1527
1548
  const script = buildStabilityScript(timeout, settleMs);
1528
- let contextId = await this.ensureFrameContextId(frameId);
1529
- try {
1530
- await this.evaluateWithGuard(contextId, script, timeout);
1531
- } catch (error) {
1532
- if (!isMissingExecutionContextError(error)) {
1533
- throw error;
1549
+ if (!retryTransientContextErrors) {
1550
+ let contextId = await this.ensureFrameContextId(frameId);
1551
+ try {
1552
+ await this.evaluateWithGuard(contextId, script, timeout);
1553
+ } catch (error) {
1554
+ if (!isMissingExecutionContextError(error)) {
1555
+ throw error;
1556
+ }
1557
+ this.contextsByFrame.delete(frameId);
1558
+ contextId = await this.ensureFrameContextId(frameId);
1559
+ await this.evaluateWithGuard(contextId, script, timeout);
1560
+ }
1561
+ return;
1562
+ }
1563
+ const deadline = Date.now() + timeout;
1564
+ while (true) {
1565
+ const remaining = Math.max(0, deadline - Date.now());
1566
+ if (remaining === 0) {
1567
+ return;
1568
+ }
1569
+ const contextId = await this.ensureFrameContextId(frameId);
1570
+ try {
1571
+ await this.evaluateWithGuard(contextId, script, remaining);
1572
+ return;
1573
+ } catch (error) {
1574
+ if (!isTransientExecutionContextError(error)) {
1575
+ throw error;
1576
+ }
1577
+ this.contextsByFrame.delete(frameId);
1578
+ const retryDelay = Math.min(
1579
+ TRANSIENT_CONTEXT_RETRY_DELAY_MS,
1580
+ Math.max(0, deadline - Date.now())
1581
+ );
1582
+ await sleep(retryDelay);
1534
1583
  }
1535
- this.contextsByFrame.delete(frameId);
1536
- contextId = await this.ensureFrameContextId(frameId);
1537
- await this.evaluateWithGuard(contextId, script, timeout);
1538
1584
  }
1539
1585
  }
1540
1586
  async initialize() {
@@ -1654,7 +1700,8 @@ async function waitForVisualStabilityAcrossFrames(page, options = {}) {
1654
1700
  await runtime.waitForFrameVisualStability(
1655
1701
  frameId,
1656
1702
  remaining,
1657
- settleMs
1703
+ settleMs,
1704
+ false
1658
1705
  );
1659
1706
  } catch (error) {
1660
1707
  if (isIgnorableFrameError(error)) return;
@@ -1692,6 +1739,11 @@ function sameFrameIds(before, after) {
1692
1739
  function formatCdpException(details) {
1693
1740
  return details.exception?.description || details.text || "CDP runtime evaluation failed.";
1694
1741
  }
1742
+ function isTransientExecutionContextError(error) {
1743
+ if (!(error instanceof Error)) return false;
1744
+ const message = error.message;
1745
+ return message.includes("Execution context was destroyed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
1746
+ }
1695
1747
  function isMissingExecutionContextError(error) {
1696
1748
  if (!(error instanceof Error)) return false;
1697
1749
  const message = error.message;
@@ -1700,7 +1752,7 @@ function isMissingExecutionContextError(error) {
1700
1752
  function isIgnorableFrameError(error) {
1701
1753
  if (!(error instanceof Error)) return false;
1702
1754
  const message = error.message;
1703
- return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context") || message.includes("No frame for given id found");
1755
+ return message.includes("Frame was detached") || message.includes("Target page, context or browser has been closed") || isTransientExecutionContextError(error) || message.includes("No frame for given id found");
1704
1756
  }
1705
1757
  function sleep(ms) {
1706
1758
  return new Promise((resolve) => {
@@ -8019,12 +8071,16 @@ function toCloudErrorCode(code) {
8019
8071
 
8020
8072
  // src/cloud/runtime.ts
8021
8073
  var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
8022
- var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
8023
- function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
8074
+ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
8075
+ const normalizedBaseUrl = normalizeCloudBaseUrl(baseUrl);
8024
8076
  return {
8025
- sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
8077
+ sessionClient: new CloudSessionClient(
8078
+ normalizedBaseUrl,
8079
+ key,
8080
+ authScheme
8081
+ ),
8026
8082
  cdpClient: new CloudCdpClient(),
8027
- appUrl: normalizeCloudAppUrl(appUrl),
8083
+ baseUrl: normalizedBaseUrl,
8028
8084
  actionClient: null,
8029
8085
  sessionId: null,
8030
8086
  localRunId: null,
@@ -8034,15 +8090,9 @@ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authSchem
8034
8090
  function resolveCloudBaseUrl() {
8035
8091
  const value = process.env.OPENSTEER_BASE_URL?.trim();
8036
8092
  if (!value) return DEFAULT_CLOUD_BASE_URL;
8037
- return value.replace(/\/+$/, "");
8093
+ return normalizeCloudBaseUrl(value);
8038
8094
  }
8039
- function resolveCloudAppUrl() {
8040
- const value = process.env.OPENSTEER_APP_URL?.trim();
8041
- if (!value) return DEFAULT_CLOUD_APP_URL;
8042
- return normalizeCloudAppUrl(value);
8043
- }
8044
- function normalizeCloudAppUrl(value) {
8045
- if (!value) return null;
8095
+ function normalizeCloudBaseUrl(value) {
8046
8096
  return value.replace(/\/+$/, "");
8047
8097
  }
8048
8098
  function readCloudActionDescription(payload) {
@@ -8100,8 +8150,7 @@ var Opensteer = class _Opensteer {
8100
8150
  this.cloud = createCloudRuntimeState(
8101
8151
  apiKey,
8102
8152
  cloudConfig?.baseUrl,
8103
- cloudConfig?.authScheme,
8104
- cloudConfig?.appUrl
8153
+ cloudConfig?.authScheme
8105
8154
  );
8106
8155
  } else {
8107
8156
  this.cloud = null;
@@ -8293,10 +8342,7 @@ var Opensteer = class _Opensteer {
8293
8342
  this.snapshotCache = null;
8294
8343
  this.cloud.actionClient = actionClient;
8295
8344
  this.cloud.sessionId = sessionId;
8296
- this.cloud.cloudSessionUrl = buildCloudSessionUrl(
8297
- this.cloud.appUrl,
8298
- session3.cloudSession.sessionId
8299
- );
8345
+ this.cloud.cloudSessionUrl = session3.cloudSessionUrl;
8300
8346
  this.announceCloudSession({
8301
8347
  sessionId: session3.sessionId,
8302
8348
  workspaceId: session3.cloudSession.workspaceId,
@@ -10353,12 +10399,6 @@ function buildLocalRunId(namespace) {
10353
10399
  const normalized = namespace.trim() || "default";
10354
10400
  return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
10355
10401
  }
10356
- function buildCloudSessionUrl(appUrl, sessionId) {
10357
- if (!appUrl) {
10358
- return null;
10359
- }
10360
- return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10361
- }
10362
10402
 
10363
10403
  // src/cli/paths.ts
10364
10404
  var import_os2 = require("os");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Opensteer
3
- } from "../chunk-GQ7HNCM2.js";
3
+ } from "../chunk-C3NM6XZH.js";
4
4
  import "../chunk-3H5RRIMZ.js";
5
5
 
6
6
  // src/cli/server.ts
package/dist/index.cjs CHANGED
@@ -1111,6 +1111,11 @@ function resolveOpensteerApiKey(env) {
1111
1111
  if (!value) return void 0;
1112
1112
  return value;
1113
1113
  }
1114
+ function resolveOpensteerBaseUrl(env) {
1115
+ const value = env.OPENSTEER_BASE_URL?.trim();
1116
+ if (!value) return void 0;
1117
+ return value;
1118
+ }
1114
1119
  function resolveOpensteerAuthScheme(env) {
1115
1120
  return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
1116
1121
  }
@@ -1189,6 +1194,7 @@ function resolveConfig(input = {}) {
1189
1194
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1190
1195
  const resolved = mergeDeep(mergedWithEnv, input);
1191
1196
  const envApiKey = resolveOpensteerApiKey(env);
1197
+ const envBaseUrl = resolveOpensteerBaseUrl(env);
1192
1198
  const envAuthScheme = resolveOpensteerAuthScheme(env);
1193
1199
  const envCloudAnnounce = parseCloudAnnounce(
1194
1200
  env.OPENSTEER_REMOTE_ANNOUNCE,
@@ -1206,6 +1212,9 @@ function resolveConfig(input = {}) {
1206
1212
  const inputHasCloudApiKey = Boolean(
1207
1213
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
1208
1214
  );
1215
+ const inputHasCloudBaseUrl = Boolean(
1216
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
1217
+ );
1209
1218
  const cloudSelection = resolveCloudSelection({
1210
1219
  cloud: resolved.cloud
1211
1220
  }, env);
@@ -1225,6 +1234,12 @@ function resolveConfig(input = {}) {
1225
1234
  apiKey: envApiKey
1226
1235
  };
1227
1236
  }
1237
+ if (envBaseUrl && cloudSelection.cloud && !inputHasCloudBaseUrl) {
1238
+ resolved.cloud = {
1239
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
1240
+ baseUrl: envBaseUrl
1241
+ };
1242
+ }
1228
1243
  return resolved;
1229
1244
  }
1230
1245
  function resolveNamespace(config, rootDir) {
@@ -1265,6 +1280,7 @@ function getCallerFilePath() {
1265
1280
  var DEFAULT_TIMEOUT = 3e4;
1266
1281
  var DEFAULT_SETTLE_MS = 750;
1267
1282
  var FRAME_EVALUATE_GRACE_MS = 200;
1283
+ var TRANSIENT_CONTEXT_RETRY_DELAY_MS = 25;
1268
1284
  var STEALTH_WORLD_NAME = "__opensteer_wait__";
1269
1285
  var StealthWaitUnavailableError = class extends Error {
1270
1286
  constructor(cause) {
@@ -1568,7 +1584,12 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
1568
1584
  const frameRecords = await this.getFrameRecords();
1569
1585
  const mainFrame = frameRecords[0];
1570
1586
  if (!mainFrame) return;
1571
- await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
1587
+ await this.waitForFrameVisualStability(
1588
+ mainFrame.frameId,
1589
+ timeout,
1590
+ settleMs,
1591
+ true
1592
+ );
1572
1593
  }
1573
1594
  async collectVisibleFrameIds() {
1574
1595
  const frameRecords = await this.getFrameRecords();
@@ -1597,19 +1618,44 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
1597
1618
  }
1598
1619
  return visibleFrameIds;
1599
1620
  }
1600
- async waitForFrameVisualStability(frameId, timeout, settleMs) {
1621
+ async waitForFrameVisualStability(frameId, timeout, settleMs, retryTransientContextErrors = true) {
1601
1622
  if (timeout <= 0) return;
1602
1623
  const script = buildStabilityScript(timeout, settleMs);
1603
- let contextId = await this.ensureFrameContextId(frameId);
1604
- try {
1605
- await this.evaluateWithGuard(contextId, script, timeout);
1606
- } catch (error) {
1607
- if (!isMissingExecutionContextError(error)) {
1608
- throw error;
1624
+ if (!retryTransientContextErrors) {
1625
+ let contextId = await this.ensureFrameContextId(frameId);
1626
+ try {
1627
+ await this.evaluateWithGuard(contextId, script, timeout);
1628
+ } catch (error) {
1629
+ if (!isMissingExecutionContextError(error)) {
1630
+ throw error;
1631
+ }
1632
+ this.contextsByFrame.delete(frameId);
1633
+ contextId = await this.ensureFrameContextId(frameId);
1634
+ await this.evaluateWithGuard(contextId, script, timeout);
1635
+ }
1636
+ return;
1637
+ }
1638
+ const deadline = Date.now() + timeout;
1639
+ while (true) {
1640
+ const remaining = Math.max(0, deadline - Date.now());
1641
+ if (remaining === 0) {
1642
+ return;
1643
+ }
1644
+ const contextId = await this.ensureFrameContextId(frameId);
1645
+ try {
1646
+ await this.evaluateWithGuard(contextId, script, remaining);
1647
+ return;
1648
+ } catch (error) {
1649
+ if (!isTransientExecutionContextError(error)) {
1650
+ throw error;
1651
+ }
1652
+ this.contextsByFrame.delete(frameId);
1653
+ const retryDelay = Math.min(
1654
+ TRANSIENT_CONTEXT_RETRY_DELAY_MS,
1655
+ Math.max(0, deadline - Date.now())
1656
+ );
1657
+ await sleep(retryDelay);
1609
1658
  }
1610
- this.contextsByFrame.delete(frameId);
1611
- contextId = await this.ensureFrameContextId(frameId);
1612
- await this.evaluateWithGuard(contextId, script, timeout);
1613
1659
  }
1614
1660
  }
1615
1661
  async initialize() {
@@ -1729,7 +1775,8 @@ async function waitForVisualStabilityAcrossFrames(page, options = {}) {
1729
1775
  await runtime.waitForFrameVisualStability(
1730
1776
  frameId,
1731
1777
  remaining,
1732
- settleMs
1778
+ settleMs,
1779
+ false
1733
1780
  );
1734
1781
  } catch (error) {
1735
1782
  if (isIgnorableFrameError(error)) return;
@@ -1767,6 +1814,11 @@ function sameFrameIds(before, after) {
1767
1814
  function formatCdpException(details) {
1768
1815
  return details.exception?.description || details.text || "CDP runtime evaluation failed.";
1769
1816
  }
1817
+ function isTransientExecutionContextError(error) {
1818
+ if (!(error instanceof Error)) return false;
1819
+ const message = error.message;
1820
+ return message.includes("Execution context was destroyed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
1821
+ }
1770
1822
  function isMissingExecutionContextError(error) {
1771
1823
  if (!(error instanceof Error)) return false;
1772
1824
  const message = error.message;
@@ -1775,7 +1827,7 @@ function isMissingExecutionContextError(error) {
1775
1827
  function isIgnorableFrameError(error) {
1776
1828
  if (!(error instanceof Error)) return false;
1777
1829
  const message = error.message;
1778
- return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context") || message.includes("No frame for given id found");
1830
+ return message.includes("Frame was detached") || message.includes("Target page, context or browser has been closed") || isTransientExecutionContextError(error) || message.includes("No frame for given id found");
1779
1831
  }
1780
1832
  function sleep(ms) {
1781
1833
  return new Promise((resolve) => {
@@ -8104,12 +8156,16 @@ function toCloudErrorCode(code) {
8104
8156
 
8105
8157
  // src/cloud/runtime.ts
8106
8158
  var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
8107
- var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
8108
- function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
8159
+ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
8160
+ const normalizedBaseUrl = normalizeCloudBaseUrl(baseUrl);
8109
8161
  return {
8110
- sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
8162
+ sessionClient: new CloudSessionClient(
8163
+ normalizedBaseUrl,
8164
+ key,
8165
+ authScheme
8166
+ ),
8111
8167
  cdpClient: new CloudCdpClient(),
8112
- appUrl: normalizeCloudAppUrl(appUrl),
8168
+ baseUrl: normalizedBaseUrl,
8113
8169
  actionClient: null,
8114
8170
  sessionId: null,
8115
8171
  localRunId: null,
@@ -8119,15 +8175,9 @@ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authSchem
8119
8175
  function resolveCloudBaseUrl() {
8120
8176
  const value = process.env.OPENSTEER_BASE_URL?.trim();
8121
8177
  if (!value) return DEFAULT_CLOUD_BASE_URL;
8122
- return value.replace(/\/+$/, "");
8178
+ return normalizeCloudBaseUrl(value);
8123
8179
  }
8124
- function resolveCloudAppUrl() {
8125
- const value = process.env.OPENSTEER_APP_URL?.trim();
8126
- if (!value) return DEFAULT_CLOUD_APP_URL;
8127
- return normalizeCloudAppUrl(value);
8128
- }
8129
- function normalizeCloudAppUrl(value) {
8130
- if (!value) return null;
8180
+ function normalizeCloudBaseUrl(value) {
8131
8181
  return value.replace(/\/+$/, "");
8132
8182
  }
8133
8183
  function readCloudActionDescription(payload) {
@@ -8185,8 +8235,7 @@ var Opensteer = class _Opensteer {
8185
8235
  this.cloud = createCloudRuntimeState(
8186
8236
  apiKey,
8187
8237
  cloudConfig?.baseUrl,
8188
- cloudConfig?.authScheme,
8189
- cloudConfig?.appUrl
8238
+ cloudConfig?.authScheme
8190
8239
  );
8191
8240
  } else {
8192
8241
  this.cloud = null;
@@ -8378,10 +8427,7 @@ var Opensteer = class _Opensteer {
8378
8427
  this.snapshotCache = null;
8379
8428
  this.cloud.actionClient = actionClient;
8380
8429
  this.cloud.sessionId = sessionId;
8381
- this.cloud.cloudSessionUrl = buildCloudSessionUrl(
8382
- this.cloud.appUrl,
8383
- session2.cloudSession.sessionId
8384
- );
8430
+ this.cloud.cloudSessionUrl = session2.cloudSessionUrl;
8385
8431
  this.announceCloudSession({
8386
8432
  sessionId: session2.sessionId,
8387
8433
  workspaceId: session2.cloudSession.workspaceId,
@@ -10438,12 +10484,6 @@ function buildLocalRunId(namespace) {
10438
10484
  const normalized = namespace.trim() || "default";
10439
10485
  return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
10440
10486
  }
10441
- function buildCloudSessionUrl(appUrl, sessionId) {
10442
- if (!appUrl) {
10443
- return null;
10444
- }
10445
- return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10446
- }
10447
10487
 
10448
10488
  // src/ai/index.ts
10449
10489
  init_resolver();
package/dist/index.d.cts CHANGED
@@ -127,7 +127,6 @@ type OpensteerCloudAnnouncePolicy = 'always' | 'off' | 'tty';
127
127
  interface OpensteerCloudOptions {
128
128
  apiKey?: string;
129
129
  baseUrl?: string;
130
- appUrl?: string;
131
130
  authScheme?: OpensteerAuthScheme;
132
131
  announce?: OpensteerCloudAnnouncePolicy;
133
132
  }
package/dist/index.d.ts CHANGED
@@ -127,7 +127,6 @@ type OpensteerCloudAnnouncePolicy = 'always' | 'off' | 'tty';
127
127
  interface OpensteerCloudOptions {
128
128
  apiKey?: string;
129
129
  baseUrl?: string;
130
- appUrl?: string;
131
130
  authScheme?: OpensteerAuthScheme;
132
131
  announce?: OpensteerCloudAnnouncePolicy;
133
132
  }
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ import {
68
68
  switchTab,
69
69
  typeText,
70
70
  waitForVisualStability
71
- } from "./chunk-GQ7HNCM2.js";
71
+ } from "./chunk-C3NM6XZH.js";
72
72
  import {
73
73
  createResolveCallback
74
74
  } from "./chunk-SRJLH34D.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensteer",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "Open-source browser automation SDK with robust selectors and deterministic replay.",
5
5
  "license": "MIT",
6
6
  "type": "module",