opensteer 0.4.6 → 0.4.8

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.
@@ -752,7 +752,7 @@ var BrowserPool = class {
752
752
  const context = contexts[0];
753
753
  const pages = context.pages();
754
754
  const page = pages.length > 0 ? pages[0] : await context.newPage();
755
- return { browser, context, page, isRemote: true };
755
+ return { browser, context, page, isExternal: true };
756
756
  } catch (error) {
757
757
  if (browser) {
758
758
  await browser.close().catch(() => void 0);
@@ -787,7 +787,7 @@ var BrowserPool = class {
787
787
  context = await browser.newContext(options.context || {});
788
788
  page = await context.newPage();
789
789
  }
790
- return { browser, context, page, isRemote: false };
790
+ return { browser, context, page, isExternal: false };
791
791
  }
792
792
  async launchSandbox(options) {
793
793
  const browser = await import_playwright.chromium.launch({
@@ -798,7 +798,7 @@ var BrowserPool = class {
798
798
  const context = await browser.newContext(options.context || {});
799
799
  const page = await context.newPage();
800
800
  this.browser = browser;
801
- return { browser, context, page, isRemote: false };
801
+ return { browser, context, page, isExternal: false };
802
802
  }
803
803
  };
804
804
 
@@ -806,6 +806,7 @@ var BrowserPool = class {
806
806
  var import_fs = __toESM(require("fs"), 1);
807
807
  var import_path3 = __toESM(require("path"), 1);
808
808
  var import_url = require("url");
809
+ var import_dotenv = require("dotenv");
809
810
 
810
811
  // src/storage/namespace.ts
811
812
  var import_path2 = __toESM(require("path"), 1);
@@ -855,6 +856,53 @@ var DEFAULT_CONFIG = {
855
856
  model: "gpt-5.1",
856
857
  debug: false
857
858
  };
859
+ function dotenvFileOrder(nodeEnv) {
860
+ const normalized = nodeEnv?.trim() || "";
861
+ const files = [];
862
+ if (normalized) {
863
+ files.push(`.env.${normalized}.local`);
864
+ }
865
+ if (normalized !== "test") {
866
+ files.push(".env.local");
867
+ }
868
+ if (normalized) {
869
+ files.push(`.env.${normalized}`);
870
+ }
871
+ files.push(".env");
872
+ return files;
873
+ }
874
+ function loadDotenvValues(rootDir, baseEnv) {
875
+ const values = {};
876
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
877
+ return values;
878
+ }
879
+ const baseDir = import_path3.default.resolve(rootDir);
880
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
881
+ for (const filename of dotenvFileOrder(nodeEnv)) {
882
+ const filePath = import_path3.default.join(baseDir, filename);
883
+ if (!import_fs.default.existsSync(filePath)) continue;
884
+ try {
885
+ const raw = import_fs.default.readFileSync(filePath, "utf8");
886
+ const parsed = (0, import_dotenv.parse)(raw);
887
+ for (const [key, value] of Object.entries(parsed)) {
888
+ if (values[key] === void 0) {
889
+ values[key] = value;
890
+ }
891
+ }
892
+ } catch {
893
+ continue;
894
+ }
895
+ }
896
+ return values;
897
+ }
898
+ function resolveEnv(rootDir) {
899
+ const baseEnv = process.env;
900
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
901
+ return {
902
+ ...dotenvValues,
903
+ ...baseEnv
904
+ };
905
+ }
858
906
  function hasOwn(config, key) {
859
907
  if (!config || typeof config !== "object") return false;
860
908
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -869,28 +917,27 @@ function assertNoLegacyAiConfig(source, config) {
869
917
  );
870
918
  }
871
919
  }
872
- function assertNoLegacyModeConfig(source, config) {
920
+ function assertNoLegacyRuntimeConfig(source, config) {
873
921
  if (!config || typeof config !== "object") return;
874
922
  const configRecord = config;
875
923
  if (hasOwn(configRecord, "runtime")) {
876
924
  throw new Error(
877
- `Legacy "runtime" config is no longer supported in ${source}. Use top-level "mode" instead.`
925
+ `Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
878
926
  );
879
927
  }
880
- if (hasOwn(configRecord, "apiKey")) {
928
+ if (hasOwn(configRecord, "mode")) {
881
929
  throw new Error(
882
- `Top-level "apiKey" config is not supported in ${source}. Use "remote.apiKey" instead.`
930
+ `Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
883
931
  );
884
932
  }
885
- const remoteValue = configRecord.remote;
886
- if (typeof remoteValue === "boolean") {
933
+ if (hasOwn(configRecord, "remote")) {
887
934
  throw new Error(
888
- `Boolean "remote" config is no longer supported in ${source}. Use "mode: \\"remote\\"" with "remote" options.`
935
+ `Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
889
936
  );
890
937
  }
891
- if (remoteValue && typeof remoteValue === "object" && !Array.isArray(remoteValue) && hasOwn(remoteValue, "key")) {
938
+ if (hasOwn(configRecord, "apiKey")) {
892
939
  throw new Error(
893
- `Legacy "remote.key" config is no longer supported in ${source}. Use "remote.apiKey" instead.`
940
+ `Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
894
941
  );
895
942
  }
896
943
  }
@@ -936,20 +983,20 @@ function parseNumber(value) {
936
983
  if (!Number.isFinite(parsed)) return void 0;
937
984
  return parsed;
938
985
  }
939
- function parseMode(value, source) {
986
+ function parseRuntimeMode(value, source) {
940
987
  if (value == null) return void 0;
941
988
  if (typeof value !== "string") {
942
989
  throw new Error(
943
- `Invalid ${source} value "${String(value)}". Use "local" or "remote".`
990
+ `Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
944
991
  );
945
992
  }
946
993
  const normalized = value.trim().toLowerCase();
947
994
  if (!normalized) return void 0;
948
- if (normalized === "local" || normalized === "remote") {
995
+ if (normalized === "local" || normalized === "cloud") {
949
996
  return normalized;
950
997
  }
951
998
  throw new Error(
952
- `Invalid ${source} value "${value}". Use "local" or "remote".`
999
+ `Invalid ${source} value "${value}". Use "local" or "cloud".`
953
1000
  );
954
1001
  }
955
1002
  function parseAuthScheme(value, source) {
@@ -968,99 +1015,138 @@ function parseAuthScheme(value, source) {
968
1015
  `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
969
1016
  );
970
1017
  }
971
- function resolveOpensteerApiKey() {
972
- const value = process.env.OPENSTEER_API_KEY?.trim();
1018
+ function parseCloudAnnounce(value, source) {
1019
+ if (value == null) return void 0;
1020
+ if (typeof value !== "string") {
1021
+ throw new Error(
1022
+ `Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
1023
+ );
1024
+ }
1025
+ const normalized = value.trim().toLowerCase();
1026
+ if (!normalized) return void 0;
1027
+ if (normalized === "always" || normalized === "off" || normalized === "tty") {
1028
+ return normalized;
1029
+ }
1030
+ throw new Error(
1031
+ `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
1032
+ );
1033
+ }
1034
+ function resolveOpensteerApiKey(env) {
1035
+ const value = env.OPENSTEER_API_KEY?.trim();
973
1036
  if (!value) return void 0;
974
1037
  return value;
975
1038
  }
976
- function resolveOpensteerAuthScheme() {
977
- return parseAuthScheme(
978
- process.env.OPENSTEER_AUTH_SCHEME,
979
- "OPENSTEER_AUTH_SCHEME"
980
- );
1039
+ function resolveOpensteerAuthScheme(env) {
1040
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
981
1041
  }
982
- function normalizeRemoteOptions(value) {
1042
+ function normalizeCloudOptions(value) {
983
1043
  if (!value || typeof value !== "object" || Array.isArray(value)) {
984
1044
  return void 0;
985
1045
  }
986
1046
  return value;
987
1047
  }
988
- function resolveModeSelection(config) {
989
- const configMode = parseMode(config.mode, "mode");
990
- if (configMode) {
1048
+ function parseCloudEnabled(value, source) {
1049
+ if (value == null) return void 0;
1050
+ if (typeof value === "boolean") return value;
1051
+ if (typeof value === "object" && !Array.isArray(value)) return true;
1052
+ throw new Error(
1053
+ `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
1054
+ );
1055
+ }
1056
+ function resolveCloudSelection(config, env = process.env) {
1057
+ const configCloud = parseCloudEnabled(config.cloud, "cloud");
1058
+ if (configCloud !== void 0) {
991
1059
  return {
992
- mode: configMode,
993
- source: "config.mode"
1060
+ cloud: configCloud,
1061
+ source: "config.cloud"
994
1062
  };
995
1063
  }
996
- const envMode = parseMode(process.env.OPENSTEER_MODE, "OPENSTEER_MODE");
1064
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
997
1065
  if (envMode) {
998
1066
  return {
999
- mode: envMode,
1067
+ cloud: envMode === "cloud",
1000
1068
  source: "env.OPENSTEER_MODE"
1001
1069
  };
1002
1070
  }
1003
1071
  return {
1004
- mode: "local",
1072
+ cloud: false,
1005
1073
  source: "default"
1006
1074
  };
1007
1075
  }
1008
1076
  function resolveConfig(input = {}) {
1009
- if (process.env.OPENSTEER_AI_MODEL) {
1077
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
1078
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
1079
+ storage: {
1080
+ rootDir: initialRootDir
1081
+ }
1082
+ });
1083
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
1084
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1085
+ const fileConfig = loadConfigFile(initialRootDir);
1086
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1087
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1088
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
1089
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
1090
+ const env = resolveEnv(envRootDir);
1091
+ if (env.OPENSTEER_AI_MODEL) {
1010
1092
  throw new Error(
1011
1093
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
1012
1094
  );
1013
1095
  }
1014
- if (process.env.OPENSTEER_RUNTIME != null) {
1096
+ if (env.OPENSTEER_RUNTIME != null) {
1015
1097
  throw new Error(
1016
1098
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
1017
1099
  );
1018
1100
  }
1019
- assertNoLegacyAiConfig("Opensteer constructor config", input);
1020
- assertNoLegacyModeConfig("Opensteer constructor config", input);
1021
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
1022
- const fileConfig = loadConfigFile(rootDir);
1023
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1024
- assertNoLegacyModeConfig(".opensteer/config.json", fileConfig);
1025
1101
  const envConfig = {
1026
1102
  browser: {
1027
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
1028
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
1029
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
1030
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
1031
- channel: process.env.OPENSTEER_CHANNEL || void 0,
1032
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
1103
+ headless: parseBool(env.OPENSTEER_HEADLESS),
1104
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
1105
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
1106
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
1107
+ channel: env.OPENSTEER_CHANNEL || void 0,
1108
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
1033
1109
  },
1034
- model: process.env.OPENSTEER_MODEL || void 0,
1035
- debug: parseBool(process.env.OPENSTEER_DEBUG)
1110
+ model: env.OPENSTEER_MODEL || void 0,
1111
+ debug: parseBool(env.OPENSTEER_DEBUG)
1036
1112
  };
1037
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
1113
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
1038
1114
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1039
1115
  const resolved = mergeDeep(mergedWithEnv, input);
1040
- const envApiKey = resolveOpensteerApiKey();
1041
- const envAuthScheme = resolveOpensteerAuthScheme();
1042
- const inputRemoteOptions = normalizeRemoteOptions(input.remote);
1116
+ const envApiKey = resolveOpensteerApiKey(env);
1117
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
1118
+ const envCloudAnnounce = parseCloudAnnounce(
1119
+ env.OPENSTEER_REMOTE_ANNOUNCE,
1120
+ "OPENSTEER_REMOTE_ANNOUNCE"
1121
+ );
1122
+ const inputCloudOptions = normalizeCloudOptions(input.cloud);
1043
1123
  const inputAuthScheme = parseAuthScheme(
1044
- inputRemoteOptions?.authScheme,
1045
- "remote.authScheme"
1124
+ inputCloudOptions?.authScheme,
1125
+ "cloud.authScheme"
1046
1126
  );
1047
- const inputHasRemoteApiKey = Boolean(
1048
- inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
1127
+ const inputCloudAnnounce = parseCloudAnnounce(
1128
+ inputCloudOptions?.announce,
1129
+ "cloud.announce"
1049
1130
  );
1050
- const modeSelection = resolveModeSelection({
1051
- mode: resolved.mode
1052
- });
1053
- if (modeSelection.mode === "remote") {
1054
- const resolvedRemote = normalizeRemoteOptions(resolved.remote) ?? {};
1055
- const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedRemote.authScheme, "remote.authScheme") ?? "api-key";
1056
- resolved.remote = {
1057
- ...resolvedRemote,
1058
- authScheme
1131
+ const inputHasCloudApiKey = Boolean(
1132
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
1133
+ );
1134
+ const cloudSelection = resolveCloudSelection({
1135
+ cloud: resolved.cloud
1136
+ }, env);
1137
+ if (cloudSelection.cloud) {
1138
+ const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
1139
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
1140
+ const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
1141
+ resolved.cloud = {
1142
+ ...resolvedCloud,
1143
+ authScheme,
1144
+ announce
1059
1145
  };
1060
1146
  }
1061
- if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
1062
- resolved.remote = {
1063
- ...normalizeRemoteOptions(resolved.remote) ?? {},
1147
+ if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
1148
+ resolved.cloud = {
1149
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
1064
1150
  apiKey: envApiKey
1065
1151
  };
1066
1152
  }
@@ -7357,36 +7443,39 @@ function clonePersistedExtractNode(node) {
7357
7443
  return JSON.parse(JSON.stringify(node));
7358
7444
  }
7359
7445
 
7360
- // src/remote/action-ws-client.ts
7446
+ // src/cloud/contracts.ts
7447
+ var cloudSessionContractVersion = "v3";
7448
+
7449
+ // src/cloud/action-ws-client.ts
7361
7450
  var import_ws2 = __toESM(require("ws"), 1);
7362
7451
 
7363
- // src/remote/errors.ts
7364
- var OpensteerRemoteError = class extends Error {
7452
+ // src/cloud/errors.ts
7453
+ var OpensteerCloudError = class extends Error {
7365
7454
  code;
7366
7455
  status;
7367
7456
  details;
7368
7457
  constructor(code, message, status, details) {
7369
7458
  super(message);
7370
- this.name = "OpensteerRemoteError";
7459
+ this.name = "OpensteerCloudError";
7371
7460
  this.code = code;
7372
7461
  this.status = status;
7373
7462
  this.details = details;
7374
7463
  }
7375
7464
  };
7376
- function remoteUnsupportedMethodError(method, message) {
7377
- return new OpensteerRemoteError(
7378
- "REMOTE_UNSUPPORTED_METHOD",
7379
- message || `${method} is not supported in remote mode.`
7465
+ function cloudUnsupportedMethodError(method, message) {
7466
+ return new OpensteerCloudError(
7467
+ "CLOUD_UNSUPPORTED_METHOD",
7468
+ message || `${method} is not supported in cloud mode.`
7380
7469
  );
7381
7470
  }
7382
- function remoteNotLaunchedError() {
7383
- return new OpensteerRemoteError(
7384
- "REMOTE_SESSION_NOT_FOUND",
7385
- "Remote session is not connected. Call launch() first."
7471
+ function cloudNotLaunchedError() {
7472
+ return new OpensteerCloudError(
7473
+ "CLOUD_SESSION_NOT_FOUND",
7474
+ "Cloud session is not connected. Call launch() first."
7386
7475
  );
7387
7476
  }
7388
7477
 
7389
- // src/remote/action-ws-client.ts
7478
+ // src/cloud/action-ws-client.ts
7390
7479
  var ActionWsClient = class _ActionWsClient {
7391
7480
  ws;
7392
7481
  sessionId;
@@ -7403,18 +7492,18 @@ var ActionWsClient = class _ActionWsClient {
7403
7492
  });
7404
7493
  ws.on("error", (error) => {
7405
7494
  this.rejectAll(
7406
- new OpensteerRemoteError(
7407
- "REMOTE_TRANSPORT_ERROR",
7408
- `Remote action websocket error: ${error.message}`
7495
+ new OpensteerCloudError(
7496
+ "CLOUD_TRANSPORT_ERROR",
7497
+ `Cloud action websocket error: ${error.message}`
7409
7498
  )
7410
7499
  );
7411
7500
  });
7412
7501
  ws.on("close", () => {
7413
7502
  this.closed = true;
7414
7503
  this.rejectAll(
7415
- new OpensteerRemoteError(
7416
- "REMOTE_SESSION_CLOSED",
7417
- "Remote action websocket closed."
7504
+ new OpensteerCloudError(
7505
+ "CLOUD_SESSION_CLOSED",
7506
+ "Cloud action websocket closed."
7418
7507
  )
7419
7508
  );
7420
7509
  });
@@ -7426,8 +7515,8 @@ var ActionWsClient = class _ActionWsClient {
7426
7515
  ws.once("open", () => resolve());
7427
7516
  ws.once("error", (error) => {
7428
7517
  reject(
7429
- new OpensteerRemoteError(
7430
- "REMOTE_TRANSPORT_ERROR",
7518
+ new OpensteerCloudError(
7519
+ "CLOUD_TRANSPORT_ERROR",
7431
7520
  `Failed to connect action websocket: ${error.message}`
7432
7521
  )
7433
7522
  );
@@ -7437,9 +7526,9 @@ var ActionWsClient = class _ActionWsClient {
7437
7526
  }
7438
7527
  async request(method, args) {
7439
7528
  if (this.closed || this.ws.readyState !== import_ws2.default.OPEN) {
7440
- throw new OpensteerRemoteError(
7441
- "REMOTE_SESSION_CLOSED",
7442
- "Remote action websocket is closed."
7529
+ throw new OpensteerCloudError(
7530
+ "CLOUD_SESSION_CLOSED",
7531
+ "Cloud action websocket is closed."
7443
7532
  );
7444
7533
  }
7445
7534
  const id = this.nextRequestId;
@@ -7458,8 +7547,8 @@ var ActionWsClient = class _ActionWsClient {
7458
7547
  this.ws.send(JSON.stringify(payload));
7459
7548
  } catch (error) {
7460
7549
  this.pending.delete(id);
7461
- const message = error instanceof Error ? error.message : "Failed to send remote action request.";
7462
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
7550
+ const message = error instanceof Error ? error.message : "Failed to send cloud action request.";
7551
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7463
7552
  }
7464
7553
  return await resultPromise;
7465
7554
  }
@@ -7477,9 +7566,9 @@ var ActionWsClient = class _ActionWsClient {
7477
7566
  parsed = JSON.parse(rawDataToUtf8(raw));
7478
7567
  } catch {
7479
7568
  this.rejectAll(
7480
- new OpensteerRemoteError(
7481
- "REMOTE_TRANSPORT_ERROR",
7482
- "Invalid remote action response payload."
7569
+ new OpensteerCloudError(
7570
+ "CLOUD_TRANSPORT_ERROR",
7571
+ "Invalid cloud action response payload."
7483
7572
  )
7484
7573
  );
7485
7574
  return;
@@ -7492,7 +7581,7 @@ var ActionWsClient = class _ActionWsClient {
7492
7581
  return;
7493
7582
  }
7494
7583
  pending.reject(
7495
- new OpensteerRemoteError(
7584
+ new OpensteerCloudError(
7496
7585
  parsed.code,
7497
7586
  parsed.error,
7498
7587
  void 0,
@@ -7520,7 +7609,7 @@ function withTokenQuery(wsUrl, token) {
7520
7609
  return url.toString();
7521
7610
  }
7522
7611
 
7523
- // src/remote/local-cache-sync.ts
7612
+ // src/cloud/local-cache-sync.ts
7524
7613
  var import_fs3 = __toESM(require("fs"), 1);
7525
7614
  var import_path5 = __toESM(require("path"), 1);
7526
7615
  function collectLocalSelectorCacheEntries(storage) {
@@ -7644,24 +7733,24 @@ function dedupeNewest(entries) {
7644
7733
  return [...byKey.values()];
7645
7734
  }
7646
7735
 
7647
- // src/remote/cdp-client.ts
7736
+ // src/cloud/cdp-client.ts
7648
7737
  var import_playwright2 = require("playwright");
7649
- var RemoteCdpClient = class {
7738
+ var CloudCdpClient = class {
7650
7739
  async connect(args) {
7651
7740
  const endpoint = withTokenQuery2(args.wsUrl, args.token);
7652
7741
  let browser;
7653
7742
  try {
7654
7743
  browser = await import_playwright2.chromium.connectOverCDP(endpoint);
7655
7744
  } catch (error) {
7656
- const message = error instanceof Error ? error.message : "Failed to connect to remote CDP endpoint.";
7657
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
7745
+ const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
7746
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7658
7747
  }
7659
7748
  const context = browser.contexts()[0];
7660
7749
  if (!context) {
7661
7750
  await browser.close();
7662
- throw new OpensteerRemoteError(
7663
- "REMOTE_INTERNAL",
7664
- "Remote browser returned no context."
7751
+ throw new OpensteerCloudError(
7752
+ "CLOUD_INTERNAL",
7753
+ "Cloud browser returned no context."
7665
7754
  );
7666
7755
  }
7667
7756
  const page = context.pages()[0] || await context.newPage();
@@ -7674,9 +7763,9 @@ function withTokenQuery2(wsUrl, token) {
7674
7763
  return url.toString();
7675
7764
  }
7676
7765
 
7677
- // src/remote/session-client.ts
7766
+ // src/cloud/session-client.ts
7678
7767
  var CACHE_IMPORT_BATCH_SIZE = 200;
7679
- var RemoteSessionClient = class {
7768
+ var CloudSessionClient = class {
7680
7769
  baseUrl;
7681
7770
  key;
7682
7771
  authScheme;
@@ -7697,7 +7786,17 @@ var RemoteSessionClient = class {
7697
7786
  if (!response.ok) {
7698
7787
  throw await parseHttpError(response);
7699
7788
  }
7700
- return await response.json();
7789
+ let body;
7790
+ try {
7791
+ body = await response.json();
7792
+ } catch {
7793
+ throw new OpensteerCloudError(
7794
+ "CLOUD_CONTRACT_MISMATCH",
7795
+ "Invalid cloud session create response: expected a JSON object.",
7796
+ response.status
7797
+ );
7798
+ }
7799
+ return parseCreateResponse(body, response.status);
7701
7800
  }
7702
7801
  async close(sessionId) {
7703
7802
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
@@ -7756,6 +7855,134 @@ var RemoteSessionClient = class {
7756
7855
  function normalizeBaseUrl(baseUrl) {
7757
7856
  return baseUrl.replace(/\/+$/, "");
7758
7857
  }
7858
+ function parseCreateResponse(body, status) {
7859
+ const root = requireObject(
7860
+ body,
7861
+ "Invalid cloud session create response: expected a JSON object.",
7862
+ status
7863
+ );
7864
+ const sessionId = requireString(root, "sessionId", status);
7865
+ const actionWsUrl = requireString(root, "actionWsUrl", status);
7866
+ const cdpWsUrl = requireString(root, "cdpWsUrl", status);
7867
+ const actionToken = requireString(root, "actionToken", status);
7868
+ const cdpToken = requireString(root, "cdpToken", status);
7869
+ const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
7870
+ const cloudSessionRoot = requireObject(
7871
+ root.cloudSession,
7872
+ "Invalid cloud session create response: cloudSession must be an object.",
7873
+ status
7874
+ );
7875
+ const cloudSession = {
7876
+ sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
7877
+ workspaceId: requireString(
7878
+ cloudSessionRoot,
7879
+ "workspaceId",
7880
+ status,
7881
+ "cloudSession"
7882
+ ),
7883
+ state: requireString(cloudSessionRoot, "state", status, "cloudSession"),
7884
+ createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
7885
+ sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
7886
+ sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
7887
+ label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
7888
+ };
7889
+ const expiresAt = optionalNumber(root, "expiresAt", status);
7890
+ return {
7891
+ sessionId,
7892
+ actionWsUrl,
7893
+ cdpWsUrl,
7894
+ actionToken,
7895
+ cdpToken,
7896
+ expiresAt,
7897
+ cloudSessionUrl,
7898
+ cloudSession
7899
+ };
7900
+ }
7901
+ function requireObject(value, message, status) {
7902
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
7903
+ throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
7904
+ }
7905
+ return value;
7906
+ }
7907
+ function requireString(source, field, status, parent) {
7908
+ const value = source[field];
7909
+ if (typeof value !== "string" || !value.trim()) {
7910
+ throw new OpensteerCloudError(
7911
+ "CLOUD_CONTRACT_MISMATCH",
7912
+ `Invalid cloud session create response: ${formatFieldPath(
7913
+ field,
7914
+ parent
7915
+ )} must be a non-empty string.`,
7916
+ status
7917
+ );
7918
+ }
7919
+ return value;
7920
+ }
7921
+ function requireNumber(source, field, status, parent) {
7922
+ const value = source[field];
7923
+ if (typeof value !== "number" || !Number.isFinite(value)) {
7924
+ throw new OpensteerCloudError(
7925
+ "CLOUD_CONTRACT_MISMATCH",
7926
+ `Invalid cloud session create response: ${formatFieldPath(
7927
+ field,
7928
+ parent
7929
+ )} must be a finite number.`,
7930
+ status
7931
+ );
7932
+ }
7933
+ return value;
7934
+ }
7935
+ function optionalString(source, field, status, parent) {
7936
+ const value = source[field];
7937
+ if (value == null) {
7938
+ return void 0;
7939
+ }
7940
+ if (typeof value !== "string") {
7941
+ throw new OpensteerCloudError(
7942
+ "CLOUD_CONTRACT_MISMATCH",
7943
+ `Invalid cloud session create response: ${formatFieldPath(
7944
+ field,
7945
+ parent
7946
+ )} must be a string when present.`,
7947
+ status
7948
+ );
7949
+ }
7950
+ return value;
7951
+ }
7952
+ function optionalNumber(source, field, status, parent) {
7953
+ const value = source[field];
7954
+ if (value == null) {
7955
+ return void 0;
7956
+ }
7957
+ if (typeof value !== "number" || !Number.isFinite(value)) {
7958
+ throw new OpensteerCloudError(
7959
+ "CLOUD_CONTRACT_MISMATCH",
7960
+ `Invalid cloud session create response: ${formatFieldPath(
7961
+ field,
7962
+ parent
7963
+ )} must be a finite number when present.`,
7964
+ status
7965
+ );
7966
+ }
7967
+ return value;
7968
+ }
7969
+ function requireSourceType(source, field, status, parent) {
7970
+ const value = source[field];
7971
+ if (value === "agent-thread" || value === "agent-run" || value === "local-cloud" || value === "manual") {
7972
+ return value;
7973
+ }
7974
+ throw new OpensteerCloudError(
7975
+ "CLOUD_CONTRACT_MISMATCH",
7976
+ `Invalid cloud session create response: ${formatFieldPath(
7977
+ field,
7978
+ parent
7979
+ )} must be one of "agent-thread", "agent-run", "local-cloud", or "manual".`,
7980
+ status
7981
+ );
7982
+ }
7983
+ function formatFieldPath(field, parent) {
7984
+ return parent ? `"${parent}.${field}"` : `"${field}"`;
7985
+ }
7759
7986
  function zeroImportResponse() {
7760
7987
  return {
7761
7988
  imported: 0,
@@ -7779,33 +8006,46 @@ async function parseHttpError(response) {
7779
8006
  } catch {
7780
8007
  body = null;
7781
8008
  }
7782
- const code = typeof body?.code === "string" ? toRemoteErrorCode(body.code) : "REMOTE_TRANSPORT_ERROR";
7783
- const message = typeof body?.error === "string" ? body.error : `Remote request failed with status ${response.status}.`;
7784
- return new OpensteerRemoteError(code, message, response.status, body?.details);
8009
+ const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
8010
+ const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
8011
+ return new OpensteerCloudError(code, message, response.status, body?.details);
7785
8012
  }
7786
- function toRemoteErrorCode(code) {
7787
- if (code === "REMOTE_AUTH_FAILED" || code === "REMOTE_SESSION_NOT_FOUND" || code === "REMOTE_SESSION_CLOSED" || code === "REMOTE_UNSUPPORTED_METHOD" || code === "REMOTE_INVALID_REQUEST" || code === "REMOTE_MODEL_NOT_ALLOWED" || code === "REMOTE_ACTION_FAILED" || code === "REMOTE_INTERNAL" || code === "REMOTE_CAPACITY_EXHAUSTED" || code === "REMOTE_RUNTIME_UNAVAILABLE" || code === "REMOTE_RUNTIME_MISMATCH" || code === "REMOTE_SESSION_STALE" || code === "REMOTE_CONTROL_PLANE_ERROR") {
8013
+ function toCloudErrorCode(code) {
8014
+ if (code === "CLOUD_AUTH_FAILED" || code === "CLOUD_SESSION_NOT_FOUND" || code === "CLOUD_SESSION_CLOSED" || code === "CLOUD_UNSUPPORTED_METHOD" || code === "CLOUD_INVALID_REQUEST" || code === "CLOUD_MODEL_NOT_ALLOWED" || code === "CLOUD_ACTION_FAILED" || code === "CLOUD_INTERNAL" || code === "CLOUD_CAPACITY_EXHAUSTED" || code === "CLOUD_RUNTIME_UNAVAILABLE" || code === "CLOUD_RUNTIME_MISMATCH" || code === "CLOUD_SESSION_STALE" || code === "CLOUD_CONTRACT_MISMATCH" || code === "CLOUD_CONTROL_PLANE_ERROR") {
7788
8015
  return code;
7789
8016
  }
7790
- return "REMOTE_TRANSPORT_ERROR";
8017
+ return "CLOUD_TRANSPORT_ERROR";
7791
8018
  }
7792
8019
 
7793
- // src/remote/runtime.ts
7794
- var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7795
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
8020
+ // src/cloud/runtime.ts
8021
+ 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()) {
7796
8024
  return {
7797
- sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7798
- cdpClient: new RemoteCdpClient(),
8025
+ sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
8026
+ cdpClient: new CloudCdpClient(),
8027
+ appUrl: normalizeCloudAppUrl(appUrl),
7799
8028
  actionClient: null,
7800
- sessionId: null
8029
+ sessionId: null,
8030
+ localRunId: null,
8031
+ cloudSessionUrl: null
7801
8032
  };
7802
8033
  }
7803
- function resolveRemoteBaseUrl() {
8034
+ function resolveCloudBaseUrl() {
7804
8035
  const value = process.env.OPENSTEER_BASE_URL?.trim();
7805
- if (!value) return DEFAULT_REMOTE_BASE_URL;
8036
+ if (!value) return DEFAULT_CLOUD_BASE_URL;
7806
8037
  return value.replace(/\/+$/, "");
7807
8038
  }
7808
- function readRemoteActionDescription(payload) {
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;
8046
+ return value.replace(/\/+$/, "");
8047
+ }
8048
+ function readCloudActionDescription(payload) {
7809
8049
  const description = payload.description;
7810
8050
  if (typeof description !== "string") return void 0;
7811
8051
  const normalized = description.trim();
@@ -7813,7 +8053,7 @@ function readRemoteActionDescription(payload) {
7813
8053
  }
7814
8054
 
7815
8055
  // src/opensteer.ts
7816
- var REMOTE_INTERACTION_METHODS = /* @__PURE__ */ new Set([
8056
+ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
7817
8057
  "click",
7818
8058
  "dblclick",
7819
8059
  "rightclick",
@@ -7830,7 +8070,7 @@ var Opensteer = class _Opensteer {
7830
8070
  namespace;
7831
8071
  storage;
7832
8072
  pool;
7833
- remote;
8073
+ cloud;
7834
8074
  browser = null;
7835
8075
  pageRef = null;
7836
8076
  contextRef = null;
@@ -7838,8 +8078,8 @@ var Opensteer = class _Opensteer {
7838
8078
  snapshotCache = null;
7839
8079
  constructor(config = {}) {
7840
8080
  const resolved = resolveConfig(config);
7841
- const modeSelection = resolveModeSelection({
7842
- mode: resolved.mode
8081
+ const cloudSelection = resolveCloudSelection({
8082
+ cloud: resolved.cloud
7843
8083
  });
7844
8084
  const model = resolved.model;
7845
8085
  this.config = resolved;
@@ -7849,21 +8089,22 @@ var Opensteer = class _Opensteer {
7849
8089
  this.namespace = resolveNamespace(resolved, rootDir);
7850
8090
  this.storage = new LocalSelectorStorage(rootDir, this.namespace);
7851
8091
  this.pool = new BrowserPool(resolved.browser || {});
7852
- if (modeSelection.mode === "remote") {
7853
- const remoteConfig = resolved.remote && typeof resolved.remote === "object" ? resolved.remote : void 0;
7854
- const apiKey = remoteConfig?.apiKey?.trim();
8092
+ if (cloudSelection.cloud) {
8093
+ const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
8094
+ const apiKey = cloudConfig?.apiKey?.trim();
7855
8095
  if (!apiKey) {
7856
8096
  throw new Error(
7857
- "Remote mode requires a non-empty API key via remote.apiKey or OPENSTEER_API_KEY."
8097
+ "Cloud mode requires a non-empty API key via cloud.apiKey or OPENSTEER_API_KEY."
7858
8098
  );
7859
8099
  }
7860
- this.remote = createRemoteRuntimeState(
8100
+ this.cloud = createCloudRuntimeState(
7861
8101
  apiKey,
7862
- remoteConfig?.baseUrl,
7863
- remoteConfig?.authScheme
8102
+ cloudConfig?.baseUrl,
8103
+ cloudConfig?.authScheme,
8104
+ cloudConfig?.appUrl
7864
8105
  );
7865
8106
  } else {
7866
- this.remote = null;
8107
+ this.cloud = null;
7867
8108
  }
7868
8109
  }
7869
8110
  createLazyResolveCallback(model) {
@@ -7901,32 +8142,32 @@ var Opensteer = class _Opensteer {
7901
8142
  };
7902
8143
  return extract;
7903
8144
  }
7904
- async invokeRemoteActionAndResetCache(method, args) {
7905
- const result = await this.invokeRemoteAction(method, args);
8145
+ async invokeCloudActionAndResetCache(method, args) {
8146
+ const result = await this.invokeCloudAction(method, args);
7906
8147
  this.snapshotCache = null;
7907
8148
  return result;
7908
8149
  }
7909
- async invokeRemoteAction(method, args) {
7910
- const actionClient = this.remote?.actionClient;
7911
- const sessionId = this.remote?.sessionId;
8150
+ async invokeCloudAction(method, args) {
8151
+ const actionClient = this.cloud?.actionClient;
8152
+ const sessionId = this.cloud?.sessionId;
7912
8153
  if (!actionClient || !sessionId) {
7913
- throw remoteNotLaunchedError();
8154
+ throw cloudNotLaunchedError();
7914
8155
  }
7915
8156
  const payload = args && typeof args === "object" ? args : {};
7916
8157
  try {
7917
8158
  return await actionClient.request(method, payload);
7918
8159
  } catch (err) {
7919
- if (err instanceof OpensteerRemoteError && err.code === "REMOTE_ACTION_FAILED" && REMOTE_INTERACTION_METHODS.has(method)) {
8160
+ if (err instanceof OpensteerCloudError && err.code === "CLOUD_ACTION_FAILED" && CLOUD_INTERACTION_METHODS.has(method)) {
7920
8161
  const detailsRecord = err.details && typeof err.details === "object" ? err.details : null;
7921
- const remoteFailure = normalizeActionFailure(
8162
+ const cloudFailure = normalizeActionFailure(
7922
8163
  detailsRecord?.actionFailure
7923
8164
  );
7924
- const failure = remoteFailure || classifyActionFailure({
8165
+ const failure = cloudFailure || classifyActionFailure({
7925
8166
  action: method,
7926
8167
  error: err,
7927
8168
  fallbackMessage: defaultActionFailureMessage(method)
7928
8169
  });
7929
- const description = readRemoteActionDescription(payload);
8170
+ const description = readCloudActionDescription(payload);
7930
8171
  throw this.buildActionError(
7931
8172
  method,
7932
8173
  description,
@@ -7967,8 +8208,36 @@ var Opensteer = class _Opensteer {
7967
8208
  }
7968
8209
  return this.contextRef;
7969
8210
  }
7970
- getRemoteSessionId() {
7971
- return this.remote?.sessionId ?? null;
8211
+ getCloudSessionId() {
8212
+ return this.cloud?.sessionId ?? null;
8213
+ }
8214
+ getCloudSessionUrl() {
8215
+ return this.cloud?.cloudSessionUrl ?? null;
8216
+ }
8217
+ announceCloudSession(args) {
8218
+ if (!this.shouldAnnounceCloudSession()) {
8219
+ return;
8220
+ }
8221
+ const fields = [
8222
+ `sessionId=${args.sessionId}`,
8223
+ `workspaceId=${args.workspaceId}`
8224
+ ];
8225
+ if (args.cloudSessionUrl) {
8226
+ fields.push(`url=${args.cloudSessionUrl}`);
8227
+ }
8228
+ process.stderr.write(`[opensteer] cloud session ready ${fields.join(" ")}
8229
+ `);
8230
+ }
8231
+ shouldAnnounceCloudSession() {
8232
+ const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : null;
8233
+ const announce = cloudConfig?.announce ?? "always";
8234
+ if (announce === "off") {
8235
+ return false;
8236
+ }
8237
+ if (announce === "tty") {
8238
+ return Boolean(process.stderr.isTTY);
8239
+ }
8240
+ return true;
7972
8241
  }
7973
8242
  async launch(options = {}) {
7974
8243
  if (this.pageRef && !this.ownsBrowser) {
@@ -7979,22 +8248,29 @@ var Opensteer = class _Opensteer {
7979
8248
  if (this.pageRef && this.ownsBrowser) {
7980
8249
  return;
7981
8250
  }
7982
- if (this.remote) {
8251
+ if (this.cloud) {
7983
8252
  let actionClient = null;
7984
8253
  let browser = null;
7985
8254
  let sessionId = null;
8255
+ let localRunId = null;
7986
8256
  try {
7987
8257
  try {
7988
- await this.syncLocalSelectorCacheToRemote();
8258
+ await this.syncLocalSelectorCacheToCloud();
7989
8259
  } catch (error) {
7990
8260
  if (this.config.debug) {
7991
8261
  const message = error instanceof Error ? error.message : String(error);
7992
8262
  console.warn(
7993
- `[opensteer] remote selector cache sync failed: ${message}`
8263
+ `[opensteer] cloud selector cache sync failed: ${message}`
7994
8264
  );
7995
8265
  }
7996
8266
  }
7997
- const session3 = await this.remote.sessionClient.create({
8267
+ localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
8268
+ this.cloud.localRunId = localRunId;
8269
+ const session3 = await this.cloud.sessionClient.create({
8270
+ cloudSessionContractVersion,
8271
+ sourceType: "local-cloud",
8272
+ clientSessionHint: this.namespace,
8273
+ localRunId,
7998
8274
  name: this.namespace,
7999
8275
  model: this.config.model,
8000
8276
  launchContext: options.context || void 0
@@ -8005,7 +8281,7 @@ var Opensteer = class _Opensteer {
8005
8281
  token: session3.actionToken,
8006
8282
  sessionId: session3.sessionId
8007
8283
  });
8008
- const cdpConnection = await this.remote.cdpClient.connect({
8284
+ const cdpConnection = await this.cloud.cdpClient.connect({
8009
8285
  wsUrl: session3.cdpWsUrl,
8010
8286
  token: session3.cdpToken
8011
8287
  });
@@ -8015,8 +8291,17 @@ var Opensteer = class _Opensteer {
8015
8291
  this.pageRef = cdpConnection.page;
8016
8292
  this.ownsBrowser = true;
8017
8293
  this.snapshotCache = null;
8018
- this.remote.actionClient = actionClient;
8019
- this.remote.sessionId = sessionId;
8294
+ this.cloud.actionClient = actionClient;
8295
+ this.cloud.sessionId = sessionId;
8296
+ this.cloud.cloudSessionUrl = buildCloudSessionUrl(
8297
+ this.cloud.appUrl,
8298
+ session3.cloudSession.sessionId
8299
+ );
8300
+ this.announceCloudSession({
8301
+ sessionId: session3.sessionId,
8302
+ workspaceId: session3.cloudSession.workspaceId,
8303
+ cloudSessionUrl: this.cloud.cloudSessionUrl
8304
+ });
8020
8305
  return;
8021
8306
  } catch (error) {
8022
8307
  if (actionClient) {
@@ -8026,8 +8311,9 @@ var Opensteer = class _Opensteer {
8026
8311
  await browser.close().catch(() => void 0);
8027
8312
  }
8028
8313
  if (sessionId) {
8029
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8314
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8030
8315
  }
8316
+ this.cloud.cloudSessionUrl = null;
8031
8317
  throw error;
8032
8318
  }
8033
8319
  }
@@ -8045,13 +8331,13 @@ var Opensteer = class _Opensteer {
8045
8331
  }
8046
8332
  static from(page, config = {}) {
8047
8333
  const resolvedConfig = resolveConfig(config);
8048
- const modeSelection = resolveModeSelection({
8049
- mode: resolvedConfig.mode
8334
+ const cloudSelection = resolveCloudSelection({
8335
+ cloud: resolvedConfig.cloud
8050
8336
  });
8051
- if (modeSelection.mode === "remote") {
8052
- throw remoteUnsupportedMethodError(
8337
+ if (cloudSelection.cloud) {
8338
+ throw cloudUnsupportedMethodError(
8053
8339
  "Opensteer.from(page)",
8054
- "Opensteer.from(page) is not supported in remote mode."
8340
+ "Opensteer.from(page) is not supported in cloud mode."
8055
8341
  );
8056
8342
  }
8057
8343
  const instance2 = new _Opensteer(config);
@@ -8064,12 +8350,14 @@ var Opensteer = class _Opensteer {
8064
8350
  }
8065
8351
  async close() {
8066
8352
  this.snapshotCache = null;
8067
- if (this.remote) {
8068
- const actionClient = this.remote.actionClient;
8069
- const sessionId = this.remote.sessionId;
8353
+ if (this.cloud) {
8354
+ const actionClient = this.cloud.actionClient;
8355
+ const sessionId = this.cloud.sessionId;
8070
8356
  const browser = this.browser;
8071
- this.remote.actionClient = null;
8072
- this.remote.sessionId = null;
8357
+ this.cloud.actionClient = null;
8358
+ this.cloud.sessionId = null;
8359
+ this.cloud.localRunId = null;
8360
+ this.cloud.cloudSessionUrl = null;
8073
8361
  this.browser = null;
8074
8362
  this.pageRef = null;
8075
8363
  this.contextRef = null;
@@ -8081,7 +8369,7 @@ var Opensteer = class _Opensteer {
8081
8369
  await browser.close().catch(() => void 0);
8082
8370
  }
8083
8371
  if (sessionId) {
8084
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8372
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8085
8373
  }
8086
8374
  return;
8087
8375
  }
@@ -8093,17 +8381,17 @@ var Opensteer = class _Opensteer {
8093
8381
  this.contextRef = null;
8094
8382
  this.ownsBrowser = false;
8095
8383
  }
8096
- async syncLocalSelectorCacheToRemote() {
8097
- if (!this.remote) return;
8384
+ async syncLocalSelectorCacheToCloud() {
8385
+ if (!this.cloud) return;
8098
8386
  const entries = collectLocalSelectorCacheEntries(this.storage);
8099
8387
  if (!entries.length) return;
8100
- await this.remote.sessionClient.importSelectorCache({
8388
+ await this.cloud.sessionClient.importSelectorCache({
8101
8389
  entries
8102
8390
  });
8103
8391
  }
8104
8392
  async goto(url, options) {
8105
- if (this.remote) {
8106
- await this.invokeRemoteActionAndResetCache("goto", { url, options });
8393
+ if (this.cloud) {
8394
+ await this.invokeCloudActionAndResetCache("goto", { url, options });
8107
8395
  return;
8108
8396
  }
8109
8397
  const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
@@ -8112,8 +8400,8 @@ var Opensteer = class _Opensteer {
8112
8400
  this.snapshotCache = null;
8113
8401
  }
8114
8402
  async snapshot(options = {}) {
8115
- if (this.remote) {
8116
- return await this.invokeRemoteActionAndResetCache("snapshot", {
8403
+ if (this.cloud) {
8404
+ return await this.invokeCloudActionAndResetCache("snapshot", {
8117
8405
  options
8118
8406
  });
8119
8407
  }
@@ -8122,8 +8410,8 @@ var Opensteer = class _Opensteer {
8122
8410
  return prepared.cleanedHtml;
8123
8411
  }
8124
8412
  async state() {
8125
- if (this.remote) {
8126
- return await this.invokeRemoteAction("state", {});
8413
+ if (this.cloud) {
8414
+ return await this.invokeCloudAction("state", {});
8127
8415
  }
8128
8416
  const html = await this.snapshot({ mode: "action" });
8129
8417
  return {
@@ -8133,8 +8421,8 @@ var Opensteer = class _Opensteer {
8133
8421
  };
8134
8422
  }
8135
8423
  async screenshot(options = {}) {
8136
- if (this.remote) {
8137
- const b64 = await this.invokeRemoteAction(
8424
+ if (this.cloud) {
8425
+ const b64 = await this.invokeCloudAction(
8138
8426
  "screenshot",
8139
8427
  options
8140
8428
  );
@@ -8148,8 +8436,8 @@ var Opensteer = class _Opensteer {
8148
8436
  });
8149
8437
  }
8150
8438
  async click(options) {
8151
- if (this.remote) {
8152
- return await this.invokeRemoteActionAndResetCache(
8439
+ if (this.cloud) {
8440
+ return await this.invokeCloudActionAndResetCache(
8153
8441
  "click",
8154
8442
  options
8155
8443
  );
@@ -8161,8 +8449,8 @@ var Opensteer = class _Opensteer {
8161
8449
  });
8162
8450
  }
8163
8451
  async dblclick(options) {
8164
- if (this.remote) {
8165
- return await this.invokeRemoteActionAndResetCache(
8452
+ if (this.cloud) {
8453
+ return await this.invokeCloudActionAndResetCache(
8166
8454
  "dblclick",
8167
8455
  options
8168
8456
  );
@@ -8174,8 +8462,8 @@ var Opensteer = class _Opensteer {
8174
8462
  });
8175
8463
  }
8176
8464
  async rightclick(options) {
8177
- if (this.remote) {
8178
- return await this.invokeRemoteActionAndResetCache(
8465
+ if (this.cloud) {
8466
+ return await this.invokeCloudActionAndResetCache(
8179
8467
  "rightclick",
8180
8468
  options
8181
8469
  );
@@ -8187,8 +8475,8 @@ var Opensteer = class _Opensteer {
8187
8475
  });
8188
8476
  }
8189
8477
  async hover(options) {
8190
- if (this.remote) {
8191
- return await this.invokeRemoteActionAndResetCache(
8478
+ if (this.cloud) {
8479
+ return await this.invokeCloudActionAndResetCache(
8192
8480
  "hover",
8193
8481
  options
8194
8482
  );
@@ -8286,8 +8574,8 @@ var Opensteer = class _Opensteer {
8286
8574
  );
8287
8575
  }
8288
8576
  async input(options) {
8289
- if (this.remote) {
8290
- return await this.invokeRemoteActionAndResetCache(
8577
+ if (this.cloud) {
8578
+ return await this.invokeCloudActionAndResetCache(
8291
8579
  "input",
8292
8580
  options
8293
8581
  );
@@ -8389,8 +8677,8 @@ var Opensteer = class _Opensteer {
8389
8677
  );
8390
8678
  }
8391
8679
  async select(options) {
8392
- if (this.remote) {
8393
- return await this.invokeRemoteActionAndResetCache(
8680
+ if (this.cloud) {
8681
+ return await this.invokeCloudActionAndResetCache(
8394
8682
  "select",
8395
8683
  options
8396
8684
  );
@@ -8499,8 +8787,8 @@ var Opensteer = class _Opensteer {
8499
8787
  );
8500
8788
  }
8501
8789
  async scroll(options = {}) {
8502
- if (this.remote) {
8503
- return await this.invokeRemoteActionAndResetCache(
8790
+ if (this.cloud) {
8791
+ return await this.invokeCloudActionAndResetCache(
8504
8792
  "scroll",
8505
8793
  options
8506
8794
  );
@@ -8601,14 +8889,14 @@ var Opensteer = class _Opensteer {
8601
8889
  }
8602
8890
  // --- Tab Management ---
8603
8891
  async tabs() {
8604
- if (this.remote) {
8605
- return await this.invokeRemoteAction("tabs", {});
8892
+ if (this.cloud) {
8893
+ return await this.invokeCloudAction("tabs", {});
8606
8894
  }
8607
8895
  return listTabs(this.context, this.page);
8608
8896
  }
8609
8897
  async newTab(url) {
8610
- if (this.remote) {
8611
- return await this.invokeRemoteActionAndResetCache("newTab", {
8898
+ if (this.cloud) {
8899
+ return await this.invokeCloudActionAndResetCache("newTab", {
8612
8900
  url
8613
8901
  });
8614
8902
  }
@@ -8618,8 +8906,8 @@ var Opensteer = class _Opensteer {
8618
8906
  return info;
8619
8907
  }
8620
8908
  async switchTab(index) {
8621
- if (this.remote) {
8622
- await this.invokeRemoteActionAndResetCache("switchTab", { index });
8909
+ if (this.cloud) {
8910
+ await this.invokeCloudActionAndResetCache("switchTab", { index });
8623
8911
  return;
8624
8912
  }
8625
8913
  const page = await switchTab(this.context, index);
@@ -8627,8 +8915,8 @@ var Opensteer = class _Opensteer {
8627
8915
  this.snapshotCache = null;
8628
8916
  }
8629
8917
  async closeTab(index) {
8630
- if (this.remote) {
8631
- await this.invokeRemoteActionAndResetCache("closeTab", { index });
8918
+ if (this.cloud) {
8919
+ await this.invokeCloudActionAndResetCache("closeTab", { index });
8632
8920
  return;
8633
8921
  }
8634
8922
  const newPage = await closeTab(this.context, this.page, index);
@@ -8639,8 +8927,8 @@ var Opensteer = class _Opensteer {
8639
8927
  }
8640
8928
  // --- Cookie Management ---
8641
8929
  async getCookies(url) {
8642
- if (this.remote) {
8643
- return await this.invokeRemoteAction(
8930
+ if (this.cloud) {
8931
+ return await this.invokeCloudAction(
8644
8932
  "getCookies",
8645
8933
  { url }
8646
8934
  );
@@ -8648,41 +8936,41 @@ var Opensteer = class _Opensteer {
8648
8936
  return getCookies(this.context, url);
8649
8937
  }
8650
8938
  async setCookie(cookie) {
8651
- if (this.remote) {
8652
- await this.invokeRemoteAction("setCookie", cookie);
8939
+ if (this.cloud) {
8940
+ await this.invokeCloudAction("setCookie", cookie);
8653
8941
  return;
8654
8942
  }
8655
8943
  return setCookie(this.context, cookie);
8656
8944
  }
8657
8945
  async clearCookies() {
8658
- if (this.remote) {
8659
- await this.invokeRemoteAction("clearCookies", {});
8946
+ if (this.cloud) {
8947
+ await this.invokeCloudAction("clearCookies", {});
8660
8948
  return;
8661
8949
  }
8662
8950
  return clearCookies(this.context);
8663
8951
  }
8664
8952
  async exportCookies(filePath, url) {
8665
- if (this.remote) {
8666
- throw remoteUnsupportedMethodError(
8953
+ if (this.cloud) {
8954
+ throw cloudUnsupportedMethodError(
8667
8955
  "exportCookies",
8668
- "exportCookies() is not supported in remote mode because it depends on local filesystem paths."
8956
+ "exportCookies() is not supported in cloud mode because it depends on local filesystem paths."
8669
8957
  );
8670
8958
  }
8671
8959
  return exportCookies(this.context, filePath, url);
8672
8960
  }
8673
8961
  async importCookies(filePath) {
8674
- if (this.remote) {
8675
- throw remoteUnsupportedMethodError(
8962
+ if (this.cloud) {
8963
+ throw cloudUnsupportedMethodError(
8676
8964
  "importCookies",
8677
- "importCookies() is not supported in remote mode because it depends on local filesystem paths."
8965
+ "importCookies() is not supported in cloud mode because it depends on local filesystem paths."
8678
8966
  );
8679
8967
  }
8680
8968
  return importCookies(this.context, filePath);
8681
8969
  }
8682
8970
  // --- Keyboard Input ---
8683
8971
  async pressKey(key) {
8684
- if (this.remote) {
8685
- await this.invokeRemoteActionAndResetCache("pressKey", { key });
8972
+ if (this.cloud) {
8973
+ await this.invokeCloudActionAndResetCache("pressKey", { key });
8686
8974
  return;
8687
8975
  }
8688
8976
  await this.runWithPostActionWait("pressKey", void 0, async () => {
@@ -8691,8 +8979,8 @@ var Opensteer = class _Opensteer {
8691
8979
  this.snapshotCache = null;
8692
8980
  }
8693
8981
  async type(text) {
8694
- if (this.remote) {
8695
- await this.invokeRemoteActionAndResetCache("type", { text });
8982
+ if (this.cloud) {
8983
+ await this.invokeCloudActionAndResetCache("type", { text });
8696
8984
  return;
8697
8985
  }
8698
8986
  await this.runWithPostActionWait("type", void 0, async () => {
@@ -8702,8 +8990,8 @@ var Opensteer = class _Opensteer {
8702
8990
  }
8703
8991
  // --- Element Info ---
8704
8992
  async getElementText(options) {
8705
- if (this.remote) {
8706
- return await this.invokeRemoteAction("getElementText", options);
8993
+ if (this.cloud) {
8994
+ return await this.invokeCloudAction("getElementText", options);
8707
8995
  }
8708
8996
  return this.executeElementInfoAction(
8709
8997
  "getElementText",
@@ -8716,8 +9004,8 @@ var Opensteer = class _Opensteer {
8716
9004
  );
8717
9005
  }
8718
9006
  async getElementValue(options) {
8719
- if (this.remote) {
8720
- return await this.invokeRemoteAction(
9007
+ if (this.cloud) {
9008
+ return await this.invokeCloudAction(
8721
9009
  "getElementValue",
8722
9010
  options
8723
9011
  );
@@ -8732,8 +9020,8 @@ var Opensteer = class _Opensteer {
8732
9020
  );
8733
9021
  }
8734
9022
  async getElementAttributes(options) {
8735
- if (this.remote) {
8736
- return await this.invokeRemoteAction(
9023
+ if (this.cloud) {
9024
+ return await this.invokeCloudAction(
8737
9025
  "getElementAttributes",
8738
9026
  options
8739
9027
  );
@@ -8754,8 +9042,8 @@ var Opensteer = class _Opensteer {
8754
9042
  );
8755
9043
  }
8756
9044
  async getElementBoundingBox(options) {
8757
- if (this.remote) {
8758
- return await this.invokeRemoteAction(
9045
+ if (this.cloud) {
9046
+ return await this.invokeCloudAction(
8759
9047
  "getElementBoundingBox",
8760
9048
  options
8761
9049
  );
@@ -8770,14 +9058,14 @@ var Opensteer = class _Opensteer {
8770
9058
  );
8771
9059
  }
8772
9060
  async getHtml(selector) {
8773
- if (this.remote) {
8774
- return await this.invokeRemoteAction("getHtml", { selector });
9061
+ if (this.cloud) {
9062
+ return await this.invokeCloudAction("getHtml", { selector });
8775
9063
  }
8776
9064
  return getPageHtml(this.page, selector);
8777
9065
  }
8778
9066
  async getTitle() {
8779
- if (this.remote) {
8780
- return await this.invokeRemoteAction("getTitle", {});
9067
+ if (this.cloud) {
9068
+ return await this.invokeCloudAction("getTitle", {});
8781
9069
  }
8782
9070
  return getPageTitle(this.page);
8783
9071
  }
@@ -8815,10 +9103,10 @@ var Opensteer = class _Opensteer {
8815
9103
  }
8816
9104
  // --- File Upload ---
8817
9105
  async uploadFile(options) {
8818
- if (this.remote) {
8819
- throw remoteUnsupportedMethodError(
9106
+ if (this.cloud) {
9107
+ throw cloudUnsupportedMethodError(
8820
9108
  "uploadFile",
8821
- "uploadFile() is not supported in remote mode because file paths must be accessible on the remote server."
9109
+ "uploadFile() is not supported in cloud mode because file paths must be accessible on the cloud runtime."
8822
9110
  );
8823
9111
  }
8824
9112
  const storageKey = this.resolveStorageKey(options.description);
@@ -8920,15 +9208,15 @@ var Opensteer = class _Opensteer {
8920
9208
  }
8921
9209
  // --- Wait for Text ---
8922
9210
  async waitForText(text, options) {
8923
- if (this.remote) {
8924
- await this.invokeRemoteAction("waitForText", { text, options });
9211
+ if (this.cloud) {
9212
+ await this.invokeCloudAction("waitForText", { text, options });
8925
9213
  return;
8926
9214
  }
8927
9215
  await this.page.getByText(text).first().waitFor({ timeout: options?.timeout ?? 3e4 });
8928
9216
  }
8929
9217
  async extract(options) {
8930
- if (this.remote) {
8931
- return await this.invokeRemoteAction("extract", options);
9218
+ if (this.cloud) {
9219
+ return await this.invokeCloudAction("extract", options);
8932
9220
  }
8933
9221
  const storageKey = this.resolveStorageKey(options.description);
8934
9222
  const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
@@ -8980,8 +9268,8 @@ var Opensteer = class _Opensteer {
8980
9268
  return inflateDataPathObject(data);
8981
9269
  }
8982
9270
  async extractFromPlan(options) {
8983
- if (this.remote) {
8984
- return await this.invokeRemoteAction(
9271
+ if (this.cloud) {
9272
+ return await this.invokeCloudAction(
8985
9273
  "extractFromPlan",
8986
9274
  options
8987
9275
  );
@@ -9030,10 +9318,10 @@ var Opensteer = class _Opensteer {
9030
9318
  return this.storage;
9031
9319
  }
9032
9320
  clearCache() {
9033
- if (this.remote) {
9321
+ if (this.cloud) {
9034
9322
  this.snapshotCache = null;
9035
- if (!this.remote.actionClient) return;
9036
- void this.invokeRemoteAction("clearCache", {});
9323
+ if (!this.cloud.actionClient) return;
9324
+ void this.invokeCloudAction("clearCache", {});
9037
9325
  return;
9038
9326
  }
9039
9327
  this.storage.clearNamespace();
@@ -10061,6 +10349,16 @@ function getScrollDelta2(options) {
10061
10349
  return { x: 0, y: absoluteAmount };
10062
10350
  }
10063
10351
  }
10352
+ function buildLocalRunId(namespace) {
10353
+ const normalized = namespace.trim() || "default";
10354
+ return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
10355
+ }
10356
+ function buildCloudSessionUrl(appUrl, sessionId) {
10357
+ if (!appUrl) {
10358
+ return null;
10359
+ }
10360
+ return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10361
+ }
10064
10362
 
10065
10363
  // src/cli/paths.ts
10066
10364
  var import_os2 = require("os");
@@ -10462,7 +10760,8 @@ async function handleRequest(request, socket) {
10462
10760
  url: instance.page.url(),
10463
10761
  session,
10464
10762
  name: activeNamespace,
10465
- remoteSessionId: instance.getRemoteSessionId() ?? void 0
10763
+ cloudSessionId: instance.getCloudSessionId() ?? void 0,
10764
+ cloudSessionUrl: instance.getCloudSessionUrl() ?? void 0
10466
10765
  }
10467
10766
  });
10468
10767
  } catch (err) {