opensteer 0.8.18 → 0.9.1

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.
Files changed (40) hide show
  1. package/README.md +5 -5
  2. package/dist/chunk-4LP7QP2O.js +4336 -0
  3. package/dist/chunk-4LP7QP2O.js.map +1 -0
  4. package/dist/{chunk-GQ3PGLYQ.js → chunk-6PGXWW3X.js} +5142 -9634
  5. package/dist/chunk-6PGXWW3X.js.map +1 -0
  6. package/dist/chunk-BMPUL66S.js +1170 -0
  7. package/dist/chunk-BMPUL66S.js.map +1 -0
  8. package/dist/{chunk-T6TG4WO2.js → chunk-L4FWHBQJ.js} +4 -3
  9. package/dist/chunk-L4FWHBQJ.js.map +1 -0
  10. package/dist/chunk-Z53HNZ7Z.js +1800 -0
  11. package/dist/chunk-Z53HNZ7Z.js.map +1 -0
  12. package/dist/cli/bin.cjs +3533 -499
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +130 -11
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +1382 -487
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +20 -27
  19. package/dist/index.d.ts +20 -27
  20. package/dist/index.js +4 -2
  21. package/dist/local-view/public/assets/app.css +770 -0
  22. package/dist/local-view/public/assets/app.js +2053 -0
  23. package/dist/local-view/public/index.html +235 -0
  24. package/dist/local-view/serve-entry.cjs +7436 -0
  25. package/dist/local-view/serve-entry.cjs.map +1 -0
  26. package/dist/local-view/serve-entry.d.cts +1 -0
  27. package/dist/local-view/serve-entry.d.ts +1 -0
  28. package/dist/local-view/serve-entry.js +23 -0
  29. package/dist/local-view/serve-entry.js.map +1 -0
  30. package/dist/opensteer-KZCRP425.js +6 -0
  31. package/dist/{opensteer-XMCWYUH3.js.map → opensteer-KZCRP425.js.map} +1 -1
  32. package/dist/session-control-VGBFOH3Y.js +39 -0
  33. package/dist/session-control-VGBFOH3Y.js.map +1 -0
  34. package/package.json +8 -8
  35. package/skills/README.md +12 -6
  36. package/skills/opensteer/SKILL.md +275 -217
  37. package/skills/recorder/SKILL.md +1 -1
  38. package/dist/chunk-GQ3PGLYQ.js.map +0 -1
  39. package/dist/chunk-T6TG4WO2.js.map +0 -1
  40. package/dist/opensteer-XMCWYUH3.js +0 -4
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var path7 = require('path');
3
+ var path10 = require('path');
4
4
  var crypto = require('crypto');
5
5
  var promises = require('fs/promises');
6
6
  var url = require('url');
@@ -10,6 +10,7 @@ var os = require('os');
10
10
  var enginePlaywright = require('@opensteer/engine-playwright');
11
11
  var util = require('util');
12
12
  var fs = require('fs');
13
+ var module$1 = require('module');
13
14
  var zlib = require('zlib');
14
15
  var async_hooks = require('async_hooks');
15
16
  var sharp = require('sharp');
@@ -18,6 +19,7 @@ var prettier = require('prettier');
18
19
  var vm = require('vm');
19
20
  var WebSocket2 = require('ws');
20
21
 
22
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
21
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
24
 
