opensteer 0.8.8 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var path6 = require('path');
3
+ var path7 = 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 async_hooks = require('async_hooks');
13
14
  var module$1 = require('module');
14
15
  var sharp = require('sharp');
15
16
  var cheerio = require('cheerio');
@@ -39,14 +40,19 @@ function _interopNamespace(e) {
39
40
  return Object.freeze(n);
40
41
  }
41
42
 
42
- var path6__default = /*#__PURE__*/_interopDefault(path6);
43
+ var path7__default = /*#__PURE__*/_interopDefault(path7);
43
44
  var sharp__default = /*#__PURE__*/_interopDefault(sharp);
44
45
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
45
46
  var prettier__namespace = /*#__PURE__*/_interopNamespace(prettier);
46
47
  var vm__default = /*#__PURE__*/_interopDefault(vm);
47
48
  var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
48
49
 
49
- // ../runtime-core/src/root.ts
50
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
51
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
52
+ }) : x)(function(x) {
53
+ if (typeof require !== "undefined") return require.apply(this, arguments);
54
+ throw Error('Dynamic require of "' + x + '" is not supported');
55
+ });
50
56
 
51
57
  // ../runtime-core/src/json.ts
52
58
  function isPlainObject(value) {
@@ -56,30 +62,30 @@ function isPlainObject(value) {
56
62
  const prototype = Object.getPrototypeOf(value);
57
63
  return prototype === Object.prototype || prototype === null;
58
64
  }
59
- function canonicalizeJsonValue(value, path13) {
65
+ function canonicalizeJsonValue(value, path15) {
60
66
  if (value === null || typeof value === "string" || typeof value === "boolean") {
61
67
  return value;
62
68
  }
63
69
  if (typeof value === "number") {
64
70
  if (!Number.isFinite(value)) {
65
- throw new TypeError(`${path13} must be a finite JSON number`);
71
+ throw new TypeError(`${path15} must be a finite JSON number`);
66
72
  }
67
73
  return value;
68
74
  }
69
75
  if (Array.isArray(value)) {
70
- return value.map((entry, index) => canonicalizeJsonValue(entry, `${path13}[${index}]`));
76
+ return value.map((entry, index) => canonicalizeJsonValue(entry, `${path15}[${index}]`));
71
77
  }
72
78
  if (!isPlainObject(value)) {
73
- throw new TypeError(`${path13} must be a plain JSON object`);
79
+ throw new TypeError(`${path15} must be a plain JSON object`);
74
80
  }
75
81
  const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
76
82
  const result = {};
77
83
  for (const key of sorted) {
78
84
  const entry = value[key];
79
85
  if (entry === void 0) {
80
- throw new TypeError(`${path13}.${key} must not be undefined`);
86
+ throw new TypeError(`${path15}.${key} must not be undefined`);
81
87
  }
82
- result[key] = canonicalizeJsonValue(entry, `${path13}.${key}`);
88
+ result[key] = canonicalizeJsonValue(entry, `${path15}.${key}`);
83
89
  }
84
90
  return result;
85
91
  }
@@ -116,7 +122,7 @@ function joinStoragePath(...segments) {
116
122
  return segments.join("/");
117
123
  }
118
124
  function resolveStoragePath(rootPath, relativePath) {
119
- if (path6__default.default.isAbsolute(relativePath)) {
125
+ if (path7__default.default.isAbsolute(relativePath)) {
120
126
  throw new TypeError(`storage path ${relativePath} must be relative`);
121
127
  }
122
128
  const segments = relativePath.split("/");
@@ -128,7 +134,7 @@ function resolveStoragePath(rootPath, relativePath) {
128
134
  throw new TypeError(`storage path ${relativePath} must not contain path traversal`);
129
135
  }
130
136
  }
131
- return path6__default.default.join(rootPath, ...segments);
137
+ return path7__default.default.join(rootPath, ...segments);
132
138
  }
133
139
  async function ensureDirectory(directoryPath) {
134
140
  await promises.mkdir(directoryPath, { recursive: true });
@@ -148,7 +154,7 @@ async function writeJsonFileAtomic(filePath, value) {
148
154
  await writeTextFileAtomic(filePath, stableJsonString(value));
149
155
  }
150
156
  async function writeTextFileAtomic(filePath, value) {
151
- await ensureDirectory(path6__default.default.dirname(filePath));
157
+ await ensureDirectory(path7__default.default.dirname(filePath));
152
158
  const temporaryPath = `${filePath}.${crypto.randomUUID()}.tmp`;
153
159
  await promises.writeFile(temporaryPath, value, "utf8");
154
160
  await promises.rename(temporaryPath, filePath);
@@ -157,7 +163,7 @@ async function writeJsonFileExclusive(filePath, value) {
157
163
  await writeTextFileExclusive(filePath, stableJsonString(value));
158
164
  }
159
165
  async function writeTextFileExclusive(filePath, value) {
160
- await ensureDirectory(path6__default.default.dirname(filePath));
166
+ await ensureDirectory(path7__default.default.dirname(filePath));
161
167
  const handle = await promises.open(filePath, "wx");
162
168
  try {
163
169
  await handle.writeFile(value, "utf8");
@@ -166,7 +172,7 @@ async function writeTextFileExclusive(filePath, value) {
166
172
  }
167
173
  }
168
174
  async function writeBufferIfMissing(filePath, value) {
169
- await ensureDirectory(path6__default.default.dirname(filePath));
175
+ await ensureDirectory(path7__default.default.dirname(filePath));
170
176
  try {
171
177
  const handle = await promises.open(filePath, "wx");
172
178
  try {
@@ -200,7 +206,7 @@ function isAlreadyExistsError(error) {
200
206
  return error?.code === "EEXIST";
201
207
  }
202
208
  async function withFilesystemLock(lockPath, task) {
203
- await ensureDirectory(path6__default.default.dirname(lockPath));
209
+ await ensureDirectory(path7__default.default.dirname(lockPath));
204
210
  let attempt = 0;
205
211
  while (true) {
206
212
  try {
@@ -212,7 +218,7 @@ async function withFilesystemLock(lockPath, task) {
212
218
  }
213
219
  const delayMs = LOCK_RETRY_DELAYS_MS[Math.min(attempt, LOCK_RETRY_DELAYS_MS.length - 1)];
214
220
  attempt += 1;
215
- await new Promise((resolve5) => setTimeout(resolve5, delayMs));
221
+ await new Promise((resolve4) => setTimeout(resolve4, delayMs));
216
222
  }
217
223
  }
218
224
  try {
@@ -255,8 +261,8 @@ async function readStructuredPayload(objectPath) {
255
261
  var FilesystemArtifactStore = class {
256
262
  constructor(rootPath) {
257
263
  this.rootPath = rootPath;
258
- this.manifestsDirectory = path6__default.default.join(this.rootPath, "artifacts", "manifests");
259
- this.objectsDirectory = path6__default.default.join(this.rootPath, "artifacts", "objects", "sha256");
264
+ this.manifestsDirectory = path7__default.default.join(this.rootPath, "artifacts", "manifests");
265
+ this.objectsDirectory = path7__default.default.join(this.rootPath, "artifacts", "objects", "sha256");
260
266
  }
261
267
  manifestsDirectory;
262
268
  objectsDirectory;
@@ -481,7 +487,7 @@ var FilesystemArtifactStore = class {
481
487
  }
482
488
  }
483
489
  manifestPath(artifactId) {
484
- return path6__default.default.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
490
+ return path7__default.default.join(this.manifestsDirectory, `${encodePathSegment(artifactId)}.json`);
485
491
  }
486
492
  };
487
493
  function createArtifactStore(rootPath) {
@@ -576,31 +582,31 @@ function oneOfSchema(members, options = {}) {
576
582
  }
577
583
 
578
584
  // ../protocol/src/validation.ts
579
- function validateJsonSchema(schema, value, path13 = "$") {
580
- return validateSchemaNode(schema, value, path13);
585
+ function validateJsonSchema(schema, value, path15 = "$") {
586
+ return validateSchemaNode(schema, value, path15);
581
587
  }
582
- function validateSchemaNode(schema, value, path13) {
588
+ function validateSchemaNode(schema, value, path15) {
583
589
  const issues = [];
584
590
  if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
585
591
  issues.push({
586
- path: path13,
592
+ path: path15,
587
593
  message: `must equal ${JSON.stringify(schema.const)}`
588
594
  });
589
595
  return issues;
590
596
  }
591
597
  if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
592
598
  issues.push({
593
- path: path13,
599
+ path: path15,
594
600
  message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
595
601
  });
596
602
  return issues;
597
603
  }
598
604
  if (schema.oneOf !== void 0) {
599
- const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path13));
605
+ const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path15));
600
606
  const validBranches = branchIssues.filter((current) => current.length === 0).length;
601
607
  if (validBranches !== 1) {
602
608
  issues.push({
603
- path: path13,
609
+ path: path15,
604
610
  message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
605
611
  });
606
612
  return issues;
@@ -608,11 +614,11 @@ function validateSchemaNode(schema, value, path13) {
608
614
  }
609
615
  if (schema.anyOf !== void 0) {
610
616
  const hasMatch = schema.anyOf.some(
611
- (member) => validateSchemaNode(member, value, path13).length === 0
617
+ (member) => validateSchemaNode(member, value, path15).length === 0
612
618
  );
613
619
  if (!hasMatch) {
614
620
  issues.push({
615
- path: path13,
621
+ path: path15,
616
622
  message: "must match at least one supported shape"
617
623
  });
618
624
  return issues;
@@ -620,7 +626,7 @@ function validateSchemaNode(schema, value, path13) {
620
626
  }
621
627
  if (schema.allOf !== void 0) {
622
628
  for (const member of schema.allOf) {
623
- issues.push(...validateSchemaNode(member, value, path13));
629
+ issues.push(...validateSchemaNode(member, value, path15));
624
630
  }
625
631
  if (issues.length > 0) {
626
632
  return issues;
@@ -628,7 +634,7 @@ function validateSchemaNode(schema, value, path13) {
628
634
  }
629
635
  if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
630
636
  issues.push({
631
- path: path13,
637
+ path: path15,
632
638
  message: `must be ${describeSchemaType(schema.type)}`
633
639
  });
634
640
  return issues;
@@ -636,19 +642,19 @@ function validateSchemaNode(schema, value, path13) {
636
642
  if (typeof value === "string") {
637
643
  if (schema.minLength !== void 0 && value.length < schema.minLength) {
638
644
  issues.push({
639
- path: path13,
645
+ path: path15,
640
646
  message: `must have length >= ${String(schema.minLength)}`
641
647
  });
642
648
  }
643
649
  if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
644
650
  issues.push({
645
- path: path13,
651
+ path: path15,
646
652
  message: `must have length <= ${String(schema.maxLength)}`
647
653
  });
648
654
  }
649
655
  if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
650
656
  issues.push({
651
- path: path13,
657
+ path: path15,
652
658
  message: `must match pattern ${schema.pattern}`
653
659
  });
654
660
  }
@@ -657,25 +663,25 @@ function validateSchemaNode(schema, value, path13) {
657
663
  if (typeof value === "number") {
658
664
  if (schema.minimum !== void 0 && value < schema.minimum) {
659
665
  issues.push({
660
- path: path13,
666
+ path: path15,
661
667
  message: `must be >= ${String(schema.minimum)}`
662
668
  });
663
669
  }
664
670
  if (schema.maximum !== void 0 && value > schema.maximum) {
665
671
  issues.push({
666
- path: path13,
672
+ path: path15,
667
673
  message: `must be <= ${String(schema.maximum)}`
668
674
  });
669
675
  }
670
676
  if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
671
677
  issues.push({
672
- path: path13,
678
+ path: path15,
673
679
  message: `must be > ${String(schema.exclusiveMinimum)}`
674
680
  });
675
681
  }
676
682
  if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
677
683
  issues.push({
678
- path: path13,
684
+ path: path15,
679
685
  message: `must be < ${String(schema.exclusiveMaximum)}`
680
686
  });
681
687
  }
@@ -684,13 +690,13 @@ function validateSchemaNode(schema, value, path13) {
684
690
  if (Array.isArray(value)) {
685
691
  if (schema.minItems !== void 0 && value.length < schema.minItems) {
686
692
  issues.push({
687
- path: path13,
693
+ path: path15,
688
694
  message: `must have at least ${String(schema.minItems)} items`
689
695
  });
690
696
  }
691
697
  if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
692
698
  issues.push({
693
- path: path13,
699
+ path: path15,
694
700
  message: `must have at most ${String(schema.maxItems)} items`
695
701
  });
696
702
  }
@@ -700,7 +706,7 @@ function validateSchemaNode(schema, value, path13) {
700
706
  const key = JSON.stringify(item);
701
707
  if (seen.has(key)) {
702
708
  issues.push({
703
- path: path13,
709
+ path: path15,
704
710
  message: "must not contain duplicate items"
705
711
  });
706
712
  break;
@@ -710,7 +716,7 @@ function validateSchemaNode(schema, value, path13) {
710
716
  }
711
717
  if (schema.items !== void 0) {
712
718
  for (let index = 0; index < value.length; index += 1) {
713
- issues.push(...validateSchemaNode(schema.items, value[index], `${path13}[${String(index)}]`));
719
+ issues.push(...validateSchemaNode(schema.items, value[index], `${path15}[${String(index)}]`));
714
720
  }
715
721
  }
716
722
  return issues;
@@ -720,7 +726,7 @@ function validateSchemaNode(schema, value, path13) {
720
726
  for (const requiredKey of schema.required ?? []) {
721
727
  if (!(requiredKey in value)) {
722
728
  issues.push({
723
- path: joinObjectPath(path13, requiredKey),
729
+ path: joinObjectPath(path15, requiredKey),
724
730
  message: "is required"
725
731
  });
726
732
  }
@@ -729,13 +735,13 @@ function validateSchemaNode(schema, value, path13) {
729
735
  const propertySchema = properties[key];
730
736
  if (propertySchema !== void 0) {
731
737
  issues.push(
732
- ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path13, key))
738
+ ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path15, key))
733
739
  );
734
740
  continue;
735
741
  }
736
742
  if (schema.additionalProperties === false) {
737
743
  issues.push({
738
- path: joinObjectPath(path13, key),
744
+ path: joinObjectPath(path15, key),
739
745
  message: "is not allowed"
740
746
  });
741
747
  continue;
@@ -745,7 +751,7 @@ function validateSchemaNode(schema, value, path13) {
745
751
  ...validateSchemaNode(
746
752
  schema.additionalProperties,
747
753
  propertyValue,
748
- joinObjectPath(path13, key)
754
+ joinObjectPath(path15, key)
749
755
  )
750
756
  );
751
757
  }
@@ -989,8 +995,8 @@ function matchesNetworkRecordFilters(record, filters) {
989
995
  }
990
996
  }
991
997
  if (filters.path !== void 0) {
992
- const path13 = getParsedUrl().pathname;
993
- if (!includesCaseInsensitive(path13, filters.path)) {
998
+ const path15 = getParsedUrl().pathname;
999
+ if (!includesCaseInsensitive(path15, filters.path)) {
994
1000
  return false;
995
1001
  }
996
1002
  }
@@ -7080,9 +7086,9 @@ function compareByCreatedAtAndId(left, right) {
7080
7086
  var FilesystemRegistryStore = class {
7081
7087
  constructor(rootPath, registryRelativePath) {
7082
7088
  this.registryRelativePath = registryRelativePath;
7083
- const basePath = path6__default.default.join(rootPath, ...registryRelativePath);
7084
- this.recordsDirectory = path6__default.default.join(basePath, "records");
7085
- this.indexesDirectory = path6__default.default.join(basePath, "indexes", "by-key");
7089
+ const basePath = path7__default.default.join(rootPath, ...registryRelativePath);
7090
+ this.recordsDirectory = path7__default.default.join(basePath, "records");
7091
+ this.indexesDirectory = path7__default.default.join(basePath, "indexes", "by-key");
7086
7092
  }
7087
7093
  recordsDirectory;
7088
7094
  indexesDirectory;
@@ -7151,7 +7157,7 @@ var FilesystemRegistryStore = class {
7151
7157
  async readRecordsFromDirectory() {
7152
7158
  const files = await listJsonFiles(this.recordsDirectory);
7153
7159
  const records = await Promise.all(
7154
- files.map((fileName) => readJsonFile(path6__default.default.join(this.recordsDirectory, fileName)))
7160
+ files.map((fileName) => readJsonFile(path7__default.default.join(this.recordsDirectory, fileName)))
7155
7161
  );
7156
7162
  records.sort(compareByCreatedAtAndId);
7157
7163
  return records;
@@ -7183,17 +7189,17 @@ var FilesystemRegistryStore = class {
7183
7189
  return record;
7184
7190
  }
7185
7191
  recordPath(id) {
7186
- return path6__default.default.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7192
+ return path7__default.default.join(this.recordsDirectory, `${encodePathSegment(id)}.json`);
7187
7193
  }
7188
7194
  indexPath(key, version) {
7189
- return path6__default.default.join(
7195
+ return path7__default.default.join(
7190
7196
  this.indexesDirectory,
7191
7197
  encodePathSegment(key),
7192
7198
  `${encodePathSegment(version)}.json`
7193
7199
  );
7194
7200
  }
7195
7201
  writeLockPath() {
7196
- return path6__default.default.join(path6__default.default.dirname(this.recordsDirectory), ".write.lock");
7202
+ return path7__default.default.join(path7__default.default.dirname(this.recordsDirectory), ".write.lock");
7197
7203
  }
7198
7204
  };
7199
7205
  var FilesystemDescriptorRegistry = class extends FilesystemRegistryStore {
@@ -7563,7 +7569,7 @@ var SqliteSavedNetworkStore = class {
7563
7569
  directoryInitialization;
7564
7570
  databaseInitialization;
7565
7571
  constructor(rootPath) {
7566
- this.databasePath = path6__default.default.join(rootPath, "registry", "saved-network.sqlite");
7572
+ this.databasePath = path7__default.default.join(rootPath, "registry", "saved-network.sqlite");
7567
7573
  }
7568
7574
  async initialize() {
7569
7575
  await this.ensureDatabaseDirectory();
@@ -7773,7 +7779,7 @@ var SqliteSavedNetworkStore = class {
7773
7779
  }
7774
7780
  }
7775
7781
  async ensureDatabaseDirectory() {
7776
- this.directoryInitialization ??= ensureDirectory(path6__default.default.dirname(this.databasePath)).catch(
7782
+ this.directoryInitialization ??= ensureDirectory(path7__default.default.dirname(this.databasePath)).catch(
7777
7783
  (error) => {
7778
7784
  this.directoryInitialization = void 0;
7779
7785
  throw error;
@@ -8163,6 +8169,546 @@ function withSqliteTransaction(database, task) {
8163
8169
  function createSavedNetworkStore(rootPath) {
8164
8170
  return new SqliteSavedNetworkStore(rootPath);
8165
8171
  }
8172
+
8173
+ // ../runtime-core/src/observation-utils.ts
8174
+ var REDACTED = "[REDACTED]";
8175
+ var SENSITIVE_KEY_PATTERN = /(authorization|proxy[_-]?authorization|cookie|set-cookie|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|token|secret|password|passwd|private[_-]?key|database[_-]?url|db[_-]?url|session(?:id)?|csrf(?:token)?)/i;
8176
+ var SENSITIVE_VALUE_PATTERNS = [
8177
+ /\bsk-[A-Za-z0-9_-]{20,}\b/g,
8178
+ /\bAIza[0-9A-Za-z_-]{20,}\b/g,
8179
+ /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g,
8180
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
8181
+ /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi
8182
+ ];
8183
+ function normalizeObservationContext(context) {
8184
+ if (context === void 0) {
8185
+ return void 0;
8186
+ }
8187
+ const normalized = {
8188
+ ...context.sessionRef === void 0 ? {} : { sessionRef: context.sessionRef },
8189
+ ...context.pageRef === void 0 ? {} : { pageRef: context.pageRef },
8190
+ ...context.frameRef === void 0 ? {} : { frameRef: context.frameRef },
8191
+ ...context.documentRef === void 0 ? {} : { documentRef: context.documentRef },
8192
+ ...context.documentEpoch === void 0 ? {} : { documentEpoch: context.documentEpoch }
8193
+ };
8194
+ return Object.keys(normalized).length === 0 ? void 0 : normalized;
8195
+ }
8196
+ function createObservationRedactor(config) {
8197
+ const state = createRedactionState(config);
8198
+ return {
8199
+ redactText(value) {
8200
+ return redactString(value, state);
8201
+ },
8202
+ redactJson(value) {
8203
+ return value === void 0 ? void 0 : redactUnknown(value, state, /* @__PURE__ */ new WeakSet());
8204
+ },
8205
+ redactError(error) {
8206
+ if (error === void 0) {
8207
+ return void 0;
8208
+ }
8209
+ return {
8210
+ ...error.code === void 0 ? {} : { code: redactString(error.code, state) },
8211
+ message: redactString(error.message, state),
8212
+ ...error.retriable === void 0 ? {} : { retriable: error.retriable },
8213
+ ...error.details === void 0 ? {} : { details: toCanonicalJsonValue(redactUnknown(error.details, state, /* @__PURE__ */ new WeakSet())) }
8214
+ };
8215
+ },
8216
+ redactLabels(labels) {
8217
+ if (labels === void 0) {
8218
+ return void 0;
8219
+ }
8220
+ const next = Object.entries(labels).reduce(
8221
+ (accumulator, [key, value]) => {
8222
+ accumulator[key] = isSensitiveKey(key, state) ? REDACTED : redactString(value, state);
8223
+ return accumulator;
8224
+ },
8225
+ {}
8226
+ );
8227
+ return Object.keys(next).length === 0 ? void 0 : next;
8228
+ },
8229
+ redactTraceContext(traceContext) {
8230
+ if (traceContext === void 0) {
8231
+ return void 0;
8232
+ }
8233
+ const next = {
8234
+ ...traceContext.traceparent === void 0 ? {} : { traceparent: redactString(traceContext.traceparent, state) },
8235
+ ...traceContext.baggage === void 0 ? {} : { baggage: redactString(traceContext.baggage, state) }
8236
+ };
8237
+ return Object.keys(next).length === 0 ? void 0 : next;
8238
+ }
8239
+ };
8240
+ }
8241
+ function createRedactionState(config) {
8242
+ return {
8243
+ sensitiveKeys: new Set(
8244
+ (config?.redaction?.sensitiveKeys ?? []).map((value) => value.trim().toLowerCase()).filter((value) => value.length > 0)
8245
+ ),
8246
+ sensitiveValues: [
8247
+ ...new Set(
8248
+ (config?.redaction?.sensitiveValues ?? []).map((value) => value.trim()).filter((value) => value.length > 0)
8249
+ )
8250
+ ]
8251
+ };
8252
+ }
8253
+ function redactUnknown(value, state, seen) {
8254
+ if (value === null || value === void 0) {
8255
+ return value;
8256
+ }
8257
+ if (typeof value === "string") {
8258
+ return redactString(value, state);
8259
+ }
8260
+ if (typeof value !== "object") {
8261
+ return value;
8262
+ }
8263
+ if (seen.has(value)) {
8264
+ return REDACTED;
8265
+ }
8266
+ seen.add(value);
8267
+ if (Array.isArray(value)) {
8268
+ return value.map((entry) => redactUnknown(entry, state, seen));
8269
+ }
8270
+ const next = {};
8271
+ for (const [key, nestedValue] of Object.entries(value)) {
8272
+ next[key] = isSensitiveKey(key, state) ? REDACTED : redactUnknown(nestedValue, state, seen);
8273
+ }
8274
+ return next;
8275
+ }
8276
+ function redactString(value, state) {
8277
+ let next = value;
8278
+ for (const secret of state.sensitiveValues) {
8279
+ next = next.split(secret).join(REDACTED);
8280
+ }
8281
+ for (const pattern of SENSITIVE_VALUE_PATTERNS) {
8282
+ next = next.replace(pattern, REDACTED);
8283
+ }
8284
+ return redactUrlString(next, state);
8285
+ }
8286
+ function redactUrlString(value, state) {
8287
+ let parsed;
8288
+ try {
8289
+ parsed = new URL(value);
8290
+ } catch {
8291
+ return value;
8292
+ }
8293
+ let changed = false;
8294
+ if (parsed.username) {
8295
+ parsed.username = REDACTED;
8296
+ changed = true;
8297
+ }
8298
+ if (parsed.password) {
8299
+ parsed.password = REDACTED;
8300
+ changed = true;
8301
+ }
8302
+ for (const [key] of parsed.searchParams) {
8303
+ if (!isSensitiveKey(key, state)) {
8304
+ continue;
8305
+ }
8306
+ parsed.searchParams.set(key, REDACTED);
8307
+ changed = true;
8308
+ }
8309
+ return changed ? parsed.toString() : value;
8310
+ }
8311
+ function isSensitiveKey(key, state) {
8312
+ return state.sensitiveKeys.has(key.trim().toLowerCase()) || SENSITIVE_KEY_PATTERN.test(key);
8313
+ }
8314
+
8315
+ // ../runtime-core/src/observations.ts
8316
+ function normalizeObservabilityConfig(input) {
8317
+ const profile = input?.profile ?? "diagnostic";
8318
+ const labels = input?.labels === void 0 ? void 0 : Object.entries(input.labels).reduce((accumulator, [key, value]) => {
8319
+ const normalizedKey = key.trim();
8320
+ const normalizedValue = value.trim();
8321
+ if (normalizedKey.length === 0 || normalizedValue.length === 0) {
8322
+ return accumulator;
8323
+ }
8324
+ if (Object.keys(accumulator).length >= 20) {
8325
+ return accumulator;
8326
+ }
8327
+ accumulator[normalizedKey] = normalizedValue;
8328
+ return accumulator;
8329
+ }, {});
8330
+ const redaction = input?.redaction === void 0 ? void 0 : {
8331
+ ...input.redaction.sensitiveKeys === void 0 ? {} : {
8332
+ sensitiveKeys: input.redaction.sensitiveKeys.map((value) => value.trim()).filter((value) => value.length > 0)
8333
+ },
8334
+ ...input.redaction.sensitiveValues === void 0 ? {} : {
8335
+ sensitiveValues: input.redaction.sensitiveValues.map((value) => value.trim()).filter((value) => value.length > 0)
8336
+ }
8337
+ };
8338
+ return {
8339
+ profile,
8340
+ ...labels === void 0 || Object.keys(labels).length === 0 ? {} : { labels },
8341
+ ...input?.traceContext === void 0 ? {} : {
8342
+ traceContext: {
8343
+ ...input.traceContext.traceparent === void 0 ? {} : { traceparent: input.traceContext.traceparent.trim() },
8344
+ ...input.traceContext.baggage === void 0 ? {} : { baggage: input.traceContext.baggage.trim() }
8345
+ }
8346
+ },
8347
+ ...redaction === void 0 ? {} : { redaction }
8348
+ };
8349
+ }
8350
+ function eventFileName(sequence) {
8351
+ return `${String(sequence).padStart(12, "0")}.json`;
8352
+ }
8353
+ var FilesystemSessionSink = class {
8354
+ constructor(store, sessionId) {
8355
+ this.store = store;
8356
+ this.sessionId = sessionId;
8357
+ }
8358
+ append(input) {
8359
+ return this.store.appendEvent(this.sessionId, input);
8360
+ }
8361
+ appendBatch(input) {
8362
+ return this.store.appendEvents(this.sessionId, input);
8363
+ }
8364
+ writeArtifact(input) {
8365
+ return this.store.writeArtifact(this.sessionId, input);
8366
+ }
8367
+ async flush() {
8368
+ }
8369
+ close(reason) {
8370
+ return this.store.closeSession(this.sessionId, reason);
8371
+ }
8372
+ };
8373
+ var FilesystemObservationStoreImpl = class {
8374
+ constructor(rootPath, artifacts) {
8375
+ this.rootPath = rootPath;
8376
+ this.artifacts = artifacts;
8377
+ this.sessionsDirectory = path7__default.default.join(this.rootPath, "observations", "sessions");
8378
+ }
8379
+ sessionsDirectory;
8380
+ redactors = /* @__PURE__ */ new Map();
8381
+ async initialize() {
8382
+ await ensureDirectory(this.sessionsDirectory);
8383
+ }
8384
+ async openSession(input) {
8385
+ const sessionId = normalizeNonEmptyString("sessionId", input.sessionId);
8386
+ const openedAt = normalizeTimestamp("openedAt", input.openedAt ?? Date.now());
8387
+ const config = normalizeObservabilityConfig(input.config);
8388
+ const redactor = createObservationRedactor(config);
8389
+ this.redactors.set(sessionId, redactor);
8390
+ const redactedLabels = redactor.redactLabels(config.labels);
8391
+ const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
8392
+ await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
8393
+ const existing = await this.reconcileSessionManifest(sessionId);
8394
+ if (existing === void 0) {
8395
+ await ensureDirectory(this.sessionEventsDirectory(sessionId));
8396
+ await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
8397
+ const session = {
8398
+ sessionId,
8399
+ profile: config.profile,
8400
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
8401
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
8402
+ openedAt,
8403
+ updatedAt: openedAt,
8404
+ currentSequence: 0,
8405
+ eventCount: 0,
8406
+ artifactCount: 0
8407
+ };
8408
+ await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
8409
+ return;
8410
+ }
8411
+ const patched = {
8412
+ ...existing,
8413
+ profile: config.profile,
8414
+ ...redactedLabels === void 0 ? {} : { labels: redactedLabels },
8415
+ ...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
8416
+ updatedAt: Math.max(existing.updatedAt, openedAt)
8417
+ };
8418
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
8419
+ });
8420
+ return new FilesystemSessionSink(this, sessionId);
8421
+ }
8422
+ async getSession(sessionId) {
8423
+ const manifestPath = this.sessionManifestPath(sessionId);
8424
+ if (!await pathExists(manifestPath)) {
8425
+ return void 0;
8426
+ }
8427
+ return readJsonFile(manifestPath);
8428
+ }
8429
+ async appendEvent(sessionId, input) {
8430
+ const [event] = await this.appendEvents(sessionId, [input]);
8431
+ if (event === void 0) {
8432
+ throw new Error(`failed to append observation event for session ${sessionId}`);
8433
+ }
8434
+ return event;
8435
+ }
8436
+ async appendEvents(sessionId, input) {
8437
+ if (input.length === 0) {
8438
+ return [];
8439
+ }
8440
+ return withFilesystemLock(this.sessionLockPath(sessionId), async () => {
8441
+ const session = await this.reconcileSessionManifest(sessionId);
8442
+ if (session === void 0) {
8443
+ throw new Error(`observation session ${sessionId} was not found`);
8444
+ }
8445
+ const redactor = this.redactors.get(sessionId) ?? createObservationRedactor(void 0);
8446
+ const events = [];
8447
+ let sequence = session.currentSequence;
8448
+ let updatedAt = session.updatedAt;
8449
+ for (const raw of input) {
8450
+ sequence += 1;
8451
+ const createdAt = normalizeTimestamp("createdAt", raw.createdAt);
8452
+ const context = normalizeObservationContext(raw.context);
8453
+ const redactedData = raw.data === void 0 ? void 0 : redactor.redactJson(toCanonicalJsonValue(raw.data));
8454
+ const redactedError = redactor.redactError(raw.error);
8455
+ const event = {
8456
+ eventId: normalizeNonEmptyString(
8457
+ "eventId",
8458
+ raw.eventId ?? `observation:${sessionId}:${String(sequence).padStart(12, "0")}`
8459
+ ),
8460
+ sessionId,
8461
+ sequence,
8462
+ kind: raw.kind,
8463
+ phase: raw.phase,
8464
+ createdAt,
8465
+ correlationId: normalizeNonEmptyString("correlationId", raw.correlationId),
8466
+ ...raw.spanId === void 0 ? {} : { spanId: normalizeNonEmptyString("spanId", raw.spanId) },
8467
+ ...raw.parentSpanId === void 0 ? {} : { parentSpanId: normalizeNonEmptyString("parentSpanId", raw.parentSpanId) },
8468
+ ...context === void 0 ? {} : { context },
8469
+ ...redactedData === void 0 ? {} : { data: redactedData },
8470
+ ...redactedError === void 0 ? {} : { error: redactedError },
8471
+ ...raw.artifactIds === void 0 || raw.artifactIds.length === 0 ? {} : { artifactIds: [...raw.artifactIds] }
8472
+ };
8473
+ await writeJsonFileExclusive(
8474
+ path7__default.default.join(this.sessionEventsDirectory(sessionId), eventFileName(sequence)),
8475
+ event
8476
+ );
8477
+ updatedAt = Math.max(updatedAt, createdAt);
8478
+ events.push(event);
8479
+ }
8480
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
8481
+ ...session,
8482
+ currentSequence: sequence,
8483
+ eventCount: session.eventCount + events.length,
8484
+ updatedAt
8485
+ });
8486
+ return events;
8487
+ });
8488
+ }
8489
+ async writeArtifact(sessionId, input) {
8490
+ return withFilesystemLock(this.sessionLockPath(sessionId), async () => {
8491
+ const session = await this.reconcileSessionManifest(sessionId);
8492
+ if (session === void 0) {
8493
+ throw new Error(`observation session ${sessionId} was not found`);
8494
+ }
8495
+ const redactor = this.redactors.get(sessionId) ?? createObservationRedactor(void 0);
8496
+ const createdAt = normalizeTimestamp("createdAt", input.createdAt);
8497
+ const context = normalizeObservationContext(input.context);
8498
+ const redactedStorageKey = input.storageKey === void 0 ? void 0 : redactor.redactText(input.storageKey);
8499
+ const redactedMetadata = input.metadata === void 0 ? void 0 : redactor.redactJson(toCanonicalJsonValue(input.metadata));
8500
+ const artifact = {
8501
+ artifactId: normalizeNonEmptyString("artifactId", input.artifactId),
8502
+ sessionId,
8503
+ kind: input.kind,
8504
+ createdAt,
8505
+ ...context === void 0 ? {} : { context },
8506
+ ...input.mediaType === void 0 ? {} : { mediaType: input.mediaType },
8507
+ ...input.byteLength === void 0 ? {} : { byteLength: input.byteLength },
8508
+ ...input.sha256 === void 0 ? {} : { sha256: input.sha256 },
8509
+ ...input.opensteerArtifactId === void 0 ? {} : { opensteerArtifactId: input.opensteerArtifactId },
8510
+ ...redactedStorageKey === void 0 ? {} : { storageKey: redactedStorageKey },
8511
+ ...redactedMetadata === void 0 ? {} : { metadata: redactedMetadata }
8512
+ };
8513
+ const artifactPath = this.sessionArtifactPath(sessionId, artifact.artifactId);
8514
+ if (!await pathExists(artifactPath)) {
8515
+ await writeJsonFileExclusive(artifactPath, artifact);
8516
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
8517
+ ...session,
8518
+ artifactCount: session.artifactCount + 1,
8519
+ updatedAt: Math.max(session.updatedAt, createdAt)
8520
+ });
8521
+ }
8522
+ return artifact;
8523
+ });
8524
+ }
8525
+ async listEvents(sessionId, input = {}) {
8526
+ const directoryPath = this.sessionEventsDirectory(sessionId);
8527
+ if (!await pathExists(directoryPath)) {
8528
+ return [];
8529
+ }
8530
+ const files = await listJsonFiles(directoryPath);
8531
+ const events = await Promise.all(
8532
+ files.map((fileName) => readJsonFile(path7__default.default.join(directoryPath, fileName)))
8533
+ );
8534
+ const filtered = events.filter((event) => {
8535
+ if (input.kind !== void 0 && event.kind !== input.kind) return false;
8536
+ if (input.phase !== void 0 && event.phase !== input.phase) return false;
8537
+ if (input.correlationId !== void 0 && event.correlationId !== input.correlationId) {
8538
+ return false;
8539
+ }
8540
+ if (input.pageRef !== void 0 && event.context?.pageRef !== input.pageRef) return false;
8541
+ if (input.afterSequence !== void 0 && event.sequence <= input.afterSequence) return false;
8542
+ if (input.from !== void 0 && event.createdAt < input.from) return false;
8543
+ if (input.to !== void 0 && event.createdAt > input.to) return false;
8544
+ return true;
8545
+ });
8546
+ if (input.limit === void 0 || filtered.length <= input.limit) {
8547
+ return filtered;
8548
+ }
8549
+ return filtered.slice(-input.limit);
8550
+ }
8551
+ async listArtifacts(sessionId, input = {}) {
8552
+ const directoryPath = this.sessionArtifactsDirectory(sessionId);
8553
+ if (!await pathExists(directoryPath)) {
8554
+ return [];
8555
+ }
8556
+ const files = await listJsonFiles(directoryPath);
8557
+ const artifacts = await Promise.all(
8558
+ files.map(
8559
+ (fileName) => readJsonFile(path7__default.default.join(directoryPath, fileName))
8560
+ )
8561
+ );
8562
+ const filtered = artifacts.filter((artifact) => {
8563
+ if (input.kind !== void 0 && artifact.kind !== input.kind) return false;
8564
+ if (input.pageRef !== void 0 && artifact.context?.pageRef !== input.pageRef) return false;
8565
+ return true;
8566
+ });
8567
+ if (input.limit === void 0 || filtered.length <= input.limit) {
8568
+ return filtered;
8569
+ }
8570
+ return filtered.slice(-input.limit);
8571
+ }
8572
+ async getArtifact(sessionId, artifactId) {
8573
+ const artifactPath = this.sessionArtifactPath(sessionId, artifactId);
8574
+ if (!await pathExists(artifactPath)) {
8575
+ return void 0;
8576
+ }
8577
+ return readJsonFile(artifactPath);
8578
+ }
8579
+ async closeSession(sessionId, _reason) {
8580
+ await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
8581
+ const session = await this.reconcileSessionManifest(sessionId);
8582
+ if (session === void 0 || session.closedAt !== void 0) {
8583
+ return;
8584
+ }
8585
+ const now = Date.now();
8586
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), {
8587
+ ...session,
8588
+ updatedAt: Math.max(session.updatedAt, now),
8589
+ closedAt: now
8590
+ });
8591
+ });
8592
+ this.redactors.delete(sessionId);
8593
+ }
8594
+ async writeArtifactFromManifest(sessionId, manifest, kind, metadata) {
8595
+ return this.writeArtifact(sessionId, {
8596
+ artifactId: manifest.artifactId,
8597
+ kind,
8598
+ createdAt: manifest.createdAt,
8599
+ context: manifest.scope,
8600
+ mediaType: manifest.mediaType,
8601
+ byteLength: manifest.byteLength,
8602
+ sha256: manifest.sha256,
8603
+ opensteerArtifactId: manifest.artifactId,
8604
+ storageKey: manifestToExternalBinaryLocation(this.rootPath, manifest).uri,
8605
+ ...metadata === void 0 ? {} : { metadata }
8606
+ });
8607
+ }
8608
+ async ensureArtifactLinked(sessionId, manifest) {
8609
+ const existing = await this.getArtifact(sessionId, manifest.artifactId);
8610
+ if (existing !== void 0) {
8611
+ return existing;
8612
+ }
8613
+ const kind = toObservationArtifactKind(manifest.kind);
8614
+ return this.writeArtifactFromManifest(sessionId, manifest, kind);
8615
+ }
8616
+ async hydrateArtifactManifests(artifactIds) {
8617
+ return (await Promise.all(
8618
+ artifactIds.map(async (artifactId) => this.artifacts.getManifest(artifactId))
8619
+ )).filter((value) => value !== void 0);
8620
+ }
8621
+ sessionDirectory(sessionId) {
8622
+ return path7__default.default.join(this.sessionsDirectory, encodePathSegment(sessionId));
8623
+ }
8624
+ sessionManifestPath(sessionId) {
8625
+ return path7__default.default.join(this.sessionDirectory(sessionId), "session.json");
8626
+ }
8627
+ sessionEventsDirectory(sessionId) {
8628
+ return path7__default.default.join(this.sessionDirectory(sessionId), "events");
8629
+ }
8630
+ sessionArtifactsDirectory(sessionId) {
8631
+ return path7__default.default.join(this.sessionDirectory(sessionId), "artifacts");
8632
+ }
8633
+ sessionArtifactPath(sessionId, artifactId) {
8634
+ return path7__default.default.join(
8635
+ this.sessionArtifactsDirectory(sessionId),
8636
+ `${encodePathSegment(artifactId)}.json`
8637
+ );
8638
+ }
8639
+ sessionLockPath(sessionId) {
8640
+ return path7__default.default.join(this.sessionDirectory(sessionId), ".lock");
8641
+ }
8642
+ async reconcileSessionManifest(sessionId) {
8643
+ const session = await this.getSession(sessionId);
8644
+ if (session === void 0) {
8645
+ return void 0;
8646
+ }
8647
+ const [hasEventDirectory, hasArtifactDirectory] = await Promise.all([
8648
+ pathExists(this.sessionEventsDirectory(sessionId)),
8649
+ pathExists(this.sessionArtifactsDirectory(sessionId))
8650
+ ]);
8651
+ const [eventFiles, artifactFiles] = await Promise.all([
8652
+ hasEventDirectory ? listJsonFiles(this.sessionEventsDirectory(sessionId)) : Promise.resolve([]),
8653
+ hasArtifactDirectory ? listJsonFiles(this.sessionArtifactsDirectory(sessionId)) : Promise.resolve([])
8654
+ ]);
8655
+ const currentSequence = eventFiles.reduce((maxSequence, fileName) => {
8656
+ const parsed = Number.parseInt(fileName.replace(/\.json$/u, ""), 10);
8657
+ return Number.isFinite(parsed) ? Math.max(maxSequence, parsed) : maxSequence;
8658
+ }, 0);
8659
+ const eventCount = eventFiles.length;
8660
+ const artifactCount = artifactFiles.length;
8661
+ if (session.currentSequence === currentSequence && session.eventCount === eventCount && session.artifactCount === artifactCount) {
8662
+ return session;
8663
+ }
8664
+ const [events, artifacts] = await Promise.all([
8665
+ Promise.all(
8666
+ eventFiles.map(
8667
+ (fileName) => readJsonFile(
8668
+ path7__default.default.join(this.sessionEventsDirectory(sessionId), fileName)
8669
+ )
8670
+ )
8671
+ ),
8672
+ Promise.all(
8673
+ artifactFiles.map(
8674
+ (fileName) => readJsonFile(
8675
+ path7__default.default.join(this.sessionArtifactsDirectory(sessionId), fileName)
8676
+ )
8677
+ )
8678
+ )
8679
+ ]);
8680
+ const updatedAt = Math.max(
8681
+ session.openedAt,
8682
+ session.closedAt ?? 0,
8683
+ ...events.map((event) => event.createdAt),
8684
+ ...artifacts.map((artifact) => artifact.createdAt)
8685
+ );
8686
+ const reconciled = {
8687
+ ...session,
8688
+ currentSequence,
8689
+ eventCount,
8690
+ artifactCount,
8691
+ updatedAt
8692
+ };
8693
+ await writeJsonFileAtomic(this.sessionManifestPath(sessionId), reconciled);
8694
+ return reconciled;
8695
+ }
8696
+ };
8697
+ function toObservationArtifactKind(kind) {
8698
+ switch (kind) {
8699
+ case "screenshot":
8700
+ return "screenshot";
8701
+ case "dom-snapshot":
8702
+ return "dom-snapshot";
8703
+ case "html-snapshot":
8704
+ return "html-snapshot";
8705
+ default:
8706
+ return "other";
8707
+ }
8708
+ }
8709
+ function createObservationStore(rootPath, artifacts) {
8710
+ return new FilesystemObservationStoreImpl(rootPath, artifacts);
8711
+ }
8166
8712
  function normalizeContext(context) {
8167
8713
  return {
8168
8714
  ...context?.sessionRef === void 0 ? {} : { sessionRef: context.sessionRef },
@@ -8179,7 +8725,7 @@ var FilesystemTraceStore = class {
8179
8725
  constructor(rootPath, artifacts) {
8180
8726
  this.rootPath = rootPath;
8181
8727
  this.artifacts = artifacts;
8182
- this.runsDirectory = path6__default.default.join(this.rootPath, "traces", "runs");
8728
+ this.runsDirectory = path7__default.default.join(this.rootPath, "traces", "runs");
8183
8729
  }
8184
8730
  runsDirectory;
8185
8731
  async initialize() {
@@ -8256,7 +8802,7 @@ var FilesystemTraceStore = class {
8256
8802
  ...input.error === void 0 ? {} : { error: input.error }
8257
8803
  };
8258
8804
  await writeJsonFileExclusive(
8259
- path6__default.default.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8805
+ path7__default.default.join(this.runEntriesDirectory(runId), sequenceFileName(sequence)),
8260
8806
  entry
8261
8807
  );
8262
8808
  await writeJsonFileAtomic(this.runManifestPath(runId), {
@@ -8275,7 +8821,7 @@ var FilesystemTraceStore = class {
8275
8821
  const files = await listJsonFiles(entriesDirectory);
8276
8822
  return Promise.all(
8277
8823
  files.map(
8278
- (fileName) => readJsonFile(path6__default.default.join(entriesDirectory, fileName))
8824
+ (fileName) => readJsonFile(path7__default.default.join(entriesDirectory, fileName))
8279
8825
  )
8280
8826
  );
8281
8827
  }
@@ -8321,16 +8867,16 @@ var FilesystemTraceStore = class {
8321
8867
  return { trace, artifacts };
8322
8868
  }
8323
8869
  runDirectory(runId) {
8324
- return path6__default.default.join(this.runsDirectory, encodeURIComponent(runId));
8870
+ return path7__default.default.join(this.runsDirectory, encodeURIComponent(runId));
8325
8871
  }
8326
8872
  runEntriesDirectory(runId) {
8327
- return path6__default.default.join(this.runDirectory(runId), "entries");
8873
+ return path7__default.default.join(this.runDirectory(runId), "entries");
8328
8874
  }
8329
8875
  runManifestPath(runId) {
8330
- return path6__default.default.join(this.runDirectory(runId), "manifest.json");
8876
+ return path7__default.default.join(this.runDirectory(runId), "manifest.json");
8331
8877
  }
8332
8878
  runWriteLockPath(runId) {
8333
- return path6__default.default.join(this.runDirectory(runId), ".append.lock");
8879
+ return path7__default.default.join(this.runDirectory(runId), ".append.lock");
8334
8880
  }
8335
8881
  };
8336
8882
  function createTraceStore(rootPath, artifacts) {
@@ -8344,7 +8890,7 @@ function normalizeWorkspaceId(workspace) {
8344
8890
  return encodePathSegment(workspace);
8345
8891
  }
8346
8892
  function resolveFilesystemWorkspacePath(input) {
8347
- return path6__default.default.join(
8893
+ return path7__default.default.join(
8348
8894
  input.rootDir,
8349
8895
  ".opensteer",
8350
8896
  "workspaces",
@@ -8353,17 +8899,18 @@ function resolveFilesystemWorkspacePath(input) {
8353
8899
  }
8354
8900
  async function createFilesystemOpensteerWorkspace(options) {
8355
8901
  await ensureDirectory(options.rootPath);
8356
- const manifestPath = path6__default.default.join(options.rootPath, "workspace.json");
8357
- const browserPath = path6__default.default.join(options.rootPath, "browser");
8358
- const browserManifestPath = path6__default.default.join(browserPath, "manifest.json");
8359
- const browserUserDataDir = path6__default.default.join(browserPath, "user-data");
8360
- const livePath = path6__default.default.join(options.rootPath, "live");
8361
- const liveLocalPath = path6__default.default.join(livePath, "local.json");
8362
- const liveCloudPath = path6__default.default.join(livePath, "cloud.json");
8363
- const artifactsPath = path6__default.default.join(options.rootPath, "artifacts");
8364
- const tracesPath = path6__default.default.join(options.rootPath, "traces");
8365
- const registryPath = path6__default.default.join(options.rootPath, "registry");
8366
- const lockPath = path6__default.default.join(options.rootPath, ".lock");
8902
+ const manifestPath = path7__default.default.join(options.rootPath, "workspace.json");
8903
+ const browserPath = path7__default.default.join(options.rootPath, "browser");
8904
+ const browserManifestPath = path7__default.default.join(browserPath, "manifest.json");
8905
+ const browserUserDataDir = path7__default.default.join(browserPath, "user-data");
8906
+ const livePath = path7__default.default.join(options.rootPath, "live");
8907
+ const liveLocalPath = path7__default.default.join(livePath, "local.json");
8908
+ const liveCloudPath = path7__default.default.join(livePath, "cloud.json");
8909
+ const artifactsPath = path7__default.default.join(options.rootPath, "artifacts");
8910
+ const tracesPath = path7__default.default.join(options.rootPath, "traces");
8911
+ const observationsPath = path7__default.default.join(options.rootPath, "observations");
8912
+ const registryPath = path7__default.default.join(options.rootPath, "registry");
8913
+ const lockPath = path7__default.default.join(options.rootPath, ".lock");
8367
8914
  let manifest;
8368
8915
  if (await pathExists(manifestPath)) {
8369
8916
  manifest = await readJsonFile(manifestPath);
@@ -8377,6 +8924,17 @@ async function createFilesystemOpensteerWorkspace(options) {
8377
8924
  `workspace ${options.rootPath} uses unsupported version ${String(manifest.version)}`
8378
8925
  );
8379
8926
  }
8927
+ if (manifest.paths.observations === void 0) {
8928
+ manifest = {
8929
+ ...manifest,
8930
+ updatedAt: Date.now(),
8931
+ paths: {
8932
+ ...manifest.paths,
8933
+ observations: "observations"
8934
+ }
8935
+ };
8936
+ await writeJsonFileAtomic(manifestPath, manifest);
8937
+ }
8380
8938
  } else {
8381
8939
  const createdAt = normalizeTimestamp("createdAt", options.createdAt ?? Date.now());
8382
8940
  manifest = {
@@ -8391,6 +8949,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8391
8949
  live: "live",
8392
8950
  artifacts: "artifacts",
8393
8951
  traces: "traces",
8952
+ observations: "observations",
8394
8953
  registry: "registry"
8395
8954
  }
8396
8955
  };
@@ -8402,6 +8961,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8402
8961
  ensureDirectory(livePath),
8403
8962
  ensureDirectory(artifactsPath),
8404
8963
  ensureDirectory(tracesPath),
8964
+ ensureDirectory(observationsPath),
8405
8965
  ensureDirectory(registryPath)
8406
8966
  ]);
8407
8967
  const artifacts = createArtifactStore(options.rootPath);
@@ -8426,6 +8986,8 @@ async function createFilesystemOpensteerWorkspace(options) {
8426
8986
  await reverseReports.initialize();
8427
8987
  const traces = createTraceStore(options.rootPath, artifacts);
8428
8988
  await traces.initialize();
8989
+ const observations = createObservationStore(options.rootPath, artifacts);
8990
+ await observations.initialize();
8429
8991
  return {
8430
8992
  rootPath: options.rootPath,
8431
8993
  manifestPath,
@@ -8438,10 +9000,12 @@ async function createFilesystemOpensteerWorkspace(options) {
8438
9000
  liveCloudPath,
8439
9001
  artifactsPath,
8440
9002
  tracesPath,
9003
+ observationsPath,
8441
9004
  registryPath,
8442
9005
  lockPath,
8443
9006
  artifacts,
8444
9007
  traces,
9008
+ observations,
8445
9009
  registry: {
8446
9010
  descriptors,
8447
9011
  requestPlans,
@@ -8628,10 +9192,10 @@ function delayWithSignal(delayMs, signal) {
8628
9192
  if (signal.aborted) {
8629
9193
  return Promise.reject(signal.reason ?? abortError());
8630
9194
  }
8631
- return new Promise((resolve5, reject) => {
9195
+ return new Promise((resolve4, reject) => {
8632
9196
  const timer = setTimeout(() => {
8633
9197
  signal.removeEventListener("abort", onAbort);
8634
- resolve5();
9198
+ resolve4();
8635
9199
  }, delayMs);
8636
9200
  const onAbort = () => {
8637
9201
  clearTimeout(timer);
@@ -9096,9 +9660,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
9096
9660
  "poster",
9097
9661
  "ping"
9098
9662
  ]);
9099
- function buildArrayFieldPathCandidates(path13) {
9100
- const strict = path13.nodes.length ? buildPathCandidates(path13.nodes) : [];
9101
- const relaxedNodes = stripPositionClauses(path13.nodes);
9663
+ function buildArrayFieldPathCandidates(path15) {
9664
+ const strict = path15.nodes.length ? buildPathCandidates(path15.nodes) : [];
9665
+ const relaxedNodes = stripPositionClauses(path15.nodes);
9102
9666
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
9103
9667
  return dedupeSelectors([...strict, ...relaxed]);
9104
9668
  }
@@ -9628,18 +10192,18 @@ function cloneStructuralElementAnchor(anchor) {
9628
10192
  nodes: anchor.nodes.map(clonePathNode)
9629
10193
  };
9630
10194
  }
9631
- function cloneReplayElementPath(path13) {
10195
+ function cloneReplayElementPath(path15) {
9632
10196
  return {
9633
10197
  resolution: "deterministic",
9634
- context: cloneContext(path13.context),
9635
- nodes: path13.nodes.map(clonePathNode)
10198
+ context: cloneContext(path15.context),
10199
+ nodes: path15.nodes.map(clonePathNode)
9636
10200
  };
9637
10201
  }
9638
- function cloneElementPath(path13) {
9639
- return cloneReplayElementPath(path13);
10202
+ function cloneElementPath(path15) {
10203
+ return cloneReplayElementPath(path15);
9640
10204
  }
9641
- function buildPathSelectorHint(path13) {
9642
- const nodes = path13?.nodes || [];
10205
+ function buildPathSelectorHint(path15) {
10206
+ const nodes = path15?.nodes || [];
9643
10207
  const last = nodes[nodes.length - 1];
9644
10208
  if (!last) {
9645
10209
  return "*";
@@ -9688,15 +10252,15 @@ function sanitizeStructuralElementAnchor(anchor) {
9688
10252
  nodes: sanitizeNodes(anchor.nodes)
9689
10253
  };
9690
10254
  }
9691
- function sanitizeReplayElementPath(path13) {
10255
+ function sanitizeReplayElementPath(path15) {
9692
10256
  return {
9693
10257
  resolution: "deterministic",
9694
- context: sanitizeContext(path13.context),
9695
- nodes: sanitizeNodes(path13.nodes)
10258
+ context: sanitizeContext(path15.context),
10259
+ nodes: sanitizeNodes(path15.nodes)
9696
10260
  };
9697
10261
  }
9698
- function sanitizeElementPath(path13) {
9699
- return sanitizeReplayElementPath(path13);
10262
+ function sanitizeElementPath(path15) {
10263
+ return sanitizeReplayElementPath(path15);
9700
10264
  }
9701
10265
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
9702
10266
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -9819,8 +10383,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
9819
10383
  }
9820
10384
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
9821
10385
  }
9822
- function buildArrayFieldCandidates(path13) {
9823
- return buildArrayFieldPathCandidates(path13);
10386
+ function buildArrayFieldCandidates(path15) {
10387
+ return buildArrayFieldPathCandidates(path15);
9824
10388
  }
9825
10389
  function firstDefinedAttribute(node, keys) {
9826
10390
  for (const key of keys) {
@@ -10274,7 +10838,8 @@ async function captureActionBoundarySnapshot(engine, pageRef) {
10274
10838
  }
10275
10839
  return {
10276
10840
  pageRef,
10277
- documentRef: mainFrame.documentRef
10841
+ documentRef: mainFrame.documentRef,
10842
+ url: mainFrame.url
10278
10843
  };
10279
10844
  }
10280
10845
  function createActionBoundaryDiagnostics(input) {
@@ -10329,14 +10894,16 @@ var DomActionExecutor = class {
10329
10894
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10330
10895
  },
10331
10896
  async (pointerTarget, point, timeout) => {
10332
- await timeout.runStep(
10897
+ const events = [];
10898
+ const moved = await timeout.runStep(
10333
10899
  () => this.options.engine.mouseMove({
10334
10900
  pageRef: pointerTarget.resolved.pageRef,
10335
10901
  point,
10336
10902
  coordinateSpace: "document-css"
10337
10903
  })
10338
10904
  );
10339
- await timeout.runStep(
10905
+ events.push(...moved.events);
10906
+ const clicked = await timeout.runStep(
10340
10907
  () => this.options.engine.mouseClick({
10341
10908
  pageRef: pointerTarget.resolved.pageRef,
10342
10909
  point,
@@ -10346,7 +10913,12 @@ var DomActionExecutor = class {
10346
10913
  ...input.modifiers === void 0 ? {} : { modifiers: input.modifiers }
10347
10914
  })
10348
10915
  );
10349
- return { resolved: pointerTarget.original, point };
10916
+ events.push(...clicked.events);
10917
+ return {
10918
+ resolved: pointerTarget.original,
10919
+ point,
10920
+ ...events.length === 0 ? {} : { events }
10921
+ };
10350
10922
  }
10351
10923
  );
10352
10924
  }
@@ -10360,14 +10932,18 @@ var DomActionExecutor = class {
10360
10932
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10361
10933
  },
10362
10934
  async (pointerTarget, point, timeout) => {
10363
- await timeout.runStep(
10935
+ const moved = await timeout.runStep(
10364
10936
  () => this.options.engine.mouseMove({
10365
10937
  pageRef: pointerTarget.resolved.pageRef,
10366
10938
  point,
10367
10939
  coordinateSpace: "document-css"
10368
10940
  })
10369
10941
  );
10370
- return { resolved: pointerTarget.original, point };
10942
+ return {
10943
+ resolved: pointerTarget.original,
10944
+ point,
10945
+ ...moved.events.length === 0 ? {} : { events: moved.events }
10946
+ };
10371
10947
  }
10372
10948
  );
10373
10949
  }
@@ -10381,14 +10957,16 @@ var DomActionExecutor = class {
10381
10957
  ...input.timeout === void 0 ? {} : { timeout: input.timeout }
10382
10958
  },
10383
10959
  async (pointerTarget, point, timeout) => {
10384
- await timeout.runStep(
10960
+ const events = [];
10961
+ const moved = await timeout.runStep(
10385
10962
  () => this.options.engine.mouseMove({
10386
10963
  pageRef: pointerTarget.resolved.pageRef,
10387
10964
  point,
10388
10965
  coordinateSpace: "document-css"
10389
10966
  })
10390
10967
  );
10391
- await timeout.runStep(
10968
+ events.push(...moved.events);
10969
+ const scrolled = await timeout.runStep(
10392
10970
  () => this.options.engine.mouseScroll({
10393
10971
  pageRef: pointerTarget.resolved.pageRef,
10394
10972
  point,
@@ -10396,7 +10974,12 @@ var DomActionExecutor = class {
10396
10974
  delta: input.delta
10397
10975
  })
10398
10976
  );
10399
- return { resolved: pointerTarget.original, point };
10977
+ events.push(...scrolled.events);
10978
+ return {
10979
+ resolved: pointerTarget.original,
10980
+ point,
10981
+ ...events.length === 0 ? {} : { events }
10982
+ };
10400
10983
  }
10401
10984
  );
10402
10985
  }
@@ -11322,21 +11905,21 @@ var DefaultDomRuntime = class {
11322
11905
  return match;
11323
11906
  }
11324
11907
  async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
11325
- const path13 = sanitizeReplayElementPath(rawPath);
11326
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11327
- const target = resolveDomPathInScope(context.index, path13.nodes, context.scope);
11908
+ const path15 = sanitizeReplayElementPath(rawPath);
11909
+ const context = await this.resolvePathContext(session, pageRef, path15.context);
11910
+ const target = resolveDomPathInScope(context.index, path15.nodes, context.scope);
11328
11911
  if (!target) {
11329
- throwTargetNotFound(context.index, path13.nodes, context.scope);
11912
+ throwTargetNotFound(context.index, path15.nodes, context.scope);
11330
11913
  }
11331
11914
  if (target.node.nodeRef === void 0) {
11332
11915
  throw new Error(
11333
- `resolved path "${buildPathSelectorHint(path13)}" does not point to a live element`
11916
+ `resolved path "${buildPathSelectorHint(path15)}" does not point to a live element`
11334
11917
  );
11335
11918
  }
11336
11919
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
11337
11920
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
11338
11921
  ...description === void 0 ? {} : { description },
11339
- replayPath: path13,
11922
+ replayPath: path15,
11340
11923
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
11341
11924
  ...descriptor === void 0 ? {} : { descriptor }
11342
11925
  });
@@ -11357,9 +11940,9 @@ var DefaultDomRuntime = class {
11357
11940
  });
11358
11941
  }
11359
11942
  async queryAllByElementPath(session, pageRef, rawPath) {
11360
- const path13 = sanitizeReplayElementPath(rawPath);
11361
- const context = await this.resolvePathContext(session, pageRef, path13.context);
11362
- return queryAllDomPathInScope(context.index, path13.nodes, context.scope).filter(
11943
+ const path15 = sanitizeReplayElementPath(rawPath);
11944
+ const context = await this.resolvePathContext(session, pageRef, path15.context);
11945
+ return queryAllDomPathInScope(context.index, path15.nodes, context.scope).filter(
11363
11946
  (node) => node.nodeRef !== void 0
11364
11947
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11365
11948
  }
@@ -11545,16 +12128,16 @@ var DefaultDomRuntime = class {
11545
12128
  const index = createSnapshotIndex(item.snapshot);
11546
12129
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
11547
12130
  }
11548
- resolveFirstArrayFieldTargetInNode(index, rootNode, path13) {
11549
- const normalizedPath = sanitizeElementPath(path13);
12131
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path15) {
12132
+ const normalizedPath = sanitizeElementPath(path15);
11550
12133
  const selectors = buildArrayFieldCandidates(normalizedPath);
11551
12134
  if (!selectors.length) {
11552
12135
  return rootNode;
11553
12136
  }
11554
12137
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
11555
12138
  }
11556
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path13) {
11557
- const normalizedPath = sanitizeElementPath(path13);
12139
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path15) {
12140
+ const normalizedPath = sanitizeElementPath(path15);
11558
12141
  const selectors = buildArrayFieldCandidates(normalizedPath);
11559
12142
  if (!selectors.length) {
11560
12143
  return rootNode;
@@ -11643,8 +12226,8 @@ function encodeDataPath(tokens) {
11643
12226
  }
11644
12227
  return out;
11645
12228
  }
11646
- function parseDataPath(path13) {
11647
- const input = path13.trim();
12229
+ function parseDataPath(path15) {
12230
+ const input = path15.trim();
11648
12231
  if (input.length === 0) {
11649
12232
  return [];
11650
12233
  }
@@ -11694,8 +12277,8 @@ function parseDataPath(path13) {
11694
12277
  function inflateDataPathObject(flat) {
11695
12278
  let root = {};
11696
12279
  let initialized = false;
11697
- for (const [path13, value] of Object.entries(flat)) {
11698
- const tokens = parseDataPath(path13);
12280
+ for (const [path15, value] of Object.entries(flat)) {
12281
+ const tokens = parseDataPath(path15);
11699
12282
  if (!tokens || tokens.length === 0) {
11700
12283
  continue;
11701
12284
  }
@@ -12027,8 +12610,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
12027
12610
  fields: mergedFields
12028
12611
  };
12029
12612
  }
12030
- function minimizePathMatchClauses(path13, mode) {
12031
- const normalized = sanitizeElementPath(path13);
12613
+ function minimizePathMatchClauses(path15, mode) {
12614
+ const normalized = sanitizeElementPath(path15);
12032
12615
  const nodes = normalized.nodes.map((node, index) => {
12033
12616
  const isLast = index === normalized.nodes.length - 1;
12034
12617
  const attrs = node.attrs || {};
@@ -12132,8 +12715,8 @@ function seedMinimalAttrClause(attrs) {
12132
12715
  }
12133
12716
  return null;
12134
12717
  }
12135
- function relaxPathForSingleSample(path13, mode) {
12136
- const normalized = sanitizeElementPath(path13);
12718
+ function relaxPathForSingleSample(path15, mode) {
12719
+ const normalized = sanitizeElementPath(path15);
12137
12720
  const relaxedNodes = normalized.nodes.map((node, index) => {
12138
12721
  const isLast = index === normalized.nodes.length - 1;
12139
12722
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -12218,8 +12801,8 @@ function shouldKeepAttrForSingleSample(key) {
12218
12801
  }
12219
12802
  return true;
12220
12803
  }
12221
- function buildPathStructureKey(path13) {
12222
- const normalized = sanitizeElementPath(path13);
12804
+ function buildPathStructureKey(path15) {
12805
+ const normalized = sanitizeElementPath(path15);
12223
12806
  return canonicalJsonString({
12224
12807
  context: (normalized.context || []).map((hop) => ({
12225
12808
  kind: hop.kind,
@@ -12346,30 +12929,30 @@ function buildArrayItemNode(fields) {
12346
12929
  }
12347
12930
  return node;
12348
12931
  }
12349
- function insertNodeAtPath(root, path13, node) {
12350
- const tokens = parseDataPath(path13);
12932
+ function insertNodeAtPath(root, path15, node) {
12933
+ const tokens = parseDataPath(path15);
12351
12934
  if (!tokens || !tokens.length) {
12352
12935
  throw new Error(
12353
- `Invalid persisted extraction path "${path13}": expected a non-empty object path.`
12936
+ `Invalid persisted extraction path "${path15}": expected a non-empty object path.`
12354
12937
  );
12355
12938
  }
12356
12939
  if (tokens.some((token) => token.kind === "index")) {
12357
12940
  throw new Error(
12358
- `Invalid persisted extraction path "${path13}": nested array indices are not supported in cached descriptors.`
12941
+ `Invalid persisted extraction path "${path15}": nested array indices are not supported in cached descriptors.`
12359
12942
  );
12360
12943
  }
12361
12944
  let current = root;
12362
12945
  for (let index = 0; index < tokens.length; index += 1) {
12363
12946
  const token = tokens[index];
12364
12947
  if (!token || token.kind !== "prop") {
12365
- throw new Error(`Invalid persisted extraction path "${path13}": expected object segment.`);
12948
+ throw new Error(`Invalid persisted extraction path "${path15}": expected object segment.`);
12366
12949
  }
12367
12950
  const isLast = index === tokens.length - 1;
12368
12951
  if (isLast) {
12369
12952
  const existing = current[token.key];
12370
12953
  if (existing) {
12371
12954
  throw new Error(
12372
- `Conflicting persisted extraction path "${path13}" detected while building descriptor tree.`
12955
+ `Conflicting persisted extraction path "${path15}" detected while building descriptor tree.`
12373
12956
  );
12374
12957
  }
12375
12958
  current[token.key] = node;
@@ -12384,7 +12967,7 @@ function insertNodeAtPath(root, path13, node) {
12384
12967
  }
12385
12968
  if (!isPersistedObjectNode(next)) {
12386
12969
  throw new Error(
12387
- `Conflicting persisted extraction path "${path13}" detected at "${token.key}".`
12970
+ `Conflicting persisted extraction path "${path15}" detected at "${token.key}".`
12388
12971
  );
12389
12972
  }
12390
12973
  current = next;
@@ -12419,7 +13002,7 @@ function buildItemRootForArrayIndex(entries) {
12419
13002
  }
12420
13003
  const paths = entries.map(
12421
13004
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
12422
- ).filter((path13) => path13 !== null);
13005
+ ).filter((path15) => path15 !== null);
12423
13006
  if (!paths.length) {
12424
13007
  return null;
12425
13008
  }
@@ -12440,7 +13023,7 @@ function getCommonPathPrefixLength(paths) {
12440
13023
  if (!paths.length) {
12441
13024
  return 0;
12442
13025
  }
12443
- const nodeChains = paths.map((path13) => path13.nodes);
13026
+ const nodeChains = paths.map((path15) => path15.nodes);
12444
13027
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
12445
13028
  if (!Number.isFinite(minLength) || minLength <= 0) {
12446
13029
  return 0;
@@ -12509,30 +13092,30 @@ function mergeElementPathsByMajority(paths) {
12509
13092
  if (!paths.length) {
12510
13093
  return null;
12511
13094
  }
12512
- const normalized = paths.map((path13) => sanitizeElementPath(path13));
13095
+ const normalized = paths.map((path15) => sanitizeElementPath(path15));
12513
13096
  const contextKey = pickModeString(
12514
- normalized.map((path13) => canonicalJsonString(path13.context)),
13097
+ normalized.map((path15) => canonicalJsonString(path15.context)),
12515
13098
  1
12516
13099
  );
12517
13100
  if (!contextKey) {
12518
13101
  return null;
12519
13102
  }
12520
- const sameContext = normalized.filter((path13) => canonicalJsonString(path13.context) === contextKey);
13103
+ const sameContext = normalized.filter((path15) => canonicalJsonString(path15.context) === contextKey);
12521
13104
  if (!sameContext.length) {
12522
13105
  return null;
12523
13106
  }
12524
13107
  const targetLength = pickModeNumber(
12525
- sameContext.map((path13) => path13.nodes.length),
13108
+ sameContext.map((path15) => path15.nodes.length),
12526
13109
  1
12527
13110
  ) ?? sameContext[0]?.nodes.length ?? 0;
12528
- const aligned = sameContext.filter((path13) => path13.nodes.length === targetLength);
13111
+ const aligned = sameContext.filter((path15) => path15.nodes.length === targetLength);
12529
13112
  if (!aligned.length) {
12530
13113
  return null;
12531
13114
  }
12532
13115
  const threshold = majorityThreshold(aligned.length);
12533
13116
  const nodes = [];
12534
13117
  for (let index = 0; index < targetLength; index += 1) {
12535
- const nodesAtIndex = aligned.map((path13) => path13.nodes[index]).filter((node) => node !== void 0);
13118
+ const nodesAtIndex = aligned.map((path15) => path15.nodes[index]).filter((node) => node !== void 0);
12536
13119
  if (!nodesAtIndex.length) {
12537
13120
  return null;
12538
13121
  }
@@ -12778,8 +13361,8 @@ function clonePathContext(context) {
12778
13361
  function clonePathNodes(nodes) {
12779
13362
  return JSON.parse(JSON.stringify(nodes || []));
12780
13363
  }
12781
- function cloneElementPath2(path13) {
12782
- return JSON.parse(JSON.stringify(path13));
13364
+ function cloneElementPath2(path15) {
13365
+ return JSON.parse(JSON.stringify(path15));
12783
13366
  }
12784
13367
  function clonePersistedOpensteerExtractionNode(node) {
12785
13368
  return JSON.parse(JSON.stringify(node));
@@ -13097,8 +13680,8 @@ function collectPersistedValueNodeRefs(node) {
13097
13680
  return [
13098
13681
  {
13099
13682
  path: sanitizeElementPath(node.$path),
13100
- replacePath: (path13) => {
13101
- node.$path = sanitizeElementPath(path13);
13683
+ replacePath: (path15) => {
13684
+ node.$path = sanitizeElementPath(path15);
13102
13685
  }
13103
13686
  }
13104
13687
  ];
@@ -13112,13 +13695,13 @@ function collectPersistedValueNodeRefs(node) {
13112
13695
  }
13113
13696
  return refs;
13114
13697
  }
13115
- function hasPositionClause(path13) {
13116
- return path13.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13698
+ function hasPositionClause(path15) {
13699
+ return path15.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
13117
13700
  }
13118
- function stripPositionClauses2(path13) {
13701
+ function stripPositionClauses2(path15) {
13119
13702
  return sanitizeElementPath({
13120
- context: path13.context,
13121
- nodes: path13.nodes.map((node) => ({
13703
+ context: path15.context,
13704
+ nodes: path15.nodes.map((node) => ({
13122
13705
  ...node,
13123
13706
  match: node.match.filter((clause) => clause.kind !== "position")
13124
13707
  }))
@@ -13528,8 +14111,8 @@ function normalizeNonEmptyString2(name, value) {
13528
14111
  function normalizeKey(value) {
13529
14112
  return String(value ?? "").trim();
13530
14113
  }
13531
- function labelForPath(path13) {
13532
- return path13.trim().length === 0 ? "$" : path13;
14114
+ function labelForPath(path15) {
14115
+ return path15.trim().length === 0 ? "$" : path15;
13533
14116
  }
13534
14117
  function sha256Hex3(value) {
13535
14118
  return crypto.createHash("sha256").update(value).digest("hex");
@@ -13558,7 +14141,7 @@ var CHROME_SINGLETON_ARTIFACTS = [
13558
14141
  async function clearChromeSingletonEntries(userDataDir) {
13559
14142
  await Promise.all(
13560
14143
  CHROME_SINGLETON_ARTIFACTS.map(
13561
- (entry) => promises.rm(path6.join(userDataDir, entry), {
14144
+ (entry) => promises.rm(path7.join(userDataDir, entry), {
13562
14145
  recursive: true,
13563
14146
  force: true
13564
14147
  }).catch(() => void 0)
@@ -13573,7 +14156,7 @@ async function sanitizeChromeProfile(userDataDir) {
13573
14156
  await Promise.all(profileDirs.map((dir) => sanitizeProfilePreferences(userDataDir, dir)));
13574
14157
  }
13575
14158
  async function sanitizeProfilePreferences(userDataDir, profileDir) {
13576
- const prefsPath = path6.join(userDataDir, profileDir, "Preferences");
14159
+ const prefsPath = path7.join(userDataDir, profileDir, "Preferences");
13577
14160
  try {
13578
14161
  const raw = await promises.readFile(prefsPath, "utf8");
13579
14162
  const prefs = JSON.parse(raw);
@@ -13585,14 +14168,13 @@ async function sanitizeProfilePreferences(userDataDir, profileDir) {
13585
14168
  profile.exited_cleanly = true;
13586
14169
  prefs.profile = profile;
13587
14170
  await promises.writeFile(prefsPath, JSON.stringify(prefs), "utf8");
13588
- await promises.rm(path6.join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
14171
+ await promises.rm(path7.join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
13589
14172
  () => void 0
13590
14173
  );
13591
14174
  } catch {
13592
14175
  }
13593
14176
  }
13594
- var PROCESS_LIST_MAX_BUFFER_BYTES2 = 16 * 1024 * 1024;
13595
- var PS_COMMAND_ENV2 = { ...process.env, LC_ALL: "C" };
14177
+ ({ ...process.env});
13596
14178
  var WINDOWS_PROGRAM_FILES = process.env.PROGRAMFILES ?? "C:\\Program Files";
13597
14179
  var WINDOWS_PROGRAM_FILES_X86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
13598
14180
  var BROWSER_BRANDS = [
@@ -13607,9 +14189,9 @@ var BROWSER_BRANDS = [
13607
14189
  },
13608
14190
  win32: {
13609
14191
  executableCandidates: [
13610
- path6.join(WINDOWS_PROGRAM_FILES, "Google", "Chrome", "Application", "chrome.exe"),
13611
- path6.join(WINDOWS_PROGRAM_FILES_X86, "Google", "Chrome", "Application", "chrome.exe"),
13612
- path6.join("~", "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe")
14192
+ path7.join(WINDOWS_PROGRAM_FILES, "Google", "Chrome", "Application", "chrome.exe"),
14193
+ path7.join(WINDOWS_PROGRAM_FILES_X86, "Google", "Chrome", "Application", "chrome.exe"),
14194
+ path7.join("~", "AppData", "Local", "Google", "Chrome", "Application", "chrome.exe")
13613
14195
  ],
13614
14196
  userDataDir: "~/AppData/Local/Google/Chrome/User Data",
13615
14197
  processNames: ["/google/chrome/application/chrome.exe"]
@@ -13639,7 +14221,7 @@ var BROWSER_BRANDS = [
13639
14221
  },
13640
14222
  win32: {
13641
14223
  executableCandidates: [
13642
- path6.join("~", "AppData", "Local", "Google", "Chrome SxS", "Application", "chrome.exe")
14224
+ path7.join("~", "AppData", "Local", "Google", "Chrome SxS", "Application", "chrome.exe")
13643
14225
  ],
13644
14226
  userDataDir: "~/AppData/Local/Google/Chrome SxS/User Data",
13645
14227
  processNames: ["/google/chrome sxs/application/chrome.exe"]
@@ -13656,9 +14238,9 @@ var BROWSER_BRANDS = [
13656
14238
  },
13657
14239
  win32: {
13658
14240
  executableCandidates: [
13659
- path6.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
13660
- path6.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
13661
- path6.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
14241
+ path7.join(WINDOWS_PROGRAM_FILES, "Chromium", "Application", "chrome.exe"),
14242
+ path7.join(WINDOWS_PROGRAM_FILES_X86, "Chromium", "Application", "chrome.exe"),
14243
+ path7.join("~", "AppData", "Local", "Chromium", "Application", "chrome.exe")
13662
14244
  ],
13663
14245
  userDataDir: "~/AppData/Local/Chromium/User Data",
13664
14246
  processNames: ["/chromium/application/chrome.exe"]
@@ -13685,15 +14267,15 @@ var BROWSER_BRANDS = [
13685
14267
  },
13686
14268
  win32: {
13687
14269
  executableCandidates: [
13688
- path6.join(WINDOWS_PROGRAM_FILES, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
13689
- path6.join(
14270
+ path7.join(WINDOWS_PROGRAM_FILES, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
14271
+ path7.join(
13690
14272
  WINDOWS_PROGRAM_FILES_X86,
13691
14273
  "BraveSoftware",
13692
14274
  "Brave-Browser",
13693
14275
  "Application",
13694
14276
  "brave.exe"
13695
14277
  ),
13696
- path6.join("~", "AppData", "Local", "BraveSoftware", "Brave-Browser", "Application", "brave.exe")
14278
+ path7.join("~", "AppData", "Local", "BraveSoftware", "Brave-Browser", "Application", "brave.exe")
13697
14279
  ],
13698
14280
  userDataDir: "~/AppData/Local/BraveSoftware/Brave-Browser/User Data",
13699
14281
  processNames: ["/bravesoftware/brave-browser/application/brave.exe"]
@@ -13719,9 +14301,9 @@ var BROWSER_BRANDS = [
13719
14301
  },
13720
14302
  win32: {
13721
14303
  executableCandidates: [
13722
- path6.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
13723
- path6.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
13724
- path6.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
14304
+ path7.join(WINDOWS_PROGRAM_FILES, "Microsoft", "Edge", "Application", "msedge.exe"),
14305
+ path7.join(WINDOWS_PROGRAM_FILES_X86, "Microsoft", "Edge", "Application", "msedge.exe"),
14306
+ path7.join("~", "AppData", "Local", "Microsoft", "Edge", "Application", "msedge.exe")
13725
14307
  ],
13726
14308
  userDataDir: "~/AppData/Local/Microsoft/Edge/User Data",
13727
14309
  processNames: ["/microsoft/edge/application/msedge.exe"]
@@ -13749,9 +14331,9 @@ var BROWSER_BRANDS = [
13749
14331
  },
13750
14332
  win32: {
13751
14333
  executableCandidates: [
13752
- path6.join(WINDOWS_PROGRAM_FILES, "Vivaldi", "Application", "vivaldi.exe"),
13753
- path6.join(WINDOWS_PROGRAM_FILES_X86, "Vivaldi", "Application", "vivaldi.exe"),
13754
- path6.join("~", "AppData", "Local", "Vivaldi", "Application", "vivaldi.exe")
14334
+ path7.join(WINDOWS_PROGRAM_FILES, "Vivaldi", "Application", "vivaldi.exe"),
14335
+ path7.join(WINDOWS_PROGRAM_FILES_X86, "Vivaldi", "Application", "vivaldi.exe"),
14336
+ path7.join("~", "AppData", "Local", "Vivaldi", "Application", "vivaldi.exe")
13755
14337
  ],
13756
14338
  userDataDir: "~/AppData/Local/Vivaldi/User Data",
13757
14339
  processNames: ["/vivaldi/application/vivaldi.exe"]
@@ -13778,9 +14360,6 @@ var BROWSER_BRANDS = [
13778
14360
  }
13779
14361
  }
13780
14362
  ];
13781
- function getAllBrowserBrands() {
13782
- return BROWSER_BRANDS;
13783
- }
13784
14363
  function getBrowserBrand(id) {
13785
14364
  const brand2 = BROWSER_BRANDS.find((candidate) => candidate.id === id);
13786
14365
  if (!brand2) {
@@ -13818,177 +14397,35 @@ function detectInstalledBrowserBrands() {
13818
14397
  brandId: brand2.id,
13819
14398
  displayName: brand2.displayName,
13820
14399
  executablePath,
13821
- userDataDir: path6.resolve(expandHome(platformConfig.userDataDir))
14400
+ userDataDir: path7.resolve(expandHome(platformConfig.userDataDir))
13822
14401
  });
13823
14402
  }
13824
14403
  return installations;
13825
14404
  }
13826
- function resolveBrandExecutablePath(brand2, explicitPath) {
13827
- if (explicitPath !== void 0) {
13828
- const resolvedPath2 = path6.resolve(expandHome(explicitPath));
13829
- if (!fs.existsSync(resolvedPath2)) {
13830
- throw new Error(`${brand2.displayName} executable was not found at "${resolvedPath2}".`);
13831
- }
13832
- return resolvedPath2;
13833
- }
13834
- const platformConfig = resolveBrandPlatformConfig(brand2);
13835
- if (!platformConfig) {
13836
- throw new Error(`${brand2.displayName} is not supported on ${process.platform}.`);
13837
- }
13838
- const resolvedPath = firstExistingPath(
13839
- resolveExecutableCandidates(platformConfig.executableCandidates)
13840
- );
13841
- if (!resolvedPath) {
13842
- throw new Error(
13843
- `Could not find a ${brand2.displayName} executable. Pass --executable-path or browser.executablePath.`
13844
- );
13845
- }
13846
- return resolvedPath;
13847
- }
13848
14405
  function resolveBrandUserDataDir(brand2, explicitDir) {
13849
14406
  if (explicitDir !== void 0) {
13850
- return path6.resolve(expandHome(explicitDir));
14407
+ return path7.resolve(expandHome(explicitDir));
13851
14408
  }
13852
14409
  const platformConfig = resolveBrandPlatformConfig(brand2);
13853
14410
  if (!platformConfig) {
13854
14411
  throw new Error(`${brand2.displayName} is not supported on ${process.platform}.`);
13855
14412
  }
13856
- return path6.resolve(expandHome(platformConfig.userDataDir));
13857
- }
13858
- function isBrandProcess(brand2, commandLine) {
13859
- const normalizedCommand = normalizeCommand(commandLine);
13860
- if (!normalizedCommand) {
13861
- return false;
13862
- }
13863
- if (normalizedCommand.includes("crashpad_handler")) {
13864
- return false;
13865
- }
13866
- if (/\s--type=/.test(normalizedCommand)) {
13867
- return false;
13868
- }
13869
- return getBrandProcessMarkers(brand2).some((marker) => normalizedCommand.includes(marker));
13870
- }
13871
- function findBrandProcess(brand2) {
13872
- for (const processEntry of listProcesses()) {
13873
- if (isBrandProcess(brand2, processEntry.commandLine)) {
13874
- return { pid: processEntry.pid };
13875
- }
13876
- }
13877
- return null;
13878
- }
13879
- function getBrandProcessMarkers(brand2) {
13880
- const markers = /* @__PURE__ */ new Set();
13881
- for (const config of [brand2.darwin, brand2.win32, brand2.linux]) {
13882
- if (!config) {
13883
- continue;
13884
- }
13885
- for (const processName of config.processNames) {
13886
- const normalized = normalizeCommand(processName);
13887
- if (normalized) {
13888
- markers.add(normalized);
13889
- }
13890
- }
13891
- for (const candidate of config.executableCandidates) {
13892
- if (!candidate) {
13893
- continue;
13894
- }
13895
- const normalized = normalizeCommand(path6.resolve(expandHome(candidate)));
13896
- if (normalized) {
13897
- markers.add(normalized);
13898
- }
13899
- }
13900
- }
13901
- return [...markers];
14413
+ return path7.resolve(expandHome(platformConfig.userDataDir));
13902
14414
  }
13903
14415
  function resolveExecutableCandidates(candidates) {
13904
- return candidates.map((candidate) => candidate ? path6.resolve(expandHome(candidate)) : null);
13905
- }
13906
- function listProcesses() {
13907
- if (process.platform === "win32") {
13908
- return listWindowsProcesses();
13909
- }
13910
- return listUnixProcesses();
13911
- }
13912
- function listUnixProcesses() {
13913
- try {
13914
- const output = child_process.execFileSync("ps", ["-A", "-o", "pid=,command="], {
13915
- encoding: "utf8",
13916
- env: PS_COMMAND_ENV2,
13917
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES2,
13918
- stdio: ["ignore", "pipe", "ignore"]
13919
- });
13920
- return output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
13921
- const match = /^(\d+)\s+(.*)$/.exec(line);
13922
- if (!match) {
13923
- return null;
13924
- }
13925
- const pid = Number.parseInt(match[1] ?? "", 10);
13926
- const commandLine = match[2]?.trim() ?? "";
13927
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13928
- return null;
13929
- }
13930
- return {
13931
- pid,
13932
- commandLine
13933
- };
13934
- }).filter(
13935
- (entry) => entry !== null
13936
- ).sort((left, right) => left.pid - right.pid);
13937
- } catch {
13938
- return [];
13939
- }
13940
- }
13941
- function listWindowsProcesses() {
13942
- try {
13943
- const output = child_process.execFileSync(
13944
- "powershell.exe",
13945
- [
13946
- "-NoProfile",
13947
- "-Command",
13948
- "Get-CimInstance Win32_Process | Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress"
13949
- ],
13950
- {
13951
- encoding: "utf8",
13952
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES2,
13953
- stdio: ["ignore", "pipe", "ignore"]
13954
- }
13955
- ).trim();
13956
- if (!output) {
13957
- return [];
13958
- }
13959
- const parsed = JSON.parse(output);
13960
- const records = Array.isArray(parsed) ? parsed : [parsed];
13961
- return records.map((record) => {
13962
- const pid = Number(record.ProcessId);
13963
- const commandLine = typeof record.CommandLine === "string" ? record.CommandLine.trim() : "";
13964
- if (!Number.isInteger(pid) || pid <= 0 || commandLine.length === 0) {
13965
- return null;
13966
- }
13967
- return {
13968
- pid,
13969
- commandLine
13970
- };
13971
- }).filter(
13972
- (entry) => entry !== null
13973
- ).sort((left, right) => left.pid - right.pid);
13974
- } catch {
13975
- return [];
13976
- }
13977
- }
13978
- function normalizeCommand(value) {
13979
- return value.trim().replaceAll("\\", "/").toLowerCase();
14416
+ return candidates.map((candidate) => candidate ? path7.resolve(expandHome(candidate)) : null);
13980
14417
  }
13981
14418
 
13982
14419
  // src/local-browser/chrome-discovery.ts
13983
14420
  function expandHome(value) {
13984
14421
  if (value === "~" || value.startsWith("~/")) {
13985
- return path6.join(os.homedir(), value.slice(1));
14422
+ return path7.join(os.homedir(), value.slice(1));
13986
14423
  }
13987
14424
  return value;
13988
14425
  }
13989
14426
  function resolveChromeUserDataDir(userDataDir) {
13990
14427
  const installation = detectLocalChromeInstallations().find(
13991
- (candidate) => fs.existsSync(path6.join(candidate.userDataDir, "Local State")) || candidate.executablePath !== null
14428
+ (candidate) => fs.existsSync(path7.join(candidate.userDataDir, "Local State")) || candidate.executablePath !== null
13992
14429
  );
13993
14430
  if (!installation) {
13994
14431
  throw new Error("Could not find a local Chrome or Chromium profile directory.");
@@ -13997,7 +14434,7 @@ function resolveChromeUserDataDir(userDataDir) {
13997
14434
  }
13998
14435
  function resolveChromeExecutablePath(executablePath) {
13999
14436
  if (executablePath !== void 0) {
14000
- const resolvedPath = path6.resolve(expandHome(executablePath));
14437
+ const resolvedPath = path7.resolve(expandHome(executablePath));
14001
14438
  if (!fs.existsSync(resolvedPath)) {
14002
14439
  throw new Error(`Chrome executable was not found at "${resolvedPath}".`);
14003
14440
  }
@@ -14021,37 +14458,37 @@ function detectLocalChromeInstallations() {
14021
14458
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
14022
14459
  "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
14023
14460
  ]),
14024
- userDataDir: path6.join(os.homedir(), "Library", "Application Support", "Google", "Chrome")
14461
+ userDataDir: path7.join(os.homedir(), "Library", "Application Support", "Google", "Chrome")
14025
14462
  },
14026
14463
  {
14027
14464
  brand: "chromium",
14028
14465
  executablePath: firstExistingPath(["/Applications/Chromium.app/Contents/MacOS/Chromium"]),
14029
- userDataDir: path6.join(os.homedir(), "Library", "Application Support", "Chromium")
14466
+ userDataDir: path7.join(os.homedir(), "Library", "Application Support", "Chromium")
14030
14467
  }
14031
14468
  ];
14032
14469
  }
14033
14470
  if (process.platform === "win32") {
14034
14471
  const programFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
14035
14472
  const programFilesX86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
14036
- const localAppData = process.env.LOCALAPPDATA ?? path6.join(os.homedir(), "AppData", "Local");
14473
+ const localAppData = process.env.LOCALAPPDATA ?? path7.join(os.homedir(), "AppData", "Local");
14037
14474
  return [
14038
14475
  {
14039
14476
  brand: "chrome",
14040
14477
  executablePath: firstExistingPath([
14041
- path6.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"),
14042
- path6.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"),
14043
- path6.join(localAppData, "Google", "Chrome", "Application", "chrome.exe")
14478
+ path7.join(programFiles, "Google", "Chrome", "Application", "chrome.exe"),
14479
+ path7.join(programFilesX86, "Google", "Chrome", "Application", "chrome.exe"),
14480
+ path7.join(localAppData, "Google", "Chrome", "Application", "chrome.exe")
14044
14481
  ]),
14045
- userDataDir: path6.join(localAppData, "Google", "Chrome", "User Data")
14482
+ userDataDir: path7.join(localAppData, "Google", "Chrome", "User Data")
14046
14483
  },
14047
14484
  {
14048
14485
  brand: "chromium",
14049
14486
  executablePath: firstExistingPath([
14050
- path6.join(programFiles, "Chromium", "Application", "chrome.exe"),
14051
- path6.join(programFilesX86, "Chromium", "Application", "chrome.exe"),
14052
- path6.join(localAppData, "Chromium", "Application", "chrome.exe")
14487
+ path7.join(programFiles, "Chromium", "Application", "chrome.exe"),
14488
+ path7.join(programFilesX86, "Chromium", "Application", "chrome.exe"),
14489
+ path7.join(localAppData, "Chromium", "Application", "chrome.exe")
14053
14490
  ]),
14054
- userDataDir: path6.join(localAppData, "Chromium", "User Data")
14491
+ userDataDir: path7.join(localAppData, "Chromium", "User Data")
14055
14492
  }
14056
14493
  ];
14057
14494
  }
@@ -14064,7 +14501,7 @@ function detectLocalChromeInstallations() {
14064
14501
  resolveBinaryFromPath("google-chrome"),
14065
14502
  resolveBinaryFromPath("google-chrome-stable")
14066
14503
  ]),
14067
- userDataDir: path6.join(os.homedir(), ".config", "google-chrome")
14504
+ userDataDir: path7.join(os.homedir(), ".config", "google-chrome")
14068
14505
  },
14069
14506
  {
14070
14507
  brand: "chromium",
@@ -14074,7 +14511,7 @@ function detectLocalChromeInstallations() {
14074
14511
  resolveBinaryFromPath("chromium"),
14075
14512
  resolveBinaryFromPath("chromium-browser")
14076
14513
  ]),
14077
- userDataDir: path6.join(os.homedir(), ".config", "chromium")
14514
+ userDataDir: path7.join(os.homedir(), ".config", "chromium")
14078
14515
  }
14079
14516
  ];
14080
14517
  }
@@ -14086,8 +14523,8 @@ function detectLocalBrowserInstallations() {
14086
14523
  }));
14087
14524
  }
14088
14525
  function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14089
- const resolvedUserDataDir = path6.resolve(expandHome(userDataDir));
14090
- const localStatePath = path6.join(resolvedUserDataDir, "Local State");
14526
+ const resolvedUserDataDir = path7.resolve(expandHome(userDataDir));
14527
+ const localStatePath = path7.join(resolvedUserDataDir, "Local State");
14091
14528
  if (!fs.existsSync(localStatePath)) {
14092
14529
  return [];
14093
14530
  }
@@ -14099,7 +14536,7 @@ function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14099
14536
  }
14100
14537
  return Object.entries(infoCache).map(([directory, info]) => {
14101
14538
  const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
14102
- const name = typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : directory || path6.basename(directory);
14539
+ const name = typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : directory || path7.basename(directory);
14103
14540
  return {
14104
14541
  directory,
14105
14542
  name,
@@ -14111,7 +14548,7 @@ function listLocalChromeProfiles(userDataDir = resolveChromeUserDataDir()) {
14111
14548
  }
14112
14549
  }
14113
14550
  function readDevToolsActivePort(userDataDir) {
14114
- const devToolsPath = path6.join(userDataDir, "DevToolsActivePort");
14551
+ const devToolsPath = path7.join(userDataDir, "DevToolsActivePort");
14115
14552
  if (!fs.existsSync(devToolsPath)) {
14116
14553
  return null;
14117
14554
  }
@@ -14344,8 +14781,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
14344
14781
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
14345
14782
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
14346
14783
  }
14347
- function normalizeWebSocketPath(path13) {
14348
- return path13.startsWith("/") ? path13 : `/${path13}`;
14784
+ function normalizeWebSocketPath(path15) {
14785
+ return path15.startsWith("/") ? path15 : `/${path15}`;
14349
14786
  }
14350
14787
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
14351
14788
  try {
@@ -14391,20 +14828,20 @@ var SESSION_SKIPPED_PROFILE_DIRECTORIES = /* @__PURE__ */ new Set([
14391
14828
  "Network"
14392
14829
  ]);
14393
14830
  async function createBrowserProfileSnapshot(input) {
14394
- const sourceUserDataDir = path6.resolve(expandHome(input.sourceUserDataDir));
14395
- const targetUserDataDir = path6.resolve(expandHome(input.targetUserDataDir));
14831
+ const sourceUserDataDir = path7.resolve(expandHome(input.sourceUserDataDir));
14832
+ const targetUserDataDir = path7.resolve(expandHome(input.targetUserDataDir));
14396
14833
  const profileDirectory = input.profileDirectory?.trim();
14397
14834
  const copyMode = input.copyMode;
14398
14835
  await promises.mkdir(targetUserDataDir, { recursive: true });
14399
14836
  await clearChromeSingletonEntries(targetUserDataDir);
14400
14837
  if (profileDirectory) {
14401
- const sourceProfileDir = path6.join(sourceUserDataDir, profileDirectory);
14838
+ const sourceProfileDir = path7.join(sourceUserDataDir, profileDirectory);
14402
14839
  if (!fs.existsSync(sourceProfileDir)) {
14403
14840
  throw new Error(
14404
14841
  `Chrome profile "${profileDirectory}" was not found in "${sourceUserDataDir}".`
14405
14842
  );
14406
14843
  }
14407
- await promises.cp(sourceProfileDir, path6.join(targetUserDataDir, profileDirectory), {
14844
+ await promises.cp(sourceProfileDir, path7.join(targetUserDataDir, profileDirectory), {
14408
14845
  recursive: true,
14409
14846
  filter: (candidate) => shouldCopyEntry({
14410
14847
  candidatePath: candidate,
@@ -14428,8 +14865,8 @@ async function copyRootLevelEntries(input) {
14428
14865
  if (CHROME_SINGLETON_ENTRIES.has(entry) || entry === input.selectedProfileDirectory) {
14429
14866
  continue;
14430
14867
  }
14431
- const sourcePath = path6.join(input.sourceUserDataDir, entry);
14432
- const targetPath = path6.join(input.targetUserDataDir, entry);
14868
+ const sourcePath = path7.join(input.sourceUserDataDir, entry);
14869
+ const targetPath = path7.join(input.targetUserDataDir, entry);
14433
14870
  const entryStat = await promises.stat(sourcePath).catch(() => null);
14434
14871
  if (!entryStat) {
14435
14872
  continue;
@@ -14461,7 +14898,7 @@ async function copyRootLevelEntries(input) {
14461
14898
  }
14462
14899
  }
14463
14900
  function isProfileDirectory(userDataDir, entry) {
14464
- return fs.existsSync(path6.join(userDataDir, entry, "Preferences"));
14901
+ return fs.existsSync(path7.join(userDataDir, entry, "Preferences"));
14465
14902
  }
14466
14903
  function shouldCopyEntry(input) {
14467
14904
  const entryName = input.candidatePath.split("/").at(-1)?.split("\\").at(-1) ?? input.candidatePath;
@@ -14471,7 +14908,7 @@ function shouldCopyEntry(input) {
14471
14908
  if (input.copyMode !== "session") {
14472
14909
  return true;
14473
14910
  }
14474
- const relativePath = path6.relative(input.rootPath, input.candidatePath);
14911
+ const relativePath = path7.relative(input.rootPath, input.candidatePath);
14475
14912
  if (relativePath.length === 0) {
14476
14913
  return true;
14477
14914
  }
@@ -14953,7 +15390,7 @@ function pickStealthProfilePreset(overrides) {
14953
15390
  var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
14954
15391
  var OPENSTEER_LIVE_SESSION_VERSION = 1;
14955
15392
  function resolveLiveSessionRecordPath(rootPath, provider) {
14956
- return path6__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
15393
+ return path7__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
14957
15394
  }
14958
15395
  function resolveLocalSessionRecordPath(rootPath) {
14959
15396
  return resolveLiveSessionRecordPath(rootPath, "local");
@@ -14990,7 +15427,7 @@ async function clearPersistedSessionRecord(rootPath, provider) {
14990
15427
  await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
14991
15428
  }
14992
15429
  function isPersistedCloudSessionRecord(value) {
14993
- 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.baseUrl === "string" && value.baseUrl.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
15430
+ 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);
14994
15431
  }
14995
15432
  function isPersistedLocalBrowserSessionRecord(value) {
14996
15433
  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;
@@ -15113,8 +15550,8 @@ var OpensteerBrowserManager = class {
15113
15550
  ...options.browser === void 0 ? {} : { browser: options.browser },
15114
15551
  ...this.contextOptions === void 0 ? {} : { context: this.contextOptions }
15115
15552
  });
15116
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6__default.default.join(os.tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
15117
- rootDir: path6__default.default.resolve(options.rootDir ?? process.cwd()),
15553
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
15554
+ rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
15118
15555
  workspace: this.workspace
15119
15556
  }));
15120
15557
  this.cleanupRootOnDisconnect = this.workspace === void 0;
@@ -15184,7 +15621,7 @@ var OpensteerBrowserManager = class {
15184
15621
  userDataDir: "browser/user-data",
15185
15622
  bootstrap: {
15186
15623
  kind: "cloneLocalProfile",
15187
- sourceUserDataDir: path6__default.default.resolve(input.sourceUserDataDir),
15624
+ sourceUserDataDir: path7__default.default.resolve(input.sourceUserDataDir),
15188
15625
  ...input.sourceProfileDirectory === void 0 ? {} : { sourceProfileDirectory: input.sourceProfileDirectory }
15189
15626
  }
15190
15627
  };
@@ -15311,7 +15748,7 @@ var OpensteerBrowserManager = class {
15311
15748
  });
15312
15749
  }
15313
15750
  async createTemporaryEngine() {
15314
- const userDataDir = await promises.mkdtemp(path6__default.default.join(os.tmpdir(), "opensteer-temporary-browser-"));
15751
+ const userDataDir = await promises.mkdtemp(path7__default.default.join(os.tmpdir(), "opensteer-temporary-browser-"));
15315
15752
  await clearChromeSingletonEntries(userDataDir);
15316
15753
  const launched = await launchOwnedBrowser({
15317
15754
  userDataDir,
@@ -15758,7 +16195,7 @@ async function terminateProcess(pid) {
15758
16195
  }
15759
16196
  }
15760
16197
  async function requestBrowserClose(endpoint) {
15761
- await new Promise((resolve5, reject) => {
16198
+ await new Promise((resolve4, reject) => {
15762
16199
  const socket = new WebSocket(endpoint);
15763
16200
  const timeout = setTimeout(() => {
15764
16201
  socket.close();
@@ -15775,7 +16212,7 @@ async function requestBrowserClose(endpoint) {
15775
16212
  reject(error);
15776
16213
  return;
15777
16214
  }
15778
- resolve5();
16215
+ resolve4();
15779
16216
  };
15780
16217
  socket.addEventListener("open", () => {
15781
16218
  socket.send(JSON.stringify({ id: 1, method: "Browser.close" }));
@@ -15813,7 +16250,7 @@ async function waitForProcessExit(pid, timeoutMs) {
15813
16250
  return !isProcessRunning(pid);
15814
16251
  }
15815
16252
  function resolveAbpSessionDir(workspace) {
15816
- return path6__default.default.join(workspace.livePath, "abp-session");
16253
+ return path7__default.default.join(workspace.livePath, "abp-session");
15817
16254
  }
15818
16255
  async function allocateEphemeralPort() {
15819
16256
  const { allocatePort } = await loadAbpModule();
@@ -15871,7 +16308,103 @@ function isStealthProfile(input) {
15871
16308
  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;
15872
16309
  }
15873
16310
  async function sleep(ms) {
15874
- await new Promise((resolve5) => setTimeout(resolve5, ms));
16311
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
16312
+ }
16313
+ var ENV_FILENAMES = [".env", ".env.local"];
16314
+ var OPENSTEER_ENV_PREFIX = "OPENSTEER_";
16315
+ var opensteerEnvironmentCache = /* @__PURE__ */ new Map();
16316
+ function resolveOpensteerEnvironment(cwd = process.cwd(), baseEnv = process.env) {
16317
+ const resolvedCwd = path7__default.default.resolve(cwd);
16318
+ const signature = buildEnvironmentSignature(baseEnv, isOpensteerEnvironmentKey);
16319
+ const cached = opensteerEnvironmentCache.get(resolvedCwd);
16320
+ if (cached && cached.signature === signature) {
16321
+ return { ...cached.values };
16322
+ }
16323
+ const resolved = resolveEnvironmentFiles(resolvedCwd, baseEnv, isOpensteerEnvironmentKey);
16324
+ opensteerEnvironmentCache.set(resolvedCwd, {
16325
+ signature,
16326
+ values: { ...resolved }
16327
+ });
16328
+ return { ...resolved };
16329
+ }
16330
+ function collectDirectories(cwd) {
16331
+ const directories = [];
16332
+ let current = path7__default.default.resolve(cwd);
16333
+ for (; ; ) {
16334
+ directories.unshift(current);
16335
+ const parent = path7__default.default.dirname(current);
16336
+ if (parent === current) {
16337
+ return directories;
16338
+ }
16339
+ current = parent;
16340
+ }
16341
+ }
16342
+ function parseEnvFile(contents) {
16343
+ const parsed = {};
16344
+ for (const rawLine of contents.split(/\r?\n/u)) {
16345
+ const trimmed = rawLine.trim();
16346
+ if (!trimmed || trimmed.startsWith("#")) {
16347
+ continue;
16348
+ }
16349
+ const line = trimmed.startsWith("export ") ? trimmed.slice("export ".length) : trimmed;
16350
+ const separatorIndex = line.indexOf("=");
16351
+ if (separatorIndex <= 0) {
16352
+ continue;
16353
+ }
16354
+ const key = line.slice(0, separatorIndex).trim();
16355
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) {
16356
+ continue;
16357
+ }
16358
+ const rawValue = line.slice(separatorIndex + 1).trim();
16359
+ parsed[key] = parseEnvValue(rawValue);
16360
+ }
16361
+ return parsed;
16362
+ }
16363
+ function parseEnvValue(rawValue) {
16364
+ if (rawValue.length >= 2 && rawValue.startsWith('"') && rawValue.endsWith('"')) {
16365
+ return rawValue.slice(1, -1).replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"');
16366
+ }
16367
+ if (rawValue.length >= 2 && rawValue.startsWith("'") && rawValue.endsWith("'")) {
16368
+ return rawValue.slice(1, -1);
16369
+ }
16370
+ return rawValue.replace(/\s+#.*$/u, "").trimEnd();
16371
+ }
16372
+ function resolveEnvironmentFiles(cwd, baseEnv, predicate) {
16373
+ const resolved = collectEnvironment(baseEnv, predicate);
16374
+ const protectedKeys = new Set(Object.keys(resolved));
16375
+ const directories = collectDirectories(cwd);
16376
+ for (const directory of directories) {
16377
+ for (const filename of ENV_FILENAMES) {
16378
+ const filePath = path7__default.default.join(directory, filename);
16379
+ if (!fs.existsSync(filePath)) {
16380
+ continue;
16381
+ }
16382
+ const parsed = parseEnvFile(fs.readFileSync(filePath, "utf8"));
16383
+ for (const [key, value] of Object.entries(parsed)) {
16384
+ if (predicate && !predicate(key) || protectedKeys.has(key)) {
16385
+ continue;
16386
+ }
16387
+ resolved[key] = value;
16388
+ }
16389
+ }
16390
+ }
16391
+ return resolved;
16392
+ }
16393
+ function collectEnvironment(baseEnv, predicate) {
16394
+ const resolved = {};
16395
+ for (const [key, value] of Object.entries(baseEnv)) {
16396
+ if (predicate && !predicate(key) || value === void 0) {
16397
+ continue;
16398
+ }
16399
+ resolved[key] = value;
16400
+ }
16401
+ return resolved;
16402
+ }
16403
+ function buildEnvironmentSignature(baseEnv, predicate) {
16404
+ return Object.entries(baseEnv).filter(([key, value]) => predicate(key) && value !== void 0).sort(([leftKey], [rightKey]) => leftKey.localeCompare(rightKey)).map(([key, value]) => `${key}=${value}`).join("\n");
16405
+ }
16406
+ function isOpensteerEnvironmentKey(key) {
16407
+ return key.startsWith(OPENSTEER_ENV_PREFIX);
15875
16408
  }
15876
16409
 
15877
16410
  // ../runtime-core/package.json
@@ -17485,7 +18018,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
17485
18018
  diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
17486
18019
  }
17487
18020
  }
17488
- function diffScalarField(path13, left, right, includeUnchanged, output) {
18021
+ function diffScalarField(path15, left, right, includeUnchanged, output) {
17489
18022
  const leftValue = stringifyFieldValue(left);
17490
18023
  const rightValue = stringifyFieldValue(right);
17491
18024
  const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
@@ -17493,7 +18026,7 @@ function diffScalarField(path13, left, right, includeUnchanged, output) {
17493
18026
  return;
17494
18027
  }
17495
18028
  output.push({
17496
- path: path13,
18029
+ path: path15,
17497
18030
  kind,
17498
18031
  ...leftValue === void 0 ? {} : { leftValue },
17499
18032
  ...rightValue === void 0 ? {} : { rightValue },
@@ -18499,9 +19032,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
18499
19032
  matches.add(`host:${host}`);
18500
19033
  }
18501
19034
  }
18502
- for (const path13 of targetHints.paths ?? []) {
18503
- if (url.pathname.includes(path13)) {
18504
- matches.add(`path:${path13}`);
19035
+ for (const path15 of targetHints.paths ?? []) {
19036
+ if (url.pathname.includes(path15)) {
19037
+ matches.add(`path:${path15}`);
18505
19038
  }
18506
19039
  }
18507
19040
  for (const operationName of targetHints.operationNames ?? []) {
@@ -19462,11 +19995,11 @@ function inferClusterRelationship(seed, record) {
19462
19995
  var MATCHED_TLS_BINARY_NAMES = ["curl-impersonate-chrome", "curl_chrome"];
19463
19996
  async function executeMatchedTlsTransportRequest(input) {
19464
19997
  const binary = await resolveMatchedTlsBinary();
19465
- const workingDirectory = await promises.mkdtemp(path6__default.default.join(os.tmpdir(), "opensteer-matched-tls-"));
19466
- const headersPath = path6__default.default.join(workingDirectory, "headers.txt");
19467
- const bodyPath = path6__default.default.join(workingDirectory, "body.bin");
19468
- const cookiesPath = path6__default.default.join(workingDirectory, "cookies.txt");
19469
- const requestBodyPath = path6__default.default.join(workingDirectory, "request-body.bin");
19998
+ const workingDirectory = await promises.mkdtemp(path7__default.default.join(os.tmpdir(), "opensteer-matched-tls-"));
19999
+ const headersPath = path7__default.default.join(workingDirectory, "headers.txt");
20000
+ const bodyPath = path7__default.default.join(workingDirectory, "body.bin");
20001
+ const cookiesPath = path7__default.default.join(workingDirectory, "cookies.txt");
20002
+ const requestBodyPath = path7__default.default.join(workingDirectory, "request-body.bin");
19470
20003
  try {
19471
20004
  await promises.writeFile(cookiesPath, toNetscapeCookieJar(input.cookies ?? []), "utf8");
19472
20005
  if (input.request.body !== void 0) {
@@ -19523,10 +20056,10 @@ async function executeMatchedTlsTransportRequest(input) {
19523
20056
  }
19524
20057
  }
19525
20058
  async function resolveMatchedTlsBinary() {
19526
- const pathEntries = (process.env.PATH ?? "").split(path6__default.default.delimiter).filter((entry) => entry.length > 0);
20059
+ const pathEntries = (process.env.PATH ?? "").split(path7__default.default.delimiter).filter((entry) => entry.length > 0);
19527
20060
  for (const directory of pathEntries) {
19528
20061
  for (const name of MATCHED_TLS_BINARY_NAMES) {
19529
- const candidate = path6__default.default.join(directory, name);
20062
+ const candidate = path7__default.default.join(directory, name);
19530
20063
  if (await isExecutable(candidate)) {
19531
20064
  return candidate;
19532
20065
  }
@@ -19534,7 +20067,7 @@ async function resolveMatchedTlsBinary() {
19534
20067
  const files = await readDirSafe(directory);
19535
20068
  const discovered = files.find((file) => file.startsWith("curl_chrome"));
19536
20069
  if (discovered !== void 0) {
19537
- const candidate = path6__default.default.join(directory, discovered);
20070
+ const candidate = path7__default.default.join(directory, discovered);
19538
20071
  if (await isExecutable(candidate)) {
19539
20072
  return candidate;
19540
20073
  }
@@ -19545,7 +20078,7 @@ async function resolveMatchedTlsBinary() {
19545
20078
  );
19546
20079
  }
19547
20080
  async function spawnAndCollect(command, args, signal) {
19548
- return await new Promise((resolve5, reject) => {
20081
+ return await new Promise((resolve4, reject) => {
19549
20082
  const child = child_process.spawn(command, args, {
19550
20083
  stdio: ["ignore", "pipe", "pipe"]
19551
20084
  });
@@ -19578,7 +20111,7 @@ async function spawnAndCollect(command, args, signal) {
19578
20111
  );
19579
20112
  return;
19580
20113
  }
19581
- resolve5({ stdout, stderr });
20114
+ resolve4({ stdout, stderr });
19582
20115
  });
19583
20116
  });
19584
20117
  }
@@ -22240,8 +22773,8 @@ function readString(value) {
22240
22773
  return typeof value === "string" && value.length > 0 ? value : void 0;
22241
22774
  }
22242
22775
  function sleep2(ms, signal) {
22243
- return new Promise((resolve5, reject) => {
22244
- const timeout = setTimeout(resolve5, ms);
22776
+ return new Promise((resolve4, reject) => {
22777
+ const timeout = setTimeout(resolve4, ms);
22245
22778
  const abort = () => {
22246
22779
  clearTimeout(timeout);
22247
22780
  reject(new Error("captcha solve aborted"));
@@ -22338,8 +22871,8 @@ function readString2(value) {
22338
22871
  return typeof value === "string" && value.length > 0 ? value : void 0;
22339
22872
  }
22340
22873
  function sleep3(ms, signal) {
22341
- return new Promise((resolve5, reject) => {
22342
- const timeout = setTimeout(resolve5, ms);
22874
+ return new Promise((resolve4, reject) => {
22875
+ const timeout = setTimeout(resolve4, ms);
22343
22876
  const abort = () => {
22344
22877
  clearTimeout(timeout);
22345
22878
  reject(new Error("captcha solve aborted"));
@@ -22538,6 +23071,9 @@ function diffInteractionTraces(left, right) {
22538
23071
  // ../runtime-core/src/sdk/runtime.ts
22539
23072
  var requireForAuthRecipeHook = 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)));
22540
23073
  var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
23074
+ var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
23075
+ var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
23076
+ var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
22541
23077
  var OpensteerSessionRuntime = class {
22542
23078
  workspace;
22543
23079
  rootPath;
@@ -22550,6 +23086,9 @@ var OpensteerSessionRuntime = class {
22550
23086
  registryOverrides;
22551
23087
  cleanupRootOnClose;
22552
23088
  sessionInfoBase;
23089
+ observationConfig;
23090
+ observationSessionId;
23091
+ injectedObservationSink;
22553
23092
  root;
22554
23093
  engine;
22555
23094
  dom;
@@ -22559,6 +23098,9 @@ var OpensteerSessionRuntime = class {
22559
23098
  sessionRef;
22560
23099
  pageRef;
22561
23100
  runId;
23101
+ observations;
23102
+ operationEventStorage = new async_hooks.AsyncLocalStorage();
23103
+ pendingOperationEventCaptures = [];
22562
23104
  cookieJars = /* @__PURE__ */ new Map();
22563
23105
  recipeCache = /* @__PURE__ */ new Map();
22564
23106
  ownsEngine = false;
@@ -22566,7 +23108,7 @@ var OpensteerSessionRuntime = class {
22566
23108
  this.workspace = normalizeNamespace2(options.name);
22567
23109
  this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
22568
23110
  this.root = options.workspace;
22569
- this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
23111
+ this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path7__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
22570
23112
  this.injectedEngine = options.engine;
22571
23113
  this.engineFactory = options.engineFactory;
22572
23114
  this.policy = options.policy ?? defaultPolicy();
@@ -22575,6 +23117,9 @@ var OpensteerSessionRuntime = class {
22575
23117
  this.registryOverrides = options.registryOverrides;
22576
23118
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
22577
23119
  this.sessionInfoBase = options.sessionInfo ?? {};
23120
+ this.observationConfig = normalizeObservabilityConfig(options.observability);
23121
+ this.observationSessionId = options.observationSessionId;
23122
+ this.injectedObservationSink = options.observationSink;
22578
23123
  if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
22579
23124
  throw new Error("OpensteerSessionRuntime requires an engine or engineFactory.");
22580
23125
  }
@@ -22606,6 +23151,20 @@ var OpensteerSessionRuntime = class {
22606
23151
  }
22607
23152
  };
22608
23153
  }
23154
+ async setObservabilityConfig(input) {
23155
+ this.observationConfig = normalizeObservabilityConfig(input);
23156
+ const observationSessionId = this.resolveObservationSessionId();
23157
+ if (observationSessionId === void 0) {
23158
+ return this.observationConfig;
23159
+ }
23160
+ const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
23161
+ this.observations = await sink.openSession({
23162
+ sessionId: observationSessionId,
23163
+ openedAt: Date.now(),
23164
+ config: this.observationConfig
23165
+ });
23166
+ return this.observationConfig;
23167
+ }
22609
23168
  async open(input = {}, options = {}) {
22610
23169
  assertValidSemanticOperationInput("session.open", input);
22611
23170
  if (input.workspace !== void 0 && normalizeNamespace2(input.workspace) !== this.workspace) {
@@ -22704,6 +23263,10 @@ var OpensteerSessionRuntime = class {
22704
23263
  return { pages: [] };
22705
23264
  }
22706
23265
  const startedAt = Date.now();
23266
+ const context = buildRuntimeTraceContext({
23267
+ sessionRef: this.sessionRef,
23268
+ pageRef: this.pageRef
23269
+ });
22707
23270
  try {
22708
23271
  const output = await this.runWithOperationTimeout(
22709
23272
  "page.list",
@@ -22718,19 +23281,18 @@ var OpensteerSessionRuntime = class {
22718
23281
  },
22719
23282
  options
22720
23283
  );
23284
+ const events = await this.drainPendingEngineEvents(context);
22721
23285
  await this.appendTrace({
22722
23286
  operation: "page.list",
22723
23287
  startedAt,
22724
23288
  completedAt: Date.now(),
22725
23289
  outcome: "ok",
23290
+ ...events === void 0 ? {} : { events },
22726
23291
  data: {
22727
23292
  count: output.pages.length,
22728
23293
  ...output.activePageRef === void 0 ? {} : { activePageRef: output.activePageRef }
22729
23294
  },
22730
- context: buildRuntimeTraceContext({
22731
- sessionRef: this.sessionRef,
22732
- pageRef: this.pageRef
22733
- })
23295
+ context
22734
23296
  });
22735
23297
  return output;
22736
23298
  } catch (error) {
@@ -22740,10 +23302,7 @@ var OpensteerSessionRuntime = class {
22740
23302
  completedAt: Date.now(),
22741
23303
  outcome: "error",
22742
23304
  error,
22743
- context: buildRuntimeTraceContext({
22744
- sessionRef: this.sessionRef,
22745
- pageRef: this.pageRef
22746
- })
23305
+ context
22747
23306
  });
22748
23307
  throw error;
22749
23308
  }
@@ -23131,22 +23690,25 @@ var OpensteerSessionRuntime = class {
23131
23690
  },
23132
23691
  options
23133
23692
  );
23693
+ const context = buildRuntimeTraceContext({
23694
+ sessionRef: this.sessionRef,
23695
+ pageRef
23696
+ });
23697
+ const events = await this.drainPendingEngineEvents(context);
23134
23698
  await this.appendTrace({
23135
23699
  operation: "page.snapshot",
23136
23700
  startedAt,
23137
23701
  completedAt: Date.now(),
23138
23702
  outcome: "ok",
23139
23703
  artifacts,
23704
+ ...events === void 0 ? {} : { events },
23140
23705
  data: {
23141
23706
  mode,
23142
23707
  url: output.url,
23143
23708
  title: output.title,
23144
23709
  counterCount: output.counters.length
23145
23710
  },
23146
- context: buildRuntimeTraceContext({
23147
- sessionRef: this.sessionRef,
23148
- pageRef
23149
- })
23711
+ context
23150
23712
  });
23151
23713
  return output;
23152
23714
  } catch (error) {
@@ -26571,11 +27133,13 @@ var OpensteerSessionRuntime = class {
26571
27133
  }
26572
27134
  );
26573
27135
  const output = toOpensteerActionResult(executed.result, preparedTarget.persistedDescription);
27136
+ const actionEvents = "events" in executed.result ? executed.result.events : void 0;
26574
27137
  await this.appendTrace({
26575
27138
  operation,
26576
27139
  startedAt,
26577
27140
  completedAt: Date.now(),
26578
27141
  outcome: "ok",
27142
+ ...actionEvents === void 0 ? {} : { events: actionEvents },
26579
27143
  data: {
26580
27144
  target: output.target,
26581
27145
  ...output.point === void 0 ? {} : { point: output.point },
@@ -28325,7 +28889,7 @@ var OpensteerSessionRuntime = class {
28325
28889
  }
28326
28890
  async executeAuthRecipeHook(step, variables) {
28327
28891
  const resolved = requireForAuthRecipeHook.resolve(step.hook.specifier, {
28328
- paths: [path6__default.default.dirname(this.rootPath)]
28892
+ paths: [path7__default.default.dirname(this.rootPath)]
28329
28893
  });
28330
28894
  const module = await import(url.pathToFileURL(resolved).href);
28331
28895
  const handler = module[step.hook.export];
@@ -28647,6 +29211,29 @@ var OpensteerSessionRuntime = class {
28647
29211
  return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
28648
29212
  }
28649
29213
  async flushPersistedNetworkHistory() {
29214
+ if (this.sessionRef === void 0) {
29215
+ return;
29216
+ }
29217
+ const root = await this.ensureRoot();
29218
+ try {
29219
+ await withDetachedTimeoutSignal(PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, async (signal) => {
29220
+ const browserRecords = await this.readBrowserNetworkRecords(
29221
+ {
29222
+ includeBodies: true,
29223
+ includeCurrentPageOnly: false
29224
+ },
29225
+ signal
29226
+ );
29227
+ await this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
29228
+ bodyWriteMode: "authoritative",
29229
+ redactSecretHeaders: false
29230
+ });
29231
+ });
29232
+ } catch (error) {
29233
+ if (!isIgnorableRuntimeBindingError(error)) {
29234
+ throw error;
29235
+ }
29236
+ }
28650
29237
  }
28651
29238
  toDomTargetRef(target) {
28652
29239
  if (target.kind === "description") {
@@ -28697,14 +29284,18 @@ var OpensteerSessionRuntime = class {
28697
29284
  return this.engine;
28698
29285
  }
28699
29286
  if (this.injectedEngine) {
28700
- this.engine = this.injectedEngine;
29287
+ this.engine = this.wrapEngineWithObservationCapture(
29288
+ this.injectedEngine
29289
+ );
28701
29290
  this.ownsEngine = false;
28702
29291
  return this.engine;
28703
29292
  }
28704
29293
  if (this.engineFactory === void 0) {
28705
29294
  throw new Error("Opensteer engine factory is not initialized");
28706
29295
  }
28707
- this.engine = await this.engineFactory(overrides);
29296
+ this.engine = this.wrapEngineWithObservationCapture(
29297
+ await this.engineFactory(overrides)
29298
+ );
28708
29299
  this.ownsEngine = true;
28709
29300
  return this.engine;
28710
29301
  }
@@ -28799,6 +29390,12 @@ var OpensteerSessionRuntime = class {
28799
29390
  return "live";
28800
29391
  } catch (error) {
28801
29392
  if (isIgnorableRuntimeBindingError(error)) {
29393
+ const remainingPages = await engine.listPages({ sessionRef }).catch(() => void 0);
29394
+ const replacementPageRef = remainingPages?.[0]?.pageRef;
29395
+ if (replacementPageRef !== void 0) {
29396
+ this.pageRef = replacementPageRef;
29397
+ return "live";
29398
+ }
28802
29399
  return "invalid";
28803
29400
  }
28804
29401
  throw error;
@@ -28904,6 +29501,15 @@ var OpensteerSessionRuntime = class {
28904
29501
  return;
28905
29502
  }
28906
29503
  const root = await this.ensureRoot();
29504
+ const capturedStepEvents = input.events ?? this.consumePendingOperationEventCapture(
29505
+ input.operation,
29506
+ input.startedAt,
29507
+ input.completedAt
29508
+ );
29509
+ const drainedStepEvents = input.events === void 0 ? await this.drainPendingEngineEvents(input.context) : void 0;
29510
+ const stepEvents = mergeObservedStepEvents(capturedStepEvents, drainedStepEvents);
29511
+ const normalizedData = input.data === void 0 ? void 0 : toCanonicalJsonValue(input.data);
29512
+ const normalizedError = input.error === void 0 ? void 0 : normalizeOpensteerError(input.error);
28907
29513
  const artifacts = input.artifacts === void 0 ? void 0 : await Promise.all(
28908
29514
  input.artifacts.manifests.map(async (manifest) => {
28909
29515
  const reference = await root.artifacts.toProtocolArtifactReference(
@@ -28916,19 +29522,56 @@ var OpensteerSessionRuntime = class {
28916
29522
  return reference;
28917
29523
  })
28918
29524
  );
28919
- await root.traces.append(runId, {
29525
+ const traceEntry = await root.traces.append(runId, {
28920
29526
  operation: input.operation,
28921
29527
  outcome: input.outcome,
28922
29528
  startedAt: input.startedAt,
28923
29529
  completedAt: input.completedAt,
28924
29530
  ...input.context === void 0 ? {} : { context: input.context },
28925
- ...input.events === void 0 ? {} : { events: input.events },
29531
+ ...stepEvents === void 0 ? {} : { events: stepEvents },
28926
29532
  ...artifacts === void 0 ? {} : { artifacts },
28927
- ...input.data === void 0 ? {} : { data: toCanonicalJsonValue(input.data) },
28928
- ...input.error === void 0 ? {} : {
28929
- error: normalizeOpensteerError(input.error)
29533
+ ...normalizedData === void 0 ? {} : { data: normalizedData },
29534
+ ...normalizedError === void 0 ? {} : {
29535
+ error: normalizedError
28930
29536
  }
28931
29537
  });
29538
+ const observationSession = await this.ensureObservationSession().catch(() => void 0);
29539
+ if (observationSession === void 0 || this.observationConfig.profile === "off") {
29540
+ return;
29541
+ }
29542
+ const observationArtifactIds = input.artifacts === void 0 ? void 0 : (await Promise.allSettled(
29543
+ input.artifacts.manifests.map(async (manifest) => {
29544
+ const artifact = await observationSession.writeArtifact({
29545
+ artifactId: manifest.artifactId,
29546
+ kind: observationArtifactKindFromManifest(manifest.kind),
29547
+ createdAt: manifest.createdAt,
29548
+ context: manifest.scope,
29549
+ mediaType: manifest.mediaType,
29550
+ byteLength: manifest.byteLength,
29551
+ sha256: manifest.sha256,
29552
+ opensteerArtifactId: manifest.artifactId,
29553
+ storageKey: manifestToExternalBinaryLocation(root.rootPath, manifest).uri
29554
+ });
29555
+ return artifact.artifactId;
29556
+ })
29557
+ )).flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
29558
+ const observationEvents = buildObservationEventsFromTrace({
29559
+ traceId: traceEntry.traceId,
29560
+ stepId: traceEntry.stepId,
29561
+ operation: input.operation,
29562
+ outcome: input.outcome,
29563
+ startedAt: input.startedAt,
29564
+ completedAt: input.completedAt,
29565
+ ...input.context === void 0 ? {} : { context: input.context },
29566
+ ...stepEvents === void 0 ? {} : { events: stepEvents },
29567
+ ...normalizedData === void 0 ? {} : { data: normalizedData },
29568
+ ...normalizedError === void 0 ? {} : { error: normalizedError },
29569
+ ...observationArtifactIds === void 0 ? {} : { artifactIds: observationArtifactIds },
29570
+ profile: this.observationConfig.profile
29571
+ });
29572
+ if (observationEvents.length > 0) {
29573
+ await observationSession.appendBatch(observationEvents).catch(() => void 0);
29574
+ }
28932
29575
  }
28933
29576
  async cleanupSessionResources(engine, pageRef, sessionRef) {
28934
29577
  if (pageRef !== void 0) {
@@ -28940,6 +29583,7 @@ var OpensteerSessionRuntime = class {
28940
29583
  }
28941
29584
  async resetRuntimeState(options) {
28942
29585
  const engine = this.engine;
29586
+ const observations = this.observations;
28943
29587
  this.networkHistory.clear();
28944
29588
  this.sessionRef = void 0;
28945
29589
  this.pageRef = void 0;
@@ -28948,20 +29592,140 @@ var OpensteerSessionRuntime = class {
28948
29592
  this.computer = void 0;
28949
29593
  this.extractionDescriptors = void 0;
28950
29594
  this.engine = void 0;
29595
+ this.observations = void 0;
29596
+ this.pendingOperationEventCaptures.length = 0;
29597
+ await observations?.close("runtime_reset").catch(() => void 0);
28951
29598
  if (options.disposeEngine && this.ownsEngine && engine?.dispose) {
28952
29599
  await engine.dispose();
28953
29600
  }
28954
29601
  this.ownsEngine = false;
28955
29602
  }
29603
+ async ensureObservationSession() {
29604
+ if (this.observationConfig.profile === "off") {
29605
+ return void 0;
29606
+ }
29607
+ if (this.observations !== void 0) {
29608
+ return this.observations;
29609
+ }
29610
+ const observationSessionId = this.resolveObservationSessionId();
29611
+ if (observationSessionId === void 0) {
29612
+ return void 0;
29613
+ }
29614
+ const sink = this.injectedObservationSink ?? (await this.ensureRoot()).observations;
29615
+ this.observations = await sink.openSession({
29616
+ sessionId: observationSessionId,
29617
+ openedAt: Date.now(),
29618
+ config: this.observationConfig
29619
+ });
29620
+ return this.observations;
29621
+ }
29622
+ resolveObservationSessionId() {
29623
+ return this.observationSessionId ?? this.sessionRef;
29624
+ }
28956
29625
  runWithOperationTimeout(operation, callback, options = {}) {
28957
- return runWithPolicyTimeout(
28958
- this.policy.timeout,
28959
- {
28960
- operation,
28961
- ...options.signal === void 0 ? {} : { signal: options.signal }
28962
- },
28963
- callback
28964
- );
29626
+ const existingCollector = this.operationEventStorage.getStore();
29627
+ if (existingCollector !== void 0) {
29628
+ return runWithPolicyTimeout(
29629
+ this.policy.timeout,
29630
+ {
29631
+ operation,
29632
+ ...options.signal === void 0 ? {} : { signal: options.signal }
29633
+ },
29634
+ callback
29635
+ );
29636
+ }
29637
+ const collector = [];
29638
+ const startedAt = Date.now();
29639
+ return this.operationEventStorage.run(collector, async () => {
29640
+ try {
29641
+ return await runWithPolicyTimeout(
29642
+ this.policy.timeout,
29643
+ {
29644
+ operation,
29645
+ ...options.signal === void 0 ? {} : { signal: options.signal }
29646
+ },
29647
+ callback
29648
+ );
29649
+ } finally {
29650
+ this.recordPendingOperationEventCapture({
29651
+ operation,
29652
+ startedAt,
29653
+ completedAt: Date.now(),
29654
+ events: collector
29655
+ });
29656
+ }
29657
+ });
29658
+ }
29659
+ wrapEngineWithObservationCapture(engine) {
29660
+ return new Proxy(engine, {
29661
+ get: (target, property, receiver) => {
29662
+ const value = Reflect.get(target, property, receiver);
29663
+ if (typeof value !== "function") {
29664
+ return value;
29665
+ }
29666
+ return (...args) => {
29667
+ const result = Reflect.apply(value, target, args);
29668
+ if (!(result instanceof Promise)) {
29669
+ return result;
29670
+ }
29671
+ return result.then((resolved) => {
29672
+ this.captureObservedStepEvents(resolved);
29673
+ return resolved;
29674
+ });
29675
+ };
29676
+ }
29677
+ });
29678
+ }
29679
+ captureObservedStepEvents(value) {
29680
+ const collector = this.operationEventStorage.getStore();
29681
+ if (collector === void 0) {
29682
+ return;
29683
+ }
29684
+ const events = readStepResultEvents(value);
29685
+ if (events === void 0 || events.length === 0) {
29686
+ return;
29687
+ }
29688
+ collector.push(...events);
29689
+ }
29690
+ recordPendingOperationEventCapture(capture) {
29691
+ if (capture.events.length === 0) {
29692
+ return;
29693
+ }
29694
+ this.pendingOperationEventCaptures.push({
29695
+ ...capture,
29696
+ events: [...capture.events]
29697
+ });
29698
+ if (this.pendingOperationEventCaptures.length > PENDING_OPERATION_EVENT_CAPTURE_LIMIT) {
29699
+ this.pendingOperationEventCaptures.splice(
29700
+ 0,
29701
+ this.pendingOperationEventCaptures.length - PENDING_OPERATION_EVENT_CAPTURE_LIMIT
29702
+ );
29703
+ }
29704
+ }
29705
+ consumePendingOperationEventCapture(operation, startedAt, completedAt) {
29706
+ for (let index = this.pendingOperationEventCaptures.length - 1; index >= 0; index -= 1) {
29707
+ const capture = this.pendingOperationEventCaptures[index];
29708
+ if (capture === void 0) {
29709
+ continue;
29710
+ }
29711
+ if (capture.operation !== operation) {
29712
+ continue;
29713
+ }
29714
+ if (capture.startedAt < startedAt - PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS || capture.completedAt > completedAt + PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS) {
29715
+ continue;
29716
+ }
29717
+ this.pendingOperationEventCaptures.splice(index, 1);
29718
+ return capture.events;
29719
+ }
29720
+ return void 0;
29721
+ }
29722
+ async drainPendingEngineEvents(context) {
29723
+ const pageRef = context?.pageRef ?? this.pageRef;
29724
+ if (pageRef === void 0 || this.engine === void 0) {
29725
+ return void 0;
29726
+ }
29727
+ const events = await this.engine.drainEvents({ pageRef }).catch(() => []);
29728
+ return events.length > 0 ? events : void 0;
28965
29729
  }
28966
29730
  async navigatePage(input, timeout) {
28967
29731
  const remainingMs = timeout.remainingMs();
@@ -28997,6 +29761,34 @@ function buildRuntimeTraceContext(input) {
28997
29761
  function buildArtifactScope(input) {
28998
29762
  return buildRuntimeTraceContext(input);
28999
29763
  }
29764
+ function readStepResultEvents(value) {
29765
+ if (value === null || typeof value !== "object") {
29766
+ return void 0;
29767
+ }
29768
+ if (!("events" in value)) {
29769
+ return void 0;
29770
+ }
29771
+ const events = value.events;
29772
+ return Array.isArray(events) ? events : void 0;
29773
+ }
29774
+ function mergeObservedStepEvents(primary, secondary) {
29775
+ if (primary === void 0 || primary.length === 0) {
29776
+ return secondary === void 0 || secondary.length === 0 ? void 0 : secondary;
29777
+ }
29778
+ if (secondary === void 0 || secondary.length === 0) {
29779
+ return primary;
29780
+ }
29781
+ const merged = /* @__PURE__ */ new Map();
29782
+ for (const event of primary) {
29783
+ merged.set(event.eventId, event);
29784
+ }
29785
+ for (const event of secondary) {
29786
+ merged.set(event.eventId, event);
29787
+ }
29788
+ return [...merged.values()].sort(
29789
+ (left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0)
29790
+ );
29791
+ }
29000
29792
  function selectLiveQueryPageRef(input, currentPageRef) {
29001
29793
  if (input.pageRef !== void 0) {
29002
29794
  return input.pageRef;
@@ -29959,12 +30751,12 @@ function extractReverseRuntimeValue(value, pointer) {
29959
30751
  }
29960
30752
  return readDotPath(value, pointer);
29961
30753
  }
29962
- function readDotPath(value, path13) {
29963
- if (path13.length === 0) {
30754
+ function readDotPath(value, path15) {
30755
+ if (path15.length === 0) {
29964
30756
  return value;
29965
30757
  }
29966
30758
  let current = value;
29967
- for (const segment of path13.split(".").filter((entry) => entry.length > 0)) {
30759
+ for (const segment of path15.split(".").filter((entry) => entry.length > 0)) {
29968
30760
  if (current === null || current === void 0) {
29969
30761
  return void 0;
29970
30762
  }
@@ -30333,7 +31125,7 @@ function normalizeRuntimeErrorMessage(error) {
30333
31125
  return error instanceof Error ? error.message : String(error);
30334
31126
  }
30335
31127
  function runtimeDelay(ms) {
30336
- return new Promise((resolve5) => setTimeout(resolve5, ms));
31128
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
30337
31129
  }
30338
31130
  function applyBrowserCookiesToTransportRequest(request, cookies) {
30339
31131
  if (cookies.length === 0) {
@@ -30437,7 +31229,7 @@ function parseSetCookieHeader(value, requestUrl) {
30437
31229
  }
30438
31230
  const url = new URL(requestUrl);
30439
31231
  let domain = url.hostname;
30440
- let path13 = defaultCookiePath(url.pathname);
31232
+ let path15 = defaultCookiePath(url.pathname);
30441
31233
  let secure = url.protocol === "https:";
30442
31234
  let expiresAt;
30443
31235
  const cookieValue = rawValueParts.join("=").trim();
@@ -30450,7 +31242,7 @@ function parseSetCookieHeader(value, requestUrl) {
30450
31242
  continue;
30451
31243
  }
30452
31244
  if (key === "path" && attributeValue.length > 0) {
30453
- path13 = attributeValue;
31245
+ path15 = attributeValue;
30454
31246
  continue;
30455
31247
  }
30456
31248
  if (key === "secure") {
@@ -30476,7 +31268,7 @@ function parseSetCookieHeader(value, requestUrl) {
30476
31268
  name,
30477
31269
  value: cookieValue,
30478
31270
  domain,
30479
- path: path13,
31271
+ path: path15,
30480
31272
  secure,
30481
31273
  ...expiresAt === void 0 ? {} : { expiresAt }
30482
31274
  }
@@ -31428,7 +32220,7 @@ async function pollUntilResult(timeout, producer) {
31428
32220
  if (produced !== void 0) {
31429
32221
  return produced;
31430
32222
  }
31431
- await new Promise((resolve5) => setTimeout(resolve5, 100));
32223
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
31432
32224
  }
31433
32225
  }
31434
32226
  async function getMainFrame(engine, pageRef) {
@@ -31486,6 +32278,133 @@ function toOpensteerResolvedTarget2(target) {
31486
32278
  function normalizeOpensteerError(error) {
31487
32279
  return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
31488
32280
  }
32281
+ function observationArtifactKindFromManifest(kind) {
32282
+ switch (kind) {
32283
+ case "screenshot":
32284
+ return "screenshot";
32285
+ case "dom-snapshot":
32286
+ return "dom-snapshot";
32287
+ case "html-snapshot":
32288
+ return "html-snapshot";
32289
+ default:
32290
+ return "other";
32291
+ }
32292
+ }
32293
+ function buildObservationEventsFromTrace(input) {
32294
+ const context = normalizeObservationContext(input.context);
32295
+ const baseCorrelationId = input.traceId;
32296
+ const startedEvent = {
32297
+ kind: input.operation === "session.open" || input.operation === "session.close" ? "session" : "operation",
32298
+ phase: "started",
32299
+ createdAt: input.startedAt,
32300
+ correlationId: baseCorrelationId,
32301
+ spanId: input.stepId,
32302
+ ...context === void 0 ? {} : { context },
32303
+ data: {
32304
+ operation: input.operation
32305
+ }
32306
+ };
32307
+ const stepEvents = (input.events ?? []).filter((event) => shouldCaptureObservationStepEvent(event, input.profile)).map((event) => {
32308
+ const eventContext = buildObservationContextFromEvent(event);
32309
+ return {
32310
+ kind: observationKindForStepEvent(event),
32311
+ phase: "occurred",
32312
+ createdAt: event.timestamp,
32313
+ correlationId: baseCorrelationId,
32314
+ parentSpanId: input.stepId,
32315
+ ...eventContext === void 0 ? {} : { context: eventContext },
32316
+ data: stripObservationStepEvent(event),
32317
+ ...event.kind === "page-error" ? {
32318
+ error: {
32319
+ message: event.message,
32320
+ ...event.stack === void 0 ? {} : { details: { stack: event.stack } }
32321
+ }
32322
+ } : {}
32323
+ };
32324
+ });
32325
+ const completedEvent = {
32326
+ kind: input.operation === "session.open" || input.operation === "session.close" ? "session" : "operation",
32327
+ phase: input.outcome === "ok" ? "completed" : "failed",
32328
+ createdAt: input.completedAt,
32329
+ correlationId: baseCorrelationId,
32330
+ spanId: input.stepId,
32331
+ ...context === void 0 ? {} : { context },
32332
+ data: {
32333
+ operation: input.operation,
32334
+ startedAt: input.startedAt,
32335
+ completedAt: input.completedAt,
32336
+ durationMs: input.completedAt - input.startedAt,
32337
+ ...input.data === void 0 ? {} : { output: input.data }
32338
+ },
32339
+ ...input.error === void 0 ? {} : {
32340
+ error: {
32341
+ ...input.error.code === void 0 ? {} : { code: input.error.code },
32342
+ message: input.error.message,
32343
+ ...input.error.retriable === void 0 ? {} : { retriable: input.error.retriable },
32344
+ ...input.error.details === void 0 ? {} : { details: toCanonicalJsonValue(input.error.details) }
32345
+ }
32346
+ },
32347
+ ...input.artifactIds === void 0 || input.artifactIds.length === 0 ? {} : { artifactIds: input.artifactIds }
32348
+ };
32349
+ return [startedEvent, ...stepEvents, completedEvent];
32350
+ }
32351
+ function buildObservationContextFromEvent(event) {
32352
+ return normalizeObservationContext({
32353
+ sessionRef: event.sessionRef,
32354
+ ...event.pageRef === void 0 ? {} : { pageRef: event.pageRef },
32355
+ ...event.frameRef === void 0 ? {} : { frameRef: event.frameRef },
32356
+ ...event.documentRef === void 0 ? {} : { documentRef: event.documentRef },
32357
+ ...event.documentEpoch === void 0 ? {} : { documentEpoch: event.documentEpoch }
32358
+ });
32359
+ }
32360
+ function shouldCaptureObservationStepEvent(event, profile) {
32361
+ if (profile === "diagnostic") {
32362
+ return true;
32363
+ }
32364
+ switch (event.kind) {
32365
+ case "page-created":
32366
+ case "popup-opened":
32367
+ case "page-closed":
32368
+ case "page-error":
32369
+ return true;
32370
+ case "console":
32371
+ return event.level === "warn" || event.level === "error";
32372
+ default:
32373
+ return false;
32374
+ }
32375
+ }
32376
+ function observationKindForStepEvent(event) {
32377
+ switch (event.kind) {
32378
+ case "console":
32379
+ return "console";
32380
+ case "page-error":
32381
+ return "error";
32382
+ case "paused":
32383
+ case "resumed":
32384
+ case "frozen":
32385
+ return "runtime";
32386
+ default:
32387
+ return "page";
32388
+ }
32389
+ }
32390
+ function stripObservationStepEvent(event) {
32391
+ const {
32392
+ eventId: _eventId,
32393
+ kind,
32394
+ timestamp,
32395
+ sessionRef: _sessionRef,
32396
+ pageRef: _pageRef,
32397
+ frameRef: _frameRef,
32398
+ documentRef: _documentRef,
32399
+ documentEpoch: _documentEpoch,
32400
+ ...rest
32401
+ } = event;
32402
+ return toCanonicalJsonValue({
32403
+ eventKind: kind,
32404
+ timestamp,
32405
+ ...rest
32406
+ });
32407
+ }
31489
32408
  function buildMutationCaptureTraceData(diagnostics) {
31490
32409
  if (diagnostics?.finalizeError === void 0) {
31491
32410
  return {};
@@ -31535,8 +32454,8 @@ function screenshotMediaType(format2) {
31535
32454
  var OpensteerRuntime = class extends OpensteerSessionRuntime {
31536
32455
  constructor(options = {}) {
31537
32456
  const publicWorkspace = normalizeWorkspace2(options.workspace);
31538
- const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path6__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
31539
- rootDir: path6__default.default.resolve(options.rootDir ?? process.cwd()),
32457
+ const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path7__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
32458
+ rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
31540
32459
  workspace: publicWorkspace
31541
32460
  }));
31542
32461
  const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
@@ -31561,14 +32480,17 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
31561
32480
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31562
32481
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31563
32482
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31564
- cleanupRootOnClose
32483
+ cleanupRootOnClose,
32484
+ ...options.observability === void 0 ? {} : { observability: options.observability },
32485
+ ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
32486
+ ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
31565
32487
  })
31566
32488
  );
31567
32489
  }
31568
32490
  };
31569
32491
  var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31570
32492
  constructor(options) {
31571
- const rootPath = options.rootPath ?? path6__default.default.resolve(options.rootDir ?? process.cwd());
32493
+ const rootPath = options.rootPath ?? path7__default.default.resolve(options.rootDir ?? process.cwd());
31572
32494
  const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
31573
32495
  const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31574
32496
  assertSupportedEngineOptions({
@@ -31590,7 +32512,10 @@ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31590
32512
  ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
31591
32513
  ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
31592
32514
  ...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
31593
- cleanupRootOnClose
32515
+ cleanupRootOnClose,
32516
+ ...options.observability === void 0 ? {} : { observability: options.observability },
32517
+ ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
32518
+ ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
31594
32519
  })
31595
32520
  );
31596
32521
  }
@@ -31616,6 +32541,9 @@ function buildSharedRuntimeOptions(input) {
31616
32541
  ...input.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: input.extractionDescriptorStore },
31617
32542
  ...input.registryOverrides === void 0 ? {} : { registryOverrides: input.registryOverrides },
31618
32543
  cleanupRootOnClose: input.cleanupRootOnClose,
32544
+ ...input.observability === void 0 ? {} : { observability: input.observability },
32545
+ ...input.observationSessionId === void 0 ? {} : { observationSessionId: input.observationSessionId },
32546
+ ...input.observationSink === void 0 ? {} : { observationSink: input.observationSink },
31619
32547
  sessionInfo: {
31620
32548
  provider: {
31621
32549
  mode: "local",
@@ -31678,345 +32606,281 @@ function resolveOpensteerProvider(input = {}) {
31678
32606
  };
31679
32607
  }
31680
32608
  var execFile2 = util.promisify(child_process.execFile);
31681
- var DEFAULT_CAPTURE_TIMEOUT_MS = 3e4;
31682
- var DEFAULT_STOP_TIMEOUT_MS = 15e3;
31683
- var DEVTOOLS_POLL_INTERVAL_MS2 = 50;
31684
- var PROCESS_LIST_MAX_BUFFER_BYTES3 = 16 * 1024 * 1024;
31685
- async function resolveCookieCaptureStrategy(input = {}) {
31686
- const timeoutMs = input.timeoutMs ?? DEFAULT_CAPTURE_TIMEOUT_MS;
31687
- if (input.attachEndpoint !== void 0) {
31688
- if (input.strategy !== void 0 && input.strategy !== "attach") {
31689
- throw new Error(
31690
- `Strategy "${input.strategy}" is incompatible with an explicit attach endpoint.`
31691
- );
31692
- }
31693
- return {
31694
- strategy: "attach",
31695
- attachEndpoint: input.attachEndpoint,
31696
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
31697
- timeoutMs
31698
- };
31699
- }
32609
+ var NODE_SQLITE_SPECIFIER2 = `node:${"sqlite"}`;
32610
+ var CHROME_EPOCH_OFFSET = 11644473600000000n;
32611
+ var CHROME_HMAC_PREFIX_LENGTH = 32;
32612
+ async function readBrowserCookies(input = {}) {
31700
32613
  const brand2 = resolveRequestedBrand(input);
31701
- const executablePath = resolveBrandExecutablePath(brand2, input.executablePath);
31702
32614
  const userDataDir = resolveBrandUserDataDir(brand2, input.userDataDir);
31703
- const profileDirectory = input.profileDirectory;
31704
- const attachEndpoint = await resolveReachableAttachEndpoint(userDataDir, timeoutMs);
31705
- const runningProcess = findBrandProcess(brand2);
31706
- const autoStrategy = attachEndpoint !== void 0 ? "attach" : runningProcess !== null ? "managed-relaunch" : "headless";
31707
- const strategy = input.strategy ?? autoStrategy;
31708
- validateRequestedStrategy({
31709
- strategy,
31710
- brand: brand2,
31711
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31712
- ...runningProcess?.pid === void 0 ? {} : { runningPid: runningProcess.pid }
31713
- });
31714
- return {
31715
- strategy,
31716
- brandId: brand2.id,
31717
- brandDisplayName: brand2.displayName,
31718
- executablePath,
31719
- userDataDir,
31720
- ...profileDirectory === void 0 ? {} : { profileDirectory },
31721
- ...attachEndpoint === void 0 ? {} : { attachEndpoint },
31722
- ...runningProcess === null ? {} : { runningPid: runningProcess.pid },
31723
- timeoutMs
31724
- };
31725
- }
31726
- async function acquireCdpEndpoint(resolved) {
31727
- if (resolved.strategy === "attach") {
31728
- if (!resolved.attachEndpoint) {
31729
- throw new Error("Attach capture requires a debuggable browser endpoint.");
31730
- }
31731
- const inspected = await inspectCdpEndpoint({
31732
- endpoint: resolved.attachEndpoint,
31733
- timeoutMs: Math.min(2e3, resolved.timeoutMs)
31734
- });
31735
- return {
31736
- strategy: "attach",
31737
- cdpEndpoint: inspected.endpoint,
31738
- ...resolved.brandId === void 0 ? {} : { brandId: resolved.brandId },
31739
- ...resolved.brandDisplayName === void 0 ? {} : { brandDisplayName: resolved.brandDisplayName },
31740
- ...resolved.userDataDir === void 0 ? {} : { userDataDir: resolved.userDataDir },
31741
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31742
- cleanup: async () => void 0
31743
- };
31744
- }
31745
- if (!resolved.brandId || !resolved.brandDisplayName || !resolved.executablePath || !resolved.userDataDir) {
32615
+ const profileDirectory = input.profileDirectory ?? "Default";
32616
+ const cookiesPath = path7.join(userDataDir, profileDirectory, "Cookies");
32617
+ if (!fs.existsSync(cookiesPath)) {
31746
32618
  throw new Error(
31747
- "Headless cookie capture requires a resolved browser brand, executable, and user-data-dir."
32619
+ `Cookies database not found at "${cookiesPath}". Verify the browser brand, user-data-dir, and profile-directory are correct.`
31748
32620
  );
31749
32621
  }
31750
- const userDataDir = resolved.userDataDir;
31751
- if (resolved.strategy === "managed-relaunch") {
31752
- if (resolved.runningPid === void 0) {
31753
- throw new Error("Managed relaunch requires a running browser process.");
31754
- }
31755
- await gracefullyStopBrowser(
31756
- getBrowserBrand(resolved.brandId),
31757
- resolved.runningPid,
31758
- resolved.timeoutMs
31759
- );
31760
- }
31761
- await clearChromeSingletonEntries(userDataDir);
32622
+ const tempDir = await promises.mkdtemp(path7.join(os.tmpdir(), "opensteer-cookies-"));
31762
32623
  try {
31763
- const capture = await launchCaptureChrome({
31764
- brandDisplayName: resolved.brandDisplayName,
31765
- executablePath: resolved.executablePath,
31766
- userDataDir,
31767
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31768
- timeoutMs: resolved.timeoutMs
31769
- });
32624
+ await copyCookiesDatabase(cookiesPath, tempDir);
32625
+ const decryptionKey = await resolveDecryptionKey(brand2.id, userDataDir);
32626
+ const rows = queryAllCookies(path7.join(tempDir, "Cookies"));
32627
+ const cookies = decryptCookieRows(rows, decryptionKey);
31770
32628
  return {
31771
- strategy: resolved.strategy,
31772
- cdpEndpoint: capture.endpoint,
31773
- brandId: resolved.brandId,
31774
- brandDisplayName: resolved.brandDisplayName,
31775
- userDataDir: resolved.userDataDir,
31776
- ...resolved.profileDirectory === void 0 ? {} : { profileDirectory: resolved.profileDirectory },
31777
- cleanup: async () => {
31778
- await capture.kill().catch(() => void 0);
31779
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31780
- }
31781
- };
31782
- } catch (error) {
31783
- await clearChromeSingletonEntries(userDataDir).catch(() => void 0);
31784
- throw error;
31785
- }
31786
- }
31787
- async function gracefullyStopBrowser(brand2, pid, timeoutMs = DEFAULT_STOP_TIMEOUT_MS) {
31788
- if (pid <= 0) {
31789
- return;
31790
- }
31791
- const platformConfig = resolveBrandPlatformConfig(brand2);
31792
- if (process.platform === "darwin" && platformConfig?.bundleId) {
31793
- await execFile2(
31794
- "osascript",
31795
- ["-e", `tell application id "${platformConfig.bundleId}" to quit`],
31796
- {
31797
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31798
- }
31799
- ).catch(() => void 0);
31800
- } else if (process.platform === "win32") {
31801
- await execFile2("taskkill", ["/PID", String(pid)], {
31802
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31803
- }).catch(() => void 0);
31804
- } else {
31805
- try {
31806
- process.kill(pid, "SIGTERM");
31807
- } catch {
31808
- }
31809
- }
31810
- if (await waitForProcessExit2(pid, timeoutMs)) {
31811
- return;
31812
- }
31813
- if (process.platform === "win32") {
31814
- await execFile2("taskkill", ["/F", "/PID", String(pid)], {
31815
- maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES3
31816
- }).catch(() => void 0);
31817
- } else {
31818
- try {
31819
- process.kill(pid, "SIGKILL");
31820
- } catch {
31821
- }
31822
- }
31823
- await waitForProcessExit2(pid, Math.min(5e3, timeoutMs));
31824
- }
31825
- async function launchCaptureChrome(input) {
31826
- const stderrLines = [];
31827
- const child = child_process.spawn(input.executablePath, buildCaptureChromeArgs(input), {
31828
- detached: process.platform !== "win32",
31829
- stdio: ["ignore", "ignore", "pipe"]
31830
- });
31831
- child.unref();
31832
- child.stderr?.setEncoding("utf8");
31833
- child.stderr?.on("data", (chunk) => {
31834
- stderrLines.push(String(chunk));
31835
- });
31836
- try {
31837
- const endpoint = await waitForCaptureEndpoint({
31838
- brandDisplayName: input.brandDisplayName,
31839
- child,
31840
- stderrLines,
31841
- timeoutMs: input.timeoutMs,
31842
- userDataDir: input.userDataDir
31843
- });
31844
- return {
31845
- endpoint,
31846
- kill: async () => {
31847
- await terminateChild(child);
31848
- }
32629
+ cookies,
32630
+ brandId: brand2.id,
32631
+ brandDisplayName: brand2.displayName,
32632
+ userDataDir,
32633
+ profileDirectory
31849
32634
  };
31850
- } catch (error) {
31851
- await terminateChild(child).catch(() => void 0);
31852
- throw error;
32635
+ } finally {
32636
+ await promises.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
31853
32637
  }
31854
32638
  }
31855
- function relaunchBrowserNormally(executablePath) {
31856
- const child = child_process.spawn(executablePath, [], {
31857
- detached: true,
31858
- stdio: "ignore"
31859
- });
31860
- child.unref();
31861
- }
31862
32639
  function resolveRequestedBrand(input) {
31863
32640
  if (input.brandId !== void 0) {
31864
32641
  return getBrowserBrand(input.brandId);
31865
32642
  }
31866
- if (input.userDataDir !== void 0) {
31867
- const inferred = inferBrandFromUserDataDir(input.userDataDir);
31868
- if (!inferred) {
31869
- throw new Error(
31870
- `Could not infer a browser brand from user-data-dir "${input.userDataDir}". Pass --browser explicitly.`
31871
- );
31872
- }
31873
- return inferred;
31874
- }
31875
- if (input.executablePath !== void 0) {
31876
- const inferred = inferBrandFromExecutablePath(input.executablePath);
31877
- if (!inferred) {
31878
- throw new Error(
31879
- `Could not infer a browser brand from executable path "${input.executablePath}". Pass --browser explicitly.`
31880
- );
31881
- }
31882
- return inferred;
31883
- }
31884
32643
  const installed = detectInstalledBrowserBrands()[0];
31885
32644
  if (!installed) {
31886
32645
  throw new Error(
31887
- "No Chromium browser found. Install a supported browser or pass --browser explicitly."
32646
+ "No Chromium browser found. Install a supported browser or pass brandId explicitly."
31888
32647
  );
31889
32648
  }
31890
32649
  return installed.brand;
31891
32650
  }
31892
- async function resolveReachableAttachEndpoint(userDataDir, timeoutMs) {
31893
- const activePort = readDevToolsActivePort(userDataDir);
31894
- if (!activePort) {
31895
- return void 0;
32651
+ async function copyCookiesDatabase(cookiesPath, destDir) {
32652
+ await promises.copyFile(cookiesPath, path7.join(destDir, "Cookies"));
32653
+ for (const suffix of ["-wal", "-journal", "-shm"]) {
32654
+ const src = cookiesPath + suffix;
32655
+ if (fs.existsSync(src)) {
32656
+ await promises.copyFile(src, path7.join(destDir, "Cookies" + suffix)).catch(() => void 0);
32657
+ }
31896
32658
  }
32659
+ }
32660
+ function queryAllCookies(dbPath) {
32661
+ let DatabaseSync;
31897
32662
  try {
31898
- return (await inspectCdpEndpoint({
31899
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31900
- timeoutMs: Math.min(2e3, timeoutMs)
31901
- })).endpoint;
32663
+ ({ DatabaseSync } = __require(NODE_SQLITE_SPECIFIER2));
31902
32664
  } catch {
31903
- return void 0;
32665
+ throw new Error(
32666
+ "Reading browser cookies requires Node's built-in SQLite support. Use Node 22.5+ or a build with node:sqlite enabled."
32667
+ );
31904
32668
  }
32669
+ const database = new DatabaseSync(dbPath, { readOnly: true });
32670
+ try {
32671
+ const stmt = database.prepare(
32672
+ `SELECT host_key, name, value, encrypted_value, path,
32673
+ expires_utc, is_secure, is_httponly, samesite, is_persistent
32674
+ FROM cookies`
32675
+ );
32676
+ stmt.setReadBigInts(true);
32677
+ return stmt.all().map(toRawCookieRow);
32678
+ } finally {
32679
+ database.close();
32680
+ }
32681
+ }
32682
+ function toRawCookieRow(row) {
32683
+ return {
32684
+ host_key: row.host_key,
32685
+ name: row.name,
32686
+ value: row.value,
32687
+ encrypted_value: row.encrypted_value,
32688
+ path: row.path,
32689
+ expires_utc: row.expires_utc,
32690
+ is_secure: row.is_secure,
32691
+ is_httponly: row.is_httponly,
32692
+ samesite: row.samesite,
32693
+ is_persistent: row.is_persistent
32694
+ };
31905
32695
  }
31906
- function validateRequestedStrategy(input) {
31907
- if (input.strategy === "attach" && input.attachEndpoint === void 0) {
32696
+ async function resolveDecryptionKey(brandId, userDataDir) {
32697
+ if (process.platform === "darwin") {
32698
+ const password = await resolveKeychainPassword(brandId);
32699
+ const key = crypto.pbkdf2Sync(password, "saltysalt", 1003, 16, "sha1");
32700
+ return { platform: "darwin", key, algorithm: "aes-128-cbc" };
32701
+ }
32702
+ if (process.platform === "linux") {
32703
+ const key = crypto.pbkdf2Sync("peanuts", "saltysalt", 1, 16, "sha1");
32704
+ return { platform: "linux", key, algorithm: "aes-128-cbc" };
32705
+ }
32706
+ if (process.platform === "win32") {
32707
+ const key = await resolveWindowsMasterKey(userDataDir);
32708
+ return { platform: "win32", key, algorithm: "aes-256-gcm" };
32709
+ }
32710
+ throw new Error(`Unsupported platform "${process.platform}" for cookie decryption.`);
32711
+ }
32712
+ var BRAND_KEYCHAIN_SERVICE = {
32713
+ chrome: "Chrome Safe Storage",
32714
+ "chrome-canary": "Chrome Safe Storage",
32715
+ chromium: "Chromium Safe Storage",
32716
+ brave: "Brave Safe Storage",
32717
+ edge: "Microsoft Edge Safe Storage",
32718
+ vivaldi: "Chrome Safe Storage",
32719
+ helium: "Chrome Safe Storage"
32720
+ };
32721
+ async function resolveKeychainPassword(brandId) {
32722
+ const service = BRAND_KEYCHAIN_SERVICE[brandId];
32723
+ try {
32724
+ const { stdout } = await execFile2("security", [
32725
+ "find-generic-password",
32726
+ "-s",
32727
+ service,
32728
+ "-w"
32729
+ ]);
32730
+ return stdout.trim();
32731
+ } catch {
31908
32732
  throw new Error(
31909
- `${input.brand.displayName} is not currently exposing a debuggable CDP endpoint for attach mode.`
32733
+ `Failed to retrieve "${service}" from macOS Keychain. Ensure the browser has been opened at least once and Keychain access is allowed.`
31910
32734
  );
31911
32735
  }
31912
- if (input.strategy === "headless" && input.runningPid !== void 0) {
32736
+ }
32737
+ async function resolveWindowsMasterKey(userDataDir) {
32738
+ const localStatePath = path7.join(userDataDir, "Local State");
32739
+ let localState;
32740
+ try {
32741
+ localState = JSON.parse(await promises.readFile(localStatePath, "utf8"));
32742
+ } catch {
31913
32743
  throw new Error(
31914
- `${input.brand.displayName} is already running. Close it first or use managed-relaunch.`
32744
+ `Failed to read "${localStatePath}". Ensure the browser has been opened at least once.`
31915
32745
  );
31916
32746
  }
31917
- if (input.strategy === "managed-relaunch" && input.runningPid === void 0) {
32747
+ const encodedKey = localState.os_crypt?.encrypted_key;
32748
+ if (!encodedKey) {
32749
+ throw new Error(`No encrypted key found in "${localStatePath}".`);
32750
+ }
32751
+ const rawKey = Buffer.from(encodedKey, "base64").subarray(5);
32752
+ const psScript = `
32753
+ Add-Type -AssemblyName System.Security
32754
+ $bytes = [byte[]]@(${Array.from(rawKey).join(",")})
32755
+ $decrypted = [System.Security.Cryptography.ProtectedData]::Unprotect($bytes, $null, 'CurrentUser')
32756
+ [Convert]::ToBase64String($decrypted)
32757
+ `;
32758
+ try {
32759
+ const { stdout } = await execFile2("powershell", [
32760
+ "-NoProfile",
32761
+ "-NonInteractive",
32762
+ "-Command",
32763
+ psScript
32764
+ ]);
32765
+ return Buffer.from(stdout.trim(), "base64");
32766
+ } catch {
31918
32767
  throw new Error(
31919
- `${input.brand.displayName} is not currently running, so managed-relaunch is not available.`
32768
+ "Failed to decrypt browser master key via Windows DPAPI. Ensure you are running as the same user who owns the browser profile."
31920
32769
  );
31921
32770
  }
31922
32771
  }
31923
- function inferBrandFromUserDataDir(userDataDir) {
31924
- const normalized = normalizePath(userDataDir);
31925
- return getAllBrowserBrands().find((brand2) => {
31926
- const config = resolveBrandPlatformConfig(brand2);
31927
- if (!config) {
31928
- return false;
32772
+ function decryptCookieRows(rows, decryptionKey) {
32773
+ const cookies = [];
32774
+ const nowSeconds = Math.floor(Date.now() / 1e3);
32775
+ for (const row of rows) {
32776
+ const name = row.name.trim();
32777
+ const domain = row.host_key.trim();
32778
+ if (!name || !domain) {
32779
+ continue;
31929
32780
  }
31930
- const defaultDir = normalizePath(config.userDataDir);
31931
- return normalized === defaultDir || normalized.startsWith(`${defaultDir}/`);
31932
- });
31933
- }
31934
- function inferBrandFromExecutablePath(executablePath) {
31935
- const normalized = normalizePath(executablePath);
31936
- return getAllBrowserBrands().find((brand2) => {
31937
- const config = resolveBrandPlatformConfig(brand2);
31938
- if (!config) {
31939
- return false;
32781
+ const value = decryptCookieValue(row, decryptionKey);
32782
+ if (value === null) {
32783
+ continue;
31940
32784
  }
31941
- return config.executableCandidates.some(
31942
- (candidate) => candidate !== null && normalizePath(candidate) === normalized
31943
- );
31944
- });
31945
- }
31946
- function buildCaptureChromeArgs(input) {
31947
- const args = [
31948
- "--remote-debugging-port=0",
31949
- "--headless=new",
31950
- "--no-first-run",
31951
- "--no-default-browser-check",
31952
- "--disable-background-networking",
31953
- "--disable-sync",
31954
- "--disable-component-update",
31955
- `--user-data-dir=${input.userDataDir}`
31956
- ];
31957
- if (input.profileDirectory !== void 0) {
31958
- args.push(`--profile-directory=${input.profileDirectory}`);
31959
- }
31960
- return args;
31961
- }
31962
- async function waitForCaptureEndpoint(input) {
31963
- const deadline = Date.now() + input.timeoutMs;
31964
- while (Date.now() < deadline) {
31965
- const activePort = readDevToolsActivePort(input.userDataDir);
31966
- if (activePort) {
31967
- try {
31968
- return (await inspectCdpEndpoint({
31969
- endpoint: `http://127.0.0.1:${String(activePort.port)}`,
31970
- timeoutMs: Math.min(2e3, input.timeoutMs)
31971
- })).endpoint;
31972
- } catch {
31973
- return `ws://127.0.0.1:${String(activePort.port)}${activePort.webSocketPath}`;
31974
- }
32785
+ const expiresSeconds = chromeDateToUnixSeconds(row.expires_utc);
32786
+ const isSession = expiresSeconds <= 0;
32787
+ if (!isSession && expiresSeconds < nowSeconds) {
32788
+ continue;
31975
32789
  }
31976
- if (input.child.exitCode !== null) {
31977
- break;
32790
+ const sameSite = chromeSameSiteToString(row.samesite);
32791
+ let secure = Number(row.is_secure) === 1;
32792
+ if (sameSite === "None") {
32793
+ secure = true;
31978
32794
  }
31979
- await sleep4(DEVTOOLS_POLL_INTERVAL_MS2);
32795
+ cookies.push({
32796
+ name,
32797
+ value,
32798
+ domain,
32799
+ path: row.path || "/",
32800
+ secure,
32801
+ httpOnly: Number(row.is_httponly) === 1,
32802
+ ...isSession ? {} : { expires: expiresSeconds },
32803
+ ...sameSite !== void 0 ? { sameSite } : {}
32804
+ });
31980
32805
  }
31981
- throw new Error(formatCaptureLaunchError(input.brandDisplayName, input.stderrLines));
32806
+ return cookies;
31982
32807
  }
31983
- function formatCaptureLaunchError(brandDisplayName, stderrLines) {
31984
- const relevantLines = stderrLines.join("").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
31985
- if (relevantLines.length === 0) {
31986
- return `${brandDisplayName} failed to launch before exposing a DevTools endpoint.`;
32808
+ function decryptCookieValue(row, decryptionKey) {
32809
+ if (row.value && row.value.length > 0) {
32810
+ return row.value;
31987
32811
  }
31988
- const focusedLines = relevantLines.filter(
31989
- (line) => /(error|fatal|sandbox|namespace|permission|cannot|failed|abort)/i.test(line)
31990
- );
31991
- return `${brandDisplayName} failed to launch before exposing a DevTools endpoint.
31992
- ${(focusedLines.length > 0 ? focusedLines : relevantLines).slice(-5).join("\n")}`;
32812
+ const encrypted = Buffer.isBuffer(row.encrypted_value) ? row.encrypted_value : Buffer.from(row.encrypted_value);
32813
+ if (encrypted.length === 0) {
32814
+ return "";
32815
+ }
32816
+ const prefix = encrypted.subarray(0, 3).toString("ascii");
32817
+ if (prefix !== "v10" && prefix !== "v11") {
32818
+ return encrypted.toString("utf8");
32819
+ }
32820
+ const ciphertext = encrypted.subarray(3);
32821
+ if (decryptionKey.algorithm === "aes-128-cbc") {
32822
+ return decryptAes128Cbc(ciphertext, decryptionKey.key);
32823
+ }
32824
+ if (decryptionKey.algorithm === "aes-256-gcm") {
32825
+ return decryptAes256Gcm(ciphertext, decryptionKey.key);
32826
+ }
32827
+ return null;
31993
32828
  }
31994
- async function terminateChild(child) {
31995
- if (child.exitCode !== null) {
31996
- return;
32829
+ function decryptAes128Cbc(ciphertext, key) {
32830
+ try {
32831
+ const iv = Buffer.alloc(16, 32);
32832
+ const decipher = crypto.createDecipheriv("aes-128-cbc", key, iv);
32833
+ let decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
32834
+ if (decrypted.length > CHROME_HMAC_PREFIX_LENGTH && containsNonPrintableAscii(decrypted, CHROME_HMAC_PREFIX_LENGTH)) {
32835
+ decrypted = decrypted.subarray(CHROME_HMAC_PREFIX_LENGTH);
32836
+ }
32837
+ if (containsNonPrintableAscii(decrypted, decrypted.length)) {
32838
+ return null;
32839
+ }
32840
+ return decrypted.toString("utf8");
32841
+ } catch {
32842
+ return null;
31997
32843
  }
32844
+ }
32845
+ function decryptAes256Gcm(ciphertext, key) {
31998
32846
  try {
31999
- child.kill("SIGKILL");
32847
+ const nonce = ciphertext.subarray(0, 12);
32848
+ const authTag = ciphertext.subarray(ciphertext.length - 16);
32849
+ const encrypted = ciphertext.subarray(12, ciphertext.length - 16);
32850
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
32851
+ decipher.setAuthTag(authTag);
32852
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
32853
+ if (containsNonPrintableAscii(decrypted, decrypted.length)) {
32854
+ return null;
32855
+ }
32856
+ return decrypted.toString("utf8");
32000
32857
  } catch {
32001
- return;
32858
+ return null;
32002
32859
  }
32003
- await sleep4(50);
32004
32860
  }
32005
- async function waitForProcessExit2(pid, timeoutMs) {
32006
- const deadline = Date.now() + timeoutMs;
32007
- while (Date.now() < deadline) {
32008
- if (!isProcessRunning(pid)) {
32861
+ function containsNonPrintableAscii(buffer, length) {
32862
+ const end = Math.min(length, buffer.length);
32863
+ for (let i = 0; i < end; i++) {
32864
+ const byte = buffer[i];
32865
+ if (byte < 32 || byte > 126) {
32009
32866
  return true;
32010
32867
  }
32011
- await sleep4(50);
32012
32868
  }
32013
- return !isProcessRunning(pid);
32869
+ return false;
32014
32870
  }
32015
- function normalizePath(value) {
32016
- return path6.resolve(expandHome(value)).replaceAll("\\", "/").toLowerCase();
32871
+ function chromeDateToUnixSeconds(chromeTimestamp) {
32872
+ const ts = BigInt(chromeTimestamp);
32873
+ if (ts <= 0n) {
32874
+ return -1;
32875
+ }
32876
+ return Number((ts - CHROME_EPOCH_OFFSET) / 1000000n);
32017
32877
  }
32018
- async function sleep4(ms) {
32019
- await new Promise((resolve5) => setTimeout(resolve5, ms));
32878
+ function chromeSameSiteToString(value) {
32879
+ const v = Number(value);
32880
+ if (v === 0) return "None";
32881
+ if (v === 1) return "Lax";
32882
+ if (v === 2) return "Strict";
32883
+ return void 0;
32020
32884
  }
32021
32885
 
32022
32886
  // src/cloud/cookie-sync.ts
@@ -32072,14 +32936,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
32072
32936
  if (!name || !domain) {
32073
32937
  return null;
32074
32938
  }
32075
- const path13 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
32939
+ const path15 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
32076
32940
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
32077
32941
  const sameSite = normalizeSameSite(cookie.sameSite);
32078
32942
  return {
32079
32943
  name,
32080
32944
  value: cookie.value,
32081
32945
  domain,
32082
- path: path13,
32946
+ path: path15,
32083
32947
  secure: cookie.secure,
32084
32948
  httpOnly: cookie.httpOnly,
32085
32949
  ...sameSite === void 0 ? {} : { sameSite },
@@ -32095,114 +32959,48 @@ function normalizeSameSite(value) {
32095
32959
  return void 0;
32096
32960
  }
32097
32961
 
32098
- // src/cloud/portable-cookie-snapshot.ts
32099
- var gzip = util.promisify(zlib.gzip);
32100
- async function capturePortableBrowserProfileSnapshot(input = {}) {
32101
- const attached = input.attachEndpoint ? await inspectCdpEndpoint({
32102
- endpoint: input.attachEndpoint,
32103
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32104
- }) : await selectAttachBrowserCandidate({
32105
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32106
- });
32107
- const browser = await enginePlaywright.connectPlaywrightChromiumBrowser({
32108
- url: attached.endpoint
32109
- });
32110
- try {
32111
- const context = browser.contexts()[0];
32112
- if (!context) {
32113
- throw new Error("Attached browser did not expose a default browser context.");
32114
- }
32115
- const prepared = prepareBrowserProfileSyncCookies({
32116
- cookies: await context.cookies(),
32117
- ...input.domains === void 0 ? {} : { domains: input.domains }
32118
- });
32119
- if (prepared.cookies.length === 0) {
32120
- throw new Error("No syncable cookies found for the selected browser and scope.");
32121
- }
32122
- const browserVersion = browser.version();
32123
- const source = parseSnapshotSource(attached.browser ?? browserVersion);
32124
- return {
32125
- version: "portable-cookies-v1",
32126
- source: {
32127
- browserFamily: "chromium",
32128
- ...source.browserName === void 0 ? {} : { browserName: source.browserName },
32129
- ...source.browserMajor === void 0 ? {} : { browserMajor: source.browserMajor },
32130
- ...input.browserBrand === void 0 ? {} : { browserBrand: input.browserBrand },
32131
- ...input.captureMethod === void 0 ? {} : { captureMethod: input.captureMethod },
32132
- platform: normalizePlatform(process.platform),
32133
- capturedAt: Date.now()
32134
- },
32135
- cookies: prepared.cookies
32136
- };
32137
- } finally {
32138
- await browser.close().catch(() => void 0);
32139
- }
32140
- }
32141
- async function encodePortableBrowserProfileSnapshot(snapshot) {
32142
- return gzip(Buffer.from(JSON.stringify(snapshot), "utf8"));
32143
- }
32144
- function parseSnapshotSource(value) {
32145
- if (!value) {
32146
- return {};
32147
- }
32148
- const trimmed = value.trim();
32149
- const browserName = trimmed.split("/")[0]?.trim() || void 0;
32150
- const majorMatch = trimmed.match(/(\d+)/);
32151
- return {
32152
- ...browserName === void 0 ? {} : { browserName },
32153
- ...majorMatch?.[1] === void 0 ? {} : { browserMajor: majorMatch[1] }
32154
- };
32155
- }
32156
- function normalizePlatform(platform) {
32157
- if (platform === "darwin") return "macos";
32158
- if (platform === "win32") return "windows";
32159
- return platform;
32160
- }
32161
-
32162
32962
  // src/cloud/profile-sync.ts
32963
+ var gzip = util.promisify(zlib.gzip);
32163
32964
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
32164
32965
  var DEFAULT_POLL_TIMEOUT_MS = 5 * 6e4;
32165
32966
  async function syncBrowserProfileCookies(client, input) {
32166
- const resolved = await resolveCookieCaptureStrategy({
32167
- ...input.attachEndpoint === void 0 ? {} : { attachEndpoint: input.attachEndpoint },
32967
+ const result = await readBrowserCookies({
32168
32968
  ...input.brandId === void 0 ? {} : { brandId: input.brandId },
32169
32969
  ...input.userDataDir === void 0 ? {} : { userDataDir: input.userDataDir },
32170
- ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory },
32171
- ...input.executablePath === void 0 ? {} : { executablePath: input.executablePath },
32172
- ...input.strategy === void 0 ? {} : { strategy: input.strategy },
32173
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32970
+ ...input.profileDirectory === void 0 ? {} : { profileDirectory: input.profileDirectory }
32174
32971
  });
32175
- const shouldRestoreBrowser = (input.restoreBrowser ?? true) && resolved.strategy === "managed-relaunch";
32176
- let captureSource;
32177
- try {
32178
- captureSource = await acquireCdpEndpoint(resolved);
32179
- const snapshot = await capturePortableBrowserProfileSnapshot({
32180
- attachEndpoint: captureSource.cdpEndpoint,
32181
- ...captureSource.brandId === void 0 ? {} : { browserBrand: captureSource.brandId },
32182
- captureMethod: captureSource.strategy,
32183
- ...input.domains === void 0 ? {} : { domains: input.domains },
32184
- ...input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs }
32185
- });
32186
- const payload = await encodePortableBrowserProfileSnapshot(snapshot);
32187
- const created = await client.createBrowserProfileImport({
32188
- profileId: input.profileId
32189
- });
32190
- if (payload.length > created.maxUploadBytes) {
32191
- throw new Error(
32192
- `Compressed cookie snapshot is ${String(payload.length)} bytes, exceeding the ${String(created.maxUploadBytes)} byte upload limit.`
32193
- );
32194
- }
32195
- const uploaded = await client.uploadBrowserProfileImportPayload({
32196
- uploadUrl: created.uploadUrl,
32197
- payload
32198
- });
32199
- return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
32200
- } finally {
32201
- await captureSource?.cleanup().catch(() => void 0);
32202
- if (shouldRestoreBrowser && resolved.executablePath !== void 0) {
32203
- relaunchBrowserNormally(resolved.executablePath);
32204
- }
32972
+ const prepared = prepareBrowserProfileSyncCookies({
32973
+ cookies: result.cookies,
32974
+ ...input.domains === void 0 ? {} : { domains: input.domains }
32975
+ });
32976
+ if (prepared.cookies.length === 0) {
32977
+ throw new Error("No syncable cookies found for the selected browser and scope.");
32978
+ }
32979
+ const snapshot = {
32980
+ version: "portable-cookies-v1",
32981
+ source: {
32982
+ browserFamily: "chromium",
32983
+ browserBrand: result.brandId,
32984
+ captureMethod: "sqlite",
32985
+ platform: normalizePlatform(process.platform),
32986
+ capturedAt: Date.now()
32987
+ },
32988
+ cookies: prepared.cookies
32989
+ };
32990
+ const payload = await gzip(Buffer.from(JSON.stringify(snapshot), "utf8"));
32991
+ const created = await client.createBrowserProfileImport({
32992
+ profileId: input.profileId
32993
+ });
32994
+ if (payload.length > created.maxUploadBytes) {
32995
+ throw new Error(
32996
+ `Compressed cookie snapshot is ${String(payload.length)} bytes, exceeding the ${String(created.maxUploadBytes)} byte upload limit.`
32997
+ );
32205
32998
  }
32999
+ const uploaded = await client.uploadBrowserProfileImportPayload({
33000
+ uploadUrl: created.uploadUrl,
33001
+ payload
33002
+ });
33003
+ return uploaded.status === "ready" ? uploaded : waitForBrowserProfileImport(client, created.importId);
32206
33004
  }
32207
33005
  async function waitForBrowserProfileImport(client, importId) {
32208
33006
  const deadline = Date.now() + DEFAULT_POLL_TIMEOUT_MS;
@@ -32214,12 +33012,17 @@ async function waitForBrowserProfileImport(client, importId) {
32214
33012
  if (current.status === "failed") {
32215
33013
  throw new Error(current.error ?? "Browser profile sync failed.");
32216
33014
  }
32217
- await sleep5(DEFAULT_POLL_INTERVAL_MS);
33015
+ await sleep4(DEFAULT_POLL_INTERVAL_MS);
32218
33016
  }
32219
33017
  throw new Error(`Timed out waiting for browser profile sync "${importId}" to finish.`);
32220
33018
  }
32221
- async function sleep5(ms) {
32222
- await new Promise((resolve5) => setTimeout(resolve5, ms));
33019
+ function normalizePlatform(platform) {
33020
+ if (platform === "darwin") return "macos";
33021
+ if (platform === "win32") return "windows";
33022
+ return platform;
33023
+ }
33024
+ async function sleep4(ms) {
33025
+ await new Promise((resolve4) => setTimeout(resolve4, ms));
32223
33026
  }
32224
33027
 
32225
33028
  // src/cloud/client.ts
@@ -32239,7 +33042,12 @@ var OpensteerCloudClient = class {
32239
33042
  ...input.name === void 0 ? {} : { name: input.name },
32240
33043
  ...input.browser === void 0 ? {} : { browser: input.browser },
32241
33044
  ...input.context === void 0 ? {} : { context: input.context },
32242
- ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile }
33045
+ ...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
33046
+ ...input.observability === void 0 ? {} : { observability: input.observability },
33047
+ ...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
33048
+ ...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
33049
+ ...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
33050
+ ...input.locality === void 0 ? {} : { locality: input.locality }
32243
33051
  }
32244
33052
  });
32245
33053
  return await response.json();
@@ -32417,8 +33225,8 @@ var OpensteerCloudClient = class {
32417
33225
  }
32418
33226
  };
32419
33227
  function delay(ms) {
32420
- return new Promise((resolve5) => {
32421
- setTimeout(resolve5, ms);
33228
+ return new Promise((resolve4) => {
33229
+ setTimeout(resolve4, ms);
32422
33230
  });
32423
33231
  }
32424
33232
  function wrapCloudFetchError(error, input) {
@@ -32441,17 +33249,17 @@ function wrapCloudFetchError(error, input) {
32441
33249
  function resolveCloudConfig(input = {}) {
32442
33250
  const provider = resolveOpensteerProvider({
32443
33251
  ...input.provider === void 0 ? {} : { provider: input.provider },
32444
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
33252
+ ...input.environment?.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: input.environment.OPENSTEER_PROVIDER }
32445
33253
  });
32446
33254
  if (provider.mode !== "cloud") {
32447
33255
  return void 0;
32448
33256
  }
32449
33257
  const cloudProvider = input.provider?.mode === "cloud" ? input.provider : void 0;
32450
- const apiKey = cloudProvider?.apiKey ?? process.env.OPENSTEER_API_KEY;
33258
+ const apiKey = cloudProvider?.apiKey ?? input.environment?.OPENSTEER_API_KEY;
32451
33259
  if (!apiKey || apiKey.trim().length === 0) {
32452
33260
  throw new Error("provider=cloud requires OPENSTEER_API_KEY or provider.apiKey.");
32453
33261
  }
32454
- const baseUrl = cloudProvider?.baseUrl ?? process.env.OPENSTEER_BASE_URL;
33262
+ const baseUrl = cloudProvider?.baseUrl ?? input.environment?.OPENSTEER_BASE_URL;
32455
33263
  if (!baseUrl || baseUrl.trim().length === 0) {
32456
33264
  throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
32457
33265
  }
@@ -32476,6 +33284,9 @@ var OpensteerSemanticRestClient = class {
32476
33284
  this.connection = connection;
32477
33285
  }
32478
33286
  async invoke(operation, input) {
33287
+ return this.invokeInternal(operation, input, false);
33288
+ }
33289
+ async invokeInternal(operation, input, hasRetried) {
32479
33290
  const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
32480
33291
  if (!endpoint) {
32481
33292
  throw new Error(`unsupported semantic operation ${operation}`);
@@ -32485,7 +33296,7 @@ var OpensteerSemanticRestClient = class {
32485
33296
  });
32486
33297
  let response;
32487
33298
  try {
32488
- response = await fetch(`${this.connection.baseUrl}${endpoint.path}`, {
33299
+ response = await fetch(`${await this.connection.getBaseUrl()}${endpoint.path}`, {
32489
33300
  method: "POST",
32490
33301
  headers: {
32491
33302
  authorization: await this.connection.getAuthorizationHeader(),
@@ -32507,6 +33318,9 @@ var OpensteerSemanticRestClient = class {
32507
33318
  }
32508
33319
  return envelope.data;
32509
33320
  } catch (error) {
33321
+ if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
33322
+ return this.invokeInternal(operation, input, true);
33323
+ }
32510
33324
  if (operation === "session.close" && isFetchFailure(error)) {
32511
33325
  return { closed: true };
32512
33326
  }
@@ -32553,9 +33367,9 @@ var OpensteerCloudAutomationClient = class {
32553
33367
  sentAt: Date.now(),
32554
33368
  ...input === void 0 ? {} : { input }
32555
33369
  };
32556
- return new Promise((resolve5, reject) => {
33370
+ return new Promise((resolve4, reject) => {
32557
33371
  this.pending.set(requestId, {
32558
- resolve: (value) => resolve5(value),
33372
+ resolve: (value) => resolve4(value),
32559
33373
  reject
32560
33374
  });
32561
33375
  try {
@@ -32618,8 +33432,8 @@ var OpensteerCloudAutomationClient = class {
32618
33432
  pending.reject(new Error(`automation connection closed before ${requestId} completed`));
32619
33433
  }
32620
33434
  this.pending.clear();
32621
- await new Promise((resolve5) => {
32622
- socket.once("close", () => resolve5());
33435
+ await new Promise((resolve4) => {
33436
+ socket.once("close", () => resolve4());
32623
33437
  socket.close();
32624
33438
  }).catch(() => void 0);
32625
33439
  }
@@ -32640,7 +33454,10 @@ var OpensteerCloudAutomationClient = class {
32640
33454
  }
32641
33455
  async connect() {
32642
33456
  const grant = await this.issueGrant("automation");
32643
- const wsUrl = new URL(grant.wsUrl);
33457
+ if (grant.transport !== "ws") {
33458
+ throw new Error(`cloud issued an invalid ${grant.kind} grant transport`);
33459
+ }
33460
+ const wsUrl = new URL(grant.url);
32644
33461
  wsUrl.searchParams.set("token", grant.token);
32645
33462
  const socket = new WebSocket2__default.default(wsUrl);
32646
33463
  this.socket = socket;
@@ -32661,8 +33478,8 @@ var OpensteerCloudAutomationClient = class {
32661
33478
  }
32662
33479
  this.pending.clear();
32663
33480
  });
32664
- await new Promise((resolve5, reject) => {
32665
- socket.once("open", () => resolve5());
33481
+ await new Promise((resolve4, reject) => {
33482
+ socket.once("open", () => resolve4());
32666
33483
  socket.once("error", reject);
32667
33484
  });
32668
33485
  this.send({
@@ -33003,16 +33820,18 @@ var CloudSessionProxy = class {
33003
33820
  workspace;
33004
33821
  cleanupRootOnClose;
33005
33822
  cloud;
33823
+ observability;
33006
33824
  sessionId;
33007
- sessionBaseUrl;
33825
+ semanticGrant;
33008
33826
  client;
33009
33827
  automation;
33010
33828
  workspaceStore;
33011
33829
  constructor(cloud, options = {}) {
33012
33830
  this.cloud = cloud;
33013
33831
  this.workspace = options.workspace;
33014
- this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path6__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
33015
- rootDir: path6__default.default.resolve(options.rootDir ?? process.cwd()),
33832
+ this.observability = options.observability;
33833
+ this.rootPath = options.rootPath ?? (this.workspace === void 0 ? path7__default.default.join(os.tmpdir(), `${TEMPORARY_CLOUD_WORKSPACE_PREFIX}${crypto.randomUUID()}`) : resolveFilesystemWorkspacePath({
33834
+ rootDir: path7__default.default.resolve(options.rootDir ?? process.cwd()),
33016
33835
  workspace: this.workspace
33017
33836
  }));
33018
33837
  this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.workspace === void 0;
@@ -33054,7 +33873,7 @@ var CloudSessionProxy = class {
33054
33873
  reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
33055
33874
  capabilities: {
33056
33875
  semanticOperations: opensteerSemanticOperationNames,
33057
- sessionGrants: ["automation", "view", "cdp"],
33876
+ sessionGrants: ["semantic", "automation", "view", "cdp"],
33058
33877
  instrumentation: {
33059
33878
  route: true,
33060
33879
  interceptScript: true,
@@ -33296,13 +34115,12 @@ var CloudSessionProxy = class {
33296
34115
  return this.requireClient().invoke("computer.execute", input);
33297
34116
  }
33298
34117
  async close() {
33299
- const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 || this.sessionBaseUrl === void 0 ? void 0 : {
34118
+ const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
33300
34119
  layout: "opensteer-session",
33301
34120
  version: 1,
33302
34121
  provider: "cloud",
33303
34122
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
33304
34123
  sessionId: this.sessionId,
33305
- baseUrl: this.sessionBaseUrl,
33306
34124
  startedAt: Date.now(),
33307
34125
  updatedAt: Date.now()
33308
34126
  });
@@ -33321,7 +34139,7 @@ var CloudSessionProxy = class {
33321
34139
  this.automation = void 0;
33322
34140
  this.client = void 0;
33323
34141
  this.sessionId = void 0;
33324
- this.sessionBaseUrl = void 0;
34142
+ this.semanticGrant = void 0;
33325
34143
  if (this.cleanupRootOnClose) {
33326
34144
  await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
33327
34145
  }
@@ -33337,38 +34155,56 @@ var CloudSessionProxy = class {
33337
34155
  await this.automation?.close().catch(() => void 0);
33338
34156
  this.automation = void 0;
33339
34157
  this.sessionId = void 0;
33340
- this.sessionBaseUrl = void 0;
34158
+ this.semanticGrant = void 0;
33341
34159
  }
33342
34160
  async ensureSession(input = {}) {
33343
34161
  if (this.client) {
33344
34162
  return;
33345
34163
  }
33346
34164
  assertSupportedCloudBrowserMode(input.browser);
34165
+ const localCloud = this.shouldUseLocalCloudTransport();
34166
+ const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
33347
34167
  const persisted = await this.loadPersistedSession();
33348
34168
  if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
33349
- await this.syncRegistryToCloud();
34169
+ if (localCloud) {
34170
+ void this.syncRegistryToCloud();
34171
+ } else {
34172
+ await this.syncRegistryToCloud();
34173
+ }
33350
34174
  this.bindClient(persisted);
33351
34175
  return;
33352
34176
  }
33353
- await this.syncRegistryToCloud();
33354
- const session = await this.cloud.createSession({
34177
+ if (localCloud) {
34178
+ void this.syncRegistryToCloud();
34179
+ } else {
34180
+ await this.syncRegistryToCloud();
34181
+ }
34182
+ const baseCreateInput = {
33355
34183
  ...this.workspace === void 0 ? {} : { name: this.workspace },
33356
34184
  ...input.launch === void 0 ? {} : { browser: input.launch },
33357
34185
  ...input.context === void 0 ? {} : { context: input.context },
33358
- ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
33359
- });
34186
+ ...this.observability === void 0 ? {} : { observability: this.observability },
34187
+ ...browserProfile === void 0 ? {} : { browserProfile }
34188
+ };
34189
+ const createInput = localCloud && this.workspace !== void 0 ? {
34190
+ ...baseCreateInput,
34191
+ sourceType: "local-cloud",
34192
+ sourceRef: this.workspace,
34193
+ localWorkspaceRootPath: this.rootPath,
34194
+ locality: "auto"
34195
+ } : baseCreateInput;
34196
+ const session = await this.cloud.createSession(createInput);
33360
34197
  const record = {
33361
34198
  layout: "opensteer-session",
33362
34199
  version: 1,
33363
34200
  provider: "cloud",
33364
34201
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
33365
34202
  sessionId: session.sessionId,
33366
- baseUrl: session.baseUrl,
33367
34203
  startedAt: Date.now(),
33368
34204
  updatedAt: Date.now()
33369
34205
  };
33370
34206
  await this.writePersistedSession(record);
33371
- this.bindClient(record);
34207
+ this.bindClient(record, session.initialGrants?.semantic);
33372
34208
  }
33373
34209
  async syncRegistryToCloud() {
33374
34210
  if (this.workspace === void 0) {
@@ -33377,15 +34213,16 @@ var CloudSessionProxy = class {
33377
34213
  try {
33378
34214
  const workspaceStore = await this.ensureWorkspaceStore();
33379
34215
  await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
33380
- } catch (error) {
34216
+ } catch {
33381
34217
  }
33382
34218
  }
33383
- bindClient(record) {
34219
+ bindClient(record, initialSemanticGrant) {
33384
34220
  this.sessionId = record.sessionId;
33385
- this.sessionBaseUrl = record.baseUrl;
34221
+ this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
33386
34222
  this.client = new OpensteerSemanticRestClient({
33387
- baseUrl: record.baseUrl,
33388
- getAuthorizationHeader: async () => this.cloud.buildAuthorizationHeader()
34223
+ getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
34224
+ getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
34225
+ handleError: (error) => this.handleSemanticClientError(error)
33389
34226
  });
33390
34227
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
33391
34228
  }
@@ -33434,6 +34271,43 @@ var CloudSessionProxy = class {
33434
34271
  }
33435
34272
  return this.automation;
33436
34273
  }
34274
+ async ensureSemanticGrant(forceRefresh = false) {
34275
+ if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
34276
+ return this.semanticGrant;
34277
+ }
34278
+ if (!this.sessionId) {
34279
+ throw new Error("Cloud session has not been initialized.");
34280
+ }
34281
+ const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
34282
+ const grant = issued.grants.semantic;
34283
+ if (!grant || grant.transport !== "http") {
34284
+ throw new Error("cloud did not issue a valid semantic grant");
34285
+ }
34286
+ this.semanticGrant = grant;
34287
+ return grant;
34288
+ }
34289
+ async handleSemanticClientError(error) {
34290
+ if (!(error instanceof OpensteerSemanticRestError)) {
34291
+ return false;
34292
+ }
34293
+ if (error.statusCode !== 401 && error.statusCode !== 404) {
34294
+ return false;
34295
+ }
34296
+ this.semanticGrant = void 0;
34297
+ try {
34298
+ await this.ensureSemanticGrant(true);
34299
+ return true;
34300
+ } catch {
34301
+ return false;
34302
+ }
34303
+ }
34304
+ shouldUseLocalCloudTransport() {
34305
+ if (this.workspace === void 0) {
34306
+ return false;
34307
+ }
34308
+ const config = this.cloud.getConfig();
34309
+ return isLoopbackBaseUrl(config.baseUrl);
34310
+ }
33437
34311
  };
33438
34312
  function resolveCloudBrowserProfile(cloud, input) {
33439
34313
  return input.browserProfile ?? cloud.getConfig().browserProfile;
@@ -33449,19 +34323,41 @@ function assertSupportedCloudBrowserMode(browser) {
33449
34323
  function isMissingCloudSessionError(error) {
33450
34324
  return error instanceof Error && /\b404\b/.test(error.message);
33451
34325
  }
34326
+ function isLoopbackBaseUrl(baseUrl) {
34327
+ let url;
34328
+ try {
34329
+ url = new URL(baseUrl);
34330
+ } catch {
34331
+ return false;
34332
+ }
34333
+ return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
34334
+ }
33452
34335
 
33453
34336
  // src/sdk/runtime-resolution.ts
34337
+ var LOCAL_ONLY_RUNTIME_OPTION_KEYS = [
34338
+ "launch",
34339
+ "context",
34340
+ "engine",
34341
+ "engineFactory",
34342
+ "policy",
34343
+ "descriptorStore",
34344
+ "extractionDescriptorStore",
34345
+ "registryOverrides",
34346
+ "observationSessionId",
34347
+ "observationSink"
34348
+ ];
33454
34349
  function resolveOpensteerRuntimeConfig(input = {}) {
34350
+ const environment = input.environment ?? process.env;
33455
34351
  const provider = resolveOpensteerProvider({
33456
34352
  ...input.provider === void 0 ? {} : { provider: input.provider },
33457
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34353
+ ...environment.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: environment.OPENSTEER_PROVIDER }
33458
34354
  });
33459
34355
  if (provider.mode === "cloud") {
33460
34356
  return {
33461
34357
  provider,
33462
34358
  cloud: resolveCloudConfig({
33463
34359
  ...input.provider === void 0 ? {} : { provider: input.provider },
33464
- ...input.environmentProvider === void 0 ? {} : { environmentProvider: input.environmentProvider }
34360
+ environment
33465
34361
  })
33466
34362
  };
33467
34363
  }
@@ -33472,15 +34368,23 @@ function createOpensteerSemanticRuntime(input = {}) {
33472
34368
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
33473
34369
  const config = resolveOpensteerRuntimeConfig({
33474
34370
  ...input.provider === void 0 ? {} : { provider: input.provider },
33475
- ...process.env.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: process.env.OPENSTEER_PROVIDER }
34371
+ ...input.environment === void 0 ? {} : { environment: input.environment }
33476
34372
  });
33477
- assertProviderSupportsEngine(config.provider.mode, engine);
33478
- if (config.provider.mode === "cloud") {
34373
+ const localOnlyRuntimeOptions = listLocalOnlyRuntimeOptions(runtimeOptions);
34374
+ const providerMode = config.provider.mode === "cloud" && config.provider.source !== "explicit" && localOnlyRuntimeOptions.length > 0 ? "local" : config.provider.mode;
34375
+ if (config.provider.mode === "cloud" && config.provider.source === "explicit" && localOnlyRuntimeOptions.length > 0) {
34376
+ throw new Error(
34377
+ `provider=cloud does not support local runtime options: ${localOnlyRuntimeOptions.join(", ")}.`
34378
+ );
34379
+ }
34380
+ assertProviderSupportsEngine(providerMode, engine);
34381
+ if (providerMode === "cloud") {
33479
34382
  return new CloudSessionProxy(new OpensteerCloudClient(config.cloud), {
33480
34383
  ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
33481
34384
  ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
33482
34385
  ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
33483
- ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose }
34386
+ ...runtimeOptions.cleanupRootOnClose === void 0 ? {} : { cleanupRootOnClose: runtimeOptions.cleanupRootOnClose },
34387
+ ...runtimeOptions.observability === void 0 ? {} : { observability: runtimeOptions.observability }
33484
34388
  });
33485
34389
  }
33486
34390
  return new OpensteerRuntime({
@@ -33488,6 +34392,9 @@ function createOpensteerSemanticRuntime(input = {}) {
33488
34392
  engineName: engine
33489
34393
  });
33490
34394
  }
34395
+ function listLocalOnlyRuntimeOptions(runtimeOptions) {
34396
+ return LOCAL_ONLY_RUNTIME_OPTION_KEYS.filter((key) => runtimeOptions[key] !== void 0);
34397
+ }
33491
34398
 
33492
34399
  // src/sdk/opensteer.ts
33493
34400
  var Opensteer = class {
@@ -33495,16 +34402,18 @@ var Opensteer = class {
33495
34402
  browserManager;
33496
34403
  browser;
33497
34404
  constructor(options = {}) {
34405
+ const environment = resolveOpensteerEnvironment(options.rootDir);
33498
34406
  const { provider, engineName, ...runtimeOptions } = options;
33499
34407
  const runtimeConfig = resolveOpensteerRuntimeConfig({
33500
34408
  ...provider === void 0 ? {} : { provider },
33501
- ...process.env.OPENSTEER_PROVIDER === void 0 ? {} : { environmentProvider: process.env.OPENSTEER_PROVIDER }
34409
+ environment
33502
34410
  });
33503
34411
  if (runtimeConfig.provider.mode === "cloud") {
33504
34412
  this.browserManager = void 0;
33505
34413
  this.runtime = createOpensteerSemanticRuntime({
33506
34414
  ...provider === void 0 ? {} : { provider },
33507
34415
  ...engineName === void 0 ? {} : { engine: engineName },
34416
+ environment,
33508
34417
  runtimeOptions: {
33509
34418
  ...runtimeOptions
33510
34419
  }
@@ -33524,6 +34433,7 @@ var Opensteer = class {
33524
34433
  this.runtime = createOpensteerSemanticRuntime({
33525
34434
  ...provider === void 0 ? {} : { provider },
33526
34435
  ...engineName === void 0 ? {} : { engine: engineName },
34436
+ environment,
33527
34437
  runtimeOptions: {
33528
34438
  ...runtimeOptions,
33529
34439
  rootPath: this.browserManager.rootPath,
@@ -33865,7 +34775,7 @@ function normalizeTargetOptions(input) {
33865
34775
  };
33866
34776
  }
33867
34777
  function delay2(ms) {
33868
- return new Promise((resolve5) => setTimeout(resolve5, ms));
34778
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
33869
34779
  }
33870
34780
 
33871
34781
  exports.CloudSessionProxy = CloudSessionProxy;
@@ -33896,8 +34806,11 @@ exports.clearPersistedSessionRecord = clearPersistedSessionRecord;
33896
34806
  exports.cloneElementPath = cloneElementPath;
33897
34807
  exports.cloneReplayElementPath = cloneReplayElementPath;
33898
34808
  exports.cloneStructuralElementAnchor = cloneStructuralElementAnchor;
34809
+ exports.createArtifactStore = createArtifactStore;
34810
+ exports.createDomDescriptorStore = createDomDescriptorStore;
33899
34811
  exports.createDomRuntime = createDomRuntime;
33900
34812
  exports.createFilesystemOpensteerWorkspace = createFilesystemOpensteerWorkspace;
34813
+ exports.createObservationStore = createObservationStore;
33901
34814
  exports.createOpensteerExtractionDescriptorStore = createOpensteerExtractionDescriptorStore;
33902
34815
  exports.createOpensteerSemanticRuntime = createOpensteerSemanticRuntime;
33903
34816
  exports.defaultFallbackPolicy = defaultFallbackPolicy;
@@ -33913,7 +34826,9 @@ exports.inspectCdpEndpoint = inspectCdpEndpoint;
33913
34826
  exports.isCurrentUrlField = isCurrentUrlField;
33914
34827
  exports.isValidCssAttributeKey = isValidCssAttributeKey;
33915
34828
  exports.listLocalChromeProfiles = listLocalChromeProfiles;
34829
+ exports.manifestToExternalBinaryLocation = manifestToExternalBinaryLocation;
33916
34830
  exports.normalizeExtractedValue = normalizeExtractedValue;
34831
+ exports.normalizeObservabilityConfig = normalizeObservabilityConfig;
33917
34832
  exports.normalizeOpensteerEngineName = normalizeOpensteerEngineName;
33918
34833
  exports.normalizeOpensteerProviderMode = normalizeOpensteerProviderMode;
33919
34834
  exports.normalizeWorkspaceId = normalizeWorkspaceId;