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.
package/dist/index.cjs CHANGED
@@ -338,6 +338,8 @@ var init_extractor = __esm({
338
338
  var index_exports = {};
339
339
  __export(index_exports, {
340
340
  ActionWsClient: () => ActionWsClient,
341
+ CloudCdpClient: () => CloudCdpClient,
342
+ CloudSessionClient: () => CloudSessionClient,
341
343
  CounterResolutionError: () => CounterResolutionError,
342
344
  ElementPathError: () => ElementPathError,
343
345
  LocalSelectorStorage: () => LocalSelectorStorage,
@@ -351,9 +353,7 @@ __export(index_exports, {
351
353
  OS_UNAVAILABLE_ATTR: () => OS_UNAVAILABLE_ATTR,
352
354
  Opensteer: () => Opensteer,
353
355
  OpensteerActionError: () => OpensteerActionError,
354
- OpensteerRemoteError: () => OpensteerRemoteError,
355
- RemoteCdpClient: () => RemoteCdpClient,
356
- RemoteSessionClient: () => RemoteSessionClient,
356
+ OpensteerCloudError: () => OpensteerCloudError,
357
357
  buildElementPathFromHandle: () => buildElementPathFromHandle,
358
358
  buildElementPathFromSelector: () => buildElementPathFromSelector,
359
359
  buildPathSelectorHint: () => buildPathSelectorHint,
@@ -365,6 +365,9 @@ __export(index_exports, {
365
365
  clearCookies: () => clearCookies,
366
366
  cloneElementPath: () => cloneElementPath,
367
367
  closeTab: () => closeTab,
368
+ cloudNotLaunchedError: () => cloudNotLaunchedError,
369
+ cloudSessionContractVersion: () => cloudSessionContractVersion,
370
+ cloudUnsupportedMethodError: () => cloudUnsupportedMethodError,
368
371
  collectLocalSelectorCacheEntries: () => collectLocalSelectorCacheEntries,
369
372
  countArrayItemsWithPath: () => countArrayItemsWithPath,
370
373
  createEmptyRegistry: () => createEmptyRegistry,
@@ -396,8 +399,6 @@ __export(index_exports, {
396
399
  performSelect: () => performSelect,
397
400
  prepareSnapshot: () => prepareSnapshot,
398
401
  pressKey: () => pressKey,
399
- remoteNotLaunchedError: () => remoteNotLaunchedError,
400
- remoteUnsupportedMethodError: () => remoteUnsupportedMethodError,
401
402
  resolveCounterElement: () => resolveCounterElement,
402
403
  resolveCountersBatch: () => resolveCountersBatch,
403
404
  resolveElementPath: () => resolveElementPath,
@@ -826,7 +827,7 @@ var BrowserPool = class {
826
827
  const context = contexts[0];
827
828
  const pages = context.pages();
828
829
  const page = pages.length > 0 ? pages[0] : await context.newPage();
829
- return { browser, context, page, isRemote: true };
830
+ return { browser, context, page, isExternal: true };
830
831
  } catch (error) {
831
832
  if (browser) {
832
833
  await browser.close().catch(() => void 0);
@@ -861,7 +862,7 @@ var BrowserPool = class {
861
862
  context = await browser.newContext(options.context || {});
862
863
  page = await context.newPage();
863
864
  }
864
- return { browser, context, page, isRemote: false };
865
+ return { browser, context, page, isExternal: false };
865
866
  }
866
867
  async launchSandbox(options) {
867
868
  const browser = await import_playwright.chromium.launch({
@@ -872,7 +873,7 @@ var BrowserPool = class {
872
873
  const context = await browser.newContext(options.context || {});
873
874
  const page = await context.newPage();
874
875
  this.browser = browser;
875
- return { browser, context, page, isRemote: false };
876
+ return { browser, context, page, isExternal: false };
876
877
  }
877
878
  };
878
879
 
@@ -880,6 +881,7 @@ var BrowserPool = class {
880
881
  var import_fs = __toESM(require("fs"), 1);
881
882
  var import_path3 = __toESM(require("path"), 1);
882
883
  var import_url = require("url");
884
+ var import_dotenv = require("dotenv");
883
885
 
884
886
  // src/storage/namespace.ts
885
887
  var import_path2 = __toESM(require("path"), 1);
@@ -929,6 +931,53 @@ var DEFAULT_CONFIG = {
929
931
  model: "gpt-5.1",
930
932
  debug: false
931
933
  };
934
+ function dotenvFileOrder(nodeEnv) {
935
+ const normalized = nodeEnv?.trim() || "";
936
+ const files = [];
937
+ if (normalized) {
938
+ files.push(`.env.${normalized}.local`);
939
+ }
940
+ if (normalized !== "test") {
941
+ files.push(".env.local");
942
+ }
943
+ if (normalized) {
944
+ files.push(`.env.${normalized}`);
945
+ }
946
+ files.push(".env");
947
+ return files;
948
+ }
949
+ function loadDotenvValues(rootDir, baseEnv) {
950
+ const values = {};
951
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
952
+ return values;
953
+ }
954
+ const baseDir = import_path3.default.resolve(rootDir);
955
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
956
+ for (const filename of dotenvFileOrder(nodeEnv)) {
957
+ const filePath = import_path3.default.join(baseDir, filename);
958
+ if (!import_fs.default.existsSync(filePath)) continue;
959
+ try {
960
+ const raw = import_fs.default.readFileSync(filePath, "utf8");
961
+ const parsed = (0, import_dotenv.parse)(raw);
962
+ for (const [key, value] of Object.entries(parsed)) {
963
+ if (values[key] === void 0) {
964
+ values[key] = value;
965
+ }
966
+ }
967
+ } catch {
968
+ continue;
969
+ }
970
+ }
971
+ return values;
972
+ }
973
+ function resolveEnv(rootDir) {
974
+ const baseEnv = process.env;
975
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
976
+ return {
977
+ ...dotenvValues,
978
+ ...baseEnv
979
+ };
980
+ }
932
981
  function hasOwn(config, key) {
933
982
  if (!config || typeof config !== "object") return false;
934
983
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -943,28 +992,27 @@ function assertNoLegacyAiConfig(source, config) {
943
992
  );
944
993
  }
945
994
  }
946
- function assertNoLegacyModeConfig(source, config) {
995
+ function assertNoLegacyRuntimeConfig(source, config) {
947
996
  if (!config || typeof config !== "object") return;
948
997
  const configRecord = config;
949
998
  if (hasOwn(configRecord, "runtime")) {
950
999
  throw new Error(
951
- `Legacy "runtime" config is no longer supported in ${source}. Use top-level "mode" instead.`
1000
+ `Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
952
1001
  );
953
1002
  }
954
- if (hasOwn(configRecord, "apiKey")) {
1003
+ if (hasOwn(configRecord, "mode")) {
955
1004
  throw new Error(
956
- `Top-level "apiKey" config is not supported in ${source}. Use "remote.apiKey" instead.`
1005
+ `Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
957
1006
  );
958
1007
  }
959
- const remoteValue = configRecord.remote;
960
- if (typeof remoteValue === "boolean") {
1008
+ if (hasOwn(configRecord, "remote")) {
961
1009
  throw new Error(
962
- `Boolean "remote" config is no longer supported in ${source}. Use "mode: \\"remote\\"" with "remote" options.`
1010
+ `Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
963
1011
  );
964
1012
  }
965
- if (remoteValue && typeof remoteValue === "object" && !Array.isArray(remoteValue) && hasOwn(remoteValue, "key")) {
1013
+ if (hasOwn(configRecord, "apiKey")) {
966
1014
  throw new Error(
967
- `Legacy "remote.key" config is no longer supported in ${source}. Use "remote.apiKey" instead.`
1015
+ `Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
968
1016
  );
969
1017
  }
970
1018
  }
@@ -1010,20 +1058,20 @@ function parseNumber(value) {
1010
1058
  if (!Number.isFinite(parsed)) return void 0;
1011
1059
  return parsed;
1012
1060
  }
1013
- function parseMode(value, source) {
1061
+ function parseRuntimeMode(value, source) {
1014
1062
  if (value == null) return void 0;
1015
1063
  if (typeof value !== "string") {
1016
1064
  throw new Error(
1017
- `Invalid ${source} value "${String(value)}". Use "local" or "remote".`
1065
+ `Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
1018
1066
  );
1019
1067
  }
1020
1068
  const normalized = value.trim().toLowerCase();
1021
1069
  if (!normalized) return void 0;
1022
- if (normalized === "local" || normalized === "remote") {
1070
+ if (normalized === "local" || normalized === "cloud") {
1023
1071
  return normalized;
1024
1072
  }
1025
1073
  throw new Error(
1026
- `Invalid ${source} value "${value}". Use "local" or "remote".`
1074
+ `Invalid ${source} value "${value}". Use "local" or "cloud".`
1027
1075
  );
1028
1076
  }
1029
1077
  function parseAuthScheme(value, source) {
@@ -1042,99 +1090,138 @@ function parseAuthScheme(value, source) {
1042
1090
  `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
1043
1091
  );
1044
1092
  }
1045
- function resolveOpensteerApiKey() {
1046
- const value = process.env.OPENSTEER_API_KEY?.trim();
1093
+ function parseCloudAnnounce(value, source) {
1094
+ if (value == null) return void 0;
1095
+ if (typeof value !== "string") {
1096
+ throw new Error(
1097
+ `Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
1098
+ );
1099
+ }
1100
+ const normalized = value.trim().toLowerCase();
1101
+ if (!normalized) return void 0;
1102
+ if (normalized === "always" || normalized === "off" || normalized === "tty") {
1103
+ return normalized;
1104
+ }
1105
+ throw new Error(
1106
+ `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
1107
+ );
1108
+ }
1109
+ function resolveOpensteerApiKey(env) {
1110
+ const value = env.OPENSTEER_API_KEY?.trim();
1047
1111
  if (!value) return void 0;
1048
1112
  return value;
1049
1113
  }
1050
- function resolveOpensteerAuthScheme() {
1051
- return parseAuthScheme(
1052
- process.env.OPENSTEER_AUTH_SCHEME,
1053
- "OPENSTEER_AUTH_SCHEME"
1054
- );
1114
+ function resolveOpensteerAuthScheme(env) {
1115
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
1055
1116
  }
1056
- function normalizeRemoteOptions(value) {
1117
+ function normalizeCloudOptions(value) {
1057
1118
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1058
1119
  return void 0;
1059
1120
  }
1060
1121
  return value;
1061
1122
  }
1062
- function resolveModeSelection(config) {
1063
- const configMode = parseMode(config.mode, "mode");
1064
- if (configMode) {
1123
+ function parseCloudEnabled(value, source) {
1124
+ if (value == null) return void 0;
1125
+ if (typeof value === "boolean") return value;
1126
+ if (typeof value === "object" && !Array.isArray(value)) return true;
1127
+ throw new Error(
1128
+ `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
1129
+ );
1130
+ }
1131
+ function resolveCloudSelection(config, env = process.env) {
1132
+ const configCloud = parseCloudEnabled(config.cloud, "cloud");
1133
+ if (configCloud !== void 0) {
1065
1134
  return {
1066
- mode: configMode,
1067
- source: "config.mode"
1135
+ cloud: configCloud,
1136
+ source: "config.cloud"
1068
1137
  };
1069
1138
  }
1070
- const envMode = parseMode(process.env.OPENSTEER_MODE, "OPENSTEER_MODE");
1139
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
1071
1140
  if (envMode) {
1072
1141
  return {
1073
- mode: envMode,
1142
+ cloud: envMode === "cloud",
1074
1143
  source: "env.OPENSTEER_MODE"
1075
1144
  };
1076
1145
  }
1077
1146
  return {
1078
- mode: "local",
1147
+ cloud: false,
1079
1148
  source: "default"
1080
1149
  };
1081
1150
  }
1082
1151
  function resolveConfig(input = {}) {
1083
- if (process.env.OPENSTEER_AI_MODEL) {
1152
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
1153
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
1154
+ storage: {
1155
+ rootDir: initialRootDir
1156
+ }
1157
+ });
1158
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
1159
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1160
+ const fileConfig = loadConfigFile(initialRootDir);
1161
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1162
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1163
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
1164
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
1165
+ const env = resolveEnv(envRootDir);
1166
+ if (env.OPENSTEER_AI_MODEL) {
1084
1167
  throw new Error(
1085
1168
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
1086
1169
  );
1087
1170
  }
1088
- if (process.env.OPENSTEER_RUNTIME != null) {
1171
+ if (env.OPENSTEER_RUNTIME != null) {
1089
1172
  throw new Error(
1090
1173
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
1091
1174
  );
1092
1175
  }
1093
- assertNoLegacyAiConfig("Opensteer constructor config", input);
1094
- assertNoLegacyModeConfig("Opensteer constructor config", input);
1095
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
1096
- const fileConfig = loadConfigFile(rootDir);
1097
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1098
- assertNoLegacyModeConfig(".opensteer/config.json", fileConfig);
1099
1176
  const envConfig = {
1100
1177
  browser: {
1101
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
1102
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
1103
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
1104
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
1105
- channel: process.env.OPENSTEER_CHANNEL || void 0,
1106
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
1178
+ headless: parseBool(env.OPENSTEER_HEADLESS),
1179
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
1180
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
1181
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
1182
+ channel: env.OPENSTEER_CHANNEL || void 0,
1183
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
1107
1184
  },
1108
- model: process.env.OPENSTEER_MODEL || void 0,
1109
- debug: parseBool(process.env.OPENSTEER_DEBUG)
1185
+ model: env.OPENSTEER_MODEL || void 0,
1186
+ debug: parseBool(env.OPENSTEER_DEBUG)
1110
1187
  };
1111
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
1188
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
1112
1189
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1113
1190
  const resolved = mergeDeep(mergedWithEnv, input);
1114
- const envApiKey = resolveOpensteerApiKey();
1115
- const envAuthScheme = resolveOpensteerAuthScheme();
1116
- const inputRemoteOptions = normalizeRemoteOptions(input.remote);
1191
+ const envApiKey = resolveOpensteerApiKey(env);
1192
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
1193
+ const envCloudAnnounce = parseCloudAnnounce(
1194
+ env.OPENSTEER_REMOTE_ANNOUNCE,
1195
+ "OPENSTEER_REMOTE_ANNOUNCE"
1196
+ );
1197
+ const inputCloudOptions = normalizeCloudOptions(input.cloud);
1117
1198
  const inputAuthScheme = parseAuthScheme(
1118
- inputRemoteOptions?.authScheme,
1119
- "remote.authScheme"
1199
+ inputCloudOptions?.authScheme,
1200
+ "cloud.authScheme"
1120
1201
  );
1121
- const inputHasRemoteApiKey = Boolean(
1122
- inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
1202
+ const inputCloudAnnounce = parseCloudAnnounce(
1203
+ inputCloudOptions?.announce,
1204
+ "cloud.announce"
1123
1205
  );
1124
- const modeSelection = resolveModeSelection({
1125
- mode: resolved.mode
1126
- });
1127
- if (modeSelection.mode === "remote") {
1128
- const resolvedRemote = normalizeRemoteOptions(resolved.remote) ?? {};
1129
- const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedRemote.authScheme, "remote.authScheme") ?? "api-key";
1130
- resolved.remote = {
1131
- ...resolvedRemote,
1132
- authScheme
1206
+ const inputHasCloudApiKey = Boolean(
1207
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
1208
+ );
1209
+ const cloudSelection = resolveCloudSelection({
1210
+ cloud: resolved.cloud
1211
+ }, env);
1212
+ if (cloudSelection.cloud) {
1213
+ const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
1214
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
1215
+ const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
1216
+ resolved.cloud = {
1217
+ ...resolvedCloud,
1218
+ authScheme,
1219
+ announce
1133
1220
  };
1134
1221
  }
1135
- if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
1136
- resolved.remote = {
1137
- ...normalizeRemoteOptions(resolved.remote) ?? {},
1222
+ if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
1223
+ resolved.cloud = {
1224
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
1138
1225
  apiKey: envApiKey
1139
1226
  };
1140
1227
  }
@@ -7441,36 +7528,39 @@ function clonePersistedExtractNode(node) {
7441
7528
  return JSON.parse(JSON.stringify(node));
7442
7529
  }
7443
7530
 
7444
- // src/remote/action-ws-client.ts
7531
+ // src/cloud/contracts.ts
7532
+ var cloudSessionContractVersion = "v3";
7533
+
7534
+ // src/cloud/action-ws-client.ts
7445
7535
  var import_ws2 = __toESM(require("ws"), 1);
7446
7536
 
7447
- // src/remote/errors.ts
7448
- var OpensteerRemoteError = class extends Error {
7537
+ // src/cloud/errors.ts
7538
+ var OpensteerCloudError = class extends Error {
7449
7539
  code;
7450
7540
  status;
7451
7541
  details;
7452
7542
  constructor(code, message, status, details) {
7453
7543
  super(message);
7454
- this.name = "OpensteerRemoteError";
7544
+ this.name = "OpensteerCloudError";
7455
7545
  this.code = code;
7456
7546
  this.status = status;
7457
7547
  this.details = details;
7458
7548
  }
7459
7549
  };
7460
- function remoteUnsupportedMethodError(method, message) {
7461
- return new OpensteerRemoteError(
7462
- "REMOTE_UNSUPPORTED_METHOD",
7463
- message || `${method} is not supported in remote mode.`
7550
+ function cloudUnsupportedMethodError(method, message) {
7551
+ return new OpensteerCloudError(
7552
+ "CLOUD_UNSUPPORTED_METHOD",
7553
+ message || `${method} is not supported in cloud mode.`
7464
7554
  );
7465
7555
  }
7466
- function remoteNotLaunchedError() {
7467
- return new OpensteerRemoteError(
7468
- "REMOTE_SESSION_NOT_FOUND",
7469
- "Remote session is not connected. Call launch() first."
7556
+ function cloudNotLaunchedError() {
7557
+ return new OpensteerCloudError(
7558
+ "CLOUD_SESSION_NOT_FOUND",
7559
+ "Cloud session is not connected. Call launch() first."
7470
7560
  );
7471
7561
  }
7472
7562
 
7473
- // src/remote/action-ws-client.ts
7563
+ // src/cloud/action-ws-client.ts
7474
7564
  var ActionWsClient = class _ActionWsClient {
7475
7565
  ws;
7476
7566
  sessionId;
@@ -7487,18 +7577,18 @@ var ActionWsClient = class _ActionWsClient {
7487
7577
  });
7488
7578
  ws.on("error", (error) => {
7489
7579
  this.rejectAll(
7490
- new OpensteerRemoteError(
7491
- "REMOTE_TRANSPORT_ERROR",
7492
- `Remote action websocket error: ${error.message}`
7580
+ new OpensteerCloudError(
7581
+ "CLOUD_TRANSPORT_ERROR",
7582
+ `Cloud action websocket error: ${error.message}`
7493
7583
  )
7494
7584
  );
7495
7585
  });
7496
7586
  ws.on("close", () => {
7497
7587
  this.closed = true;
7498
7588
  this.rejectAll(
7499
- new OpensteerRemoteError(
7500
- "REMOTE_SESSION_CLOSED",
7501
- "Remote action websocket closed."
7589
+ new OpensteerCloudError(
7590
+ "CLOUD_SESSION_CLOSED",
7591
+ "Cloud action websocket closed."
7502
7592
  )
7503
7593
  );
7504
7594
  });
@@ -7510,8 +7600,8 @@ var ActionWsClient = class _ActionWsClient {
7510
7600
  ws.once("open", () => resolve());
7511
7601
  ws.once("error", (error) => {
7512
7602
  reject(
7513
- new OpensteerRemoteError(
7514
- "REMOTE_TRANSPORT_ERROR",
7603
+ new OpensteerCloudError(
7604
+ "CLOUD_TRANSPORT_ERROR",
7515
7605
  `Failed to connect action websocket: ${error.message}`
7516
7606
  )
7517
7607
  );
@@ -7521,9 +7611,9 @@ var ActionWsClient = class _ActionWsClient {
7521
7611
  }
7522
7612
  async request(method, args) {
7523
7613
  if (this.closed || this.ws.readyState !== import_ws2.default.OPEN) {
7524
- throw new OpensteerRemoteError(
7525
- "REMOTE_SESSION_CLOSED",
7526
- "Remote action websocket is closed."
7614
+ throw new OpensteerCloudError(
7615
+ "CLOUD_SESSION_CLOSED",
7616
+ "Cloud action websocket is closed."
7527
7617
  );
7528
7618
  }
7529
7619
  const id = this.nextRequestId;
@@ -7542,8 +7632,8 @@ var ActionWsClient = class _ActionWsClient {
7542
7632
  this.ws.send(JSON.stringify(payload));
7543
7633
  } catch (error) {
7544
7634
  this.pending.delete(id);
7545
- const message = error instanceof Error ? error.message : "Failed to send remote action request.";
7546
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
7635
+ const message = error instanceof Error ? error.message : "Failed to send cloud action request.";
7636
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7547
7637
  }
7548
7638
  return await resultPromise;
7549
7639
  }
@@ -7561,9 +7651,9 @@ var ActionWsClient = class _ActionWsClient {
7561
7651
  parsed = JSON.parse(rawDataToUtf8(raw));
7562
7652
  } catch {
7563
7653
  this.rejectAll(
7564
- new OpensteerRemoteError(
7565
- "REMOTE_TRANSPORT_ERROR",
7566
- "Invalid remote action response payload."
7654
+ new OpensteerCloudError(
7655
+ "CLOUD_TRANSPORT_ERROR",
7656
+ "Invalid cloud action response payload."
7567
7657
  )
7568
7658
  );
7569
7659
  return;
@@ -7576,7 +7666,7 @@ var ActionWsClient = class _ActionWsClient {
7576
7666
  return;
7577
7667
  }
7578
7668
  pending.reject(
7579
- new OpensteerRemoteError(
7669
+ new OpensteerCloudError(
7580
7670
  parsed.code,
7581
7671
  parsed.error,
7582
7672
  void 0,
@@ -7604,7 +7694,7 @@ function withTokenQuery(wsUrl, token) {
7604
7694
  return url.toString();
7605
7695
  }
7606
7696
 
7607
- // src/remote/local-cache-sync.ts
7697
+ // src/cloud/local-cache-sync.ts
7608
7698
  var import_fs3 = __toESM(require("fs"), 1);
7609
7699
  var import_path5 = __toESM(require("path"), 1);
7610
7700
  function collectLocalSelectorCacheEntries(storage) {
@@ -7728,24 +7818,24 @@ function dedupeNewest(entries) {
7728
7818
  return [...byKey.values()];
7729
7819
  }
7730
7820
 
7731
- // src/remote/cdp-client.ts
7821
+ // src/cloud/cdp-client.ts
7732
7822
  var import_playwright2 = require("playwright");
7733
- var RemoteCdpClient = class {
7823
+ var CloudCdpClient = class {
7734
7824
  async connect(args) {
7735
7825
  const endpoint = withTokenQuery2(args.wsUrl, args.token);
7736
7826
  let browser;
7737
7827
  try {
7738
7828
  browser = await import_playwright2.chromium.connectOverCDP(endpoint);
7739
7829
  } catch (error) {
7740
- const message = error instanceof Error ? error.message : "Failed to connect to remote CDP endpoint.";
7741
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
7830
+ const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
7831
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7742
7832
  }
7743
7833
  const context = browser.contexts()[0];
7744
7834
  if (!context) {
7745
7835
  await browser.close();
7746
- throw new OpensteerRemoteError(
7747
- "REMOTE_INTERNAL",
7748
- "Remote browser returned no context."
7836
+ throw new OpensteerCloudError(
7837
+ "CLOUD_INTERNAL",
7838
+ "Cloud browser returned no context."
7749
7839
  );
7750
7840
  }
7751
7841
  const page = context.pages()[0] || await context.newPage();
@@ -7758,9 +7848,9 @@ function withTokenQuery2(wsUrl, token) {
7758
7848
  return url.toString();
7759
7849
  }
7760
7850
 
7761
- // src/remote/session-client.ts
7851
+ // src/cloud/session-client.ts
7762
7852
  var CACHE_IMPORT_BATCH_SIZE = 200;
7763
- var RemoteSessionClient = class {
7853
+ var CloudSessionClient = class {
7764
7854
  baseUrl;
7765
7855
  key;
7766
7856
  authScheme;
@@ -7781,7 +7871,17 @@ var RemoteSessionClient = class {
7781
7871
  if (!response.ok) {
7782
7872
  throw await parseHttpError(response);
7783
7873
  }
7784
- return await response.json();
7874
+ let body;
7875
+ try {
7876
+ body = await response.json();
7877
+ } catch {
7878
+ throw new OpensteerCloudError(
7879
+ "CLOUD_CONTRACT_MISMATCH",
7880
+ "Invalid cloud session create response: expected a JSON object.",
7881
+ response.status
7882
+ );
7883
+ }
7884
+ return parseCreateResponse(body, response.status);
7785
7885
  }
7786
7886
  async close(sessionId) {
7787
7887
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
@@ -7840,6 +7940,134 @@ var RemoteSessionClient = class {
7840
7940
  function normalizeBaseUrl(baseUrl) {
7841
7941
  return baseUrl.replace(/\/+$/, "");
7842
7942
  }
7943
+ function parseCreateResponse(body, status) {
7944
+ const root = requireObject(
7945
+ body,
7946
+ "Invalid cloud session create response: expected a JSON object.",
7947
+ status
7948
+ );
7949
+ const sessionId = requireString(root, "sessionId", status);
7950
+ const actionWsUrl = requireString(root, "actionWsUrl", status);
7951
+ const cdpWsUrl = requireString(root, "cdpWsUrl", status);
7952
+ const actionToken = requireString(root, "actionToken", status);
7953
+ const cdpToken = requireString(root, "cdpToken", status);
7954
+ const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
7955
+ const cloudSessionRoot = requireObject(
7956
+ root.cloudSession,
7957
+ "Invalid cloud session create response: cloudSession must be an object.",
7958
+ status
7959
+ );
7960
+ const cloudSession = {
7961
+ sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
7962
+ workspaceId: requireString(
7963
+ cloudSessionRoot,
7964
+ "workspaceId",
7965
+ status,
7966
+ "cloudSession"
7967
+ ),
7968
+ state: requireString(cloudSessionRoot, "state", status, "cloudSession"),
7969
+ createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
7970
+ sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
7971
+ sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
7972
+ label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
7973
+ };
7974
+ const expiresAt = optionalNumber(root, "expiresAt", status);
7975
+ return {
7976
+ sessionId,
7977
+ actionWsUrl,
7978
+ cdpWsUrl,
7979
+ actionToken,
7980
+ cdpToken,
7981
+ expiresAt,
7982
+ cloudSessionUrl,
7983
+ cloudSession
7984
+ };
7985
+ }
7986
+ function requireObject(value, message, status) {
7987
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
7988
+ throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
7989
+ }
7990
+ return value;
7991
+ }
7992
+ function requireString(source, field, status, parent) {
7993
+ const value = source[field];
7994
+ if (typeof value !== "string" || !value.trim()) {
7995
+ throw new OpensteerCloudError(
7996
+ "CLOUD_CONTRACT_MISMATCH",
7997
+ `Invalid cloud session create response: ${formatFieldPath(
7998
+ field,
7999
+ parent
8000
+ )} must be a non-empty string.`,
8001
+ status
8002
+ );
8003
+ }
8004
+ return value;
8005
+ }
8006
+ function requireNumber(source, field, status, parent) {
8007
+ const value = source[field];
8008
+ if (typeof value !== "number" || !Number.isFinite(value)) {
8009
+ throw new OpensteerCloudError(
8010
+ "CLOUD_CONTRACT_MISMATCH",
8011
+ `Invalid cloud session create response: ${formatFieldPath(
8012
+ field,
8013
+ parent
8014
+ )} must be a finite number.`,
8015
+ status
8016
+ );
8017
+ }
8018
+ return value;
8019
+ }
8020
+ function optionalString(source, field, status, parent) {
8021
+ const value = source[field];
8022
+ if (value == null) {
8023
+ return void 0;
8024
+ }
8025
+ if (typeof value !== "string") {
8026
+ throw new OpensteerCloudError(
8027
+ "CLOUD_CONTRACT_MISMATCH",
8028
+ `Invalid cloud session create response: ${formatFieldPath(
8029
+ field,
8030
+ parent
8031
+ )} must be a string when present.`,
8032
+ status
8033
+ );
8034
+ }
8035
+ return value;
8036
+ }
8037
+ function optionalNumber(source, field, status, parent) {
8038
+ const value = source[field];
8039
+ if (value == null) {
8040
+ return void 0;
8041
+ }
8042
+ if (typeof value !== "number" || !Number.isFinite(value)) {
8043
+ throw new OpensteerCloudError(
8044
+ "CLOUD_CONTRACT_MISMATCH",
8045
+ `Invalid cloud session create response: ${formatFieldPath(
8046
+ field,
8047
+ parent
8048
+ )} must be a finite number when present.`,
8049
+ status
8050
+ );
8051
+ }
8052
+ return value;
8053
+ }
8054
+ function requireSourceType(source, field, status, parent) {
8055
+ const value = source[field];
8056
+ if (value === "agent-thread" || value === "agent-run" || value === "local-cloud" || value === "manual") {
8057
+ return value;
8058
+ }
8059
+ throw new OpensteerCloudError(
8060
+ "CLOUD_CONTRACT_MISMATCH",
8061
+ `Invalid cloud session create response: ${formatFieldPath(
8062
+ field,
8063
+ parent
8064
+ )} must be one of "agent-thread", "agent-run", "local-cloud", or "manual".`,
8065
+ status
8066
+ );
8067
+ }
8068
+ function formatFieldPath(field, parent) {
8069
+ return parent ? `"${parent}.${field}"` : `"${field}"`;
8070
+ }
7843
8071
  function zeroImportResponse() {
7844
8072
  return {
7845
8073
  imported: 0,
@@ -7863,33 +8091,46 @@ async function parseHttpError(response) {
7863
8091
  } catch {
7864
8092
  body = null;
7865
8093
  }
7866
- const code = typeof body?.code === "string" ? toRemoteErrorCode(body.code) : "REMOTE_TRANSPORT_ERROR";
7867
- const message = typeof body?.error === "string" ? body.error : `Remote request failed with status ${response.status}.`;
7868
- return new OpensteerRemoteError(code, message, response.status, body?.details);
8094
+ const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
8095
+ const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
8096
+ return new OpensteerCloudError(code, message, response.status, body?.details);
7869
8097
  }
7870
- function toRemoteErrorCode(code) {
7871
- 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") {
8098
+ function toCloudErrorCode(code) {
8099
+ 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") {
7872
8100
  return code;
7873
8101
  }
7874
- return "REMOTE_TRANSPORT_ERROR";
8102
+ return "CLOUD_TRANSPORT_ERROR";
7875
8103
  }
7876
8104
 
7877
- // src/remote/runtime.ts
7878
- var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7879
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
8105
+ // src/cloud/runtime.ts
8106
+ 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()) {
7880
8109
  return {
7881
- sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7882
- cdpClient: new RemoteCdpClient(),
8110
+ sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
8111
+ cdpClient: new CloudCdpClient(),
8112
+ appUrl: normalizeCloudAppUrl(appUrl),
7883
8113
  actionClient: null,
7884
- sessionId: null
8114
+ sessionId: null,
8115
+ localRunId: null,
8116
+ cloudSessionUrl: null
7885
8117
  };
7886
8118
  }
7887
- function resolveRemoteBaseUrl() {
8119
+ function resolveCloudBaseUrl() {
7888
8120
  const value = process.env.OPENSTEER_BASE_URL?.trim();
7889
- if (!value) return DEFAULT_REMOTE_BASE_URL;
8121
+ if (!value) return DEFAULT_CLOUD_BASE_URL;
7890
8122
  return value.replace(/\/+$/, "");
7891
8123
  }
7892
- function readRemoteActionDescription(payload) {
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;
8131
+ return value.replace(/\/+$/, "");
8132
+ }
8133
+ function readCloudActionDescription(payload) {
7893
8134
  const description = payload.description;
7894
8135
  if (typeof description !== "string") return void 0;
7895
8136
  const normalized = description.trim();
@@ -7897,7 +8138,7 @@ function readRemoteActionDescription(payload) {
7897
8138
  }
7898
8139
 
7899
8140
  // src/opensteer.ts
7900
- var REMOTE_INTERACTION_METHODS = /* @__PURE__ */ new Set([
8141
+ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
7901
8142
  "click",
7902
8143
  "dblclick",
7903
8144
  "rightclick",
@@ -7914,7 +8155,7 @@ var Opensteer = class _Opensteer {
7914
8155
  namespace;
7915
8156
  storage;
7916
8157
  pool;
7917
- remote;
8158
+ cloud;
7918
8159
  browser = null;
7919
8160
  pageRef = null;
7920
8161
  contextRef = null;
@@ -7922,8 +8163,8 @@ var Opensteer = class _Opensteer {
7922
8163
  snapshotCache = null;
7923
8164
  constructor(config = {}) {
7924
8165
  const resolved = resolveConfig(config);
7925
- const modeSelection = resolveModeSelection({
7926
- mode: resolved.mode
8166
+ const cloudSelection = resolveCloudSelection({
8167
+ cloud: resolved.cloud
7927
8168
  });
7928
8169
  const model = resolved.model;
7929
8170
  this.config = resolved;
@@ -7933,21 +8174,22 @@ var Opensteer = class _Opensteer {
7933
8174
  this.namespace = resolveNamespace(resolved, rootDir);
7934
8175
  this.storage = new LocalSelectorStorage(rootDir, this.namespace);
7935
8176
  this.pool = new BrowserPool(resolved.browser || {});
7936
- if (modeSelection.mode === "remote") {
7937
- const remoteConfig = resolved.remote && typeof resolved.remote === "object" ? resolved.remote : void 0;
7938
- const apiKey = remoteConfig?.apiKey?.trim();
8177
+ if (cloudSelection.cloud) {
8178
+ const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
8179
+ const apiKey = cloudConfig?.apiKey?.trim();
7939
8180
  if (!apiKey) {
7940
8181
  throw new Error(
7941
- "Remote mode requires a non-empty API key via remote.apiKey or OPENSTEER_API_KEY."
8182
+ "Cloud mode requires a non-empty API key via cloud.apiKey or OPENSTEER_API_KEY."
7942
8183
  );
7943
8184
  }
7944
- this.remote = createRemoteRuntimeState(
8185
+ this.cloud = createCloudRuntimeState(
7945
8186
  apiKey,
7946
- remoteConfig?.baseUrl,
7947
- remoteConfig?.authScheme
8187
+ cloudConfig?.baseUrl,
8188
+ cloudConfig?.authScheme,
8189
+ cloudConfig?.appUrl
7948
8190
  );
7949
8191
  } else {
7950
- this.remote = null;
8192
+ this.cloud = null;
7951
8193
  }
7952
8194
  }
7953
8195
  createLazyResolveCallback(model) {
@@ -7985,32 +8227,32 @@ var Opensteer = class _Opensteer {
7985
8227
  };
7986
8228
  return extract;
7987
8229
  }
7988
- async invokeRemoteActionAndResetCache(method, args) {
7989
- const result = await this.invokeRemoteAction(method, args);
8230
+ async invokeCloudActionAndResetCache(method, args) {
8231
+ const result = await this.invokeCloudAction(method, args);
7990
8232
  this.snapshotCache = null;
7991
8233
  return result;
7992
8234
  }
7993
- async invokeRemoteAction(method, args) {
7994
- const actionClient = this.remote?.actionClient;
7995
- const sessionId = this.remote?.sessionId;
8235
+ async invokeCloudAction(method, args) {
8236
+ const actionClient = this.cloud?.actionClient;
8237
+ const sessionId = this.cloud?.sessionId;
7996
8238
  if (!actionClient || !sessionId) {
7997
- throw remoteNotLaunchedError();
8239
+ throw cloudNotLaunchedError();
7998
8240
  }
7999
8241
  const payload = args && typeof args === "object" ? args : {};
8000
8242
  try {
8001
8243
  return await actionClient.request(method, payload);
8002
8244
  } catch (err) {
8003
- if (err instanceof OpensteerRemoteError && err.code === "REMOTE_ACTION_FAILED" && REMOTE_INTERACTION_METHODS.has(method)) {
8245
+ if (err instanceof OpensteerCloudError && err.code === "CLOUD_ACTION_FAILED" && CLOUD_INTERACTION_METHODS.has(method)) {
8004
8246
  const detailsRecord = err.details && typeof err.details === "object" ? err.details : null;
8005
- const remoteFailure = normalizeActionFailure(
8247
+ const cloudFailure = normalizeActionFailure(
8006
8248
  detailsRecord?.actionFailure
8007
8249
  );
8008
- const failure = remoteFailure || classifyActionFailure({
8250
+ const failure = cloudFailure || classifyActionFailure({
8009
8251
  action: method,
8010
8252
  error: err,
8011
8253
  fallbackMessage: defaultActionFailureMessage(method)
8012
8254
  });
8013
- const description = readRemoteActionDescription(payload);
8255
+ const description = readCloudActionDescription(payload);
8014
8256
  throw this.buildActionError(
8015
8257
  method,
8016
8258
  description,
@@ -8051,8 +8293,36 @@ var Opensteer = class _Opensteer {
8051
8293
  }
8052
8294
  return this.contextRef;
8053
8295
  }
8054
- getRemoteSessionId() {
8055
- return this.remote?.sessionId ?? null;
8296
+ getCloudSessionId() {
8297
+ return this.cloud?.sessionId ?? null;
8298
+ }
8299
+ getCloudSessionUrl() {
8300
+ return this.cloud?.cloudSessionUrl ?? null;
8301
+ }
8302
+ announceCloudSession(args) {
8303
+ if (!this.shouldAnnounceCloudSession()) {
8304
+ return;
8305
+ }
8306
+ const fields = [
8307
+ `sessionId=${args.sessionId}`,
8308
+ `workspaceId=${args.workspaceId}`
8309
+ ];
8310
+ if (args.cloudSessionUrl) {
8311
+ fields.push(`url=${args.cloudSessionUrl}`);
8312
+ }
8313
+ process.stderr.write(`[opensteer] cloud session ready ${fields.join(" ")}
8314
+ `);
8315
+ }
8316
+ shouldAnnounceCloudSession() {
8317
+ const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : null;
8318
+ const announce = cloudConfig?.announce ?? "always";
8319
+ if (announce === "off") {
8320
+ return false;
8321
+ }
8322
+ if (announce === "tty") {
8323
+ return Boolean(process.stderr.isTTY);
8324
+ }
8325
+ return true;
8056
8326
  }
8057
8327
  async launch(options = {}) {
8058
8328
  if (this.pageRef && !this.ownsBrowser) {
@@ -8063,22 +8333,29 @@ var Opensteer = class _Opensteer {
8063
8333
  if (this.pageRef && this.ownsBrowser) {
8064
8334
  return;
8065
8335
  }
8066
- if (this.remote) {
8336
+ if (this.cloud) {
8067
8337
  let actionClient = null;
8068
8338
  let browser = null;
8069
8339
  let sessionId = null;
8340
+ let localRunId = null;
8070
8341
  try {
8071
8342
  try {
8072
- await this.syncLocalSelectorCacheToRemote();
8343
+ await this.syncLocalSelectorCacheToCloud();
8073
8344
  } catch (error) {
8074
8345
  if (this.config.debug) {
8075
8346
  const message = error instanceof Error ? error.message : String(error);
8076
8347
  console.warn(
8077
- `[opensteer] remote selector cache sync failed: ${message}`
8348
+ `[opensteer] cloud selector cache sync failed: ${message}`
8078
8349
  );
8079
8350
  }
8080
8351
  }
8081
- const session2 = await this.remote.sessionClient.create({
8352
+ localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
8353
+ this.cloud.localRunId = localRunId;
8354
+ const session2 = await this.cloud.sessionClient.create({
8355
+ cloudSessionContractVersion,
8356
+ sourceType: "local-cloud",
8357
+ clientSessionHint: this.namespace,
8358
+ localRunId,
8082
8359
  name: this.namespace,
8083
8360
  model: this.config.model,
8084
8361
  launchContext: options.context || void 0
@@ -8089,7 +8366,7 @@ var Opensteer = class _Opensteer {
8089
8366
  token: session2.actionToken,
8090
8367
  sessionId: session2.sessionId
8091
8368
  });
8092
- const cdpConnection = await this.remote.cdpClient.connect({
8369
+ const cdpConnection = await this.cloud.cdpClient.connect({
8093
8370
  wsUrl: session2.cdpWsUrl,
8094
8371
  token: session2.cdpToken
8095
8372
  });
@@ -8099,8 +8376,17 @@ var Opensteer = class _Opensteer {
8099
8376
  this.pageRef = cdpConnection.page;
8100
8377
  this.ownsBrowser = true;
8101
8378
  this.snapshotCache = null;
8102
- this.remote.actionClient = actionClient;
8103
- this.remote.sessionId = sessionId;
8379
+ this.cloud.actionClient = actionClient;
8380
+ this.cloud.sessionId = sessionId;
8381
+ this.cloud.cloudSessionUrl = buildCloudSessionUrl(
8382
+ this.cloud.appUrl,
8383
+ session2.cloudSession.sessionId
8384
+ );
8385
+ this.announceCloudSession({
8386
+ sessionId: session2.sessionId,
8387
+ workspaceId: session2.cloudSession.workspaceId,
8388
+ cloudSessionUrl: this.cloud.cloudSessionUrl
8389
+ });
8104
8390
  return;
8105
8391
  } catch (error) {
8106
8392
  if (actionClient) {
@@ -8110,8 +8396,9 @@ var Opensteer = class _Opensteer {
8110
8396
  await browser.close().catch(() => void 0);
8111
8397
  }
8112
8398
  if (sessionId) {
8113
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8399
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8114
8400
  }
8401
+ this.cloud.cloudSessionUrl = null;
8115
8402
  throw error;
8116
8403
  }
8117
8404
  }
@@ -8129,13 +8416,13 @@ var Opensteer = class _Opensteer {
8129
8416
  }
8130
8417
  static from(page, config = {}) {
8131
8418
  const resolvedConfig = resolveConfig(config);
8132
- const modeSelection = resolveModeSelection({
8133
- mode: resolvedConfig.mode
8419
+ const cloudSelection = resolveCloudSelection({
8420
+ cloud: resolvedConfig.cloud
8134
8421
  });
8135
- if (modeSelection.mode === "remote") {
8136
- throw remoteUnsupportedMethodError(
8422
+ if (cloudSelection.cloud) {
8423
+ throw cloudUnsupportedMethodError(
8137
8424
  "Opensteer.from(page)",
8138
- "Opensteer.from(page) is not supported in remote mode."
8425
+ "Opensteer.from(page) is not supported in cloud mode."
8139
8426
  );
8140
8427
  }
8141
8428
  const instance = new _Opensteer(config);
@@ -8148,12 +8435,14 @@ var Opensteer = class _Opensteer {
8148
8435
  }
8149
8436
  async close() {
8150
8437
  this.snapshotCache = null;
8151
- if (this.remote) {
8152
- const actionClient = this.remote.actionClient;
8153
- const sessionId = this.remote.sessionId;
8438
+ if (this.cloud) {
8439
+ const actionClient = this.cloud.actionClient;
8440
+ const sessionId = this.cloud.sessionId;
8154
8441
  const browser = this.browser;
8155
- this.remote.actionClient = null;
8156
- this.remote.sessionId = null;
8442
+ this.cloud.actionClient = null;
8443
+ this.cloud.sessionId = null;
8444
+ this.cloud.localRunId = null;
8445
+ this.cloud.cloudSessionUrl = null;
8157
8446
  this.browser = null;
8158
8447
  this.pageRef = null;
8159
8448
  this.contextRef = null;
@@ -8165,7 +8454,7 @@ var Opensteer = class _Opensteer {
8165
8454
  await browser.close().catch(() => void 0);
8166
8455
  }
8167
8456
  if (sessionId) {
8168
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8457
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8169
8458
  }
8170
8459
  return;
8171
8460
  }
@@ -8177,17 +8466,17 @@ var Opensteer = class _Opensteer {
8177
8466
  this.contextRef = null;
8178
8467
  this.ownsBrowser = false;
8179
8468
  }
8180
- async syncLocalSelectorCacheToRemote() {
8181
- if (!this.remote) return;
8469
+ async syncLocalSelectorCacheToCloud() {
8470
+ if (!this.cloud) return;
8182
8471
  const entries = collectLocalSelectorCacheEntries(this.storage);
8183
8472
  if (!entries.length) return;
8184
- await this.remote.sessionClient.importSelectorCache({
8473
+ await this.cloud.sessionClient.importSelectorCache({
8185
8474
  entries
8186
8475
  });
8187
8476
  }
8188
8477
  async goto(url, options) {
8189
- if (this.remote) {
8190
- await this.invokeRemoteActionAndResetCache("goto", { url, options });
8478
+ if (this.cloud) {
8479
+ await this.invokeCloudActionAndResetCache("goto", { url, options });
8191
8480
  return;
8192
8481
  }
8193
8482
  const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
@@ -8196,8 +8485,8 @@ var Opensteer = class _Opensteer {
8196
8485
  this.snapshotCache = null;
8197
8486
  }
8198
8487
  async snapshot(options = {}) {
8199
- if (this.remote) {
8200
- return await this.invokeRemoteActionAndResetCache("snapshot", {
8488
+ if (this.cloud) {
8489
+ return await this.invokeCloudActionAndResetCache("snapshot", {
8201
8490
  options
8202
8491
  });
8203
8492
  }
@@ -8206,8 +8495,8 @@ var Opensteer = class _Opensteer {
8206
8495
  return prepared.cleanedHtml;
8207
8496
  }
8208
8497
  async state() {
8209
- if (this.remote) {
8210
- return await this.invokeRemoteAction("state", {});
8498
+ if (this.cloud) {
8499
+ return await this.invokeCloudAction("state", {});
8211
8500
  }
8212
8501
  const html = await this.snapshot({ mode: "action" });
8213
8502
  return {
@@ -8217,8 +8506,8 @@ var Opensteer = class _Opensteer {
8217
8506
  };
8218
8507
  }
8219
8508
  async screenshot(options = {}) {
8220
- if (this.remote) {
8221
- const b64 = await this.invokeRemoteAction(
8509
+ if (this.cloud) {
8510
+ const b64 = await this.invokeCloudAction(
8222
8511
  "screenshot",
8223
8512
  options
8224
8513
  );
@@ -8232,8 +8521,8 @@ var Opensteer = class _Opensteer {
8232
8521
  });
8233
8522
  }
8234
8523
  async click(options) {
8235
- if (this.remote) {
8236
- return await this.invokeRemoteActionAndResetCache(
8524
+ if (this.cloud) {
8525
+ return await this.invokeCloudActionAndResetCache(
8237
8526
  "click",
8238
8527
  options
8239
8528
  );
@@ -8245,8 +8534,8 @@ var Opensteer = class _Opensteer {
8245
8534
  });
8246
8535
  }
8247
8536
  async dblclick(options) {
8248
- if (this.remote) {
8249
- return await this.invokeRemoteActionAndResetCache(
8537
+ if (this.cloud) {
8538
+ return await this.invokeCloudActionAndResetCache(
8250
8539
  "dblclick",
8251
8540
  options
8252
8541
  );
@@ -8258,8 +8547,8 @@ var Opensteer = class _Opensteer {
8258
8547
  });
8259
8548
  }
8260
8549
  async rightclick(options) {
8261
- if (this.remote) {
8262
- return await this.invokeRemoteActionAndResetCache(
8550
+ if (this.cloud) {
8551
+ return await this.invokeCloudActionAndResetCache(
8263
8552
  "rightclick",
8264
8553
  options
8265
8554
  );
@@ -8271,8 +8560,8 @@ var Opensteer = class _Opensteer {
8271
8560
  });
8272
8561
  }
8273
8562
  async hover(options) {
8274
- if (this.remote) {
8275
- return await this.invokeRemoteActionAndResetCache(
8563
+ if (this.cloud) {
8564
+ return await this.invokeCloudActionAndResetCache(
8276
8565
  "hover",
8277
8566
  options
8278
8567
  );
@@ -8370,8 +8659,8 @@ var Opensteer = class _Opensteer {
8370
8659
  );
8371
8660
  }
8372
8661
  async input(options) {
8373
- if (this.remote) {
8374
- return await this.invokeRemoteActionAndResetCache(
8662
+ if (this.cloud) {
8663
+ return await this.invokeCloudActionAndResetCache(
8375
8664
  "input",
8376
8665
  options
8377
8666
  );
@@ -8473,8 +8762,8 @@ var Opensteer = class _Opensteer {
8473
8762
  );
8474
8763
  }
8475
8764
  async select(options) {
8476
- if (this.remote) {
8477
- return await this.invokeRemoteActionAndResetCache(
8765
+ if (this.cloud) {
8766
+ return await this.invokeCloudActionAndResetCache(
8478
8767
  "select",
8479
8768
  options
8480
8769
  );
@@ -8583,8 +8872,8 @@ var Opensteer = class _Opensteer {
8583
8872
  );
8584
8873
  }
8585
8874
  async scroll(options = {}) {
8586
- if (this.remote) {
8587
- return await this.invokeRemoteActionAndResetCache(
8875
+ if (this.cloud) {
8876
+ return await this.invokeCloudActionAndResetCache(
8588
8877
  "scroll",
8589
8878
  options
8590
8879
  );
@@ -8685,14 +8974,14 @@ var Opensteer = class _Opensteer {
8685
8974
  }
8686
8975
  // --- Tab Management ---
8687
8976
  async tabs() {
8688
- if (this.remote) {
8689
- return await this.invokeRemoteAction("tabs", {});
8977
+ if (this.cloud) {
8978
+ return await this.invokeCloudAction("tabs", {});
8690
8979
  }
8691
8980
  return listTabs(this.context, this.page);
8692
8981
  }
8693
8982
  async newTab(url) {
8694
- if (this.remote) {
8695
- return await this.invokeRemoteActionAndResetCache("newTab", {
8983
+ if (this.cloud) {
8984
+ return await this.invokeCloudActionAndResetCache("newTab", {
8696
8985
  url
8697
8986
  });
8698
8987
  }
@@ -8702,8 +8991,8 @@ var Opensteer = class _Opensteer {
8702
8991
  return info;
8703
8992
  }
8704
8993
  async switchTab(index) {
8705
- if (this.remote) {
8706
- await this.invokeRemoteActionAndResetCache("switchTab", { index });
8994
+ if (this.cloud) {
8995
+ await this.invokeCloudActionAndResetCache("switchTab", { index });
8707
8996
  return;
8708
8997
  }
8709
8998
  const page = await switchTab(this.context, index);
@@ -8711,8 +9000,8 @@ var Opensteer = class _Opensteer {
8711
9000
  this.snapshotCache = null;
8712
9001
  }
8713
9002
  async closeTab(index) {
8714
- if (this.remote) {
8715
- await this.invokeRemoteActionAndResetCache("closeTab", { index });
9003
+ if (this.cloud) {
9004
+ await this.invokeCloudActionAndResetCache("closeTab", { index });
8716
9005
  return;
8717
9006
  }
8718
9007
  const newPage = await closeTab(this.context, this.page, index);
@@ -8723,8 +9012,8 @@ var Opensteer = class _Opensteer {
8723
9012
  }
8724
9013
  // --- Cookie Management ---
8725
9014
  async getCookies(url) {
8726
- if (this.remote) {
8727
- return await this.invokeRemoteAction(
9015
+ if (this.cloud) {
9016
+ return await this.invokeCloudAction(
8728
9017
  "getCookies",
8729
9018
  { url }
8730
9019
  );
@@ -8732,41 +9021,41 @@ var Opensteer = class _Opensteer {
8732
9021
  return getCookies(this.context, url);
8733
9022
  }
8734
9023
  async setCookie(cookie) {
8735
- if (this.remote) {
8736
- await this.invokeRemoteAction("setCookie", cookie);
9024
+ if (this.cloud) {
9025
+ await this.invokeCloudAction("setCookie", cookie);
8737
9026
  return;
8738
9027
  }
8739
9028
  return setCookie(this.context, cookie);
8740
9029
  }
8741
9030
  async clearCookies() {
8742
- if (this.remote) {
8743
- await this.invokeRemoteAction("clearCookies", {});
9031
+ if (this.cloud) {
9032
+ await this.invokeCloudAction("clearCookies", {});
8744
9033
  return;
8745
9034
  }
8746
9035
  return clearCookies(this.context);
8747
9036
  }
8748
9037
  async exportCookies(filePath, url) {
8749
- if (this.remote) {
8750
- throw remoteUnsupportedMethodError(
9038
+ if (this.cloud) {
9039
+ throw cloudUnsupportedMethodError(
8751
9040
  "exportCookies",
8752
- "exportCookies() is not supported in remote mode because it depends on local filesystem paths."
9041
+ "exportCookies() is not supported in cloud mode because it depends on local filesystem paths."
8753
9042
  );
8754
9043
  }
8755
9044
  return exportCookies(this.context, filePath, url);
8756
9045
  }
8757
9046
  async importCookies(filePath) {
8758
- if (this.remote) {
8759
- throw remoteUnsupportedMethodError(
9047
+ if (this.cloud) {
9048
+ throw cloudUnsupportedMethodError(
8760
9049
  "importCookies",
8761
- "importCookies() is not supported in remote mode because it depends on local filesystem paths."
9050
+ "importCookies() is not supported in cloud mode because it depends on local filesystem paths."
8762
9051
  );
8763
9052
  }
8764
9053
  return importCookies(this.context, filePath);
8765
9054
  }
8766
9055
  // --- Keyboard Input ---
8767
9056
  async pressKey(key) {
8768
- if (this.remote) {
8769
- await this.invokeRemoteActionAndResetCache("pressKey", { key });
9057
+ if (this.cloud) {
9058
+ await this.invokeCloudActionAndResetCache("pressKey", { key });
8770
9059
  return;
8771
9060
  }
8772
9061
  await this.runWithPostActionWait("pressKey", void 0, async () => {
@@ -8775,8 +9064,8 @@ var Opensteer = class _Opensteer {
8775
9064
  this.snapshotCache = null;
8776
9065
  }
8777
9066
  async type(text) {
8778
- if (this.remote) {
8779
- await this.invokeRemoteActionAndResetCache("type", { text });
9067
+ if (this.cloud) {
9068
+ await this.invokeCloudActionAndResetCache("type", { text });
8780
9069
  return;
8781
9070
  }
8782
9071
  await this.runWithPostActionWait("type", void 0, async () => {
@@ -8786,8 +9075,8 @@ var Opensteer = class _Opensteer {
8786
9075
  }
8787
9076
  // --- Element Info ---
8788
9077
  async getElementText(options) {
8789
- if (this.remote) {
8790
- return await this.invokeRemoteAction("getElementText", options);
9078
+ if (this.cloud) {
9079
+ return await this.invokeCloudAction("getElementText", options);
8791
9080
  }
8792
9081
  return this.executeElementInfoAction(
8793
9082
  "getElementText",
@@ -8800,8 +9089,8 @@ var Opensteer = class _Opensteer {
8800
9089
  );
8801
9090
  }
8802
9091
  async getElementValue(options) {
8803
- if (this.remote) {
8804
- return await this.invokeRemoteAction(
9092
+ if (this.cloud) {
9093
+ return await this.invokeCloudAction(
8805
9094
  "getElementValue",
8806
9095
  options
8807
9096
  );
@@ -8816,8 +9105,8 @@ var Opensteer = class _Opensteer {
8816
9105
  );
8817
9106
  }
8818
9107
  async getElementAttributes(options) {
8819
- if (this.remote) {
8820
- return await this.invokeRemoteAction(
9108
+ if (this.cloud) {
9109
+ return await this.invokeCloudAction(
8821
9110
  "getElementAttributes",
8822
9111
  options
8823
9112
  );
@@ -8838,8 +9127,8 @@ var Opensteer = class _Opensteer {
8838
9127
  );
8839
9128
  }
8840
9129
  async getElementBoundingBox(options) {
8841
- if (this.remote) {
8842
- return await this.invokeRemoteAction(
9130
+ if (this.cloud) {
9131
+ return await this.invokeCloudAction(
8843
9132
  "getElementBoundingBox",
8844
9133
  options
8845
9134
  );
@@ -8854,14 +9143,14 @@ var Opensteer = class _Opensteer {
8854
9143
  );
8855
9144
  }
8856
9145
  async getHtml(selector) {
8857
- if (this.remote) {
8858
- return await this.invokeRemoteAction("getHtml", { selector });
9146
+ if (this.cloud) {
9147
+ return await this.invokeCloudAction("getHtml", { selector });
8859
9148
  }
8860
9149
  return getPageHtml(this.page, selector);
8861
9150
  }
8862
9151
  async getTitle() {
8863
- if (this.remote) {
8864
- return await this.invokeRemoteAction("getTitle", {});
9152
+ if (this.cloud) {
9153
+ return await this.invokeCloudAction("getTitle", {});
8865
9154
  }
8866
9155
  return getPageTitle(this.page);
8867
9156
  }
@@ -8899,10 +9188,10 @@ var Opensteer = class _Opensteer {
8899
9188
  }
8900
9189
  // --- File Upload ---
8901
9190
  async uploadFile(options) {
8902
- if (this.remote) {
8903
- throw remoteUnsupportedMethodError(
9191
+ if (this.cloud) {
9192
+ throw cloudUnsupportedMethodError(
8904
9193
  "uploadFile",
8905
- "uploadFile() is not supported in remote mode because file paths must be accessible on the remote server."
9194
+ "uploadFile() is not supported in cloud mode because file paths must be accessible on the cloud runtime."
8906
9195
  );
8907
9196
  }
8908
9197
  const storageKey = this.resolveStorageKey(options.description);
@@ -9004,15 +9293,15 @@ var Opensteer = class _Opensteer {
9004
9293
  }
9005
9294
  // --- Wait for Text ---
9006
9295
  async waitForText(text, options) {
9007
- if (this.remote) {
9008
- await this.invokeRemoteAction("waitForText", { text, options });
9296
+ if (this.cloud) {
9297
+ await this.invokeCloudAction("waitForText", { text, options });
9009
9298
  return;
9010
9299
  }
9011
9300
  await this.page.getByText(text).first().waitFor({ timeout: options?.timeout ?? 3e4 });
9012
9301
  }
9013
9302
  async extract(options) {
9014
- if (this.remote) {
9015
- return await this.invokeRemoteAction("extract", options);
9303
+ if (this.cloud) {
9304
+ return await this.invokeCloudAction("extract", options);
9016
9305
  }
9017
9306
  const storageKey = this.resolveStorageKey(options.description);
9018
9307
  const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
@@ -9064,8 +9353,8 @@ var Opensteer = class _Opensteer {
9064
9353
  return inflateDataPathObject(data);
9065
9354
  }
9066
9355
  async extractFromPlan(options) {
9067
- if (this.remote) {
9068
- return await this.invokeRemoteAction(
9356
+ if (this.cloud) {
9357
+ return await this.invokeCloudAction(
9069
9358
  "extractFromPlan",
9070
9359
  options
9071
9360
  );
@@ -9114,10 +9403,10 @@ var Opensteer = class _Opensteer {
9114
9403
  return this.storage;
9115
9404
  }
9116
9405
  clearCache() {
9117
- if (this.remote) {
9406
+ if (this.cloud) {
9118
9407
  this.snapshotCache = null;
9119
- if (!this.remote.actionClient) return;
9120
- void this.invokeRemoteAction("clearCache", {});
9408
+ if (!this.cloud.actionClient) return;
9409
+ void this.invokeCloudAction("clearCache", {});
9121
9410
  return;
9122
9411
  }
9123
9412
  this.storage.clearNamespace();
@@ -10145,6 +10434,16 @@ function getScrollDelta2(options) {
10145
10434
  return { x: 0, y: absoluteAmount };
10146
10435
  }
10147
10436
  }
10437
+ function buildLocalRunId(namespace) {
10438
+ const normalized = namespace.trim() || "default";
10439
+ return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
10440
+ }
10441
+ function buildCloudSessionUrl(appUrl, sessionId) {
10442
+ if (!appUrl) {
10443
+ return null;
10444
+ }
10445
+ return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10446
+ }
10148
10447
 
10149
10448
  // src/ai/index.ts
10150
10449
  init_resolver();
@@ -10153,6 +10452,8 @@ init_model();
10153
10452
  // Annotate the CommonJS export names for ESM import in node:
10154
10453
  0 && (module.exports = {
10155
10454
  ActionWsClient,
10455
+ CloudCdpClient,
10456
+ CloudSessionClient,
10156
10457
  CounterResolutionError,
10157
10458
  ElementPathError,
10158
10459
  LocalSelectorStorage,
@@ -10166,9 +10467,7 @@ init_model();
10166
10467
  OS_UNAVAILABLE_ATTR,
10167
10468
  Opensteer,
10168
10469
  OpensteerActionError,
10169
- OpensteerRemoteError,
10170
- RemoteCdpClient,
10171
- RemoteSessionClient,
10470
+ OpensteerCloudError,
10172
10471
  buildElementPathFromHandle,
10173
10472
  buildElementPathFromSelector,
10174
10473
  buildPathSelectorHint,
@@ -10180,6 +10479,9 @@ init_model();
10180
10479
  clearCookies,
10181
10480
  cloneElementPath,
10182
10481
  closeTab,
10482
+ cloudNotLaunchedError,
10483
+ cloudSessionContractVersion,
10484
+ cloudUnsupportedMethodError,
10183
10485
  collectLocalSelectorCacheEntries,
10184
10486
  countArrayItemsWithPath,
10185
10487
  createEmptyRegistry,
@@ -10211,8 +10513,6 @@ init_model();
10211
10513
  performSelect,
10212
10514
  prepareSnapshot,
10213
10515
  pressKey,
10214
- remoteNotLaunchedError,
10215
- remoteUnsupportedMethodError,
10216
10516
  resolveCounterElement,
10217
10517
  resolveCountersBatch,
10218
10518
  resolveElementPath,