opensteer 0.4.6 → 0.4.7

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
 
@@ -943,28 +944,27 @@ function assertNoLegacyAiConfig(source, config) {
943
944
  );
944
945
  }
945
946
  }
946
- function assertNoLegacyModeConfig(source, config) {
947
+ function assertNoLegacyRuntimeConfig(source, config) {
947
948
  if (!config || typeof config !== "object") return;
948
949
  const configRecord = config;
949
950
  if (hasOwn(configRecord, "runtime")) {
950
951
  throw new Error(
951
- `Legacy "runtime" config is no longer supported in ${source}. Use top-level "mode" instead.`
952
+ `Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
952
953
  );
953
954
  }
954
- if (hasOwn(configRecord, "apiKey")) {
955
+ if (hasOwn(configRecord, "mode")) {
955
956
  throw new Error(
956
- `Top-level "apiKey" config is not supported in ${source}. Use "remote.apiKey" instead.`
957
+ `Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
957
958
  );
958
959
  }
959
- const remoteValue = configRecord.remote;
960
- if (typeof remoteValue === "boolean") {
960
+ if (hasOwn(configRecord, "remote")) {
961
961
  throw new Error(
962
- `Boolean "remote" config is no longer supported in ${source}. Use "mode: \\"remote\\"" with "remote" options.`
962
+ `Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
963
963
  );
964
964
  }
965
- if (remoteValue && typeof remoteValue === "object" && !Array.isArray(remoteValue) && hasOwn(remoteValue, "key")) {
965
+ if (hasOwn(configRecord, "apiKey")) {
966
966
  throw new Error(
967
- `Legacy "remote.key" config is no longer supported in ${source}. Use "remote.apiKey" instead.`
967
+ `Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
968
968
  );
969
969
  }
970
970
  }
@@ -1010,20 +1010,20 @@ function parseNumber(value) {
1010
1010
  if (!Number.isFinite(parsed)) return void 0;
1011
1011
  return parsed;
1012
1012
  }
1013
- function parseMode(value, source) {
1013
+ function parseRuntimeMode(value, source) {
1014
1014
  if (value == null) return void 0;
1015
1015
  if (typeof value !== "string") {
1016
1016
  throw new Error(
1017
- `Invalid ${source} value "${String(value)}". Use "local" or "remote".`
1017
+ `Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
1018
1018
  );
1019
1019
  }
1020
1020
  const normalized = value.trim().toLowerCase();
1021
1021
  if (!normalized) return void 0;
1022
- if (normalized === "local" || normalized === "remote") {
1022
+ if (normalized === "local" || normalized === "cloud") {
1023
1023
  return normalized;
1024
1024
  }
1025
1025
  throw new Error(
1026
- `Invalid ${source} value "${value}". Use "local" or "remote".`
1026
+ `Invalid ${source} value "${value}". Use "local" or "cloud".`
1027
1027
  );
1028
1028
  }
1029
1029
  function parseAuthScheme(value, source) {
@@ -1042,6 +1042,22 @@ function parseAuthScheme(value, source) {
1042
1042
  `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
1043
1043
  );
1044
1044
  }
1045
+ function parseCloudAnnounce(value, source) {
1046
+ if (value == null) return void 0;
1047
+ if (typeof value !== "string") {
1048
+ throw new Error(
1049
+ `Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
1050
+ );
1051
+ }
1052
+ const normalized = value.trim().toLowerCase();
1053
+ if (!normalized) return void 0;
1054
+ if (normalized === "always" || normalized === "off" || normalized === "tty") {
1055
+ return normalized;
1056
+ }
1057
+ throw new Error(
1058
+ `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
1059
+ );
1060
+ }
1045
1061
  function resolveOpensteerApiKey() {
1046
1062
  const value = process.env.OPENSTEER_API_KEY?.trim();
1047
1063
  if (!value) return void 0;
@@ -1053,29 +1069,40 @@ function resolveOpensteerAuthScheme() {
1053
1069
  "OPENSTEER_AUTH_SCHEME"
1054
1070
  );
1055
1071
  }
1056
- function normalizeRemoteOptions(value) {
1072
+ function normalizeCloudOptions(value) {
1057
1073
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1058
1074
  return void 0;
1059
1075
  }
1060
1076
  return value;
1061
1077
  }
1062
- function resolveModeSelection(config) {
1063
- const configMode = parseMode(config.mode, "mode");
1064
- if (configMode) {
1078
+ function parseCloudEnabled(value, source) {
1079
+ if (value == null) return void 0;
1080
+ if (typeof value === "boolean") return value;
1081
+ if (typeof value === "object" && !Array.isArray(value)) return true;
1082
+ throw new Error(
1083
+ `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
1084
+ );
1085
+ }
1086
+ function resolveCloudSelection(config) {
1087
+ const configCloud = parseCloudEnabled(config.cloud, "cloud");
1088
+ if (configCloud !== void 0) {
1065
1089
  return {
1066
- mode: configMode,
1067
- source: "config.mode"
1090
+ cloud: configCloud,
1091
+ source: "config.cloud"
1068
1092
  };
1069
1093
  }
1070
- const envMode = parseMode(process.env.OPENSTEER_MODE, "OPENSTEER_MODE");
1094
+ const envMode = parseRuntimeMode(
1095
+ process.env.OPENSTEER_MODE,
1096
+ "OPENSTEER_MODE"
1097
+ );
1071
1098
  if (envMode) {
1072
1099
  return {
1073
- mode: envMode,
1100
+ cloud: envMode === "cloud",
1074
1101
  source: "env.OPENSTEER_MODE"
1075
1102
  };
1076
1103
  }
1077
1104
  return {
1078
- mode: "local",
1105
+ cloud: false,
1079
1106
  source: "default"
1080
1107
  };
1081
1108
  }
@@ -1091,11 +1118,11 @@ function resolveConfig(input = {}) {
1091
1118
  );
1092
1119
  }
1093
1120
  assertNoLegacyAiConfig("Opensteer constructor config", input);
1094
- assertNoLegacyModeConfig("Opensteer constructor config", input);
1121
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1095
1122
  const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
1096
1123
  const fileConfig = loadConfigFile(rootDir);
1097
1124
  assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1098
- assertNoLegacyModeConfig(".opensteer/config.json", fileConfig);
1125
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1099
1126
  const envConfig = {
1100
1127
  browser: {
1101
1128
  headless: parseBool(process.env.OPENSTEER_HEADLESS),
@@ -1113,28 +1140,38 @@ function resolveConfig(input = {}) {
1113
1140
  const resolved = mergeDeep(mergedWithEnv, input);
1114
1141
  const envApiKey = resolveOpensteerApiKey();
1115
1142
  const envAuthScheme = resolveOpensteerAuthScheme();
1116
- const inputRemoteOptions = normalizeRemoteOptions(input.remote);
1143
+ const envCloudAnnounce = parseCloudAnnounce(
1144
+ process.env.OPENSTEER_REMOTE_ANNOUNCE,
1145
+ "OPENSTEER_REMOTE_ANNOUNCE"
1146
+ );
1147
+ const inputCloudOptions = normalizeCloudOptions(input.cloud);
1117
1148
  const inputAuthScheme = parseAuthScheme(
1118
- inputRemoteOptions?.authScheme,
1119
- "remote.authScheme"
1149
+ inputCloudOptions?.authScheme,
1150
+ "cloud.authScheme"
1120
1151
  );
1121
- const inputHasRemoteApiKey = Boolean(
1122
- inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
1152
+ const inputCloudAnnounce = parseCloudAnnounce(
1153
+ inputCloudOptions?.announce,
1154
+ "cloud.announce"
1123
1155
  );
1124
- const modeSelection = resolveModeSelection({
1125
- mode: resolved.mode
1156
+ const inputHasCloudApiKey = Boolean(
1157
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
1158
+ );
1159
+ const cloudSelection = resolveCloudSelection({
1160
+ cloud: resolved.cloud
1126
1161
  });
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
1162
+ if (cloudSelection.cloud) {
1163
+ const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
1164
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
1165
+ const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
1166
+ resolved.cloud = {
1167
+ ...resolvedCloud,
1168
+ authScheme,
1169
+ announce
1133
1170
  };
1134
1171
  }
1135
- if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
1136
- resolved.remote = {
1137
- ...normalizeRemoteOptions(resolved.remote) ?? {},
1172
+ if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
1173
+ resolved.cloud = {
1174
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
1138
1175
  apiKey: envApiKey
1139
1176
  };
1140
1177
  }
@@ -7441,36 +7478,39 @@ function clonePersistedExtractNode(node) {
7441
7478
  return JSON.parse(JSON.stringify(node));
7442
7479
  }
7443
7480
 
7444
- // src/remote/action-ws-client.ts
7481
+ // src/cloud/contracts.ts
7482
+ var cloudSessionContractVersion = "v3";
7483
+
7484
+ // src/cloud/action-ws-client.ts
7445
7485
  var import_ws2 = __toESM(require("ws"), 1);
7446
7486
 
7447
- // src/remote/errors.ts
7448
- var OpensteerRemoteError = class extends Error {
7487
+ // src/cloud/errors.ts
7488
+ var OpensteerCloudError = class extends Error {
7449
7489
  code;
7450
7490
  status;
7451
7491
  details;
7452
7492
  constructor(code, message, status, details) {
7453
7493
  super(message);
7454
- this.name = "OpensteerRemoteError";
7494
+ this.name = "OpensteerCloudError";
7455
7495
  this.code = code;
7456
7496
  this.status = status;
7457
7497
  this.details = details;
7458
7498
  }
7459
7499
  };
7460
- function remoteUnsupportedMethodError(method, message) {
7461
- return new OpensteerRemoteError(
7462
- "REMOTE_UNSUPPORTED_METHOD",
7463
- message || `${method} is not supported in remote mode.`
7500
+ function cloudUnsupportedMethodError(method, message) {
7501
+ return new OpensteerCloudError(
7502
+ "CLOUD_UNSUPPORTED_METHOD",
7503
+ message || `${method} is not supported in cloud mode.`
7464
7504
  );
7465
7505
  }
7466
- function remoteNotLaunchedError() {
7467
- return new OpensteerRemoteError(
7468
- "REMOTE_SESSION_NOT_FOUND",
7469
- "Remote session is not connected. Call launch() first."
7506
+ function cloudNotLaunchedError() {
7507
+ return new OpensteerCloudError(
7508
+ "CLOUD_SESSION_NOT_FOUND",
7509
+ "Cloud session is not connected. Call launch() first."
7470
7510
  );
7471
7511
  }
7472
7512
 
7473
- // src/remote/action-ws-client.ts
7513
+ // src/cloud/action-ws-client.ts
7474
7514
  var ActionWsClient = class _ActionWsClient {
7475
7515
  ws;
7476
7516
  sessionId;
@@ -7487,18 +7527,18 @@ var ActionWsClient = class _ActionWsClient {
7487
7527
  });
7488
7528
  ws.on("error", (error) => {
7489
7529
  this.rejectAll(
7490
- new OpensteerRemoteError(
7491
- "REMOTE_TRANSPORT_ERROR",
7492
- `Remote action websocket error: ${error.message}`
7530
+ new OpensteerCloudError(
7531
+ "CLOUD_TRANSPORT_ERROR",
7532
+ `Cloud action websocket error: ${error.message}`
7493
7533
  )
7494
7534
  );
7495
7535
  });
7496
7536
  ws.on("close", () => {
7497
7537
  this.closed = true;
7498
7538
  this.rejectAll(
7499
- new OpensteerRemoteError(
7500
- "REMOTE_SESSION_CLOSED",
7501
- "Remote action websocket closed."
7539
+ new OpensteerCloudError(
7540
+ "CLOUD_SESSION_CLOSED",
7541
+ "Cloud action websocket closed."
7502
7542
  )
7503
7543
  );
7504
7544
  });
@@ -7510,8 +7550,8 @@ var ActionWsClient = class _ActionWsClient {
7510
7550
  ws.once("open", () => resolve());
7511
7551
  ws.once("error", (error) => {
7512
7552
  reject(
7513
- new OpensteerRemoteError(
7514
- "REMOTE_TRANSPORT_ERROR",
7553
+ new OpensteerCloudError(
7554
+ "CLOUD_TRANSPORT_ERROR",
7515
7555
  `Failed to connect action websocket: ${error.message}`
7516
7556
  )
7517
7557
  );
@@ -7521,9 +7561,9 @@ var ActionWsClient = class _ActionWsClient {
7521
7561
  }
7522
7562
  async request(method, args) {
7523
7563
  if (this.closed || this.ws.readyState !== import_ws2.default.OPEN) {
7524
- throw new OpensteerRemoteError(
7525
- "REMOTE_SESSION_CLOSED",
7526
- "Remote action websocket is closed."
7564
+ throw new OpensteerCloudError(
7565
+ "CLOUD_SESSION_CLOSED",
7566
+ "Cloud action websocket is closed."
7527
7567
  );
7528
7568
  }
7529
7569
  const id = this.nextRequestId;
@@ -7542,8 +7582,8 @@ var ActionWsClient = class _ActionWsClient {
7542
7582
  this.ws.send(JSON.stringify(payload));
7543
7583
  } catch (error) {
7544
7584
  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);
7585
+ const message = error instanceof Error ? error.message : "Failed to send cloud action request.";
7586
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7547
7587
  }
7548
7588
  return await resultPromise;
7549
7589
  }
@@ -7561,9 +7601,9 @@ var ActionWsClient = class _ActionWsClient {
7561
7601
  parsed = JSON.parse(rawDataToUtf8(raw));
7562
7602
  } catch {
7563
7603
  this.rejectAll(
7564
- new OpensteerRemoteError(
7565
- "REMOTE_TRANSPORT_ERROR",
7566
- "Invalid remote action response payload."
7604
+ new OpensteerCloudError(
7605
+ "CLOUD_TRANSPORT_ERROR",
7606
+ "Invalid cloud action response payload."
7567
7607
  )
7568
7608
  );
7569
7609
  return;
@@ -7576,7 +7616,7 @@ var ActionWsClient = class _ActionWsClient {
7576
7616
  return;
7577
7617
  }
7578
7618
  pending.reject(
7579
- new OpensteerRemoteError(
7619
+ new OpensteerCloudError(
7580
7620
  parsed.code,
7581
7621
  parsed.error,
7582
7622
  void 0,
@@ -7604,7 +7644,7 @@ function withTokenQuery(wsUrl, token) {
7604
7644
  return url.toString();
7605
7645
  }
7606
7646
 
7607
- // src/remote/local-cache-sync.ts
7647
+ // src/cloud/local-cache-sync.ts
7608
7648
  var import_fs3 = __toESM(require("fs"), 1);
7609
7649
  var import_path5 = __toESM(require("path"), 1);
7610
7650
  function collectLocalSelectorCacheEntries(storage) {
@@ -7728,24 +7768,24 @@ function dedupeNewest(entries) {
7728
7768
  return [...byKey.values()];
7729
7769
  }
7730
7770
 
7731
- // src/remote/cdp-client.ts
7771
+ // src/cloud/cdp-client.ts
7732
7772
  var import_playwright2 = require("playwright");
7733
- var RemoteCdpClient = class {
7773
+ var CloudCdpClient = class {
7734
7774
  async connect(args) {
7735
7775
  const endpoint = withTokenQuery2(args.wsUrl, args.token);
7736
7776
  let browser;
7737
7777
  try {
7738
7778
  browser = await import_playwright2.chromium.connectOverCDP(endpoint);
7739
7779
  } 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);
7780
+ const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
7781
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
7742
7782
  }
7743
7783
  const context = browser.contexts()[0];
7744
7784
  if (!context) {
7745
7785
  await browser.close();
7746
- throw new OpensteerRemoteError(
7747
- "REMOTE_INTERNAL",
7748
- "Remote browser returned no context."
7786
+ throw new OpensteerCloudError(
7787
+ "CLOUD_INTERNAL",
7788
+ "Cloud browser returned no context."
7749
7789
  );
7750
7790
  }
7751
7791
  const page = context.pages()[0] || await context.newPage();
@@ -7758,9 +7798,9 @@ function withTokenQuery2(wsUrl, token) {
7758
7798
  return url.toString();
7759
7799
  }
7760
7800
 
7761
- // src/remote/session-client.ts
7801
+ // src/cloud/session-client.ts
7762
7802
  var CACHE_IMPORT_BATCH_SIZE = 200;
7763
- var RemoteSessionClient = class {
7803
+ var CloudSessionClient = class {
7764
7804
  baseUrl;
7765
7805
  key;
7766
7806
  authScheme;
@@ -7781,7 +7821,17 @@ var RemoteSessionClient = class {
7781
7821
  if (!response.ok) {
7782
7822
  throw await parseHttpError(response);
7783
7823
  }
7784
- return await response.json();
7824
+ let body;
7825
+ try {
7826
+ body = await response.json();
7827
+ } catch {
7828
+ throw new OpensteerCloudError(
7829
+ "CLOUD_CONTRACT_MISMATCH",
7830
+ "Invalid cloud session create response: expected a JSON object.",
7831
+ response.status
7832
+ );
7833
+ }
7834
+ return parseCreateResponse(body, response.status);
7785
7835
  }
7786
7836
  async close(sessionId) {
7787
7837
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
@@ -7840,6 +7890,134 @@ var RemoteSessionClient = class {
7840
7890
  function normalizeBaseUrl(baseUrl) {
7841
7891
  return baseUrl.replace(/\/+$/, "");
7842
7892
  }
7893
+ function parseCreateResponse(body, status) {
7894
+ const root = requireObject(
7895
+ body,
7896
+ "Invalid cloud session create response: expected a JSON object.",
7897
+ status
7898
+ );
7899
+ const sessionId = requireString(root, "sessionId", status);
7900
+ const actionWsUrl = requireString(root, "actionWsUrl", status);
7901
+ const cdpWsUrl = requireString(root, "cdpWsUrl", status);
7902
+ const actionToken = requireString(root, "actionToken", status);
7903
+ const cdpToken = requireString(root, "cdpToken", status);
7904
+ const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
7905
+ const cloudSessionRoot = requireObject(
7906
+ root.cloudSession,
7907
+ "Invalid cloud session create response: cloudSession must be an object.",
7908
+ status
7909
+ );
7910
+ const cloudSession = {
7911
+ sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
7912
+ workspaceId: requireString(
7913
+ cloudSessionRoot,
7914
+ "workspaceId",
7915
+ status,
7916
+ "cloudSession"
7917
+ ),
7918
+ state: requireString(cloudSessionRoot, "state", status, "cloudSession"),
7919
+ createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
7920
+ sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
7921
+ sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
7922
+ label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
7923
+ };
7924
+ const expiresAt = optionalNumber(root, "expiresAt", status);
7925
+ return {
7926
+ sessionId,
7927
+ actionWsUrl,
7928
+ cdpWsUrl,
7929
+ actionToken,
7930
+ cdpToken,
7931
+ expiresAt,
7932
+ cloudSessionUrl,
7933
+ cloudSession
7934
+ };
7935
+ }
7936
+ function requireObject(value, message, status) {
7937
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
7938
+ throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
7939
+ }
7940
+ return value;
7941
+ }
7942
+ function requireString(source, field, status, parent) {
7943
+ const value = source[field];
7944
+ if (typeof value !== "string" || !value.trim()) {
7945
+ throw new OpensteerCloudError(
7946
+ "CLOUD_CONTRACT_MISMATCH",
7947
+ `Invalid cloud session create response: ${formatFieldPath(
7948
+ field,
7949
+ parent
7950
+ )} must be a non-empty string.`,
7951
+ status
7952
+ );
7953
+ }
7954
+ return value;
7955
+ }
7956
+ function requireNumber(source, field, status, parent) {
7957
+ const value = source[field];
7958
+ if (typeof value !== "number" || !Number.isFinite(value)) {
7959
+ throw new OpensteerCloudError(
7960
+ "CLOUD_CONTRACT_MISMATCH",
7961
+ `Invalid cloud session create response: ${formatFieldPath(
7962
+ field,
7963
+ parent
7964
+ )} must be a finite number.`,
7965
+ status
7966
+ );
7967
+ }
7968
+ return value;
7969
+ }
7970
+ function optionalString(source, field, status, parent) {
7971
+ const value = source[field];
7972
+ if (value == null) {
7973
+ return void 0;
7974
+ }
7975
+ if (typeof value !== "string") {
7976
+ throw new OpensteerCloudError(
7977
+ "CLOUD_CONTRACT_MISMATCH",
7978
+ `Invalid cloud session create response: ${formatFieldPath(
7979
+ field,
7980
+ parent
7981
+ )} must be a string when present.`,
7982
+ status
7983
+ );
7984
+ }
7985
+ return value;
7986
+ }
7987
+ function optionalNumber(source, field, status, parent) {
7988
+ const value = source[field];
7989
+ if (value == null) {
7990
+ return void 0;
7991
+ }
7992
+ if (typeof value !== "number" || !Number.isFinite(value)) {
7993
+ throw new OpensteerCloudError(
7994
+ "CLOUD_CONTRACT_MISMATCH",
7995
+ `Invalid cloud session create response: ${formatFieldPath(
7996
+ field,
7997
+ parent
7998
+ )} must be a finite number when present.`,
7999
+ status
8000
+ );
8001
+ }
8002
+ return value;
8003
+ }
8004
+ function requireSourceType(source, field, status, parent) {
8005
+ const value = source[field];
8006
+ if (value === "agent-thread" || value === "agent-run" || value === "local-cloud" || value === "manual") {
8007
+ return value;
8008
+ }
8009
+ throw new OpensteerCloudError(
8010
+ "CLOUD_CONTRACT_MISMATCH",
8011
+ `Invalid cloud session create response: ${formatFieldPath(
8012
+ field,
8013
+ parent
8014
+ )} must be one of "agent-thread", "agent-run", "local-cloud", or "manual".`,
8015
+ status
8016
+ );
8017
+ }
8018
+ function formatFieldPath(field, parent) {
8019
+ return parent ? `"${parent}.${field}"` : `"${field}"`;
8020
+ }
7843
8021
  function zeroImportResponse() {
7844
8022
  return {
7845
8023
  imported: 0,
@@ -7863,33 +8041,46 @@ async function parseHttpError(response) {
7863
8041
  } catch {
7864
8042
  body = null;
7865
8043
  }
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);
8044
+ const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
8045
+ const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
8046
+ return new OpensteerCloudError(code, message, response.status, body?.details);
7869
8047
  }
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") {
8048
+ function toCloudErrorCode(code) {
8049
+ 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
8050
  return code;
7873
8051
  }
7874
- return "REMOTE_TRANSPORT_ERROR";
8052
+ return "CLOUD_TRANSPORT_ERROR";
7875
8053
  }
7876
8054
 
7877
- // src/remote/runtime.ts
7878
- var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7879
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
8055
+ // src/cloud/runtime.ts
8056
+ var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
8057
+ var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
8058
+ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
7880
8059
  return {
7881
- sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7882
- cdpClient: new RemoteCdpClient(),
8060
+ sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
8061
+ cdpClient: new CloudCdpClient(),
8062
+ appUrl: normalizeCloudAppUrl(appUrl),
7883
8063
  actionClient: null,
7884
- sessionId: null
8064
+ sessionId: null,
8065
+ localRunId: null,
8066
+ cloudSessionUrl: null
7885
8067
  };
7886
8068
  }
7887
- function resolveRemoteBaseUrl() {
8069
+ function resolveCloudBaseUrl() {
7888
8070
  const value = process.env.OPENSTEER_BASE_URL?.trim();
7889
- if (!value) return DEFAULT_REMOTE_BASE_URL;
8071
+ if (!value) return DEFAULT_CLOUD_BASE_URL;
7890
8072
  return value.replace(/\/+$/, "");
7891
8073
  }
7892
- function readRemoteActionDescription(payload) {
8074
+ function resolveCloudAppUrl() {
8075
+ const value = process.env.OPENSTEER_APP_URL?.trim();
8076
+ if (!value) return DEFAULT_CLOUD_APP_URL;
8077
+ return normalizeCloudAppUrl(value);
8078
+ }
8079
+ function normalizeCloudAppUrl(value) {
8080
+ if (!value) return null;
8081
+ return value.replace(/\/+$/, "");
8082
+ }
8083
+ function readCloudActionDescription(payload) {
7893
8084
  const description = payload.description;
7894
8085
  if (typeof description !== "string") return void 0;
7895
8086
  const normalized = description.trim();
@@ -7897,7 +8088,7 @@ function readRemoteActionDescription(payload) {
7897
8088
  }
7898
8089
 
7899
8090
  // src/opensteer.ts
7900
- var REMOTE_INTERACTION_METHODS = /* @__PURE__ */ new Set([
8091
+ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
7901
8092
  "click",
7902
8093
  "dblclick",
7903
8094
  "rightclick",
@@ -7914,7 +8105,7 @@ var Opensteer = class _Opensteer {
7914
8105
  namespace;
7915
8106
  storage;
7916
8107
  pool;
7917
- remote;
8108
+ cloud;
7918
8109
  browser = null;
7919
8110
  pageRef = null;
7920
8111
  contextRef = null;
@@ -7922,8 +8113,8 @@ var Opensteer = class _Opensteer {
7922
8113
  snapshotCache = null;
7923
8114
  constructor(config = {}) {
7924
8115
  const resolved = resolveConfig(config);
7925
- const modeSelection = resolveModeSelection({
7926
- mode: resolved.mode
8116
+ const cloudSelection = resolveCloudSelection({
8117
+ cloud: resolved.cloud
7927
8118
  });
7928
8119
  const model = resolved.model;
7929
8120
  this.config = resolved;
@@ -7933,21 +8124,22 @@ var Opensteer = class _Opensteer {
7933
8124
  this.namespace = resolveNamespace(resolved, rootDir);
7934
8125
  this.storage = new LocalSelectorStorage(rootDir, this.namespace);
7935
8126
  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();
8127
+ if (cloudSelection.cloud) {
8128
+ const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
8129
+ const apiKey = cloudConfig?.apiKey?.trim();
7939
8130
  if (!apiKey) {
7940
8131
  throw new Error(
7941
- "Remote mode requires a non-empty API key via remote.apiKey or OPENSTEER_API_KEY."
8132
+ "Cloud mode requires a non-empty API key via cloud.apiKey or OPENSTEER_API_KEY."
7942
8133
  );
7943
8134
  }
7944
- this.remote = createRemoteRuntimeState(
8135
+ this.cloud = createCloudRuntimeState(
7945
8136
  apiKey,
7946
- remoteConfig?.baseUrl,
7947
- remoteConfig?.authScheme
8137
+ cloudConfig?.baseUrl,
8138
+ cloudConfig?.authScheme,
8139
+ cloudConfig?.appUrl
7948
8140
  );
7949
8141
  } else {
7950
- this.remote = null;
8142
+ this.cloud = null;
7951
8143
  }
7952
8144
  }
7953
8145
  createLazyResolveCallback(model) {
@@ -7985,32 +8177,32 @@ var Opensteer = class _Opensteer {
7985
8177
  };
7986
8178
  return extract;
7987
8179
  }
7988
- async invokeRemoteActionAndResetCache(method, args) {
7989
- const result = await this.invokeRemoteAction(method, args);
8180
+ async invokeCloudActionAndResetCache(method, args) {
8181
+ const result = await this.invokeCloudAction(method, args);
7990
8182
  this.snapshotCache = null;
7991
8183
  return result;
7992
8184
  }
7993
- async invokeRemoteAction(method, args) {
7994
- const actionClient = this.remote?.actionClient;
7995
- const sessionId = this.remote?.sessionId;
8185
+ async invokeCloudAction(method, args) {
8186
+ const actionClient = this.cloud?.actionClient;
8187
+ const sessionId = this.cloud?.sessionId;
7996
8188
  if (!actionClient || !sessionId) {
7997
- throw remoteNotLaunchedError();
8189
+ throw cloudNotLaunchedError();
7998
8190
  }
7999
8191
  const payload = args && typeof args === "object" ? args : {};
8000
8192
  try {
8001
8193
  return await actionClient.request(method, payload);
8002
8194
  } catch (err) {
8003
- if (err instanceof OpensteerRemoteError && err.code === "REMOTE_ACTION_FAILED" && REMOTE_INTERACTION_METHODS.has(method)) {
8195
+ if (err instanceof OpensteerCloudError && err.code === "CLOUD_ACTION_FAILED" && CLOUD_INTERACTION_METHODS.has(method)) {
8004
8196
  const detailsRecord = err.details && typeof err.details === "object" ? err.details : null;
8005
- const remoteFailure = normalizeActionFailure(
8197
+ const cloudFailure = normalizeActionFailure(
8006
8198
  detailsRecord?.actionFailure
8007
8199
  );
8008
- const failure = remoteFailure || classifyActionFailure({
8200
+ const failure = cloudFailure || classifyActionFailure({
8009
8201
  action: method,
8010
8202
  error: err,
8011
8203
  fallbackMessage: defaultActionFailureMessage(method)
8012
8204
  });
8013
- const description = readRemoteActionDescription(payload);
8205
+ const description = readCloudActionDescription(payload);
8014
8206
  throw this.buildActionError(
8015
8207
  method,
8016
8208
  description,
@@ -8051,8 +8243,36 @@ var Opensteer = class _Opensteer {
8051
8243
  }
8052
8244
  return this.contextRef;
8053
8245
  }
8054
- getRemoteSessionId() {
8055
- return this.remote?.sessionId ?? null;
8246
+ getCloudSessionId() {
8247
+ return this.cloud?.sessionId ?? null;
8248
+ }
8249
+ getCloudSessionUrl() {
8250
+ return this.cloud?.cloudSessionUrl ?? null;
8251
+ }
8252
+ announceCloudSession(args) {
8253
+ if (!this.shouldAnnounceCloudSession()) {
8254
+ return;
8255
+ }
8256
+ const fields = [
8257
+ `sessionId=${args.sessionId}`,
8258
+ `workspaceId=${args.workspaceId}`
8259
+ ];
8260
+ if (args.cloudSessionUrl) {
8261
+ fields.push(`url=${args.cloudSessionUrl}`);
8262
+ }
8263
+ process.stderr.write(`[opensteer] cloud session ready ${fields.join(" ")}
8264
+ `);
8265
+ }
8266
+ shouldAnnounceCloudSession() {
8267
+ const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : null;
8268
+ const announce = cloudConfig?.announce ?? "always";
8269
+ if (announce === "off") {
8270
+ return false;
8271
+ }
8272
+ if (announce === "tty") {
8273
+ return Boolean(process.stderr.isTTY);
8274
+ }
8275
+ return true;
8056
8276
  }
8057
8277
  async launch(options = {}) {
8058
8278
  if (this.pageRef && !this.ownsBrowser) {
@@ -8063,22 +8283,29 @@ var Opensteer = class _Opensteer {
8063
8283
  if (this.pageRef && this.ownsBrowser) {
8064
8284
  return;
8065
8285
  }
8066
- if (this.remote) {
8286
+ if (this.cloud) {
8067
8287
  let actionClient = null;
8068
8288
  let browser = null;
8069
8289
  let sessionId = null;
8290
+ let localRunId = null;
8070
8291
  try {
8071
8292
  try {
8072
- await this.syncLocalSelectorCacheToRemote();
8293
+ await this.syncLocalSelectorCacheToCloud();
8073
8294
  } catch (error) {
8074
8295
  if (this.config.debug) {
8075
8296
  const message = error instanceof Error ? error.message : String(error);
8076
8297
  console.warn(
8077
- `[opensteer] remote selector cache sync failed: ${message}`
8298
+ `[opensteer] cloud selector cache sync failed: ${message}`
8078
8299
  );
8079
8300
  }
8080
8301
  }
8081
- const session2 = await this.remote.sessionClient.create({
8302
+ localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
8303
+ this.cloud.localRunId = localRunId;
8304
+ const session2 = await this.cloud.sessionClient.create({
8305
+ cloudSessionContractVersion,
8306
+ sourceType: "local-cloud",
8307
+ clientSessionHint: this.namespace,
8308
+ localRunId,
8082
8309
  name: this.namespace,
8083
8310
  model: this.config.model,
8084
8311
  launchContext: options.context || void 0
@@ -8089,7 +8316,7 @@ var Opensteer = class _Opensteer {
8089
8316
  token: session2.actionToken,
8090
8317
  sessionId: session2.sessionId
8091
8318
  });
8092
- const cdpConnection = await this.remote.cdpClient.connect({
8319
+ const cdpConnection = await this.cloud.cdpClient.connect({
8093
8320
  wsUrl: session2.cdpWsUrl,
8094
8321
  token: session2.cdpToken
8095
8322
  });
@@ -8099,8 +8326,17 @@ var Opensteer = class _Opensteer {
8099
8326
  this.pageRef = cdpConnection.page;
8100
8327
  this.ownsBrowser = true;
8101
8328
  this.snapshotCache = null;
8102
- this.remote.actionClient = actionClient;
8103
- this.remote.sessionId = sessionId;
8329
+ this.cloud.actionClient = actionClient;
8330
+ this.cloud.sessionId = sessionId;
8331
+ this.cloud.cloudSessionUrl = buildCloudSessionUrl(
8332
+ this.cloud.appUrl,
8333
+ session2.cloudSession.sessionId
8334
+ );
8335
+ this.announceCloudSession({
8336
+ sessionId: session2.sessionId,
8337
+ workspaceId: session2.cloudSession.workspaceId,
8338
+ cloudSessionUrl: this.cloud.cloudSessionUrl
8339
+ });
8104
8340
  return;
8105
8341
  } catch (error) {
8106
8342
  if (actionClient) {
@@ -8110,8 +8346,9 @@ var Opensteer = class _Opensteer {
8110
8346
  await browser.close().catch(() => void 0);
8111
8347
  }
8112
8348
  if (sessionId) {
8113
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8349
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8114
8350
  }
8351
+ this.cloud.cloudSessionUrl = null;
8115
8352
  throw error;
8116
8353
  }
8117
8354
  }
@@ -8129,13 +8366,13 @@ var Opensteer = class _Opensteer {
8129
8366
  }
8130
8367
  static from(page, config = {}) {
8131
8368
  const resolvedConfig = resolveConfig(config);
8132
- const modeSelection = resolveModeSelection({
8133
- mode: resolvedConfig.mode
8369
+ const cloudSelection = resolveCloudSelection({
8370
+ cloud: resolvedConfig.cloud
8134
8371
  });
8135
- if (modeSelection.mode === "remote") {
8136
- throw remoteUnsupportedMethodError(
8372
+ if (cloudSelection.cloud) {
8373
+ throw cloudUnsupportedMethodError(
8137
8374
  "Opensteer.from(page)",
8138
- "Opensteer.from(page) is not supported in remote mode."
8375
+ "Opensteer.from(page) is not supported in cloud mode."
8139
8376
  );
8140
8377
  }
8141
8378
  const instance = new _Opensteer(config);
@@ -8148,12 +8385,14 @@ var Opensteer = class _Opensteer {
8148
8385
  }
8149
8386
  async close() {
8150
8387
  this.snapshotCache = null;
8151
- if (this.remote) {
8152
- const actionClient = this.remote.actionClient;
8153
- const sessionId = this.remote.sessionId;
8388
+ if (this.cloud) {
8389
+ const actionClient = this.cloud.actionClient;
8390
+ const sessionId = this.cloud.sessionId;
8154
8391
  const browser = this.browser;
8155
- this.remote.actionClient = null;
8156
- this.remote.sessionId = null;
8392
+ this.cloud.actionClient = null;
8393
+ this.cloud.sessionId = null;
8394
+ this.cloud.localRunId = null;
8395
+ this.cloud.cloudSessionUrl = null;
8157
8396
  this.browser = null;
8158
8397
  this.pageRef = null;
8159
8398
  this.contextRef = null;
@@ -8165,7 +8404,7 @@ var Opensteer = class _Opensteer {
8165
8404
  await browser.close().catch(() => void 0);
8166
8405
  }
8167
8406
  if (sessionId) {
8168
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8407
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
8169
8408
  }
8170
8409
  return;
8171
8410
  }
@@ -8177,17 +8416,17 @@ var Opensteer = class _Opensteer {
8177
8416
  this.contextRef = null;
8178
8417
  this.ownsBrowser = false;
8179
8418
  }
8180
- async syncLocalSelectorCacheToRemote() {
8181
- if (!this.remote) return;
8419
+ async syncLocalSelectorCacheToCloud() {
8420
+ if (!this.cloud) return;
8182
8421
  const entries = collectLocalSelectorCacheEntries(this.storage);
8183
8422
  if (!entries.length) return;
8184
- await this.remote.sessionClient.importSelectorCache({
8423
+ await this.cloud.sessionClient.importSelectorCache({
8185
8424
  entries
8186
8425
  });
8187
8426
  }
8188
8427
  async goto(url, options) {
8189
- if (this.remote) {
8190
- await this.invokeRemoteActionAndResetCache("goto", { url, options });
8428
+ if (this.cloud) {
8429
+ await this.invokeCloudActionAndResetCache("goto", { url, options });
8191
8430
  return;
8192
8431
  }
8193
8432
  const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
@@ -8196,8 +8435,8 @@ var Opensteer = class _Opensteer {
8196
8435
  this.snapshotCache = null;
8197
8436
  }
8198
8437
  async snapshot(options = {}) {
8199
- if (this.remote) {
8200
- return await this.invokeRemoteActionAndResetCache("snapshot", {
8438
+ if (this.cloud) {
8439
+ return await this.invokeCloudActionAndResetCache("snapshot", {
8201
8440
  options
8202
8441
  });
8203
8442
  }
@@ -8206,8 +8445,8 @@ var Opensteer = class _Opensteer {
8206
8445
  return prepared.cleanedHtml;
8207
8446
  }
8208
8447
  async state() {
8209
- if (this.remote) {
8210
- return await this.invokeRemoteAction("state", {});
8448
+ if (this.cloud) {
8449
+ return await this.invokeCloudAction("state", {});
8211
8450
  }
8212
8451
  const html = await this.snapshot({ mode: "action" });
8213
8452
  return {
@@ -8217,8 +8456,8 @@ var Opensteer = class _Opensteer {
8217
8456
  };
8218
8457
  }
8219
8458
  async screenshot(options = {}) {
8220
- if (this.remote) {
8221
- const b64 = await this.invokeRemoteAction(
8459
+ if (this.cloud) {
8460
+ const b64 = await this.invokeCloudAction(
8222
8461
  "screenshot",
8223
8462
  options
8224
8463
  );
@@ -8232,8 +8471,8 @@ var Opensteer = class _Opensteer {
8232
8471
  });
8233
8472
  }
8234
8473
  async click(options) {
8235
- if (this.remote) {
8236
- return await this.invokeRemoteActionAndResetCache(
8474
+ if (this.cloud) {
8475
+ return await this.invokeCloudActionAndResetCache(
8237
8476
  "click",
8238
8477
  options
8239
8478
  );
@@ -8245,8 +8484,8 @@ var Opensteer = class _Opensteer {
8245
8484
  });
8246
8485
  }
8247
8486
  async dblclick(options) {
8248
- if (this.remote) {
8249
- return await this.invokeRemoteActionAndResetCache(
8487
+ if (this.cloud) {
8488
+ return await this.invokeCloudActionAndResetCache(
8250
8489
  "dblclick",
8251
8490
  options
8252
8491
  );
@@ -8258,8 +8497,8 @@ var Opensteer = class _Opensteer {
8258
8497
  });
8259
8498
  }
8260
8499
  async rightclick(options) {
8261
- if (this.remote) {
8262
- return await this.invokeRemoteActionAndResetCache(
8500
+ if (this.cloud) {
8501
+ return await this.invokeCloudActionAndResetCache(
8263
8502
  "rightclick",
8264
8503
  options
8265
8504
  );
@@ -8271,8 +8510,8 @@ var Opensteer = class _Opensteer {
8271
8510
  });
8272
8511
  }
8273
8512
  async hover(options) {
8274
- if (this.remote) {
8275
- return await this.invokeRemoteActionAndResetCache(
8513
+ if (this.cloud) {
8514
+ return await this.invokeCloudActionAndResetCache(
8276
8515
  "hover",
8277
8516
  options
8278
8517
  );
@@ -8370,8 +8609,8 @@ var Opensteer = class _Opensteer {
8370
8609
  );
8371
8610
  }
8372
8611
  async input(options) {
8373
- if (this.remote) {
8374
- return await this.invokeRemoteActionAndResetCache(
8612
+ if (this.cloud) {
8613
+ return await this.invokeCloudActionAndResetCache(
8375
8614
  "input",
8376
8615
  options
8377
8616
  );
@@ -8473,8 +8712,8 @@ var Opensteer = class _Opensteer {
8473
8712
  );
8474
8713
  }
8475
8714
  async select(options) {
8476
- if (this.remote) {
8477
- return await this.invokeRemoteActionAndResetCache(
8715
+ if (this.cloud) {
8716
+ return await this.invokeCloudActionAndResetCache(
8478
8717
  "select",
8479
8718
  options
8480
8719
  );
@@ -8583,8 +8822,8 @@ var Opensteer = class _Opensteer {
8583
8822
  );
8584
8823
  }
8585
8824
  async scroll(options = {}) {
8586
- if (this.remote) {
8587
- return await this.invokeRemoteActionAndResetCache(
8825
+ if (this.cloud) {
8826
+ return await this.invokeCloudActionAndResetCache(
8588
8827
  "scroll",
8589
8828
  options
8590
8829
  );
@@ -8685,14 +8924,14 @@ var Opensteer = class _Opensteer {
8685
8924
  }
8686
8925
  // --- Tab Management ---
8687
8926
  async tabs() {
8688
- if (this.remote) {
8689
- return await this.invokeRemoteAction("tabs", {});
8927
+ if (this.cloud) {
8928
+ return await this.invokeCloudAction("tabs", {});
8690
8929
  }
8691
8930
  return listTabs(this.context, this.page);
8692
8931
  }
8693
8932
  async newTab(url) {
8694
- if (this.remote) {
8695
- return await this.invokeRemoteActionAndResetCache("newTab", {
8933
+ if (this.cloud) {
8934
+ return await this.invokeCloudActionAndResetCache("newTab", {
8696
8935
  url
8697
8936
  });
8698
8937
  }
@@ -8702,8 +8941,8 @@ var Opensteer = class _Opensteer {
8702
8941
  return info;
8703
8942
  }
8704
8943
  async switchTab(index) {
8705
- if (this.remote) {
8706
- await this.invokeRemoteActionAndResetCache("switchTab", { index });
8944
+ if (this.cloud) {
8945
+ await this.invokeCloudActionAndResetCache("switchTab", { index });
8707
8946
  return;
8708
8947
  }
8709
8948
  const page = await switchTab(this.context, index);
@@ -8711,8 +8950,8 @@ var Opensteer = class _Opensteer {
8711
8950
  this.snapshotCache = null;
8712
8951
  }
8713
8952
  async closeTab(index) {
8714
- if (this.remote) {
8715
- await this.invokeRemoteActionAndResetCache("closeTab", { index });
8953
+ if (this.cloud) {
8954
+ await this.invokeCloudActionAndResetCache("closeTab", { index });
8716
8955
  return;
8717
8956
  }
8718
8957
  const newPage = await closeTab(this.context, this.page, index);
@@ -8723,8 +8962,8 @@ var Opensteer = class _Opensteer {
8723
8962
  }
8724
8963
  // --- Cookie Management ---
8725
8964
  async getCookies(url) {
8726
- if (this.remote) {
8727
- return await this.invokeRemoteAction(
8965
+ if (this.cloud) {
8966
+ return await this.invokeCloudAction(
8728
8967
  "getCookies",
8729
8968
  { url }
8730
8969
  );
@@ -8732,41 +8971,41 @@ var Opensteer = class _Opensteer {
8732
8971
  return getCookies(this.context, url);
8733
8972
  }
8734
8973
  async setCookie(cookie) {
8735
- if (this.remote) {
8736
- await this.invokeRemoteAction("setCookie", cookie);
8974
+ if (this.cloud) {
8975
+ await this.invokeCloudAction("setCookie", cookie);
8737
8976
  return;
8738
8977
  }
8739
8978
  return setCookie(this.context, cookie);
8740
8979
  }
8741
8980
  async clearCookies() {
8742
- if (this.remote) {
8743
- await this.invokeRemoteAction("clearCookies", {});
8981
+ if (this.cloud) {
8982
+ await this.invokeCloudAction("clearCookies", {});
8744
8983
  return;
8745
8984
  }
8746
8985
  return clearCookies(this.context);
8747
8986
  }
8748
8987
  async exportCookies(filePath, url) {
8749
- if (this.remote) {
8750
- throw remoteUnsupportedMethodError(
8988
+ if (this.cloud) {
8989
+ throw cloudUnsupportedMethodError(
8751
8990
  "exportCookies",
8752
- "exportCookies() is not supported in remote mode because it depends on local filesystem paths."
8991
+ "exportCookies() is not supported in cloud mode because it depends on local filesystem paths."
8753
8992
  );
8754
8993
  }
8755
8994
  return exportCookies(this.context, filePath, url);
8756
8995
  }
8757
8996
  async importCookies(filePath) {
8758
- if (this.remote) {
8759
- throw remoteUnsupportedMethodError(
8997
+ if (this.cloud) {
8998
+ throw cloudUnsupportedMethodError(
8760
8999
  "importCookies",
8761
- "importCookies() is not supported in remote mode because it depends on local filesystem paths."
9000
+ "importCookies() is not supported in cloud mode because it depends on local filesystem paths."
8762
9001
  );
8763
9002
  }
8764
9003
  return importCookies(this.context, filePath);
8765
9004
  }
8766
9005
  // --- Keyboard Input ---
8767
9006
  async pressKey(key) {
8768
- if (this.remote) {
8769
- await this.invokeRemoteActionAndResetCache("pressKey", { key });
9007
+ if (this.cloud) {
9008
+ await this.invokeCloudActionAndResetCache("pressKey", { key });
8770
9009
  return;
8771
9010
  }
8772
9011
  await this.runWithPostActionWait("pressKey", void 0, async () => {
@@ -8775,8 +9014,8 @@ var Opensteer = class _Opensteer {
8775
9014
  this.snapshotCache = null;
8776
9015
  }
8777
9016
  async type(text) {
8778
- if (this.remote) {
8779
- await this.invokeRemoteActionAndResetCache("type", { text });
9017
+ if (this.cloud) {
9018
+ await this.invokeCloudActionAndResetCache("type", { text });
8780
9019
  return;
8781
9020
  }
8782
9021
  await this.runWithPostActionWait("type", void 0, async () => {
@@ -8786,8 +9025,8 @@ var Opensteer = class _Opensteer {
8786
9025
  }
8787
9026
  // --- Element Info ---
8788
9027
  async getElementText(options) {
8789
- if (this.remote) {
8790
- return await this.invokeRemoteAction("getElementText", options);
9028
+ if (this.cloud) {
9029
+ return await this.invokeCloudAction("getElementText", options);
8791
9030
  }
8792
9031
  return this.executeElementInfoAction(
8793
9032
  "getElementText",
@@ -8800,8 +9039,8 @@ var Opensteer = class _Opensteer {
8800
9039
  );
8801
9040
  }
8802
9041
  async getElementValue(options) {
8803
- if (this.remote) {
8804
- return await this.invokeRemoteAction(
9042
+ if (this.cloud) {
9043
+ return await this.invokeCloudAction(
8805
9044
  "getElementValue",
8806
9045
  options
8807
9046
  );
@@ -8816,8 +9055,8 @@ var Opensteer = class _Opensteer {
8816
9055
  );
8817
9056
  }
8818
9057
  async getElementAttributes(options) {
8819
- if (this.remote) {
8820
- return await this.invokeRemoteAction(
9058
+ if (this.cloud) {
9059
+ return await this.invokeCloudAction(
8821
9060
  "getElementAttributes",
8822
9061
  options
8823
9062
  );
@@ -8838,8 +9077,8 @@ var Opensteer = class _Opensteer {
8838
9077
  );
8839
9078
  }
8840
9079
  async getElementBoundingBox(options) {
8841
- if (this.remote) {
8842
- return await this.invokeRemoteAction(
9080
+ if (this.cloud) {
9081
+ return await this.invokeCloudAction(
8843
9082
  "getElementBoundingBox",
8844
9083
  options
8845
9084
  );
@@ -8854,14 +9093,14 @@ var Opensteer = class _Opensteer {
8854
9093
  );
8855
9094
  }
8856
9095
  async getHtml(selector) {
8857
- if (this.remote) {
8858
- return await this.invokeRemoteAction("getHtml", { selector });
9096
+ if (this.cloud) {
9097
+ return await this.invokeCloudAction("getHtml", { selector });
8859
9098
  }
8860
9099
  return getPageHtml(this.page, selector);
8861
9100
  }
8862
9101
  async getTitle() {
8863
- if (this.remote) {
8864
- return await this.invokeRemoteAction("getTitle", {});
9102
+ if (this.cloud) {
9103
+ return await this.invokeCloudAction("getTitle", {});
8865
9104
  }
8866
9105
  return getPageTitle(this.page);
8867
9106
  }
@@ -8899,10 +9138,10 @@ var Opensteer = class _Opensteer {
8899
9138
  }
8900
9139
  // --- File Upload ---
8901
9140
  async uploadFile(options) {
8902
- if (this.remote) {
8903
- throw remoteUnsupportedMethodError(
9141
+ if (this.cloud) {
9142
+ throw cloudUnsupportedMethodError(
8904
9143
  "uploadFile",
8905
- "uploadFile() is not supported in remote mode because file paths must be accessible on the remote server."
9144
+ "uploadFile() is not supported in cloud mode because file paths must be accessible on the cloud runtime."
8906
9145
  );
8907
9146
  }
8908
9147
  const storageKey = this.resolveStorageKey(options.description);
@@ -9004,15 +9243,15 @@ var Opensteer = class _Opensteer {
9004
9243
  }
9005
9244
  // --- Wait for Text ---
9006
9245
  async waitForText(text, options) {
9007
- if (this.remote) {
9008
- await this.invokeRemoteAction("waitForText", { text, options });
9246
+ if (this.cloud) {
9247
+ await this.invokeCloudAction("waitForText", { text, options });
9009
9248
  return;
9010
9249
  }
9011
9250
  await this.page.getByText(text).first().waitFor({ timeout: options?.timeout ?? 3e4 });
9012
9251
  }
9013
9252
  async extract(options) {
9014
- if (this.remote) {
9015
- return await this.invokeRemoteAction("extract", options);
9253
+ if (this.cloud) {
9254
+ return await this.invokeCloudAction("extract", options);
9016
9255
  }
9017
9256
  const storageKey = this.resolveStorageKey(options.description);
9018
9257
  const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
@@ -9064,8 +9303,8 @@ var Opensteer = class _Opensteer {
9064
9303
  return inflateDataPathObject(data);
9065
9304
  }
9066
9305
  async extractFromPlan(options) {
9067
- if (this.remote) {
9068
- return await this.invokeRemoteAction(
9306
+ if (this.cloud) {
9307
+ return await this.invokeCloudAction(
9069
9308
  "extractFromPlan",
9070
9309
  options
9071
9310
  );
@@ -9114,10 +9353,10 @@ var Opensteer = class _Opensteer {
9114
9353
  return this.storage;
9115
9354
  }
9116
9355
  clearCache() {
9117
- if (this.remote) {
9356
+ if (this.cloud) {
9118
9357
  this.snapshotCache = null;
9119
- if (!this.remote.actionClient) return;
9120
- void this.invokeRemoteAction("clearCache", {});
9358
+ if (!this.cloud.actionClient) return;
9359
+ void this.invokeCloudAction("clearCache", {});
9121
9360
  return;
9122
9361
  }
9123
9362
  this.storage.clearNamespace();
@@ -10145,6 +10384,16 @@ function getScrollDelta2(options) {
10145
10384
  return { x: 0, y: absoluteAmount };
10146
10385
  }
10147
10386
  }
10387
+ function buildLocalRunId(namespace) {
10388
+ const normalized = namespace.trim() || "default";
10389
+ return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
10390
+ }
10391
+ function buildCloudSessionUrl(appUrl, sessionId) {
10392
+ if (!appUrl) {
10393
+ return null;
10394
+ }
10395
+ return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10396
+ }
10148
10397
 
10149
10398
  // src/ai/index.ts
10150
10399
  init_resolver();
@@ -10153,6 +10402,8 @@ init_model();
10153
10402
  // Annotate the CommonJS export names for ESM import in node:
10154
10403
  0 && (module.exports = {
10155
10404
  ActionWsClient,
10405
+ CloudCdpClient,
10406
+ CloudSessionClient,
10156
10407
  CounterResolutionError,
10157
10408
  ElementPathError,
10158
10409
  LocalSelectorStorage,
@@ -10166,9 +10417,7 @@ init_model();
10166
10417
  OS_UNAVAILABLE_ATTR,
10167
10418
  Opensteer,
10168
10419
  OpensteerActionError,
10169
- OpensteerRemoteError,
10170
- RemoteCdpClient,
10171
- RemoteSessionClient,
10420
+ OpensteerCloudError,
10172
10421
  buildElementPathFromHandle,
10173
10422
  buildElementPathFromSelector,
10174
10423
  buildPathSelectorHint,
@@ -10180,6 +10429,9 @@ init_model();
10180
10429
  clearCookies,
10181
10430
  cloneElementPath,
10182
10431
  closeTab,
10432
+ cloudNotLaunchedError,
10433
+ cloudSessionContractVersion,
10434
+ cloudUnsupportedMethodError,
10183
10435
  collectLocalSelectorCacheEntries,
10184
10436
  countArrayItemsWithPath,
10185
10437
  createEmptyRegistry,
@@ -10211,8 +10463,6 @@ init_model();
10211
10463
  performSelect,
10212
10464
  prepareSnapshot,
10213
10465
  pressKey,
10214
- remoteNotLaunchedError,
10215
- remoteUnsupportedMethodError,
10216
10466
  resolveCounterElement,
10217
10467
  resolveCountersBatch,
10218
10468
  resolveElementPath,