opensteer 0.8.18 → 0.9.0

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/cli/bin.cjs CHANGED
@@ -4001,7 +4001,7 @@ var init_requests = __esm({
4001
4001
  }
4002
4002
  );
4003
4003
  opensteerSessionFetchTransportSchema = enumSchema(
4004
- ["auto", "direct", "matched-tls", "page"],
4004
+ ["auto", "direct", "matched-tls", "context", "page"],
4005
4005
  {
4006
4006
  title: "OpensteerSessionFetchTransport"
4007
4007
  }
@@ -9501,6 +9501,7 @@ var init_semantic = __esm({
9501
9501
  case "direct":
9502
9502
  return [];
9503
9503
  case "matched-tls":
9504
+ case "context":
9504
9505
  return ["inspect.cookies"];
9505
9506
  case "page":
9506
9507
  return ["pages.manage"];
@@ -10521,13 +10522,6 @@ var init_saved_store = __esm({
10521
10522
  }
10522
10523
  async save(records, options) {
10523
10524
  const database = await this.requireDatabase();
10524
- const readExisting = database.prepare(`
10525
- SELECT record_id
10526
- FROM saved_network_records
10527
- WHERE session_ref = @session_ref
10528
- AND page_ref_key = @page_ref_key
10529
- AND request_id = @request_id
10530
- `);
10531
10525
  const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
10532
10526
  const insertTag = database.prepare(`
10533
10527
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
@@ -10538,14 +10532,8 @@ var init_saved_store = __esm({
10538
10532
  for (const entry of records) {
10539
10533
  const url = new URL(entry.record.url);
10540
10534
  const pageRefKey = entry.record.pageRef ?? "";
10541
- const existing = readExisting.get({
10542
- session_ref: entry.record.sessionRef,
10543
- page_ref_key: pageRefKey,
10544
- request_id: entry.record.requestId
10545
- }) ?? void 0;
10546
- const recordId = existing?.record_id ?? entry.recordId;
10547
10535
  upsertRecord.run({
10548
- record_id: recordId,
10536
+ record_id: entry.recordId,
10549
10537
  request_id: entry.record.requestId,
10550
10538
  session_ref: entry.record.sessionRef,
10551
10539
  page_ref: entry.record.pageRef ?? null,
@@ -10590,7 +10578,7 @@ var init_saved_store = __esm({
10590
10578
  }
10591
10579
  for (const currentTag of tags) {
10592
10580
  const result = insertTag.run({
10593
- record_id: recordId,
10581
+ record_id: entry.recordId,
10594
10582
  tag: currentTag
10595
10583
  });
10596
10584
  savedCount += result.changes ?? 0;
@@ -10693,6 +10681,49 @@ var init_saved_store = __esm({
10693
10681
  return cleared;
10694
10682
  });
10695
10683
  }
10684
+ async *iterateBatches(options = {}) {
10685
+ const database = await this.requireDatabase();
10686
+ const batchSize = Math.max(1, Math.min(options.batchSize ?? 500, 1e3));
10687
+ let cursor;
10688
+ while (true) {
10689
+ const rows = database.prepare(
10690
+ `
10691
+ SELECT
10692
+ r.*,
10693
+ GROUP_CONCAT(t.tag, '${TAG_DELIMITER}') AS tags
10694
+ FROM saved_network_records r
10695
+ LEFT JOIN saved_network_tags t
10696
+ ON t.record_id = r.record_id
10697
+ ${cursor === void 0 ? "" : "WHERE r.saved_at > ? OR (r.saved_at = ? AND r.record_id > ?)"}
10698
+ GROUP BY r.record_id
10699
+ ORDER BY r.saved_at ASC, r.record_id ASC
10700
+ LIMIT ?
10701
+ `
10702
+ ).all(
10703
+ ...cursor === void 0 ? [] : [cursor.savedAt, cursor.savedAt, cursor.recordId],
10704
+ batchSize
10705
+ );
10706
+ if (rows.length === 0) {
10707
+ return;
10708
+ }
10709
+ yield rows.map((row) => inflateSavedNetworkRow(row, options.includeBodies ?? true));
10710
+ const lastRow = rows.at(-1);
10711
+ if (lastRow === void 0) {
10712
+ return;
10713
+ }
10714
+ cursor = {
10715
+ savedAt: lastRow.saved_at,
10716
+ recordId: lastRow.record_id
10717
+ };
10718
+ }
10719
+ }
10720
+ close() {
10721
+ if (this.database !== void 0) {
10722
+ closeSqliteDatabase(this.database);
10723
+ this.database = void 0;
10724
+ this.databaseInitialization = void 0;
10725
+ }
10726
+ }
10696
10727
  async requireDatabase() {
10697
10728
  if (this.database) {
10698
10729
  return this.database;
@@ -10777,15 +10808,6 @@ var init_saved_store = __esm({
10777
10808
  saved_at INTEGER NOT NULL
10778
10809
  );
10779
10810
 
10780
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
10781
- ON saved_network_records (session_ref, page_ref_key, request_id);
10782
-
10783
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
10784
- ON saved_network_records (saved_at DESC);
10785
-
10786
- CREATE INDEX IF NOT EXISTS saved_network_records_capture
10787
- ON saved_network_records (capture);
10788
-
10789
10811
  CREATE TABLE IF NOT EXISTS saved_network_tags (
10790
10812
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
10791
10813
  tag TEXT NOT NULL,
@@ -10794,6 +10816,19 @@ var init_saved_store = __esm({
10794
10816
 
10795
10817
  CREATE INDEX IF NOT EXISTS saved_network_tags_tag
10796
10818
  ON saved_network_tags (tag);
10819
+ `);
10820
+ database.exec(`DROP INDEX IF EXISTS saved_network_records_scope_request`);
10821
+ database.exec(`
10822
+ CREATE INDEX IF NOT EXISTS saved_network_records_scope_request
10823
+ ON saved_network_records (session_ref, page_ref_key, request_id)
10824
+ `);
10825
+ database.exec(`
10826
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
10827
+ ON saved_network_records (saved_at DESC)
10828
+ `);
10829
+ database.exec(`
10830
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
10831
+ ON saved_network_records (capture)
10797
10832
  `);
10798
10833
  this.ensureColumn(
10799
10834
  database,
@@ -11933,8 +11968,14 @@ async function launchOwnedBrowser(input) {
11933
11968
  await ensureDirectory(input.userDataDir);
11934
11969
  await clearChromeSingletonEntries(input.userDataDir);
11935
11970
  await sanitizeChromeProfile(input.userDataDir);
11971
+ const requestedRemoteDebuggingPort = readRequestedRemoteDebuggingPort(input.launch?.args);
11936
11972
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
11937
- const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
11973
+ const args = buildChromeArgs(
11974
+ input.userDataDir,
11975
+ input.launch,
11976
+ input.viewport,
11977
+ requestedRemoteDebuggingPort
11978
+ );
11938
11979
  const child = child_process.spawn(executablePath, args, {
11939
11980
  stdio: ["ignore", "ignore", "pipe"],
11940
11981
  detached: process.platform !== "win32"
@@ -11950,7 +11991,8 @@ async function launchOwnedBrowser(input) {
11950
11991
  userDataDir: input.userDataDir,
11951
11992
  timeoutMs: input.launch?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
11952
11993
  childExited: async () => child.exitCode,
11953
- stderrLines
11994
+ stderrLines,
11995
+ ...requestedRemoteDebuggingPort !== void 0 && requestedRemoteDebuggingPort > 0 ? { requestedRemoteDebuggingPort } : {}
11954
11996
  }).catch(async (error) => {
11955
11997
  child.kill("SIGKILL");
11956
11998
  throw error;
@@ -11961,10 +12003,10 @@ async function launchOwnedBrowser(input) {
11961
12003
  executablePath
11962
12004
  };
11963
12005
  }
11964
- function buildChromeArgs(userDataDir, launch, viewport) {
12006
+ function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebuggingPort) {
11965
12007
  const isHeadless = launch?.headless ?? true;
11966
12008
  const args = [
11967
- "--remote-debugging-port=0",
12009
+ ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
11968
12010
  "--no-first-run",
11969
12011
  "--no-default-browser-check",
11970
12012
  "--disable-blink-features=AutomationControlled",
@@ -12015,6 +12057,15 @@ async function waitForDevToolsEndpoint(input) {
12015
12057
  return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
12016
12058
  }
12017
12059
  }
12060
+ if (input.requestedRemoteDebuggingPort !== void 0) {
12061
+ const endpoint = await tryInspectRemoteDebuggingPort(
12062
+ input.requestedRemoteDebuggingPort,
12063
+ input.timeoutMs
12064
+ );
12065
+ if (endpoint !== void 0) {
12066
+ return endpoint;
12067
+ }
12068
+ }
12018
12069
  const exitCode = await input.childExited();
12019
12070
  if (exitCode !== null) {
12020
12071
  throw new Error(formatChromeLaunchError(input.stderrLines));
@@ -12023,6 +12074,52 @@ async function waitForDevToolsEndpoint(input) {
12023
12074
  }
12024
12075
  throw new Error(formatChromeLaunchError(input.stderrLines));
12025
12076
  }
12077
+ function readRequestedRemoteDebuggingPort(args) {
12078
+ if (args === void 0 || args.length === 0) {
12079
+ return void 0;
12080
+ }
12081
+ let explicitFlagFound = false;
12082
+ let port;
12083
+ for (let index = 0; index < args.length; index += 1) {
12084
+ const entry = args[index];
12085
+ if (entry === "--remote-debugging-port") {
12086
+ explicitFlagFound = true;
12087
+ const next = args[index + 1];
12088
+ if (next !== void 0) {
12089
+ port = parseRemoteDebuggingPort(next);
12090
+ index += 1;
12091
+ }
12092
+ continue;
12093
+ }
12094
+ if (entry.startsWith("--remote-debugging-port=")) {
12095
+ explicitFlagFound = true;
12096
+ port = parseRemoteDebuggingPort(entry.slice("--remote-debugging-port=".length));
12097
+ }
12098
+ }
12099
+ return explicitFlagFound ? port : void 0;
12100
+ }
12101
+ function parseRemoteDebuggingPort(value) {
12102
+ const trimmed = value.trim();
12103
+ if (!/^\d+$/.test(trimmed)) {
12104
+ return void 0;
12105
+ }
12106
+ const parsed = Number.parseInt(trimmed, 10);
12107
+ if (!Number.isInteger(parsed) || parsed < 0) {
12108
+ return void 0;
12109
+ }
12110
+ return parsed;
12111
+ }
12112
+ async function tryInspectRemoteDebuggingPort(port, timeoutMs) {
12113
+ try {
12114
+ const inspected = await inspectCdpEndpoint({
12115
+ endpoint: `http://127.0.0.1:${String(port)}`,
12116
+ timeoutMs: Math.min(2e3, timeoutMs)
12117
+ });
12118
+ return inspected.endpoint;
12119
+ } catch {
12120
+ return void 0;
12121
+ }
12122
+ }
12026
12123
  function formatChromeLaunchError(stderrLines) {
12027
12124
  const collapsed = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
12028
12125
  if (collapsed.length === 0) {
@@ -13143,6 +13240,13 @@ var init_profile_sync = __esm({
13143
13240
  });
13144
13241
 
13145
13242
  // src/cloud/client.ts
13243
+ function createRequestSignal(options) {
13244
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
13245
+ if (options.signal === void 0) {
13246
+ return timeoutSignal;
13247
+ }
13248
+ return AbortSignal.any([options.signal, timeoutSignal]);
13249
+ }
13146
13250
  function delay(ms) {
13147
13251
  return new Promise((resolve4) => {
13148
13252
  setTimeout(resolve4, ms);
@@ -13176,21 +13280,24 @@ var init_client = __esm({
13176
13280
  getConfig() {
13177
13281
  return this.config;
13178
13282
  }
13179
- async createSession(input = {}) {
13180
- const response = await this.request("/v1/sessions", {
13181
- method: "POST",
13182
- body: {
13183
- ...input.name === void 0 ? {} : { name: input.name },
13184
- ...input.browser === void 0 ? {} : { browser: input.browser },
13185
- ...input.context === void 0 ? {} : { context: input.context },
13186
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
13187
- ...input.observability === void 0 ? {} : { observability: input.observability },
13188
- ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
13189
- ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
13190
- ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
13191
- ...input.locality === void 0 ? {} : { locality: input.locality }
13192
- }
13193
- });
13283
+ async createSession(input = {}, options = {}) {
13284
+ const response = await this.request(
13285
+ "/v1/sessions",
13286
+ {
13287
+ method: "POST",
13288
+ body: {
13289
+ ...input.name === void 0 ? {} : { name: input.name },
13290
+ ...input.browser === void 0 ? {} : { browser: input.browser },
13291
+ ...input.context === void 0 ? {} : { context: input.context },
13292
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
13293
+ ...input.observability === void 0 ? {} : { observability: input.observability },
13294
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
13295
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
13296
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath }
13297
+ }
13298
+ },
13299
+ options
13300
+ );
13194
13301
  return await response.json();
13195
13302
  }
13196
13303
  async listSessions() {
@@ -13199,19 +13306,27 @@ var init_client = __esm({
13199
13306
  });
13200
13307
  return response.json();
13201
13308
  }
13202
- async getSession(sessionId) {
13203
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
13204
- method: "GET"
13205
- });
13309
+ async getSession(sessionId, options = {}) {
13310
+ const response = await this.request(
13311
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
13312
+ {
13313
+ method: "GET"
13314
+ },
13315
+ options
13316
+ );
13206
13317
  return await response.json();
13207
13318
  }
13208
- async issueAccess(sessionId, capabilities) {
13209
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/access`, {
13210
- method: "POST",
13211
- body: {
13212
- capabilities
13213
- }
13214
- });
13319
+ async issueAccess(sessionId, capabilities, options = {}) {
13320
+ const response = await this.request(
13321
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
13322
+ {
13323
+ method: "POST",
13324
+ body: {
13325
+ capabilities
13326
+ }
13327
+ },
13328
+ options
13329
+ );
13215
13330
  return await response.json();
13216
13331
  }
13217
13332
  async getSessionRecording(sessionId) {
@@ -13293,28 +13408,17 @@ var init_client = __esm({
13293
13408
  async syncBrowserProfileCookies(input) {
13294
13409
  return syncBrowserProfileCookies(this, input);
13295
13410
  }
13296
- async importSelectorCache(entries) {
13297
- const response = await this.request("/selector-cache/import", {
13411
+ async importDescriptors(entries) {
13412
+ const response = await this.request("/registry/descriptors/import", {
13298
13413
  method: "POST",
13299
- body: {
13300
- entries: entries.map((entry) => ({
13301
- workspace: entry.workspace,
13302
- method: entry.method,
13303
- persistHash: entry.persistHash,
13304
- ...entry.persist === void 0 ? {} : { persist: entry.persist },
13305
- path: entry.path,
13306
- ...entry.schemaHash === void 0 ? {} : { schemaHash: entry.schemaHash },
13307
- createdAt: entry.createdAt,
13308
- updatedAt: entry.updatedAt
13309
- }))
13310
- }
13414
+ body: { entries }
13311
13415
  });
13312
13416
  return await response.json();
13313
13417
  }
13314
- async importDescriptors(entries) {
13315
- const response = await this.request("/registry/descriptors/import", {
13418
+ async importRequestPlans(input) {
13419
+ const response = await this.request("/registry/request-plans/import", {
13316
13420
  method: "POST",
13317
- body: { entries }
13421
+ body: input
13318
13422
  });
13319
13423
  return await response.json();
13320
13424
  }
@@ -13327,7 +13431,7 @@ var init_client = __esm({
13327
13431
  "content-type": "application/json; charset=utf-8"
13328
13432
  };
13329
13433
  }
13330
- async request(pathname, init) {
13434
+ async request(pathname, init, options = {}) {
13331
13435
  const url = `${this.config.baseUrl}${pathname}`;
13332
13436
  let response;
13333
13437
  try {
@@ -13335,7 +13439,7 @@ var init_client = __esm({
13335
13439
  method: init.method,
13336
13440
  headers: this.buildHeaders(),
13337
13441
  ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
13338
- signal: AbortSignal.timeout(3e4)
13442
+ signal: createRequestSignal(options)
13339
13443
  });
13340
13444
  } catch (error) {
13341
13445
  throw wrapCloudFetchError(error, {
@@ -13415,11 +13519,26 @@ var init_package = __esm({
13415
13519
  "../runtime-core/package.json"() {
13416
13520
  package_default2 = {
13417
13521
  name: "@opensteer/runtime-core",
13418
- version: "0.1.7",
13522
+ version: "0.2.0",
13419
13523
  description: "Shared semantic runtime for Opensteer local and cloud execution.",
13420
13524
  license: "MIT",
13421
13525
  type: "module",
13422
13526
  sideEffects: false,
13527
+ repository: {
13528
+ type: "git",
13529
+ url: "git+https://github.com/steerlabs/opensteer.git",
13530
+ directory: "packages/runtime-core"
13531
+ },
13532
+ bugs: {
13533
+ url: "https://github.com/steerlabs/opensteer/issues"
13534
+ },
13535
+ homepage: "https://github.com/steerlabs/opensteer/tree/main/packages/runtime-core#readme",
13536
+ keywords: [
13537
+ "opensteer",
13538
+ "runtime",
13539
+ "agents",
13540
+ "browser-automation"
13541
+ ],
13423
13542
  engines: {
13424
13543
  node: ">=22"
13425
13544
  },
@@ -13569,7 +13688,6 @@ var init_defaults = __esm({
13569
13688
  "dom.extract": 15e3,
13570
13689
  "network.query": 15e3,
13571
13690
  "network.detail": 15e3,
13572
- "network.replay": 3e4,
13573
13691
  "scripts.capture": 15e3,
13574
13692
  "session.cookies": 1e4,
13575
13693
  "session.storage": 1e4,
@@ -22729,6 +22847,21 @@ function buildEngineNetworkRecordFilters(input) {
22729
22847
  function normalizeNetworkStatusFilter(status) {
22730
22848
  return String(status);
22731
22849
  }
22850
+ function normalizeNetworkQueryInput(input) {
22851
+ return {
22852
+ ...input,
22853
+ ...input.recordId === void 0 ? {} : { recordId: normalizeNetworkRecordId(input.recordId) },
22854
+ ...input.before === void 0 ? {} : { before: normalizeNetworkRecordId(input.before) },
22855
+ ...input.after === void 0 ? {} : { after: normalizeNetworkRecordId(input.after) }
22856
+ };
22857
+ }
22858
+ function normalizeNetworkRecordId(recordId) {
22859
+ const trimmed = recordId.trim();
22860
+ if (trimmed.length === 0 || trimmed.startsWith("record:")) {
22861
+ return trimmed;
22862
+ }
22863
+ return `record:${trimmed}`;
22864
+ }
22732
22865
  function resolveLiveQueryRequestIds(input, history) {
22733
22866
  const requestIdCandidates = [];
22734
22867
  if (input.recordId !== void 0) {
@@ -22926,6 +23059,20 @@ function jsonStructureShape(value) {
22926
23059
  }
22927
23060
  return typeof value;
22928
23061
  }
23062
+ function resolveReplayProbeAttemptTimeoutMs(input) {
23063
+ const attemptCapMs = input.recommendedFound ? REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS : REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS;
23064
+ const clampedRemaining = input.remainingMs === void 0 ? void 0 : Math.max(0, input.remainingMs);
23065
+ if (clampedRemaining === 0) {
23066
+ return 0;
23067
+ }
23068
+ if (clampedRemaining === void 0) {
23069
+ return attemptCapMs;
23070
+ }
23071
+ const sliceMs = Math.floor(clampedRemaining / Math.max(1, input.transportsRemaining));
23072
+ const minimumBudgetAffordable = clampedRemaining >= REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS * input.transportsRemaining;
23073
+ const attemptBudgetMs = minimumBudgetAffordable ? Math.max(REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, sliceMs) : sliceMs;
23074
+ return Math.min(clampedRemaining, attemptCapMs, Math.max(1, attemptBudgetMs));
23075
+ }
22929
23076
  function filterNetworkSummaryRecords(records, input) {
22930
23077
  return records.filter((record) => {
22931
23078
  if (record.record.resourceType === "preflight" || record.record.method === "OPTIONS") {
@@ -23098,10 +23245,10 @@ function extractGraphqlOperationName(queryText) {
23098
23245
  function shouldShowRequestBody(method) {
23099
23246
  return !["GET", "HEAD", "DELETE", "OPTIONS"].includes(method.trim().toUpperCase());
23100
23247
  }
23101
- function buildStructuredBodyPreview(body, headers) {
23248
+ function buildStructuredBodyPreview(body, headers, options = {}) {
23102
23249
  const contentType = headerValue(headers, "content-type") ?? body?.mimeType;
23103
23250
  const parsed = parseStructuredPayload(body, contentType);
23104
- const data = parsed === void 0 ? void 0 : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
23251
+ const data = parsed === void 0 ? void 0 : options.truncateData === false ? parsed : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
23105
23252
  return {
23106
23253
  bytes: body?.originalByteLength ?? body?.capturedByteLength ?? 0,
23107
23254
  ...contentType === void 0 ? {} : { contentType },
@@ -23314,10 +23461,12 @@ function resolveSessionFetchTransportLadder(transport) {
23314
23461
  return ["direct-http"];
23315
23462
  case "matched-tls":
23316
23463
  return ["matched-tls"];
23464
+ case "context":
23465
+ return ["context-http"];
23317
23466
  case "page":
23318
23467
  return ["page-http"];
23319
23468
  case "auto":
23320
- return ["direct-http", "matched-tls", "page-http"];
23469
+ return ["direct-http", "matched-tls", "context-http", "page-http"];
23321
23470
  }
23322
23471
  }
23323
23472
  function detectChallengeNoteFromRecord(record) {
@@ -23462,6 +23611,12 @@ function diffStorageSnapshot(left, right) {
23462
23611
  function normalizeRuntimeErrorMessage(error) {
23463
23612
  return error instanceof Error ? error.message : String(error);
23464
23613
  }
23614
+ function normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs) {
23615
+ if (attemptTimeoutMs !== void 0 && error instanceof OpensteerProtocolError && error.code === "timeout") {
23616
+ return `${transport} probe exceeded ${String(attemptTimeoutMs)}ms`;
23617
+ }
23618
+ return normalizeRuntimeErrorMessage(error);
23619
+ }
23465
23620
  function applyBrowserCookiesToTransportRequest(request, cookies) {
23466
23621
  if (cookies.length === 0) {
23467
23622
  return request;
@@ -23836,7 +23991,7 @@ function screenshotMediaType(format2) {
23836
23991
  return "image/webp";
23837
23992
  }
23838
23993
  }
23839
- var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, PENDING_OPERATION_EVENT_CAPTURE_LIMIT, PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS, OpensteerSessionRuntime, DEFAULT_STATE_GLOBAL_NAMES, REPLAY_TRANSPORT_LADDER, CAPTURE_PAGE_STATE_SCRIPT, INTERACTION_RECORDER_INSTALL_SCRIPT, INTERACTION_RECORDER_READ_SCRIPT, INTERACTION_REPLAY_SCRIPT, PAGE_HTTP_REQUEST_SCRIPT;
23994
+ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS, PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, PENDING_OPERATION_EVENT_CAPTURE_LIMIT, PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS, REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS, REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS, OpensteerSessionRuntime, DEFAULT_STATE_GLOBAL_NAMES, REPLAY_TRANSPORT_LADDER, CAPTURE_PAGE_STATE_SCRIPT, INTERACTION_RECORDER_INSTALL_SCRIPT, INTERACTION_RECORDER_READ_SCRIPT, INTERACTION_REPLAY_SCRIPT, PAGE_HTTP_REQUEST_SCRIPT;
23840
23995
  var init_runtime3 = __esm({
23841
23996
  "../runtime-core/src/sdk/runtime.ts"() {
23842
23997
  init_src();
@@ -23872,6 +24027,9 @@ var init_runtime3 = __esm({
23872
24027
  PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
23873
24028
  PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
23874
24029
  PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
24030
+ REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
24031
+ REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS = 15e3;
24032
+ REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS = 5e3;
23875
24033
  OpensteerSessionRuntime = class {
23876
24034
  workspace;
23877
24035
  rootPath;
@@ -24733,26 +24891,27 @@ var init_runtime3 = __esm({
24733
24891
  }
24734
24892
  }
24735
24893
  async queryNetwork(input = {}, options = {}) {
24736
- assertValidSemanticOperationInput("network.query", input);
24894
+ const normalizedInput = normalizeNetworkQueryInput(input);
24895
+ assertValidSemanticOperationInput("network.query", normalizedInput);
24737
24896
  const root = await this.ensureRoot();
24738
24897
  const startedAt = Date.now();
24739
24898
  try {
24740
24899
  const output = await this.runWithOperationTimeout(
24741
24900
  "network.query",
24742
24901
  async (timeout) => {
24743
- await this.syncPersistedNetworkSelection(timeout, input, {
24902
+ await this.syncPersistedNetworkSelection(timeout, normalizedInput, {
24744
24903
  includeBodies: false
24745
24904
  });
24746
24905
  const rawRecords = await timeout.runStep(
24747
24906
  () => root.registry.savedNetwork.query({
24748
- ...this.toSavedNetworkQueryInput(input),
24749
- limit: Math.max(input.limit ?? 50, 1e3)
24907
+ ...this.toSavedNetworkQueryInput(normalizedInput),
24908
+ limit: Math.max(normalizedInput.limit ?? 50, 1e3)
24750
24909
  })
24751
24910
  );
24752
- const filtered = filterNetworkSummaryRecords(rawRecords, input);
24911
+ const filtered = filterNetworkSummaryRecords(rawRecords, normalizedInput);
24753
24912
  const sorted = sortPersistedNetworkRecordsChronologically(filtered);
24754
- const sliced = sliceNetworkSummaryWindow(sorted, input);
24755
- const limited = sliced.slice(0, Math.max(1, Math.min(input.limit ?? 50, 200)));
24913
+ const sliced = sliceNetworkSummaryWindow(sorted, normalizedInput);
24914
+ const limited = sliced.slice(0, Math.max(1, Math.min(normalizedInput.limit ?? 50, 200)));
24756
24915
  const summaries = await this.buildNetworkSummaryRecords(limited, timeout);
24757
24916
  return {
24758
24917
  records: summaries
@@ -24766,9 +24925,9 @@ var init_runtime3 = __esm({
24766
24925
  completedAt: Date.now(),
24767
24926
  outcome: "ok",
24768
24927
  data: {
24769
- limit: input.limit ?? 50,
24770
- ...input.capture === void 0 ? {} : { capture: input.capture },
24771
- ...input.json === true ? { json: true } : {},
24928
+ limit: normalizedInput.limit ?? 50,
24929
+ ...normalizedInput.capture === void 0 ? {} : { capture: normalizedInput.capture },
24930
+ ...normalizedInput.json === true ? { json: true } : {},
24772
24931
  count: output.records.length
24773
24932
  },
24774
24933
  context: buildRuntimeTraceContext({
@@ -24793,12 +24952,13 @@ var init_runtime3 = __esm({
24793
24952
  }
24794
24953
  }
24795
24954
  async getNetworkDetail(input, options = {}) {
24955
+ const normalizedRecordId = normalizeNetworkRecordId(input.recordId);
24796
24956
  const startedAt = Date.now();
24797
24957
  try {
24798
24958
  const output = await this.runWithOperationTimeout(
24799
24959
  "network.detail",
24800
24960
  async (timeout) => {
24801
- const record = await this.resolveNetworkRecordByRecordId(input.recordId, timeout, {
24961
+ const record = await this.resolveNetworkRecordByRecordId(normalizedRecordId, timeout, {
24802
24962
  includeBodies: true,
24803
24963
  redactSecretHeaders: false
24804
24964
  });
@@ -24817,8 +24977,8 @@ var init_runtime3 = __esm({
24817
24977
  completedAt: Date.now(),
24818
24978
  outcome: "ok",
24819
24979
  data: {
24820
- recordId: input.recordId,
24821
- status: output.summary.status,
24980
+ recordId: normalizedRecordId,
24981
+ ...output.summary.status === void 0 ? {} : { status: output.summary.status },
24822
24982
  url: output.summary.url
24823
24983
  },
24824
24984
  context: buildRuntimeTraceContext({
@@ -26222,7 +26382,9 @@ var init_runtime3 = __esm({
26222
26382
  ...graphql.persisted === void 0 ? {} : { persisted: graphql.persisted },
26223
26383
  ...graphqlVariables === void 0 ? {} : { variables: graphqlVariables }
26224
26384
  };
26225
- const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders) : void 0;
26385
+ const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders, {
26386
+ truncateData: false
26387
+ }) : void 0;
26226
26388
  const responseBody = record.record.responseBody === void 0 ? void 0 : buildStructuredBodyPreview(record.record.responseBody, record.record.responseHeaders);
26227
26389
  const notes = detectNetworkRecordNotes(record);
26228
26390
  return {
@@ -26252,8 +26414,18 @@ var init_runtime3 = __esm({
26252
26414
  let recommended;
26253
26415
  for (const transport of REPLAY_TRANSPORT_LADDER) {
26254
26416
  const attemptStartedAt = Date.now();
26417
+ const attemptTimeoutMs = resolveReplayProbeAttemptTimeoutMs({
26418
+ remainingMs: timeout.remainingMs(),
26419
+ transportsRemaining: REPLAY_TRANSPORT_LADDER.length - attempts.length,
26420
+ recommendedFound: recommended !== void 0
26421
+ });
26255
26422
  try {
26256
- const output = await this.executeReplayTransportAttempt(transport, request, timeout);
26423
+ const output = await this.executeReplayTransportAttemptWithinBudget(
26424
+ transport,
26425
+ request,
26426
+ timeout,
26427
+ attemptTimeoutMs
26428
+ );
26257
26429
  const ok = matchesSuccessFingerprintFromProtocolResponse(output.response, fingerprint);
26258
26430
  attempts.push({
26259
26431
  transport,
@@ -26269,7 +26441,7 @@ var init_runtime3 = __esm({
26269
26441
  transport,
26270
26442
  ok: false,
26271
26443
  durationMs: Date.now() - attemptStartedAt,
26272
- error: normalizeRuntimeErrorMessage(error)
26444
+ error: normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs)
26273
26445
  });
26274
26446
  }
26275
26447
  }
@@ -26473,6 +26645,23 @@ var init_runtime3 = __esm({
26473
26645
  }
26474
26646
  }
26475
26647
  }
26648
+ async executeReplayTransportAttemptWithinBudget(transport, request, timeout, attemptTimeoutMs) {
26649
+ if (attemptTimeoutMs === void 0) {
26650
+ return this.executeReplayTransportAttempt(transport, request, timeout);
26651
+ }
26652
+ return runWithPolicyTimeout(
26653
+ {
26654
+ resolveTimeoutMs() {
26655
+ return attemptTimeoutMs;
26656
+ }
26657
+ },
26658
+ {
26659
+ operation: timeout.operation,
26660
+ signal: timeout.signal
26661
+ },
26662
+ (attemptTimeout) => this.executeReplayTransportAttempt(transport, request, attemptTimeout)
26663
+ );
26664
+ }
26476
26665
  async executeFetchTransportAttempt(transport, request, timeout, input) {
26477
26666
  let prepared = finalizeMaterializedTransportRequest(request, transport);
26478
26667
  if (input.cookies !== false && transport === "direct-http" && this.currentBinding() !== void 0) {
@@ -27387,10 +27576,15 @@ var init_runtime3 = __esm({
27387
27576
  return this.observationSessionId ?? this.sessionRef;
27388
27577
  }
27389
27578
  runWithOperationTimeout(operation, callback, options = {}) {
27579
+ const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
27580
+ resolveTimeoutMs() {
27581
+ return options.timeoutMs;
27582
+ }
27583
+ };
27390
27584
  const existingCollector = this.operationEventStorage.getStore();
27391
27585
  if (existingCollector !== void 0) {
27392
27586
  return runWithPolicyTimeout(
27393
- this.policy.timeout,
27587
+ timeoutPolicy,
27394
27588
  {
27395
27589
  operation,
27396
27590
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -27403,7 +27597,7 @@ var init_runtime3 = __esm({
27403
27597
  return this.operationEventStorage.run(collector, async () => {
27404
27598
  try {
27405
27599
  return await runWithPolicyTimeout(
27406
- this.policy.timeout,
27600
+ timeoutPolicy,
27407
27601
  {
27408
27602
  operation,
27409
27603
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -29707,6 +29901,20 @@ var init_src3 = __esm({
29707
29901
  init_recorder();
29708
29902
  }
29709
29903
  });
29904
+
29905
+ // src/policy/index.ts
29906
+ var init_policy2 = __esm({
29907
+ "src/policy/index.ts"() {
29908
+ init_policy();
29909
+ }
29910
+ });
29911
+ function createRequestSignal2(options) {
29912
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
29913
+ if (options.signal === void 0) {
29914
+ return timeoutSignal;
29915
+ }
29916
+ return AbortSignal.any([options.signal, timeoutSignal]);
29917
+ }
29710
29918
  function isFetchFailure(error) {
29711
29919
  if (!(error instanceof Error)) {
29712
29920
  return false;
@@ -29731,10 +29939,10 @@ var init_semantic_rest_client = __esm({
29731
29939
  constructor(connection) {
29732
29940
  this.connection = connection;
29733
29941
  }
29734
- async invoke(operation, input) {
29735
- return this.invokeInternal(operation, input, false);
29942
+ async invoke(operation, input, options = {}) {
29943
+ return this.invokeInternal(operation, input, false, options);
29736
29944
  }
29737
- async invokeInternal(operation, input, hasRetried) {
29945
+ async invokeInternal(operation, input, hasRetried, options) {
29738
29946
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
29739
29947
  if (!endpoint) {
29740
29948
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -29748,10 +29956,11 @@ var init_semantic_rest_client = __esm({
29748
29956
  method: "POST",
29749
29957
  headers: {
29750
29958
  authorization: await this.connection.getAuthorizationHeader(),
29751
- "content-type": "application/json; charset=utf-8"
29959
+ "content-type": "application/json; charset=utf-8",
29960
+ ...options.timeoutMs === void 0 ? {} : { "x-opensteer-timeout-ms": String(options.timeoutMs) }
29752
29961
  },
29753
29962
  body: JSON.stringify(request),
29754
- signal: AbortSignal.timeout(3e4)
29963
+ signal: createRequestSignal2(options)
29755
29964
  });
29756
29965
  } catch (error) {
29757
29966
  if (operation === "session.close" && isFetchFailure(error)) {
@@ -29767,7 +29976,7 @@ var init_semantic_rest_client = __esm({
29767
29976
  return envelope.data;
29768
29977
  } catch (error) {
29769
29978
  if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
29770
- return this.invokeInternal(operation, input, true);
29979
+ return this.invokeInternal(operation, input, true, options);
29771
29980
  }
29772
29981
  if (operation === "session.close" && isFetchFailure(error)) {
29773
29982
  return { closed: true };
@@ -30148,11 +30357,42 @@ var init_automation_client = __esm({
30148
30357
  }
30149
30358
  });
30150
30359
 
30151
- // src/cloud/registry-sync.ts
30152
- async function syncLocalRegistryToCloud(client, workspace, store) {
30360
+ // src/cloud/workspace-sync.ts
30361
+ async function syncLocalWorkspaceToCloud(client, workspace, store) {
30362
+ await syncDescriptorRegistryToCloud(client, workspace, store);
30363
+ await syncRequestPlansToCloud(client, workspace, store);
30364
+ }
30365
+ async function syncDescriptorRegistryToCloud(client, workspace, store) {
30153
30366
  const descriptors = await store.registry.descriptors.list();
30154
- const descriptorEntries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
30155
- await importInBatches(descriptorEntries, (entries) => client.importDescriptors(entries));
30367
+ const entries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
30368
+ await importInBatches(entries, {
30369
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
30370
+ importBatch: (batch) => client.importDescriptors(batch)
30371
+ });
30372
+ }
30373
+ async function syncRequestPlansToCloud(client, workspace, store) {
30374
+ const requestPlans = await store.registry.requestPlans.list();
30375
+ const entries = requestPlans.map((record) => toRequestPlanImportEntry(workspace, record)).filter((entry) => entry !== void 0);
30376
+ await importInBatches(entries, {
30377
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
30378
+ importBatch: (batch) => client.importRequestPlans({ entries: batch })
30379
+ });
30380
+ }
30381
+ function toRequestPlanImportEntry(workspace, record) {
30382
+ const entry = {
30383
+ workspace,
30384
+ recordId: record.id,
30385
+ key: record.key,
30386
+ version: record.version,
30387
+ contentHash: record.contentHash,
30388
+ tags: [...record.tags],
30389
+ ...record.provenance === void 0 ? {} : { provenance: record.provenance },
30390
+ ...record.freshness === void 0 ? {} : { freshness: record.freshness },
30391
+ payload: record.payload,
30392
+ createdAt: record.createdAt,
30393
+ updatedAt: record.updatedAt
30394
+ };
30395
+ return payloadByteLength({ entries: [entry] }) <= WORKSPACE_SYNC_MAX_PAYLOAD_BYTES ? entry : void 0;
30156
30396
  }
30157
30397
  function toDescriptorImportEntry(workspace, record) {
30158
30398
  return {
@@ -30168,19 +30408,19 @@ function toDescriptorImportEntry(workspace, record) {
30168
30408
  updatedAt: record.updatedAt
30169
30409
  };
30170
30410
  }
30171
- async function importInBatches(entries, importBatch) {
30411
+ async function importInBatches(entries, options) {
30172
30412
  if (entries.length === 0) {
30173
30413
  return;
30174
30414
  }
30175
- for (const batch of chunkEntries(entries)) {
30176
- await importBatch(batch);
30415
+ for (const batch of chunkEntries(entries, options.getPayloadByteLength)) {
30416
+ await options.importBatch(batch);
30177
30417
  }
30178
30418
  }
30179
- function chunkEntries(entries) {
30419
+ function chunkEntries(entries, getPayloadByteLength) {
30180
30420
  const batches = [];
30181
30421
  let currentBatch = [];
30182
30422
  for (const entry of entries) {
30183
- if (payloadByteLength([entry]) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
30423
+ if (getPayloadByteLength([entry]) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
30184
30424
  continue;
30185
30425
  }
30186
30426
  if (currentBatch.length === 0) {
@@ -30188,7 +30428,7 @@ function chunkEntries(entries) {
30188
30428
  continue;
30189
30429
  }
30190
30430
  const nextBatch = [...currentBatch, entry];
30191
- if (nextBatch.length > REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH || payloadByteLength(nextBatch) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
30431
+ if (nextBatch.length > WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH || getPayloadByteLength(nextBatch) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
30192
30432
  batches.push(currentBatch);
30193
30433
  currentBatch = [entry];
30194
30434
  continue;
@@ -30200,14 +30440,14 @@ function chunkEntries(entries) {
30200
30440
  }
30201
30441
  return batches;
30202
30442
  }
30203
- function payloadByteLength(entries) {
30204
- return Buffer.byteLength(JSON.stringify({ entries }), "utf8");
30443
+ function payloadByteLength(value) {
30444
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
30205
30445
  }
30206
- var REGISTRY_SYNC_MAX_PAYLOAD_BYTES, REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH;
30207
- var init_registry_sync = __esm({
30208
- "src/cloud/registry-sync.ts"() {
30209
- REGISTRY_SYNC_MAX_PAYLOAD_BYTES = 15e5;
30210
- REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH = 100;
30446
+ var WORKSPACE_SYNC_MAX_PAYLOAD_BYTES, WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH;
30447
+ var init_workspace_sync = __esm({
30448
+ "src/cloud/workspace-sync.ts"() {
30449
+ WORKSPACE_SYNC_MAX_PAYLOAD_BYTES = 15e5;
30450
+ WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH = 100;
30211
30451
  }
30212
30452
  });
30213
30453
  function resolveCloudBrowserProfile(cloud, input) {
@@ -30240,9 +30480,10 @@ var init_session_proxy = __esm({
30240
30480
  init_src2();
30241
30481
  init_live_session();
30242
30482
  init_root2();
30483
+ init_policy2();
30243
30484
  init_semantic_rest_client();
30244
30485
  init_automation_client();
30245
- init_registry_sync();
30486
+ init_workspace_sync();
30246
30487
  TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
30247
30488
  CloudSessionProxy = class {
30248
30489
  rootPath;
@@ -30250,14 +30491,17 @@ var init_session_proxy = __esm({
30250
30491
  cleanupRootOnClose;
30251
30492
  cloud;
30252
30493
  observability;
30494
+ policy;
30253
30495
  sessionId;
30254
30496
  semanticGrant;
30255
30497
  client;
30256
30498
  automation;
30257
30499
  workspaceStore;
30500
+ syncWorkspaceOnClose = false;
30258
30501
  constructor(cloud, options = {}) {
30259
30502
  this.cloud = cloud;
30260
30503
  this.workspace = options.workspace;
30504
+ this.policy = options.policy ?? defaultPolicy();
30261
30505
  this.observability = options.observability;
30262
30506
  this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
30263
30507
  rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
@@ -30266,14 +30510,17 @@ var init_session_proxy = __esm({
30266
30510
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
30267
30511
  }
30268
30512
  async open(input = {}) {
30269
- await this.ensureSession({
30270
- ...input.browser === void 0 ? {} : { browser: input.browser },
30271
- ...input.launch === void 0 ? {} : { launch: input.launch },
30272
- ...input.context === void 0 ? {} : { context: input.context }
30273
- });
30274
- return this.requireClient().invoke("session.open", {
30275
- ...input.url === void 0 ? {} : { url: input.url }
30276
- });
30513
+ return this.invokeSemanticOperation(
30514
+ "session.open",
30515
+ {
30516
+ ...input.url === void 0 ? {} : { url: input.url }
30517
+ },
30518
+ {
30519
+ ...input.browser === void 0 ? {} : { browser: input.browser },
30520
+ ...input.launch === void 0 ? {} : { launch: input.launch },
30521
+ ...input.context === void 0 ? {} : { context: input.context }
30522
+ }
30523
+ );
30277
30524
  }
30278
30525
  async info() {
30279
30526
  const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
@@ -30316,108 +30563,88 @@ var init_session_proxy = __esm({
30316
30563
  };
30317
30564
  }
30318
30565
  async listPages(input = {}) {
30319
- await this.ensureSession();
30320
- return this.requireClient().invoke("page.list", input);
30566
+ return this.invokeSemanticOperation("page.list", input);
30321
30567
  }
30322
30568
  async newPage(input = {}) {
30323
- await this.ensureSession();
30324
- return this.requireAutomation().invoke("page.new", input);
30569
+ return this.invokeAutomationOperation(
30570
+ "page.new",
30571
+ (automation) => automation.invoke("page.new", input)
30572
+ );
30325
30573
  }
30326
30574
  async activatePage(input) {
30327
- await this.ensureSession();
30328
- return this.requireClient().invoke("page.activate", input);
30575
+ return this.invokeSemanticOperation("page.activate", input);
30329
30576
  }
30330
30577
  async closePage(input = {}) {
30331
- await this.ensureSession();
30332
- return this.requireClient().invoke("page.close", input);
30578
+ return this.invokeSemanticOperation("page.close", input);
30333
30579
  }
30334
30580
  async goto(input) {
30335
- await this.ensureSession();
30336
- return this.requireClient().invoke("page.goto", input);
30581
+ return this.invokeSemanticOperation("page.goto", input);
30337
30582
  }
30338
30583
  async evaluate(input) {
30339
- await this.ensureSession();
30340
- return this.requireAutomation().invoke("page.evaluate", input);
30584
+ return this.invokeAutomationOperation(
30585
+ "page.evaluate",
30586
+ (automation) => automation.invoke("page.evaluate", input)
30587
+ );
30341
30588
  }
30342
30589
  async addInitScript(input) {
30343
- await this.ensureSession();
30344
- return this.requireClient().invoke("page.add-init-script", input);
30590
+ return this.invokeSemanticOperation("page.add-init-script", input);
30345
30591
  }
30346
30592
  async snapshot(input = {}) {
30347
- await this.ensureSession();
30348
- return this.requireClient().invoke("page.snapshot", input);
30593
+ return this.invokeSemanticOperation("page.snapshot", input);
30349
30594
  }
30350
30595
  async click(input) {
30351
- await this.ensureSession();
30352
- return this.requireClient().invoke("dom.click", input);
30596
+ return this.invokeSemanticOperation("dom.click", input);
30353
30597
  }
30354
30598
  async hover(input) {
30355
- await this.ensureSession();
30356
- return this.requireClient().invoke("dom.hover", input);
30599
+ return this.invokeSemanticOperation("dom.hover", input);
30357
30600
  }
30358
30601
  async input(input) {
30359
- await this.ensureSession();
30360
- return this.requireClient().invoke("dom.input", input);
30602
+ return this.invokeSemanticOperation("dom.input", input);
30361
30603
  }
30362
30604
  async scroll(input) {
30363
- await this.ensureSession();
30364
- return this.requireClient().invoke("dom.scroll", input);
30605
+ return this.invokeSemanticOperation("dom.scroll", input);
30365
30606
  }
30366
30607
  async extract(input) {
30367
- await this.ensureSession();
30368
- return this.requireClient().invoke("dom.extract", input);
30608
+ return this.invokeSemanticOperation("dom.extract", input);
30369
30609
  }
30370
30610
  async queryNetwork(input = {}) {
30371
- await this.ensureSession();
30372
- return this.requireClient().invoke("network.query", input);
30611
+ return this.invokeSemanticOperation("network.query", input);
30373
30612
  }
30374
30613
  async getNetworkDetail(input) {
30375
- await this.ensureSession();
30376
- return this.requireClient().invoke("network.detail", input);
30614
+ return this.invokeSemanticOperation("network.detail", input);
30377
30615
  }
30378
30616
  async captureInteraction(input) {
30379
- await this.ensureSession();
30380
- return this.requireClient().invoke("interaction.capture", input);
30617
+ return this.invokeSemanticOperation("interaction.capture", input);
30381
30618
  }
30382
30619
  async getInteraction(input) {
30383
- await this.ensureSession();
30384
- return this.requireClient().invoke("interaction.get", input);
30620
+ return this.invokeSemanticOperation("interaction.get", input);
30385
30621
  }
30386
30622
  async diffInteraction(input) {
30387
- await this.ensureSession();
30388
- return this.requireClient().invoke("interaction.diff", input);
30623
+ return this.invokeSemanticOperation("interaction.diff", input);
30389
30624
  }
30390
30625
  async replayInteraction(input) {
30391
- await this.ensureSession();
30392
- return this.requireClient().invoke("interaction.replay", input);
30626
+ return this.invokeSemanticOperation("interaction.replay", input);
30393
30627
  }
30394
30628
  async captureScripts(input = {}) {
30395
- await this.ensureSession();
30396
- return this.requireClient().invoke("scripts.capture", input);
30629
+ return this.invokeSemanticOperation("scripts.capture", input);
30397
30630
  }
30398
30631
  async readArtifact(input) {
30399
- await this.ensureSession();
30400
- return this.requireClient().invoke("artifact.read", input);
30632
+ return this.invokeSemanticOperation("artifact.read", input);
30401
30633
  }
30402
30634
  async beautifyScript(input) {
30403
- await this.ensureSession();
30404
- return this.requireClient().invoke("scripts.beautify", input);
30635
+ return this.invokeSemanticOperation("scripts.beautify", input);
30405
30636
  }
30406
30637
  async deobfuscateScript(input) {
30407
- await this.ensureSession();
30408
- return this.requireClient().invoke("scripts.deobfuscate", input);
30638
+ return this.invokeSemanticOperation("scripts.deobfuscate", input);
30409
30639
  }
30410
30640
  async sandboxScript(input) {
30411
- await this.ensureSession();
30412
- return this.requireClient().invoke("scripts.sandbox", input);
30641
+ return this.invokeSemanticOperation("scripts.sandbox", input);
30413
30642
  }
30414
30643
  async solveCaptcha(input) {
30415
- await this.ensureSession();
30416
- return this.requireClient().invoke("captcha.solve", input);
30644
+ return this.invokeSemanticOperation("captcha.solve", input);
30417
30645
  }
30418
30646
  async getCookies(input = {}) {
30419
- await this.ensureSession();
30420
- return this.requireClient().invoke("session.cookies", input);
30647
+ return this.invokeSemanticOperation("session.cookies", input);
30421
30648
  }
30422
30649
  async route(input) {
30423
30650
  await this.ensureSession();
@@ -30428,20 +30655,16 @@ var init_session_proxy = __esm({
30428
30655
  return this.requireAutomation().interceptScript(input);
30429
30656
  }
30430
30657
  async getStorageSnapshot(input = {}) {
30431
- await this.ensureSession();
30432
- return this.requireClient().invoke("session.storage", input);
30658
+ return this.invokeSemanticOperation("session.storage", input);
30433
30659
  }
30434
30660
  async getBrowserState(input = {}) {
30435
- await this.ensureSession();
30436
- return this.requireClient().invoke("session.state", input);
30661
+ return this.invokeSemanticOperation("session.state", input);
30437
30662
  }
30438
30663
  async fetch(input) {
30439
- await this.ensureSession();
30440
- return this.requireClient().invoke("session.fetch", input);
30664
+ return this.invokeSemanticOperation("session.fetch", input);
30441
30665
  }
30442
30666
  async computerExecute(input) {
30443
- await this.ensureSession();
30444
- return this.requireClient().invoke("computer.execute", input);
30667
+ return this.invokeSemanticOperation("computer.execute", input);
30445
30668
  }
30446
30669
  async close() {
30447
30670
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
@@ -30453,6 +30676,14 @@ var init_session_proxy = __esm({
30453
30676
  startedAt: Date.now(),
30454
30677
  updatedAt: Date.now()
30455
30678
  });
30679
+ let syncError;
30680
+ if (this.syncWorkspaceOnClose) {
30681
+ try {
30682
+ await this.syncWorkspaceToCloud();
30683
+ } catch (error) {
30684
+ syncError = error;
30685
+ }
30686
+ }
30456
30687
  try {
30457
30688
  if (session !== void 0) {
30458
30689
  await this.cloud.closeSession(session.sessionId).catch((error) => {
@@ -30473,6 +30704,9 @@ var init_session_proxy = __esm({
30473
30704
  await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
30474
30705
  }
30475
30706
  }
30707
+ if (syncError !== void 0) {
30708
+ throw syncError;
30709
+ }
30476
30710
  return { closed: true };
30477
30711
  }
30478
30712
  async disconnect() {
@@ -30480,34 +30714,38 @@ var init_session_proxy = __esm({
30480
30714
  await this.close();
30481
30715
  return;
30482
30716
  }
30717
+ let syncError;
30718
+ if (this.syncWorkspaceOnClose) {
30719
+ try {
30720
+ await this.syncWorkspaceToCloud();
30721
+ } catch (error) {
30722
+ syncError = error;
30723
+ }
30724
+ }
30483
30725
  this.client = void 0;
30484
30726
  await this.automation?.close().catch(() => void 0);
30485
30727
  this.automation = void 0;
30486
30728
  this.sessionId = void 0;
30487
30729
  this.semanticGrant = void 0;
30730
+ if (syncError !== void 0) {
30731
+ throw syncError;
30732
+ }
30488
30733
  }
30489
- async ensureSession(input = {}) {
30734
+ async ensureSession(input = {}, timeout) {
30490
30735
  if (this.client) {
30491
30736
  return;
30492
30737
  }
30493
30738
  assertSupportedCloudBrowserMode(input.browser);
30494
30739
  const localCloud = this.shouldUseLocalCloudTransport();
30740
+ this.syncWorkspaceOnClose = localCloud && this.workspace !== void 0;
30495
30741
  const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
30496
30742
  const persisted = await this.loadPersistedSession();
30497
- if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
30498
- if (localCloud) {
30499
- void this.syncRegistryToCloud();
30500
- } else {
30501
- await this.syncRegistryToCloud();
30502
- }
30743
+ if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId, timeout)) {
30744
+ await this.syncWorkspaceToCloud();
30503
30745
  this.bindClient(persisted);
30504
30746
  return;
30505
30747
  }
30506
- if (localCloud) {
30507
- void this.syncRegistryToCloud();
30508
- } else {
30509
- await this.syncRegistryToCloud();
30510
- }
30748
+ await this.syncWorkspaceToCloud();
30511
30749
  const baseCreateInput = {
30512
30750
  ...this.workspace === void 0 ? {} : { name: this.workspace },
30513
30751
  ...input.launch === void 0 ? {} : { browser: input.launch },
@@ -30519,10 +30757,12 @@ var init_session_proxy = __esm({
30519
30757
  ...baseCreateInput,
30520
30758
  sourceType: "local-cloud",
30521
30759
  sourceRef: this.workspace,
30522
- localWorkspaceRootPath: this.rootPath,
30523
- locality: "auto"
30760
+ localWorkspaceRootPath: this.rootPath
30524
30761
  } : baseCreateInput;
30525
- const session = await this.cloud.createSession(createInput);
30762
+ const session = await this.cloud.createSession(createInput, {
30763
+ signal: timeout?.signal,
30764
+ timeoutMs: timeout?.remainingMs()
30765
+ });
30526
30766
  const record = {
30527
30767
  layout: "opensteer-session",
30528
30768
  version: 1,
@@ -30535,15 +30775,12 @@ var init_session_proxy = __esm({
30535
30775
  await this.writePersistedSession(record);
30536
30776
  this.bindClient(record, session.initialGrants?.semantic);
30537
30777
  }
30538
- async syncRegistryToCloud() {
30778
+ async syncWorkspaceToCloud() {
30539
30779
  if (this.workspace === void 0) {
30540
30780
  return;
30541
30781
  }
30542
- try {
30543
- const workspaceStore = await this.ensureWorkspaceStore();
30544
- await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
30545
- } catch {
30546
- }
30782
+ const workspaceStore = await this.ensureWorkspaceStore();
30783
+ await syncLocalWorkspaceToCloud(this.cloud, this.workspace, workspaceStore);
30547
30784
  }
30548
30785
  bindClient(record, initialSemanticGrant) {
30549
30786
  this.sessionId = record.sessionId;
@@ -30577,9 +30814,12 @@ var init_session_proxy = __esm({
30577
30814
  async clearPersistedSession() {
30578
30815
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
30579
30816
  }
30580
- async isReusableCloudSession(sessionId) {
30817
+ async isReusableCloudSession(sessionId, timeout) {
30581
30818
  try {
30582
- const session = await this.cloud.getSession(sessionId);
30819
+ const session = await this.cloud.getSession(sessionId, {
30820
+ signal: timeout?.signal,
30821
+ timeoutMs: timeout?.remainingMs()
30822
+ });
30583
30823
  return session.status !== "closed" && session.status !== "failed";
30584
30824
  } catch (error) {
30585
30825
  if (isMissingCloudSessionError(error)) {
@@ -30600,14 +30840,17 @@ var init_session_proxy = __esm({
30600
30840
  }
30601
30841
  return this.automation;
30602
30842
  }
30603
- async ensureSemanticGrant(forceRefresh = false) {
30843
+ async ensureSemanticGrant(forceRefresh = false, timeout) {
30604
30844
  if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
30605
30845
  return this.semanticGrant;
30606
30846
  }
30607
30847
  if (!this.sessionId) {
30608
30848
  throw new Error("Cloud session has not been initialized.");
30609
30849
  }
30610
- const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
30850
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"], {
30851
+ signal: timeout?.signal,
30852
+ timeoutMs: timeout?.remainingMs()
30853
+ });
30611
30854
  const grant = issued.grants.semantic;
30612
30855
  if (!grant || grant.transport !== "http") {
30613
30856
  throw new Error("cloud did not issue a valid semantic grant");
@@ -30630,6 +30873,25 @@ var init_session_proxy = __esm({
30630
30873
  return false;
30631
30874
  }
30632
30875
  }
30876
+ async invokeSemanticOperation(operation, input, sessionInit = {}) {
30877
+ return this.runOperationWithPolicy(operation, async (timeout) => {
30878
+ await this.ensureSession(sessionInit, timeout);
30879
+ await this.ensureSemanticGrant(false, timeout);
30880
+ return this.requireClient().invoke(operation, input, {
30881
+ signal: timeout.signal,
30882
+ timeoutMs: timeout.remainingMs()
30883
+ });
30884
+ });
30885
+ }
30886
+ async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
30887
+ return this.runOperationWithPolicy(operation, async (timeout) => {
30888
+ await this.ensureSession(sessionInit, timeout);
30889
+ return invoke(this.requireAutomation());
30890
+ });
30891
+ }
30892
+ async runOperationWithPolicy(operation, invoke) {
30893
+ return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
30894
+ }
30633
30895
  shouldUseLocalCloudTransport() {
30634
30896
  if (this.workspace === void 0) {
30635
30897
  return false;
@@ -30761,6 +31023,7 @@ function createOpensteerSemanticRuntime(input = {}) {
30761
31023
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
30762
31024
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
30763
31025
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
31026
+ ...runtimeOptions.policy === void 0 ? {} : { policy: runtimeOptions.policy },
30764
31027
  ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
30765
31028
  ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
30766
31029
  });
@@ -31261,7 +31524,7 @@ var init_opensteer = __esm({
31261
31524
 
31262
31525
  // package.json
31263
31526
  var package_default = {
31264
- version: "0.8.18"};
31527
+ version: "0.9.0"};
31265
31528
 
31266
31529
  // src/cli/bin.ts
31267
31530
  init_browser_manager();
@@ -31308,7 +31571,7 @@ Network:
31308
31571
  network query [--capture <label>] [--url <pattern>] [--hostname <host>] [--path <path>] [--method <m>] [--status <code>] [--type <resourceType>] [--json] [--before <id>] [--after <id>] [--limit <n>]
31309
31572
  --json filters to JSON and GraphQL responses only
31310
31573
  network detail <recordId> [--probe]
31311
- fetch <url> [--method <m>] [--header key=value ...] [--query key=value ...] [--body <json>] [--body-text <text>] [--transport auto|direct|matched-tls|page] [--cookies] [--follow-redirects]
31574
+ fetch <url> [--method <m>] [--header key=value ...] [--query key=value ...] [--body <json>] [--body-text <text>] [--transport auto|direct|matched-tls|context|page] [--cookies] [--follow-redirects]
31312
31575
 
31313
31576
  Browser State:
31314
31577
  state [domain]
@@ -31724,7 +31987,7 @@ function readJsonArray(options, name) {
31724
31987
 
31725
31988
  // src/cli/operation-input.ts
31726
31989
  var CLICK_BUTTONS = /* @__PURE__ */ new Set(["left", "middle", "right"]);
31727
- var FETCH_TRANSPORTS = /* @__PURE__ */ new Set(["auto", "direct", "matched-tls", "page"]);
31990
+ var FETCH_TRANSPORTS = /* @__PURE__ */ new Set(["auto", "direct", "matched-tls", "context", "page"]);
31728
31991
  var CAPTCHA_PROVIDERS = /* @__PURE__ */ new Set(["2captcha", "capsolver"]);
31729
31992
  var CAPTCHA_TYPES = /* @__PURE__ */ new Set(["recaptcha-v2", "hcaptcha", "turnstile"]);
31730
31993
  var SANDBOX_FIDELITIES = /* @__PURE__ */ new Set(["minimal", "standard", "full"]);
@@ -32205,7 +32468,9 @@ function readClickButton(value) {
32205
32468
  }
32206
32469
  function readFetchTransport(value) {
32207
32470
  if (value === void 0 || !FETCH_TRANSPORTS.has(value)) {
32208
- throw new Error('Expected "--transport" to be one of: auto, direct, matched-tls, page.');
32471
+ throw new Error(
32472
+ 'Expected "--transport" to be one of: auto, direct, matched-tls, context, page.'
32473
+ );
32209
32474
  }
32210
32475
  return value;
32211
32476
  }
@@ -33402,7 +33667,7 @@ function formatLaneRow(input) {
33402
33667
  const provider = input.provider.padEnd(7, " ");
33403
33668
  const status = input.status.padEnd(9, " ");
33404
33669
  const summary = input.summary.padEnd(16, " ");
33405
- return `${input.marker} ${provider} ${status} ${summary}${input.detail ?? ""}`.trimEnd();
33670
+ return `${input.marker} ${provider} ${status} ${summary}${input.detail === void 0 ? "" : ` ${input.detail}`}`.trimEnd();
33406
33671
  }
33407
33672
 
33408
33673
  // src/cli/exec.ts