23
25
  function _interopNamespace(e) {
@@ -38,7 +40,7 @@ function _interopNamespace(e) {
38
40
  return Object.freeze(n);
39
41
  }
40
42
 
41
- var path7__default = /*#__PURE__*/_interopDefault(path7);
43
+ var path10__default = /*#__PURE__*/_interopDefault(path10);
42
44
  var sharp__default = /*#__PURE__*/_interopDefault(sharp);
43
45
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
44
46
  var prettier__namespace = /*#__PURE__*/_interopNamespace(prettier);
@@ -60,30 +62,30 @@ function isPlainObject(value) {
60
62
  const prototype = Object.getPrototypeOf(value);
61
63
  return prototype === Object.prototype || prototype === null;
62
64
  }
63
- function canonicalizeJsonValue(value, path15) {
65
+ function canonicalizeJsonValue(value, path18) {
64
66
  if (value === null || typeof value === "string" || typeof value === "boolean") {
65
67
  return value;
66
68
  }
67
69
  if (typeof value === "number") {
68
70
  if (!Number.isFinite(value)) {
69
- throw new TypeError(`${path15} must be a finite JSON number`);
71
+ throw new TypeError(`${path18} must be a finite JSON number`);
70
72
  }
71
73
  return value;
72
74
  }
73
75
  if (Array.isArray(value)) {
74
- return value.map((entry, index) => canonicalizeJsonValue(entry, `${path15}[${index}]`));
76
+ return value.map((entry, index) => canonicalizeJsonValue(entry, `${path18}[${index}]`));
75
77
  }
76
78
  if (!isPlainObject(value)) {
77
- throw new TypeError(`${path15} must be a plain JSON object`);
79
+ throw new TypeError(`${path18} must be a plain JSON object`);
78
80
  }
79
81
  const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
80
82
  const result = {};
81
83
  for (const key of sorted) {
82
84
  const entry = value[key];
83
85
  if (entry === void 0) {
84
- throw new TypeError(`${path15}.${key} must not be undefined`);
86
+ throw new TypeError(`${path18}.${key} must not be undefined`);
85
87
  }
86
- result[key] = canonicalizeJsonValue(entry, `${path15}.${key}`);
88
+ result[key] = canonicalizeJsonValue(entry, `${path18}.${key}`);
87
89
  }
88
90
  return result;
89
91
  }
@@ -120,7 +122,7 @@ function joinStoragePath(...segments) {
120
122
  return segments.join("/");
121
123
  }
122
124
  function resolveStoragePath(rootPath, relativePath) {
123
- if (path7__default.default.isAbsolute(relativePath)) {
125
+ if (path10__default.default.isAbsolute(relativePath)) {
124
126
  throw new TypeError(`storage path ${relativePath} must be relative`);
125
127
  }
126
128
  const segments = relativePath.split("/");
@@ -132,7 +134,7 @@ function resolveStoragePath(rootPath, relativePath) {
132
134
  throw new TypeError(`storage path ${relativePath} must not contain path traversal`);
133
135
  }
134
136
  }
135
- return path7__default.default.join(rootPath, ...segments);
137
+ return path10__default.default.join(rootPath, ...segments);
136
138
  }
137
139
  async function ensureDirectory(directoryPath) {
138
140
  await promises.mkdir(directoryPath, { recursive: true });
@@ -152,7 +154,7 @@ async function writeJsonFileAtomic(filePath, value) {
152
154
  await writeTextFileAtomic(filePath, stableJsonString(value));
153
155
  }
154
156
  async function writeTextFileAtomic(filePath, value) {
155
- await ensureDirectory(path7__default.default.dirname(filePath));
157
+ await ensureDirectory(path10__default.default.dirname(filePath));
156
158
  const temporaryPath = `${filePath}.${crypto.randomUUID()}.tmp`;
157
159
  await promises.writeFile(temporaryPath, value, "utf8");
158
160
  await promises.rename(temporaryPath, filePath);
@@ -161,7 +163,7 @@ async function writeJsonFileExclusive(filePath, value) {
161
163
  await writeTextFileExclusive(filePath, stableJsonString(value));
162
164
  }
163
165
  async function writeTextFileExclusive(filePath, value) {
164
- await ensureDirectory(path7__default.default.dirname(filePath));
166
+ await ensureDirectory(path10__default.default.dirname(filePath));
165
167
  const handle = await promises.open(filePath, "wx");
166
168
  try {
167
169
  await handle.writeFile(value, "utf8");
@@ -170,7 +172,7 @@ async function writeTextFileExclusive(filePath, value) {
170
172
  }
171
173
  }
172
174
  async function writeBufferIfMissing(filePath, value) {
173
- await ensureDirectory(path7__default.default.dirname(filePath));
175
+ await ensureDirectory(path10__default.default.dirname(filePath));
174
176
  try {
175
177
  const handle = await promises.open(filePath, "wx");
176
178
  try {
@@ -204,7 +206,7 @@ function isAlreadyExistsError(error) {
204
206
  return error?.code === "EEXIST";
205
207
  }
206
208
  async function withFilesystemLock(lockPath, task) {
207
- await ensureDirectory(path7__default.default.dirname(lockPath));
209
+ await ensureDirectory(path10__default.default.dirname(lockPath));
208
210
  let attempt = 0;
209
211
  while (true) {
210
212
  try {
@@ -259,8 +261,8 @@ async function readStructuredPayload(objectPath) {
259
261
  var FilesystemArtifactStore = class {
260
262
  constructor(rootPath) {
261
263
  this.rootPath = rootPath;
262
- this.manifestsDirectory = path7__default.default.join(this.rootPath, "artifacts", "manifests");
263
- this.objectsDirectory = path7__default.default.join(this.rootPath, "artifacts", "objects", "sha256");
264
+ this.manifestsDirectory = path10__default.default.join(this.rootPath, "artifacts", "manifests");
265
+ this.objectsDirectory = path10__default.default.join(this.rootPath, "artifacts", "objects", "sha256");
264
266
  }
265
267
  manifestsDirectory;
266
268
  objectsDirectory;
@@ -485,7 +487,7 @@ var FilesystemArtifactStore = class {
485
487
  }
486
488
  }
487
489
  manifestPath(artifactId) {
488
- return path7__default.default.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
490
+ return path10__default.default.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
489
491
  }
490
492
  };
491
493
  function createArtifactStore(rootPath) {
@@ -580,31 +582,31 @@ function oneOfSchema(members, options = {}) {
580
582
  }
581
583
 
582
584
  // ../protocol/src/validation.ts
583
- function validateJsonSchema(schema, value, path15 = "$") {
584
- return validateSchemaNode(schema, value, path15);
585
+ function validateJsonSchema(schema, value, path18 = "$") {
586
+ return validateSchemaNode(schema, value, path18);
585
587
  }
586
- function validateSchemaNode(schema, value, path15) {
588
+ function validateSchemaNode(schema, value, path18) {
587
589
  const issues = [];
588
590
  if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
589
591
  issues.push({
590
- path: path15,
592
+ path: path18,
591
593
  message: `must equal ${JSON.stringify(schema.const)}`
592
594
  });
593
595
  return issues;
594
596
  }
595
597
  if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
596
598
  issues.push({
597
- path: path15,
599
+ path: path18,
598
600
  message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
599
601
  });
600
602
  return issues;
601
603
  }
602
604
  if (schema.oneOf !== void 0) {
603
- const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path15));
605
+ const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path18));
604
606
  const validBranches = branchIssues.filter((current) => current.length === 0).length;
605
607
  if (validBranches !== 1) {
606
608
  issues.push({
607
- path: path15,
609
+ path: path18,
608
610
  message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
609
611
  });
610
612
  return issues;
@@ -612,11 +614,11 @@ function validateSchemaNode(schema, value, path15) {
612
614
  }
613
615
  if (schema.anyOf !== void 0) {
614
616
  const hasMatch = schema.anyOf.some(
615
- (member) => validateSchemaNode(member, value, path15).length === 0
617
+ (member) => validateSchemaNode(member, value, path18).length === 0
616
618
  );
617
619
  if (!hasMatch) {
618
620
  issues.push({
619
- path: path15,
621
+ path: path18,
620
622
  message: "must match at least one supported shape"
621
623
  });
622
624
  return issues;
@@ -624,7 +626,7 @@ function validateSchemaNode(schema, value, path15) {
624
626
  }
625
627
  if (schema.allOf !== void 0) {
626
628
  for (const member of schema.allOf) {
627
- issues.push(...validateSchemaNode(member, value, path15));
629
+ issues.push(...validateSchemaNode(member, value, path18));
628
630
  }
629
631
  if (issues.length > 0) {
630
632
  return issues;
@@ -632,7 +634,7 @@ function validateSchemaNode(schema, value, path15) {
632
634
  }
633
635
  if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
634
636
  issues.push({
635
- path: path15,
637
+ path: path18,
636
638
  message: `must be ${describeSchemaType(schema.type)}`
637
639
  });
638
640
  return issues;
@@ -640,19 +642,19 @@ function validateSchemaNode(schema, value, path15) {
640
642
  if (typeof value === "string") {
641
643
  if (schema.minLength !== void 0 && value.length < schema.minLength) {
642
644
  issues.push({
643
- path: path15,
645
+ path: path18,
644
646
  message: `must have length >= ${String(schema.minLength)}`
645
647
  });
646
648
  }
647
649
  if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
648
650
  issues.push({
649
- path: path15,
651
+ path: path18,
650
652
  message: `must have length <= ${String(schema.maxLength)}`
651
653
  });
652
654
  }
653
655
  if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
654
656
  issues.push({
655
- path: path15,
657
+ path: path18,
656
658
  message: `must match pattern ${schema.pattern}`
657
659
  });
658
660
  }
@@ -661,25 +663,25 @@ function validateSchemaNode(schema, value, path15) {
661
663
  if (typeof value === "number") {
662
664
  if (schema.minimum !== void 0 && value < schema.minimum) {
663
665
  issues.push({
664
- path: path15,
666
+ path: path18,
665
667
  message: `must be >= ${String(schema.minimum)}`
666
668
  });
667
669
  }
668
670
  if (schema.maximum !== void 0 && value > schema.maximum) {
669
671
  issues.push({
670
- path: path15,
672
+ path: path18,
671
673
  message: `must be <= ${String(schema.maximum)}`
672
674
  });
673
675
  }
674
676
  if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
675
677
  issues.push({
676
- path: path15,
678
+ path: path18,
677
679
  message: `must be > ${String(schema.exclusiveMinimum)}`
678
680
  });
679
681
  }
680
682
  if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
681
683
  issues.push({
682
- path: path15,
684
+ path: path18,
683
685
  message: `must be < ${String(schema.exclusiveMaximum)}`
684
686
  });
685
687
  }
@@ -688,13 +690,13 @@ function validateSchemaNode(schema, value, path15) {
688
690
  if (Array.isArray(value)) {
689
691
  if (schema.minItems !== void 0 && value.length < schema.minItems) {
690
692
  issues.push({
691
- path: path15,
693
+ path: path18,
692
694
  message: `must have at least ${String(schema.minItems)} items`
693
695
  });
694
696
  }
695
697
  if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
696
698
  issues.push({
697
- path: path15,
699
+ path: path18,
698
700
  message: `must have at most ${String(schema.maxItems)} items`
699
701
  });
700
702
  }
@@ -704,7 +706,7 @@ function validateSchemaNode(schema, value, path15) {
704
706
  const key = JSON.stringify(item);
705
707
  if (seen.has(key)) {
706
708
  issues.push({
707
- path: path15,
709
+ path: path18,
708
710
  message: "must not contain duplicate items"
709
711
  });
710
712
  break;
@@ -714,7 +716,7 @@ function validateSchemaNode(schema, value, path15) {
714
716
  }
715
717
  if (schema.items !== void 0) {
716
718
  for (let index = 0; index < value.length; index += 1) {
717
- issues.push(...validateSchemaNode(schema.items, value[index], `${path15}[${String(index)}]`));
719
+ issues.push(...validateSchemaNode(schema.items, value[index], `${path18}[${String(index)}]`));
718
720
  }
719
721
  }
720
722
  return issues;
@@ -724,7 +726,7 @@ function validateSchemaNode(schema, value, path15) {
724
726
  for (const requiredKey of schema.required ?? []) {
725
727
  if (!(requiredKey in value)) {
726
728
  issues.push({
727
- path: joinObjectPath(path15, requiredKey),
729
+ path: joinObjectPath(path18, requiredKey),
728
730
  message: "is required"
729
731
  });
730
732
  }
@@ -733,13 +735,13 @@ function validateSchemaNode(schema, value, path15) {
733
735
  const propertySchema = properties[key];
734
736
  if (propertySchema !== void 0) {
735
737
  issues.push(
736
- ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path15, key))
738
+ ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path18, key))
737
739
  );
738
740
  continue;
739
741
  }
740
742
  if (schema.additionalProperties === false) {
741
743
  issues.push({
742
- path: joinObjectPath(path15, key),
744
+ path: joinObjectPath(path18, key),
743
745
  message: "is not allowed"
744
746
  });
745
747
  continue;
@@ -749,7 +751,7 @@ function validateSchemaNode(schema, value, path15) {
749
751
  ...validateSchemaNode(
750
752
  schema.additionalProperties,
751
753
  propertyValue,
752
- joinObjectPath(path15, key)
754
+ joinObjectPath(path18, key)
753
755
  )
754
756
  );
755
757
  }
@@ -993,8 +995,8 @@ function matchesNetworkRecordFilters(record, filters) {
993
995
  }
994
996
  }
995
997
  if (filters.path !== void 0) {
996
- const path15 = getParsedUrl().pathname;
997
- if (!includesCaseInsensitive(path15, filters.path)) {
998
+ const path18 = getParsedUrl().pathname;
999
+ if (!includesCaseInsensitive(path18, filters.path)) {
998
1000
  return false;
999
1001
  }
1000
1002
  }
@@ -2507,7 +2509,7 @@ objectSchema(
2507
2509
  }
2508
2510
  );
2509
2511
  var opensteerSessionFetchTransportSchema = enumSchema(
2510
- ["auto", "direct", "matched-tls", "page"],
2512
+ ["auto", "direct", "matched-tls", "context", "page"],
2511
2513
  {
2512
2514
  title: "OpensteerSessionFetchTransport"
2513
2515
  }
@@ -6860,6 +6862,7 @@ var opensteerSemanticOperationSpecificationsBase = [
6860
6862
  case "direct":
6861
6863
  return [];
6862
6864
  case "matched-tls":
6865
+ case "context":
6863
6866
  return ["inspect.cookies"];
6864
6867
  case "page":
6865
6868
  return ["pages.manage"];
@@ -6991,9 +6994,9 @@ function compareByCreatedAtAndId(left, right) {
6991
6994
  var FilesystemRegistryStore = class {
6992
6995
  constructor(rootPath, registryRelativePath) {
6993
6996
  this.registryRelativePath = registryRelativePath;
6994
- const basePath = path7__default.default.join(rootPath, ...registryRelativePath);
6995
- this.recordsDirectory = path7__default.default.join(basePath, "records");
6996
- this.indexesDirectory = path7__default.default.join(basePath, "indexes", "by-key");
6997
+ const basePath = path10__default.default.join(rootPath, ...registryRelativePath);
6998
+ this.recordsDirectory = path10__default.default.join(basePath, "records");
6999
+ this.indexesDirectory = path10__default.default.join(basePath, "indexes", "by-key");
6997
7000
  }
6998
7001
  recordsDirectory;
6999
7002
  indexesDirectory;
@@ -7062,7 +7065,7 @@ var FilesystemRegistryStore = class {
7062
7065
  async readRecordsFromDirectory() {
7063
7066
  const files = await listJsonFiles(this.recordsDirectory);
7064
7067
  const records = await Promise.all(
7065
- files.map((fileName) => readJsonFile(path7__default.default.join(this.recordsDirectory, fileName)))
7068
+ files.map((fileName) => readJsonFile(path10__default.default.join(this.recordsDirectory, fileName)))
7066
7069
  );
7067
7070
  records.sort(compareByCreatedAtAndId);
7068
7071
  return records;
@@ -7094,17 +7097,17 @@ var FilesystemRegistryStore = class {
7094
7097
  return record;
7095
7098
  }
7096
7099
  recordPath(id) {
7097
- return path7__default.default.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7100
+ return path10__default.default.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7098
7101
  }
7099
7102
  indexPath(key, version) {
7100
- return path7__default.default.join(
7103
+ return path10__default.default.join(
7101
7104
  this.indexesDirectory,
7102
7105
  encodePathSegment(key),
7103
7106
  `${encodePathSegment(version)}.json`
7104
7107
  );
7105
7108
  }
7106
7109
  writeLockPath() {
7107
- return path7__default.default.join(path7__default.default.dirname(this.recordsDirectory), ".write.lock");
7110
+ return path10__default.default.join(path10__default.default.dirname(this.recordsDirectory), ".write.lock");
7108
7111
  }
7109
7112
  };
7110
7113
  var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
@@ -7474,20 +7477,13 @@ var SqliteSavedNetworkStore = class {
7474
7477
  directoryInitialization;
7475
7478
  databaseInitialization;
7476
7479
  constructor(rootPath) {
7477
- this.databasePath = path7__default.default.join(rootPath, "registry", "saved-network.sqlite");
7480
+ this.databasePath = path10__default.default.join(rootPath, "registry", "saved-network.sqlite");
7478
7481
  }
7479
7482
  async initialize() {
7480
7483
  await this.ensureDatabaseDirectory();
7481
7484
  }
7482
7485
  async save(records, options) {
7483
7486
  const database = await this.requireDatabase();
7484
- const readExisting = database.prepare(`
7485
- SELECT record_id
7486
- FROM saved_network_records
7487
- WHERE session_ref = @session_ref
7488
- AND page_ref_key = @page_ref_key
7489
- AND request_id = @request_id
7490
- `);
7491
7487
  const upsertRecord = database.prepare(buildSavedNetworkUpsertSql(options.bodyWriteMode));
7492
7488
  const insertTag = database.prepare(`
7493
7489
  INSERT OR IGNORE INTO saved_network_tags (record_id, tag)
@@ -7498,14 +7494,8 @@ var SqliteSavedNetworkStore = class {
7498
7494
  for (const entry of records) {
7499
7495
  const url = new URL(entry.record.url);
7500
7496
  const pageRefKey = entry.record.pageRef ?? "";
7501
- const existing = readExisting.get({
7502
- session_ref: entry.record.sessionRef,
7503
- page_ref_key: pageRefKey,
7504
- request_id: entry.record.requestId
7505
- }) ?? void 0;
7506
- const recordId = existing?.record_id ?? entry.recordId;
7507
7497
  upsertRecord.run({
7508
- record_id: recordId,
7498
+ record_id: entry.recordId,
7509
7499
  request_id: entry.record.requestId,
7510
7500
  session_ref: entry.record.sessionRef,
7511
7501
  page_ref: entry.record.pageRef ?? null,
@@ -7550,7 +7540,7 @@ var SqliteSavedNetworkStore = class {
7550
7540
  }
7551
7541
  for (const currentTag of tags) {
7552
7542
  const result = insertTag.run({
7553
- record_id: recordId,
7543
+ record_id: entry.recordId,
7554
7544
  tag: currentTag
7555
7545
  });
7556
7546
  savedCount += result.changes ?? 0;
@@ -7653,6 +7643,49 @@ var SqliteSavedNetworkStore = class {
7653
7643
  return cleared;
7654
7644
  });
7655
7645
  }
7646
+ async *iterateBatches(options = {}) {
7647
+ const database = await this.requireDatabase();
7648
+ const batchSize = Math.max(1, Math.min(options.batchSize ?? 500, 1e3));
7649
+ let cursor;
7650
+ while (true) {
7651
+ const rows = database.prepare(
7652
+ `
7653
+ SELECT
7654
+ r.*,
7655
+ GROUP_CONCAT(t.tag, '${TAG_DELIMITER}') AS tags
7656
+ FROM saved_network_records r
7657
+ LEFT JOIN saved_network_tags t
7658
+ ON t.record_id = r.record_id
7659
+ ${cursor === void 0 ? "" : "WHERE r.saved_at > ? OR (r.saved_at = ? AND r.record_id > ?)"}
7660
+ GROUP BY r.record_id
7661
+ ORDER BY r.saved_at ASC, r.record_id ASC
7662
+ LIMIT ?
7663
+ `
7664
+ ).all(
7665
+ ...cursor === void 0 ? [] : [cursor.savedAt, cursor.savedAt, cursor.recordId],
7666
+ batchSize
7667
+ );
7668
+ if (rows.length === 0) {
7669
+ return;
7670
+ }
7671
+ yield rows.map((row) => inflateSavedNetworkRow(row, options.includeBodies ?? true));
7672
+ const lastRow = rows.at(-1);
7673
+ if (lastRow === void 0) {
7674
+ return;
7675
+ }
7676
+ cursor = {
7677
+ savedAt: lastRow.saved_at,
7678
+ recordId: lastRow.record_id
7679
+ };
7680
+ }
7681
+ }
7682
+ close() {
7683
+ if (this.database !== void 0) {
7684
+ closeSqliteDatabase(this.database);
7685
+ this.database = void 0;
7686
+ this.databaseInitialization = void 0;
7687
+ }
7688
+ }
7656
7689
  async requireDatabase() {
7657
7690
  if (this.database) {
7658
7691
  return this.database;
@@ -7684,7 +7717,7 @@ var SqliteSavedNetworkStore = class {
7684
7717
  }
7685
7718
  }
7686
7719
  async ensureDatabaseDirectory() {
7687
- this.directoryInitialization ??= ensureDirectory(path7__default.default.dirname(this.databasePath)).catch(
7720
+ this.directoryInitialization ??= ensureDirectory(path10__default.default.dirname(this.databasePath)).catch(
7688
7721
  (error) => {
7689
7722
  this.directoryInitialization = void 0;
7690
7723
  throw error;
@@ -7737,15 +7770,6 @@ var SqliteSavedNetworkStore = class {
7737
7770
  saved_at INTEGER NOT NULL
7738
7771
  );
7739
7772
 
7740
- CREATE UNIQUE INDEX IF NOT EXISTS saved_network_records_scope_request
7741
- ON saved_network_records (session_ref, page_ref_key, request_id);
7742
-
7743
- CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7744
- ON saved_network_records (saved_at DESC);
7745
-
7746
- CREATE INDEX IF NOT EXISTS saved_network_records_capture
7747
- ON saved_network_records (capture);
7748
-
7749
7773
  CREATE TABLE IF NOT EXISTS saved_network_tags (
7750
7774
  record_id TEXT NOT NULL REFERENCES saved_network_records(record_id) ON DELETE CASCADE,
7751
7775
  tag TEXT NOT NULL,
@@ -7755,6 +7779,19 @@ var SqliteSavedNetworkStore = class {
7755
7779
  CREATE INDEX IF NOT EXISTS saved_network_tags_tag
7756
7780
  ON saved_network_tags (tag);
7757
7781
  `);
7782
+ database.exec(`DROP INDEX IF EXISTS saved_network_records_scope_request`);
7783
+ database.exec(`
7784
+ CREATE INDEX IF NOT EXISTS saved_network_records_scope_request
7785
+ ON saved_network_records (session_ref, page_ref_key, request_id)
7786
+ `);
7787
+ database.exec(`
7788
+ CREATE INDEX IF NOT EXISTS saved_network_records_saved_at
7789
+ ON saved_network_records (saved_at DESC)
7790
+ `);
7791
+ database.exec(`
7792
+ CREATE INDEX IF NOT EXISTS saved_network_records_capture
7793
+ ON saved_network_records (capture)
7794
+ `);
7758
7795
  this.ensureColumn(
7759
7796
  database,
7760
7797
  "saved_network_records",
@@ -8279,7 +8316,7 @@ var FilesystemObservationStoreImpl = class {
8279
8316
  constructor(rootPath, artifacts) {
8280
8317
  this.rootPath = rootPath;
8281
8318
  this.artifacts = artifacts;
8282
- this.sessionsDirectory = path7__default.default.join(this.rootPath, "observations", "sessions");
8319
+ this.sessionsDirectory = path10__default.default.join(this.rootPath, "observations", "sessions");
8283
8320
  }
8284
8321
  sessionsDirectory;
8285
8322
  redactors = /* @__PURE__ */ new Map();
@@ -8376,7 +8413,7 @@ var FilesystemObservationStoreImpl = class {
8376
8413
  ...raw.artifactIds === void 0 || raw.artifactIds.length === 0 ? {} : { artifactIds: [...raw.artifactIds] }
8377
8414
  };
8378
8415
  await writeJsonFileExclusive(
8379
- path7__default.default.join(this.sessionEventsDirectory(sessionId), eventFileName(sequence)),
8416
+ path10__default.default.join(this.sessionEventsDirectory(sessionId), eventFileName(sequence)),
8380
8417
  event
8381
8418
  );
8382
8419
  updatedAt = Math.max(updatedAt, createdAt);
@@ -8434,7 +8471,7 @@ var FilesystemObservationStoreImpl = class {
8434
8471
  }
8435
8472
  const files = await listJsonFiles(directoryPath);
8436
8473
  const events = await Promise.all(
8437
- files.map((fileName) => readJsonFile(path7__default.default.join(directoryPath, fileName)))
8474
+ files.map((fileName) => readJsonFile(path10__default.default.join(directoryPath, fileName)))
8438
8475
  );
8439
8476
  const filtered = events.filter((event) => {
8440
8477
  if (input.kind !== void 0 && event.kind !== input.kind) return false;
@@ -8461,7 +8498,7 @@ var FilesystemObservationStoreImpl = class {
8461
8498
  const files = await listJsonFiles(directoryPath);
8462
8499
  const artifacts = await Promise.all(
8463
8500
  files.map(
8464
- (fileName) => readJsonFile(path7__default.default.join(directoryPath, fileName))
8501
+ (fileName) => readJsonFile(path10__default.default.join(directoryPath, fileName))
8465
8502
  )
8466
8503
  );
8467
8504
  const filtered = artifacts.filter((artifact) => {
@@ -8524,25 +8561,25 @@ var FilesystemObservationStoreImpl = class {
8524
8561
  )).filter((value) => value !== void 0);
8525
8562
  }
8526
8563
  sessionDirectory(sessionId) {
8527
- return path7__default.default.join(this.sessionsDirectory, encodePathSegment(sessionId));
8564
+ return path10__default.default.join(this.sessionsDirectory, encodePathSegment(sessionId));
8528
8565
  }
8529
8566
  sessionManifestPath(sessionId) {
8530
- return path7__default.default.join(this.sessionDirectory(sessionId), "session.json");
8567
+ return path10__default.default.join(this.sessionDirectory(sessionId), "session.json");
8531
8568
  }
8532
8569
  sessionEventsDirectory(sessionId) {
8533
- return path7__default.default.join(this.sessionDirectory(sessionId), "events");
8570
+ return path10__default.default.join(this.sessionDirectory(sessionId), "events");
8534
8571
  }
8535
8572
  sessionArtifactsDirectory(sessionId) {
8536
- return path7__default.default.join(this.sessionDirectory(sessionId), "artifacts");
8573
+ return path10__default.default.join(this.sessionDirectory(sessionId), "artifacts");
8537
8574
  }
8538
8575
  sessionArtifactPath(sessionId, artifactId) {
8539
- return path7__default.default.join(
8576
+ return path10__default.default.join(
8540
8577
  this.sessionArtifactsDirectory(sessionId),
8541
8578
  `${encodePathSegment(artifactId)}.json`
8542
8579
  );
8543
8580
  }
8544
8581
  sessionLockPath(sessionId) {
8545
- return path7__default.default.join(this.sessionDirectory(sessionId), ".lock");
8582
+ return path10__default.default.join(this.sessionDirectory(sessionId), ".lock");
8546
8583
  }
8547
8584
  async reconcileSessionManifest(sessionId) {
8548
8585
  const session = await this.getSession(sessionId);
@@ -8570,14 +8607,14 @@ var FilesystemObservationStoreImpl = class {
8570
8607
  Promise.all(
8571
8608
  eventFiles.map(
8572
8609
  (fileName) => readJsonFile(
8573
- path7__default.default.join(this.sessionEventsDirectory(sessionId), fileName)
8610
+ path10__default.default.join(this.sessionEventsDirectory(sessionId), fileName)
8574
8611
  )
8575
8612
  )
8576
8613
  ),
8577
8614
  Promise.all(
8578
8615
  artifactFiles.map(
8579
8616
  (fileName) => readJsonFile(
8580
- path7__default.default.join(this.sessionArtifactsDirectory(sessionId), fileName)
8617
+ path10__default.default.join(this.sessionArtifactsDirectory(sessionId), fileName)
8581
8618
  )
8582
8619
  )
8583
8620
  )
@@ -8630,7 +8667,7 @@ var FilesystemTraceStore = class {
8630
8667
  constructor(rootPath, artifacts) {
8631
8668
  this.rootPath = rootPath;
8632
8669
  this.artifacts = artifacts;
8633
- this.runsDirectory = path7__default.default.join(this.rootPath, "traces", "runs");
8670
+ this.runsDirectory = path10__default.default.join(this.rootPath, "traces", "runs");
8634
8671
  }
8635
8672
  runsDirectory;
8636
8673
  async initialize() {
@@ -8707,7 +8744,7 @@ var FilesystemTraceStore = class {
8707
8744
  ...input.error === void 0 ? {} : { error: input.error }
8708
8745
  };
8709
8746
  await writeJsonFileExclusive(
8710
- path7__default.default.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8747
+ path10__default.default.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8711
8748
  entry
8712
8749
  );
8713
8750
  await writeJsonFileAtomic(this.runManifestPath(runId), {
@@ -8726,7 +8763,7 @@ var FilesystemTraceStore = class {
8726
8763
  const files = await listJsonFiles(entriesDirectory);
8727
8764
  return Promise.all(
8728
8765
  files.map(
8729
- (fileName) => readJsonFile(path7__default.default.join(entriesDirectory, fileName))
8766
+ (fileName) => readJsonFile(path10__default.default.join(entriesDirectory, fileName))
8730
8767
  )
8731
8768
  );
8732
8769
  }
@@ -8772,16 +8809,16 @@ var FilesystemTraceStore = class {
8772
8809
  return { trace, artifacts };
8773
8810
  }
8774
8811
  runDirectory(runId) {
8775
- return path7__default.default.join(this.runsDirectory, encodeURIComponent(runId));
8812
+ return path10__default.default.join(this.runsDirectory, encodeURIComponent(runId));
8776
8813
  }
8777
8814
  runEntriesDirectory(runId) {
8778
- return path7__default.default.join(this.runDirectory(runId), "entries");
8815
+ return path10__default.default.join(this.runDirectory(runId), "entries");
8779
8816
  }
8780
8817
  runManifestPath(runId) {
8781
- return path7__default.default.join(this.runDirectory(runId), "manifest.json");
8818
+ return path10__default.default.join(this.runDirectory(runId), "manifest.json");
8782
8819
  }
8783
8820
  runWriteLockPath(runId) {
8784
- return path7__default.default.join(this.runDirectory(runId), ".append.lock");
8821
+ return path10__default.default.join(this.runDirectory(runId), ".append.lock");
8785
8822
  }
8786
8823
  };
8787
8824
  function createTraceStore(rootPath, artifacts) {
@@ -8795,7 +8832,7 @@ function normalizeWorkspaceId(workspace) {
8795
8832
  return encodePathSegment(workspace);
8796
8833
  }
8797
8834
  function resolveFilesystemWorkspacePath(input) {
8798
- return path7__default.default.join(
8835
+ return path10__default.default.join(
8799
8836
  input.rootDir,
8800
8837
  ".opensteer",
8801
8838
  "workspaces",
@@ -8804,18 +8841,18 @@ function resolveFilesystemWorkspacePath(input) {
8804
8841
  }
8805
8842
  async function createFilesystemOpensteerWorkspace(options) {
8806
8843
  await ensureDirectory(options.rootPath);
8807
- const manifestPath = path7__default.default.join(options.rootPath, "workspace.json");
8808
- const browserPath = path7__default.default.join(options.rootPath, "browser");
8809
- const browserManifestPath = path7__default.default.join(browserPath, "manifest.json");
8810
- const browserUserDataDir = path7__default.default.join(browserPath, "user-data");
8811
- const livePath = path7__default.default.join(options.rootPath, "live");
8812
- const liveLocalPath = path7__default.default.join(livePath, "local.json");
8813
- const liveCloudPath = path7__default.default.join(livePath, "cloud.json");
8814
- const artifactsPath = path7__default.default.join(options.rootPath, "artifacts");
8815
- const tracesPath = path7__default.default.join(options.rootPath, "traces");
8816
- const observationsPath = path7__default.default.join(options.rootPath, "observations");
8817
- const registryPath = path7__default.default.join(options.rootPath, "registry");
8818
- const lockPath = path7__default.default.join(options.rootPath, ".lock");
8844
+ const manifestPath = path10__default.default.join(options.rootPath, "workspace.json");
8845
+ const browserPath = path10__default.default.join(options.rootPath, "browser");
8846
+ const browserManifestPath = path10__default.default.join(browserPath, "manifest.json");
8847
+ const browserUserDataDir = path10__default.default.join(browserPath, "user-data");
8848
+ const livePath = path10__default.default.join(options.rootPath, "live");
8849
+ const liveLocalPath = path10__default.default.join(livePath, "local.json");
8850
+ const liveCloudPath = path10__default.default.join(livePath, "cloud.json");
8851
+ const artifactsPath = path10__default.default.join(options.rootPath, "artifacts");
8852
+ const tracesPath = path10__default.default.join(options.rootPath, "traces");
8853
+ const observationsPath = path10__default.default.join(options.rootPath, "observations");
8854
+ const registryPath = path10__default.default.join(options.rootPath, "registry");
8855
+ const lockPath = path10__default.default.join(options.rootPath, ".lock");
8819
8856
  let manifest;
8820
8857
  if (await pathExists(manifestPath)) {
8821
8858
  manifest = await readJsonFile(manifestPath);
@@ -8942,7 +8979,6 @@ var DEFAULT_TIMEOUTS = {
8942
8979
  "dom.extract": 15e3,
8943
8980
  "network.query": 15e3,
8944
8981
  "network.detail": 15e3,
8945
- "network.replay": 3e4,
8946
8982
  "scripts.capture": 15e3,
8947
8983
  "session.cookies": 1e4,
8948
8984
  "session.storage": 1e4,
@@ -9571,9 +9607,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
9571
9607
  "poster",
9572
9608
  "ping"
9573
9609
  ]);
9574
- function buildArrayFieldPathCandidates(path15) {
9575
- const strict = path15.nodes.length ? buildPathCandidates(path15.nodes) : [];
9576
- const relaxedNodes = stripPositionClauses(path15.nodes);
9610
+ function buildArrayFieldPathCandidates(path18) {
9611
+ const strict = path18.nodes.length ? buildPathCandidates(path18.nodes) : [];
9612
+ const relaxedNodes = stripPositionClauses(path18.nodes);
9577
9613
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
9578
9614
  return dedupeSelectors([...strict, ...relaxed]);
9579
9615
  }
@@ -10103,18 +10139,18 @@ function cloneStructuralElementAnchor(anchor) {
10103
10139
  nodes: anchor.nodes.map(clonePathNode)
10104
10140
  };
10105
10141
  }
10106
- function cloneReplayElementPath(path15) {
10142
+ function cloneReplayElementPath(path18) {
10107
10143
  return {
10108
10144
  resolution: "deterministic",
10109
- context: cloneContext(path15.context),
10110
- nodes: path15.nodes.map(clonePathNode)
10145
+ context: cloneContext(path18.context),
10146
+ nodes: path18.nodes.map(clonePathNode)
10111
10147
  };
10112
10148
  }
10113
- function cloneElementPath(path15) {
10114
- return cloneReplayElementPath(path15);
10149
+ function cloneElementPath(path18) {
10150
+ return cloneReplayElementPath(path18);
10115
10151
  }
10116
- function buildPathSelectorHint(path15) {
10117
- const nodes = path15?.nodes || [];
10152
+ function buildPathSelectorHint(path18) {
10153
+ const nodes = path18?.nodes || [];
10118
10154
  const last = nodes[nodes.length - 1];
10119
10155
  if (!last) {
10120
10156
  return "*";
@@ -10163,15 +10199,15 @@ function sanitizeStructuralElementAnchor(anchor) {
10163
10199
  nodes: sanitizeNodes(anchor.nodes)
10164
10200
  };
10165
10201
  }
10166
- function sanitizeReplayElementPath(path15) {
10202
+ function sanitizeReplayElementPath(path18) {
10167
10203
  return {
10168
10204
  resolution: "deterministic",
10169
- context: sanitizeContext(path15.context),
10170
- nodes: sanitizeNodes(path15.nodes)
10205
+ context: sanitizeContext(path18.context),
10206
+ nodes: sanitizeNodes(path18.nodes)
10171
10207
  };
10172
10208
  }
10173
- function sanitizeElementPath(path15) {
10174
- return sanitizeReplayElementPath(path15);
10209
+ function sanitizeElementPath(path18) {
10210
+ return sanitizeReplayElementPath(path18);
10175
10211
  }
10176
10212
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
10177
10213
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -10294,8 +10330,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
10294
10330
  }
10295
10331
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
10296
10332
  }
10297
- function buildArrayFieldCandidates(path15) {
10298
- return buildArrayFieldPathCandidates(path15);
10333
+ function buildArrayFieldCandidates(path18) {
10334
+ return buildArrayFieldPathCandidates(path18);
10299
10335
  }
10300
10336
  function firstDefinedAttribute(node, keys) {
10301
10337
  for (const key of keys) {
@@ -11831,21 +11867,21 @@ var DefaultDomRuntime = class {
11831
11867
  return match;
11832
11868
  }
11833
11869
  async resolvePathTarget(session, pageRef, rawPath, source, persist, descriptor) {
11834
- const path15 = sanitizeReplayElementPath(rawPath);
11835
- const context = await this.resolvePathContext(session, pageRef, path15.context);
11836
- const target = resolveDomPathInScope(context.index, path15.nodes, context.scope);
11870
+ const path18 = sanitizeReplayElementPath(rawPath);
11871
+ const context = await this.resolvePathContext(session, pageRef, path18.context);
11872
+ const target = resolveDomPathInScope(context.index, path18.nodes, context.scope);
11837
11873
  if (!target) {
11838
- throwTargetNotFound(context.index, path15.nodes, context.scope);
11874
+ throwTargetNotFound(context.index, path18.nodes, context.scope);
11839
11875
  }
11840
11876
  if (target.node.nodeRef === void 0) {
11841
11877
  throw new Error(
11842
- `resolved path "${buildPathSelectorHint(path15)}" does not point to a live element`
11878
+ `resolved path "${buildPathSelectorHint(path18)}" does not point to a live element`
11843
11879
  );
11844
11880
  }
11845
11881
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
11846
11882
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
11847
11883
  ...persist === void 0 ? {} : { persist },
11848
- replayPath: path15,
11884
+ replayPath: path18,
11849
11885
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
11850
11886
  ...descriptor === void 0 ? {} : { descriptor }
11851
11887
  });
@@ -11866,9 +11902,9 @@ var DefaultDomRuntime = class {
11866
11902
  });
11867
11903
  }
11868
11904
  async queryAllByElementPath(session, pageRef, rawPath) {
11869
- const path15 = sanitizeReplayElementPath(rawPath);
11870
- const context = await this.resolvePathContext(session, pageRef, path15.context);
11871
- return queryAllDomPathInScope(context.index, path15.nodes, context.scope).filter(
11905
+ const path18 = sanitizeReplayElementPath(rawPath);
11906
+ const context = await this.resolvePathContext(session, pageRef, path18.context);
11907
+ return queryAllDomPathInScope(context.index, path18.nodes, context.scope).filter(
11872
11908
  (node) => node.nodeRef !== void 0
11873
11909
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11874
11910
  }
@@ -12054,16 +12090,16 @@ var DefaultDomRuntime = class {
12054
12090
  const index = createSnapshotIndex(item.snapshot);
12055
12091
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
12056
12092
  }
12057
- resolveFirstArrayFieldTargetInNode(index, rootNode, path15) {
12058
- const normalizedPath = sanitizeElementPath(path15);
12093
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path18) {
12094
+ const normalizedPath = sanitizeElementPath(path18);
12059
12095
  const selectors = buildArrayFieldCandidates(normalizedPath);
12060
12096
  if (!selectors.length) {
12061
12097
  return rootNode;
12062
12098
  }
12063
12099
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
12064
12100
  }
12065
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path15) {
12066
- const normalizedPath = sanitizeElementPath(path15);
12101
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path18) {
12102
+ const normalizedPath = sanitizeElementPath(path18);
12067
12103
  const selectors = buildArrayFieldCandidates(normalizedPath);
12068
12104
  if (!selectors.length) {
12069
12105
  return rootNode;
@@ -12152,8 +12188,8 @@ function encodeDataPath(tokens) {
12152
12188
  }
12153
12189
  return out;
12154
12190
  }
12155
- function parseDataPath(path15) {
12156
- const input = path15.trim();
12191
+ function parseDataPath(path18) {
12192
+ const input = path18.trim();
12157
12193
  if (input.length === 0) {
12158
12194
  return [];
12159
12195
  }
@@ -12203,8 +12239,8 @@ function parseDataPath(path15) {
12203
12239
  function inflateDataPathObject(flat) {
12204
12240
  let root = {};
12205
12241
  let initialized = false;
12206
- for (const [path15, value] of Object.entries(flat)) {
12207
- const tokens = parseDataPath(path15);
12242
+ for (const [path18, value] of Object.entries(flat)) {
12243
+ const tokens = parseDataPath(path18);
12208
12244
  if (!tokens || tokens.length === 0) {
12209
12245
  continue;
12210
12246
  }
@@ -12536,8 +12572,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
12536
12572
  fields: mergedFields
12537
12573
  };
12538
12574
  }
12539
- function minimizePathMatchClauses(path15, mode) {
12540
- const normalized = sanitizeElementPath(path15);
12575
+ function minimizePathMatchClauses(path18, mode) {
12576
+ const normalized = sanitizeElementPath(path18);
12541
12577
  const nodes = normalized.nodes.map((node, index) => {
12542
12578
  const isLast = index === normalized.nodes.length - 1;
12543
12579
  const attrs = node.attrs || {};
@@ -12641,8 +12677,8 @@ function seedMinimalAttrClause(attrs) {
12641
12677
  }
12642
12678
  return null;
12643
12679
  }
12644
- function relaxPathForSingleSample(path15, mode) {
12645
- const normalized = sanitizeElementPath(path15);
12680
+ function relaxPathForSingleSample(path18, mode) {
12681
+ const normalized = sanitizeElementPath(path18);
12646
12682
  const relaxedNodes = normalized.nodes.map((node, index) => {
12647
12683
  const isLast = index === normalized.nodes.length - 1;
12648
12684
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -12727,8 +12763,8 @@ function shouldKeepAttrForSingleSample(key) {
12727
12763
  }
12728
12764
  return true;
12729
12765
  }
12730
- function buildPathStructureKey(path15) {
12731
- const normalized = sanitizeElementPath(path15);
12766
+ function buildPathStructureKey(path18) {
12767
+ const normalized = sanitizeElementPath(path18);
12732
12768
  return canonicalJsonString({
12733
12769
  context: (normalized.context || []).map((hop) => ({
12734
12770
  kind: hop.kind,
@@ -12855,30 +12891,30 @@ function buildArrayItemNode(fields) {
12855
12891
  }
12856
12892
  return node;
12857
12893
  }
12858
- function insertNodeAtPath(root, path15, node) {
12859
- const tokens = parseDataPath(path15);
12894
+ function insertNodeAtPath(root, path18, node) {
12895
+ const tokens = parseDataPath(path18);
12860
12896
  if (!tokens || !tokens.length) {
12861
12897
  throw new Error(
12862
- `Invalid persisted extraction path "${path15}": expected a non-empty object path.`
12898
+ `Invalid persisted extraction path "${path18}": expected a non-empty object path.`
12863
12899
  );
12864
12900
  }
12865
12901
  if (tokens.some((token) => token.kind === "index")) {
12866
12902
  throw new Error(
12867
- `Invalid persisted extraction path "${path15}": nested array indices are not supported in cached descriptors.`
12903
+ `Invalid persisted extraction path "${path18}": nested array indices are not supported in cached descriptors.`
12868
12904
  );
12869
12905
  }
12870
12906
  let current = root;
12871
12907
  for (let index = 0; index < tokens.length; index += 1) {
12872
12908
  const token = tokens[index];
12873
12909
  if (!token || token.kind !== "prop") {
12874
- throw new Error(`Invalid persisted extraction path "${path15}": expected object segment.`);
12910
+ throw new Error(`Invalid persisted extraction path "${path18}": expected object segment.`);
12875
12911
  }
12876
12912
  const isLast = index === tokens.length - 1;
12877
12913
  if (isLast) {
12878
12914
  const existing = current[token.key];
12879
12915
  if (existing) {
12880
12916
  throw new Error(
12881
- `Conflicting persisted extraction path "${path15}" detected while building descriptor tree.`
12917
+ `Conflicting persisted extraction path "${path18}" detected while building descriptor tree.`
12882
12918
  );
12883
12919
  }
12884
12920
  current[token.key] = node;
@@ -12893,7 +12929,7 @@ function insertNodeAtPath(root, path15, node) {
12893
12929
  }
12894
12930
  if (!isPersistedObjectNode(next)) {
12895
12931
  throw new Error(
12896
- `Conflicting persisted extraction path "${path15}" detected at "${token.key}".`
12932
+ `Conflicting persisted extraction path "${path18}" detected at "${token.key}".`
12897
12933
  );
12898
12934
  }
12899
12935
  current = next;
@@ -12928,7 +12964,7 @@ function buildItemRootForArrayIndex(entries) {
12928
12964
  }
12929
12965
  const paths = entries.map(
12930
12966
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
12931
- ).filter((path15) => path15 !== null);
12967
+ ).filter((path18) => path18 !== null);
12932
12968
  if (!paths.length) {
12933
12969
  return null;
12934
12970
  }
@@ -12949,7 +12985,7 @@ function getCommonPathPrefixLength(paths) {
12949
12985
  if (!paths.length) {
12950
12986
  return 0;
12951
12987
  }
12952
- const nodeChains = paths.map((path15) => path15.nodes);
12988
+ const nodeChains = paths.map((path18) => path18.nodes);
12953
12989
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
12954
12990
  if (!Number.isFinite(minLength) || minLength <= 0) {
12955
12991
  return 0;
@@ -13018,30 +13054,30 @@ function mergeElementPathsByMajority(paths) {
13018
13054
  if (!paths.length) {
13019
13055
  return null;
13020
13056
  }
13021
- const normalized = paths.map((path15) => sanitizeElementPath(path15));
13057
+ const normalized = paths.map((path18) => sanitizeElementPath(path18));
13022
13058
  const contextKey = pickModeString(
13023
- normalized.map((path15) => canonicalJsonString(path15.context)),
13059
+ normalized.map((path18) => canonicalJsonString(path18.context)),
13024
13060
  1
13025
13061
  );
13026
13062
  if (!contextKey) {
13027
13063
  return null;
13028
13064
  }
13029
- const sameContext = normalized.filter((path15) => canonicalJsonString(path15.context) === contextKey);
13065
+ const sameContext = normalized.filter((path18) => canonicalJsonString(path18.context) === contextKey);
13030
13066
  if (!sameContext.length) {
13031
13067
  return null;
13032
13068
  }
13033
13069
  const targetLength = pickModeNumber(
13034
- sameContext.map((path15) => path15.nodes.length),
13070
+ sameContext.map((path18) => path18.nodes.length),
13035
13071
  1
13036
13072
  ) ?? sameContext[0]?.nodes.length ?? 0;
13037
- const aligned = sameContext.filter((path15) => path15.nodes.length === targetLength);
13073
+ const aligned = sameContext.filter((path18) => path18.nodes.length === targetLength);
13038
13074
  if (!aligned.length) {
13039
13075
  return null;
13040
13076
  }
13041
13077
  const threshold = majorityThreshold(aligned.length);
13042
13078
  const nodes = [];
13043
13079
  for (let index = 0; index < targetLength; index += 1) {
13044
- const nodesAtIndex = aligned.map((path15) => path15.nodes[index]).filter((node) => node !== void 0);
13080
+ const nodesAtIndex = aligned.map((path18) => path18.nodes[index]).filter((node) => node !== void 0);
13045
13081
  if (!nodesAtIndex.length) {
13046
13082
  return null;
13047
13083
  }
@@ -13287,8 +13323,8 @@ function clonePathContext(context) {
13287
13323
  function clonePathNodes(nodes) {
13288
13324
  return JSON.parse(JSON.stringify(nodes || []));
13289
13325
  }
13290
- function cloneElementPath2(path15) {
13291
- return JSON.parse(JSON.stringify(path15));
13326
+ function cloneElementPath2(path18) {
13327
+ return JSON.parse(JSON.stringify(path18));
13292
13328
  }
13293
13329
  function clonePersistedOpensteerExtractionNode(node) {
13294
13330
  return JSON.parse(JSON.stringify(node));
@@ -13606,8 +13642,8 @@ function collectPersistedValueNodeRefs(node) {
13606
13642
  return [
13607
13643
  {
13608
13644
  path: sanitizeElementPath(node.$path),
13609
- replacePath: (path15) => {
13610
- node.$path = sanitizeElementPath(path15);
13645
+ replacePath: (path18) => {
13646
+ node.$path = sanitizeElementPath(path18);
13611
13647
  }
13612
13648
  }
13613
13649
  ];
@@ -13621,13 +13657,13 @@ function collectPersistedValueNodeRefs(node) {
13621
13657
  }
13622
13658
  return refs;
13623
13659
  }
13624
- function hasPositionClause(path15) {
13625
- return path15.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13660
+ function hasPositionClause(path18) {
13661
+ return path18.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13626
13662
  }
13627
- function stripPositionClauses2(path15) {
13663
+ function stripPositionClauses2(path18) {
13628
13664
  return sanitizeElementPath({
13629
- context: path15.context,
13630
- nodes: path15.nodes.map((node) => ({
13665
+ context: path18.context,
13666
+ nodes: path18.nodes.map((node) => ({
13631
13667
  ...node,
13632
13668
  match: node.match.filter((clause) => clause.kind !== "position")
13633
13669
  }))
@@ -14037,17 +14073,57 @@ function normalizeNonEmptyString2(name, value) {
14037
14073
  function normalizeKey(value) {
14038
14074
  return String(value ?? "").trim();
14039
14075
  }
14040
- function labelForPath(path15) {
14041
- return path15.trim().length === 0 ? "$" : path15;
14076
+ function labelForPath(path18) {
14077
+ return path18.trim().length === 0 ? "$" : path18;
14042
14078
  }
14043
14079
  function sha256Hex3(value) {
14044
14080
  return crypto.createHash("sha256").update(value).digest("hex");
14045
14081
  }
14046
- util.promisify(child_process.execFile);
14047
- Math.floor(Date.now() - process.uptime() * 1e3);
14048
- ({ ...process.env});
14049
- ({
14050
- pid: process.pid});
14082
+ var execFileAsync = util.promisify(child_process.execFile);
14083
+ var PROCESS_STARTED_AT_MS = Math.floor(Date.now() - process.uptime() * 1e3);
14084
+ var PROCESS_START_TIME_TOLERANCE_MS = 1e3;
14085
+ var PROCESS_LIST_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
14086
+ var PS_COMMAND_ENV = { ...process.env, LC_ALL: "C" };
14087
+ var LINUX_STAT_START_TIME_FIELD_INDEX = 19;
14088
+ var CURRENT_PROCESS_OWNER = {
14089
+ pid: process.pid,
14090
+ processStartedAtMs: PROCESS_STARTED_AT_MS
14091
+ };
14092
+ var linuxClockTicksPerSecondPromise = null;
14093
+ function parseProcessOwner(value) {
14094
+ if (!value || typeof value !== "object") {
14095
+ return null;
14096
+ }
14097
+ const parsed = value;
14098
+ const pid = Number(parsed.pid);
14099
+ const processStartedAtMs = Number(parsed.processStartedAtMs);
14100
+ if (!Number.isInteger(pid) || pid <= 0) {
14101
+ return null;
14102
+ }
14103
+ if (!Number.isInteger(processStartedAtMs) || processStartedAtMs <= 0) {
14104
+ return null;
14105
+ }
14106
+ return {
14107
+ pid,
14108
+ processStartedAtMs
14109
+ };
14110
+ }
14111
+ function processOwnersEqual(left, right) {
14112
+ if (!left || !right) {
14113
+ return left === right;
14114
+ }
14115
+ return left.pid === right.pid && left.processStartedAtMs === right.processStartedAtMs;
14116
+ }
14117
+ async function getProcessLiveness(owner) {
14118
+ if (owner.pid === process.pid && hasMatchingProcessStartTime(owner.processStartedAtMs, PROCESS_STARTED_AT_MS)) {
14119
+ return "live";
14120
+ }
14121
+ const startedAtMs = await readProcessStartedAtMs(owner.pid);
14122
+ if (typeof startedAtMs === "number") {
14123
+ return hasMatchingProcessStartTime(owner.processStartedAtMs, startedAtMs) ? "live" : "dead";
14124
+ }
14125
+ return isProcessRunning(owner.pid) ? "unknown" : "dead";
14126
+ }
14051
14127
  function isProcessRunning(pid) {
14052
14128
  try {
14053
14129
  process.kill(pid, 0);
@@ -14057,6 +14133,116 @@ function isProcessRunning(pid) {
14057
14133
  return code !== "ESRCH";
14058
14134
  }
14059
14135
  }
14136
+ function hasMatchingProcessStartTime(expectedStartedAtMs, actualStartedAtMs) {
14137
+ return Math.abs(expectedStartedAtMs - actualStartedAtMs) <= PROCESS_START_TIME_TOLERANCE_MS;
14138
+ }
14139
+ async function readProcessStartedAtMs(pid) {
14140
+ if (pid <= 0) {
14141
+ return null;
14142
+ }
14143
+ if (process.platform === "linux") {
14144
+ return readLinuxProcessStartedAtMs(pid);
14145
+ }
14146
+ if (process.platform === "win32") {
14147
+ return readWindowsProcessStartedAtMs(pid);
14148
+ }
14149
+ return readPsProcessStartedAtMs(pid);
14150
+ }
14151
+ async function readLinuxProcessStartedAtMs(pid) {
14152
+ let statRaw;
14153
+ try {
14154
+ statRaw = await promises.readFile(`/proc/${String(pid)}/stat`, "utf8");
14155
+ } catch {
14156
+ return null;
14157
+ }
14158
+ const startTicks = parseLinuxProcessStartTicks(statRaw);
14159
+ if (startTicks === null) {
14160
+ return null;
14161
+ }
14162
+ const [bootTimeMs, clockTicksPerSecond] = await Promise.all([
14163
+ readLinuxBootTimeMs(),
14164
+ readLinuxClockTicksPerSecond()
14165
+ ]);
14166
+ if (bootTimeMs === null || clockTicksPerSecond === null) {
14167
+ return null;
14168
+ }
14169
+ return Math.floor(bootTimeMs + startTicks * 1e3 / clockTicksPerSecond);
14170
+ }
14171
+ function parseLinuxProcessStartTicks(statRaw) {
14172
+ const closingParenIndex = statRaw.lastIndexOf(")");
14173
+ if (closingParenIndex === -1) {
14174
+ return null;
14175
+ }
14176
+ const fields = statRaw.slice(closingParenIndex + 2).trim().split(/\s+/);
14177
+ const startTicks = Number(fields[LINUX_STAT_START_TIME_FIELD_INDEX]);
14178
+ return Number.isFinite(startTicks) && startTicks >= 0 ? startTicks : null;
14179
+ }
14180
+ async function readLinuxBootTimeMs() {
14181
+ try {
14182
+ const statRaw = await promises.readFile("/proc/stat", "utf8");
14183
+ const bootTimeLine = statRaw.split("\n").find((line) => line.startsWith("btime "));
14184
+ if (!bootTimeLine) {
14185
+ return null;
14186
+ }
14187
+ const bootTimeSeconds = Number.parseInt(bootTimeLine.slice("btime ".length), 10);
14188
+ return Number.isFinite(bootTimeSeconds) ? bootTimeSeconds * 1e3 : null;
14189
+ } catch {
14190
+ return null;
14191
+ }
14192
+ }
14193
+ async function readLinuxClockTicksPerSecond() {
14194
+ if (!linuxClockTicksPerSecondPromise) {
14195
+ linuxClockTicksPerSecondPromise = execFileAsync("getconf", ["CLK_TCK"], {
14196
+ encoding: "utf8",
14197
+ maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
14198
+ }).then(({ stdout }) => {
14199
+ const value = Number.parseInt(stdout.trim(), 10);
14200
+ return Number.isFinite(value) && value > 0 ? value : null;
14201
+ }).catch(() => null);
14202
+ }
14203
+ return linuxClockTicksPerSecondPromise;
14204
+ }
14205
+ async function readWindowsProcessStartedAtMs(pid) {
14206
+ try {
14207
+ const { stdout } = await execFileAsync(
14208
+ "powershell.exe",
14209
+ [
14210
+ "-NoProfile",
14211
+ "-Command",
14212
+ `(Get-Process -Id ${String(pid)}).StartTime.ToUniversalTime().ToString("o")`
14213
+ ],
14214
+ {
14215
+ encoding: "utf8",
14216
+ maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
14217
+ }
14218
+ );
14219
+ const isoTimestamp = stdout.trim();
14220
+ if (!isoTimestamp) {
14221
+ return null;
14222
+ }
14223
+ const startedAtMs = Date.parse(isoTimestamp);
14224
+ return Number.isFinite(startedAtMs) ? startedAtMs : null;
14225
+ } catch {
14226
+ return null;
14227
+ }
14228
+ }
14229
+ async function readPsProcessStartedAtMs(pid) {
14230
+ try {
14231
+ const { stdout } = await execFileAsync("ps", ["-o", "lstart=", "-p", String(pid)], {
14232
+ encoding: "utf8",
14233
+ env: PS_COMMAND_ENV,
14234
+ maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
14235
+ });
14236
+ const startedAt = stdout.trim();
14237
+ if (!startedAt) {
14238
+ return null;
14239
+ }
14240
+ const startedAtMs = Date.parse(startedAt.replace(/\s+/g, " "));
14241
+ return Number.isFinite(startedAtMs) ? startedAtMs : null;
14242
+ } catch {
14243
+ return null;
14244
+ }
14245
+ }
14060
14246
  var CHROME_SINGLETON_ARTIFACTS = [
14061
14247
  "SingletonCookie",
14062
14248
  "SingletonLock",
@@ -14067,7 +14253,7 @@ var CHROME_SINGLETON_ARTIFACTS = [
14067
14253
  async function clearChromeSingletonEntries(userDataDir) {
14068
14254
  await Promise.all(
14069
14255
  CHROME_SINGLETON_ARTIFACTS.map(
14070
- (entry) => promises.rm(path7.join(userDataDir, entry), {
14256
+ (entry) => promises.rm(path10.join(userDataDir, entry), {
14071
14257
  recursive: true,
14072
14258
  force: true
14073
14259
  }).catch(() => void 0)
@@ -14082,7 +14268,7 @@ async function sanitizeChromeProfile(userDataDir) {
14082
14268
  await Promise.all(profileDirs.map((dir) => sanitizeProfilePreferences(userDataDir, dir)));
14083
14269
  }
14084
14270
  async function sanitizeProfilePreferences(userDataDir, profileDir) {
14085
- const prefsPath = path7.join(userDataDir, profileDir, "Preferences");
14271
+ const prefsPath = path10.join(userDataDir, profileDir, "Preferences");
14086
14272
  try {
14087
14273
  const raw = await promises.readFile(prefsPath, "utf8");
14088
14274
  const prefs = JSON.parse(raw);
@@ -14094,7 +14280,7 @@ async function sanitizeProfilePreferences(userDataDir, profileDir) {
14094
14280
  profile.exited_cleanly = true;
14095
14281
  prefs.profile = profile;
14096
14282
  await promises.writeFile(prefsPath, JSON.stringify(prefs), "utf8");
14097
- await promises.rm(path7.join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
14283
+ await promises.rm(path10.join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
14098
14284
  () => void 0
14099
14285
  );
14100
14286
  } catch {
@@ -14115,9 +14301,9 @@ var BROWSER_BRANDS = [
14115
14301
  },
14116
14302
  win32: {
14117
14303
  executableCandidates: [
14118
- path7.join(WINDOWS_PROGRAM_FILES, "Google", "Chrome", "Application", "chrome.exe"),
14119
- path7.join(WINDOWS_PROGRAM_FILES_X86, "Google", "Chrome", "Application", "chrome.exe"),
14120
- path7.join("~", "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe")
14304
+ path10.join(WINDOWS_PROGRAM_FILES, "Google", "Chrome", "Application", "chrome.exe"),
14305
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Google", "Chrome", "Application", "chrome.exe"),
14306
+ path10.join("~", "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe")
14121
14307
  ],
14122
14308
  userDataDir: "~/AppData/Local/Google/Chrome/User Data",
14123
14309
  processNames: ["/google/chrome/application/chrome.exe"]
@@ -14147,7 +14333,7 @@ var BROWSER_BRANDS = [
14147
14333
  },
14148
14334
  win32: {
14149
14335
  executableCandidates: [
14150
- path7.join("~", "AppData", "Local", "Google", "Chrome SxS", "Application", "chrome.exe")
14336
+ path10.join("~", "AppData", "Local", "Google", "Chrome SxS", "Application", "chrome.exe")
14151
14337
  ],
14152
14338
  userDataDir: "~/AppData/Local/Google/Chrome SxS/User Data",
14153
14339
  processNames: ["/google/chrome sxs/application/chrome.exe"]
@@ -14164,9 +14350,9 @@ var BROWSER_BRANDS = [
14164
14350
  },
14165
14351
  win32: {
14166
14352
  executableCandidates: [
14167
- path7.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
14168
- path7.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
14169
- path7.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
14353
+ path10.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
14354
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
14355
+ path10.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
14170
14356
  ],
14171
14357
  userDataDir: "~/AppData/Local/Chromium/User Data",
14172
14358
  processNames: ["/chromium/application/chrome.exe"]
@@ -14193,15 +14379,15 @@ var BROWSER_BRANDS = [
14193
14379
  },
14194
14380
  win32: {
14195
14381
  executableCandidates: [
14196
- path7.join(WINDOWS_PROGRAM_FILES, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
14197
- path7.join(
14382
+ path10.join(WINDOWS_PROGRAM_FILES, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
14383
+ path10.join(
14198
14384
  WINDOWS_PROGRAM_FILES_X86,
14199
14385
  "BraveSoftware",
14200
14386
  "Brave-Browser",
14201
14387
  "Application",
14202
14388
  "brave.exe"
14203
14389
  ),
14204
- path7.join("~", "AppData", "Local", "BraveSoftware", "Brave-Browser", "Application", "brave.exe")
14390
+ path10.join("~", "AppData", "Local", "BraveSoftware", "Brave-Browser", "Application", "brave.exe")
14205
14391
  ],
14206
14392
  userDataDir: "~/AppData/Local/BraveSoftware/Brave-Browser/User Data",
14207
14393
  processNames: ["/bravesoftware/brave-browser/application/brave.exe"]
@@ -14227,9 +14413,9 @@ var BROWSER_BRANDS = [
14227
14413
  },
14228
14414
  win32: {
14229
14415
  executableCandidates: [
14230
- path7.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
14231
- path7.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
14232
- path7.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
14416
+ path10.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
14417
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
14418
+ path10.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
14233
14419
  ],
14234
14420
  userDataDir: "~/AppData/Local/Microsoft/Edge/User Data",
14235
14421
  processNames: ["/microsoft/edge/application/msedge.exe"]
@@ -14257,9 +14443,9 @@ var BROWSER_BRANDS = [
14257
14443
  },
14258
14444
  win32: {
14259
14445
  executableCandidates: [
14260
- path7.join(WINDOWS_PROGRAM_FILES, "Vivaldi", "Application", "vivaldi.exe"),
14261
- path7.join(WINDOWS_PROGRAM_FILES_X86, "Vivaldi", "Application", "vivaldi.exe"),
14262
- path7.join("~", "AppData", "Local", "Vivaldi", "Application", "vivaldi.exe")
14446
+ path10.join(WINDOWS_PROGRAM_FILES, "Vivaldi", "Application", "vivaldi.exe"),
14447
+ path10.join(WINDOWS_PROGRAM_FILES_X86, "Vivaldi", "Application", "vivaldi.exe"),
14448
+ path10.join("~", "AppData", "Local", "Vivaldi", "Application", "vivaldi.exe")
14263
14449
  ],
14264
14450
  userDataDir: "~/AppData/Local/Vivaldi/User Data",
14265
14451
  processNames: ["/vivaldi/application/vivaldi.exe"]
@@ -14323,35 +14509,35 @@ function detectInstalledBrowserBrands() {
14323
14509
  brandId: brand2.id,
14324
14510
  displayName: brand2.displayName,
14325
14511
  executablePath,
14326
- userDataDir: path7.resolve(expandHome(platformConfig.userDataDir))
14512
+ userDataDir: path10.resolve(expandHome(platformConfig.userDataDir))
14327
14513
  });
14328
14514
  }
14329
14515
  return installations;
14330
14516
  }
14331
14517
  function resolveBrandUserDataDir(brand2, explicitDir) {
14332
14518
  if (explicitDir !== void 0) {
14333
- return path7.resolve(expandHome(explicitDir));
14519
+ return path10.resolve(expandHome(explicitDir));
14334
14520
  }
14335
14521
  const platformConfig = resolveBrandPlatformConfig(brand2);
14336
14522
  if (!platformConfig) {
14337
14523
  throw new Error(`${brand2.displayName} is not supported on ${process.platform}.`);
14338
14524
  }
14339
- return path7.resolve(expandHome(platformConfig.userDataDir));
14525
+ return path10.resolve(expandHome(platformConfig.userDataDir));
14340
14526
  }
14341
14527
  function resolveExecutableCandidates(candidates) {
14342
- return candidates.map((candidate) => candidate ? path7.resolve(expandHome(candidate)) : null);
14528
+ return candidates.map((candidate) => candidate ? path10.resolve(expandHome(candidate)) : null);
14343
14529
  }
14344
14530
 
14345
14531
  // src/local-browser/chrome-discovery.ts
14346
14532
  function expandHome(value) {
14347
14533
  if (value === "~" || value.startsWith("~/")) {
14348
- return path7.join(os.homedir(), value.slice(1));
14534
+ return path10.join(os.homedir(), value.slice(1));
14349
14535
  }
14350
14536
  return value;
14351
14537
  }
14352
14538
  function resolveChromeUserDataDir(userDataDir) {
14353
14539
  const installation = detectLocalChromeInstallations().find(
14354
- (candidate) => fs.existsSync(path7.join(candidate.userDataDir, "Local State")) || candidate.executablePath !== null
14540
+ (candidate) => fs.existsSync(path10.join(candidate.userDataDir, "Local State")) || candidate.executablePath !== null
14355
14541
  );
14356
14542
  if (!installation) {
14357
14543
  throw new Error("Could not find a local Chrome or Chromium profile directory.");
@@ -14360,7 +14546,7 @@ function resolveChromeUserDataDir(userDataDir) {
14360
14546
  }
14361
14547
  function resolveChromeExecutablePath(executablePath) {
14362
14548
  if (executablePath !== void 0) {
14363
- const resolvedPath = path7.resolve(expandHome(executablePath));
14549
+ const resolvedPath = path10.resolve(expandHome(executablePath));
14364
14550
  if (!fs.existsSync(resolvedPath)) {
14365
14551
  throw new Error(`Chrome executable was not found at "${resolvedPath}".`);
14366
14552
  }
@@ -14384,37 +14570,37 @@ function detectLocalChromeInstallations() {
14384
14570
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
14385
14571
  "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
14386
14572
  ]),
14387
- userDataDir: path7.join(os.homedir(), "Library", "Application Support", "Google", "Chrome")
14573
+ userDataDir: path10.join(os.homedir(), "Library", "Application Support", "Google", "Chrome")
14388
14574
  },
14389
14575
  {
14390
14576
  brand: "chromium",
14391
14577
  executablePath: firstExistingPath(["/Applications/Chromium.app/Contents/MacOS/Chromium"]),
14392
- userDataDir: path7.join(os.homedir(), "Library", "Application Support", "Chromium")
14578
+ userDataDir: path10.join(os.homedir(), "Library", "Application Support", "Chromium")
14393
14579
  }
14394
14580
  ];
14395
14581
  }
14396
14582
  if (process.platform === "win32") {
14397
14583
  const programFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
14398
14584
  const programFilesX86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
14399
- const localAppData = process.env.LOCALAPPDATA ?? path7.join(os.homedir(), "AppData", "Local");
14585
+ const localAppData = process.env.LOCALAPPDATA ?? path10.join(os.homedir(), "AppData", "Local");
14400
14586
  return [
14401
14587
  {
14402
14588
  brand: "chrome",
14403
14589
  executablePath: firstExistingPath([
14404
- path7.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"),
14405
- path7.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"),
14406
- path7.join(localAppData, "Google", "Chrome", "Application", "chrome.exe")
14590
+ path10.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"),
14591
+ path10.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"),
14592
+ path10.join(localAppData, "Google", "Chrome", "Application", "chrome.exe")
14407
14593
  ]),
14408
- userDataDir: path7.join(localAppData, "Google", "Chrome", "User Data")
14594
+ userDataDir: path10.join(localAppData, "Google", "Chrome", "User Data")
14409
14595
  },
14410
14596
  {
14411
14597
  brand: "chromium",
14412
14598
  executablePath: firstExistingPath([
14413
- path7.join(programFiles, "Chromium", "Application", "chrome.exe"),
14414
- path7.join(programFilesX86, "Chromium", "Application", "chrome.exe"),
14415
- path7.join(localAppData, "Chromium", "Application", "chrome.exe")
14599
+ path10.join(programFiles, "Chromium", "Application", "chrome.exe"),
14600
+ path10.join(programFilesX86, "Chromium", "Application", "chrome.exe"),
14601
+ path10.join(localAppData, "Chromium", "Application", "chrome.exe")
14416
14602
  ]),
14417
- userDataDir: path7.join(localAppData, "Chromium", "User Data")
14603
+ userDataDir: path10.join(localAppData, "Chromium", "User Data")
14418
14604
  }
14419
14605
  ];
14420
14606
  }
@@ -14427,7 +14613,7 @@ function detectLocalChromeInstallations() {
14427
14613
  resolveBinaryFromPath("google-chrome"),
14428
14614
  resolveBinaryFromPath("google-chrome-stable")
14429
14615
  ]),
14430
- userDataDir: path7.join(os.homedir(), ".config", "google-chrome")
14616
+ userDataDir: path10.join(os.homedir(), ".config", "google-chrome")
14431
14617
  },
14432
14618
  {
14433
14619
  brand: "chromium",
@@ -14437,7 +14623,7 @@ function detectLocalChromeInstallations() {
14437
14623
  resolveBinaryFromPath("chromium"),
14438
14624
  resolveBinaryFromPath("chromium-browser")
14439
14625
  ]),
14440
- userDataDir: path7.join(os.homedir(), ".config", "chromium")
14626
+ userDataDir: path10.join(os.homedir(), ".config", "chromium")
14441
14627
  }
14442
14628
  ];
14443
14629
  }
@@ -14449,8 +14635,8 @@ function detectLocalBrowserInstallations() {
14449
14635
  }));
14450
14636
  }
14451
14637
  function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14452
- const resolvedUserDataDir = path7.resolve(expandHome(userDataDir));
14453
- const localStatePath = path7.join(resolvedUserDataDir, "Local State");
14638
+ const resolvedUserDataDir = path10.resolve(expandHome(userDataDir));
14639
+ const localStatePath = path10.join(resolvedUserDataDir, "Local State");
14454
14640
  if (!fs.existsSync(localStatePath)) {
14455
14641
  return [];
14456
14642
  }
@@ -14462,7 +14648,7 @@ function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14462
14648
  }
14463
14649
  return Object.entries(infoCache).map(([directory, info]) => {
14464
14650
  const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
14465
- const name = typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : directory || path7.basename(directory);
14651
+ const name = typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : directory || path10.basename(directory);
14466
14652
  return {
14467
14653
  directory,
14468
14654
  name,
@@ -14474,7 +14660,7 @@ function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14474
14660
  }
14475
14661
  }
14476
14662
  function readDevToolsActivePort(userDataDir) {
14477
- const devToolsPath = path7.join(userDataDir, "DevToolsActivePort");
14663
+ const devToolsPath = path10.join(userDataDir, "DevToolsActivePort");
14478
14664
  if (!fs.existsSync(devToolsPath)) {
14479
14665
  return null;
14480
14666
  }
@@ -14707,8 +14893,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
14707
14893
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
14708
14894
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
14709
14895
  }
14710
- function normalizeWebSocketPath(path15) {
14711
- return path15.startsWith("/") ? path15 : `/${path15}`;
14896
+ function normalizeWebSocketPath(path18) {
14897
+ return path18.startsWith("/") ? path18 : `/${path18}`;
14712
14898
  }
14713
14899
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
14714
14900
  try {
@@ -14754,20 +14940,20 @@ var SESSION_SKIPPED_PROFILE_DIRECTORIES = /* @__PURE__ */ new Set([
14754
14940
  "Network"
14755
14941
  ]);
14756
14942
  async function createBrowserProfileSnapshot(input) {
14757
- const sourceUserDataDir = path7.resolve(expandHome(input.sourceUserDataDir));
14758
- const targetUserDataDir = path7.resolve(expandHome(input.targetUserDataDir));
14943
+ const sourceUserDataDir = path10.resolve(expandHome(input.sourceUserDataDir));
14944
+ const targetUserDataDir = path10.resolve(expandHome(input.targetUserDataDir));
14759
14945
  const profileDirectory = input.profileDirectory?.trim();
14760
14946
  const copyMode = input.copyMode;
14761
14947
  await promises.mkdir(targetUserDataDir, { recursive: true });
14762
14948
  await clearChromeSingletonEntries(targetUserDataDir);
14763
14949
  if (profileDirectory) {
14764
- const sourceProfileDir = path7.join(sourceUserDataDir, profileDirectory);
14950
+ const sourceProfileDir = path10.join(sourceUserDataDir, profileDirectory);
14765
14951
  if (!fs.existsSync(sourceProfileDir)) {
14766
14952
  throw new Error(
14767
14953
  `Chrome profile "${profileDirectory}" was not found in "${sourceUserDataDir}".`
14768
14954
  );
14769
14955
  }
14770
- await promises.cp(sourceProfileDir, path7.join(targetUserDataDir, profileDirectory), {
14956
+ await promises.cp(sourceProfileDir, path10.join(targetUserDataDir, profileDirectory), {
14771
14957
  recursive: true,
14772
14958
  filter: (candidate) => shouldCopyEntry({
14773
14959
  candidatePath: candidate,
@@ -14791,8 +14977,8 @@ async function copyRootLevelEntries(input) {
14791
14977
  if (CHROME_SINGLETON_ENTRIES.has(entry) || entry === input.selectedProfileDirectory) {
14792
14978
  continue;
14793
14979
  }
14794
- const sourcePath = path7.join(input.sourceUserDataDir, entry);
14795
- const targetPath = path7.join(input.targetUserDataDir, entry);
14980
+ const sourcePath = path10.join(input.sourceUserDataDir, entry);
14981
+ const targetPath = path10.join(input.targetUserDataDir, entry);
14796
14982
  const entryStat = await promises.stat(sourcePath).catch(() => null);
14797
14983
  if (!entryStat) {
14798
14984
  continue;
@@ -14824,7 +15010,7 @@ async function copyRootLevelEntries(input) {
14824
15010
  }
14825
15011
  }
14826
15012
  function isProfileDirectory(userDataDir, entry) {
14827
- return fs.existsSync(path7.join(userDataDir, entry, "Preferences"));
15013
+ return fs.existsSync(path10.join(userDataDir, entry, "Preferences"));
14828
15014
  }
14829
15015
  function shouldCopyEntry(input) {
14830
15016
  const entryName = input.candidatePath.split("/").at(-1)?.split("\\").at(-1) ?? input.candidatePath;
@@ -14834,7 +15020,7 @@ function shouldCopyEntry(input) {
14834
15020
  if (input.copyMode !== "session") {
14835
15021
  return true;
14836
15022
  }
14837
- const relativePath = path7.relative(input.rootPath, input.candidatePath);
15023
+ const relativePath = path10.relative(input.rootPath, input.candidatePath);
14838
15024
  if (relativePath.length === 0) {
14839
15025
  return true;
14840
15026
  }
@@ -15316,7 +15502,7 @@ function pickStealthProfilePreset(overrides) {
15316
15502
  var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
15317
15503
  var OPENSTEER_LIVE_SESSION_VERSION = 1;
15318
15504
  function resolveLiveSessionRecordPath(rootPath, provider) {
15319
- return path7__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
15505
+ return path10__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
15320
15506
  }
15321
15507
  function resolveLocalSessionRecordPath(rootPath) {
15322
15508
  return resolveLiveSessionRecordPath(rootPath, "local");
@@ -15336,27 +15522,467 @@ async function readPersistedSessionRecord(rootPath, provider) {
15336
15522
  if (provider === "cloud" && isPersistedCloudSessionRecord(parsed)) {
15337
15523
  return parsed;
15338
15524
  }
15339
- return void 0;
15525
+ return void 0;
15526
+ }
15527
+ async function readPersistedCloudSessionRecord(rootPath) {
15528
+ const record = await readPersistedSessionRecord(rootPath, "cloud");
15529
+ return record?.provider === "cloud" ? record : void 0;
15530
+ }
15531
+ async function readPersistedLocalBrowserSessionRecord(rootPath) {
15532
+ const record = await readPersistedSessionRecord(rootPath, "local");
15533
+ return record?.provider === "local" ? record : void 0;
15534
+ }
15535
+ async function writePersistedSessionRecord(rootPath, record) {
15536
+ await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath, record.provider), record);
15537
+ }
15538
+ async function clearPersistedSessionRecord(rootPath, provider) {
15539
+ await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
15540
+ }
15541
+ function isPersistedCloudSessionRecord(value) {
15542
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
15543
+ }
15544
+ function isPersistedLocalBrowserSessionRecord(value) {
15545
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
15546
+ }
15547
+ function resolveOpensteerStateDir() {
15548
+ const explicit = process.env.OPENSTEER_HOME?.trim();
15549
+ if (explicit) {
15550
+ return path10__default.default.resolve(explicit);
15551
+ }
15552
+ if (process.platform === "win32") {
15553
+ return path10__default.default.join(
15554
+ process.env.LOCALAPPDATA ?? path10__default.default.join(os.homedir(), "AppData", "Local"),
15555
+ "Opensteer"
15556
+ );
15557
+ }
15558
+ if (process.platform === "darwin") {
15559
+ return path10__default.default.join(os.homedir(), "Library", "Application Support", "Opensteer");
15560
+ }
15561
+ return path10__default.default.join(
15562
+ process.env.XDG_STATE_HOME ?? path10__default.default.join(os.homedir(), ".local", "state"),
15563
+ "opensteer"
15564
+ );
15565
+ }
15566
+ function resolveLocalViewRootDir() {
15567
+ return path10__default.default.join(resolveOpensteerStateDir(), "local-view");
15568
+ }
15569
+ function resolveLocalViewPreferencesPath() {
15570
+ return path10__default.default.join(resolveLocalViewRootDir(), "preferences.json");
15571
+ }
15572
+ function resolveLocalViewServiceDir() {
15573
+ return path10__default.default.join(resolveLocalViewRootDir(), "service");
15574
+ }
15575
+ function resolveLocalViewSessionsDir() {
15576
+ return path10__default.default.join(resolveLocalViewRootDir(), "sessions");
15577
+ }
15578
+ function resolveLocalViewServiceLockDir() {
15579
+ return path10__default.default.join(resolveLocalViewServiceDir(), "startup.lock");
15580
+ }
15581
+ function resolveLocalViewServiceStatePath() {
15582
+ return path10__default.default.join(resolveLocalViewServiceDir(), "state.json");
15583
+ }
15584
+
15585
+ // src/local-view/preferences.ts
15586
+ var OPENSTEER_LOCAL_VIEW_PREFERENCES_LAYOUT = "opensteer-local-view-preferences";
15587
+ var OPENSTEER_LOCAL_VIEW_PREFERENCES_VERSION = 1;
15588
+ async function resolveLocalViewMode() {
15589
+ const preferences = await readLocalViewPreferences();
15590
+ return preferences?.mode ?? "auto";
15591
+ }
15592
+ async function readLocalViewPreferences() {
15593
+ const preferencesPath = resolveLocalViewPreferencesPath();
15594
+ if (!await pathExists(preferencesPath)) {
15595
+ return void 0;
15596
+ }
15597
+ const parsed = await readJsonFile(preferencesPath);
15598
+ return isPersistedLocalViewPreferences(parsed) ? parsed : void 0;
15599
+ }
15600
+ function isPersistedLocalViewPreferences(value) {
15601
+ return value?.layout === OPENSTEER_LOCAL_VIEW_PREFERENCES_LAYOUT && value.version === OPENSTEER_LOCAL_VIEW_PREFERENCES_VERSION && (value.mode === "auto" || value.mode === "manual") && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
15602
+ }
15603
+ var LOCK_OWNER_FILE = "owner.json";
15604
+ var LOCK_RECLAIMER_DIR = "reclaimer";
15605
+ var LOCK_RETRY_DELAY_MS = 50;
15606
+ async function acquireDirLock(lockDirPath) {
15607
+ while (true) {
15608
+ const releaseLock = await tryAcquireDirLock(lockDirPath);
15609
+ if (releaseLock) {
15610
+ return releaseLock;
15611
+ }
15612
+ await sleep(LOCK_RETRY_DELAY_MS);
15613
+ }
15614
+ }
15615
+ async function tryAcquireDirLock(lockDirPath) {
15616
+ await promises.mkdir(path10.dirname(lockDirPath), { recursive: true });
15617
+ while (true) {
15618
+ const tempLockDirPath = `${lockDirPath}-${String(process.pid)}-${String(CURRENT_PROCESS_OWNER.processStartedAtMs)}-${crypto.randomUUID()}`;
15619
+ try {
15620
+ await promises.mkdir(tempLockDirPath);
15621
+ await writeLockOwner(tempLockDirPath, CURRENT_PROCESS_OWNER);
15622
+ try {
15623
+ await promises.rename(tempLockDirPath, lockDirPath);
15624
+ break;
15625
+ } catch (error) {
15626
+ if (!wasDirPublishedByAnotherProcess(error, lockDirPath)) {
15627
+ throw error;
15628
+ }
15629
+ }
15630
+ } finally {
15631
+ await promises.rm(tempLockDirPath, {
15632
+ recursive: true,
15633
+ force: true
15634
+ }).catch(() => void 0);
15635
+ }
15636
+ const owner = await readLockOwner(lockDirPath);
15637
+ if ((!owner || await getProcessLiveness(owner) === "dead") && await tryReclaimStaleLock(lockDirPath, owner)) {
15638
+ continue;
15639
+ }
15640
+ return null;
15641
+ }
15642
+ return async () => {
15643
+ await promises.rm(lockDirPath, {
15644
+ recursive: true,
15645
+ force: true
15646
+ }).catch(() => void 0);
15647
+ };
15648
+ }
15649
+ function getErrorCode(error) {
15650
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
15651
+ }
15652
+ function wasDirPublishedByAnotherProcess(error, targetDirPath) {
15653
+ const code = getErrorCode(error);
15654
+ return fs.existsSync(targetDirPath) && (code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM");
15655
+ }
15656
+ async function writeLockOwner(lockDirPath, owner) {
15657
+ await promises.writeFile(path10.join(lockDirPath, LOCK_OWNER_FILE), JSON.stringify(owner));
15658
+ }
15659
+ async function readLockOwner(lockDirPath) {
15660
+ return readLockParticipant(path10.join(lockDirPath, LOCK_OWNER_FILE));
15661
+ }
15662
+ async function readLockParticipant(filePath) {
15663
+ return (await readLockParticipantRecord(filePath)).owner;
15664
+ }
15665
+ async function readLockParticipantRecord(filePath) {
15666
+ try {
15667
+ const raw = await promises.readFile(filePath, "utf8");
15668
+ return {
15669
+ exists: true,
15670
+ owner: parseProcessOwner(JSON.parse(raw))
15671
+ };
15672
+ } catch (error) {
15673
+ return {
15674
+ exists: getErrorCode(error) !== "ENOENT",
15675
+ owner: null
15676
+ };
15677
+ }
15678
+ }
15679
+ async function readLockReclaimerRecord(lockDirPath) {
15680
+ return readLockParticipantRecord(path10.join(buildLockReclaimerDirPath(lockDirPath), LOCK_OWNER_FILE));
15681
+ }
15682
+ async function tryReclaimStaleLock(lockDirPath, expectedOwner) {
15683
+ if (!await tryAcquireLockReclaimer(lockDirPath)) {
15684
+ return false;
15685
+ }
15686
+ let reclaimed = false;
15687
+ try {
15688
+ const owner = await readLockOwner(lockDirPath);
15689
+ if (!processOwnersEqual(owner, expectedOwner)) {
15690
+ return false;
15691
+ }
15692
+ if (owner && await getProcessLiveness(owner) !== "dead") {
15693
+ return false;
15694
+ }
15695
+ await promises.rm(lockDirPath, {
15696
+ recursive: true,
15697
+ force: true
15698
+ }).catch(() => void 0);
15699
+ reclaimed = !fs.existsSync(lockDirPath);
15700
+ return reclaimed;
15701
+ } finally {
15702
+ if (!reclaimed) {
15703
+ await promises.rm(buildLockReclaimerDirPath(lockDirPath), {
15704
+ recursive: true,
15705
+ force: true
15706
+ }).catch(() => void 0);
15707
+ }
15708
+ }
15709
+ }
15710
+ async function tryAcquireLockReclaimer(lockDirPath) {
15711
+ const reclaimerDirPath = buildLockReclaimerDirPath(lockDirPath);
15712
+ while (true) {
15713
+ const tempReclaimerDirPath = `${reclaimerDirPath}-${String(process.pid)}-${String(CURRENT_PROCESS_OWNER.processStartedAtMs)}-${crypto.randomUUID()}`;
15714
+ try {
15715
+ await promises.mkdir(tempReclaimerDirPath);
15716
+ await writeLockOwner(tempReclaimerDirPath, CURRENT_PROCESS_OWNER);
15717
+ try {
15718
+ await promises.rename(tempReclaimerDirPath, reclaimerDirPath);
15719
+ return true;
15720
+ } catch (error) {
15721
+ if (getErrorCode(error) === "ENOENT") {
15722
+ return false;
15723
+ }
15724
+ if (!wasDirPublishedByAnotherProcess(error, reclaimerDirPath)) {
15725
+ throw error;
15726
+ }
15727
+ }
15728
+ } catch (error) {
15729
+ if (getErrorCode(error) === "ENOENT") {
15730
+ return false;
15731
+ }
15732
+ throw error;
15733
+ } finally {
15734
+ await promises.rm(tempReclaimerDirPath, {
15735
+ recursive: true,
15736
+ force: true
15737
+ }).catch(() => void 0);
15738
+ }
15739
+ const reclaimerRecord = await readLockReclaimerRecord(lockDirPath);
15740
+ if (!reclaimerRecord.exists || !reclaimerRecord.owner) {
15741
+ return false;
15742
+ }
15743
+ if (await getProcessLiveness(reclaimerRecord.owner) !== "dead") {
15744
+ return false;
15745
+ }
15746
+ await promises.rm(reclaimerDirPath, {
15747
+ recursive: true,
15748
+ force: true
15749
+ }).catch(() => void 0);
15750
+ }
15751
+ }
15752
+ function buildLockReclaimerDirPath(lockDirPath) {
15753
+ return path10.join(lockDirPath, LOCK_RECLAIMER_DIR);
15754
+ }
15755
+ async function sleep(ms) {
15756
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
15757
+ }
15758
+ var OPENSTEER_LOCAL_VIEW_SERVICE_LAYOUT = "opensteer-local-view-service";
15759
+ var OPENSTEER_LOCAL_VIEW_SERVICE_VERSION = 3;
15760
+ async function readLocalViewServiceState() {
15761
+ const statePath = resolveLocalViewServiceStatePath();
15762
+ if (!await pathExists(statePath)) {
15763
+ return void 0;
15764
+ }
15765
+ const parsed = await readJsonFile(statePath);
15766
+ if (!isPersistedLocalViewServiceState(parsed)) {
15767
+ return void 0;
15768
+ }
15769
+ return parsed;
15770
+ }
15771
+ async function isLocalViewServiceStateLive(state) {
15772
+ return await getLocalViewServiceStateLiveness(state) !== "dead";
15773
+ }
15774
+ async function getLocalViewServiceStateLiveness(state) {
15775
+ if (state === void 0) {
15776
+ return "dead";
15777
+ }
15778
+ return getProcessLiveness({
15779
+ pid: state.pid,
15780
+ processStartedAtMs: state.processStartedAtMs
15781
+ });
15782
+ }
15783
+ function isPersistedLocalViewServiceState(value) {
15784
+ return value?.layout === OPENSTEER_LOCAL_VIEW_SERVICE_LAYOUT && value.version === OPENSTEER_LOCAL_VIEW_SERVICE_VERSION && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.processStartedAtMs === "number" && Number.isFinite(value.processStartedAtMs) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.port === "number" && Number.isFinite(value.port) && typeof value.token === "string" && value.token.length > 0 && typeof value.url === "string" && value.url.length > 0;
15785
+ }
15786
+
15787
+ // src/local-view/service.ts
15788
+ var LOCAL_VIEW_STARTUP_TIMEOUT_MS = 1e4;
15789
+ var LOCAL_VIEW_STARTUP_POLL_MS = 100;
15790
+ async function ensureLocalViewServiceRunning() {
15791
+ const current = await readReachableLocalViewServiceState();
15792
+ if (current !== void 0) {
15793
+ return current;
15794
+ }
15795
+ const releaseLock = await acquireDirLock(resolveLocalViewServiceLockDir());
15796
+ try {
15797
+ const lockedState = await readReachableLocalViewServiceState();
15798
+ if (lockedState !== void 0) {
15799
+ return lockedState;
15800
+ }
15801
+ await spawnLocalViewService();
15802
+ const started = await waitForLocalViewService();
15803
+ if (!started) {
15804
+ throw new Error("Timed out while starting the local view service.");
15805
+ }
15806
+ return started;
15807
+ } finally {
15808
+ await releaseLock();
15809
+ }
15810
+ }
15811
+ function spawnLocalViewService() {
15812
+ const command = resolveLocalViewSpawnCommand();
15813
+ const child = child_process.spawn(command.executable, command.args, {
15814
+ cwd: process.cwd(),
15815
+ env: {
15816
+ ...process.env,
15817
+ ...command.env ?? {},
15818
+ OPENSTEER_LOCAL_VIEW_BOOT_TOKEN: process.env.OPENSTEER_LOCAL_VIEW_BOOT_TOKEN ?? crypto.randomBytes(24).toString("hex")
15819
+ },
15820
+ detached: process.platform !== "win32",
15821
+ stdio: "ignore"
15822
+ });
15823
+ child.unref();
15824
+ }
15825
+ async function waitForLocalViewService() {
15826
+ const deadline = Date.now() + LOCAL_VIEW_STARTUP_TIMEOUT_MS;
15827
+ while (Date.now() < deadline) {
15828
+ const state = await readReachableLocalViewServiceState();
15829
+ if (state !== void 0) {
15830
+ return state;
15831
+ }
15832
+ await delay(LOCAL_VIEW_STARTUP_POLL_MS);
15833
+ }
15834
+ return void 0;
15835
+ }
15836
+ async function readReachableLocalViewServiceState() {
15837
+ const state = await readLocalViewServiceState();
15838
+ if (state === void 0 || !await isLocalViewServiceStateLive(state)) {
15839
+ return void 0;
15840
+ }
15841
+ return await isLocalViewServiceReachable(state.url, state.token) ? state : void 0;
15842
+ }
15843
+ async function isLocalViewServiceReachable(baseUrl, token) {
15844
+ try {
15845
+ const response = await fetch(new URL("/api/health", baseUrl), {
15846
+ headers: {
15847
+ "x-opensteer-local-token": token
15848
+ }
15849
+ });
15850
+ return response.ok;
15851
+ } catch {
15852
+ return false;
15853
+ }
15854
+ }
15855
+ function resolveLocalViewSpawnCommand() {
15856
+ const moduleDir = path10__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
15857
+ const distServicePath = findExistingPath([
15858
+ path10__default.default.join(moduleDir, "local-view", "serve-entry.js"),
15859
+ path10__default.default.join(moduleDir, "serve-entry.js"),
15860
+ path10__default.default.join(moduleDir, "..", "local-view", "serve-entry.js")
15861
+ ]);
15862
+ if (distServicePath) {
15863
+ return {
15864
+ executable: process.execPath,
15865
+ args: [distServicePath]
15866
+ };
15867
+ }
15868
+ const distCliPath = findExistingPath([
15869
+ path10__default.default.join(moduleDir, "cli", "bin.js"),
15870
+ path10__default.default.join(moduleDir, "..", "cli", "bin.js")
15871
+ ]);
15872
+ if (distCliPath) {
15873
+ return {
15874
+ executable: process.execPath,
15875
+ args: [distCliPath, "view", "serve"]
15876
+ };
15877
+ }
15878
+ const srcServicePath = findExistingPath([
15879
+ path10__default.default.join(moduleDir, "serve-entry.ts"),
15880
+ path10__default.default.join(moduleDir, "..", "local-view", "serve-entry.ts"),
15881
+ path10__default.default.join(moduleDir, "..", "src", "local-view", "serve-entry.ts")
15882
+ ]);
15883
+ if (srcServicePath) {
15884
+ const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
15885
+ const tsxLoaderPath = require2.resolve("tsx");
15886
+ const tsconfigPath = findNearestTsconfig(path10__default.default.resolve(moduleDir, "..", "..", ".."));
15887
+ return {
15888
+ executable: process.execPath,
15889
+ args: ["--import", tsxLoaderPath, srcServicePath],
15890
+ ...tsconfigPath ? { env: { TSX_TSCONFIG_PATH: tsconfigPath } } : {}
15891
+ };
15892
+ }
15893
+ const srcCliPath = findExistingPath([
15894
+ path10__default.default.join(moduleDir, "..", "cli", "bin.ts"),
15895
+ path10__default.default.join(moduleDir, "..", "src", "cli", "bin.ts")
15896
+ ]);
15897
+ if (srcCliPath) {
15898
+ const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
15899
+ const tsxLoaderPath = require2.resolve("tsx");
15900
+ const tsconfigPath = findNearestTsconfig(path10__default.default.resolve(moduleDir, "..", "..", ".."));
15901
+ return {
15902
+ executable: process.execPath,
15903
+ args: ["--import", tsxLoaderPath, srcCliPath, "view", "serve"],
15904
+ ...tsconfigPath ? { env: { TSX_TSCONFIG_PATH: tsconfigPath } } : {}
15905
+ };
15906
+ }
15907
+ throw new Error(`Could not resolve the Opensteer CLI entrypoint from ${moduleDir}.`);
15908
+ }
15909
+ function findExistingPath(candidates) {
15910
+ return candidates.find((candidate) => fs.existsSync(candidate));
15911
+ }
15912
+ function findNearestTsconfig(startDir) {
15913
+ let currentDir = startDir;
15914
+ while (true) {
15915
+ const candidate = path10__default.default.join(currentDir, "tsconfig.json");
15916
+ if (fs.existsSync(candidate)) {
15917
+ return candidate;
15918
+ }
15919
+ const parentDir = path10__default.default.dirname(currentDir);
15920
+ if (parentDir === currentDir) {
15921
+ return void 0;
15922
+ }
15923
+ currentDir = parentDir;
15924
+ }
15340
15925
  }
15341
- async function readPersistedCloudSessionRecord(rootPath) {
15342
- const record = await readPersistedSessionRecord(rootPath, "cloud");
15343
- return record?.provider === "cloud" ? record : void 0;
15926
+ function delay(ms) {
15927
+ return new Promise((resolve4) => {
15928
+ setTimeout(resolve4, ms);
15929
+ });
15344
15930
  }
15345
- async function readPersistedLocalBrowserSessionRecord(rootPath) {
15346
- const record = await readPersistedSessionRecord(rootPath, "local");
15347
- return record?.provider === "local" ? record : void 0;
15931
+ var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
15932
+ var OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
15933
+ function buildLocalViewSessionId(input) {
15934
+ const hash = crypto.createHash("sha256").update(`${input.rootPath}
15935
+ ${String(input.pid)}
15936
+ ${String(input.startedAt)}`).digest("hex");
15937
+ return `local_${hash.slice(0, 24)}`;
15348
15938
  }
15349
- async function writePersistedSessionRecord(rootPath, record) {
15350
- await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath, record.provider), record);
15939
+ function createLocalViewSessionManifest(input) {
15940
+ return {
15941
+ layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
15942
+ version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
15943
+ sessionId: buildLocalViewSessionId({
15944
+ rootPath: input.rootPath,
15945
+ pid: input.live.pid,
15946
+ startedAt: input.live.startedAt
15947
+ }),
15948
+ rootPath: input.rootPath,
15949
+ ...input.workspace === void 0 ? {} : { workspace: input.workspace },
15950
+ engine: input.live.engine,
15951
+ ownership: input.ownership,
15952
+ pid: input.live.pid,
15953
+ startedAt: input.live.startedAt,
15954
+ updatedAt: Date.now()
15955
+ };
15351
15956
  }
15352
- async function clearPersistedSessionRecord(rootPath, provider) {
15353
- await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
15957
+ async function writeLocalViewSessionManifest(manifest) {
15958
+ await ensureDirectory(resolveLocalViewSessionsDir());
15959
+ await writeJsonFileAtomic(resolveLocalViewSessionManifestPath(manifest.sessionId), manifest);
15354
15960
  }
15355
- function isPersistedCloudSessionRecord(value) {
15356
- return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
15961
+ async function deleteLocalViewSessionManifest(sessionId) {
15962
+ await promises.rm(resolveLocalViewSessionManifestPath(sessionId), { force: true }).catch(() => void 0);
15357
15963
  }
15358
- function isPersistedLocalBrowserSessionRecord(value) {
15359
- return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
15964
+ function resolveLocalViewSessionManifestPath(sessionId) {
15965
+ return path10__default.default.join(resolveLocalViewSessionsDir(), `${sessionId}.json`);
15966
+ }
15967
+
15968
+ // src/local-view/registration.ts
15969
+ async function bestEffortRegisterLocalViewSession(input) {
15970
+ try {
15971
+ const manifest = createLocalViewSessionManifest(input);
15972
+ await writeLocalViewSessionManifest(manifest);
15973
+ if (await resolveLocalViewMode() === "auto") {
15974
+ void ensureLocalViewServiceRunning().catch(() => void 0);
15975
+ }
15976
+ return manifest;
15977
+ } catch {
15978
+ return void 0;
15979
+ }
15980
+ }
15981
+ async function bestEffortUnregisterLocalViewSession(sessionId) {
15982
+ if (!sessionId) {
15983
+ return;
15984
+ }
15985
+ await deleteLocalViewSessionManifest(sessionId).catch(() => void 0);
15360
15986
  }
15361
15987
 
15362
15988
  // ../runtime-core/src/internal/engine-selection.ts
@@ -15476,8 +16102,8 @@ var OpensteerBrowserManager = class {
15476
16102
  ...options.browser === void 0 ? {} : { browser: options.browser },
15477
16103
  ...this.contextOptions === void 0 ? {} : { context: this.contextOptions }
15478
16104
  });
15479
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
15480
- rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
16105
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path10__default.default.join(os.tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
16106
+ rootDir: path10__default.default.resolve(options.rootDir ?? process.cwd()),
15481
16107
  workspace: this.workspace
15482
16108
  }));
15483
16109
  this.cleanupRootOnDisconnect = this.workspace === void 0;
@@ -15538,7 +16164,7 @@ var OpensteerBrowserManager = class {
15538
16164
  userDataDir: "browser/user-data",
15539
16165
  bootstrap: {
15540
16166
  kind: "cloneLocalProfile",
15541
- sourceUserDataDir: path7__default.default.resolve(input.sourceUserDataDir),
16167
+ sourceUserDataDir: path10__default.default.resolve(input.sourceUserDataDir),
15542
16168
  ...input.sourceProfileDirectory === void 0 ? {} : { sourceProfileDirectory: input.sourceProfileDirectory }
15543
16169
  }
15544
16170
  };
@@ -15611,6 +16237,12 @@ var OpensteerBrowserManager = class {
15611
16237
  `workspace "${this.workspace}" already has a live ${live.engine} browser. Close it before reopening with engine "abp".`
15612
16238
  );
15613
16239
  }
16240
+ await bestEffortRegisterLocalViewSession({
16241
+ rootPath: workspace.rootPath,
16242
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
16243
+ live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
16244
+ ownership: "owned"
16245
+ });
15614
16246
  return this.createAdoptedAbpEngine(live);
15615
16247
  }
15616
16248
  await this.ensurePersistentBrowserManifest(workspace);
@@ -15643,9 +16275,17 @@ var OpensteerBrowserManager = class {
15643
16275
  ...launch?.browserExecutablePath === void 0 ? {} : { executablePath: launch.browserExecutablePath }
15644
16276
  };
15645
16277
  await this.writeLivePersistentBrowser(workspace, liveRecord);
16278
+ const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
16279
+ await bestEffortRegisterLocalViewSession({
16280
+ rootPath: workspace.rootPath,
16281
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
16282
+ live: persistedLiveRecord,
16283
+ ownership: "owned"
16284
+ });
15646
16285
  try {
15647
16286
  return await this.createAdoptedAbpEngine(liveRecord);
15648
16287
  } catch (error) {
16288
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
15649
16289
  await terminateProcess(launched.process.pid ?? 0).catch(() => void 0);
15650
16290
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
15651
16291
  throw error;
@@ -15665,23 +16305,45 @@ var OpensteerBrowserManager = class {
15665
16305
  });
15666
16306
  }
15667
16307
  async createTemporaryEngine() {
15668
- const userDataDir = await promises.mkdtemp(path7__default.default.join(os.tmpdir(), "opensteer-temporary-browser-"));
16308
+ const userDataDir = await promises.mkdtemp(path10__default.default.join(os.tmpdir(), "opensteer-temporary-browser-"));
15669
16309
  await clearChromeSingletonEntries(userDataDir);
15670
16310
  const launched = await launchOwnedBrowser({
15671
16311
  userDataDir,
15672
16312
  ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions },
15673
16313
  ...this.contextOptions?.viewport === void 0 ? {} : { viewport: this.contextOptions.viewport }
15674
16314
  });
16315
+ const temporaryLiveRecord = {
16316
+ layout: "opensteer-session",
16317
+ version: 1,
16318
+ provider: "local",
16319
+ engine: "playwright",
16320
+ endpoint: launched.endpoint,
16321
+ pid: launched.pid,
16322
+ startedAt: Date.now(),
16323
+ updatedAt: Date.now(),
16324
+ executablePath: launched.executablePath,
16325
+ userDataDir
16326
+ };
16327
+ await writePersistedSessionRecord(this.rootPath, temporaryLiveRecord);
16328
+ const localViewManifest = await bestEffortRegisterLocalViewSession({
16329
+ rootPath: this.rootPath,
16330
+ live: temporaryLiveRecord,
16331
+ ownership: "owned"
16332
+ });
15675
16333
  try {
15676
16334
  return await this.createAttachedEngine({
15677
16335
  endpoint: launched.endpoint,
15678
16336
  freshTab: false,
15679
16337
  onDispose: async () => {
16338
+ await bestEffortUnregisterLocalViewSession(localViewManifest?.sessionId);
16339
+ await clearPersistedSessionRecord(this.rootPath, "local").catch(() => void 0);
15680
16340
  await terminateProcess(launched.pid).catch(() => void 0);
15681
16341
  await promises.rm(userDataDir, { recursive: true, force: true }).catch(() => void 0);
15682
16342
  }
15683
16343
  });
15684
16344
  } catch (error) {
16345
+ await bestEffortUnregisterLocalViewSession(localViewManifest?.sessionId);
16346
+ await clearPersistedSessionRecord(this.rootPath, "local").catch(() => void 0);
15685
16347
  await terminateProcess(launched.pid).catch(() => void 0);
15686
16348
  await promises.rm(userDataDir, { recursive: true, force: true }).catch(() => void 0);
15687
16349
  throw error;
@@ -15709,6 +16371,12 @@ var OpensteerBrowserManager = class {
15709
16371
  if (live.endpoint === void 0) {
15710
16372
  throw new Error("workspace live browser record is missing a DevTools endpoint.");
15711
16373
  }
16374
+ await bestEffortRegisterLocalViewSession({
16375
+ rootPath: workspace.rootPath,
16376
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
16377
+ live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
16378
+ ownership: "owned"
16379
+ });
15712
16380
  return this.createAttachedEngine({
15713
16381
  endpoint: live.endpoint,
15714
16382
  freshTab: false,
@@ -15731,6 +16399,13 @@ var OpensteerBrowserManager = class {
15731
16399
  userDataDir: workspace.browserUserDataDir
15732
16400
  };
15733
16401
  await this.writeLivePersistentBrowser(workspace, liveRecord);
16402
+ const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
16403
+ await bestEffortRegisterLocalViewSession({
16404
+ rootPath: workspace.rootPath,
16405
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
16406
+ live: persistedLiveRecord,
16407
+ ownership: "owned"
16408
+ });
15734
16409
  try {
15735
16410
  return await this.createAttachedEngine({
15736
16411
  endpoint: launched.endpoint,
@@ -15738,6 +16413,7 @@ var OpensteerBrowserManager = class {
15738
16413
  onDispose: async () => void 0
15739
16414
  });
15740
16415
  } catch (error) {
16416
+ await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
15741
16417
  await terminateProcess(launched.pid).catch(() => void 0);
15742
16418
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
15743
16419
  throw error;
@@ -15888,6 +16564,10 @@ var OpensteerBrowserManager = class {
15888
16564
  await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
15889
16565
  return;
15890
16566
  }
16567
+ await this.unregisterLocalViewSessionForRecord(
16568
+ workspace.rootPath,
16569
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
16570
+ );
15891
16571
  if (live.engine === "playwright") {
15892
16572
  if (live.endpoint !== void 0) {
15893
16573
  await requestBrowserClose(live.endpoint).catch(() => void 0);
@@ -15914,6 +16594,15 @@ var OpensteerBrowserManager = class {
15914
16594
  throw new Error(`browser.${method}() requires a persistent workspace browser.`);
15915
16595
  }
15916
16596
  }
16597
+ async unregisterLocalViewSessionForRecord(rootPath, record) {
16598
+ await bestEffortUnregisterLocalViewSession(
16599
+ buildLocalViewSessionId({
16600
+ rootPath,
16601
+ pid: record.pid,
16602
+ startedAt: record.startedAt
16603
+ })
16604
+ );
16605
+ }
15917
16606
  };
15918
16607
  function normalizeWorkspace(workspace) {
15919
16608
  const normalized = workspace?.trim();
@@ -15977,8 +16666,14 @@ async function launchOwnedBrowser(input) {
15977
16666
  await ensureDirectory(input.userDataDir);
15978
16667
  await clearChromeSingletonEntries(input.userDataDir);
15979
16668
  await sanitizeChromeProfile(input.userDataDir);
16669
+ const requestedRemoteDebuggingPort = readRequestedRemoteDebuggingPort(input.launch?.args);
15980
16670
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
15981
- const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
16671
+ const args = buildChromeArgs(
16672
+ input.userDataDir,
16673
+ input.launch,
16674
+ input.viewport,
16675
+ requestedRemoteDebuggingPort
16676
+ );
15982
16677
  const child = child_process.spawn(executablePath, args, {
15983
16678
  stdio: ["ignore", "ignore", "pipe"],
15984
16679
  detached: process.platform !== "win32"
@@ -15994,7 +16689,8 @@ async function launchOwnedBrowser(input) {
15994
16689
  userDataDir: input.userDataDir,
15995
16690
  timeoutMs: input.launch?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
15996
16691
  childExited: async () => child.exitCode,
15997
- stderrLines
16692
+ stderrLines,
16693
+ ...requestedRemoteDebuggingPort !== void 0 && requestedRemoteDebuggingPort > 0 ? { requestedRemoteDebuggingPort } : {}
15998
16694
  }).catch(async (error) => {
15999
16695
  child.kill("SIGKILL");
16000
16696
  throw error;
@@ -16005,10 +16701,10 @@ async function launchOwnedBrowser(input) {
16005
16701
  executablePath
16006
16702
  };
16007
16703
  }
16008
- function buildChromeArgs(userDataDir, launch, viewport) {
16704
+ function buildChromeArgs(userDataDir, launch, viewport, requestedRemoteDebuggingPort) {
16009
16705
  const isHeadless = launch?.headless ?? true;
16010
16706
  const args = [
16011
- "--remote-debugging-port=0",
16707
+ ...requestedRemoteDebuggingPort === void 0 ? ["--remote-debugging-port=0"] : [],
16012
16708
  "--no-first-run",
16013
16709
  "--no-default-browser-check",
16014
16710
  "--disable-blink-features=AutomationControlled",
@@ -16059,14 +16755,69 @@ async function waitForDevToolsEndpoint(input) {
16059
16755
  return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
16060
16756
  }
16061
16757
  }
16758
+ if (input.requestedRemoteDebuggingPort !== void 0) {
16759
+ const endpoint = await tryInspectRemoteDebuggingPort(
16760
+ input.requestedRemoteDebuggingPort,
16761
+ input.timeoutMs
16762
+ );
16763
+ if (endpoint !== void 0) {
16764
+ return endpoint;
16765
+ }
16766
+ }
16062
16767
  const exitCode = await input.childExited();
16063
16768
  if (exitCode !== null) {
16064
16769
  throw new Error(formatChromeLaunchError(input.stderrLines));
16065
16770
  }
16066
- await sleep(DEVTOOLS_POLL_INTERVAL_MS);
16771
+ await sleep2(DEVTOOLS_POLL_INTERVAL_MS);
16067
16772
  }
16068
16773
  throw new Error(formatChromeLaunchError(input.stderrLines));
16069
16774
  }
16775
+ function readRequestedRemoteDebuggingPort(args) {
16776
+ if (args === void 0 || args.length === 0) {
16777
+ return void 0;
16778
+ }
16779
+ let explicitFlagFound = false;
16780
+ let port;
16781
+ for (let index = 0; index < args.length; index += 1) {
16782
+ const entry = args[index];
16783
+ if (entry === "--remote-debugging-port") {
16784
+ explicitFlagFound = true;
16785
+ const next = args[index + 1];
16786
+ if (next !== void 0) {
16787
+ port = parseRemoteDebuggingPort(next);
16788
+ index += 1;
16789
+ }
16790
+ continue;
16791
+ }
16792
+ if (entry.startsWith("--remote-debugging-port=")) {
16793
+ explicitFlagFound = true;
16794
+ port = parseRemoteDebuggingPort(entry.slice("--remote-debugging-port=".length));
16795
+ }
16796
+ }
16797
+ return explicitFlagFound ? port : void 0;
16798
+ }
16799
+ function parseRemoteDebuggingPort(value) {
16800
+ const trimmed = value.trim();
16801
+ if (!/^\d+$/.test(trimmed)) {
16802
+ return void 0;
16803
+ }
16804
+ const parsed = Number.parseInt(trimmed, 10);
16805
+ if (!Number.isInteger(parsed) || parsed < 0) {
16806
+ return void 0;
16807
+ }
16808
+ return parsed;
16809
+ }
16810
+ async function tryInspectRemoteDebuggingPort(port, timeoutMs) {
16811
+ try {
16812
+ const inspected = await inspectCdpEndpoint({
16813
+ endpoint: `http://127.0.0.1:${String(port)}`,
16814
+ timeoutMs: Math.min(2e3, timeoutMs)
16815
+ });
16816
+ return inspected.endpoint;
16817
+ } catch {
16818
+ return void 0;
16819
+ }
16820
+ }
16070
16821
  function formatChromeLaunchError(stderrLines) {
16071
16822
  const collapsed = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
16072
16823
  if (collapsed.length === 0) {
@@ -16165,12 +16916,12 @@ async function waitForProcessExit(pid, timeoutMs) {
16165
16916
  if (!isProcessRunning(pid)) {
16166
16917
  return true;
16167
16918
  }
16168
- await sleep(50);
16919
+ await sleep2(50);
16169
16920
  }
16170
16921
  return !isProcessRunning(pid);
16171
16922
  }
16172
16923
  function resolveAbpSessionDir(workspace) {
16173
- return path7__default.default.join(workspace.livePath, "abp-session");
16924
+ return path10__default.default.join(workspace.livePath, "abp-session");
16174
16925
  }
16175
16926
  async function allocateEphemeralPort() {
16176
16927
  const { allocatePort } = await loadAbpModule();
@@ -16227,14 +16978,14 @@ function resolveStealthProfile(input) {
16227
16978
  function isStealthProfile(input) {
16228
16979
  return input.id !== void 0 && input.platform !== void 0 && input.browserBrand !== void 0 && input.browserVersion !== void 0 && input.userAgent !== void 0 && input.viewport !== void 0 && input.screenResolution !== void 0 && input.devicePixelRatio !== void 0 && input.maxTouchPoints !== void 0 && input.webglVendor !== void 0 && input.webglRenderer !== void 0 && input.fonts !== void 0 && input.canvasNoiseSeed !== void 0 && input.audioNoiseSeed !== void 0 && input.locale !== void 0 && input.timezoneId !== void 0;
16229
16980
  }
16230
- async function sleep(ms) {
16981
+ async function sleep2(ms) {
16231
16982
  await new Promise((resolve4) => setTimeout(resolve4, ms));
16232
16983
  }
16233
16984
  var ENV_FILENAMES = [".env", ".env.local"];
16234
16985
  var OPENSTEER_ENV_PREFIX = "OPENSTEER_";
16235
16986
  var opensteerEnvironmentCache = /* @__PURE__ */ new Map();
16236
16987
  function resolveOpensteerEnvironment(cwd = process.cwd(), baseEnv = process.env) {
16237
- const resolvedCwd = path7__default.default.resolve(cwd);
16988
+ const resolvedCwd = path10__default.default.resolve(cwd);
16238
16989
  const signature = buildEnvironmentSignature(baseEnv, isOpensteerEnvironmentKey);
16239
16990
  const cached = opensteerEnvironmentCache.get(resolvedCwd);
16240
16991
  if (cached && cached.signature === signature) {
@@ -16249,10 +17000,10 @@ function resolveOpensteerEnvironment(cwd = process.cwd(), baseEnv = process.env)
16249
17000
  }
16250
17001
  function collectDirectories(cwd) {
16251
17002
  const directories = [];
16252
- let current = path7__default.default.resolve(cwd);
17003
+ let current = path10__default.default.resolve(cwd);
16253
17004
  for (; ; ) {
16254
17005
  directories.unshift(current);
16255
- const parent = path7__default.default.dirname(current);
17006
+ const parent = path10__default.default.dirname(current);
16256
17007
  if (parent === current) {
16257
17008
  return directories;
16258
17009
  }
@@ -16295,7 +17046,7 @@ function resolveEnvironmentFiles(cwd, baseEnv, predicate) {
16295
17046
  const directories = collectDirectories(cwd);
16296
17047
  for (const directory of directories) {
16297
17048
  for (const filename of ENV_FILENAMES) {
16298
- const filePath = path7__default.default.join(directory, filename);
17049
+ const filePath = path10__default.default.join(directory, filename);
16299
17050
  if (!fs.existsSync(filePath)) {
16300
17051
  continue;
16301
17052
  }
@@ -16374,17 +17125,17 @@ async function readBrowserCookies(input = {}) {
16374
17125
  const brand2 = resolveRequestedBrand(input);
16375
17126
  const userDataDir = resolveBrandUserDataDir(brand2, input.userDataDir);
16376
17127
  const profileDirectory = input.profileDirectory ?? "Default";
16377
- const cookiesPath = path7.join(userDataDir, profileDirectory, "Cookies");
17128
+ const cookiesPath = path10.join(userDataDir, profileDirectory, "Cookies");
16378
17129
  if (!fs.existsSync(cookiesPath)) {
16379
17130
  throw new Error(
16380
17131
  `Cookies database not found at "${cookiesPath}". Verify the browser brand, user-data-dir, and profile-directory are correct.`
16381
17132
  );
16382
17133
  }
16383
- const tempDir = await promises.mkdtemp(path7.join(os.tmpdir(), "opensteer-cookies-"));
17134
+ const tempDir = await promises.mkdtemp(path10.join(os.tmpdir(), "opensteer-cookies-"));
16384
17135
  try {
16385
17136
  await copyCookiesDatabase(cookiesPath, tempDir);
16386
17137
  const decryptionKey = await resolveDecryptionKey(brand2.id, userDataDir);
16387
- const rows = queryAllCookies(path7.join(tempDir, "Cookies"));
17138
+ const rows = queryAllCookies(path10.join(tempDir, "Cookies"));
16388
17139
  const cookies = decryptCookieRows(rows, decryptionKey);
16389
17140
  return {
16390
17141
  cookies,
@@ -16410,11 +17161,11 @@ function resolveRequestedBrand(input) {
16410
17161
  return installed.brand;
16411
17162
  }
16412
17163
  async function copyCookiesDatabase(cookiesPath, destDir) {
16413
- await promises.copyFile(cookiesPath, path7.join(destDir, "Cookies"));
17164
+ await promises.copyFile(cookiesPath, path10.join(destDir, "Cookies"));
16414
17165
  for (const suffix of ["-wal", "-journal", "-shm"]) {
16415
17166
  const src = cookiesPath + suffix;
16416
17167
  if (fs.existsSync(src)) {
16417
- await promises.copyFile(src, path7.join(destDir, "Cookies" + suffix)).catch(() => void 0);
17168
+ await promises.copyFile(src, path10.join(destDir, "Cookies" + suffix)).catch(() => void 0);
16418
17169
  }
16419
17170
  }
16420
17171
  }
@@ -16491,7 +17242,7 @@ async function resolveKeychainPassword(brandId) {
16491
17242
  }
16492
17243
  }
16493
17244
  async function resolveWindowsMasterKey(userDataDir) {
16494
- const localStatePath = path7.join(userDataDir, "Local State");
17245
+ const localStatePath = path10.join(userDataDir, "Local State");
16495
17246
  let localState;
16496
17247
  try {
16497
17248
  localState = JSON.parse(await promises.readFile(localStatePath, "utf8"));
@@ -16692,14 +17443,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
16692
17443
  if (!name || !domain) {
16693
17444
  return null;
16694
17445
  }
16695
- const path15 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
17446
+ const path18 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
16696
17447
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
16697
17448
  const sameSite = normalizeSameSite(cookie.sameSite);
16698
17449
  return {
16699
17450
  name,
16700
17451
  value: cookie.value,
16701
17452
  domain,
16702
- path: path15,
17453
+ path: path18,
16703
17454
  secure: cookie.secure,
16704
17455
  httpOnly: cookie.httpOnly,
16705
17456
  ...sameSite === void 0 ? {} : { sameSite },
@@ -16768,7 +17519,7 @@ async function waitForBrowserProfileImport(client, importId) {
16768
17519
  if (current.status === "failed") {
16769
17520
  throw new Error(current.error ?? "Browser profile sync failed.");
16770
17521
  }
16771
- await sleep2(DEFAULT_POLL_INTERVAL_MS);
17522
+ await sleep3(DEFAULT_POLL_INTERVAL_MS);
16772
17523
  }
16773
17524
  throw new Error(`Timed out waiting for browser profile sync "${importId}" to finish.`);
16774
17525
  }
@@ -16777,7 +17528,7 @@ function normalizePlatform(platform) {
16777
17528
  if (platform === "win32") return "windows";
16778
17529
  return platform;
16779
17530
  }
16780
- async function sleep2(ms) {
17531
+ async function sleep3(ms) {
16781
17532
  await new Promise((resolve4) => setTimeout(resolve4, ms));
16782
17533
  }
16783
17534
 
@@ -16791,21 +17542,24 @@ var OpensteerCloudClient = class {
16791
17542
  getConfig() {
16792
17543
  return this.config;
16793
17544
  }
16794
- async createSession(input = {}) {
16795
- const response = await this.request("/v1/sessions", {
16796
- method: "POST",
16797
- body: {
16798
- ...input.name === void 0 ? {} : { name: input.name },
16799
- ...input.browser === void 0 ? {} : { browser: input.browser },
16800
- ...input.context === void 0 ? {} : { context: input.context },
16801
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
16802
- ...input.observability === void 0 ? {} : { observability: input.observability },
16803
- ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
16804
- ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
16805
- ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
16806
- ...input.locality === void 0 ? {} : { locality: input.locality }
16807
- }
16808
- });
17545
+ async createSession(input = {}, options = {}) {
17546
+ const response = await this.request(
17547
+ "/v1/sessions",
17548
+ {
17549
+ method: "POST",
17550
+ body: {
17551
+ ...input.name === void 0 ? {} : { name: input.name },
17552
+ ...input.browser === void 0 ? {} : { browser: input.browser },
17553
+ ...input.context === void 0 ? {} : { context: input.context },
17554
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
17555
+ ...input.observability === void 0 ? {} : { observability: input.observability },
17556
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
17557
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
17558
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath }
17559
+ }
17560
+ },
17561
+ options
17562
+ );
16809
17563
  return await response.json();
16810
17564
  }
16811
17565
  async listSessions() {
@@ -16814,19 +17568,27 @@ var OpensteerCloudClient = class {
16814
17568
  });
16815
17569
  return response.json();
16816
17570
  }
16817
- async getSession(sessionId) {
16818
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
16819
- method: "GET"
16820
- });
17571
+ async getSession(sessionId, options = {}) {
17572
+ const response = await this.request(
17573
+ `/v1/sessions/${encodeURIComponent(sessionId)}`,
17574
+ {
17575
+ method: "GET"
17576
+ },
17577
+ options
17578
+ );
16821
17579
  return await response.json();
16822
17580
  }
16823
- async issueAccess(sessionId, capabilities) {
16824
- const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/access`, {
16825
- method: "POST",
16826
- body: {
16827
- capabilities
16828
- }
16829
- });
17581
+ async issueAccess(sessionId, capabilities, options = {}) {
17582
+ const response = await this.request(
17583
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
17584
+ {
17585
+ method: "POST",
17586
+ body: {
17587
+ capabilities
17588
+ }
17589
+ },
17590
+ options
17591
+ );
16830
17592
  return await response.json();
16831
17593
  }
16832
17594
  async getSessionRecording(sessionId) {
@@ -16908,28 +17670,17 @@ var OpensteerCloudClient = class {
16908
17670
  async syncBrowserProfileCookies(input) {
16909
17671
  return syncBrowserProfileCookies(this, input);
16910
17672
  }
16911
- async importSelectorCache(entries) {
16912
- const response = await this.request("/selector-cache/import", {
17673
+ async importDescriptors(entries) {
17674
+ const response = await this.request("/registry/descriptors/import", {
16913
17675
  method: "POST",
16914
- body: {
16915
- entries: entries.map((entry) => ({
16916
- workspace: entry.workspace,
16917
- method: entry.method,
16918
- persistHash: entry.persistHash,
16919
- ...entry.persist === void 0 ? {} : { persist: entry.persist },
16920
- path: entry.path,
16921
- ...entry.schemaHash === void 0 ? {} : { schemaHash: entry.schemaHash },
16922
- createdAt: entry.createdAt,
16923
- updatedAt: entry.updatedAt
16924
- }))
16925
- }
17676
+ body: { entries }
16926
17677
  });
16927
17678
  return await response.json();
16928
17679
  }
16929
- async importDescriptors(entries) {
16930
- const response = await this.request("/registry/descriptors/import", {
17680
+ async importRequestPlans(input) {
17681
+ const response = await this.request("/registry/request-plans/import", {
16931
17682
  method: "POST",
16932
- body: { entries }
17683
+ body: input
16933
17684
  });
16934
17685
  return await response.json();
16935
17686
  }
@@ -16942,7 +17693,7 @@ var OpensteerCloudClient = class {
16942
17693
  "content-type": "application/json; charset=utf-8"
16943
17694
  };
16944
17695
  }
16945
- async request(pathname, init) {
17696
+ async request(pathname, init, options = {}) {
16946
17697
  const url = `${this.config.baseUrl}${pathname}`;
16947
17698
  let response;
16948
17699
  try {
@@ -16950,7 +17701,7 @@ var OpensteerCloudClient = class {
16950
17701
  method: init.method,
16951
17702
  headers: this.buildHeaders(),
16952
17703
  ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) },
16953
- signal: AbortSignal.timeout(3e4)
17704
+ signal: createRequestSignal(options)
16954
17705
  });
16955
17706
  } catch (error) {
16956
17707
  throw wrapCloudFetchError(error, {
@@ -16975,12 +17726,19 @@ var OpensteerCloudClient = class {
16975
17726
  `Unexpected cloud session status "${String(session.status)}" while waiting for close.`
16976
17727
  );
16977
17728
  }
16978
- await delay(CLOUD_CLOSE_POLL_INTERVAL_MS);
17729
+ await delay2(CLOUD_CLOSE_POLL_INTERVAL_MS);
16979
17730
  }
16980
17731
  throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
16981
17732
  }
16982
17733
  };
16983
- function delay(ms) {
17734
+ function createRequestSignal(options) {
17735
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
17736
+ if (options.signal === void 0) {
17737
+ return timeoutSignal;
17738
+ }
17739
+ return AbortSignal.any([options.signal, timeoutSignal]);
17740
+ }
17741
+ function delay2(ms) {
16984
17742
  return new Promise((resolve4) => {
16985
17743
  setTimeout(resolve4, ms);
16986
17744
  });
@@ -17030,7 +17788,7 @@ function resolveCloudConfig(input = {}) {
17030
17788
 
17031
17789
  // ../runtime-core/package.json
17032
17790
  var package_default = {
17033
- version: "0.1.7"};
17791
+ version: "0.2.1"};
17034
17792
 
17035
17793
  // ../runtime-core/src/version.ts
17036
17794
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -17786,11 +18544,11 @@ var NetworkHistory = class {
17786
18544
  var MATCHED_TLS_BINARY_NAMES = ["curl-impersonate-chrome", "curl_chrome"];
17787
18545
  async function executeMatchedTlsTransportRequest(input) {
17788
18546
  const binary = await resolveMatchedTlsBinary();
17789
- const workingDirectory = await promises.mkdtemp(path7__default.default.join(os.tmpdir(), "opensteer-matched-tls-"));
17790
- const headersPath = path7__default.default.join(workingDirectory, "headers.txt");
17791
- const bodyPath = path7__default.default.join(workingDirectory, "body.bin");
17792
- const cookiesPath = path7__default.default.join(workingDirectory, "cookies.txt");
17793
- const requestBodyPath = path7__default.default.join(workingDirectory, "request-body.bin");
18547
+ const workingDirectory = await promises.mkdtemp(path10__default.default.join(os.tmpdir(), "opensteer-matched-tls-"));
18548
+ const headersPath = path10__default.default.join(workingDirectory, "headers.txt");
18549
+ const bodyPath = path10__default.default.join(workingDirectory, "body.bin");
18550
+ const cookiesPath = path10__default.default.join(workingDirectory, "cookies.txt");
18551
+ const requestBodyPath = path10__default.default.join(workingDirectory, "request-body.bin");
17794
18552
  try {
17795
18553
  await promises.writeFile(cookiesPath, toNetscapeCookieJar(input.cookies ?? []), "utf8");
17796
18554
  if (input.request.body !== void 0) {
@@ -17847,10 +18605,10 @@ async function executeMatchedTlsTransportRequest(input) {
17847
18605
  }
17848
18606
  }
17849
18607
  async function resolveMatchedTlsBinary() {
17850
- const pathEntries = (process.env.PATH ?? "").split(path7__default.default.delimiter).filter((entry) => entry.length > 0);
18608
+ const pathEntries = (process.env.PATH ?? "").split(path10__default.default.delimiter).filter((entry) => entry.length > 0);
17851
18609
  for (const directory of pathEntries) {
17852
18610
  for (const name of MATCHED_TLS_BINARY_NAMES) {
17853
- const candidate = path7__default.default.join(directory, name);
18611
+ const candidate = path10__default.default.join(directory, name);
17854
18612
  if (await isExecutable(candidate)) {
17855
18613
  return candidate;
17856
18614
  }
@@ -17858,7 +18616,7 @@ async function resolveMatchedTlsBinary() {
17858
18616
  const files = await readDirSafe(directory);
17859
18617
  const discovered = files.find((file) => file.startsWith("curl_chrome"));
17860
18618
  if (discovered !== void 0) {
17861
- const candidate = path7__default.default.join(directory, discovered);
18619
+ const candidate = path10__default.default.join(directory, discovered);
17862
18620
  if (await isExecutable(candidate)) {
17863
18621
  return candidate;
17864
18622
  }
@@ -20312,11 +21070,11 @@ var SandboxClock = class {
20312
21070
  performanceNow() {
20313
21071
  return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
20314
21072
  }
20315
- setTimeout(callback, delay3 = 0, ...args) {
20316
- return this.registerTimer(false, callback, delay3, args);
21073
+ setTimeout(callback, delay4 = 0, ...args) {
21074
+ return this.registerTimer(false, callback, delay4, args);
20317
21075
  }
20318
- setInterval(callback, delay3 = 0, ...args) {
20319
- return this.registerTimer(true, callback, delay3, args);
21076
+ setInterval(callback, delay4 = 0, ...args) {
21077
+ return this.registerTimer(true, callback, delay4, args);
20320
21078
  }
20321
21079
  clearTimeout(timerId) {
20322
21080
  this.clearTimer(timerId);
@@ -20337,9 +21095,9 @@ var SandboxClock = class {
20337
21095
  this.clearTimer(timerId);
20338
21096
  }
20339
21097
  }
20340
- registerTimer(repeat, callback, delay3, args) {
21098
+ registerTimer(repeat, callback, delay4, args) {
20341
21099
  const timerId = this.nextTimerId++;
20342
- const normalizedDelay = Math.max(0, delay3);
21100
+ const normalizedDelay = Math.max(0, delay4);
20343
21101
  const record = {
20344
21102
  callback,
20345
21103
  args,
@@ -20503,7 +21261,7 @@ async function pollTask(apiKey, taskId, signal) {
20503
21261
  const deadline = Date.now() + 12e4;
20504
21262
  while (Date.now() < deadline) {
20505
21263
  signal?.throwIfAborted?.();
20506
- await sleep3(3e3, signal);
21264
+ await sleep4(3e3, signal);
20507
21265
  const response = await fetch(CAPSOLVER_GET_TASK_RESULT_URL, {
20508
21266
  method: "POST",
20509
21267
  headers: {
@@ -20547,7 +21305,7 @@ function extractCaptchaToken(solution) {
20547
21305
  function readString(value) {
20548
21306
  return typeof value === "string" && value.length > 0 ? value : void 0;
20549
21307
  }
20550
- function sleep3(ms, signal) {
21308
+ function sleep4(ms, signal) {
20551
21309
  return new Promise((resolve4, reject) => {
20552
21310
  const timeout = setTimeout(resolve4, ms);
20553
21311
  const abort = () => {
@@ -20601,7 +21359,7 @@ async function pollTask2(apiKey, taskId, signal) {
20601
21359
  const deadline = Date.now() + 12e4;
20602
21360
  while (Date.now() < deadline) {
20603
21361
  signal?.throwIfAborted?.();
20604
- await sleep4(5e3, signal);
21362
+ await sleep5(5e3, signal);
20605
21363
  const response = await fetch(TWO_CAPTCHA_GET_TASK_RESULT_URL, {
20606
21364
  method: "POST",
20607
21365
  headers: {
@@ -20645,7 +21403,7 @@ function extractCaptchaToken2(solution) {
20645
21403
  function readString2(value) {
20646
21404
  return typeof value === "string" && value.length > 0 ? value : void 0;
20647
21405
  }
20648
- function sleep4(ms, signal) {
21406
+ function sleep5(ms, signal) {
20649
21407
  return new Promise((resolve4, reject) => {
20650
21408
  const timeout = setTimeout(resolve4, ms);
20651
21409
  const abort = () => {
@@ -20848,6 +21606,9 @@ var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
20848
21606
  var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
20849
21607
  var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
20850
21608
  var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
21609
+ var REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
21610
+ var REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS = 15e3;
21611
+ var REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS = 5e3;
20851
21612
  var OpensteerSessionRuntime = class {
20852
21613
  workspace;
20853
21614
  rootPath;
@@ -20879,7 +21640,7 @@ var OpensteerSessionRuntime = class {
20879
21640
  this.workspace = normalizeNamespace2(options.name);
20880
21641
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
20881
21642
  this.root = options.workspace;
20882
- this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path7__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
21643
+ this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path10__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
20883
21644
  this.injectedEngine = options.engine;
20884
21645
  this.engineFactory = options.engineFactory;
20885
21646
  this.policy = options.policy ?? defaultPolicy();
@@ -21709,26 +22470,27 @@ var OpensteerSessionRuntime = class {
21709
22470
  }
21710
22471
  }
21711
22472
  async queryNetwork(input = {}, options = {}) {
21712
- assertValidSemanticOperationInput("network.query", input);
22473
+ const normalizedInput = normalizeNetworkQueryInput(input);
22474
+ assertValidSemanticOperationInput("network.query", normalizedInput);
21713
22475
  const root = await this.ensureRoot();
21714
22476
  const startedAt = Date.now();
21715
22477
  try {
21716
22478
  const output = await this.runWithOperationTimeout(
21717
22479
  "network.query",
21718
22480
  async (timeout) => {
21719
- await this.syncPersistedNetworkSelection(timeout, input, {
22481
+ await this.syncPersistedNetworkSelection(timeout, normalizedInput, {
21720
22482
  includeBodies: false
21721
22483
  });
21722
22484
  const rawRecords = await timeout.runStep(
21723
22485
  () => root.registry.savedNetwork.query({
21724
- ...this.toSavedNetworkQueryInput(input),
21725
- limit: Math.max(input.limit ?? 50, 1e3)
22486
+ ...this.toSavedNetworkQueryInput(normalizedInput),
22487
+ limit: Math.max(normalizedInput.limit ?? 50, 1e3)
21726
22488
  })
21727
22489
  );
21728
- const filtered = filterNetworkSummaryRecords(rawRecords, input);
22490
+ const filtered = filterNetworkSummaryRecords(rawRecords, normalizedInput);
21729
22491
  const sorted = sortPersistedNetworkRecordsChronologically(filtered);
21730
- const sliced = sliceNetworkSummaryWindow(sorted, input);
21731
- const limited = sliced.slice(0, Math.max(1, Math.min(input.limit ?? 50, 200)));
22492
+ const sliced = sliceNetworkSummaryWindow(sorted, normalizedInput);
22493
+ const limited = sliced.slice(0, Math.max(1, Math.min(normalizedInput.limit ?? 50, 200)));
21732
22494
  const summaries = await this.buildNetworkSummaryRecords(limited, timeout);
21733
22495
  return {
21734
22496
  records: summaries
@@ -21742,9 +22504,9 @@ var OpensteerSessionRuntime = class {
21742
22504
  completedAt: Date.now(),
21743
22505
  outcome: "ok",
21744
22506
  data: {
21745
- limit: input.limit ?? 50,
21746
- ...input.capture === void 0 ? {} : { capture: input.capture },
21747
- ...input.json === true ? { json: true } : {},
22507
+ limit: normalizedInput.limit ?? 50,
22508
+ ...normalizedInput.capture === void 0 ? {} : { capture: normalizedInput.capture },
22509
+ ...normalizedInput.json === true ? { json: true } : {},
21748
22510
  count: output.records.length
21749
22511
  },
21750
22512
  context: buildRuntimeTraceContext({
@@ -21769,12 +22531,13 @@ var OpensteerSessionRuntime = class {
21769
22531
  }
21770
22532
  }
21771
22533
  async getNetworkDetail(input, options = {}) {
22534
+ const normalizedRecordId = normalizeNetworkRecordId(input.recordId);
21772
22535
  const startedAt = Date.now();
21773
22536
  try {
21774
22537
  const output = await this.runWithOperationTimeout(
21775
22538
  "network.detail",
21776
22539
  async (timeout) => {
21777
- const record = await this.resolveNetworkRecordByRecordId(input.recordId, timeout, {
22540
+ const record = await this.resolveNetworkRecordByRecordId(normalizedRecordId, timeout, {
21778
22541
  includeBodies: true,
21779
22542
  redactSecretHeaders: false
21780
22543
  });
@@ -21793,8 +22556,8 @@ var OpensteerSessionRuntime = class {
21793
22556
  completedAt: Date.now(),
21794
22557
  outcome: "ok",
21795
22558
  data: {
21796
- recordId: input.recordId,
21797
- status: output.summary.status,
22559
+ recordId: normalizedRecordId,
22560
+ ...output.summary.status === void 0 ? {} : { status: output.summary.status },
21798
22561
  url: output.summary.url
21799
22562
  },
21800
22563
  context: buildRuntimeTraceContext({
@@ -23198,7 +23961,9 @@ var OpensteerSessionRuntime = class {
23198
23961
  ...graphql.persisted === void 0 ? {} : { persisted: graphql.persisted },
23199
23962
  ...graphqlVariables === void 0 ? {} : { variables: graphqlVariables }
23200
23963
  };
23201
- const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders) : void 0;
23964
+ const requestBody = shouldShowRequestBody(record.record.method) && record.record.requestBody !== void 0 ? buildStructuredBodyPreview(record.record.requestBody, record.record.requestHeaders, {
23965
+ truncateData: false
23966
+ }) : void 0;
23202
23967
  const responseBody = record.record.responseBody === void 0 ? void 0 : buildStructuredBodyPreview(record.record.responseBody, record.record.responseHeaders);
23203
23968
  const notes = detectNetworkRecordNotes(record);
23204
23969
  return {
@@ -23228,8 +23993,18 @@ var OpensteerSessionRuntime = class {
23228
23993
  let recommended;
23229
23994
  for (const transport of REPLAY_TRANSPORT_LADDER) {
23230
23995
  const attemptStartedAt = Date.now();
23996
+ const attemptTimeoutMs = resolveReplayProbeAttemptTimeoutMs({
23997
+ remainingMs: timeout.remainingMs(),
23998
+ transportsRemaining: REPLAY_TRANSPORT_LADDER.length - attempts.length,
23999
+ recommendedFound: recommended !== void 0
24000
+ });
23231
24001
  try {
23232
- const output = await this.executeReplayTransportAttempt(transport, request, timeout);
24002
+ const output = await this.executeReplayTransportAttemptWithinBudget(
24003
+ transport,
24004
+ request,
24005
+ timeout,
24006
+ attemptTimeoutMs
24007
+ );
23233
24008
  const ok = matchesSuccessFingerprintFromProtocolResponse(output.response, fingerprint);
23234
24009
  attempts.push({
23235
24010
  transport,
@@ -23245,7 +24020,7 @@ var OpensteerSessionRuntime = class {
23245
24020
  transport,
23246
24021
  ok: false,
23247
24022
  durationMs: Date.now() - attemptStartedAt,
23248
- error: normalizeRuntimeErrorMessage(error)
24023
+ error: normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs)
23249
24024
  });
23250
24025
  }
23251
24026
  }
@@ -23449,6 +24224,23 @@ var OpensteerSessionRuntime = class {
23449
24224
  }
23450
24225
  }
23451
24226
  }
24227
+ async executeReplayTransportAttemptWithinBudget(transport, request, timeout, attemptTimeoutMs) {
24228
+ if (attemptTimeoutMs === void 0) {
24229
+ return this.executeReplayTransportAttempt(transport, request, timeout);
24230
+ }
24231
+ return runWithPolicyTimeout(
24232
+ {
24233
+ resolveTimeoutMs() {
24234
+ return attemptTimeoutMs;
24235
+ }
24236
+ },
24237
+ {
24238
+ operation: timeout.operation,
24239
+ signal: timeout.signal
24240
+ },
24241
+ (attemptTimeout) => this.executeReplayTransportAttempt(transport, request, attemptTimeout)
24242
+ );
24243
+ }
23452
24244
  async executeFetchTransportAttempt(transport, request, timeout, input) {
23453
24245
  let prepared = finalizeMaterializedTransportRequest(request, transport);
23454
24246
  if (input.cookies !== false && transport === "direct-http" && this.currentBinding() !== void 0) {
@@ -24363,10 +25155,15 @@ var OpensteerSessionRuntime = class {
24363
25155
  return this.observationSessionId ?? this.sessionRef;
24364
25156
  }
24365
25157
  runWithOperationTimeout(operation, callback, options = {}) {
25158
+ const timeoutPolicy = options.timeoutMs === void 0 ? this.policy.timeout : {
25159
+ resolveTimeoutMs() {
25160
+ return options.timeoutMs;
25161
+ }
25162
+ };
24366
25163
  const existingCollector = this.operationEventStorage.getStore();
24367
25164
  if (existingCollector !== void 0) {
24368
25165
  return runWithPolicyTimeout(
24369
- this.policy.timeout,
25166
+ timeoutPolicy,
24370
25167
  {
24371
25168
  operation,
24372
25169
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24379,7 +25176,7 @@ var OpensteerSessionRuntime = class {
24379
25176
  return this.operationEventStorage.run(collector, async () => {
24380
25177
  try {
24381
25178
  return await runWithPolicyTimeout(
24382
- this.policy.timeout,
25179
+ timeoutPolicy,
24383
25180
  {
24384
25181
  operation,
24385
25182
  ...options.signal === void 0 ? {} : { signal: options.signal }
@@ -24549,6 +25346,21 @@ function buildEngineNetworkRecordFilters(input) {
24549
25346
  function normalizeNetworkStatusFilter(status) {
24550
25347
  return String(status);
24551
25348
  }
25349
+ function normalizeNetworkQueryInput(input) {
25350
+ return {
25351
+ ...input,
25352
+ ...input.recordId === void 0 ? {} : { recordId: normalizeNetworkRecordId(input.recordId) },
25353
+ ...input.before === void 0 ? {} : { before: normalizeNetworkRecordId(input.before) },
25354
+ ...input.after === void 0 ? {} : { after: normalizeNetworkRecordId(input.after) }
25355
+ };
25356
+ }
25357
+ function normalizeNetworkRecordId(recordId) {
25358
+ const trimmed = recordId.trim();
25359
+ if (trimmed.length === 0 || trimmed.startsWith("record:")) {
25360
+ return trimmed;
25361
+ }
25362
+ return `record:${trimmed}`;
25363
+ }
24552
25364
  function resolveLiveQueryRequestIds(input, history) {
24553
25365
  const requestIdCandidates = [];
24554
25366
  if (input.recordId !== void 0) {
@@ -24761,6 +25573,20 @@ var REPLAY_TRANSPORT_LADDER = [
24761
25573
  "context-http",
24762
25574
  "page-http"
24763
25575
  ];
25576
+ function resolveReplayProbeAttemptTimeoutMs(input) {
25577
+ const attemptCapMs = input.recommendedFound ? REPLAY_PROBE_POST_SUCCESS_ATTEMPT_TIMEOUT_MS : REPLAY_PROBE_MAX_ATTEMPT_TIMEOUT_MS;
25578
+ const clampedRemaining = input.remainingMs === void 0 ? void 0 : Math.max(0, input.remainingMs);
25579
+ if (clampedRemaining === 0) {
25580
+ return 0;
25581
+ }
25582
+ if (clampedRemaining === void 0) {
25583
+ return attemptCapMs;
25584
+ }
25585
+ const sliceMs = Math.floor(clampedRemaining / Math.max(1, input.transportsRemaining));
25586
+ const minimumBudgetAffordable = clampedRemaining >= REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS * input.transportsRemaining;
25587
+ const attemptBudgetMs = minimumBudgetAffordable ? Math.max(REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS, sliceMs) : sliceMs;
25588
+ return Math.min(clampedRemaining, attemptCapMs, Math.max(1, attemptBudgetMs));
25589
+ }
24764
25590
  function filterNetworkSummaryRecords(records, input) {
24765
25591
  return records.filter((record) => {
24766
25592
  if (record.record.resourceType === "preflight" || record.record.method === "OPTIONS") {
@@ -24933,10 +25759,10 @@ function extractGraphqlOperationName(queryText) {
24933
25759
  function shouldShowRequestBody(method) {
24934
25760
  return !["GET", "HEAD", "DELETE", "OPTIONS"].includes(method.trim().toUpperCase());
24935
25761
  }
24936
- function buildStructuredBodyPreview(body, headers) {
25762
+ function buildStructuredBodyPreview(body, headers, options = {}) {
24937
25763
  const contentType = headerValue(headers, "content-type") ?? body?.mimeType;
24938
25764
  const parsed = parseStructuredPayload(body, contentType);
24939
- const data = parsed === void 0 ? void 0 : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
25765
+ const data = parsed === void 0 ? void 0 : options.truncateData === false ? parsed : typeof parsed === "string" ? truncateInlineText(parsed) : truncateStructuredValue(parsed);
24940
25766
  return {
24941
25767
  bytes: body?.originalByteLength ?? body?.capturedByteLength ?? 0,
24942
25768
  ...contentType === void 0 ? {} : { contentType },
@@ -25149,10 +25975,12 @@ function resolveSessionFetchTransportLadder(transport) {
25149
25975
  return ["direct-http"];
25150
25976
  case "matched-tls":
25151
25977
  return ["matched-tls"];
25978
+ case "context":
25979
+ return ["context-http"];
25152
25980
  case "page":
25153
25981
  return ["page-http"];
25154
25982
  case "auto":
25155
- return ["direct-http", "matched-tls", "page-http"];
25983
+ return ["direct-http", "matched-tls", "context-http", "page-http"];
25156
25984
  }
25157
25985
  }
25158
25986
  function detectChallengeNoteFromRecord(record) {
@@ -25297,6 +26125,12 @@ function diffStorageSnapshot(left, right) {
25297
26125
  function normalizeRuntimeErrorMessage(error) {
25298
26126
  return error instanceof Error ? error.message : String(error);
25299
26127
  }
26128
+ function normalizeProbeTransportAttemptError(transport, error, attemptTimeoutMs) {
26129
+ if (attemptTimeoutMs !== void 0 && error instanceof OpensteerProtocolError && error.code === "timeout") {
26130
+ return `${transport} probe exceeded ${String(attemptTimeoutMs)}ms`;
26131
+ }
26132
+ return normalizeRuntimeErrorMessage(error);
26133
+ }
25300
26134
  function applyBrowserCookiesToTransportRequest(request, cookies) {
25301
26135
  if (cookies.length === 0) {
25302
26136
  return request;
@@ -26064,10 +26898,10 @@ var OpensteerSemanticRestClient = class {
26064
26898
  constructor(connection) {
26065
26899
  this.connection = connection;
26066
26900
  }
26067
- async invoke(operation, input) {
26068
- return this.invokeInternal(operation, input, false);
26901
+ async invoke(operation, input, options = {}) {
26902
+ return this.invokeInternal(operation, input, false, options);
26069
26903
  }
26070
- async invokeInternal(operation, input, hasRetried) {
26904
+ async invokeInternal(operation, input, hasRetried, options) {
26071
26905
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
26072
26906
  if (!endpoint) {
26073
26907
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -26081,10 +26915,11 @@ var OpensteerSemanticRestClient = class {
26081
26915
  method: "POST",
26082
26916
  headers: {
26083
26917
  authorization: await this.connection.getAuthorizationHeader(),
26084
- "content-type": "application/json; charset=utf-8"
26918
+ "content-type": "application/json; charset=utf-8",
26919
+ ...options.timeoutMs === void 0 ? {} : { "x-opensteer-timeout-ms": String(options.timeoutMs) }
26085
26920
  },
26086
26921
  body: JSON.stringify(request),
26087
- signal: AbortSignal.timeout(3e4)
26922
+ signal: createRequestSignal2(options)
26088
26923
  });
26089
26924
  } catch (error) {
26090
26925
  if (operation === "session.close" && isFetchFailure(error)) {
@@ -26100,7 +26935,7 @@ var OpensteerSemanticRestClient = class {
26100
26935
  return envelope.data;
26101
26936
  } catch (error) {
26102
26937
  if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
26103
- return this.invokeInternal(operation, input, true);
26938
+ return this.invokeInternal(operation, input, true, options);
26104
26939
  }
26105
26940
  if (operation === "session.close" && isFetchFailure(error)) {
26106
26941
  return { closed: true };
@@ -26112,6 +26947,13 @@ var OpensteerSemanticRestClient = class {
26112
26947
  return this.invoke("session.close", {});
26113
26948
  }
26114
26949
  };
26950
+ function createRequestSignal2(options) {
26951
+ const timeoutSignal = AbortSignal.timeout(options.timeoutMs ?? 3e4);
26952
+ if (options.signal === void 0) {
26953
+ return timeoutSignal;
26954
+ }
26955
+ return AbortSignal.any([options.signal, timeoutSignal]);
26956
+ }
26115
26957
  function isFetchFailure(error) {
26116
26958
  if (!(error instanceof Error)) {
26117
26959
  return false;
@@ -26478,13 +27320,44 @@ function asRecord(value) {
26478
27320
  return value;
26479
27321
  }
26480
27322
 
26481
- // src/cloud/registry-sync.ts
26482
- var REGISTRY_SYNC_MAX_PAYLOAD_BYTES = 15e5;
26483
- var REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH = 100;
26484
- async function syncLocalRegistryToCloud(client, workspace, store) {
27323
+ // src/cloud/workspace-sync.ts
27324
+ var WORKSPACE_SYNC_MAX_PAYLOAD_BYTES = 15e5;
27325
+ var WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH = 100;
27326
+ async function syncLocalWorkspaceToCloud(client, workspace, store) {
27327
+ await syncDescriptorRegistryToCloud(client, workspace, store);
27328
+ await syncRequestPlansToCloud(client, workspace, store);
27329
+ }
27330
+ async function syncDescriptorRegistryToCloud(client, workspace, store) {
26485
27331
  const descriptors = await store.registry.descriptors.list();
26486
- const descriptorEntries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
26487
- await importInBatches(descriptorEntries, (entries) => client.importDescriptors(entries));
27332
+ const entries = descriptors.map((record) => toDescriptorImportEntry(workspace, record));
27333
+ await importInBatches(entries, {
27334
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
27335
+ importBatch: (batch) => client.importDescriptors(batch)
27336
+ });
27337
+ }
27338
+ async function syncRequestPlansToCloud(client, workspace, store) {
27339
+ const requestPlans = await store.registry.requestPlans.list();
27340
+ const entries = requestPlans.map((record) => toRequestPlanImportEntry(workspace, record)).filter((entry) => entry !== void 0);
27341
+ await importInBatches(entries, {
27342
+ getPayloadByteLength: (batch) => payloadByteLength({ entries: batch }),
27343
+ importBatch: (batch) => client.importRequestPlans({ entries: batch })
27344
+ });
27345
+ }
27346
+ function toRequestPlanImportEntry(workspace, record) {
27347
+ const entry = {
27348
+ workspace,
27349
+ recordId: record.id,
27350
+ key: record.key,
27351
+ version: record.version,
27352
+ contentHash: record.contentHash,
27353
+ tags: [...record.tags],
27354
+ ...record.provenance === void 0 ? {} : { provenance: record.provenance },
27355
+ ...record.freshness === void 0 ? {} : { freshness: record.freshness },
27356
+ payload: record.payload,
27357
+ createdAt: record.createdAt,
27358
+ updatedAt: record.updatedAt
27359
+ };
27360
+ return payloadByteLength({ entries: [entry] }) <= WORKSPACE_SYNC_MAX_PAYLOAD_BYTES ? entry : void 0;
26488
27361
  }
26489
27362
  function toDescriptorImportEntry(workspace, record) {
26490
27363
  return {
@@ -26500,19 +27373,19 @@ function toDescriptorImportEntry(workspace, record) {
26500
27373
  updatedAt: record.updatedAt
26501
27374
  };
26502
27375
  }
26503
- async function importInBatches(entries, importBatch) {
27376
+ async function importInBatches(entries, options) {
26504
27377
  if (entries.length === 0) {
26505
27378
  return;
26506
27379
  }
26507
- for (const batch of chunkEntries(entries)) {
26508
- await importBatch(batch);
27380
+ for (const batch of chunkEntries(entries, options.getPayloadByteLength)) {
27381
+ await options.importBatch(batch);
26509
27382
  }
26510
27383
  }
26511
- function chunkEntries(entries) {
27384
+ function chunkEntries(entries, getPayloadByteLength) {
26512
27385
  const batches = [];
26513
27386
  let currentBatch = [];
26514
27387
  for (const entry of entries) {
26515
- if (payloadByteLength([entry]) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
27388
+ if (getPayloadByteLength([entry]) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
26516
27389
  continue;
26517
27390
  }
26518
27391
  if (currentBatch.length === 0) {
@@ -26520,7 +27393,7 @@ function chunkEntries(entries) {
26520
27393
  continue;
26521
27394
  }
26522
27395
  const nextBatch = [...currentBatch, entry];
26523
- if (nextBatch.length > REGISTRY_SYNC_MAX_ENTRIES_PER_BATCH || payloadByteLength(nextBatch) > REGISTRY_SYNC_MAX_PAYLOAD_BYTES) {
27396
+ if (nextBatch.length > WORKSPACE_SYNC_MAX_ENTRIES_PER_BATCH || getPayloadByteLength(nextBatch) > WORKSPACE_SYNC_MAX_PAYLOAD_BYTES) {
26524
27397
  batches.push(currentBatch);
26525
27398
  currentBatch = [entry];
26526
27399
  continue;
@@ -26532,8 +27405,8 @@ function chunkEntries(entries) {
26532
27405
  }
26533
27406
  return batches;
26534
27407
  }
26535
- function payloadByteLength(entries) {
26536
- return Buffer.byteLength(JSON.stringify({ entries }), "utf8");
27408
+ function payloadByteLength(value) {
27409
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
26537
27410
  }
26538
27411
 
26539
27412
  // src/cloud/session-proxy.ts
@@ -26544,30 +27417,36 @@ var CloudSessionProxy = class {
26544
27417
  cleanupRootOnClose;
26545
27418
  cloud;
26546
27419
  observability;
27420
+ policy;
26547
27421
  sessionId;
26548
27422
  semanticGrant;
26549
27423
  client;
26550
27424
  automation;
26551
27425
  workspaceStore;
27426
+ syncWorkspaceOnClose = false;
26552
27427
  constructor(cloud, options = {}) {
26553
27428
  this.cloud = cloud;
26554
27429
  this.workspace = options.workspace;
27430
+ this.policy = options.policy ?? defaultPolicy();
26555
27431
  this.observability = options.observability;
26556
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
26557
- rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
27432
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path10__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
27433
+ rootDir: path10__default.default.resolve(options.rootDir ?? process.cwd()),
26558
27434
  workspace: this.workspace
26559
27435
  }));
26560
27436
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
26561
27437
  }
26562
27438
  async open(input = {}) {
26563
- await this.ensureSession({
26564
- ...input.browser === void 0 ? {} : { browser: input.browser },
26565
- ...input.launch === void 0 ? {} : { launch: input.launch },
26566
- ...input.context === void 0 ? {} : { context: input.context }
26567
- });
26568
- return this.requireClient().invoke("session.open", {
26569
- ...input.url === void 0 ? {} : { url: input.url }
26570
- });
27439
+ return this.invokeSemanticOperation(
27440
+ "session.open",
27441
+ {
27442
+ ...input.url === void 0 ? {} : { url: input.url }
27443
+ },
27444
+ {
27445
+ ...input.browser === void 0 ? {} : { browser: input.browser },
27446
+ ...input.launch === void 0 ? {} : { launch: input.launch },
27447
+ ...input.context === void 0 ? {} : { context: input.context }
27448
+ }
27449
+ );
26571
27450
  }
26572
27451
  async info() {
26573
27452
  const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
@@ -26610,108 +27489,88 @@ var CloudSessionProxy = class {
26610
27489
  };
26611
27490
  }
26612
27491
  async listPages(input = {}) {
26613
- await this.ensureSession();
26614
- return this.requireClient().invoke("page.list", input);
27492
+ return this.invokeSemanticOperation("page.list", input);
26615
27493
  }
26616
27494
  async newPage(input = {}) {
26617
- await this.ensureSession();
26618
- return this.requireAutomation().invoke("page.new", input);
27495
+ return this.invokeAutomationOperation(
27496
+ "page.new",
27497
+ (automation) => automation.invoke("page.new", input)
27498
+ );
26619
27499
  }
26620
27500
  async activatePage(input) {
26621
- await this.ensureSession();
26622
- return this.requireClient().invoke("page.activate", input);
27501
+ return this.invokeSemanticOperation("page.activate", input);
26623
27502
  }
26624
27503
  async closePage(input = {}) {
26625
- await this.ensureSession();
26626
- return this.requireClient().invoke("page.close", input);
27504
+ return this.invokeSemanticOperation("page.close", input);
26627
27505
  }
26628
27506
  async goto(input) {
26629
- await this.ensureSession();
26630
- return this.requireClient().invoke("page.goto", input);
27507
+ return this.invokeSemanticOperation("page.goto", input);
26631
27508
  }
26632
27509
  async evaluate(input) {
26633
- await this.ensureSession();
26634
- return this.requireAutomation().invoke("page.evaluate", input);
27510
+ return this.invokeAutomationOperation(
27511
+ "page.evaluate",
27512
+ (automation) => automation.invoke("page.evaluate", input)
27513
+ );
26635
27514
  }
26636
27515
  async addInitScript(input) {
26637
- await this.ensureSession();
26638
- return this.requireClient().invoke("page.add-init-script", input);
27516
+ return this.invokeSemanticOperation("page.add-init-script", input);
26639
27517
  }
26640
27518
  async snapshot(input = {}) {
26641
- await this.ensureSession();
26642
- return this.requireClient().invoke("page.snapshot", input);
27519
+ return this.invokeSemanticOperation("page.snapshot", input);
26643
27520
  }
26644
27521
  async click(input) {
26645
- await this.ensureSession();
26646
- return this.requireClient().invoke("dom.click", input);
27522
+ return this.invokeSemanticOperation("dom.click", input);
26647
27523
  }
26648
27524
  async hover(input) {
26649
- await this.ensureSession();
26650
- return this.requireClient().invoke("dom.hover", input);
27525
+ return this.invokeSemanticOperation("dom.hover", input);
26651
27526
  }
26652
27527
  async input(input) {
26653
- await this.ensureSession();
26654
- return this.requireClient().invoke("dom.input", input);
27528
+ return this.invokeSemanticOperation("dom.input", input);
26655
27529
  }
26656
27530
  async scroll(input) {
26657
- await this.ensureSession();
26658
- return this.requireClient().invoke("dom.scroll", input);
27531
+ return this.invokeSemanticOperation("dom.scroll", input);
26659
27532
  }
26660
27533
  async extract(input) {
26661
- await this.ensureSession();
26662
- return this.requireClient().invoke("dom.extract", input);
27534
+ return this.invokeSemanticOperation("dom.extract", input);
26663
27535
  }
26664
27536
  async queryNetwork(input = {}) {
26665
- await this.ensureSession();
26666
- return this.requireClient().invoke("network.query", input);
27537
+ return this.invokeSemanticOperation("network.query", input);
26667
27538
  }
26668
27539
  async getNetworkDetail(input) {
26669
- await this.ensureSession();
26670
- return this.requireClient().invoke("network.detail", input);
27540
+ return this.invokeSemanticOperation("network.detail", input);
26671
27541
  }
26672
27542
  async captureInteraction(input) {
26673
- await this.ensureSession();
26674
- return this.requireClient().invoke("interaction.capture", input);
27543
+ return this.invokeSemanticOperation("interaction.capture", input);
26675
27544
  }
26676
27545
  async getInteraction(input) {
26677
- await this.ensureSession();
26678
- return this.requireClient().invoke("interaction.get", input);
27546
+ return this.invokeSemanticOperation("interaction.get", input);
26679
27547
  }
26680
27548
  async diffInteraction(input) {
26681
- await this.ensureSession();
26682
- return this.requireClient().invoke("interaction.diff", input);
27549
+ return this.invokeSemanticOperation("interaction.diff", input);
26683
27550
  }
26684
27551
  async replayInteraction(input) {
26685
- await this.ensureSession();
26686
- return this.requireClient().invoke("interaction.replay", input);
27552
+ return this.invokeSemanticOperation("interaction.replay", input);
26687
27553
  }
26688
27554
  async captureScripts(input = {}) {
26689
- await this.ensureSession();
26690
- return this.requireClient().invoke("scripts.capture", input);
27555
+ return this.invokeSemanticOperation("scripts.capture", input);
26691
27556
  }
26692
27557
  async readArtifact(input) {
26693
- await this.ensureSession();
26694
- return this.requireClient().invoke("artifact.read", input);
27558
+ return this.invokeSemanticOperation("artifact.read", input);
26695
27559
  }
26696
27560
  async beautifyScript(input) {
26697
- await this.ensureSession();
26698
- return this.requireClient().invoke("scripts.beautify", input);
27561
+ return this.invokeSemanticOperation("scripts.beautify", input);
26699
27562
  }
26700
27563
  async deobfuscateScript(input) {
26701
- await this.ensureSession();
26702
- return this.requireClient().invoke("scripts.deobfuscate", input);
27564
+ return this.invokeSemanticOperation("scripts.deobfuscate", input);
26703
27565
  }
26704
27566
  async sandboxScript(input) {
26705
- await this.ensureSession();
26706
- return this.requireClient().invoke("scripts.sandbox", input);
27567
+ return this.invokeSemanticOperation("scripts.sandbox", input);
26707
27568
  }
26708
27569
  async solveCaptcha(input) {
26709
- await this.ensureSession();
26710
- return this.requireClient().invoke("captcha.solve", input);
27570
+ return this.invokeSemanticOperation("captcha.solve", input);
26711
27571
  }
26712
27572
  async getCookies(input = {}) {
26713
- await this.ensureSession();
26714
- return this.requireClient().invoke("session.cookies", input);
27573
+ return this.invokeSemanticOperation("session.cookies", input);
26715
27574
  }
26716
27575
  async route(input) {
26717
27576
  await this.ensureSession();
@@ -26722,20 +27581,16 @@ var CloudSessionProxy = class {
26722
27581
  return this.requireAutomation().interceptScript(input);
26723
27582
  }
26724
27583
  async getStorageSnapshot(input = {}) {
26725
- await this.ensureSession();
26726
- return this.requireClient().invoke("session.storage", input);
27584
+ return this.invokeSemanticOperation("session.storage", input);
26727
27585
  }
26728
27586
  async getBrowserState(input = {}) {
26729
- await this.ensureSession();
26730
- return this.requireClient().invoke("session.state", input);
27587
+ return this.invokeSemanticOperation("session.state", input);
26731
27588
  }
26732
27589
  async fetch(input) {
26733
- await this.ensureSession();
26734
- return this.requireClient().invoke("session.fetch", input);
27590
+ return this.invokeSemanticOperation("session.fetch", input);
26735
27591
  }
26736
27592
  async computerExecute(input) {
26737
- await this.ensureSession();
26738
- return this.requireClient().invoke("computer.execute", input);
27593
+ return this.invokeSemanticOperation("computer.execute", input);
26739
27594
  }
26740
27595
  async close() {
26741
27596
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
@@ -26747,6 +27602,14 @@ var CloudSessionProxy = class {
26747
27602
  startedAt: Date.now(),
26748
27603
  updatedAt: Date.now()
26749
27604
  });
27605
+ let syncError;
27606
+ if (this.syncWorkspaceOnClose) {
27607
+ try {
27608
+ await this.syncWorkspaceToCloud();
27609
+ } catch (error) {
27610
+ syncError = error;
27611
+ }
27612
+ }
26750
27613
  try {
26751
27614
  if (session !== void 0) {
26752
27615
  await this.cloud.closeSession(session.sessionId).catch((error) => {
@@ -26767,6 +27630,9 @@ var CloudSessionProxy = class {
26767
27630
  await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
26768
27631
  }
26769
27632
  }
27633
+ if (syncError !== void 0) {
27634
+ throw syncError;
27635
+ }
26770
27636
  return { closed: true };
26771
27637
  }
26772
27638
  async disconnect() {
@@ -26774,34 +27640,38 @@ var CloudSessionProxy = class {
26774
27640
  await this.close();
26775
27641
  return;
26776
27642
  }
27643
+ let syncError;
27644
+ if (this.syncWorkspaceOnClose) {
27645
+ try {
27646
+ await this.syncWorkspaceToCloud();
27647
+ } catch (error) {
27648
+ syncError = error;
27649
+ }
27650
+ }
26777
27651
  this.client = void 0;
26778
27652
  await this.automation?.close().catch(() => void 0);
26779
27653
  this.automation = void 0;
26780
27654
  this.sessionId = void 0;
26781
27655
  this.semanticGrant = void 0;
27656
+ if (syncError !== void 0) {
27657
+ throw syncError;
27658
+ }
26782
27659
  }
26783
- async ensureSession(input = {}) {
27660
+ async ensureSession(input = {}, timeout) {
26784
27661
  if (this.client) {
26785
27662
  return;
26786
27663
  }
26787
27664
  assertSupportedCloudBrowserMode(input.browser);
26788
27665
  const localCloud = this.shouldUseLocalCloudTransport();
27666
+ this.syncWorkspaceOnClose = localCloud && this.workspace !== void 0;
26789
27667
  const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
26790
27668
  const persisted = await this.loadPersistedSession();
26791
- if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
26792
- if (localCloud) {
26793
- void this.syncRegistryToCloud();
26794
- } else {
26795
- await this.syncRegistryToCloud();
26796
- }
27669
+ if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId, timeout)) {
27670
+ await this.syncWorkspaceToCloud();
26797
27671
  this.bindClient(persisted);
26798
27672
  return;
26799
27673
  }
26800
- if (localCloud) {
26801
- void this.syncRegistryToCloud();
26802
- } else {
26803
- await this.syncRegistryToCloud();
26804
- }
27674
+ await this.syncWorkspaceToCloud();
26805
27675
  const baseCreateInput = {
26806
27676
  ...this.workspace === void 0 ? {} : { name: this.workspace },
26807
27677
  ...input.launch === void 0 ? {} : { browser: input.launch },
@@ -26813,10 +27683,12 @@ var CloudSessionProxy = class {
26813
27683
  ...baseCreateInput,
26814
27684
  sourceType: "local-cloud",
26815
27685
  sourceRef: this.workspace,
26816
- localWorkspaceRootPath: this.rootPath,
26817
- locality: "auto"
27686
+ localWorkspaceRootPath: this.rootPath
26818
27687
  } : baseCreateInput;
26819
- const session = await this.cloud.createSession(createInput);
27688
+ const session = await this.cloud.createSession(createInput, {
27689
+ signal: timeout?.signal,
27690
+ timeoutMs: timeout?.remainingMs()
27691
+ });
26820
27692
  const record = {
26821
27693
  layout: "opensteer-session",
26822
27694
  version: 1,
@@ -26829,15 +27701,12 @@ var CloudSessionProxy = class {
26829
27701
  await this.writePersistedSession(record);
26830
27702
  this.bindClient(record, session.initialGrants?.semantic);
26831
27703
  }
26832
- async syncRegistryToCloud() {
27704
+ async syncWorkspaceToCloud() {
26833
27705
  if (this.workspace === void 0) {
26834
27706
  return;
26835
27707
  }
26836
- try {
26837
- const workspaceStore = await this.ensureWorkspaceStore();
26838
- await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
26839
- } catch {
26840
- }
27708
+ const workspaceStore = await this.ensureWorkspaceStore();
27709
+ await syncLocalWorkspaceToCloud(this.cloud, this.workspace, workspaceStore);
26841
27710
  }
26842
27711
  bindClient(record, initialSemanticGrant) {
26843
27712
  this.sessionId = record.sessionId;
@@ -26871,9 +27740,12 @@ var CloudSessionProxy = class {
26871
27740
  async clearPersistedSession() {
26872
27741
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
26873
27742
  }
26874
- async isReusableCloudSession(sessionId) {
27743
+ async isReusableCloudSession(sessionId, timeout) {
26875
27744
  try {
26876
- const session = await this.cloud.getSession(sessionId);
27745
+ const session = await this.cloud.getSession(sessionId, {
27746
+ signal: timeout?.signal,
27747
+ timeoutMs: timeout?.remainingMs()
27748
+ });
26877
27749
  return session.status !== "closed" && session.status !== "failed";
26878
27750
  } catch (error) {
26879
27751
  if (isMissingCloudSessionError(error)) {
@@ -26894,14 +27766,17 @@ var CloudSessionProxy = class {
26894
27766
  }
26895
27767
  return this.automation;
26896
27768
  }
26897
- async ensureSemanticGrant(forceRefresh = false) {
27769
+ async ensureSemanticGrant(forceRefresh = false, timeout) {
26898
27770
  if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
26899
27771
  return this.semanticGrant;
26900
27772
  }
26901
27773
  if (!this.sessionId) {
26902
27774
  throw new Error("Cloud session has not been initialized.");
26903
27775
  }
26904
- const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
27776
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"], {
27777
+ signal: timeout?.signal,
27778
+ timeoutMs: timeout?.remainingMs()
27779
+ });
26905
27780
  const grant = issued.grants.semantic;
26906
27781
  if (!grant || grant.transport !== "http") {
26907
27782
  throw new Error("cloud did not issue a valid semantic grant");
@@ -26924,6 +27799,25 @@ var CloudSessionProxy = class {
26924
27799
  return false;
26925
27800
  }
26926
27801
  }
27802
+ async invokeSemanticOperation(operation, input, sessionInit = {}) {
27803
+ return this.runOperationWithPolicy(operation, async (timeout) => {
27804
+ await this.ensureSession(sessionInit, timeout);
27805
+ await this.ensureSemanticGrant(false, timeout);
27806
+ return this.requireClient().invoke(operation, input, {
27807
+ signal: timeout.signal,
27808
+ timeoutMs: timeout.remainingMs()
27809
+ });
27810
+ });
27811
+ }
27812
+ async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
27813
+ return this.runOperationWithPolicy(operation, async (timeout) => {
27814
+ await this.ensureSession(sessionInit, timeout);
27815
+ return invoke(this.requireAutomation());
27816
+ });
27817
+ }
27818
+ async runOperationWithPolicy(operation, invoke) {
27819
+ return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
27820
+ }
26927
27821
  shouldUseLocalCloudTransport() {
26928
27822
  if (this.workspace === void 0) {
26929
27823
  return false;
@@ -26958,8 +27852,8 @@ function isLoopbackBaseUrl(baseUrl) {
26958
27852
  var OpensteerRuntime = class extends OpensteerSessionRuntime {
26959
27853
  constructor(options = {}) {
26960
27854
  const publicWorkspace = normalizeWorkspace2(options.workspace);
26961
- const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path7__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
26962
- rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
27855
+ const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path10__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
27856
+ rootDir: path10__default.default.resolve(options.rootDir ?? process.cwd()),
26963
27857
  workspace: publicWorkspace
26964
27858
  }));
26965
27859
  const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
@@ -26993,7 +27887,7 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
26993
27887
  };
26994
27888
  var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
26995
27889
  constructor(options) {
26996
- const rootPath = options.rootPath ?? path7__default.default.resolve(options.rootDir ?? process.cwd());
27890
+ const rootPath = options.rootPath ?? path10__default.default.resolve(options.rootDir ?? process.cwd());
26997
27891
  const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
26998
27892
  const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
26999
27893
  assertSupportedEngineOptions({
@@ -27098,6 +27992,7 @@ function createOpensteerSemanticRuntime(input = {}) {
27098
27992
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
27099
27993
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
27100
27994
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
27995
+ ...runtimeOptions.policy === void 0 ? {} : { policy: runtimeOptions.policy },
27101
27996
  ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
27102
27997
  ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
27103
27998
  });
@@ -27282,7 +28177,7 @@ var Opensteer = class {
27282
28177
  if (Date.now() >= timeoutAt) {
27283
28178
  throw new Error("waitForNetwork timed out");
27284
28179
  }
27285
- await delay2(pollInterval);
28180
+ await delay3(pollInterval);
27286
28181
  }
27287
28182
  }
27288
28183
  async waitForResponse(input) {
@@ -27311,7 +28206,7 @@ var Opensteer = class {
27311
28206
  if (Date.now() >= timeoutAt) {
27312
28207
  throw new Error("waitForPage timed out");
27313
28208
  }
27314
- await delay2(pollIntervalMs);
28209
+ await delay3(pollIntervalMs);
27315
28210
  }
27316
28211
  }
27317
28212
  async snapshot(mode = "action") {
@@ -27463,7 +28358,7 @@ function decodeBody(response) {
27463
28358
  }
27464
28359
  return Uint8Array.from(Buffer.from(response.body.data, "base64"));
27465
28360
  }
27466
- function delay2(ms) {
28361
+ function delay3(ms) {
27467
28362
  return new Promise((resolve4) => setTimeout(resolve4, ms));
27468
28363
  }
27469
28364