opensteer 0.8.0 → 0.8.3

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
@@ -16,6 +16,7 @@ var cheerio = require('cheerio');
16
16
  var prettier = require('prettier');
17
17
  var vm = require('vm');
18
18
  var zlib = require('zlib');
19
+ var WebSocket2 = require('ws');
19
20
 
20
21
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
21
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -43,10 +44,11 @@ var sharp__default = /*#__PURE__*/_interopDefault(sharp);
43
44
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
44
45
  var prettier__namespace = /*#__PURE__*/_interopNamespace(prettier);
45
46
  var vm__default = /*#__PURE__*/_interopDefault(vm);
47
+ var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
46
48
 
47
- // src/root.ts
49
+ // ../runtime-core/src/root.ts
48
50
 
49
- // src/json.ts
51
+ // ../runtime-core/src/json.ts
50
52
  function isPlainObject(value) {
51
53
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
52
54
  return false;
@@ -54,30 +56,30 @@ function isPlainObject(value) {
54
56
  const prototype = Object.getPrototypeOf(value);
55
57
  return prototype === Object.prototype || prototype === null;
56
58
  }
57
- function canonicalizeJsonValue(value, path11) {
59
+ function canonicalizeJsonValue(value, path13) {
58
60
  if (value === null || typeof value === "string" || typeof value === "boolean") {
59
61
  return value;
60
62
  }
61
63
  if (typeof value === "number") {
62
64
  if (!Number.isFinite(value)) {
63
- throw new TypeError(`${path11} must be a finite JSON number`);
65
+ throw new TypeError(`${path13} must be a finite JSON number`);
64
66
  }
65
67
  return value;
66
68
  }
67
69
  if (Array.isArray(value)) {
68
- return value.map((entry, index) => canonicalizeJsonValue(entry, `${path11}[${index}]`));
70
+ return value.map((entry, index) => canonicalizeJsonValue(entry, `${path13}[${index}]`));
69
71
  }
70
72
  if (!isPlainObject(value)) {
71
- throw new TypeError(`${path11} must be a plain JSON object`);
73
+ throw new TypeError(`${path13} must be a plain JSON object`);
72
74
  }
73
75
  const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
74
76
  const result = {};
75
77
  for (const key of sorted) {
76
78
  const entry = value[key];
77
79
  if (entry === void 0) {
78
- throw new TypeError(`${path11}.${key} must not be undefined`);
80
+ throw new TypeError(`${path13}.${key} must not be undefined`);
79
81
  }
80
- result[key] = canonicalizeJsonValue(entry, `${path11}.${key}`);
82
+ result[key] = canonicalizeJsonValue(entry, `${path13}.${key}`);
81
83
  }
82
84
  return result;
83
85
  }
@@ -92,7 +94,7 @@ function stableJsonString(value) {
92
94
  `;
93
95
  }
94
96
 
95
- // src/internal/filesystem.ts
97
+ // ../runtime-core/src/internal/filesystem.ts
96
98
  var LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
97
99
  function normalizeNonEmptyString(name, value) {
98
100
  const normalized = value.trim();
@@ -220,7 +222,7 @@ async function withFilesystemLock(lockPath, task) {
220
222
  }
221
223
  }
222
224
 
223
- // src/artifacts.ts
225
+ // ../runtime-core/src/artifacts.ts
224
226
  function normalizeScope(scope) {
225
227
  if (scope === void 0) {
226
228
  return {};
@@ -574,31 +576,31 @@ function oneOfSchema(members, options = {}) {
574
576
  }
575
577
 
576
578
  // ../protocol/src/validation.ts
577
- function validateJsonSchema(schema, value, path11 = "$") {
578
- return validateSchemaNode(schema, value, path11);
579
+ function validateJsonSchema(schema, value, path13 = "$") {
580
+ return validateSchemaNode(schema, value, path13);
579
581
  }
580
- function validateSchemaNode(schema, value, path11) {
582
+ function validateSchemaNode(schema, value, path13) {
581
583
  const issues = [];
582
584
  if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
583
585
  issues.push({
584
- path: path11,
586
+ path: path13,
585
587
  message: `must equal ${JSON.stringify(schema.const)}`
586
588
  });
587
589
  return issues;
588
590
  }
589
591
  if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
590
592
  issues.push({
591
- path: path11,
593
+ path: path13,
592
594
  message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
593
595
  });
594
596
  return issues;
595
597
  }
596
598
  if (schema.oneOf !== void 0) {
597
- const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path11));
599
+ const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path13));
598
600
  const validBranches = branchIssues.filter((current) => current.length === 0).length;
599
601
  if (validBranches !== 1) {
600
602
  issues.push({
601
- path: path11,
603
+ path: path13,
602
604
  message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
603
605
  });
604
606
  return issues;
@@ -606,11 +608,11 @@ function validateSchemaNode(schema, value, path11) {
606
608
  }
607
609
  if (schema.anyOf !== void 0) {
608
610
  const hasMatch = schema.anyOf.some(
609
- (member) => validateSchemaNode(member, value, path11).length === 0
611
+ (member) => validateSchemaNode(member, value, path13).length === 0
610
612
  );
611
613
  if (!hasMatch) {
612
614
  issues.push({
613
- path: path11,
615
+ path: path13,
614
616
  message: "must match at least one supported shape"
615
617
  });
616
618
  return issues;
@@ -618,7 +620,7 @@ function validateSchemaNode(schema, value, path11) {
618
620
  }
619
621
  if (schema.allOf !== void 0) {
620
622
  for (const member of schema.allOf) {
621
- issues.push(...validateSchemaNode(member, value, path11));
623
+ issues.push(...validateSchemaNode(member, value, path13));
622
624
  }
623
625
  if (issues.length > 0) {
624
626
  return issues;
@@ -626,7 +628,7 @@ function validateSchemaNode(schema, value, path11) {
626
628
  }
627
629
  if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
628
630
  issues.push({
629
- path: path11,
631
+ path: path13,
630
632
  message: `must be ${describeSchemaType(schema.type)}`
631
633
  });
632
634
  return issues;
@@ -634,19 +636,19 @@ function validateSchemaNode(schema, value, path11) {
634
636
  if (typeof value === "string") {
635
637
  if (schema.minLength !== void 0 && value.length < schema.minLength) {
636
638
  issues.push({
637
- path: path11,
639
+ path: path13,
638
640
  message: `must have length >= ${String(schema.minLength)}`
639
641
  });
640
642
  }
641
643
  if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
642
644
  issues.push({
643
- path: path11,
645
+ path: path13,
644
646
  message: `must have length <= ${String(schema.maxLength)}`
645
647
  });
646
648
  }
647
649
  if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
648
650
  issues.push({
649
- path: path11,
651
+ path: path13,
650
652
  message: `must match pattern ${schema.pattern}`
651
653
  });
652
654
  }
@@ -655,25 +657,25 @@ function validateSchemaNode(schema, value, path11) {
655
657
  if (typeof value === "number") {
656
658
  if (schema.minimum !== void 0 && value < schema.minimum) {
657
659
  issues.push({
658
- path: path11,
660
+ path: path13,
659
661
  message: `must be >= ${String(schema.minimum)}`
660
662
  });
661
663
  }
662
664
  if (schema.maximum !== void 0 && value > schema.maximum) {
663
665
  issues.push({
664
- path: path11,
666
+ path: path13,
665
667
  message: `must be <= ${String(schema.maximum)}`
666
668
  });
667
669
  }
668
670
  if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
669
671
  issues.push({
670
- path: path11,
672
+ path: path13,
671
673
  message: `must be > ${String(schema.exclusiveMinimum)}`
672
674
  });
673
675
  }
674
676
  if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
675
677
  issues.push({
676
- path: path11,
678
+ path: path13,
677
679
  message: `must be < ${String(schema.exclusiveMaximum)}`
678
680
  });
679
681
  }
@@ -682,13 +684,13 @@ function validateSchemaNode(schema, value, path11) {
682
684
  if (Array.isArray(value)) {
683
685
  if (schema.minItems !== void 0 && value.length < schema.minItems) {
684
686
  issues.push({
685
- path: path11,
687
+ path: path13,
686
688
  message: `must have at least ${String(schema.minItems)} items`
687
689
  });
688
690
  }
689
691
  if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
690
692
  issues.push({
691
- path: path11,
693
+ path: path13,
692
694
  message: `must have at most ${String(schema.maxItems)} items`
693
695
  });
694
696
  }
@@ -698,7 +700,7 @@ function validateSchemaNode(schema, value, path11) {
698
700
  const key = JSON.stringify(item);
699
701
  if (seen.has(key)) {
700
702
  issues.push({
701
- path: path11,
703
+ path: path13,
702
704
  message: "must not contain duplicate items"
703
705
  });
704
706
  break;
@@ -708,7 +710,7 @@ function validateSchemaNode(schema, value, path11) {
708
710
  }
709
711
  if (schema.items !== void 0) {
710
712
  for (let index = 0; index < value.length; index += 1) {
711
- issues.push(...validateSchemaNode(schema.items, value[index], `${path11}[${String(index)}]`));
713
+ issues.push(...validateSchemaNode(schema.items, value[index], `${path13}[${String(index)}]`));
712
714
  }
713
715
  }
714
716
  return issues;
@@ -718,7 +720,7 @@ function validateSchemaNode(schema, value, path11) {
718
720
  for (const requiredKey of schema.required ?? []) {
719
721
  if (!(requiredKey in value)) {
720
722
  issues.push({
721
- path: joinObjectPath(path11, requiredKey),
723
+ path: joinObjectPath(path13, requiredKey),
722
724
  message: "is required"
723
725
  });
724
726
  }
@@ -727,13 +729,13 @@ function validateSchemaNode(schema, value, path11) {
727
729
  const propertySchema = properties[key];
728
730
  if (propertySchema !== void 0) {
729
731
  issues.push(
730
- ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path11, key))
732
+ ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path13, key))
731
733
  );
732
734
  continue;
733
735
  }
734
736
  if (schema.additionalProperties === false) {
735
737
  issues.push({
736
- path: joinObjectPath(path11, key),
738
+ path: joinObjectPath(path13, key),
737
739
  message: "is not allowed"
738
740
  });
739
741
  continue;
@@ -743,7 +745,7 @@ function validateSchemaNode(schema, value, path11) {
743
745
  ...validateSchemaNode(
744
746
  schema.additionalProperties,
745
747
  propertyValue,
746
- joinObjectPath(path11, key)
748
+ joinObjectPath(path13, key)
747
749
  )
748
750
  );
749
751
  }
@@ -987,8 +989,8 @@ function matchesNetworkRecordFilters(record, filters) {
987
989
  }
988
990
  }
989
991
  if (filters.path !== void 0) {
990
- const path11 = getParsedUrl().pathname;
991
- if (!includesCaseInsensitive(path11, filters.path)) {
992
+ const path13 = getParsedUrl().pathname;
993
+ if (!includesCaseInsensitive(path13, filters.path)) {
992
994
  return false;
993
995
  }
994
996
  }
@@ -5703,6 +5705,65 @@ var opensteerComputerAnnotationNames = [
5703
5705
  "grid",
5704
5706
  "selected"
5705
5707
  ];
5708
+ var opensteerSemanticOperationNames = [
5709
+ "session.open",
5710
+ "page.list",
5711
+ "page.new",
5712
+ "page.activate",
5713
+ "page.close",
5714
+ "page.goto",
5715
+ "page.evaluate",
5716
+ "page.add-init-script",
5717
+ "page.snapshot",
5718
+ "dom.click",
5719
+ "dom.hover",
5720
+ "dom.input",
5721
+ "dom.scroll",
5722
+ "dom.extract",
5723
+ "network.query",
5724
+ "network.save",
5725
+ "network.clear",
5726
+ "network.minimize",
5727
+ "network.diff",
5728
+ "network.probe",
5729
+ "reverse.discover",
5730
+ "reverse.query",
5731
+ "reverse.package.create",
5732
+ "reverse.package.run",
5733
+ "reverse.export",
5734
+ "reverse.report",
5735
+ "reverse.package.get",
5736
+ "reverse.package.list",
5737
+ "reverse.package.patch",
5738
+ "interaction.capture",
5739
+ "interaction.get",
5740
+ "interaction.diff",
5741
+ "interaction.replay",
5742
+ "artifact.read",
5743
+ "inspect.cookies",
5744
+ "inspect.storage",
5745
+ "scripts.capture",
5746
+ "scripts.beautify",
5747
+ "scripts.deobfuscate",
5748
+ "scripts.sandbox",
5749
+ "captcha.solve",
5750
+ "request.raw",
5751
+ "request-plan.infer",
5752
+ "request-plan.write",
5753
+ "request-plan.get",
5754
+ "request-plan.list",
5755
+ "recipe.write",
5756
+ "recipe.get",
5757
+ "recipe.list",
5758
+ "recipe.run",
5759
+ "auth-recipe.write",
5760
+ "auth-recipe.get",
5761
+ "auth-recipe.list",
5762
+ "auth-recipe.run",
5763
+ "request.execute",
5764
+ "computer.execute",
5765
+ "session.close"
5766
+ ];
5706
5767
  function defineSemanticOperationSpec(spec) {
5707
5768
  return spec;
5708
5769
  }
@@ -7002,7 +7063,7 @@ function resolveDomActionBridge(engine) {
7002
7063
  return isDomActionBridgeFactory(candidate) ? candidate.call(engine) : void 0;
7003
7064
  }
7004
7065
 
7005
- // src/registry.ts
7066
+ // ../runtime-core/src/registry.ts
7006
7067
  function normalizeTags(tags) {
7007
7068
  if (tags === void 0) {
7008
7069
  return [];
@@ -8236,7 +8297,7 @@ function createTraceStore(rootPath, artifacts) {
8236
8297
  return new FilesystemTraceStore(rootPath, artifacts);
8237
8298
  }
8238
8299
 
8239
- // src/root.ts
8300
+ // ../runtime-core/src/root.ts
8240
8301
  var OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT = "opensteer-workspace";
8241
8302
  var OPENSTEER_FILESYSTEM_WORKSPACE_VERSION = 2;
8242
8303
  function normalizeWorkspaceId(workspace) {
@@ -8257,6 +8318,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8257
8318
  const browserManifestPath = path6__default.default.join(browserPath, "manifest.json");
8258
8319
  const browserUserDataDir = path6__default.default.join(browserPath, "user-data");
8259
8320
  const livePath = path6__default.default.join(options.rootPath, "live");
8321
+ const liveSessionPath = path6__default.default.join(livePath, "session.json");
8260
8322
  const liveBrowserPath = path6__default.default.join(livePath, "browser.json");
8261
8323
  const artifactsPath = path6__default.default.join(options.rootPath, "artifacts");
8262
8324
  const tracesPath = path6__default.default.join(options.rootPath, "traces");
@@ -8332,6 +8394,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8332
8394
  browserManifestPath,
8333
8395
  browserUserDataDir,
8334
8396
  livePath,
8397
+ liveSessionPath,
8335
8398
  liveBrowserPath,
8336
8399
  artifactsPath,
8337
8400
  tracesPath,
@@ -8356,7 +8419,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8356
8419
  };
8357
8420
  }
8358
8421
 
8359
- // src/policy/defaults.ts
8422
+ // ../runtime-core/src/policy/defaults.ts
8360
8423
  var DEFAULT_TIMEOUTS = {
8361
8424
  "session.open": 3e4,
8362
8425
  "page.goto": 3e4,
@@ -8439,7 +8502,7 @@ function defaultPolicy() {
8439
8502
  return DEFAULT_POLICY;
8440
8503
  }
8441
8504
 
8442
- // src/policy/settle.ts
8505
+ // ../runtime-core/src/policy/settle.ts
8443
8506
  async function settleWithPolicy(policy, input) {
8444
8507
  for (const observer of policy.observers ?? []) {
8445
8508
  if (await observer.settle(input)) {
@@ -8478,7 +8541,7 @@ function abortError() {
8478
8541
  return error;
8479
8542
  }
8480
8543
 
8481
- // src/policy/timeout.ts
8544
+ // ../runtime-core/src/policy/timeout.ts
8482
8545
  var PolicyTimeoutController = class {
8483
8546
  constructor(input, budgetMs) {
8484
8547
  this.input = input;
@@ -8588,7 +8651,7 @@ function abortError2() {
8588
8651
  return error;
8589
8652
  }
8590
8653
 
8591
- // src/runtimes/dom/match-policy.ts
8654
+ // ../runtime-core/src/runtimes/dom/match-policy.ts
8592
8655
  var ATTRIBUTE_DENY_KEYS = /* @__PURE__ */ new Set([
8593
8656
  "style",
8594
8657
  "nonce",
@@ -8820,7 +8883,7 @@ function getClauseAttributeValue(node, clause) {
8820
8883
  return String(raw);
8821
8884
  }
8822
8885
 
8823
- // src/runtimes/dom/match-selectors.ts
8886
+ // ../runtime-core/src/runtimes/dom/match-selectors.ts
8824
8887
  function buildPathCandidates(domPath) {
8825
8888
  const nodes = Array.isArray(domPath) ? domPath : [];
8826
8889
  if (!nodes.length) {
@@ -8905,7 +8968,7 @@ function buildSuffixCandidates(segments) {
8905
8968
  return out;
8906
8969
  }
8907
8970
 
8908
- // src/runtimes/dom/extraction.ts
8971
+ // ../runtime-core/src/runtimes/dom/extraction.ts
8909
8972
  var URL_LIST_ATTRIBUTES = /* @__PURE__ */ new Set(["srcset", "imagesrcset", "ping"]);
8910
8973
  var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
8911
8974
  "href",
@@ -8917,9 +8980,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
8917
8980
  "poster",
8918
8981
  "ping"
8919
8982
  ]);
8920
- function buildArrayFieldPathCandidates(path11) {
8921
- const strict = path11.nodes.length ? buildPathCandidates(path11.nodes) : [];
8922
- const relaxedNodes = stripPositionClauses(path11.nodes);
8983
+ function buildArrayFieldPathCandidates(path13) {
8984
+ const strict = path13.nodes.length ? buildPathCandidates(path13.nodes) : [];
8985
+ const relaxedNodes = stripPositionClauses(path13.nodes);
8923
8986
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
8924
8987
  return dedupeSelectors([...strict, ...relaxed]);
8925
8988
  }
@@ -9152,7 +9215,7 @@ function readDescriptorToken(value, index) {
9152
9215
  };
9153
9216
  }
9154
9217
 
9155
- // src/runtimes/dom/errors.ts
9218
+ // ../runtime-core/src/runtimes/dom/errors.ts
9156
9219
  var ElementPathError = class extends Error {
9157
9220
  code;
9158
9221
  constructor(code, message) {
@@ -9228,6 +9291,45 @@ var selectorAdapter = {
9228
9291
  },
9229
9292
  equals(left, right) {
9230
9293
  return left === right;
9294
+ },
9295
+ existsOne(test, nodes) {
9296
+ return selectorAdapter.findOne(test, nodes) !== null;
9297
+ },
9298
+ findAll(test, nodes) {
9299
+ const matches = [];
9300
+ const visit = (node) => {
9301
+ if (selectorAdapter.isTag(node) && test(node)) {
9302
+ matches.push(node);
9303
+ }
9304
+ for (const child of selectorAdapter.getChildren(node)) {
9305
+ visit(child);
9306
+ }
9307
+ };
9308
+ for (const node of nodes) {
9309
+ visit(node);
9310
+ }
9311
+ return matches;
9312
+ },
9313
+ findOne(test, nodes) {
9314
+ const visit = (node) => {
9315
+ if (selectorAdapter.isTag(node) && test(node)) {
9316
+ return node;
9317
+ }
9318
+ for (const child of selectorAdapter.getChildren(node)) {
9319
+ const match = visit(child);
9320
+ if (match !== null) {
9321
+ return match;
9322
+ }
9323
+ }
9324
+ return null;
9325
+ };
9326
+ for (const node of nodes) {
9327
+ const match = visit(node);
9328
+ if (match !== null) {
9329
+ return match;
9330
+ }
9331
+ }
9332
+ return null;
9231
9333
  }
9232
9334
  };
9233
9335
  function createDomSnapshotIndex(snapshot) {
@@ -9411,7 +9513,7 @@ function sortNodes(nodes) {
9411
9513
  return [...nodes].sort((left, right) => left.snapshotNodeId - right.snapshotNodeId);
9412
9514
  }
9413
9515
 
9414
- // src/runtimes/dom/path.ts
9516
+ // ../runtime-core/src/runtimes/dom/path.ts
9415
9517
  var MAX_ATTRIBUTE_VALUE_LENGTH = 300;
9416
9518
  function cloneStructuralElementAnchor(anchor) {
9417
9519
  return {
@@ -9420,18 +9522,18 @@ function cloneStructuralElementAnchor(anchor) {
9420
9522
  nodes: anchor.nodes.map(clonePathNode)
9421
9523
  };
9422
9524
  }
9423
- function cloneReplayElementPath(path11) {
9525
+ function cloneReplayElementPath(path13) {
9424
9526
  return {
9425
9527
  resolution: "deterministic",
9426
- context: cloneContext(path11.context),
9427
- nodes: path11.nodes.map(clonePathNode)
9528
+ context: cloneContext(path13.context),
9529
+ nodes: path13.nodes.map(clonePathNode)
9428
9530
  };
9429
9531
  }
9430
- function cloneElementPath(path11) {
9431
- return cloneReplayElementPath(path11);
9532
+ function cloneElementPath(path13) {
9533
+ return cloneReplayElementPath(path13);
9432
9534
  }
9433
- function buildPathSelectorHint(path11) {
9434
- const nodes = path11?.nodes || [];
9535
+ function buildPathSelectorHint(path13) {
9536
+ const nodes = path13?.nodes || [];
9435
9537
  const last = nodes[nodes.length - 1];
9436
9538
  if (!last) {
9437
9539
  return "*";
@@ -9480,15 +9582,15 @@ function sanitizeStructuralElementAnchor(anchor) {
9480
9582
  nodes: sanitizeNodes(anchor.nodes)
9481
9583
  };
9482
9584
  }
9483
- function sanitizeReplayElementPath(path11) {
9585
+ function sanitizeReplayElementPath(path13) {
9484
9586
  return {
9485
9587
  resolution: "deterministic",
9486
- context: sanitizeContext(path11.context),
9487
- nodes: sanitizeNodes(path11.nodes)
9588
+ context: sanitizeContext(path13.context),
9589
+ nodes: sanitizeNodes(path13.nodes)
9488
9590
  };
9489
9591
  }
9490
- function sanitizeElementPath(path11) {
9491
- return sanitizeReplayElementPath(path11);
9592
+ function sanitizeElementPath(path13) {
9593
+ return sanitizeReplayElementPath(path13);
9492
9594
  }
9493
9595
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
9494
9596
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -9611,8 +9713,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
9611
9713
  }
9612
9714
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
9613
9715
  }
9614
- function buildArrayFieldCandidates(path11) {
9615
- return buildArrayFieldPathCandidates(path11);
9716
+ function buildArrayFieldCandidates(path13) {
9717
+ return buildArrayFieldPathCandidates(path13);
9616
9718
  }
9617
9719
  function firstDefinedAttribute(node, keys) {
9618
9720
  for (const key of keys) {
@@ -10028,7 +10130,7 @@ var MemoryDomDescriptorStore = class {
10028
10130
  }
10029
10131
  };
10030
10132
 
10031
- // src/runtimes/dom/executor.ts
10133
+ // ../runtime-core/src/runtimes/dom/executor.ts
10032
10134
  var MAX_DOM_ACTION_ATTEMPTS = 3;
10033
10135
  var DEFAULT_SCROLL_OPTIONS = {
10034
10136
  block: "center",
@@ -10592,7 +10694,7 @@ function pointFallsWithinQuads(point, quads) {
10592
10694
  return quads.some((quad) => rectContainsPoint(quadBounds(quad), point));
10593
10695
  }
10594
10696
 
10595
- // src/runtimes/dom/runtime.ts
10697
+ // ../runtime-core/src/runtimes/dom/runtime.ts
10596
10698
  var SnapshotSession = class {
10597
10699
  constructor(engine) {
10598
10700
  this.engine = engine;
@@ -10996,21 +11098,21 @@ var DefaultDomRuntime = class {
10996
11098
  return match;
10997
11099
  }
10998
11100
  async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
10999
- const path11 = sanitizeReplayElementPath(rawPath);
11000
- const context = await this.resolvePathContext(session, pageRef, path11.context);
11001
- const target = resolveDomPathInScope(context.index, path11.nodes, context.scope);
11101
+ const path13 = sanitizeReplayElementPath(rawPath);
11102
+ const context = await this.resolvePathContext(session, pageRef, path13.context);
11103
+ const target = resolveDomPathInScope(context.index, path13.nodes, context.scope);
11002
11104
  if (!target) {
11003
- throwTargetNotFound(context.index, path11.nodes, context.scope);
11105
+ throwTargetNotFound(context.index, path13.nodes, context.scope);
11004
11106
  }
11005
11107
  if (target.node.nodeRef === void 0) {
11006
11108
  throw new Error(
11007
- `resolved path "${buildPathSelectorHint(path11)}" does not point to a live element`
11109
+ `resolved path "${buildPathSelectorHint(path13)}" does not point to a live element`
11008
11110
  );
11009
11111
  }
11010
11112
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
11011
11113
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
11012
11114
  ...description === void 0 ? {} : { description },
11013
- replayPath: path11,
11115
+ replayPath: path13,
11014
11116
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
11015
11117
  ...descriptor === void 0 ? {} : { descriptor }
11016
11118
  });
@@ -11031,9 +11133,9 @@ var DefaultDomRuntime = class {
11031
11133
  });
11032
11134
  }
11033
11135
  async queryAllByElementPath(session, pageRef, rawPath) {
11034
- const path11 = sanitizeReplayElementPath(rawPath);
11035
- const context = await this.resolvePathContext(session, pageRef, path11.context);
11036
- return queryAllDomPathInScope(context.index, path11.nodes, context.scope).filter(
11136
+ const path13 = sanitizeReplayElementPath(rawPath);
11137
+ const context = await this.resolvePathContext(session, pageRef, path13.context);
11138
+ return queryAllDomPathInScope(context.index, path13.nodes, context.scope).filter(
11037
11139
  (node) => node.nodeRef !== void 0
11038
11140
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11039
11141
  }
@@ -11219,16 +11321,16 @@ var DefaultDomRuntime = class {
11219
11321
  const index = createSnapshotIndex(item.snapshot);
11220
11322
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
11221
11323
  }
11222
- resolveFirstArrayFieldTargetInNode(index, rootNode, path11) {
11223
- const normalizedPath = sanitizeElementPath(path11);
11324
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path13) {
11325
+ const normalizedPath = sanitizeElementPath(path13);
11224
11326
  const selectors = buildArrayFieldCandidates(normalizedPath);
11225
11327
  if (!selectors.length) {
11226
11328
  return rootNode;
11227
11329
  }
11228
11330
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
11229
11331
  }
11230
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path11) {
11231
- const normalizedPath = sanitizeElementPath(path11);
11332
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path13) {
11333
+ const normalizedPath = sanitizeElementPath(path13);
11232
11334
  const selectors = buildArrayFieldCandidates(normalizedPath);
11233
11335
  if (!selectors.length) {
11234
11336
  return rootNode;
@@ -11316,6 +11418,32 @@ async function clearChromeSingletonEntries(userDataDir) {
11316
11418
  )
11317
11419
  );
11318
11420
  }
11421
+ async function sanitizeChromeProfile(userDataDir) {
11422
+ const entries = await promises.readdir(userDataDir).catch(() => []);
11423
+ const profileDirs = entries.filter(
11424
+ (entry) => entry === "Default" || /^Profile \d+$/i.test(entry)
11425
+ );
11426
+ await Promise.all(profileDirs.map((dir) => sanitizeProfilePreferences(userDataDir, dir)));
11427
+ }
11428
+ async function sanitizeProfilePreferences(userDataDir, profileDir) {
11429
+ const prefsPath = path6.join(userDataDir, profileDir, "Preferences");
11430
+ try {
11431
+ const raw = await promises.readFile(prefsPath, "utf8");
11432
+ const prefs = JSON.parse(raw);
11433
+ const profile = prefs.profile ?? {};
11434
+ if (profile.exit_type === "Normal" && profile.exited_cleanly === true) {
11435
+ return;
11436
+ }
11437
+ profile.exit_type = "Normal";
11438
+ profile.exited_cleanly = true;
11439
+ prefs.profile = profile;
11440
+ await promises.writeFile(prefsPath, JSON.stringify(prefs), "utf8");
11441
+ await promises.rm(path6.join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
11442
+ () => void 0
11443
+ );
11444
+ } catch {
11445
+ }
11446
+ }
11319
11447
  var PROCESS_LIST_MAX_BUFFER_BYTES2 = 16 * 1024 * 1024;
11320
11448
  var PS_COMMAND_ENV2 = { ...process.env, LC_ALL: "C" };
11321
11449
  var WINDOWS_PROGRAM_FILES = process.env.PROGRAMFILES ?? "C:\\Program Files";
@@ -12069,8 +12197,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
12069
12197
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
12070
12198
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
12071
12199
  }
12072
- function normalizeWebSocketPath(path11) {
12073
- return path11.startsWith("/") ? path11 : `/${path11}`;
12200
+ function normalizeWebSocketPath(path13) {
12201
+ return path13.startsWith("/") ? path13 : `/${path13}`;
12074
12202
  }
12075
12203
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
12076
12204
  try {
@@ -12145,6 +12273,7 @@ async function createBrowserProfileSnapshot(input) {
12145
12273
  ...profileDirectory === void 0 ? {} : { selectedProfileDirectory: profileDirectory }
12146
12274
  });
12147
12275
  await clearChromeSingletonEntries(targetUserDataDir);
12276
+ await sanitizeChromeProfile(targetUserDataDir);
12148
12277
  }
12149
12278
  async function copyRootLevelEntries(input) {
12150
12279
  const entries = await promises.readdir(input.sourceUserDataDir).catch(() => []);
@@ -12205,17 +12334,28 @@ function shouldCopyEntry(input) {
12205
12334
 
12206
12335
  // src/local-browser/stealth-init-script.ts
12207
12336
  function generateStealthInitScript(profile) {
12208
- const encodedProfile = JSON.stringify(profile);
12337
+ const encodedProfile = JSON.stringify({
12338
+ ...profile,
12339
+ platformString: getPlatformString(profile.platform),
12340
+ userAgentData: buildUserAgentData(profile)
12341
+ });
12209
12342
  return `(() => {
12210
12343
  const profile = ${encodedProfile};
12211
- const define = (target, key, value) => {
12344
+ var define = function(target, key, value) {
12212
12345
  Object.defineProperty(target, key, {
12213
12346
  configurable: true,
12214
- get: typeof value === "function" ? value : () => value,
12347
+ get: typeof value === 'function' ? value : function() { return value; },
12215
12348
  });
12216
12349
  };
12217
- define(Navigator.prototype, 'webdriver', false);
12218
- define(Navigator.prototype, 'platform', profile.platform === 'macos' ? 'MacIntel' : profile.platform === 'windows' ? 'Win32' : 'Linux x86_64');
12350
+
12351
+ // --- navigator / screen mirrors for future pages ---
12352
+ if (navigator.webdriver === true) {
12353
+ Object.defineProperty(Navigator.prototype, 'webdriver', {
12354
+ configurable: true,
12355
+ get: function() { return false; },
12356
+ });
12357
+ }
12358
+ define(Navigator.prototype, 'platform', profile.platformString);
12219
12359
  define(Navigator.prototype, 'userAgent', profile.userAgent);
12220
12360
  define(Navigator.prototype, 'language', profile.locale);
12221
12361
  define(Navigator.prototype, 'languages', [profile.locale, 'en']);
@@ -12225,53 +12365,191 @@ function generateStealthInitScript(profile) {
12225
12365
  define(window.screen, 'height', profile.screenResolution.height);
12226
12366
  define(window.screen, 'availWidth', profile.screenResolution.width);
12227
12367
  define(window.screen, 'availHeight', profile.screenResolution.height - 40);
12228
- if (document.fonts && typeof document.fonts.check === 'function') {
12229
- const originalCheck = document.fonts.check.bind(document.fonts);
12230
- document.fonts.check = function(font, text) {
12231
- const family = String(font).match(/["']([^"']+)["']/)?.[1] || String(font).split(/s+/).at(-1);
12232
- if (family && profile.fonts.includes(family)) {
12233
- return true;
12368
+ define(window.screen, 'colorDepth', 24);
12369
+ define(window.screen, 'pixelDepth', 24);
12370
+ define(Navigator.prototype, 'userAgentData', {
12371
+ brands: profile.userAgentData.brands,
12372
+ mobile: false,
12373
+ platform: profile.userAgentData.platform,
12374
+ toJSON: function() {
12375
+ return {
12376
+ brands: this.brands,
12377
+ mobile: this.mobile,
12378
+ platform: this.platform,
12379
+ };
12380
+ },
12381
+ getHighEntropyValues: async function(hints) {
12382
+ var source = {
12383
+ architecture: profile.userAgentData.architecture,
12384
+ bitness: profile.userAgentData.bitness,
12385
+ brands: profile.userAgentData.brands,
12386
+ fullVersionList: profile.userAgentData.fullVersionList,
12387
+ mobile: false,
12388
+ model: '',
12389
+ platform: profile.userAgentData.platform,
12390
+ platformVersion: profile.userAgentData.platformVersion,
12391
+ uaFullVersion: profile.browserVersion,
12392
+ wow64: false,
12393
+ };
12394
+ var values = {};
12395
+ for (var i = 0; i < hints.length; i++) {
12396
+ var hint = hints[i];
12397
+ if (Object.prototype.hasOwnProperty.call(source, hint)) {
12398
+ values[hint] = source[hint];
12399
+ }
12234
12400
  }
12235
- return originalCheck(font, text);
12401
+ return values;
12402
+ },
12403
+ });
12404
+
12405
+ if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
12406
+ var originalResolvedOptions = Intl.DateTimeFormat.prototype.resolvedOptions;
12407
+ Intl.DateTimeFormat.prototype.resolvedOptions = function() {
12408
+ var options = originalResolvedOptions.call(this);
12409
+ options.timeZone = profile.timezoneId;
12410
+ return options;
12411
+ };
12412
+ }
12413
+
12414
+ if (Date.prototype.getTimezoneOffset) {
12415
+ var originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
12416
+ var calculateTimezoneOffset = function(date) {
12417
+ try {
12418
+ var formatter = new Intl.DateTimeFormat('en-US', {
12419
+ timeZone: profile.timezoneId,
12420
+ hour12: false,
12421
+ year: 'numeric',
12422
+ month: '2-digit',
12423
+ day: '2-digit',
12424
+ hour: '2-digit',
12425
+ minute: '2-digit',
12426
+ second: '2-digit',
12427
+ });
12428
+ var parts = formatter.formatToParts(date);
12429
+ var values = {};
12430
+ for (var i = 0; i < parts.length; i++) {
12431
+ if (parts[i].type !== 'literal') {
12432
+ values[parts[i].type] = Number(parts[i].value);
12433
+ }
12434
+ }
12435
+ var utcTime = Date.UTC(
12436
+ values.year,
12437
+ values.month - 1,
12438
+ values.day,
12439
+ values.hour,
12440
+ values.minute,
12441
+ values.second,
12442
+ );
12443
+ return Math.round((date.getTime() - utcTime) / 60000);
12444
+ } catch {
12445
+ return originalGetTimezoneOffset.call(date);
12446
+ }
12447
+ };
12448
+ Date.prototype.getTimezoneOffset = function() {
12449
+ return calculateTimezoneOffset(this);
12236
12450
  };
12237
12451
  }
12238
- const seedNoise = (seed, input) => {
12239
- const value = Math.sin(seed + input * 12.9898) * 43758.5453;
12452
+
12453
+ // --- CDP Runtime.enable leak defense ---
12454
+ var _wrap = function(name) {
12455
+ var orig = console[name];
12456
+ if (typeof orig !== 'function') return;
12457
+ console[name] = new Proxy(orig, {
12458
+ apply: function(target, thisArg, args) {
12459
+ for (var i = 0; i < args.length; i++) {
12460
+ if (args[i] instanceof Error) {
12461
+ var d = Object.getOwnPropertyDescriptor(args[i], 'stack');
12462
+ if (d && typeof d.get === 'function') return undefined;
12463
+ }
12464
+ }
12465
+ return Reflect.apply(target, thisArg, args);
12466
+ },
12467
+ });
12468
+ };
12469
+ ['debug', 'log', 'info', 'error', 'warn', 'trace', 'dir'].forEach(_wrap);
12470
+
12471
+ // --- Canvas fingerprint noise ---
12472
+ var seedNoise = function(seed, input) {
12473
+ var value = Math.sin(seed + input * 12.9898) * 43758.5453;
12240
12474
  return value - Math.floor(value);
12241
12475
  };
12242
12476
  if (HTMLCanvasElement.prototype.toDataURL) {
12243
- const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
12244
- HTMLCanvasElement.prototype.toDataURL = function(...args) {
12245
- const context = this.getContext('2d');
12477
+ var originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
12478
+ HTMLCanvasElement.prototype.toDataURL = function() {
12479
+ var context = this.getContext('2d');
12246
12480
  if (context) {
12247
- const x = Math.min(1, Math.max(0, this.width - 1));
12248
- const y = Math.min(1, Math.max(0, this.height - 1));
12249
- const imageData = context.getImageData(x, y, 1, 1);
12481
+ var x = Math.min(1, Math.max(0, this.width - 1));
12482
+ var y = Math.min(1, Math.max(0, this.height - 1));
12483
+ var imageData = context.getImageData(x, y, 1, 1);
12250
12484
  imageData.data[0] = Math.max(0, Math.min(255, imageData.data[0] + Math.floor(seedNoise(profile.canvasNoiseSeed, 1) * 2)));
12251
12485
  context.putImageData(imageData, x, y);
12252
12486
  }
12253
- return originalToDataURL.apply(this, args);
12487
+ return originalToDataURL.apply(this, arguments);
12254
12488
  };
12255
12489
  }
12490
+
12491
+ // --- WebGL vendor/renderer spoofing ---
12256
12492
  if (typeof WebGLRenderingContext !== 'undefined') {
12257
- const originalGetParameter = WebGLRenderingContext.prototype.getParameter;
12493
+ var originalGetParameter = WebGLRenderingContext.prototype.getParameter;
12258
12494
  WebGLRenderingContext.prototype.getParameter = function(parameter) {
12259
12495
  if (parameter === 37445) return profile.webglVendor;
12260
12496
  if (parameter === 37446) return profile.webglRenderer;
12261
12497
  return originalGetParameter.call(this, parameter);
12262
12498
  };
12263
12499
  }
12500
+
12501
+ // --- AudioBuffer fingerprint noise ---
12264
12502
  if (typeof AudioBuffer !== 'undefined' && typeof AnalyserNode !== 'undefined') {
12265
- const originalGetFloatFrequencyData = AnalyserNode.prototype.getFloatFrequencyData;
12503
+ var originalGetFloatFrequencyData = AnalyserNode.prototype.getFloatFrequencyData;
12266
12504
  AnalyserNode.prototype.getFloatFrequencyData = function(array) {
12267
12505
  originalGetFloatFrequencyData.call(this, array);
12268
- for (let index = 0; index < array.length; index += 16) {
12506
+ for (var index = 0; index < array.length; index += 16) {
12269
12507
  array[index] += (seedNoise(profile.audioNoiseSeed, index) - 0.5) * 0.0001;
12270
12508
  }
12271
12509
  };
12272
12510
  }
12511
+
12512
+ // --- Font availability spoofing ---
12513
+ if (document.fonts && typeof document.fonts.check === 'function') {
12514
+ var originalCheck = document.fonts.check.bind(document.fonts);
12515
+ document.fonts.check = function(font, text) {
12516
+ var family = String(font).match(/["']([^"']+)["']/)?.[1] || String(font).split(/\\s+/).at(-1);
12517
+ if (family && profile.fonts.includes(family)) {
12518
+ return true;
12519
+ }
12520
+ return originalCheck(font, text);
12521
+ };
12522
+ }
12273
12523
  })();`;
12274
12524
  }
12525
+ function buildUserAgentData(profile) {
12526
+ const majorVersion = profile.browserVersion.split(".")[0] ?? "136";
12527
+ const platformData = {
12528
+ macos: { platform: "macOS", platformVersion: "14.4.0", architecture: "arm" },
12529
+ windows: { platform: "Windows", platformVersion: "15.0.0", architecture: "x86" },
12530
+ linux: { platform: "Linux", platformVersion: "6.5.0", architecture: "x86" }
12531
+ };
12532
+ const platformInfo = platformData[profile.platform];
12533
+ return {
12534
+ architecture: platformInfo.architecture,
12535
+ bitness: "64",
12536
+ brands: [
12537
+ { brand: "Chromium", version: majorVersion },
12538
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: majorVersion }] : [{ brand: "Google Chrome", version: majorVersion }],
12539
+ { brand: "Not-A.Brand", version: "99" }
12540
+ ],
12541
+ fullVersionList: [
12542
+ { brand: "Chromium", version: profile.browserVersion },
12543
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: profile.browserVersion }] : [{ brand: "Google Chrome", version: profile.browserVersion }],
12544
+ { brand: "Not-A.Brand", version: "99.0.0.0" }
12545
+ ],
12546
+ platform: platformInfo.platform,
12547
+ platformVersion: platformInfo.platformVersion
12548
+ };
12549
+ }
12550
+ function getPlatformString(platform) {
12551
+ return platform === "macos" ? "MacIntel" : platform === "windows" ? "Win32" : "Linux x86_64";
12552
+ }
12275
12553
 
12276
12554
  // src/local-browser/stealth.ts
12277
12555
  var STEALTH_INIT_SCRIPT = `(() => {
@@ -12315,25 +12593,137 @@ var STEALTH_INIT_SCRIPT = `(() => {
12315
12593
  ['debug', 'log', 'info', 'error', 'warn', 'trace', 'dir'].forEach(_wrap);
12316
12594
  })();`;
12317
12595
  async function injectBrowserStealthScripts(context, input = {}) {
12596
+ if (input.profile !== void 0) {
12597
+ await installContextNetworkHeaders(context, input.profile);
12598
+ await installCdpStealthOverrides(context, input.profile, input.page);
12599
+ }
12318
12600
  if (typeof context.addInitScript === "function") {
12319
12601
  await context.addInitScript({
12320
12602
  content: input.profile === void 0 ? STEALTH_INIT_SCRIPT : generateStealthInitScript(input.profile)
12321
12603
  });
12322
12604
  }
12323
12605
  }
12606
+ function buildUserAgentMetadata(profile) {
12607
+ const majorVersion = profile.browserVersion.split(".")[0] ?? "136";
12608
+ const brands = [
12609
+ { brand: "Chromium", version: majorVersion },
12610
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: majorVersion }] : [{ brand: "Google Chrome", version: majorVersion }],
12611
+ { brand: "Not-A.Brand", version: "99" }
12612
+ ];
12613
+ const fullVersionList = [
12614
+ { brand: "Chromium", version: profile.browserVersion },
12615
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: profile.browserVersion }] : [{ brand: "Google Chrome", version: profile.browserVersion }],
12616
+ { brand: "Not-A.Brand", version: "99.0.0.0" }
12617
+ ];
12618
+ const platformMap = {
12619
+ // Chromium keeps the reduced macOS UA token frozen to Intel even on Apple Silicon.
12620
+ macos: { platform: "macOS", platformVersion: "14.4.0", architecture: "arm" },
12621
+ windows: { platform: "Windows", platformVersion: "15.0.0", architecture: "x86" },
12622
+ linux: { platform: "Linux", platformVersion: "6.5.0", architecture: "x86" }
12623
+ };
12624
+ const platformInfo = platformMap[profile.platform];
12625
+ return {
12626
+ brands,
12627
+ fullVersionList,
12628
+ platform: platformInfo.platform,
12629
+ platformVersion: platformInfo.platformVersion,
12630
+ architecture: platformInfo.architecture,
12631
+ model: "",
12632
+ mobile: false,
12633
+ bitness: "64",
12634
+ wow64: false
12635
+ };
12636
+ }
12637
+ async function installCdpStealthOverrides(context, profile, initialPage) {
12638
+ const pages = initialPage === void 0 ? context.pages() : Array.from(/* @__PURE__ */ new Set([initialPage, ...context.pages()]));
12639
+ await Promise.all(pages.map((page) => applyPageOverrides(context, page, profile)));
12640
+ const appliedPages = /* @__PURE__ */ new WeakSet();
12641
+ const applyFuturePageOverrides = async (page) => {
12642
+ if (appliedPages.has(page)) {
12643
+ return;
12644
+ }
12645
+ appliedPages.add(page);
12646
+ await applyPageOverrides(context, page, profile);
12647
+ };
12648
+ if (typeof context.on === "function") {
12649
+ context.on("page", applyFuturePageOverrides);
12650
+ }
12651
+ }
12652
+ async function installContextNetworkHeaders(context, profile) {
12653
+ if (typeof context.setExtraHTTPHeaders !== "function") {
12654
+ return;
12655
+ }
12656
+ await context.setExtraHTTPHeaders(buildStealthRequestHeaders(profile)).catch(() => void 0);
12657
+ }
12658
+ async function applyPageOverrides(context, page, profile) {
12659
+ const contextWithCdp = context;
12660
+ if (typeof contextWithCdp.newCDPSession !== "function") {
12661
+ return;
12662
+ }
12663
+ let cdp;
12664
+ try {
12665
+ cdp = await contextWithCdp.newCDPSession(page);
12666
+ } catch {
12667
+ return;
12668
+ }
12669
+ try {
12670
+ await applyCdpStealthCommands((method, params) => cdp.send(method, params), profile);
12671
+ } catch {
12672
+ } finally {
12673
+ await cdp.detach().catch(() => void 0);
12674
+ }
12675
+ }
12676
+ async function applyCdpStealthCommands(send, profile) {
12677
+ await send("Network.setUserAgentOverride", {
12678
+ userAgent: profile.userAgent,
12679
+ acceptLanguage: `${profile.locale},en;q=0.9`,
12680
+ platform: getPlatformString2(profile.platform),
12681
+ userAgentMetadata: buildUserAgentMetadata(profile)
12682
+ });
12683
+ await send("Emulation.setDeviceMetricsOverride", {
12684
+ width: profile.viewport.width,
12685
+ height: profile.viewport.height,
12686
+ deviceScaleFactor: profile.devicePixelRatio,
12687
+ mobile: false,
12688
+ screenWidth: profile.screenResolution.width,
12689
+ screenHeight: profile.screenResolution.height
12690
+ });
12691
+ await send("Emulation.setLocaleOverride", {
12692
+ locale: profile.locale
12693
+ }).catch(() => void 0);
12694
+ await send("Emulation.setTimezoneOverride", {
12695
+ timezoneId: profile.timezoneId
12696
+ }).catch(() => void 0);
12697
+ }
12698
+ function buildStealthRequestHeaders(profile) {
12699
+ const metadata = buildUserAgentMetadata(profile);
12700
+ return {
12701
+ "Accept-Language": `${profile.locale},en;q=0.9`,
12702
+ "Sec-CH-UA": metadata.brands.map(formatClientHintBrand).join(", "),
12703
+ "Sec-CH-UA-Mobile": "?0",
12704
+ "Sec-CH-UA-Platform": `"${metadata.platform}"`,
12705
+ "User-Agent": profile.userAgent
12706
+ };
12707
+ }
12708
+ function formatClientHintBrand(brand2) {
12709
+ return `"${brand2.brand}";v="${brand2.version}"`;
12710
+ }
12711
+ function getPlatformString2(platform) {
12712
+ return platform === "macos" ? "MacIntel" : platform === "windows" ? "Win32" : "Linux x86_64";
12713
+ }
12324
12714
 
12325
12715
  // src/local-browser/stealth-profiles.ts
12326
12716
  var PROFILE_PRESETS = [
12327
12717
  {
12328
12718
  platform: "macos",
12329
12719
  browserBrand: "chrome",
12330
- browserVersion: "133.0.6943.99",
12331
- userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.99 Safari/537.36",
12720
+ browserVersion: "136.0.7103.93",
12721
+ userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
12332
12722
  viewport: { width: 1440, height: 900 },
12333
12723
  screenResolution: { width: 1512, height: 982 },
12334
12724
  devicePixelRatio: 2,
12335
12725
  maxTouchPoints: 0,
12336
- webglVendor: "Apple Inc.",
12726
+ webglVendor: "Apple",
12337
12727
  webglRenderer: "Apple M2",
12338
12728
  fonts: ["SF Pro Text", "Helvetica Neue", "Arial", "Menlo", "Apple Color Emoji"],
12339
12729
  locale: "en-US",
@@ -12342,8 +12732,8 @@ var PROFILE_PRESETS = [
12342
12732
  {
12343
12733
  platform: "windows",
12344
12734
  browserBrand: "chrome",
12345
- browserVersion: "133.0.6943.99",
12346
- userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.99 Safari/537.36",
12735
+ browserVersion: "136.0.7103.93",
12736
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
12347
12737
  viewport: { width: 1536, height: 864 },
12348
12738
  screenResolution: { width: 1920, height: 1080 },
12349
12739
  devicePixelRatio: 1.25,
@@ -12357,8 +12747,8 @@ var PROFILE_PRESETS = [
12357
12747
  {
12358
12748
  platform: "windows",
12359
12749
  browserBrand: "edge",
12360
- browserVersion: "133.0.3065.82",
12361
- userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.3065.82",
12750
+ browserVersion: "136.0.3240.50",
12751
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.3240.50",
12362
12752
  viewport: { width: 1536, height: 864 },
12363
12753
  screenResolution: { width: 1920, height: 1080 },
12364
12754
  devicePixelRatio: 1.25,
@@ -12372,8 +12762,8 @@ var PROFILE_PRESETS = [
12372
12762
  {
12373
12763
  platform: "linux",
12374
12764
  browserBrand: "chrome",
12375
- browserVersion: "133.0.6943.99",
12376
- userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.99 Safari/537.36",
12765
+ browserVersion: "136.0.7103.93",
12766
+ userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
12377
12767
  viewport: { width: 1366, height: 768 },
12378
12768
  screenResolution: { width: 1366, height: 768 },
12379
12769
  devicePixelRatio: 1,
@@ -12413,8 +12803,115 @@ function pickStealthProfilePreset(overrides) {
12413
12803
  const pool = candidates.length > 0 ? candidates : PROFILE_PRESETS;
12414
12804
  return pool[Math.floor(Math.random() * pool.length)];
12415
12805
  }
12806
+ var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
12807
+ var OPENSTEER_LIVE_SESSION_VERSION = 1;
12808
+ var LEGACY_CLOUD_SESSION_LAYOUT = "opensteer-cloud-session";
12809
+ var LEGACY_CLOUD_SESSION_VERSION = 1;
12810
+ function resolveLiveSessionRecordPath(rootPath) {
12811
+ return path6__default.default.join(rootPath, "live", "session.json");
12812
+ }
12813
+ function resolveLegacyLiveBrowserRecordPath(rootPath) {
12814
+ return path6__default.default.join(rootPath, "live", "browser.json");
12815
+ }
12816
+ function resolveLegacyCloudSessionRecordPath(rootPath) {
12817
+ return path6__default.default.join(rootPath, "live", "cloud-session.json");
12818
+ }
12819
+ async function readPersistedSessionRecord(rootPath) {
12820
+ const sessionPath = resolveLiveSessionRecordPath(rootPath);
12821
+ if (await pathExists(sessionPath)) {
12822
+ const parsed = await readJsonFile(sessionPath);
12823
+ if (isPersistedLocalBrowserSessionRecord(parsed)) {
12824
+ return parsed;
12825
+ }
12826
+ if (isPersistedCloudSessionRecord(parsed)) {
12827
+ return parsed;
12828
+ }
12829
+ }
12830
+ const legacyCloudPath = resolveLegacyCloudSessionRecordPath(rootPath);
12831
+ if (await pathExists(legacyCloudPath)) {
12832
+ const parsed = await readJsonFile(legacyCloudPath);
12833
+ if (isLegacyCloudSessionRecord(parsed)) {
12834
+ return {
12835
+ layout: OPENSTEER_LIVE_SESSION_LAYOUT,
12836
+ version: OPENSTEER_LIVE_SESSION_VERSION,
12837
+ provider: "cloud",
12838
+ mode: "cloud",
12839
+ ...parsed.workspace === void 0 ? {} : { workspace: parsed.workspace },
12840
+ sessionId: parsed.sessionId,
12841
+ baseUrl: parsed.baseUrl,
12842
+ startedAt: parsed.startedAt,
12843
+ updatedAt: parsed.updatedAt
12844
+ };
12845
+ }
12846
+ }
12847
+ const legacyBrowserPath = resolveLegacyLiveBrowserRecordPath(rootPath);
12848
+ if (await pathExists(legacyBrowserPath)) {
12849
+ const parsed = await readJsonFile(legacyBrowserPath);
12850
+ if (isLegacyLocalBrowserSessionRecord(parsed)) {
12851
+ return {
12852
+ layout: OPENSTEER_LIVE_SESSION_LAYOUT,
12853
+ version: OPENSTEER_LIVE_SESSION_VERSION,
12854
+ provider: "local",
12855
+ mode: "browser",
12856
+ engine: parsed.engine ?? "playwright",
12857
+ ...parsed.endpoint === void 0 ? {} : { endpoint: parsed.endpoint },
12858
+ ...parsed.baseUrl === void 0 ? {} : { baseUrl: parsed.baseUrl },
12859
+ ...parsed.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: parsed.remoteDebuggingUrl },
12860
+ ...parsed.sessionDir === void 0 ? {} : { sessionDir: parsed.sessionDir },
12861
+ pid: parsed.pid,
12862
+ startedAt: parsed.startedAt,
12863
+ updatedAt: parsed.startedAt,
12864
+ ...parsed.executablePath === void 0 ? {} : { executablePath: parsed.executablePath },
12865
+ userDataDir: parsed.userDataDir
12866
+ };
12867
+ }
12868
+ }
12869
+ return void 0;
12870
+ }
12871
+ async function readPersistedCloudSessionRecord(rootPath) {
12872
+ const record = await readPersistedSessionRecord(rootPath);
12873
+ return record?.provider === "cloud" ? record : void 0;
12874
+ }
12875
+ async function readPersistedLocalBrowserSessionRecord(rootPath) {
12876
+ const record = await readPersistedSessionRecord(rootPath);
12877
+ return record?.provider === "local" ? record : void 0;
12878
+ }
12879
+ async function hasPersistedCloudSession(rootPath) {
12880
+ return await readPersistedCloudSessionRecord(rootPath) !== void 0;
12881
+ }
12882
+ async function writePersistedSessionRecord(rootPath, record) {
12883
+ await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath), record);
12884
+ await clearLegacySessionRecordPaths(rootPath);
12885
+ }
12886
+ async function clearPersistedSessionRecord(rootPath) {
12887
+ await Promise.all([
12888
+ removeIfPresent(resolveLiveSessionRecordPath(rootPath)),
12889
+ clearLegacySessionRecordPaths(rootPath)
12890
+ ]);
12891
+ }
12892
+ function isPersistedCloudSessionRecord(value) {
12893
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && value.mode === "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);
12894
+ }
12895
+ function isPersistedLocalBrowserSessionRecord(value) {
12896
+ return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && value.mode === "browser" && (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;
12897
+ }
12898
+ function isLegacyCloudSessionRecord(value) {
12899
+ return value.layout === LEGACY_CLOUD_SESSION_LAYOUT && value.version === LEGACY_CLOUD_SESSION_VERSION && value.mode === "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);
12900
+ }
12901
+ function isLegacyLocalBrowserSessionRecord(value) {
12902
+ return value.mode === "persistent" && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
12903
+ }
12904
+ async function clearLegacySessionRecordPaths(rootPath) {
12905
+ await Promise.all([
12906
+ removeIfPresent(resolveLegacyLiveBrowserRecordPath(rootPath)),
12907
+ removeIfPresent(resolveLegacyCloudSessionRecordPath(rootPath))
12908
+ ]);
12909
+ }
12910
+ async function removeIfPresent(filePath) {
12911
+ await promises.rm(filePath, { force: true }).catch(() => void 0);
12912
+ }
12416
12913
 
12417
- // src/internal/engine-selection.ts
12914
+ // ../runtime-core/src/internal/engine-selection.ts
12418
12915
  var OPENSTEER_ENGINE_NAMES = ["playwright", "abp"];
12419
12916
  var DEFAULT_OPENSTEER_ENGINE = "playwright";
12420
12917
  function resolveOpensteerEngineName(input = {}) {
@@ -12617,7 +13114,7 @@ var OpensteerBrowserManager = class {
12617
13114
  await this.closePersistentBrowser(workspace);
12618
13115
  await promises.rm(resolveAbpSessionDir(workspace), { recursive: true, force: true });
12619
13116
  await promises.rm(workspace.browserPath, { recursive: true, force: true });
12620
- await promises.rm(workspace.liveBrowserPath, { force: true });
13117
+ await clearPersistedSessionRecord(workspace.rootPath);
12621
13118
  await ensureDirectory(workspace.browserUserDataDir);
12622
13119
  });
12623
13120
  }
@@ -12628,7 +13125,7 @@ var OpensteerBrowserManager = class {
12628
13125
  await this.closePersistentBrowser(workspace);
12629
13126
  await promises.rm(resolveAbpSessionDir(workspace), { recursive: true, force: true });
12630
13127
  await promises.rm(workspace.browserPath, { recursive: true, force: true });
12631
- await promises.rm(workspace.liveBrowserPath, { force: true });
13128
+ await clearPersistedSessionRecord(workspace.rootPath);
12632
13129
  });
12633
13130
  }
12634
13131
  async close() {
@@ -12706,12 +13203,12 @@ var OpensteerBrowserManager = class {
12706
13203
  sessionDir: resolveAbpSessionDir(workspace),
12707
13204
  ...launch?.browserExecutablePath === void 0 ? {} : { executablePath: launch.browserExecutablePath }
12708
13205
  };
12709
- await writeJsonFileAtomic(workspace.liveBrowserPath, liveRecord);
13206
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
12710
13207
  try {
12711
13208
  return await this.createAdoptedAbpEngine(liveRecord);
12712
13209
  } catch (error) {
12713
13210
  await terminateProcess(launched.process.pid ?? 0).catch(() => void 0);
12714
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13211
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12715
13212
  throw error;
12716
13213
  }
12717
13214
  });
@@ -12733,7 +13230,8 @@ var OpensteerBrowserManager = class {
12733
13230
  await clearChromeSingletonEntries(userDataDir);
12734
13231
  const launched = await launchOwnedBrowser({
12735
13232
  userDataDir,
12736
- ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions }
13233
+ ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions },
13234
+ ...this.contextOptions?.viewport === void 0 ? {} : { viewport: this.contextOptions.viewport }
12737
13235
  });
12738
13236
  try {
12739
13237
  return await this.createAttachedEngine({
@@ -12781,7 +13279,8 @@ var OpensteerBrowserManager = class {
12781
13279
  await this.ensurePersistentBrowserManifest(workspace);
12782
13280
  const launched = await launchOwnedBrowser({
12783
13281
  userDataDir: workspace.browserUserDataDir,
12784
- ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions }
13282
+ ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions },
13283
+ ...this.contextOptions?.viewport === void 0 ? {} : { viewport: this.contextOptions.viewport }
12785
13284
  });
12786
13285
  const liveRecord = {
12787
13286
  mode: "persistent",
@@ -12792,7 +13291,7 @@ var OpensteerBrowserManager = class {
12792
13291
  executablePath: launched.executablePath,
12793
13292
  userDataDir: workspace.browserUserDataDir
12794
13293
  };
12795
- await writeJsonFileAtomic(workspace.liveBrowserPath, liveRecord);
13294
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
12796
13295
  try {
12797
13296
  return await this.createAttachedEngine({
12798
13297
  endpoint: launched.endpoint,
@@ -12801,7 +13300,7 @@ var OpensteerBrowserManager = class {
12801
13300
  });
12802
13301
  } catch (error) {
12803
13302
  await terminateProcess(launched.pid).catch(() => void 0);
12804
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13303
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12805
13304
  throw error;
12806
13305
  }
12807
13306
  });
@@ -12816,13 +13315,16 @@ var OpensteerBrowserManager = class {
12816
13315
  if (!context) {
12817
13316
  throw new Error("Connected browser did not expose a Chromium browser context.");
12818
13317
  }
13318
+ const page = input.freshTab || context.pages()[0] === void 0 ? await context.newPage() : context.pages()[0];
13319
+ await page.bringToFront?.();
12819
13320
  const stealthProfile = resolveStealthProfile(this.contextOptions?.stealthProfile);
12820
13321
  await injectBrowserStealthScripts(
12821
13322
  context,
12822
- stealthProfile === void 0 ? {} : { profile: stealthProfile }
13323
+ stealthProfile === void 0 ? {} : {
13324
+ profile: stealthProfile,
13325
+ page
13326
+ }
12823
13327
  );
12824
- const page = input.freshTab || context.pages()[0] === void 0 ? await context.newPage() : context.pages()[0];
12825
- await page.bringToFront?.();
12826
13328
  const engine = await enginePlaywright.createPlaywrightBrowserCoreEngine({
12827
13329
  browser,
12828
13330
  attachedContext: context,
@@ -12904,7 +13406,7 @@ var OpensteerBrowserManager = class {
12904
13406
  return void 0;
12905
13407
  }
12906
13408
  if (!isProcessRunning(live.pid)) {
12907
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13409
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12908
13410
  return void 0;
12909
13411
  }
12910
13412
  if (live.engine === "playwright") {
@@ -12923,19 +13425,8 @@ var OpensteerBrowserManager = class {
12923
13425
  return live;
12924
13426
  }
12925
13427
  async readStoredLiveBrowser(workspace) {
12926
- if (!await pathExists(workspace.liveBrowserPath)) {
12927
- return void 0;
12928
- }
12929
- const live = await readJsonFile(
12930
- workspace.liveBrowserPath
12931
- );
12932
- if (live.engine === "abp") {
12933
- return live;
12934
- }
12935
- return {
12936
- ...live,
12937
- engine: "playwright"
12938
- };
13428
+ const live = await readPersistedLocalBrowserSessionRecord(workspace.rootPath);
13429
+ return live === void 0 ? void 0 : toWorkspaceLiveBrowserRecord(live);
12939
13430
  }
12940
13431
  async resolveLivePersistentEngineName() {
12941
13432
  if (this.mode !== "persistent") {
@@ -12955,7 +13446,7 @@ var OpensteerBrowserManager = class {
12955
13446
  async closePersistentBrowser(workspace) {
12956
13447
  const live = await this.readStoredLiveBrowser(workspace);
12957
13448
  if (!live) {
12958
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13449
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12959
13450
  return;
12960
13451
  }
12961
13452
  if (live.engine === "playwright") {
@@ -12963,15 +13454,21 @@ var OpensteerBrowserManager = class {
12963
13454
  await requestBrowserClose(live.endpoint).catch(() => void 0);
12964
13455
  }
12965
13456
  if (await waitForProcessExit(live.pid, BROWSER_CLOSE_TIMEOUT_MS)) {
12966
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13457
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12967
13458
  return;
12968
13459
  }
12969
13460
  await terminateProcess(live.pid).catch(() => void 0);
12970
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13461
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12971
13462
  return;
12972
13463
  }
12973
13464
  await terminateProcess(live.pid).catch(() => void 0);
12974
- await promises.rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13465
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
13466
+ }
13467
+ async writeLivePersistentBrowser(workspace, live) {
13468
+ await writePersistedSessionRecord(
13469
+ workspace.rootPath,
13470
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
13471
+ );
12975
13472
  }
12976
13473
  requirePersistentMode(method) {
12977
13474
  if (this.mode !== "persistent" || this.workspace === void 0) {
@@ -12983,6 +13480,39 @@ function normalizeWorkspace(workspace) {
12983
13480
  const normalized = workspace?.trim();
12984
13481
  return normalized === void 0 || normalized.length === 0 ? void 0 : normalized;
12985
13482
  }
13483
+ function toPersistedLocalBrowserSessionRecord(workspace, live) {
13484
+ return {
13485
+ layout: "opensteer-session",
13486
+ version: 1,
13487
+ provider: "local",
13488
+ mode: "browser",
13489
+ ...workspace === void 0 ? {} : { workspace },
13490
+ engine: live.engine,
13491
+ ...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
13492
+ ...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
13493
+ ...live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: live.remoteDebuggingUrl },
13494
+ ...live.sessionDir === void 0 ? {} : { sessionDir: live.sessionDir },
13495
+ pid: live.pid,
13496
+ startedAt: live.startedAt,
13497
+ updatedAt: Date.now(),
13498
+ ...live.executablePath === void 0 ? {} : { executablePath: live.executablePath },
13499
+ userDataDir: live.userDataDir
13500
+ };
13501
+ }
13502
+ function toWorkspaceLiveBrowserRecord(record) {
13503
+ return {
13504
+ mode: "persistent",
13505
+ engine: record.engine,
13506
+ ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
13507
+ ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
13508
+ ...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl },
13509
+ ...record.sessionDir === void 0 ? {} : { sessionDir: record.sessionDir },
13510
+ pid: record.pid,
13511
+ startedAt: record.startedAt,
13512
+ ...record.executablePath === void 0 ? {} : { executablePath: record.executablePath },
13513
+ userDataDir: record.userDataDir
13514
+ };
13515
+ }
12986
13516
  function resolveBrowserMode(workspace, browser) {
12987
13517
  if (browser === void 0) {
12988
13518
  return workspace === void 0 ? "temporary" : "persistent";
@@ -13008,8 +13538,9 @@ async function resolveAttachEndpoint(browser) {
13008
13538
  async function launchOwnedBrowser(input) {
13009
13539
  await ensureDirectory(input.userDataDir);
13010
13540
  await clearChromeSingletonEntries(input.userDataDir);
13541
+ await sanitizeChromeProfile(input.userDataDir);
13011
13542
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
13012
- const args = buildChromeArgs(input.userDataDir, input.launch);
13543
+ const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
13013
13544
  const child = child_process.spawn(executablePath, args, {
13014
13545
  stdio: ["ignore", "ignore", "pipe"],
13015
13546
  detached: process.platform !== "win32"
@@ -13036,7 +13567,8 @@ async function launchOwnedBrowser(input) {
13036
13567
  executablePath
13037
13568
  };
13038
13569
  }
13039
- function buildChromeArgs(userDataDir, launch) {
13570
+ function buildChromeArgs(userDataDir, launch, viewport) {
13571
+ const isHeadless = launch?.headless ?? true;
13040
13572
  const args = [
13041
13573
  "--remote-debugging-port=0",
13042
13574
  "--no-first-run",
@@ -13050,17 +13582,23 @@ function buildChromeArgs(userDataDir, launch) {
13050
13582
  "--disable-popup-blocking",
13051
13583
  "--disable-prompt-on-repost",
13052
13584
  "--disable-sync",
13585
+ "--disable-infobars",
13053
13586
  "--disable-features=Translate",
13054
13587
  "--enable-features=NetworkService,NetworkServiceInProcess",
13055
13588
  "--password-store=basic",
13056
13589
  "--use-mock-keychain",
13057
13590
  `--user-data-dir=${userDataDir}`
13058
13591
  ];
13059
- if (launch?.headless ?? true) {
13592
+ if (isHeadless) {
13060
13593
  args.push("--headless=new");
13061
- if (!(launch?.args ?? []).some((entry) => entry.startsWith("--window-size"))) {
13062
- args.push("--window-size=1280,800");
13063
- }
13594
+ }
13595
+ const hasUserWindowSize = (launch?.args ?? []).some((entry) => entry.startsWith("--window-size"));
13596
+ if (!hasUserWindowSize) {
13597
+ const width = viewport?.width ?? 1440;
13598
+ const height = viewport?.height ?? 900;
13599
+ args.push(
13600
+ isHeadless ? `--window-size=${String(width)},${String(height)}` : `--window-size=${String(width)},${String(height + 150)}`
13601
+ );
13064
13602
  }
13065
13603
  args.push(...launch?.args ?? []);
13066
13604
  return args;
@@ -13252,7 +13790,14 @@ async function sleep(ms) {
13252
13790
  await new Promise((resolve5) => setTimeout(resolve5, ms));
13253
13791
  }
13254
13792
 
13255
- // src/internal/errors.ts
13793
+ // ../runtime-core/package.json
13794
+ var package_default = {
13795
+ version: "0.1.0"};
13796
+
13797
+ // ../runtime-core/src/version.ts
13798
+ var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
13799
+
13800
+ // ../runtime-core/src/internal/errors.ts
13256
13801
  function normalizeThrownOpensteerError(error, fallbackMessage) {
13257
13802
  if (isOpensteerProtocolError(error)) {
13258
13803
  return toOpensteerError(error);
@@ -13263,15 +13808,6 @@ function normalizeThrownOpensteerError(error, fallbackMessage) {
13263
13808
  ...error.details === void 0 ? {} : { details: error.details }
13264
13809
  });
13265
13810
  }
13266
- if (error instanceof OpensteerAttachAmbiguousError) {
13267
- return createOpensteerError("conflict", error.message, {
13268
- details: {
13269
- candidates: error.candidates,
13270
- code: error.code,
13271
- name: error.name
13272
- }
13273
- });
13274
- }
13275
13811
  if (error instanceof Error) {
13276
13812
  return createOpensteerError("operation-failed", error.message, {
13277
13813
  details: {
@@ -13441,7 +13977,7 @@ function roundScale(value) {
13441
13977
  return Number(value.toFixed(6));
13442
13978
  }
13443
13979
 
13444
- // src/runtimes/computer-use/trace-enrichment.ts
13980
+ // ../runtime-core/src/runtimes/computer-use/trace-enrichment.ts
13445
13981
  async function enrichComputerUseTrace(input) {
13446
13982
  const tracePoints = toTracePoints(input.action);
13447
13983
  if (tracePoints.length === 0) {
@@ -13534,7 +14070,7 @@ function toOpensteerResolvedTarget(target) {
13534
14070
  };
13535
14071
  }
13536
14072
 
13537
- // src/runtimes/computer-use/runtime.ts
14073
+ // ../runtime-core/src/runtimes/computer-use/runtime.ts
13538
14074
  function createComputerUseRuntime(options) {
13539
14075
  return new DefaultComputerUseRuntime(options);
13540
14076
  }
@@ -13629,7 +14165,7 @@ function normalizeScreenshotOptions(input) {
13629
14165
  };
13630
14166
  }
13631
14167
 
13632
- // src/sdk/semantic-dispatch.ts
14168
+ // ../runtime-core/src/sdk/semantic-dispatch.ts
13633
14169
  async function dispatchSemanticOperation(runtime, operation, input, options = {}) {
13634
14170
  switch (operation) {
13635
14171
  case "session.open":
@@ -13890,7 +14426,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
13890
14426
  }
13891
14427
  }
13892
14428
 
13893
- // src/requests/shared.ts
14429
+ // ../runtime-core/src/requests/shared.ts
13894
14430
  var REDACTED_HEADER_VALUE = "[redacted]";
13895
14431
  var HTTP_HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
13896
14432
  var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -14106,14 +14642,14 @@ function resolveTextEncoding(charset) {
14106
14642
  }
14107
14643
  }
14108
14644
 
14109
- // src/requests/errors.ts
14645
+ // ../runtime-core/src/requests/errors.ts
14110
14646
  function invalidRequestPlanError(message, details = {}) {
14111
14647
  return new OpensteerProtocolError("invalid-request", message, {
14112
14648
  details
14113
14649
  });
14114
14650
  }
14115
14651
 
14116
- // src/requests/plans/index.ts
14652
+ // ../runtime-core/src/requests/plans/index.ts
14117
14653
  var HTTP_METHOD_PATTERN = /^[A-Za-z]+$/;
14118
14654
  var URL_TEMPLATE_PLACEHOLDER_PATTERN = /\{([A-Za-z][A-Za-z0-9_-]*)\}/g;
14119
14655
  function assertValidRequestPlanPayload(payload) {
@@ -14521,7 +15057,7 @@ function normalizeTrimmedString(field, value) {
14521
15057
  return normalized;
14522
15058
  }
14523
15059
 
14524
- // src/requests/inference.ts
15060
+ // ../runtime-core/src/requests/inference.ts
14525
15061
  function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14526
15062
  const url = new URL(record.record.url);
14527
15063
  const defaultQuery = Array.from(url.searchParams.entries()).map(
@@ -14534,6 +15070,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14534
15070
  const responseContentType = headerValue(record.record.responseHeaders, "content-type") ?? record.record.responseBody?.mimeType;
14535
15071
  const defaultHeaders = inferDefaultHeaders(record);
14536
15072
  const auth = inferAuth(record.record.requestHeaders);
15073
+ const body = inferRequestPlanBody(record.record.requestBody, requestContentType);
14537
15074
  const payload = normalizeRequestPlanPayload({
14538
15075
  transport: {
14539
15076
  kind: "context-http"
@@ -14544,12 +15081,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14544
15081
  ...defaultQuery.length === 0 ? {} : { defaultQuery },
14545
15082
  ...defaultHeaders.length === 0 ? {} : { defaultHeaders }
14546
15083
  },
14547
- ...requestContentType === void 0 && record.record.requestBody === void 0 ? {} : {
14548
- body: {
14549
- ...requestContentType === void 0 ? {} : { contentType: requestContentType },
14550
- required: true
14551
- }
14552
- },
15084
+ ...body === void 0 ? {} : { body },
14553
15085
  ...record.record.status === void 0 ? {} : {
14554
15086
  response: {
14555
15087
  status: record.record.status,
@@ -14605,22 +15137,66 @@ function inferAuth(headers) {
14605
15137
  }
14606
15138
  return void 0;
14607
15139
  }
14608
-
14609
- // src/reverse/materialization.ts
14610
- var ALWAYS_OMIT_HEADER_NAMES = /* @__PURE__ */ new Set(["content-length", "host", "priority"]);
14611
- var BROWSER_OWNED_HEADER_PREFIXES = ["sec-"];
14612
- function isManagedRequestHeaderName(name, transport) {
14613
- const normalized = normalizeHeaderName(name);
14614
- if (!isValidHttpHeaderName(name)) {
14615
- return true;
14616
- }
14617
- if (normalized.startsWith(":")) {
14618
- return true;
14619
- }
14620
- if (ALWAYS_OMIT_HEADER_NAMES.has(normalized)) {
14621
- return true;
15140
+ function inferRequestPlanBody(body, contentType) {
15141
+ if (body === void 0) {
15142
+ return void 0;
14622
15143
  }
14623
- if (transport !== void 0 && (transport === "page-http" || transport === "session-http")) {
15144
+ const text = Buffer.from(body.data, "base64").toString("utf8");
15145
+ const normalizedContentType = contentType?.toLowerCase();
15146
+ const trimmedText = text.trim();
15147
+ const parsedJson = parseJsonBody(trimmedText);
15148
+ if (normalizedContentType?.includes("application/json") === true || normalizedContentType?.includes("+json") === true || parsedJson !== void 0) {
15149
+ return {
15150
+ kind: "json",
15151
+ required: true,
15152
+ ...contentType === void 0 ? {} : { contentType },
15153
+ template: parsedJson ?? text
15154
+ };
15155
+ }
15156
+ if (normalizedContentType?.includes("application/x-www-form-urlencoded") === true) {
15157
+ return {
15158
+ kind: "form",
15159
+ required: true,
15160
+ ...contentType === void 0 ? {} : { contentType },
15161
+ fields: Array.from(new URLSearchParams(text).entries()).map(([name, value]) => ({
15162
+ name,
15163
+ value
15164
+ }))
15165
+ };
15166
+ }
15167
+ return {
15168
+ kind: "text",
15169
+ required: true,
15170
+ ...contentType === void 0 ? {} : { contentType },
15171
+ template: text
15172
+ };
15173
+ }
15174
+ function parseJsonBody(value) {
15175
+ if (value.length === 0 || !value.startsWith("{") && !value.startsWith("[")) {
15176
+ return void 0;
15177
+ }
15178
+ try {
15179
+ return JSON.parse(value);
15180
+ } catch {
15181
+ return void 0;
15182
+ }
15183
+ }
15184
+
15185
+ // ../runtime-core/src/reverse/materialization.ts
15186
+ var ALWAYS_OMIT_HEADER_NAMES = /* @__PURE__ */ new Set(["content-length", "host", "priority"]);
15187
+ var BROWSER_OWNED_HEADER_PREFIXES = ["sec-"];
15188
+ function isManagedRequestHeaderName(name, transport) {
15189
+ const normalized = normalizeHeaderName(name);
15190
+ if (!isValidHttpHeaderName(name)) {
15191
+ return true;
15192
+ }
15193
+ if (normalized.startsWith(":")) {
15194
+ return true;
15195
+ }
15196
+ if (ALWAYS_OMIT_HEADER_NAMES.has(normalized)) {
15197
+ return true;
15198
+ }
15199
+ if (transport !== void 0 && (transport === "page-http" || transport === "session-http")) {
14624
15200
  if (BROWSER_OWNED_HEADER_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
14625
15201
  return true;
14626
15202
  }
@@ -14642,7 +15218,7 @@ function finalizeMaterializedTransportRequest(request, transport) {
14642
15218
  };
14643
15219
  }
14644
15220
 
14645
- // src/network/diff.ts
15221
+ // ../runtime-core/src/network/diff.ts
14646
15222
  function diffNetworkRecords(left, right, input) {
14647
15223
  const requestDiffs = [];
14648
15224
  const responseDiffs = [];
@@ -14802,7 +15378,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
14802
15378
  diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
14803
15379
  }
14804
15380
  }
14805
- function diffScalarField(path11, left, right, includeUnchanged, output) {
15381
+ function diffScalarField(path13, left, right, includeUnchanged, output) {
14806
15382
  const leftValue = stringifyFieldValue(left);
14807
15383
  const rightValue = stringifyFieldValue(right);
14808
15384
  const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
@@ -14810,7 +15386,7 @@ function diffScalarField(path11, left, right, includeUnchanged, output) {
14810
15386
  return;
14811
15387
  }
14812
15388
  output.push({
14813
- path: path11,
15389
+ path: path13,
14814
15390
  kind,
14815
15391
  ...leftValue === void 0 ? {} : { leftValue },
14816
15392
  ...rightValue === void 0 ? {} : { rightValue },
@@ -14999,7 +15575,7 @@ var NetworkJournal = class {
14999
15575
  }
15000
15576
  };
15001
15577
 
15002
- // src/network/minimize.ts
15578
+ // ../runtime-core/src/network/minimize.ts
15003
15579
  function prepareMinimizationRequest(record, preserve = []) {
15004
15580
  const requestUrl = new URL(record.record.url);
15005
15581
  const preservedNames = new Set(
@@ -15412,7 +15988,7 @@ function resolveBodyEncoding2(charset) {
15412
15988
  }
15413
15989
  }
15414
15990
 
15415
- // src/network/probe.ts
15991
+ // ../runtime-core/src/network/probe.ts
15416
15992
  var TRANSPORT_PROBE_LADDER = [
15417
15993
  "direct-http",
15418
15994
  "matched-tls",
@@ -15424,7 +16000,7 @@ function selectTransportProbeRecommendation(results) {
15424
16000
  return results.find((entry) => entry.success)?.transport ?? results.at(-1)?.transport ?? "session-http";
15425
16001
  }
15426
16002
 
15427
- // src/reverse/analysis.ts
16003
+ // ../runtime-core/src/reverse/analysis.ts
15428
16004
  var TELEMETRY_HOST_PATTERNS = [
15429
16005
  "google-analytics",
15430
16006
  "doubleclick",
@@ -15764,9 +16340,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
15764
16340
  matches.add(`host:${host}`);
15765
16341
  }
15766
16342
  }
15767
- for (const path11 of targetHints.paths ?? []) {
15768
- if (url.pathname.includes(path11)) {
15769
- matches.add(`path:${path11}`);
16343
+ for (const path13 of targetHints.paths ?? []) {
16344
+ if (url.pathname.includes(path13)) {
16345
+ matches.add(`path:${path13}`);
15770
16346
  }
15771
16347
  }
15772
16348
  for (const operationName of targetHints.operationNames ?? []) {
@@ -16618,7 +17194,7 @@ function looksHighEntropy(value) {
16618
17194
  return uniqueCharacters >= Math.min(20, Math.floor(trimmed.length * 0.6));
16619
17195
  }
16620
17196
 
16621
- // src/reverse/discovery.ts
17197
+ // ../runtime-core/src/reverse/discovery.ts
16622
17198
  function clusterReverseObservationRecords(input) {
16623
17199
  const groups = /* @__PURE__ */ new Map();
16624
17200
  for (const item of sortClusterableRecords(input.records)) {
@@ -16918,14 +17494,14 @@ async function isExecutable(candidate) {
16918
17494
  }
16919
17495
  async function readDirSafe(directory) {
16920
17496
  try {
16921
- const { readdir: readdir3 } = await import('fs/promises');
16922
- return await readdir3(directory);
17497
+ const { readdir: readdir4 } = await import('fs/promises');
17498
+ return await readdir4(directory);
16923
17499
  } catch {
16924
17500
  return [];
16925
17501
  }
16926
17502
  }
16927
17503
 
16928
- // src/reverse/validation.ts
17504
+ // ../runtime-core/src/reverse/validation.ts
16929
17505
  function buildReverseValidationRules(input) {
16930
17506
  switch (input.channel.kind) {
16931
17507
  case "http":
@@ -17202,7 +17778,7 @@ function jsonStructureShape(value) {
17202
17778
  return typeof value;
17203
17779
  }
17204
17780
 
17205
- // src/reverse/workflows.ts
17781
+ // ../runtime-core/src/reverse/workflows.ts
17206
17782
  function buildReversePackageWorkflow(input) {
17207
17783
  if (input.template === void 0 && input.executeStepInput === void 0) {
17208
17784
  return [];
@@ -17571,7 +18147,7 @@ function dedupeSuggestedEdits(suggestions) {
17571
18147
  return [...new Map(suggestions.map((suggestion) => [suggestion.id, suggestion])).values()];
17572
18148
  }
17573
18149
 
17574
- // src/sdk/extraction-data-path.ts
18150
+ // ../runtime-core/src/sdk/extraction-data-path.ts
17575
18151
  function joinDataPath(base, key) {
17576
18152
  const normalizedBase = base.trim();
17577
18153
  const normalizedKey = key.trim();
@@ -17602,8 +18178,8 @@ function encodeDataPath(tokens) {
17602
18178
  }
17603
18179
  return out;
17604
18180
  }
17605
- function parseDataPath(path11) {
17606
- const input = path11.trim();
18181
+ function parseDataPath(path13) {
18182
+ const input = path13.trim();
17607
18183
  if (input.length === 0) {
17608
18184
  return [];
17609
18185
  }
@@ -17653,8 +18229,8 @@ function parseDataPath(path11) {
17653
18229
  function inflateDataPathObject(flat) {
17654
18230
  let root = {};
17655
18231
  let initialized = false;
17656
- for (const [path11, value] of Object.entries(flat)) {
17657
- const tokens = parseDataPath(path11);
18232
+ for (const [path13, value] of Object.entries(flat)) {
18233
+ const tokens = parseDataPath(path13);
17658
18234
  if (!tokens || tokens.length === 0) {
17659
18235
  continue;
17660
18236
  }
@@ -17712,7 +18288,7 @@ function assignDataPathValue(root, tokens, value) {
17712
18288
  }
17713
18289
  }
17714
18290
 
17715
- // src/sdk/extraction-consolidation.ts
18291
+ // ../runtime-core/src/sdk/extraction-consolidation.ts
17716
18292
  var STRUCTURAL_ATTR_KEYS = /* @__PURE__ */ new Set([
17717
18293
  "class",
17718
18294
  "role",
@@ -17986,8 +18562,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
17986
18562
  fields: mergedFields
17987
18563
  };
17988
18564
  }
17989
- function minimizePathMatchClauses(path11, mode) {
17990
- const normalized = sanitizeElementPath(path11);
18565
+ function minimizePathMatchClauses(path13, mode) {
18566
+ const normalized = sanitizeElementPath(path13);
17991
18567
  const nodes = normalized.nodes.map((node, index) => {
17992
18568
  const isLast = index === normalized.nodes.length - 1;
17993
18569
  const attrs = node.attrs || {};
@@ -18091,8 +18667,8 @@ function seedMinimalAttrClause(attrs) {
18091
18667
  }
18092
18668
  return null;
18093
18669
  }
18094
- function relaxPathForSingleSample(path11, mode) {
18095
- const normalized = sanitizeElementPath(path11);
18670
+ function relaxPathForSingleSample(path13, mode) {
18671
+ const normalized = sanitizeElementPath(path13);
18096
18672
  const relaxedNodes = normalized.nodes.map((node, index) => {
18097
18673
  const isLast = index === normalized.nodes.length - 1;
18098
18674
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -18177,8 +18753,8 @@ function shouldKeepAttrForSingleSample(key) {
18177
18753
  }
18178
18754
  return true;
18179
18755
  }
18180
- function buildPathStructureKey(path11) {
18181
- const normalized = sanitizeElementPath(path11);
18756
+ function buildPathStructureKey(path13) {
18757
+ const normalized = sanitizeElementPath(path13);
18182
18758
  return canonicalJsonString({
18183
18759
  context: (normalized.context || []).map((hop) => ({
18184
18760
  kind: hop.kind,
@@ -18305,30 +18881,30 @@ function buildArrayItemNode(fields) {
18305
18881
  }
18306
18882
  return node;
18307
18883
  }
18308
- function insertNodeAtPath(root, path11, node) {
18309
- const tokens = parseDataPath(path11);
18884
+ function insertNodeAtPath(root, path13, node) {
18885
+ const tokens = parseDataPath(path13);
18310
18886
  if (!tokens || !tokens.length) {
18311
18887
  throw new Error(
18312
- `Invalid persisted extraction path "${path11}": expected a non-empty object path.`
18888
+ `Invalid persisted extraction path "${path13}": expected a non-empty object path.`
18313
18889
  );
18314
18890
  }
18315
18891
  if (tokens.some((token) => token.kind === "index")) {
18316
18892
  throw new Error(
18317
- `Invalid persisted extraction path "${path11}": nested array indices are not supported in cached descriptors.`
18893
+ `Invalid persisted extraction path "${path13}": nested array indices are not supported in cached descriptors.`
18318
18894
  );
18319
18895
  }
18320
18896
  let current = root;
18321
18897
  for (let index = 0; index < tokens.length; index += 1) {
18322
18898
  const token = tokens[index];
18323
18899
  if (!token || token.kind !== "prop") {
18324
- throw new Error(`Invalid persisted extraction path "${path11}": expected object segment.`);
18900
+ throw new Error(`Invalid persisted extraction path "${path13}": expected object segment.`);
18325
18901
  }
18326
18902
  const isLast = index === tokens.length - 1;
18327
18903
  if (isLast) {
18328
18904
  const existing = current[token.key];
18329
18905
  if (existing) {
18330
18906
  throw new Error(
18331
- `Conflicting persisted extraction path "${path11}" detected while building descriptor tree.`
18907
+ `Conflicting persisted extraction path "${path13}" detected while building descriptor tree.`
18332
18908
  );
18333
18909
  }
18334
18910
  current[token.key] = node;
@@ -18343,7 +18919,7 @@ function insertNodeAtPath(root, path11, node) {
18343
18919
  }
18344
18920
  if (!isPersistedObjectNode(next)) {
18345
18921
  throw new Error(
18346
- `Conflicting persisted extraction path "${path11}" detected at "${token.key}".`
18922
+ `Conflicting persisted extraction path "${path13}" detected at "${token.key}".`
18347
18923
  );
18348
18924
  }
18349
18925
  current = next;
@@ -18378,7 +18954,7 @@ function buildItemRootForArrayIndex(entries) {
18378
18954
  }
18379
18955
  const paths = entries.map(
18380
18956
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
18381
- ).filter((path11) => path11 !== null);
18957
+ ).filter((path13) => path13 !== null);
18382
18958
  if (!paths.length) {
18383
18959
  return null;
18384
18960
  }
@@ -18399,7 +18975,7 @@ function getCommonPathPrefixLength(paths) {
18399
18975
  if (!paths.length) {
18400
18976
  return 0;
18401
18977
  }
18402
- const nodeChains = paths.map((path11) => path11.nodes);
18978
+ const nodeChains = paths.map((path13) => path13.nodes);
18403
18979
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
18404
18980
  if (!Number.isFinite(minLength) || minLength <= 0) {
18405
18981
  return 0;
@@ -18468,30 +19044,30 @@ function mergeElementPathsByMajority(paths) {
18468
19044
  if (!paths.length) {
18469
19045
  return null;
18470
19046
  }
18471
- const normalized = paths.map((path11) => sanitizeElementPath(path11));
19047
+ const normalized = paths.map((path13) => sanitizeElementPath(path13));
18472
19048
  const contextKey = pickModeString(
18473
- normalized.map((path11) => canonicalJsonString(path11.context)),
19049
+ normalized.map((path13) => canonicalJsonString(path13.context)),
18474
19050
  1
18475
19051
  );
18476
19052
  if (!contextKey) {
18477
19053
  return null;
18478
19054
  }
18479
- const sameContext = normalized.filter((path11) => canonicalJsonString(path11.context) === contextKey);
19055
+ const sameContext = normalized.filter((path13) => canonicalJsonString(path13.context) === contextKey);
18480
19056
  if (!sameContext.length) {
18481
19057
  return null;
18482
19058
  }
18483
19059
  const targetLength = pickModeNumber(
18484
- sameContext.map((path11) => path11.nodes.length),
19060
+ sameContext.map((path13) => path13.nodes.length),
18485
19061
  1
18486
19062
  ) ?? sameContext[0]?.nodes.length ?? 0;
18487
- const aligned = sameContext.filter((path11) => path11.nodes.length === targetLength);
19063
+ const aligned = sameContext.filter((path13) => path13.nodes.length === targetLength);
18488
19064
  if (!aligned.length) {
18489
19065
  return null;
18490
19066
  }
18491
19067
  const threshold = majorityThreshold(aligned.length);
18492
19068
  const nodes = [];
18493
19069
  for (let index = 0; index < targetLength; index += 1) {
18494
- const nodesAtIndex = aligned.map((path11) => path11.nodes[index]).filter((node) => node !== void 0);
19070
+ const nodesAtIndex = aligned.map((path13) => path13.nodes[index]).filter((node) => node !== void 0);
18495
19071
  if (!nodesAtIndex.length) {
18496
19072
  return null;
18497
19073
  }
@@ -18737,8 +19313,8 @@ function clonePathContext(context) {
18737
19313
  function clonePathNodes(nodes) {
18738
19314
  return JSON.parse(JSON.stringify(nodes || []));
18739
19315
  }
18740
- function cloneElementPath2(path11) {
18741
- return JSON.parse(JSON.stringify(path11));
19316
+ function cloneElementPath2(path13) {
19317
+ return JSON.parse(JSON.stringify(path13));
18742
19318
  }
18743
19319
  function clonePersistedOpensteerExtractionNode(node) {
18744
19320
  return JSON.parse(JSON.stringify(node));
@@ -18759,7 +19335,7 @@ function isPersistedObjectNode(node) {
18759
19335
  return !!node && typeof node === "object" && !Array.isArray(node) && !isPersistedOpensteerExtractionValueNode(node) && !isPersistedOpensteerExtractionSourceNode(node) && !isPersistedOpensteerExtractionArrayNode(node);
18760
19336
  }
18761
19337
 
18762
- // src/sdk/extraction.ts
19338
+ // ../runtime-core/src/sdk/extraction.ts
18763
19339
  function assertValidOpensteerExtractionSchemaRoot(schema) {
18764
19340
  if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
18765
19341
  throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
@@ -19024,12 +19600,12 @@ async function resolvePersistableFieldTargets(options) {
19024
19600
  anchor: field.anchor
19025
19601
  }
19026
19602
  });
19027
- const path11 = resolved.replayPath ?? await options.dom.buildPath({
19603
+ const path13 = resolved.replayPath ?? await options.dom.buildPath({
19028
19604
  locator: resolved.locator
19029
19605
  });
19030
19606
  fields.push({
19031
19607
  key: field.key,
19032
- path: sanitizeElementPath(path11),
19608
+ path: sanitizeElementPath(path13),
19033
19609
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
19034
19610
  });
19035
19611
  }
@@ -19112,8 +19688,8 @@ function collectPersistedValueNodeRefs(node) {
19112
19688
  return [
19113
19689
  {
19114
19690
  path: sanitizeElementPath(node.$path),
19115
- replacePath: (path11) => {
19116
- node.$path = sanitizeElementPath(path11);
19691
+ replacePath: (path13) => {
19692
+ node.$path = sanitizeElementPath(path13);
19117
19693
  }
19118
19694
  }
19119
19695
  ];
@@ -19127,13 +19703,13 @@ function collectPersistedValueNodeRefs(node) {
19127
19703
  }
19128
19704
  return refs;
19129
19705
  }
19130
- function hasPositionClause(path11) {
19131
- return path11.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
19706
+ function hasPositionClause(path13) {
19707
+ return path13.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
19132
19708
  }
19133
- function stripPositionClauses2(path11) {
19709
+ function stripPositionClauses2(path13) {
19134
19710
  return sanitizeElementPath({
19135
- context: path11.context,
19136
- nodes: path11.nodes.map((node) => ({
19711
+ context: path13.context,
19712
+ nodes: path13.nodes.map((node) => ({
19137
19713
  ...node,
19138
19714
  match: node.match.filter((clause) => clause.kind !== "position")
19139
19715
  }))
@@ -19543,14 +20119,14 @@ function normalizeNonEmptyString2(name, value) {
19543
20119
  function normalizeKey(value) {
19544
20120
  return String(value ?? "").trim();
19545
20121
  }
19546
- function labelForPath(path11) {
19547
- return path11.trim().length === 0 ? "$" : path11;
20122
+ function labelForPath(path13) {
20123
+ return path13.trim().length === 0 ? "$" : path13;
19548
20124
  }
19549
20125
  function sha256Hex3(value) {
19550
20126
  return crypto.createHash("sha256").update(value).digest("hex");
19551
20127
  }
19552
20128
 
19553
- // src/sdk/snapshot/constants.ts
20129
+ // ../runtime-core/src/sdk/snapshot/constants.ts
19554
20130
  var OPENSTEER_INTERACTIVE_ATTR = "data-opensteer-interactive";
19555
20131
  var OPENSTEER_HIDDEN_ATTR = "data-opensteer-hidden";
19556
20132
  var OPENSTEER_SCROLLABLE_ATTR = "data-opensteer-scrollable";
@@ -19618,7 +20194,7 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
19618
20194
  "wbr"
19619
20195
  ]);
19620
20196
 
19621
- // src/sdk/snapshot/cleaner.ts
20197
+ // ../runtime-core/src/sdk/snapshot/cleaner.ts
19622
20198
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
19623
20199
  var TEXT_ATTR_MAX = 150;
19624
20200
  var URL_ATTR_MAX = 500;
@@ -20058,7 +20634,7 @@ var VOID_TAGS2 = /* @__PURE__ */ new Set([
20058
20634
  "wbr"
20059
20635
  ]);
20060
20636
 
20061
- // src/sdk/snapshot/compiler.ts
20637
+ // ../runtime-core/src/sdk/snapshot/compiler.ts
20062
20638
  async function compileOpensteerSnapshot(options) {
20063
20639
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
20064
20640
  const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
@@ -20609,7 +21185,7 @@ async function beautifyScriptContent(content) {
20609
21185
  });
20610
21186
  }
20611
21187
 
20612
- // src/scripts/deobfuscate.ts
21188
+ // ../runtime-core/src/scripts/deobfuscate.ts
20613
21189
  async function deobfuscateScriptContent(input) {
20614
21190
  const webcrack = await loadWebcrack();
20615
21191
  const result = await webcrack(input.content, {
@@ -20667,7 +21243,7 @@ function inferTransforms(original, deobfuscated) {
20667
21243
  return [...inferred];
20668
21244
  }
20669
21245
 
20670
- // src/scripts/sandbox-shims/minimal.ts
21246
+ // ../runtime-core/src/scripts/sandbox-shims/minimal.ts
20671
21247
  function createMinimalSandboxGlobals(options) {
20672
21248
  return {
20673
21249
  console: options.console,
@@ -20691,7 +21267,7 @@ function createMinimalSandboxGlobals(options) {
20691
21267
  };
20692
21268
  }
20693
21269
 
20694
- // src/scripts/sandbox-shims/standard.ts
21270
+ // ../runtime-core/src/scripts/sandbox-shims/standard.ts
20695
21271
  function createStandardSandboxGlobals(options) {
20696
21272
  const globals = createMinimalSandboxGlobals(options);
20697
21273
  const eventApi = createEventTargetApi();
@@ -20962,7 +21538,7 @@ function normalizeErrorMessage(error) {
20962
21538
  return error instanceof Error ? error.message : String(error);
20963
21539
  }
20964
21540
 
20965
- // src/scripts/sandbox-shims/full.ts
21541
+ // ../runtime-core/src/scripts/sandbox-shims/full.ts
20966
21542
  function createFullSandboxGlobals(options) {
20967
21543
  const globals = createStandardSandboxGlobals(options);
20968
21544
  const eventListeners = /* @__PURE__ */ new WeakMap();
@@ -21082,7 +21658,7 @@ function createFullSandboxGlobals(options) {
21082
21658
  };
21083
21659
  }
21084
21660
 
21085
- // src/scripts/sandbox.ts
21661
+ // ../runtime-core/src/scripts/sandbox.ts
21086
21662
  async function runScriptSandbox(input) {
21087
21663
  const startedAt = Date.now();
21088
21664
  const errors = [];
@@ -21306,7 +21882,7 @@ function normalizeErrorMessage2(error) {
21306
21882
  return error instanceof Error ? error.message : String(error);
21307
21883
  }
21308
21884
 
21309
- // src/captcha/solver-capsolver.ts
21885
+ // ../runtime-core/src/captcha/solver-capsolver.ts
21310
21886
  var CAPSOLVER_CREATE_TASK_URL = "https://api.capsolver.com/createTask";
21311
21887
  var CAPSOLVER_GET_TASK_RESULT_URL = "https://api.capsolver.com/getTaskResult";
21312
21888
  function createCapSolver(apiKey) {
@@ -21404,7 +21980,7 @@ function sleep2(ms, signal) {
21404
21980
  });
21405
21981
  }
21406
21982
 
21407
- // src/captcha/solver-2captcha.ts
21983
+ // ../runtime-core/src/captcha/solver-2captcha.ts
21408
21984
  var TWO_CAPTCHA_CREATE_TASK_URL = "https://api.2captcha.com/createTask";
21409
21985
  var TWO_CAPTCHA_GET_TASK_RESULT_URL = "https://api.2captcha.com/getTaskResult";
21410
21986
  function createTwoCaptchaSolver(apiKey) {
@@ -21502,7 +22078,7 @@ function sleep3(ms, signal) {
21502
22078
  });
21503
22079
  }
21504
22080
 
21505
- // src/captcha/detect.ts
22081
+ // ../runtime-core/src/captcha/detect.ts
21506
22082
  var CAPTCHA_DETECTION_SCRIPT = `(() => {
21507
22083
  const pageUrl = location.href;
21508
22084
  const findSiteKey = (selectors) => {
@@ -21564,7 +22140,7 @@ async function detectCaptchaOnPage(engine, pageRef) {
21564
22140
  return candidate;
21565
22141
  }
21566
22142
 
21567
- // src/captcha/inject.ts
22143
+ // ../runtime-core/src/captcha/inject.ts
21568
22144
  async function injectCaptchaToken(input) {
21569
22145
  const result = await input.engine.evaluatePage({
21570
22146
  pageRef: input.pageRef,
@@ -21620,7 +22196,7 @@ var CAPTCHA_INJECTION_SCRIPT = `(({ type, token }) => {
21620
22196
  return true;
21621
22197
  })`;
21622
22198
 
21623
- // src/interaction/diff.ts
22199
+ // ../runtime-core/src/interaction/diff.ts
21624
22200
  function diffInteractionTraces(left, right) {
21625
22201
  const eventSequenceMismatches = [];
21626
22202
  const eventPropertyMismatches = [];
@@ -21689,20 +22265,17 @@ function diffInteractionTraces(left, right) {
21689
22265
  };
21690
22266
  }
21691
22267
 
21692
- // src/sdk/runtime.ts
22268
+ // ../runtime-core/src/sdk/runtime.ts
21693
22269
  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)));
21694
- var OpensteerRuntime = class {
22270
+ var OpensteerSessionRuntime = class {
21695
22271
  workspace;
21696
22272
  rootPath;
21697
- publicWorkspace;
21698
- configuredBrowser;
21699
- configuredLaunch;
21700
- configuredContext;
21701
- configuredEngineName;
22273
+ workspaceName;
21702
22274
  injectedEngine;
21703
22275
  engineFactory;
21704
22276
  policy;
21705
22277
  cleanupRootOnClose;
22278
+ sessionInfoBase;
21706
22279
  root;
21707
22280
  engine;
21708
22281
  dom;
@@ -21717,35 +22290,46 @@ var OpensteerRuntime = class {
21717
22290
  cookieJars = /* @__PURE__ */ new Map();
21718
22291
  recipeCache = /* @__PURE__ */ new Map();
21719
22292
  ownsEngine = false;
21720
- constructor(options = {}) {
21721
- this.publicWorkspace = options.workspace?.trim() === void 0 || options.workspace?.trim().length === 0 ? void 0 : options.workspace.trim();
21722
- this.workspace = normalizeNamespace3(options.workspace);
21723
- this.rootPath = options.rootPath ?? (this.publicWorkspace === void 0 ? path6__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : path6__default.default.resolve(
21724
- options.rootDir ?? process.cwd(),
21725
- ".opensteer",
21726
- "workspaces",
21727
- encodeURIComponent(this.publicWorkspace)
21728
- ));
21729
- this.configuredBrowser = options.browser;
21730
- this.configuredLaunch = options.launch;
21731
- this.configuredContext = options.context;
21732
- this.configuredEngineName = options.engineName;
22293
+ constructor(options) {
22294
+ this.workspace = normalizeNamespace3(options.name);
22295
+ this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
22296
+ this.root = options.workspace;
22297
+ this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6__default.default.resolve(process.cwd(), ".opensteer", "temporary", crypto.randomUUID());
21733
22298
  this.injectedEngine = options.engine;
21734
- this.engineFactory = options.engineFactory ?? ((factoryOptions) => {
21735
- const browser = factoryOptions.browser ?? this.configuredBrowser;
21736
- const launch = factoryOptions.launch ?? this.configuredLaunch;
21737
- const context = factoryOptions.context ?? this.configuredContext;
21738
- return new OpensteerBrowserManager({
21739
- rootPath: this.rootPath,
21740
- ...this.publicWorkspace === void 0 ? {} : { workspace: this.publicWorkspace },
21741
- ...this.configuredEngineName === void 0 ? {} : { engineName: this.configuredEngineName },
21742
- ...browser === void 0 ? {} : { browser },
21743
- ...launch === void 0 ? {} : { launch },
21744
- ...context === void 0 ? {} : { context }
21745
- }).createEngine();
21746
- });
22299
+ this.engineFactory = options.engineFactory;
21747
22300
  this.policy = options.policy ?? defaultPolicy();
21748
- this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.publicWorkspace === void 0;
22301
+ this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
22302
+ this.sessionInfoBase = options.sessionInfo ?? {};
22303
+ if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
22304
+ throw new Error("OpensteerSessionRuntime requires an engine or engineFactory.");
22305
+ }
22306
+ }
22307
+ async info() {
22308
+ const base = this.sessionInfoBase;
22309
+ return {
22310
+ provider: base.provider ?? {
22311
+ kind: "local",
22312
+ ownership: "owned",
22313
+ engine: "playwright"
22314
+ },
22315
+ ...base.workspace === void 0 ? {} : { workspace: base.workspace },
22316
+ ...this.sessionRef === void 0 ? {} : { sessionId: this.sessionRef },
22317
+ ...this.pageRef === void 0 ? {} : { activePageRef: this.pageRef },
22318
+ reconnectable: base.reconnectable ?? !this.cleanupRootOnClose,
22319
+ capabilities: base.capabilities ?? {
22320
+ semanticOperations: opensteerSemanticOperationNames,
22321
+ instrumentation: {
22322
+ route: true,
22323
+ interceptScript: true,
22324
+ networkStream: false
22325
+ }
22326
+ },
22327
+ ...base.grants === void 0 ? {} : { grants: base.grants },
22328
+ runtime: base.runtime ?? {
22329
+ protocolVersion: OPENSTEER_PROTOCOL_VERSION,
22330
+ runtimeCoreVersion: OPENSTEER_RUNTIME_CORE_VERSION
22331
+ }
22332
+ };
21749
22333
  }
21750
22334
  async open(input = {}, options = {}) {
21751
22335
  assertValidSemanticOperationInput("session.open", input);
@@ -26116,14 +26700,14 @@ var OpensteerRuntime = class {
26116
26700
  return saved;
26117
26701
  }
26118
26702
  resolveCurrentStateSource() {
26119
- const browser = this.configuredBrowser;
26120
- if (browser === void 0 || browser === "temporary") {
26121
- return "temporary";
26703
+ const ownership = this.sessionInfoBase.provider?.ownership;
26704
+ if (ownership === "attached") {
26705
+ return "attach";
26122
26706
  }
26123
- if (browser === "persistent") {
26707
+ if (this.workspaceName !== void 0 || this.cleanupRootOnClose === false) {
26124
26708
  return "persistent";
26125
26709
  }
26126
- return "attach";
26710
+ return "temporary";
26127
26711
  }
26128
26712
  async resolveReverseCaseById(caseId) {
26129
26713
  const record = await (await this.ensureRoot()).registry.reverseCases.getById(caseId);
@@ -27690,8 +28274,8 @@ var OpensteerRuntime = class {
27690
28274
  async ensureRoot() {
27691
28275
  this.root ??= await createFilesystemOpensteerWorkspace({
27692
28276
  rootPath: this.rootPath,
27693
- ...this.publicWorkspace === void 0 ? {} : { workspace: this.publicWorkspace },
27694
- scope: this.publicWorkspace === void 0 ? "temporary" : "workspace"
28277
+ ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
28278
+ scope: this.workspaceName === void 0 ? "temporary" : "workspace"
27695
28279
  });
27696
28280
  return this.root;
27697
28281
  }
@@ -27704,15 +28288,10 @@ var OpensteerRuntime = class {
27704
28288
  this.ownsEngine = false;
27705
28289
  return this.engine;
27706
28290
  }
27707
- const browser = overrides.browser ?? this.configuredBrowser;
27708
- const launch = overrides.launch ?? this.configuredLaunch;
27709
- const context = overrides.context ?? this.configuredContext;
27710
- const factoryOptions = {
27711
- ...browser === void 0 ? {} : { browser },
27712
- ...launch === void 0 ? {} : { launch },
27713
- ...context === void 0 ? {} : { context }
27714
- };
27715
- this.engine = await this.engineFactory(factoryOptions);
28291
+ if (this.engineFactory === void 0) {
28292
+ throw new Error("Opensteer engine factory is not initialized");
28293
+ }
28294
+ this.engine = await this.engineFactory(overrides);
27716
28295
  this.ownsEngine = true;
27717
28296
  return this.engine;
27718
28297
  }
@@ -28169,6 +28748,9 @@ function parseContentType2(contentType) {
28169
28748
  };
28170
28749
  }
28171
28750
  function toJsonValueOrNull(value) {
28751
+ if (value === void 0) {
28752
+ return null;
28753
+ }
28172
28754
  return toCanonicalJsonValue(value) ?? null;
28173
28755
  }
28174
28756
  function stringifyRecipeVariableValue(value) {
@@ -28966,12 +29548,12 @@ function extractReverseRuntimeValue(value, pointer) {
28966
29548
  }
28967
29549
  return readDotPath(value, pointer);
28968
29550
  }
28969
- function readDotPath(value, path11) {
28970
- if (path11.length === 0) {
29551
+ function readDotPath(value, path13) {
29552
+ if (path13.length === 0) {
28971
29553
  return value;
28972
29554
  }
28973
29555
  let current = value;
28974
- for (const segment of path11.split(".").filter((entry) => entry.length > 0)) {
29556
+ for (const segment of path13.split(".").filter((entry) => entry.length > 0)) {
28975
29557
  if (current === null || current === void 0) {
28976
29558
  return void 0;
28977
29559
  }
@@ -29444,7 +30026,7 @@ function parseSetCookieHeader(value, requestUrl) {
29444
30026
  }
29445
30027
  const url = new URL(requestUrl);
29446
30028
  let domain = url.hostname;
29447
- let path11 = defaultCookiePath(url.pathname);
30029
+ let path13 = defaultCookiePath(url.pathname);
29448
30030
  let secure = url.protocol === "https:";
29449
30031
  let expiresAt;
29450
30032
  const cookieValue = rawValueParts.join("=").trim();
@@ -29457,7 +30039,7 @@ function parseSetCookieHeader(value, requestUrl) {
29457
30039
  continue;
29458
30040
  }
29459
30041
  if (key === "path" && attributeValue.length > 0) {
29460
- path11 = attributeValue;
30042
+ path13 = attributeValue;
29461
30043
  continue;
29462
30044
  }
29463
30045
  if (key === "secure") {
@@ -29483,7 +30065,7 @@ function parseSetCookieHeader(value, requestUrl) {
29483
30065
  name,
29484
30066
  value: cookieValue,
29485
30067
  domain,
29486
- path: path11,
30068
+ path: path13,
29487
30069
  secure,
29488
30070
  ...expiresAt === void 0 ? {} : { expiresAt }
29489
30071
  }
@@ -30507,6 +31089,100 @@ function screenshotMediaType(format2) {
30507
31089
  }
30508
31090
  }
30509
31091
 
31092
+ // src/sdk/runtime.ts
31093
+ var OpensteerRuntime = class extends OpensteerSessionRuntime {
31094
+ constructor(options = {}) {
31095
+ const publicWorkspace = normalizeWorkspace2(options.workspace);
31096
+ const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path6__default.default.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", crypto.randomUUID()) : resolveFilesystemWorkspacePath({
31097
+ rootDir: path6__default.default.resolve(options.rootDir ?? process.cwd()),
31098
+ workspace: publicWorkspace
31099
+ }));
31100
+ const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
31101
+ const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31102
+ assertSupportedEngineOptions({
31103
+ engineName,
31104
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31105
+ ...options.context === void 0 ? {} : { context: options.context }
31106
+ });
31107
+ super(buildSharedRuntimeOptions({
31108
+ name: publicWorkspace ?? "default",
31109
+ rootPath,
31110
+ ...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
31111
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31112
+ ...options.launch === void 0 ? {} : { launch: options.launch },
31113
+ ...options.context === void 0 ? {} : { context: options.context },
31114
+ engineName,
31115
+ ...options.engine === void 0 ? {} : { engine: options.engine },
31116
+ ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
31117
+ ...options.policy === void 0 ? {} : { policy: options.policy },
31118
+ cleanupRootOnClose
31119
+ }));
31120
+ }
31121
+ };
31122
+ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31123
+ constructor(options) {
31124
+ const rootPath = options.rootPath ?? path6__default.default.resolve(options.rootDir ?? process.cwd());
31125
+ const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
31126
+ const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31127
+ assertSupportedEngineOptions({
31128
+ engineName,
31129
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31130
+ ...options.context === void 0 ? {} : { context: options.context }
31131
+ });
31132
+ super(buildSharedRuntimeOptions({
31133
+ name: options.name,
31134
+ rootPath,
31135
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31136
+ ...options.launch === void 0 ? {} : { launch: options.launch },
31137
+ ...options.context === void 0 ? {} : { context: options.context },
31138
+ engineName,
31139
+ ...options.engine === void 0 ? {} : { engine: options.engine },
31140
+ ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
31141
+ ...options.policy === void 0 ? {} : { policy: options.policy },
31142
+ cleanupRootOnClose
31143
+ }));
31144
+ }
31145
+ };
31146
+ function buildSharedRuntimeOptions(input) {
31147
+ const ownership = resolveOwnership(input.browser);
31148
+ const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
31149
+ rootPath: input.rootPath,
31150
+ ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
31151
+ engineName: input.engineName,
31152
+ ...(factoryOptions.browser ?? input.browser) === void 0 ? {} : { browser: factoryOptions.browser ?? input.browser },
31153
+ ...(factoryOptions.launch ?? input.launch) === void 0 ? {} : { launch: factoryOptions.launch ?? input.launch },
31154
+ ...(factoryOptions.context ?? input.context) === void 0 ? {} : { context: factoryOptions.context ?? input.context }
31155
+ }).createEngine());
31156
+ return {
31157
+ name: input.name,
31158
+ ...input.workspaceName === void 0 ? {} : { workspaceName: input.workspaceName },
31159
+ rootPath: input.rootPath,
31160
+ ...input.engine === void 0 ? {} : { engine: input.engine },
31161
+ ...input.engine === void 0 ? { engineFactory } : {},
31162
+ ...input.policy === void 0 ? {} : { policy: input.policy },
31163
+ cleanupRootOnClose: input.cleanupRootOnClose,
31164
+ sessionInfo: {
31165
+ provider: {
31166
+ kind: "local",
31167
+ ownership,
31168
+ engine: input.engineName
31169
+ },
31170
+ ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
31171
+ reconnectable: !input.cleanupRootOnClose
31172
+ }
31173
+ };
31174
+ }
31175
+ function normalizeWorkspace2(workspace) {
31176
+ if (workspace === void 0) {
31177
+ return void 0;
31178
+ }
31179
+ const trimmed = workspace.trim();
31180
+ return trimmed.length === 0 ? void 0 : trimmed;
31181
+ }
31182
+ function resolveOwnership(browser) {
31183
+ return typeof browser === "object" && browser.mode === "attach" ? "attached" : "owned";
31184
+ }
31185
+
30510
31186
  // src/mode/config.ts
30511
31187
  var OPENSTEER_EXECUTION_MODES = ["local", "cloud"];
30512
31188
  function assertExecutionModeSupportsEngine(mode, engine) {
@@ -30942,14 +31618,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
30942
31618
  if (!name || !domain) {
30943
31619
  return null;
30944
31620
  }
30945
- const path11 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
31621
+ const path13 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
30946
31622
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
30947
31623
  const sameSite = normalizeSameSite(cookie.sameSite);
30948
31624
  return {
30949
31625
  name,
30950
31626
  value: cookie.value,
30951
31627
  domain,
30952
- path: path11,
31628
+ path: path13,
30953
31629
  secure: cookie.secure,
30954
31630
  httpOnly: cookie.httpOnly,
30955
31631
  ...sameSite === void 0 ? {} : { sameSite },
@@ -31126,6 +31802,18 @@ var OpensteerCloudClient = class {
31126
31802
  });
31127
31803
  return await response.json();
31128
31804
  }
31805
+ async issueAccess(sessionId, capabilities) {
31806
+ const response = await this.request(
31807
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
31808
+ {
31809
+ method: "POST",
31810
+ body: {
31811
+ capabilities
31812
+ }
31813
+ }
31814
+ );
31815
+ return await response.json();
31816
+ }
31129
31817
  async closeSession(sessionId) {
31130
31818
  const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
31131
31819
  method: "DELETE"
@@ -31331,58 +32019,367 @@ function isFetchFailure(error) {
31331
32019
  }
31332
32020
  return error.name === "TypeError" || /fetch failed/i.test(error.message);
31333
32021
  }
31334
-
31335
- // src/cloud/session-proxy.ts
31336
- var CLOUD_SESSION_LAYOUT = "opensteer-cloud-session";
31337
- var CLOUD_SESSION_VERSION = 1;
31338
- var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
31339
- var SUPPORTED_CLOUD_OPERATIONS = /* @__PURE__ */ new Set([
31340
- "session.open",
31341
- "page.goto",
31342
- "page.snapshot",
31343
- "dom.click",
31344
- "dom.hover",
31345
- "dom.input",
31346
- "dom.scroll",
31347
- "dom.extract",
31348
- "network.query",
31349
- "network.save",
31350
- "network.clear",
31351
- "request.raw",
31352
- "request-plan.infer",
31353
- "request-plan.write",
31354
- "request-plan.get",
31355
- "request-plan.list",
31356
- "request.execute",
31357
- "computer.execute",
31358
- "session.close"
31359
- ]);
31360
- function resolveCloudSessionRecordPath(rootPath) {
31361
- return path6__default.default.join(rootPath, "live", "cloud-session.json");
32022
+ var OpensteerCloudAutomationError = class extends Error {
32023
+ opensteerError;
32024
+ constructor(error) {
32025
+ super(error.message);
32026
+ this.name = "OpensteerCloudAutomationError";
32027
+ this.opensteerError = error;
32028
+ }
32029
+ };
32030
+ var OpensteerCloudAutomationClient = class {
32031
+ constructor(cloud, sessionId) {
32032
+ this.cloud = cloud;
32033
+ this.sessionId = sessionId;
32034
+ }
32035
+ socket;
32036
+ connectPromise;
32037
+ pending = /* @__PURE__ */ new Map();
32038
+ routes = /* @__PURE__ */ new Map();
32039
+ grant;
32040
+ async invoke(operation, input) {
32041
+ await this.ensureConnected();
32042
+ const requestId = `automation:${crypto.randomUUID()}`;
32043
+ const message = {
32044
+ protocol: OPENSTEER_PROTOCOL_NAME,
32045
+ version: OPENSTEER_PROTOCOL_VERSION,
32046
+ kind: "invoke",
32047
+ requestId,
32048
+ operation,
32049
+ sentAt: Date.now(),
32050
+ ...input === void 0 ? {} : { input }
32051
+ };
32052
+ return new Promise((resolve5, reject) => {
32053
+ this.pending.set(requestId, {
32054
+ resolve: (value) => resolve5(value),
32055
+ reject
32056
+ });
32057
+ try {
32058
+ this.requireSocket().send(JSON.stringify(message));
32059
+ } catch (error) {
32060
+ this.pending.delete(requestId);
32061
+ reject(error);
32062
+ }
32063
+ });
32064
+ }
32065
+ async getSessionInfo() {
32066
+ const result = await this.invoke("session.info", {});
32067
+ const sessionInfo = result;
32068
+ assertCompatibleRuntimeCoreVersion(sessionInfo);
32069
+ return sessionInfo;
32070
+ }
32071
+ async route(input) {
32072
+ const routeId = `route:${crypto.randomUUID()}`;
32073
+ const registration = await this.invoke("route.register", {
32074
+ routeId,
32075
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
32076
+ urlPattern: input.urlPattern,
32077
+ ...input.resourceTypes === void 0 ? {} : { resourceTypes: input.resourceTypes },
32078
+ ...input.times === void 0 ? {} : { times: input.times },
32079
+ includeOriginal: true
32080
+ });
32081
+ this.routes.set(routeId, {
32082
+ kind: "route",
32083
+ routeId,
32084
+ input
32085
+ });
32086
+ return registration;
32087
+ }
32088
+ async interceptScript(input) {
32089
+ const routeId = `route:${crypto.randomUUID()}`;
32090
+ const registration = await this.invoke("route.register", {
32091
+ routeId,
32092
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
32093
+ urlPattern: input.urlPattern,
32094
+ resourceTypes: ["script"],
32095
+ ...input.times === void 0 ? {} : { times: input.times },
32096
+ includeOriginal: true
32097
+ });
32098
+ this.routes.set(routeId, {
32099
+ kind: "intercept-script",
32100
+ routeId,
32101
+ input
32102
+ });
32103
+ return registration;
32104
+ }
32105
+ async close() {
32106
+ this.connectPromise = void 0;
32107
+ this.grant = void 0;
32108
+ if (!this.socket) {
32109
+ return;
32110
+ }
32111
+ const socket = this.socket;
32112
+ this.socket = void 0;
32113
+ for (const [requestId, pending] of this.pending) {
32114
+ pending.reject(new Error(`automation connection closed before ${requestId} completed`));
32115
+ }
32116
+ this.pending.clear();
32117
+ await new Promise((resolve5) => {
32118
+ socket.once("close", () => resolve5());
32119
+ socket.close();
32120
+ }).catch(() => void 0);
32121
+ }
32122
+ async ensureConnected() {
32123
+ if (this.socket?.readyState === WebSocket2__default.default.OPEN) {
32124
+ return;
32125
+ }
32126
+ if (this.connectPromise) {
32127
+ await this.connectPromise;
32128
+ return;
32129
+ }
32130
+ this.connectPromise = this.connect();
32131
+ try {
32132
+ await this.connectPromise;
32133
+ } finally {
32134
+ this.connectPromise = void 0;
32135
+ }
32136
+ }
32137
+ async connect() {
32138
+ const grant = await this.issueGrant("automation");
32139
+ const wsUrl = new URL(grant.wsUrl);
32140
+ wsUrl.searchParams.set("token", grant.token);
32141
+ const socket = new WebSocket2__default.default(wsUrl);
32142
+ this.socket = socket;
32143
+ socket.on("message", (data, isBinary) => {
32144
+ if (isBinary) {
32145
+ return;
32146
+ }
32147
+ this.handleMessage(data.toString());
32148
+ });
32149
+ socket.on("close", () => {
32150
+ if (this.socket === socket) {
32151
+ this.socket = void 0;
32152
+ }
32153
+ });
32154
+ socket.on("error", (error) => {
32155
+ for (const pending of this.pending.values()) {
32156
+ pending.reject(error);
32157
+ }
32158
+ this.pending.clear();
32159
+ });
32160
+ await new Promise((resolve5, reject) => {
32161
+ socket.once("open", () => resolve5());
32162
+ socket.once("error", reject);
32163
+ });
32164
+ this.send({
32165
+ protocol: OPENSTEER_PROTOCOL_NAME,
32166
+ version: OPENSTEER_PROTOCOL_VERSION,
32167
+ kind: "hello",
32168
+ sessionId: this.sessionId,
32169
+ grantKind: grant.kind
32170
+ });
32171
+ await this.restoreRoutes();
32172
+ }
32173
+ async restoreRoutes() {
32174
+ const stored = [...this.routes.values()];
32175
+ this.routes.clear();
32176
+ for (const registration of stored) {
32177
+ if (registration.kind === "route") {
32178
+ await this.route(registration.input);
32179
+ } else {
32180
+ await this.interceptScript(
32181
+ registration.input
32182
+ );
32183
+ }
32184
+ }
32185
+ }
32186
+ handleMessage(json) {
32187
+ const message = JSON.parse(json);
32188
+ if (message.protocol !== OPENSTEER_PROTOCOL_NAME) {
32189
+ return;
32190
+ }
32191
+ switch (message.kind) {
32192
+ case "result": {
32193
+ const pending = this.pending.get(message.requestId);
32194
+ if (!pending) {
32195
+ return;
32196
+ }
32197
+ this.pending.delete(message.requestId);
32198
+ pending.resolve(message.data);
32199
+ return;
32200
+ }
32201
+ case "error": {
32202
+ if (!message.requestId) {
32203
+ return;
32204
+ }
32205
+ const pending = this.pending.get(message.requestId);
32206
+ if (!pending) {
32207
+ return;
32208
+ }
32209
+ this.pending.delete(message.requestId);
32210
+ pending.reject(new OpensteerCloudAutomationError(message.error));
32211
+ return;
32212
+ }
32213
+ case "event":
32214
+ void this.handleEvent(message.event, message.data);
32215
+ return;
32216
+ case "pong":
32217
+ return;
32218
+ }
32219
+ }
32220
+ async handleEvent(event, payload) {
32221
+ if (event !== "route.request") {
32222
+ return;
32223
+ }
32224
+ const data = asRecord(payload);
32225
+ const request = asRecord(data.request);
32226
+ const original = asRecord(data.original);
32227
+ const routeId = typeof data.routeId === "string" ? data.routeId : "";
32228
+ const routeRequestId = typeof data.routeRequestId === "string" ? data.routeRequestId : "";
32229
+ if (!routeId || !routeRequestId) {
32230
+ return;
32231
+ }
32232
+ const registration = this.routes.get(routeId);
32233
+ if (!registration) {
32234
+ await this.invoke("route.resolve", {
32235
+ routeRequestId,
32236
+ decision: { kind: "continue" }
32237
+ }).catch(() => void 0);
32238
+ return;
32239
+ }
32240
+ try {
32241
+ const decision = registration.kind === "route" ? await registration.input.handler({
32242
+ request: toRouteRequest(request),
32243
+ fetchOriginal: async () => toFetchedRouteResponse(original)
32244
+ }) : {
32245
+ kind: "fulfill",
32246
+ body: await registration.input.handler({
32247
+ url: typeof request.url === "string" ? request.url : "",
32248
+ content: typeof original.body === "string" ? original.body : "",
32249
+ headers: Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32250
+ status: typeof original.status === "number" ? original.status : 200
32251
+ }),
32252
+ headers: Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32253
+ status: typeof original.status === "number" ? original.status : 200,
32254
+ contentType: findHeaderValue(
32255
+ Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32256
+ "content-type"
32257
+ ) ?? "application/javascript; charset=utf-8"
32258
+ };
32259
+ await this.invoke("route.resolve", {
32260
+ routeRequestId,
32261
+ decision: serializeRouteDecision(decision)
32262
+ }).catch(() => void 0);
32263
+ } catch {
32264
+ await this.invoke("route.resolve", {
32265
+ routeRequestId,
32266
+ decision: { kind: "continue" }
32267
+ }).catch(() => void 0);
32268
+ }
32269
+ }
32270
+ async issueGrant(kind) {
32271
+ if (this.grant && this.grant.kind === kind && this.grant.expiresAt > Date.now() + 1e4) {
32272
+ return this.grant;
32273
+ }
32274
+ const issued = await this.cloud.issueAccess(this.sessionId, [kind]);
32275
+ const grant = issued.grants[kind];
32276
+ if (!grant) {
32277
+ throw new OpensteerCloudAutomationError(
32278
+ createOpensteerError(
32279
+ "permission-denied",
32280
+ `cloud did not issue an ${kind} automation grant`
32281
+ )
32282
+ );
32283
+ }
32284
+ this.grant = grant;
32285
+ return grant;
32286
+ }
32287
+ requireSocket() {
32288
+ if (!this.socket || this.socket.readyState !== WebSocket2__default.default.OPEN) {
32289
+ throw new Error("cloud automation socket is not connected");
32290
+ }
32291
+ return this.socket;
32292
+ }
32293
+ send(message) {
32294
+ this.requireSocket().send(JSON.stringify(message));
32295
+ }
32296
+ };
32297
+ function assertCompatibleRuntimeCoreVersion(sessionInfo) {
32298
+ const runtimeCoreVersion = sessionInfo.runtime?.runtimeCoreVersion;
32299
+ if (runtimeCoreVersion === void 0) {
32300
+ return;
32301
+ }
32302
+ const expectedMajor = parseMajorVersion(OPENSTEER_RUNTIME_CORE_VERSION);
32303
+ const actualMajor = parseMajorVersion(runtimeCoreVersion);
32304
+ if (expectedMajor === null || actualMajor === null || expectedMajor === actualMajor) {
32305
+ return;
32306
+ }
32307
+ throw new Error(
32308
+ `cloud runtime-core major version ${runtimeCoreVersion} is incompatible with local SDK runtime-core ${OPENSTEER_RUNTIME_CORE_VERSION}`
32309
+ );
31362
32310
  }
31363
- async function readPersistedCloudSessionRecord(rootPath) {
31364
- const sessionPath = resolveCloudSessionRecordPath(rootPath);
31365
- if (!await pathExists(sessionPath)) {
31366
- return void 0;
32311
+ function parseMajorVersion(version) {
32312
+ const major = Number.parseInt(version.split(".", 1)[0] ?? "", 10);
32313
+ return Number.isFinite(major) ? major : null;
32314
+ }
32315
+ function serializeRouteDecision(decision) {
32316
+ if (decision.kind === "continue") {
32317
+ return { kind: "continue" };
31367
32318
  }
31368
- const parsed = await readJsonFile(sessionPath);
31369
- if (parsed.layout !== CLOUD_SESSION_LAYOUT || parsed.version !== CLOUD_SESSION_VERSION || parsed.mode !== "cloud" || typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0 || typeof parsed.baseUrl !== "string" || parsed.baseUrl.length === 0 || typeof parsed.startedAt !== "number" || !Number.isFinite(parsed.startedAt) || typeof parsed.updatedAt !== "number" || !Number.isFinite(parsed.updatedAt)) {
31370
- return void 0;
32319
+ if (decision.kind === "abort") {
32320
+ return {
32321
+ kind: "abort",
32322
+ ...decision.errorCode === void 0 ? {} : { errorCode: decision.errorCode }
32323
+ };
31371
32324
  }
31372
32325
  return {
31373
- layout: CLOUD_SESSION_LAYOUT,
31374
- version: CLOUD_SESSION_VERSION,
31375
- mode: "cloud",
31376
- ...parsed.workspace === void 0 ? {} : { workspace: parsed.workspace },
31377
- sessionId: parsed.sessionId,
31378
- baseUrl: parsed.baseUrl,
31379
- startedAt: parsed.startedAt,
31380
- updatedAt: parsed.updatedAt
32326
+ kind: "fulfill",
32327
+ ...decision.status === void 0 ? {} : { status: decision.status },
32328
+ ...decision.headers === void 0 ? {} : { headers: decision.headers },
32329
+ ...decision.body === void 0 ? {} : typeof decision.body === "string" ? { body: decision.body } : { bodyBase64: Buffer.from(decision.body).toString("base64") },
32330
+ ...decision.contentType === void 0 ? {} : { contentType: decision.contentType }
31381
32331
  };
31382
32332
  }
31383
- async function hasPersistedCloudSession(rootPath) {
31384
- return await readPersistedCloudSessionRecord(rootPath) !== void 0;
32333
+ function toRouteRequest(record) {
32334
+ const pageRef = typeof record.pageRef === "string" ? record.pageRef : void 0;
32335
+ return {
32336
+ url: typeof record.url === "string" ? record.url : "",
32337
+ method: typeof record.method === "string" ? record.method : "GET",
32338
+ headers: Array.isArray(record.headers) ? record.headers.filter(isHeaderEntry) : [],
32339
+ resourceType: typeof record.resourceType === "string" ? record.resourceType : "other",
32340
+ ...pageRef === void 0 ? {} : { pageRef },
32341
+ ...typeof record.postData === "string" ? {
32342
+ postData: {
32343
+ bytes: Uint8Array.from(Buffer.from(record.postData)),
32344
+ encoding: "identity",
32345
+ truncated: false,
32346
+ capturedByteLength: Buffer.byteLength(record.postData)
32347
+ }
32348
+ } : {}
32349
+ };
31385
32350
  }
32351
+ function toFetchedRouteResponse(record) {
32352
+ return {
32353
+ url: typeof record.url === "string" ? record.url : "",
32354
+ status: typeof record.status === "number" ? record.status : 200,
32355
+ statusText: typeof record.statusText === "string" ? record.statusText : "OK",
32356
+ headers: Array.isArray(record.headers) ? record.headers.filter(isHeaderEntry) : [],
32357
+ ...typeof record.body === "string" ? {
32358
+ body: {
32359
+ bytes: Uint8Array.from(Buffer.from(record.body)),
32360
+ encoding: "identity",
32361
+ truncated: false,
32362
+ capturedByteLength: Buffer.byteLength(record.body)
32363
+ }
32364
+ } : {},
32365
+ redirected: Boolean(record.redirected)
32366
+ };
32367
+ }
32368
+ function findHeaderValue(headers, name) {
32369
+ return headers.find((header) => header.name.toLowerCase() === name)?.value;
32370
+ }
32371
+ function isHeaderEntry(value) {
32372
+ return value !== null && typeof value === "object" && typeof value.name === "string" && typeof value.value === "string";
32373
+ }
32374
+ function asRecord(value) {
32375
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
32376
+ return {};
32377
+ }
32378
+ return value;
32379
+ }
32380
+
32381
+ // src/cloud/session-proxy.ts
32382
+ var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
31386
32383
  var CloudSessionProxy = class {
31387
32384
  rootPath;
31388
32385
  workspace;
@@ -31391,6 +32388,7 @@ var CloudSessionProxy = class {
31391
32388
  sessionId;
31392
32389
  sessionBaseUrl;
31393
32390
  client;
32391
+ automation;
31394
32392
  workspaceStore;
31395
32393
  constructor(cloud, options = {}) {
31396
32394
  this.cloud = cloud;
@@ -31411,27 +32409,73 @@ var CloudSessionProxy = class {
31411
32409
  ...input.url === void 0 ? {} : { url: input.url }
31412
32410
  });
31413
32411
  }
32412
+ async info() {
32413
+ const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
32414
+ if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
32415
+ this.bindClient(persisted);
32416
+ }
32417
+ if (this.automation) {
32418
+ try {
32419
+ const sessionInfo = await this.automation.getSessionInfo();
32420
+ return {
32421
+ ...sessionInfo,
32422
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace }
32423
+ };
32424
+ } catch {
32425
+ }
32426
+ }
32427
+ return {
32428
+ provider: {
32429
+ kind: "cloud",
32430
+ ownership: "managed",
32431
+ engine: "playwright",
32432
+ baseUrl: this.cloud.getConfig().baseUrl
32433
+ },
32434
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
32435
+ ...this.sessionId === void 0 ? persisted?.sessionId === void 0 ? {} : { sessionId: persisted.sessionId } : { sessionId: this.sessionId },
32436
+ reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
32437
+ capabilities: {
32438
+ semanticOperations: opensteerSemanticOperationNames,
32439
+ sessionGrants: ["automation", "view", "cdp"],
32440
+ instrumentation: {
32441
+ route: true,
32442
+ interceptScript: true,
32443
+ networkStream: true
32444
+ }
32445
+ },
32446
+ runtime: {
32447
+ protocolVersion: OPENSTEER_PROTOCOL_VERSION,
32448
+ runtimeCoreVersion: OPENSTEER_RUNTIME_CORE_VERSION
32449
+ }
32450
+ };
32451
+ }
31414
32452
  async listPages(input = {}) {
31415
- throw unsupportedCloudOperation("page.list");
32453
+ await this.ensureSession();
32454
+ return this.requireClient().invoke("page.list", input);
31416
32455
  }
31417
32456
  async newPage(input = {}) {
31418
- throw unsupportedCloudOperation("page.new");
32457
+ await this.ensureSession();
32458
+ return this.requireAutomation().invoke("page.new", input);
31419
32459
  }
31420
32460
  async activatePage(input) {
31421
- throw unsupportedCloudOperation("page.activate");
32461
+ await this.ensureSession();
32462
+ return this.requireClient().invoke("page.activate", input);
31422
32463
  }
31423
32464
  async closePage(input = {}) {
31424
- throw unsupportedCloudOperation("page.close");
32465
+ await this.ensureSession();
32466
+ return this.requireClient().invoke("page.close", input);
31425
32467
  }
31426
32468
  async goto(input) {
31427
32469
  await this.ensureSession();
31428
32470
  return this.requireClient().invoke("page.goto", input);
31429
32471
  }
31430
32472
  async evaluate(input) {
31431
- throw unsupportedCloudOperation("page.evaluate");
32473
+ await this.ensureSession();
32474
+ return this.requireAutomation().invoke("page.evaluate", input);
31432
32475
  }
31433
32476
  async addInitScript(input) {
31434
- throw unsupportedCloudOperation("page.add-init-script");
32477
+ await this.ensureSession();
32478
+ return this.requireClient().invoke("page.add-init-script", input);
31435
32479
  }
31436
32480
  async snapshot(input = {}) {
31437
32481
  await this.ensureSession();
@@ -31466,80 +32510,112 @@ var CloudSessionProxy = class {
31466
32510
  return this.requireClient().invoke("network.save", input);
31467
32511
  }
31468
32512
  async minimizeNetwork(input) {
31469
- throw unsupportedCloudOperation("network.minimize");
32513
+ await this.ensureSession();
32514
+ return this.requireClient().invoke("network.minimize", input);
31470
32515
  }
31471
32516
  async diffNetwork(input) {
31472
- throw unsupportedCloudOperation("network.diff");
32517
+ await this.ensureSession();
32518
+ return this.requireClient().invoke("network.diff", input);
31473
32519
  }
31474
32520
  async probeNetwork(input) {
31475
- throw unsupportedCloudOperation("network.probe");
32521
+ await this.ensureSession();
32522
+ return this.requireClient().invoke("network.probe", input);
31476
32523
  }
31477
32524
  async discoverReverse(input) {
31478
- throw unsupportedCloudOperation("reverse.discover");
32525
+ await this.ensureSession();
32526
+ return this.requireClient().invoke("reverse.discover", input);
31479
32527
  }
31480
32528
  async queryReverse(input) {
31481
- throw unsupportedCloudOperation("reverse.query");
32529
+ await this.ensureSession();
32530
+ return this.requireClient().invoke("reverse.query", input);
31482
32531
  }
31483
32532
  async createReversePackage(input) {
31484
- throw unsupportedCloudOperation("reverse.package.create");
32533
+ await this.ensureSession();
32534
+ return this.requireClient().invoke("reverse.package.create", input);
31485
32535
  }
31486
32536
  async runReversePackage(input) {
31487
- throw unsupportedCloudOperation("reverse.package.run");
32537
+ await this.ensureSession();
32538
+ return this.requireClient().invoke("reverse.package.run", input);
31488
32539
  }
31489
32540
  async exportReverse(input) {
31490
- throw unsupportedCloudOperation("reverse.export");
32541
+ await this.ensureSession();
32542
+ return this.requireClient().invoke("reverse.export", input);
31491
32543
  }
31492
32544
  async getReverseReport(input) {
31493
- throw unsupportedCloudOperation("reverse.report");
32545
+ await this.ensureSession();
32546
+ return this.requireClient().invoke("reverse.report", input);
31494
32547
  }
31495
32548
  async getReversePackage(input) {
31496
- throw unsupportedCloudOperation("reverse.package.get");
32549
+ await this.ensureSession();
32550
+ return this.requireClient().invoke("reverse.package.get", input);
31497
32551
  }
31498
32552
  async listReversePackages(input = {}) {
31499
- throw unsupportedCloudOperation("reverse.package.list");
32553
+ await this.ensureSession();
32554
+ return this.requireClient().invoke("reverse.package.list", input);
31500
32555
  }
31501
32556
  async patchReversePackage(input) {
31502
- throw unsupportedCloudOperation("reverse.package.patch");
32557
+ await this.ensureSession();
32558
+ return this.requireClient().invoke("reverse.package.patch", input);
31503
32559
  }
31504
32560
  async captureInteraction(input) {
31505
- throw unsupportedCloudOperation("interaction.capture");
32561
+ await this.ensureSession();
32562
+ return this.requireClient().invoke("interaction.capture", input);
31506
32563
  }
31507
32564
  async getInteraction(input) {
31508
- throw unsupportedCloudOperation("interaction.get");
32565
+ await this.ensureSession();
32566
+ return this.requireClient().invoke("interaction.get", input);
31509
32567
  }
31510
32568
  async diffInteraction(input) {
31511
- throw unsupportedCloudOperation("interaction.diff");
32569
+ await this.ensureSession();
32570
+ return this.requireClient().invoke("interaction.diff", input);
31512
32571
  }
31513
32572
  async replayInteraction(input) {
31514
- throw unsupportedCloudOperation("interaction.replay");
32573
+ await this.ensureSession();
32574
+ return this.requireClient().invoke("interaction.replay", input);
31515
32575
  }
31516
32576
  async clearNetwork(input = {}) {
31517
32577
  await this.ensureSession();
31518
32578
  return this.requireClient().invoke("network.clear", input);
31519
32579
  }
31520
32580
  async captureScripts(input = {}) {
31521
- throw unsupportedCloudOperation("scripts.capture");
32581
+ await this.ensureSession();
32582
+ return this.requireClient().invoke("scripts.capture", input);
31522
32583
  }
31523
32584
  async readArtifact(input) {
31524
- throw unsupportedCloudOperation("artifact.read");
32585
+ await this.ensureSession();
32586
+ return this.requireClient().invoke("artifact.read", input);
31525
32587
  }
31526
32588
  async beautifyScript(input) {
31527
- throw unsupportedCloudOperation("scripts.beautify");
32589
+ await this.ensureSession();
32590
+ return this.requireClient().invoke("scripts.beautify", input);
31528
32591
  }
31529
32592
  async deobfuscateScript(input) {
31530
- throw unsupportedCloudOperation("scripts.deobfuscate");
32593
+ await this.ensureSession();
32594
+ return this.requireClient().invoke("scripts.deobfuscate", input);
31531
32595
  }
31532
32596
  async sandboxScript(input) {
31533
- throw unsupportedCloudOperation("scripts.sandbox");
32597
+ await this.ensureSession();
32598
+ return this.requireClient().invoke("scripts.sandbox", input);
31534
32599
  }
31535
32600
  async solveCaptcha(input) {
31536
- throw unsupportedCloudOperation("captcha.solve");
32601
+ await this.ensureSession();
32602
+ return this.requireClient().invoke("captcha.solve", input);
31537
32603
  }
31538
32604
  async getCookies(input = {}) {
31539
- throw unsupportedCloudOperation("inspect.cookies");
32605
+ await this.ensureSession();
32606
+ return this.requireAutomation().invoke("inspect.cookies", input);
32607
+ }
32608
+ async route(input) {
32609
+ await this.ensureSession();
32610
+ return this.requireAutomation().route(input);
32611
+ }
32612
+ async interceptScript(input) {
32613
+ await this.ensureSession();
32614
+ return this.requireAutomation().interceptScript(input);
31540
32615
  }
31541
32616
  async getStorageSnapshot(input = {}) {
31542
- throw unsupportedCloudOperation("inspect.storage");
32617
+ await this.ensureSession();
32618
+ return this.requireClient().invoke("inspect.storage", input);
31543
32619
  }
31544
32620
  async rawRequest(input) {
31545
32621
  await this.ensureSession();
@@ -31562,28 +32638,36 @@ var CloudSessionProxy = class {
31562
32638
  return this.requireClient().invoke("request-plan.list", input);
31563
32639
  }
31564
32640
  async writeAuthRecipe(input) {
31565
- throw unsupportedCloudOperation("auth-recipe.write");
32641
+ await this.ensureSession();
32642
+ return this.requireClient().invoke("auth-recipe.write", input);
31566
32643
  }
31567
32644
  async writeRecipe(input) {
31568
- throw unsupportedCloudOperation("recipe.write");
32645
+ await this.ensureSession();
32646
+ return this.requireClient().invoke("recipe.write", input);
31569
32647
  }
31570
32648
  async getAuthRecipe(input) {
31571
- throw unsupportedCloudOperation("auth-recipe.get");
32649
+ await this.ensureSession();
32650
+ return this.requireClient().invoke("auth-recipe.get", input);
31572
32651
  }
31573
32652
  async getRecipe(input) {
31574
- throw unsupportedCloudOperation("recipe.get");
32653
+ await this.ensureSession();
32654
+ return this.requireClient().invoke("recipe.get", input);
31575
32655
  }
31576
32656
  async listAuthRecipes(input = {}) {
31577
- throw unsupportedCloudOperation("auth-recipe.list");
32657
+ await this.ensureSession();
32658
+ return this.requireClient().invoke("auth-recipe.list", input);
31578
32659
  }
31579
32660
  async listRecipes(input = {}) {
31580
- throw unsupportedCloudOperation("recipe.list");
32661
+ await this.ensureSession();
32662
+ return this.requireClient().invoke("recipe.list", input);
31581
32663
  }
31582
32664
  async runAuthRecipe(input) {
31583
- throw unsupportedCloudOperation("auth-recipe.run");
32665
+ await this.ensureSession();
32666
+ return this.requireClient().invoke("auth-recipe.run", input);
31584
32667
  }
31585
32668
  async runRecipe(input) {
31586
- throw unsupportedCloudOperation("recipe.run");
32669
+ await this.ensureSession();
32670
+ return this.requireClient().invoke("recipe.run", input);
31587
32671
  }
31588
32672
  async request(input) {
31589
32673
  await this.ensureSession();
@@ -31595,8 +32679,9 @@ var CloudSessionProxy = class {
31595
32679
  }
31596
32680
  async close() {
31597
32681
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 || this.sessionBaseUrl === void 0 ? void 0 : {
31598
- layout: CLOUD_SESSION_LAYOUT,
31599
- version: CLOUD_SESSION_VERSION,
32682
+ layout: "opensteer-session",
32683
+ version: 1,
32684
+ provider: "cloud",
31600
32685
  mode: "cloud",
31601
32686
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
31602
32687
  sessionId: this.sessionId,
@@ -31614,7 +32699,9 @@ var CloudSessionProxy = class {
31614
32699
  });
31615
32700
  }
31616
32701
  } finally {
32702
+ await this.automation?.close().catch(() => void 0);
31617
32703
  await this.clearPersistedSession();
32704
+ this.automation = void 0;
31618
32705
  this.client = void 0;
31619
32706
  this.sessionId = void 0;
31620
32707
  this.sessionBaseUrl = void 0;
@@ -31630,6 +32717,8 @@ var CloudSessionProxy = class {
31630
32717
  return;
31631
32718
  }
31632
32719
  this.client = void 0;
32720
+ await this.automation?.close().catch(() => void 0);
32721
+ this.automation = void 0;
31633
32722
  this.sessionId = void 0;
31634
32723
  this.sessionBaseUrl = void 0;
31635
32724
  }
@@ -31650,8 +32739,9 @@ var CloudSessionProxy = class {
31650
32739
  ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
31651
32740
  });
31652
32741
  const record = {
31653
- layout: CLOUD_SESSION_LAYOUT,
31654
- version: CLOUD_SESSION_VERSION,
32742
+ layout: "opensteer-session",
32743
+ version: 1,
32744
+ provider: "cloud",
31655
32745
  mode: "cloud",
31656
32746
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
31657
32747
  sessionId: session.sessionId,
@@ -31669,6 +32759,7 @@ var CloudSessionProxy = class {
31669
32759
  baseUrl: record.baseUrl,
31670
32760
  getAuthorizationHeader: async () => this.cloud.buildAuthorizationHeader()
31671
32761
  });
32762
+ this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
31672
32763
  }
31673
32764
  async ensureWorkspaceStore() {
31674
32765
  if (this.workspaceStore !== void 0) {
@@ -31687,10 +32778,10 @@ var CloudSessionProxy = class {
31687
32778
  }
31688
32779
  async writePersistedSession(record) {
31689
32780
  const workspace = await this.ensureWorkspaceStore();
31690
- await writeJsonFileAtomic(resolveCloudSessionRecordPath(workspace.rootPath), record);
32781
+ await writePersistedSessionRecord(workspace.rootPath, record);
31691
32782
  }
31692
32783
  async clearPersistedSession() {
31693
- await promises.rm(resolveCloudSessionRecordPath(this.rootPath), { force: true }).catch(() => void 0);
32784
+ await clearPersistedSessionRecord(this.rootPath).catch(() => void 0);
31694
32785
  }
31695
32786
  async isReusableCloudSession(sessionId) {
31696
32787
  try {
@@ -31709,6 +32800,12 @@ var CloudSessionProxy = class {
31709
32800
  }
31710
32801
  return this.client;
31711
32802
  }
32803
+ requireAutomation() {
32804
+ if (!this.automation) {
32805
+ throw new Error("Cloud automation session has not been initialized.");
32806
+ }
32807
+ return this.automation;
32808
+ }
31712
32809
  };
31713
32810
  function resolveCloudBrowserProfile(cloud, input) {
31714
32811
  return input.browserProfile ?? cloud.getConfig().browserProfile;
@@ -31724,22 +32821,24 @@ function assertSupportedCloudBrowserMode(browser) {
31724
32821
  function isMissingCloudSessionError(error) {
31725
32822
  return error instanceof Error && /\b404\b/.test(error.message);
31726
32823
  }
31727
- function unsupportedCloudOperation(operation) {
31728
- return new OpensteerProtocolError(
31729
- "unsupported-operation",
31730
- `Cloud mode does not currently support ${operation}.`,
31731
- {
31732
- details: {
31733
- mode: "cloud",
31734
- operation,
31735
- supportedOperations: [...SUPPORTED_CLOUD_OPERATIONS]
31736
- }
31737
- }
31738
- );
31739
- }
31740
32824
 
31741
32825
  // src/sdk/runtime-resolution.ts
31742
32826
  function resolveOpensteerRuntimeConfig(input = {}) {
32827
+ if (input.provider?.kind === "cloud") {
32828
+ return {
32829
+ mode: "cloud",
32830
+ cloud: resolveCloudConfig({
32831
+ enabled: true,
32832
+ ...input.provider,
32833
+ mode: "cloud"
32834
+ })
32835
+ };
32836
+ }
32837
+ if (input.provider?.kind === "local") {
32838
+ return {
32839
+ mode: "local"
32840
+ };
32841
+ }
31743
32842
  const mode = resolveOpensteerExecutionMode({
31744
32843
  ...input.mode === void 0 ? {} : { explicit: input.mode },
31745
32844
  cloud: input.cloud !== void 0 && input.cloud !== false,
@@ -31762,6 +32861,7 @@ function createOpensteerSemanticRuntime(input = {}) {
31762
32861
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31763
32862
  const config = resolveOpensteerRuntimeConfig({
31764
32863
  ...input.cloud === void 0 ? {} : { cloud: input.cloud },
32864
+ ...input.provider === void 0 ? {} : { provider: input.provider },
31765
32865
  ...input.mode === void 0 ? {} : { mode: input.mode },
31766
32866
  ...process.env.OPENSTEER_MODE === void 0 ? {} : { environmentMode: process.env.OPENSTEER_MODE }
31767
32867
  });
@@ -31788,6 +32888,7 @@ var Opensteer = class {
31788
32888
  constructor(options = {}) {
31789
32889
  const runtimeConfig = resolveOpensteerRuntimeConfig({
31790
32890
  ...options.cloud === void 0 ? {} : { cloud: options.cloud },
32891
+ ...options.provider === void 0 ? {} : { provider: options.provider },
31791
32892
  ...process.env.OPENSTEER_MODE === void 0 ? {} : { environmentMode: process.env.OPENSTEER_MODE }
31792
32893
  });
31793
32894
  if (runtimeConfig.mode === "cloud") {
@@ -31795,6 +32896,7 @@ var Opensteer = class {
31795
32896
  this.runtime = createOpensteerSemanticRuntime({
31796
32897
  mode: runtimeConfig.mode,
31797
32898
  ...options.cloud === void 0 ? {} : { cloud: options.cloud },
32899
+ ...options.provider === void 0 ? {} : { provider: options.provider },
31798
32900
  ...options.engineName === void 0 ? {} : { engine: options.engineName },
31799
32901
  runtimeOptions: {
31800
32902
  ...options
@@ -31815,6 +32917,7 @@ var Opensteer = class {
31815
32917
  this.runtime = createOpensteerSemanticRuntime({
31816
32918
  mode: runtimeConfig.mode,
31817
32919
  ...options.cloud === void 0 ? {} : { cloud: options.cloud },
32920
+ ...options.provider === void 0 ? {} : { provider: options.provider },
31818
32921
  ...options.engineName === void 0 ? {} : { engine: options.engineName },
31819
32922
  runtimeOptions: {
31820
32923
  ...options,
@@ -31832,6 +32935,9 @@ var Opensteer = class {
31832
32935
  async open(input = {}) {
31833
32936
  return this.runtime.open(typeof input === "string" ? { url: input } : input);
31834
32937
  }
32938
+ async info() {
32939
+ return this.runtime.info();
32940
+ }
31835
32941
  async listPages(input = {}) {
31836
32942
  return this.runtime.listPages(input);
31837
32943
  }
@@ -32090,12 +33196,15 @@ var Opensteer = class {
32090
33196
  await this.runtime.disconnect();
32091
33197
  }
32092
33198
  requireOwnedInstrumentationRuntime(method) {
32093
- if (this.runtime instanceof OpensteerRuntime) {
33199
+ if (isInstrumentableRuntime(this.runtime)) {
32094
33200
  return this.runtime;
32095
33201
  }
32096
- throw new Error(`${method}() is only available on owned local SDK sessions.`);
33202
+ throw new Error(`${method}() is not available for this session runtime.`);
32097
33203
  }
32098
33204
  };
33205
+ function isInstrumentableRuntime(runtime) {
33206
+ return typeof runtime.route === "function" && typeof runtime.interceptScript === "function";
33207
+ }
32099
33208
  function createUnsupportedBrowserController() {
32100
33209
  const fail = async () => {
32101
33210
  throw new Error("browser.* helpers are only available in local mode.");
@@ -32162,11 +33271,13 @@ exports.OpensteerAttachAmbiguousError = OpensteerAttachAmbiguousError;
32162
33271
  exports.OpensteerBrowserManager = OpensteerBrowserManager;
32163
33272
  exports.OpensteerCloudClient = OpensteerCloudClient;
32164
33273
  exports.OpensteerRuntime = OpensteerRuntime;
33274
+ exports.OpensteerSessionRuntime = OpensteerSessionRuntime2;
32165
33275
  exports.STABLE_PRIMARY_ATTR_KEYS = STABLE_PRIMARY_ATTR_KEYS;
32166
33276
  exports.buildArrayFieldPathCandidates = buildArrayFieldPathCandidates;
32167
33277
  exports.buildPathCandidates = buildPathCandidates;
32168
33278
  exports.buildPathSelectorHint = buildPathSelectorHint;
32169
33279
  exports.buildSegmentSelector = buildSegmentSelector;
33280
+ exports.clearPersistedSessionRecord = clearPersistedSessionRecord;
32170
33281
  exports.cloneElementPath = cloneElementPath;
32171
33282
  exports.cloneReplayElementPath = cloneReplayElementPath;
32172
33283
  exports.cloneStructuralElementAnchor = cloneStructuralElementAnchor;
@@ -32191,11 +33302,14 @@ exports.normalizeOpensteerEngineName = normalizeOpensteerEngineName;
32191
33302
  exports.normalizeOpensteerExecutionMode = normalizeOpensteerExecutionMode;
32192
33303
  exports.normalizeWorkspaceId = normalizeWorkspaceId;
32193
33304
  exports.readPersistedCloudSessionRecord = readPersistedCloudSessionRecord;
33305
+ exports.readPersistedLocalBrowserSessionRecord = readPersistedLocalBrowserSessionRecord;
33306
+ exports.readPersistedSessionRecord = readPersistedSessionRecord;
32194
33307
  exports.resolveCloudConfig = resolveCloudConfig;
32195
- exports.resolveCloudSessionRecordPath = resolveCloudSessionRecordPath;
33308
+ exports.resolveCloudSessionRecordPath = resolveLiveSessionRecordPath;
32196
33309
  exports.resolveDomActionBridge = resolveDomActionBridge;
32197
33310
  exports.resolveExtractedValueInContext = resolveExtractedValueInContext;
32198
33311
  exports.resolveFilesystemWorkspacePath = resolveFilesystemWorkspacePath;
33312
+ exports.resolveLiveSessionRecordPath = resolveLiveSessionRecordPath;
32199
33313
  exports.resolveOpensteerEngineName = resolveOpensteerEngineName;
32200
33314
  exports.resolveOpensteerExecutionMode = resolveOpensteerExecutionMode;
32201
33315
  exports.resolveOpensteerRuntimeConfig = resolveOpensteerRuntimeConfig;
@@ -32205,5 +33319,6 @@ exports.sanitizeReplayElementPath = sanitizeReplayElementPath;
32205
33319
  exports.sanitizeStructuralElementAnchor = sanitizeStructuralElementAnchor;
32206
33320
  exports.settleWithPolicy = settleWithPolicy;
32207
33321
  exports.shouldKeepAttributeForPath = shouldKeepAttributeForPath;
33322
+ exports.writePersistedSessionRecord = writePersistedSessionRecord;
32208
33323
  //# sourceMappingURL=index.cjs.map
32209
33324
  //# sourceMappingURL=index.cjs.map