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.
@@ -4936,33 +4936,36 @@ var OpensteerActionError = class extends Error {
4936
4936
  }
4937
4937
  };
4938
4938
 
4939
- // src/remote/errors.ts
4940
- var OpensteerRemoteError = class extends Error {
4939
+ // src/cloud/contracts.ts
4940
+ var cloudSessionContractVersion = "v3";
4941
+
4942
+ // src/cloud/errors.ts
4943
+ var OpensteerCloudError = class extends Error {
4941
4944
  code;
4942
4945
  status;
4943
4946
  details;
4944
4947
  constructor(code, message, status, details) {
4945
4948
  super(message);
4946
- this.name = "OpensteerRemoteError";
4949
+ this.name = "OpensteerCloudError";
4947
4950
  this.code = code;
4948
4951
  this.status = status;
4949
4952
  this.details = details;
4950
4953
  }
4951
4954
  };
4952
- function remoteUnsupportedMethodError(method, message) {
4953
- return new OpensteerRemoteError(
4954
- "REMOTE_UNSUPPORTED_METHOD",
4955
- message || `${method} is not supported in remote mode.`
4955
+ function cloudUnsupportedMethodError(method, message) {
4956
+ return new OpensteerCloudError(
4957
+ "CLOUD_UNSUPPORTED_METHOD",
4958
+ message || `${method} is not supported in cloud mode.`
4956
4959
  );
4957
4960
  }
4958
- function remoteNotLaunchedError() {
4959
- return new OpensteerRemoteError(
4960
- "REMOTE_SESSION_NOT_FOUND",
4961
- "Remote session is not connected. Call launch() first."
4961
+ function cloudNotLaunchedError() {
4962
+ return new OpensteerCloudError(
4963
+ "CLOUD_SESSION_NOT_FOUND",
4964
+ "Cloud session is not connected. Call launch() first."
4962
4965
  );
4963
4966
  }
4964
4967
 
4965
- // src/remote/action-ws-client.ts
4968
+ // src/cloud/action-ws-client.ts
4966
4969
  import WebSocket from "ws";
4967
4970
  var ActionWsClient = class _ActionWsClient {
4968
4971
  ws;
@@ -4980,18 +4983,18 @@ var ActionWsClient = class _ActionWsClient {
4980
4983
  });
4981
4984
  ws.on("error", (error) => {
4982
4985
  this.rejectAll(
4983
- new OpensteerRemoteError(
4984
- "REMOTE_TRANSPORT_ERROR",
4985
- `Remote action websocket error: ${error.message}`
4986
+ new OpensteerCloudError(
4987
+ "CLOUD_TRANSPORT_ERROR",
4988
+ `Cloud action websocket error: ${error.message}`
4986
4989
  )
4987
4990
  );
4988
4991
  });
4989
4992
  ws.on("close", () => {
4990
4993
  this.closed = true;
4991
4994
  this.rejectAll(
4992
- new OpensteerRemoteError(
4993
- "REMOTE_SESSION_CLOSED",
4994
- "Remote action websocket closed."
4995
+ new OpensteerCloudError(
4996
+ "CLOUD_SESSION_CLOSED",
4997
+ "Cloud action websocket closed."
4995
4998
  )
4996
4999
  );
4997
5000
  });
@@ -5003,8 +5006,8 @@ var ActionWsClient = class _ActionWsClient {
5003
5006
  ws.once("open", () => resolve());
5004
5007
  ws.once("error", (error) => {
5005
5008
  reject(
5006
- new OpensteerRemoteError(
5007
- "REMOTE_TRANSPORT_ERROR",
5009
+ new OpensteerCloudError(
5010
+ "CLOUD_TRANSPORT_ERROR",
5008
5011
  `Failed to connect action websocket: ${error.message}`
5009
5012
  )
5010
5013
  );
@@ -5014,9 +5017,9 @@ var ActionWsClient = class _ActionWsClient {
5014
5017
  }
5015
5018
  async request(method, args) {
5016
5019
  if (this.closed || this.ws.readyState !== WebSocket.OPEN) {
5017
- throw new OpensteerRemoteError(
5018
- "REMOTE_SESSION_CLOSED",
5019
- "Remote action websocket is closed."
5020
+ throw new OpensteerCloudError(
5021
+ "CLOUD_SESSION_CLOSED",
5022
+ "Cloud action websocket is closed."
5020
5023
  );
5021
5024
  }
5022
5025
  const id = this.nextRequestId;
@@ -5035,8 +5038,8 @@ var ActionWsClient = class _ActionWsClient {
5035
5038
  this.ws.send(JSON.stringify(payload));
5036
5039
  } catch (error) {
5037
5040
  this.pending.delete(id);
5038
- const message = error instanceof Error ? error.message : "Failed to send remote action request.";
5039
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
5041
+ const message = error instanceof Error ? error.message : "Failed to send cloud action request.";
5042
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
5040
5043
  }
5041
5044
  return await resultPromise;
5042
5045
  }
@@ -5054,9 +5057,9 @@ var ActionWsClient = class _ActionWsClient {
5054
5057
  parsed = JSON.parse(rawDataToUtf8(raw));
5055
5058
  } catch {
5056
5059
  this.rejectAll(
5057
- new OpensteerRemoteError(
5058
- "REMOTE_TRANSPORT_ERROR",
5059
- "Invalid remote action response payload."
5060
+ new OpensteerCloudError(
5061
+ "CLOUD_TRANSPORT_ERROR",
5062
+ "Invalid cloud action response payload."
5060
5063
  )
5061
5064
  );
5062
5065
  return;
@@ -5069,7 +5072,7 @@ var ActionWsClient = class _ActionWsClient {
5069
5072
  return;
5070
5073
  }
5071
5074
  pending.reject(
5072
- new OpensteerRemoteError(
5075
+ new OpensteerCloudError(
5073
5076
  parsed.code,
5074
5077
  parsed.error,
5075
5078
  void 0,
@@ -5097,7 +5100,7 @@ function withTokenQuery(wsUrl, token) {
5097
5100
  return url.toString();
5098
5101
  }
5099
5102
 
5100
- // src/remote/local-cache-sync.ts
5103
+ // src/cloud/local-cache-sync.ts
5101
5104
  import fs2 from "fs";
5102
5105
  import path3 from "path";
5103
5106
  function collectLocalSelectorCacheEntries(storage) {
@@ -5221,26 +5224,26 @@ function dedupeNewest(entries) {
5221
5224
  return [...byKey.values()];
5222
5225
  }
5223
5226
 
5224
- // src/remote/cdp-client.ts
5227
+ // src/cloud/cdp-client.ts
5225
5228
  import {
5226
5229
  chromium
5227
5230
  } from "playwright";
5228
- var RemoteCdpClient = class {
5231
+ var CloudCdpClient = class {
5229
5232
  async connect(args) {
5230
5233
  const endpoint = withTokenQuery2(args.wsUrl, args.token);
5231
5234
  let browser;
5232
5235
  try {
5233
5236
  browser = await chromium.connectOverCDP(endpoint);
5234
5237
  } catch (error) {
5235
- const message = error instanceof Error ? error.message : "Failed to connect to remote CDP endpoint.";
5236
- throw new OpensteerRemoteError("REMOTE_TRANSPORT_ERROR", message);
5238
+ const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
5239
+ throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
5237
5240
  }
5238
5241
  const context = browser.contexts()[0];
5239
5242
  if (!context) {
5240
5243
  await browser.close();
5241
- throw new OpensteerRemoteError(
5242
- "REMOTE_INTERNAL",
5243
- "Remote browser returned no context."
5244
+ throw new OpensteerCloudError(
5245
+ "CLOUD_INTERNAL",
5246
+ "Cloud browser returned no context."
5244
5247
  );
5245
5248
  }
5246
5249
  const page = context.pages()[0] || await context.newPage();
@@ -5253,9 +5256,9 @@ function withTokenQuery2(wsUrl, token) {
5253
5256
  return url.toString();
5254
5257
  }
5255
5258
 
5256
- // src/remote/session-client.ts
5259
+ // src/cloud/session-client.ts
5257
5260
  var CACHE_IMPORT_BATCH_SIZE = 200;
5258
- var RemoteSessionClient = class {
5261
+ var CloudSessionClient = class {
5259
5262
  baseUrl;
5260
5263
  key;
5261
5264
  authScheme;
@@ -5276,7 +5279,17 @@ var RemoteSessionClient = class {
5276
5279
  if (!response.ok) {
5277
5280
  throw await parseHttpError(response);
5278
5281
  }
5279
- return await response.json();
5282
+ let body;
5283
+ try {
5284
+ body = await response.json();
5285
+ } catch {
5286
+ throw new OpensteerCloudError(
5287
+ "CLOUD_CONTRACT_MISMATCH",
5288
+ "Invalid cloud session create response: expected a JSON object.",
5289
+ response.status
5290
+ );
5291
+ }
5292
+ return parseCreateResponse(body, response.status);
5280
5293
  }
5281
5294
  async close(sessionId) {
5282
5295
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
@@ -5335,6 +5348,134 @@ var RemoteSessionClient = class {
5335
5348
  function normalizeBaseUrl(baseUrl) {
5336
5349
  return baseUrl.replace(/\/+$/, "");
5337
5350
  }
5351
+ function parseCreateResponse(body, status) {
5352
+ const root = requireObject(
5353
+ body,
5354
+ "Invalid cloud session create response: expected a JSON object.",
5355
+ status
5356
+ );
5357
+ const sessionId = requireString(root, "sessionId", status);
5358
+ const actionWsUrl = requireString(root, "actionWsUrl", status);
5359
+ const cdpWsUrl = requireString(root, "cdpWsUrl", status);
5360
+ const actionToken = requireString(root, "actionToken", status);
5361
+ const cdpToken = requireString(root, "cdpToken", status);
5362
+ const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
5363
+ const cloudSessionRoot = requireObject(
5364
+ root.cloudSession,
5365
+ "Invalid cloud session create response: cloudSession must be an object.",
5366
+ status
5367
+ );
5368
+ const cloudSession = {
5369
+ sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
5370
+ workspaceId: requireString(
5371
+ cloudSessionRoot,
5372
+ "workspaceId",
5373
+ status,
5374
+ "cloudSession"
5375
+ ),
5376
+ state: requireString(cloudSessionRoot, "state", status, "cloudSession"),
5377
+ createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
5378
+ sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
5379
+ sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
5380
+ label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
5381
+ };
5382
+ const expiresAt = optionalNumber(root, "expiresAt", status);
5383
+ return {
5384
+ sessionId,
5385
+ actionWsUrl,
5386
+ cdpWsUrl,
5387
+ actionToken,
5388
+ cdpToken,
5389
+ expiresAt,
5390
+ cloudSessionUrl,
5391
+ cloudSession
5392
+ };
5393
+ }
5394
+ function requireObject(value, message, status) {
5395
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
5396
+ throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
5397
+ }
5398
+ return value;
5399
+ }
5400
+ function requireString(source, field, status, parent) {
5401
+ const value = source[field];
5402
+ if (typeof value !== "string" || !value.trim()) {
5403
+ throw new OpensteerCloudError(
5404
+ "CLOUD_CONTRACT_MISMATCH",
5405
+ `Invalid cloud session create response: ${formatFieldPath(
5406
+ field,
5407
+ parent
5408
+ )} must be a non-empty string.`,
5409
+ status
5410
+ );
5411
+ }
5412
+ return value;
5413
+ }
5414
+ function requireNumber(source, field, status, parent) {
5415
+ const value = source[field];
5416
+ if (typeof value !== "number" || !Number.isFinite(value)) {
5417
+ throw new OpensteerCloudError(
5418
+ "CLOUD_CONTRACT_MISMATCH",
5419
+ `Invalid cloud session create response: ${formatFieldPath(
5420
+ field,
5421
+ parent
5422
+ )} must be a finite number.`,
5423
+ status
5424
+ );
5425
+ }
5426
+ return value;
5427
+ }
5428
+ function optionalString(source, field, status, parent) {
5429
+ const value = source[field];
5430
+ if (value == null) {
5431
+ return void 0;
5432
+ }
5433
+ if (typeof value !== "string") {
5434
+ throw new OpensteerCloudError(
5435
+ "CLOUD_CONTRACT_MISMATCH",
5436
+ `Invalid cloud session create response: ${formatFieldPath(
5437
+ field,
5438
+ parent
5439
+ )} must be a string when present.`,
5440
+ status
5441
+ );
5442
+ }
5443
+ return value;
5444
+ }
5445
+ function optionalNumber(source, field, status, parent) {
5446
+ const value = source[field];
5447
+ if (value == null) {
5448
+ return void 0;
5449
+ }
5450
+ if (typeof value !== "number" || !Number.isFinite(value)) {
5451
+ throw new OpensteerCloudError(
5452
+ "CLOUD_CONTRACT_MISMATCH",
5453
+ `Invalid cloud session create response: ${formatFieldPath(
5454
+ field,
5455
+ parent
5456
+ )} must be a finite number when present.`,
5457
+ status
5458
+ );
5459
+ }
5460
+ return value;
5461
+ }
5462
+ function requireSourceType(source, field, status, parent) {
5463
+ const value = source[field];
5464
+ if (value === "agent-thread" || value === "agent-run" || value === "local-cloud" || value === "manual") {
5465
+ return value;
5466
+ }
5467
+ throw new OpensteerCloudError(
5468
+ "CLOUD_CONTRACT_MISMATCH",
5469
+ `Invalid cloud session create response: ${formatFieldPath(
5470
+ field,
5471
+ parent
5472
+ )} must be one of "agent-thread", "agent-run", "local-cloud", or "manual".`,
5473
+ status
5474
+ );
5475
+ }
5476
+ function formatFieldPath(field, parent) {
5477
+ return parent ? `"${parent}.${field}"` : `"${field}"`;
5478
+ }
5338
5479
  function zeroImportResponse() {
5339
5480
  return {
5340
5481
  imported: 0,
@@ -5358,19 +5499,19 @@ async function parseHttpError(response) {
5358
5499
  } catch {
5359
5500
  body = null;
5360
5501
  }
5361
- const code = typeof body?.code === "string" ? toRemoteErrorCode(body.code) : "REMOTE_TRANSPORT_ERROR";
5362
- const message = typeof body?.error === "string" ? body.error : `Remote request failed with status ${response.status}.`;
5363
- return new OpensteerRemoteError(code, message, response.status, body?.details);
5502
+ const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
5503
+ const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
5504
+ return new OpensteerCloudError(code, message, response.status, body?.details);
5364
5505
  }
5365
- function toRemoteErrorCode(code) {
5366
- 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") {
5506
+ function toCloudErrorCode(code) {
5507
+ 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") {
5367
5508
  return code;
5368
5509
  }
5369
- return "REMOTE_TRANSPORT_ERROR";
5510
+ return "CLOUD_TRANSPORT_ERROR";
5370
5511
  }
5371
5512
 
5372
5513
  // src/opensteer.ts
5373
- import { createHash } from "crypto";
5514
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
5374
5515
 
5375
5516
  // src/browser/pool.ts
5376
5517
  import {
@@ -5786,7 +5927,7 @@ var BrowserPool = class {
5786
5927
  const context = contexts[0];
5787
5928
  const pages = context.pages();
5788
5929
  const page = pages.length > 0 ? pages[0] : await context.newPage();
5789
- return { browser, context, page, isRemote: true };
5930
+ return { browser, context, page, isExternal: true };
5790
5931
  } catch (error) {
5791
5932
  if (browser) {
5792
5933
  await browser.close().catch(() => void 0);
@@ -5821,7 +5962,7 @@ var BrowserPool = class {
5821
5962
  context = await browser.newContext(options.context || {});
5822
5963
  page = await context.newPage();
5823
5964
  }
5824
- return { browser, context, page, isRemote: false };
5965
+ return { browser, context, page, isExternal: false };
5825
5966
  }
5826
5967
  async launchSandbox(options) {
5827
5968
  const browser = await chromium2.launch({
@@ -5832,7 +5973,7 @@ var BrowserPool = class {
5832
5973
  const context = await browser.newContext(options.context || {});
5833
5974
  const page = await context.newPage();
5834
5975
  this.browser = browser;
5835
- return { browser, context, page, isRemote: false };
5976
+ return { browser, context, page, isExternal: false };
5836
5977
  }
5837
5978
  };
5838
5979
 
@@ -5840,6 +5981,7 @@ var BrowserPool = class {
5840
5981
  import fs3 from "fs";
5841
5982
  import path4 from "path";
5842
5983
  import { fileURLToPath } from "url";
5984
+ import { parse as parseDotenv } from "dotenv";
5843
5985
  var DEFAULT_CONFIG = {
5844
5986
  browser: {
5845
5987
  headless: false,
@@ -5855,6 +5997,53 @@ var DEFAULT_CONFIG = {
5855
5997
  model: "gpt-5.1",
5856
5998
  debug: false
5857
5999
  };
6000
+ function dotenvFileOrder(nodeEnv) {
6001
+ const normalized = nodeEnv?.trim() || "";
6002
+ const files = [];
6003
+ if (normalized) {
6004
+ files.push(`.env.${normalized}.local`);
6005
+ }
6006
+ if (normalized !== "test") {
6007
+ files.push(".env.local");
6008
+ }
6009
+ if (normalized) {
6010
+ files.push(`.env.${normalized}`);
6011
+ }
6012
+ files.push(".env");
6013
+ return files;
6014
+ }
6015
+ function loadDotenvValues(rootDir, baseEnv) {
6016
+ const values = {};
6017
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
6018
+ return values;
6019
+ }
6020
+ const baseDir = path4.resolve(rootDir);
6021
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
6022
+ for (const filename of dotenvFileOrder(nodeEnv)) {
6023
+ const filePath = path4.join(baseDir, filename);
6024
+ if (!fs3.existsSync(filePath)) continue;
6025
+ try {
6026
+ const raw = fs3.readFileSync(filePath, "utf8");
6027
+ const parsed = parseDotenv(raw);
6028
+ for (const [key, value] of Object.entries(parsed)) {
6029
+ if (values[key] === void 0) {
6030
+ values[key] = value;
6031
+ }
6032
+ }
6033
+ } catch {
6034
+ continue;
6035
+ }
6036
+ }
6037
+ return values;
6038
+ }
6039
+ function resolveEnv(rootDir) {
6040
+ const baseEnv = process.env;
6041
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
6042
+ return {
6043
+ ...dotenvValues,
6044
+ ...baseEnv
6045
+ };
6046
+ }
5858
6047
  function hasOwn(config, key) {
5859
6048
  if (!config || typeof config !== "object") return false;
5860
6049
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -5869,28 +6058,27 @@ function assertNoLegacyAiConfig(source, config) {
5869
6058
  );
5870
6059
  }
5871
6060
  }
5872
- function assertNoLegacyModeConfig(source, config) {
6061
+ function assertNoLegacyRuntimeConfig(source, config) {
5873
6062
  if (!config || typeof config !== "object") return;
5874
6063
  const configRecord = config;
5875
6064
  if (hasOwn(configRecord, "runtime")) {
5876
6065
  throw new Error(
5877
- `Legacy "runtime" config is no longer supported in ${source}. Use top-level "mode" instead.`
6066
+ `Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
5878
6067
  );
5879
6068
  }
5880
- if (hasOwn(configRecord, "apiKey")) {
6069
+ if (hasOwn(configRecord, "mode")) {
5881
6070
  throw new Error(
5882
- `Top-level "apiKey" config is not supported in ${source}. Use "remote.apiKey" instead.`
6071
+ `Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
5883
6072
  );
5884
6073
  }
5885
- const remoteValue = configRecord.remote;
5886
- if (typeof remoteValue === "boolean") {
6074
+ if (hasOwn(configRecord, "remote")) {
5887
6075
  throw new Error(
5888
- `Boolean "remote" config is no longer supported in ${source}. Use "mode: \\"remote\\"" with "remote" options.`
6076
+ `Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
5889
6077
  );
5890
6078
  }
5891
- if (remoteValue && typeof remoteValue === "object" && !Array.isArray(remoteValue) && hasOwn(remoteValue, "key")) {
6079
+ if (hasOwn(configRecord, "apiKey")) {
5892
6080
  throw new Error(
5893
- `Legacy "remote.key" config is no longer supported in ${source}. Use "remote.apiKey" instead.`
6081
+ `Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
5894
6082
  );
5895
6083
  }
5896
6084
  }
@@ -5936,20 +6124,20 @@ function parseNumber(value) {
5936
6124
  if (!Number.isFinite(parsed)) return void 0;
5937
6125
  return parsed;
5938
6126
  }
5939
- function parseMode(value, source) {
6127
+ function parseRuntimeMode(value, source) {
5940
6128
  if (value == null) return void 0;
5941
6129
  if (typeof value !== "string") {
5942
6130
  throw new Error(
5943
- `Invalid ${source} value "${String(value)}". Use "local" or "remote".`
6131
+ `Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
5944
6132
  );
5945
6133
  }
5946
6134
  const normalized = value.trim().toLowerCase();
5947
6135
  if (!normalized) return void 0;
5948
- if (normalized === "local" || normalized === "remote") {
6136
+ if (normalized === "local" || normalized === "cloud") {
5949
6137
  return normalized;
5950
6138
  }
5951
6139
  throw new Error(
5952
- `Invalid ${source} value "${value}". Use "local" or "remote".`
6140
+ `Invalid ${source} value "${value}". Use "local" or "cloud".`
5953
6141
  );
5954
6142
  }
5955
6143
  function parseAuthScheme(value, source) {
@@ -5968,99 +6156,138 @@ function parseAuthScheme(value, source) {
5968
6156
  `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
5969
6157
  );
5970
6158
  }
5971
- function resolveOpensteerApiKey() {
5972
- const value = process.env.OPENSTEER_API_KEY?.trim();
6159
+ function parseCloudAnnounce(value, source) {
6160
+ if (value == null) return void 0;
6161
+ if (typeof value !== "string") {
6162
+ throw new Error(
6163
+ `Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
6164
+ );
6165
+ }
6166
+ const normalized = value.trim().toLowerCase();
6167
+ if (!normalized) return void 0;
6168
+ if (normalized === "always" || normalized === "off" || normalized === "tty") {
6169
+ return normalized;
6170
+ }
6171
+ throw new Error(
6172
+ `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
6173
+ );
6174
+ }
6175
+ function resolveOpensteerApiKey(env) {
6176
+ const value = env.OPENSTEER_API_KEY?.trim();
5973
6177
  if (!value) return void 0;
5974
6178
  return value;
5975
6179
  }
5976
- function resolveOpensteerAuthScheme() {
5977
- return parseAuthScheme(
5978
- process.env.OPENSTEER_AUTH_SCHEME,
5979
- "OPENSTEER_AUTH_SCHEME"
5980
- );
6180
+ function resolveOpensteerAuthScheme(env) {
6181
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
5981
6182
  }
5982
- function normalizeRemoteOptions(value) {
6183
+ function normalizeCloudOptions(value) {
5983
6184
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5984
6185
  return void 0;
5985
6186
  }
5986
6187
  return value;
5987
6188
  }
5988
- function resolveModeSelection(config) {
5989
- const configMode = parseMode(config.mode, "mode");
5990
- if (configMode) {
6189
+ function parseCloudEnabled(value, source) {
6190
+ if (value == null) return void 0;
6191
+ if (typeof value === "boolean") return value;
6192
+ if (typeof value === "object" && !Array.isArray(value)) return true;
6193
+ throw new Error(
6194
+ `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
6195
+ );
6196
+ }
6197
+ function resolveCloudSelection(config, env = process.env) {
6198
+ const configCloud = parseCloudEnabled(config.cloud, "cloud");
6199
+ if (configCloud !== void 0) {
5991
6200
  return {
5992
- mode: configMode,
5993
- source: "config.mode"
6201
+ cloud: configCloud,
6202
+ source: "config.cloud"
5994
6203
  };
5995
6204
  }
5996
- const envMode = parseMode(process.env.OPENSTEER_MODE, "OPENSTEER_MODE");
6205
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
5997
6206
  if (envMode) {
5998
6207
  return {
5999
- mode: envMode,
6208
+ cloud: envMode === "cloud",
6000
6209
  source: "env.OPENSTEER_MODE"
6001
6210
  };
6002
6211
  }
6003
6212
  return {
6004
- mode: "local",
6213
+ cloud: false,
6005
6214
  source: "default"
6006
6215
  };
6007
6216
  }
6008
6217
  function resolveConfig(input = {}) {
6009
- if (process.env.OPENSTEER_AI_MODEL) {
6218
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
6219
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
6220
+ storage: {
6221
+ rootDir: initialRootDir
6222
+ }
6223
+ });
6224
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
6225
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
6226
+ const fileConfig = loadConfigFile(initialRootDir);
6227
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
6228
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
6229
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
6230
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
6231
+ const env = resolveEnv(envRootDir);
6232
+ if (env.OPENSTEER_AI_MODEL) {
6010
6233
  throw new Error(
6011
6234
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
6012
6235
  );
6013
6236
  }
6014
- if (process.env.OPENSTEER_RUNTIME != null) {
6237
+ if (env.OPENSTEER_RUNTIME != null) {
6015
6238
  throw new Error(
6016
6239
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
6017
6240
  );
6018
6241
  }
6019
- assertNoLegacyAiConfig("Opensteer constructor config", input);
6020
- assertNoLegacyModeConfig("Opensteer constructor config", input);
6021
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
6022
- const fileConfig = loadConfigFile(rootDir);
6023
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
6024
- assertNoLegacyModeConfig(".opensteer/config.json", fileConfig);
6025
6242
  const envConfig = {
6026
6243
  browser: {
6027
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
6028
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
6029
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
6030
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
6031
- channel: process.env.OPENSTEER_CHANNEL || void 0,
6032
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
6244
+ headless: parseBool(env.OPENSTEER_HEADLESS),
6245
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
6246
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
6247
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
6248
+ channel: env.OPENSTEER_CHANNEL || void 0,
6249
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
6033
6250
  },
6034
- model: process.env.OPENSTEER_MODEL || void 0,
6035
- debug: parseBool(process.env.OPENSTEER_DEBUG)
6251
+ model: env.OPENSTEER_MODEL || void 0,
6252
+ debug: parseBool(env.OPENSTEER_DEBUG)
6036
6253
  };
6037
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
6254
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
6038
6255
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
6039
6256
  const resolved = mergeDeep(mergedWithEnv, input);
6040
- const envApiKey = resolveOpensteerApiKey();
6041
- const envAuthScheme = resolveOpensteerAuthScheme();
6042
- const inputRemoteOptions = normalizeRemoteOptions(input.remote);
6257
+ const envApiKey = resolveOpensteerApiKey(env);
6258
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
6259
+ const envCloudAnnounce = parseCloudAnnounce(
6260
+ env.OPENSTEER_REMOTE_ANNOUNCE,
6261
+ "OPENSTEER_REMOTE_ANNOUNCE"
6262
+ );
6263
+ const inputCloudOptions = normalizeCloudOptions(input.cloud);
6043
6264
  const inputAuthScheme = parseAuthScheme(
6044
- inputRemoteOptions?.authScheme,
6045
- "remote.authScheme"
6265
+ inputCloudOptions?.authScheme,
6266
+ "cloud.authScheme"
6046
6267
  );
6047
- const inputHasRemoteApiKey = Boolean(
6048
- inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
6268
+ const inputCloudAnnounce = parseCloudAnnounce(
6269
+ inputCloudOptions?.announce,
6270
+ "cloud.announce"
6049
6271
  );
6050
- const modeSelection = resolveModeSelection({
6051
- mode: resolved.mode
6052
- });
6053
- if (modeSelection.mode === "remote") {
6054
- const resolvedRemote = normalizeRemoteOptions(resolved.remote) ?? {};
6055
- const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedRemote.authScheme, "remote.authScheme") ?? "api-key";
6056
- resolved.remote = {
6057
- ...resolvedRemote,
6058
- authScheme
6272
+ const inputHasCloudApiKey = Boolean(
6273
+ inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
6274
+ );
6275
+ const cloudSelection = resolveCloudSelection({
6276
+ cloud: resolved.cloud
6277
+ }, env);
6278
+ if (cloudSelection.cloud) {
6279
+ const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
6280
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
6281
+ const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
6282
+ resolved.cloud = {
6283
+ ...resolvedCloud,
6284
+ authScheme,
6285
+ announce
6059
6286
  };
6060
6287
  }
6061
- if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
6062
- resolved.remote = {
6063
- ...normalizeRemoteOptions(resolved.remote) ?? {},
6288
+ if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
6289
+ resolved.cloud = {
6290
+ ...normalizeCloudOptions(resolved.cloud) ?? {},
6064
6291
  apiKey: envApiKey
6065
6292
  };
6066
6293
  }
@@ -7456,22 +7683,35 @@ function clonePersistedExtractNode(node) {
7456
7683
  return JSON.parse(JSON.stringify(node));
7457
7684
  }
7458
7685
 
7459
- // src/remote/runtime.ts
7460
- var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7461
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
7686
+ // src/cloud/runtime.ts
7687
+ var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
7688
+ var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
7689
+ function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
7462
7690
  return {
7463
- sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7464
- cdpClient: new RemoteCdpClient(),
7691
+ sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
7692
+ cdpClient: new CloudCdpClient(),
7693
+ appUrl: normalizeCloudAppUrl(appUrl),
7465
7694
  actionClient: null,
7466
- sessionId: null
7695
+ sessionId: null,
7696
+ localRunId: null,
7697
+ cloudSessionUrl: null
7467
7698
  };
7468
7699
  }
7469
- function resolveRemoteBaseUrl() {
7700
+ function resolveCloudBaseUrl() {
7470
7701
  const value = process.env.OPENSTEER_BASE_URL?.trim();
7471
- if (!value) return DEFAULT_REMOTE_BASE_URL;
7702
+ if (!value) return DEFAULT_CLOUD_BASE_URL;
7472
7703
  return value.replace(/\/+$/, "");
7473
7704
  }
7474
- function readRemoteActionDescription(payload) {
7705
+ function resolveCloudAppUrl() {
7706
+ const value = process.env.OPENSTEER_APP_URL?.trim();
7707
+ if (!value) return DEFAULT_CLOUD_APP_URL;
7708
+ return normalizeCloudAppUrl(value);
7709
+ }
7710
+ function normalizeCloudAppUrl(value) {
7711
+ if (!value) return null;
7712
+ return value.replace(/\/+$/, "");
7713
+ }
7714
+ function readCloudActionDescription(payload) {
7475
7715
  const description = payload.description;
7476
7716
  if (typeof description !== "string") return void 0;
7477
7717
  const normalized = description.trim();
@@ -7479,7 +7719,7 @@ function readRemoteActionDescription(payload) {
7479
7719
  }
7480
7720
 
7481
7721
  // src/opensteer.ts
7482
- var REMOTE_INTERACTION_METHODS = /* @__PURE__ */ new Set([
7722
+ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
7483
7723
  "click",
7484
7724
  "dblclick",
7485
7725
  "rightclick",
@@ -7496,7 +7736,7 @@ var Opensteer = class _Opensteer {
7496
7736
  namespace;
7497
7737
  storage;
7498
7738
  pool;
7499
- remote;
7739
+ cloud;
7500
7740
  browser = null;
7501
7741
  pageRef = null;
7502
7742
  contextRef = null;
@@ -7504,8 +7744,8 @@ var Opensteer = class _Opensteer {
7504
7744
  snapshotCache = null;
7505
7745
  constructor(config = {}) {
7506
7746
  const resolved = resolveConfig(config);
7507
- const modeSelection = resolveModeSelection({
7508
- mode: resolved.mode
7747
+ const cloudSelection = resolveCloudSelection({
7748
+ cloud: resolved.cloud
7509
7749
  });
7510
7750
  const model = resolved.model;
7511
7751
  this.config = resolved;
@@ -7515,21 +7755,22 @@ var Opensteer = class _Opensteer {
7515
7755
  this.namespace = resolveNamespace(resolved, rootDir);
7516
7756
  this.storage = new LocalSelectorStorage(rootDir, this.namespace);
7517
7757
  this.pool = new BrowserPool(resolved.browser || {});
7518
- if (modeSelection.mode === "remote") {
7519
- const remoteConfig = resolved.remote && typeof resolved.remote === "object" ? resolved.remote : void 0;
7520
- const apiKey = remoteConfig?.apiKey?.trim();
7758
+ if (cloudSelection.cloud) {
7759
+ const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
7760
+ const apiKey = cloudConfig?.apiKey?.trim();
7521
7761
  if (!apiKey) {
7522
7762
  throw new Error(
7523
- "Remote mode requires a non-empty API key via remote.apiKey or OPENSTEER_API_KEY."
7763
+ "Cloud mode requires a non-empty API key via cloud.apiKey or OPENSTEER_API_KEY."
7524
7764
  );
7525
7765
  }
7526
- this.remote = createRemoteRuntimeState(
7766
+ this.cloud = createCloudRuntimeState(
7527
7767
  apiKey,
7528
- remoteConfig?.baseUrl,
7529
- remoteConfig?.authScheme
7768
+ cloudConfig?.baseUrl,
7769
+ cloudConfig?.authScheme,
7770
+ cloudConfig?.appUrl
7530
7771
  );
7531
7772
  } else {
7532
- this.remote = null;
7773
+ this.cloud = null;
7533
7774
  }
7534
7775
  }
7535
7776
  createLazyResolveCallback(model) {
@@ -7567,32 +7808,32 @@ var Opensteer = class _Opensteer {
7567
7808
  };
7568
7809
  return extract;
7569
7810
  }
7570
- async invokeRemoteActionAndResetCache(method, args) {
7571
- const result = await this.invokeRemoteAction(method, args);
7811
+ async invokeCloudActionAndResetCache(method, args) {
7812
+ const result = await this.invokeCloudAction(method, args);
7572
7813
  this.snapshotCache = null;
7573
7814
  return result;
7574
7815
  }
7575
- async invokeRemoteAction(method, args) {
7576
- const actionClient = this.remote?.actionClient;
7577
- const sessionId = this.remote?.sessionId;
7816
+ async invokeCloudAction(method, args) {
7817
+ const actionClient = this.cloud?.actionClient;
7818
+ const sessionId = this.cloud?.sessionId;
7578
7819
  if (!actionClient || !sessionId) {
7579
- throw remoteNotLaunchedError();
7820
+ throw cloudNotLaunchedError();
7580
7821
  }
7581
7822
  const payload = args && typeof args === "object" ? args : {};
7582
7823
  try {
7583
7824
  return await actionClient.request(method, payload);
7584
7825
  } catch (err) {
7585
- if (err instanceof OpensteerRemoteError && err.code === "REMOTE_ACTION_FAILED" && REMOTE_INTERACTION_METHODS.has(method)) {
7826
+ if (err instanceof OpensteerCloudError && err.code === "CLOUD_ACTION_FAILED" && CLOUD_INTERACTION_METHODS.has(method)) {
7586
7827
  const detailsRecord = err.details && typeof err.details === "object" ? err.details : null;
7587
- const remoteFailure = normalizeActionFailure(
7828
+ const cloudFailure = normalizeActionFailure(
7588
7829
  detailsRecord?.actionFailure
7589
7830
  );
7590
- const failure = remoteFailure || classifyActionFailure({
7831
+ const failure = cloudFailure || classifyActionFailure({
7591
7832
  action: method,
7592
7833
  error: err,
7593
7834
  fallbackMessage: defaultActionFailureMessage(method)
7594
7835
  });
7595
- const description = readRemoteActionDescription(payload);
7836
+ const description = readCloudActionDescription(payload);
7596
7837
  throw this.buildActionError(
7597
7838
  method,
7598
7839
  description,
@@ -7633,8 +7874,36 @@ var Opensteer = class _Opensteer {
7633
7874
  }
7634
7875
  return this.contextRef;
7635
7876
  }
7636
- getRemoteSessionId() {
7637
- return this.remote?.sessionId ?? null;
7877
+ getCloudSessionId() {
7878
+ return this.cloud?.sessionId ?? null;
7879
+ }
7880
+ getCloudSessionUrl() {
7881
+ return this.cloud?.cloudSessionUrl ?? null;
7882
+ }
7883
+ announceCloudSession(args) {
7884
+ if (!this.shouldAnnounceCloudSession()) {
7885
+ return;
7886
+ }
7887
+ const fields = [
7888
+ `sessionId=${args.sessionId}`,
7889
+ `workspaceId=${args.workspaceId}`
7890
+ ];
7891
+ if (args.cloudSessionUrl) {
7892
+ fields.push(`url=${args.cloudSessionUrl}`);
7893
+ }
7894
+ process.stderr.write(`[opensteer] cloud session ready ${fields.join(" ")}
7895
+ `);
7896
+ }
7897
+ shouldAnnounceCloudSession() {
7898
+ const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : null;
7899
+ const announce = cloudConfig?.announce ?? "always";
7900
+ if (announce === "off") {
7901
+ return false;
7902
+ }
7903
+ if (announce === "tty") {
7904
+ return Boolean(process.stderr.isTTY);
7905
+ }
7906
+ return true;
7638
7907
  }
7639
7908
  async launch(options = {}) {
7640
7909
  if (this.pageRef && !this.ownsBrowser) {
@@ -7645,22 +7914,29 @@ var Opensteer = class _Opensteer {
7645
7914
  if (this.pageRef && this.ownsBrowser) {
7646
7915
  return;
7647
7916
  }
7648
- if (this.remote) {
7917
+ if (this.cloud) {
7649
7918
  let actionClient = null;
7650
7919
  let browser = null;
7651
7920
  let sessionId = null;
7921
+ let localRunId = null;
7652
7922
  try {
7653
7923
  try {
7654
- await this.syncLocalSelectorCacheToRemote();
7924
+ await this.syncLocalSelectorCacheToCloud();
7655
7925
  } catch (error) {
7656
7926
  if (this.config.debug) {
7657
7927
  const message = error instanceof Error ? error.message : String(error);
7658
7928
  console.warn(
7659
- `[opensteer] remote selector cache sync failed: ${message}`
7929
+ `[opensteer] cloud selector cache sync failed: ${message}`
7660
7930
  );
7661
7931
  }
7662
7932
  }
7663
- const session2 = await this.remote.sessionClient.create({
7933
+ localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
7934
+ this.cloud.localRunId = localRunId;
7935
+ const session2 = await this.cloud.sessionClient.create({
7936
+ cloudSessionContractVersion,
7937
+ sourceType: "local-cloud",
7938
+ clientSessionHint: this.namespace,
7939
+ localRunId,
7664
7940
  name: this.namespace,
7665
7941
  model: this.config.model,
7666
7942
  launchContext: options.context || void 0
@@ -7671,7 +7947,7 @@ var Opensteer = class _Opensteer {
7671
7947
  token: session2.actionToken,
7672
7948
  sessionId: session2.sessionId
7673
7949
  });
7674
- const cdpConnection = await this.remote.cdpClient.connect({
7950
+ const cdpConnection = await this.cloud.cdpClient.connect({
7675
7951
  wsUrl: session2.cdpWsUrl,
7676
7952
  token: session2.cdpToken
7677
7953
  });
@@ -7681,8 +7957,17 @@ var Opensteer = class _Opensteer {
7681
7957
  this.pageRef = cdpConnection.page;
7682
7958
  this.ownsBrowser = true;
7683
7959
  this.snapshotCache = null;
7684
- this.remote.actionClient = actionClient;
7685
- this.remote.sessionId = sessionId;
7960
+ this.cloud.actionClient = actionClient;
7961
+ this.cloud.sessionId = sessionId;
7962
+ this.cloud.cloudSessionUrl = buildCloudSessionUrl(
7963
+ this.cloud.appUrl,
7964
+ session2.cloudSession.sessionId
7965
+ );
7966
+ this.announceCloudSession({
7967
+ sessionId: session2.sessionId,
7968
+ workspaceId: session2.cloudSession.workspaceId,
7969
+ cloudSessionUrl: this.cloud.cloudSessionUrl
7970
+ });
7686
7971
  return;
7687
7972
  } catch (error) {
7688
7973
  if (actionClient) {
@@ -7692,8 +7977,9 @@ var Opensteer = class _Opensteer {
7692
7977
  await browser.close().catch(() => void 0);
7693
7978
  }
7694
7979
  if (sessionId) {
7695
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
7980
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
7696
7981
  }
7982
+ this.cloud.cloudSessionUrl = null;
7697
7983
  throw error;
7698
7984
  }
7699
7985
  }
@@ -7711,13 +7997,13 @@ var Opensteer = class _Opensteer {
7711
7997
  }
7712
7998
  static from(page, config = {}) {
7713
7999
  const resolvedConfig = resolveConfig(config);
7714
- const modeSelection = resolveModeSelection({
7715
- mode: resolvedConfig.mode
8000
+ const cloudSelection = resolveCloudSelection({
8001
+ cloud: resolvedConfig.cloud
7716
8002
  });
7717
- if (modeSelection.mode === "remote") {
7718
- throw remoteUnsupportedMethodError(
8003
+ if (cloudSelection.cloud) {
8004
+ throw cloudUnsupportedMethodError(
7719
8005
  "Opensteer.from(page)",
7720
- "Opensteer.from(page) is not supported in remote mode."
8006
+ "Opensteer.from(page) is not supported in cloud mode."
7721
8007
  );
7722
8008
  }
7723
8009
  const instance = new _Opensteer(config);
@@ -7730,12 +8016,14 @@ var Opensteer = class _Opensteer {
7730
8016
  }
7731
8017
  async close() {
7732
8018
  this.snapshotCache = null;
7733
- if (this.remote) {
7734
- const actionClient = this.remote.actionClient;
7735
- const sessionId = this.remote.sessionId;
8019
+ if (this.cloud) {
8020
+ const actionClient = this.cloud.actionClient;
8021
+ const sessionId = this.cloud.sessionId;
7736
8022
  const browser = this.browser;
7737
- this.remote.actionClient = null;
7738
- this.remote.sessionId = null;
8023
+ this.cloud.actionClient = null;
8024
+ this.cloud.sessionId = null;
8025
+ this.cloud.localRunId = null;
8026
+ this.cloud.cloudSessionUrl = null;
7739
8027
  this.browser = null;
7740
8028
  this.pageRef = null;
7741
8029
  this.contextRef = null;
@@ -7747,7 +8035,7 @@ var Opensteer = class _Opensteer {
7747
8035
  await browser.close().catch(() => void 0);
7748
8036
  }
7749
8037
  if (sessionId) {
7750
- await this.remote.sessionClient.close(sessionId).catch(() => void 0);
8038
+ await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
7751
8039
  }
7752
8040
  return;
7753
8041
  }
@@ -7759,17 +8047,17 @@ var Opensteer = class _Opensteer {
7759
8047
  this.contextRef = null;
7760
8048
  this.ownsBrowser = false;
7761
8049
  }
7762
- async syncLocalSelectorCacheToRemote() {
7763
- if (!this.remote) return;
8050
+ async syncLocalSelectorCacheToCloud() {
8051
+ if (!this.cloud) return;
7764
8052
  const entries = collectLocalSelectorCacheEntries(this.storage);
7765
8053
  if (!entries.length) return;
7766
- await this.remote.sessionClient.importSelectorCache({
8054
+ await this.cloud.sessionClient.importSelectorCache({
7767
8055
  entries
7768
8056
  });
7769
8057
  }
7770
8058
  async goto(url, options) {
7771
- if (this.remote) {
7772
- await this.invokeRemoteActionAndResetCache("goto", { url, options });
8059
+ if (this.cloud) {
8060
+ await this.invokeCloudActionAndResetCache("goto", { url, options });
7773
8061
  return;
7774
8062
  }
7775
8063
  const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
@@ -7778,8 +8066,8 @@ var Opensteer = class _Opensteer {
7778
8066
  this.snapshotCache = null;
7779
8067
  }
7780
8068
  async snapshot(options = {}) {
7781
- if (this.remote) {
7782
- return await this.invokeRemoteActionAndResetCache("snapshot", {
8069
+ if (this.cloud) {
8070
+ return await this.invokeCloudActionAndResetCache("snapshot", {
7783
8071
  options
7784
8072
  });
7785
8073
  }
@@ -7788,8 +8076,8 @@ var Opensteer = class _Opensteer {
7788
8076
  return prepared.cleanedHtml;
7789
8077
  }
7790
8078
  async state() {
7791
- if (this.remote) {
7792
- return await this.invokeRemoteAction("state", {});
8079
+ if (this.cloud) {
8080
+ return await this.invokeCloudAction("state", {});
7793
8081
  }
7794
8082
  const html = await this.snapshot({ mode: "action" });
7795
8083
  return {
@@ -7799,8 +8087,8 @@ var Opensteer = class _Opensteer {
7799
8087
  };
7800
8088
  }
7801
8089
  async screenshot(options = {}) {
7802
- if (this.remote) {
7803
- const b64 = await this.invokeRemoteAction(
8090
+ if (this.cloud) {
8091
+ const b64 = await this.invokeCloudAction(
7804
8092
  "screenshot",
7805
8093
  options
7806
8094
  );
@@ -7814,8 +8102,8 @@ var Opensteer = class _Opensteer {
7814
8102
  });
7815
8103
  }
7816
8104
  async click(options) {
7817
- if (this.remote) {
7818
- return await this.invokeRemoteActionAndResetCache(
8105
+ if (this.cloud) {
8106
+ return await this.invokeCloudActionAndResetCache(
7819
8107
  "click",
7820
8108
  options
7821
8109
  );
@@ -7827,8 +8115,8 @@ var Opensteer = class _Opensteer {
7827
8115
  });
7828
8116
  }
7829
8117
  async dblclick(options) {
7830
- if (this.remote) {
7831
- return await this.invokeRemoteActionAndResetCache(
8118
+ if (this.cloud) {
8119
+ return await this.invokeCloudActionAndResetCache(
7832
8120
  "dblclick",
7833
8121
  options
7834
8122
  );
@@ -7840,8 +8128,8 @@ var Opensteer = class _Opensteer {
7840
8128
  });
7841
8129
  }
7842
8130
  async rightclick(options) {
7843
- if (this.remote) {
7844
- return await this.invokeRemoteActionAndResetCache(
8131
+ if (this.cloud) {
8132
+ return await this.invokeCloudActionAndResetCache(
7845
8133
  "rightclick",
7846
8134
  options
7847
8135
  );
@@ -7853,8 +8141,8 @@ var Opensteer = class _Opensteer {
7853
8141
  });
7854
8142
  }
7855
8143
  async hover(options) {
7856
- if (this.remote) {
7857
- return await this.invokeRemoteActionAndResetCache(
8144
+ if (this.cloud) {
8145
+ return await this.invokeCloudActionAndResetCache(
7858
8146
  "hover",
7859
8147
  options
7860
8148
  );
@@ -7952,8 +8240,8 @@ var Opensteer = class _Opensteer {
7952
8240
  );
7953
8241
  }
7954
8242
  async input(options) {
7955
- if (this.remote) {
7956
- return await this.invokeRemoteActionAndResetCache(
8243
+ if (this.cloud) {
8244
+ return await this.invokeCloudActionAndResetCache(
7957
8245
  "input",
7958
8246
  options
7959
8247
  );
@@ -8055,8 +8343,8 @@ var Opensteer = class _Opensteer {
8055
8343
  );
8056
8344
  }
8057
8345
  async select(options) {
8058
- if (this.remote) {
8059
- return await this.invokeRemoteActionAndResetCache(
8346
+ if (this.cloud) {
8347
+ return await this.invokeCloudActionAndResetCache(
8060
8348
  "select",
8061
8349
  options
8062
8350
  );
@@ -8165,8 +8453,8 @@ var Opensteer = class _Opensteer {
8165
8453
  );
8166
8454
  }
8167
8455
  async scroll(options = {}) {
8168
- if (this.remote) {
8169
- return await this.invokeRemoteActionAndResetCache(
8456
+ if (this.cloud) {
8457
+ return await this.invokeCloudActionAndResetCache(
8170
8458
  "scroll",
8171
8459
  options
8172
8460
  );
@@ -8267,14 +8555,14 @@ var Opensteer = class _Opensteer {
8267
8555
  }
8268
8556
  // --- Tab Management ---
8269
8557
  async tabs() {
8270
- if (this.remote) {
8271
- return await this.invokeRemoteAction("tabs", {});
8558
+ if (this.cloud) {
8559
+ return await this.invokeCloudAction("tabs", {});
8272
8560
  }
8273
8561
  return listTabs(this.context, this.page);
8274
8562
  }
8275
8563
  async newTab(url) {
8276
- if (this.remote) {
8277
- return await this.invokeRemoteActionAndResetCache("newTab", {
8564
+ if (this.cloud) {
8565
+ return await this.invokeCloudActionAndResetCache("newTab", {
8278
8566
  url
8279
8567
  });
8280
8568
  }
@@ -8284,8 +8572,8 @@ var Opensteer = class _Opensteer {
8284
8572
  return info;
8285
8573
  }
8286
8574
  async switchTab(index) {
8287
- if (this.remote) {
8288
- await this.invokeRemoteActionAndResetCache("switchTab", { index });
8575
+ if (this.cloud) {
8576
+ await this.invokeCloudActionAndResetCache("switchTab", { index });
8289
8577
  return;
8290
8578
  }
8291
8579
  const page = await switchTab(this.context, index);
@@ -8293,8 +8581,8 @@ var Opensteer = class _Opensteer {
8293
8581
  this.snapshotCache = null;
8294
8582
  }
8295
8583
  async closeTab(index) {
8296
- if (this.remote) {
8297
- await this.invokeRemoteActionAndResetCache("closeTab", { index });
8584
+ if (this.cloud) {
8585
+ await this.invokeCloudActionAndResetCache("closeTab", { index });
8298
8586
  return;
8299
8587
  }
8300
8588
  const newPage = await closeTab(this.context, this.page, index);
@@ -8305,8 +8593,8 @@ var Opensteer = class _Opensteer {
8305
8593
  }
8306
8594
  // --- Cookie Management ---
8307
8595
  async getCookies(url) {
8308
- if (this.remote) {
8309
- return await this.invokeRemoteAction(
8596
+ if (this.cloud) {
8597
+ return await this.invokeCloudAction(
8310
8598
  "getCookies",
8311
8599
  { url }
8312
8600
  );
@@ -8314,41 +8602,41 @@ var Opensteer = class _Opensteer {
8314
8602
  return getCookies(this.context, url);
8315
8603
  }
8316
8604
  async setCookie(cookie) {
8317
- if (this.remote) {
8318
- await this.invokeRemoteAction("setCookie", cookie);
8605
+ if (this.cloud) {
8606
+ await this.invokeCloudAction("setCookie", cookie);
8319
8607
  return;
8320
8608
  }
8321
8609
  return setCookie(this.context, cookie);
8322
8610
  }
8323
8611
  async clearCookies() {
8324
- if (this.remote) {
8325
- await this.invokeRemoteAction("clearCookies", {});
8612
+ if (this.cloud) {
8613
+ await this.invokeCloudAction("clearCookies", {});
8326
8614
  return;
8327
8615
  }
8328
8616
  return clearCookies(this.context);
8329
8617
  }
8330
8618
  async exportCookies(filePath, url) {
8331
- if (this.remote) {
8332
- throw remoteUnsupportedMethodError(
8619
+ if (this.cloud) {
8620
+ throw cloudUnsupportedMethodError(
8333
8621
  "exportCookies",
8334
- "exportCookies() is not supported in remote mode because it depends on local filesystem paths."
8622
+ "exportCookies() is not supported in cloud mode because it depends on local filesystem paths."
8335
8623
  );
8336
8624
  }
8337
8625
  return exportCookies(this.context, filePath, url);
8338
8626
  }
8339
8627
  async importCookies(filePath) {
8340
- if (this.remote) {
8341
- throw remoteUnsupportedMethodError(
8628
+ if (this.cloud) {
8629
+ throw cloudUnsupportedMethodError(
8342
8630
  "importCookies",
8343
- "importCookies() is not supported in remote mode because it depends on local filesystem paths."
8631
+ "importCookies() is not supported in cloud mode because it depends on local filesystem paths."
8344
8632
  );
8345
8633
  }
8346
8634
  return importCookies(this.context, filePath);
8347
8635
  }
8348
8636
  // --- Keyboard Input ---
8349
8637
  async pressKey(key) {
8350
- if (this.remote) {
8351
- await this.invokeRemoteActionAndResetCache("pressKey", { key });
8638
+ if (this.cloud) {
8639
+ await this.invokeCloudActionAndResetCache("pressKey", { key });
8352
8640
  return;
8353
8641
  }
8354
8642
  await this.runWithPostActionWait("pressKey", void 0, async () => {
@@ -8357,8 +8645,8 @@ var Opensteer = class _Opensteer {
8357
8645
  this.snapshotCache = null;
8358
8646
  }
8359
8647
  async type(text) {
8360
- if (this.remote) {
8361
- await this.invokeRemoteActionAndResetCache("type", { text });
8648
+ if (this.cloud) {
8649
+ await this.invokeCloudActionAndResetCache("type", { text });
8362
8650
  return;
8363
8651
  }
8364
8652
  await this.runWithPostActionWait("type", void 0, async () => {
@@ -8368,8 +8656,8 @@ var Opensteer = class _Opensteer {
8368
8656
  }
8369
8657
  // --- Element Info ---
8370
8658
  async getElementText(options) {
8371
- if (this.remote) {
8372
- return await this.invokeRemoteAction("getElementText", options);
8659
+ if (this.cloud) {
8660
+ return await this.invokeCloudAction("getElementText", options);
8373
8661
  }
8374
8662
  return this.executeElementInfoAction(
8375
8663
  "getElementText",
@@ -8382,8 +8670,8 @@ var Opensteer = class _Opensteer {
8382
8670
  );
8383
8671
  }
8384
8672
  async getElementValue(options) {
8385
- if (this.remote) {
8386
- return await this.invokeRemoteAction(
8673
+ if (this.cloud) {
8674
+ return await this.invokeCloudAction(
8387
8675
  "getElementValue",
8388
8676
  options
8389
8677
  );
@@ -8398,8 +8686,8 @@ var Opensteer = class _Opensteer {
8398
8686
  );
8399
8687
  }
8400
8688
  async getElementAttributes(options) {
8401
- if (this.remote) {
8402
- return await this.invokeRemoteAction(
8689
+ if (this.cloud) {
8690
+ return await this.invokeCloudAction(
8403
8691
  "getElementAttributes",
8404
8692
  options
8405
8693
  );
@@ -8420,8 +8708,8 @@ var Opensteer = class _Opensteer {
8420
8708
  );
8421
8709
  }
8422
8710
  async getElementBoundingBox(options) {
8423
- if (this.remote) {
8424
- return await this.invokeRemoteAction(
8711
+ if (this.cloud) {
8712
+ return await this.invokeCloudAction(
8425
8713
  "getElementBoundingBox",
8426
8714
  options
8427
8715
  );
@@ -8436,14 +8724,14 @@ var Opensteer = class _Opensteer {
8436
8724
  );
8437
8725
  }
8438
8726
  async getHtml(selector) {
8439
- if (this.remote) {
8440
- return await this.invokeRemoteAction("getHtml", { selector });
8727
+ if (this.cloud) {
8728
+ return await this.invokeCloudAction("getHtml", { selector });
8441
8729
  }
8442
8730
  return getPageHtml(this.page, selector);
8443
8731
  }
8444
8732
  async getTitle() {
8445
- if (this.remote) {
8446
- return await this.invokeRemoteAction("getTitle", {});
8733
+ if (this.cloud) {
8734
+ return await this.invokeCloudAction("getTitle", {});
8447
8735
  }
8448
8736
  return getPageTitle(this.page);
8449
8737
  }
@@ -8481,10 +8769,10 @@ var Opensteer = class _Opensteer {
8481
8769
  }
8482
8770
  // --- File Upload ---
8483
8771
  async uploadFile(options) {
8484
- if (this.remote) {
8485
- throw remoteUnsupportedMethodError(
8772
+ if (this.cloud) {
8773
+ throw cloudUnsupportedMethodError(
8486
8774
  "uploadFile",
8487
- "uploadFile() is not supported in remote mode because file paths must be accessible on the remote server."
8775
+ "uploadFile() is not supported in cloud mode because file paths must be accessible on the cloud runtime."
8488
8776
  );
8489
8777
  }
8490
8778
  const storageKey = this.resolveStorageKey(options.description);
@@ -8586,15 +8874,15 @@ var Opensteer = class _Opensteer {
8586
8874
  }
8587
8875
  // --- Wait for Text ---
8588
8876
  async waitForText(text, options) {
8589
- if (this.remote) {
8590
- await this.invokeRemoteAction("waitForText", { text, options });
8877
+ if (this.cloud) {
8878
+ await this.invokeCloudAction("waitForText", { text, options });
8591
8879
  return;
8592
8880
  }
8593
8881
  await this.page.getByText(text).first().waitFor({ timeout: options?.timeout ?? 3e4 });
8594
8882
  }
8595
8883
  async extract(options) {
8596
- if (this.remote) {
8597
- return await this.invokeRemoteAction("extract", options);
8884
+ if (this.cloud) {
8885
+ return await this.invokeCloudAction("extract", options);
8598
8886
  }
8599
8887
  const storageKey = this.resolveStorageKey(options.description);
8600
8888
  const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
@@ -8646,8 +8934,8 @@ var Opensteer = class _Opensteer {
8646
8934
  return inflateDataPathObject(data);
8647
8935
  }
8648
8936
  async extractFromPlan(options) {
8649
- if (this.remote) {
8650
- return await this.invokeRemoteAction(
8937
+ if (this.cloud) {
8938
+ return await this.invokeCloudAction(
8651
8939
  "extractFromPlan",
8652
8940
  options
8653
8941
  );
@@ -8696,10 +8984,10 @@ var Opensteer = class _Opensteer {
8696
8984
  return this.storage;
8697
8985
  }
8698
8986
  clearCache() {
8699
- if (this.remote) {
8987
+ if (this.cloud) {
8700
8988
  this.snapshotCache = null;
8701
- if (!this.remote.actionClient) return;
8702
- void this.invokeRemoteAction("clearCache", {});
8989
+ if (!this.cloud.actionClient) return;
8990
+ void this.invokeCloudAction("clearCache", {});
8703
8991
  return;
8704
8992
  }
8705
8993
  this.storage.clearNamespace();
@@ -9727,6 +10015,16 @@ function getScrollDelta2(options) {
9727
10015
  return { x: 0, y: absoluteAmount };
9728
10016
  }
9729
10017
  }
10018
+ function buildLocalRunId(namespace) {
10019
+ const normalized = namespace.trim() || "default";
10020
+ return `${normalized}-${Date.now().toString(36)}-${randomUUID2().slice(0, 8)}`;
10021
+ }
10022
+ function buildCloudSessionUrl(appUrl, sessionId) {
10023
+ if (!appUrl) {
10024
+ return null;
10025
+ }
10026
+ return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
10027
+ }
9730
10028
 
9731
10029
  export {
9732
10030
  normalizeNamespace,
@@ -9789,12 +10087,13 @@ export {
9789
10087
  getPageTitle,
9790
10088
  performFileUpload,
9791
10089
  OpensteerActionError,
9792
- OpensteerRemoteError,
9793
- remoteUnsupportedMethodError,
9794
- remoteNotLaunchedError,
10090
+ cloudSessionContractVersion,
10091
+ OpensteerCloudError,
10092
+ cloudUnsupportedMethodError,
10093
+ cloudNotLaunchedError,
9795
10094
  ActionWsClient,
9796
10095
  collectLocalSelectorCacheEntries,
9797
- RemoteCdpClient,
9798
- RemoteSessionClient,
10096
+ CloudCdpClient,
10097
+ CloudSessionClient,
9799
10098
  Opensteer
9800
10099
  };