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.
@@ -1,6 +1,6 @@
1
1
  import path6, { join, resolve, basename, relative } from 'path';
2
2
  import { randomUUID, createHash } from 'crypto';
3
- import { rm, mkdtemp, mkdir, access, readFile, cp, writeFile, rename, readdir, stat, copyFile, open } from 'fs/promises';
3
+ import { rm, mkdtemp, mkdir, access, readFile, cp, readdir, writeFile, rename, stat, copyFile, open } from 'fs/promises';
4
4
  import { pathToFileURL } from 'url';
5
5
  import { selectAll } from 'css-select';
6
6
  import { execFileSync, execFile, spawn } from 'child_process';
@@ -14,6 +14,7 @@ import * as cheerio from 'cheerio';
14
14
  import * as prettier from 'prettier';
15
15
  import vm from 'vm';
16
16
  import { gzip as gzip$1 } from 'zlib';
17
+ import WebSocket2 from 'ws';
17
18
 
18
19
  // ../protocol/src/dom-action-bridge.ts
19
20
  var OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL = /* @__PURE__ */ Symbol.for("@opensteer/dom-action-bridge");
@@ -25,7 +26,7 @@ function resolveDomActionBridge(engine) {
25
26
  return isDomActionBridgeFactory(candidate) ? candidate.call(engine) : void 0;
26
27
  }
27
28
 
28
- // src/json.ts
29
+ // ../runtime-core/src/json.ts
29
30
  function isPlainObject(value) {
30
31
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
31
32
  return false;
@@ -33,30 +34,30 @@ function isPlainObject(value) {
33
34
  const prototype = Object.getPrototypeOf(value);
34
35
  return prototype === Object.prototype || prototype === null;
35
36
  }
36
- function canonicalizeJsonValue(value, path11) {
37
+ function canonicalizeJsonValue(value, path13) {
37
38
  if (value === null || typeof value === "string" || typeof value === "boolean") {
38
39
  return value;
39
40
  }
40
41
  if (typeof value === "number") {
41
42
  if (!Number.isFinite(value)) {
42
- throw new TypeError(`${path11} must be a finite JSON number`);
43
+ throw new TypeError(`${path13} must be a finite JSON number`);
43
44
  }
44
45
  return value;
45
46
  }
46
47
  if (Array.isArray(value)) {
47
- return value.map((entry, index) => canonicalizeJsonValue(entry, `${path11}[${index}]`));
48
+ return value.map((entry, index) => canonicalizeJsonValue(entry, `${path13}[${index}]`));
48
49
  }
49
50
  if (!isPlainObject(value)) {
50
- throw new TypeError(`${path11} must be a plain JSON object`);
51
+ throw new TypeError(`${path13} must be a plain JSON object`);
51
52
  }
52
53
  const sorted = Object.keys(value).sort((left, right) => left.localeCompare(right));
53
54
  const result = {};
54
55
  for (const key of sorted) {
55
56
  const entry = value[key];
56
57
  if (entry === void 0) {
57
- throw new TypeError(`${path11}.${key} must not be undefined`);
58
+ throw new TypeError(`${path13}.${key} must not be undefined`);
58
59
  }
59
- result[key] = canonicalizeJsonValue(entry, `${path11}.${key}`);
60
+ result[key] = canonicalizeJsonValue(entry, `${path13}.${key}`);
60
61
  }
61
62
  return result;
62
63
  }
@@ -71,7 +72,7 @@ function stableJsonString(value) {
71
72
  `;
72
73
  }
73
74
 
74
- // src/internal/filesystem.ts
75
+ // ../runtime-core/src/internal/filesystem.ts
75
76
  var LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
76
77
  function normalizeNonEmptyString(name, value) {
77
78
  const normalized = value.trim();
@@ -199,7 +200,7 @@ async function withFilesystemLock(lockPath, task) {
199
200
  }
200
201
  }
201
202
 
202
- // src/artifacts.ts
203
+ // ../runtime-core/src/artifacts.ts
203
204
  function normalizeScope(scope) {
204
205
  if (scope === void 0) {
205
206
  return {};
@@ -553,31 +554,31 @@ function oneOfSchema(members, options = {}) {
553
554
  }
554
555
 
555
556
  // ../protocol/src/validation.ts
556
- function validateJsonSchema(schema, value, path11 = "$") {
557
- return validateSchemaNode(schema, value, path11);
557
+ function validateJsonSchema(schema, value, path13 = "$") {
558
+ return validateSchemaNode(schema, value, path13);
558
559
  }
559
- function validateSchemaNode(schema, value, path11) {
560
+ function validateSchemaNode(schema, value, path13) {
560
561
  const issues = [];
561
562
  if ("const" in schema && !isJsonValueEqual(schema.const, value)) {
562
563
  issues.push({
563
- path: path11,
564
+ path: path13,
564
565
  message: `must equal ${JSON.stringify(schema.const)}`
565
566
  });
566
567
  return issues;
567
568
  }
568
569
  if (schema.enum !== void 0 && !schema.enum.some((candidate) => isJsonValueEqual(candidate, value))) {
569
570
  issues.push({
570
- path: path11,
571
+ path: path13,
571
572
  message: `must be one of ${schema.enum.map((candidate) => JSON.stringify(candidate)).join(", ")}`
572
573
  });
573
574
  return issues;
574
575
  }
575
576
  if (schema.oneOf !== void 0) {
576
- const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path11));
577
+ const branchIssues = schema.oneOf.map((member) => validateSchemaNode(member, value, path13));
577
578
  const validBranches = branchIssues.filter((current) => current.length === 0).length;
578
579
  if (validBranches !== 1) {
579
580
  issues.push({
580
- path: path11,
581
+ path: path13,
581
582
  message: validBranches === 0 ? "must match exactly one supported shape" : "matches multiple supported shapes"
582
583
  });
583
584
  return issues;
@@ -585,11 +586,11 @@ function validateSchemaNode(schema, value, path11) {
585
586
  }
586
587
  if (schema.anyOf !== void 0) {
587
588
  const hasMatch = schema.anyOf.some(
588
- (member) => validateSchemaNode(member, value, path11).length === 0
589
+ (member) => validateSchemaNode(member, value, path13).length === 0
589
590
  );
590
591
  if (!hasMatch) {
591
592
  issues.push({
592
- path: path11,
593
+ path: path13,
593
594
  message: "must match at least one supported shape"
594
595
  });
595
596
  return issues;
@@ -597,7 +598,7 @@ function validateSchemaNode(schema, value, path11) {
597
598
  }
598
599
  if (schema.allOf !== void 0) {
599
600
  for (const member of schema.allOf) {
600
- issues.push(...validateSchemaNode(member, value, path11));
601
+ issues.push(...validateSchemaNode(member, value, path13));
601
602
  }
602
603
  if (issues.length > 0) {
603
604
  return issues;
@@ -605,7 +606,7 @@ function validateSchemaNode(schema, value, path11) {
605
606
  }
606
607
  if (schema.type !== void 0 && !matchesSchemaType(schema.type, value)) {
607
608
  issues.push({
608
- path: path11,
609
+ path: path13,
609
610
  message: `must be ${describeSchemaType(schema.type)}`
610
611
  });
611
612
  return issues;
@@ -613,19 +614,19 @@ function validateSchemaNode(schema, value, path11) {
613
614
  if (typeof value === "string") {
614
615
  if (schema.minLength !== void 0 && value.length < schema.minLength) {
615
616
  issues.push({
616
- path: path11,
617
+ path: path13,
617
618
  message: `must have length >= ${String(schema.minLength)}`
618
619
  });
619
620
  }
620
621
  if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
621
622
  issues.push({
622
- path: path11,
623
+ path: path13,
623
624
  message: `must have length <= ${String(schema.maxLength)}`
624
625
  });
625
626
  }
626
627
  if (schema.pattern !== void 0 && !new RegExp(schema.pattern).test(value)) {
627
628
  issues.push({
628
- path: path11,
629
+ path: path13,
629
630
  message: `must match pattern ${schema.pattern}`
630
631
  });
631
632
  }
@@ -634,25 +635,25 @@ function validateSchemaNode(schema, value, path11) {
634
635
  if (typeof value === "number") {
635
636
  if (schema.minimum !== void 0 && value < schema.minimum) {
636
637
  issues.push({
637
- path: path11,
638
+ path: path13,
638
639
  message: `must be >= ${String(schema.minimum)}`
639
640
  });
640
641
  }
641
642
  if (schema.maximum !== void 0 && value > schema.maximum) {
642
643
  issues.push({
643
- path: path11,
644
+ path: path13,
644
645
  message: `must be <= ${String(schema.maximum)}`
645
646
  });
646
647
  }
647
648
  if (schema.exclusiveMinimum !== void 0 && value <= schema.exclusiveMinimum) {
648
649
  issues.push({
649
- path: path11,
650
+ path: path13,
650
651
  message: `must be > ${String(schema.exclusiveMinimum)}`
651
652
  });
652
653
  }
653
654
  if (schema.exclusiveMaximum !== void 0 && value >= schema.exclusiveMaximum) {
654
655
  issues.push({
655
- path: path11,
656
+ path: path13,
656
657
  message: `must be < ${String(schema.exclusiveMaximum)}`
657
658
  });
658
659
  }
@@ -661,13 +662,13 @@ function validateSchemaNode(schema, value, path11) {
661
662
  if (Array.isArray(value)) {
662
663
  if (schema.minItems !== void 0 && value.length < schema.minItems) {
663
664
  issues.push({
664
- path: path11,
665
+ path: path13,
665
666
  message: `must have at least ${String(schema.minItems)} items`
666
667
  });
667
668
  }
668
669
  if (schema.maxItems !== void 0 && value.length > schema.maxItems) {
669
670
  issues.push({
670
- path: path11,
671
+ path: path13,
671
672
  message: `must have at most ${String(schema.maxItems)} items`
672
673
  });
673
674
  }
@@ -677,7 +678,7 @@ function validateSchemaNode(schema, value, path11) {
677
678
  const key = JSON.stringify(item);
678
679
  if (seen.has(key)) {
679
680
  issues.push({
680
- path: path11,
681
+ path: path13,
681
682
  message: "must not contain duplicate items"
682
683
  });
683
684
  break;
@@ -687,7 +688,7 @@ function validateSchemaNode(schema, value, path11) {
687
688
  }
688
689
  if (schema.items !== void 0) {
689
690
  for (let index = 0; index < value.length; index += 1) {
690
- issues.push(...validateSchemaNode(schema.items, value[index], `${path11}[${String(index)}]`));
691
+ issues.push(...validateSchemaNode(schema.items, value[index], `${path13}[${String(index)}]`));
691
692
  }
692
693
  }
693
694
  return issues;
@@ -697,7 +698,7 @@ function validateSchemaNode(schema, value, path11) {
697
698
  for (const requiredKey of schema.required ?? []) {
698
699
  if (!(requiredKey in value)) {
699
700
  issues.push({
700
- path: joinObjectPath(path11, requiredKey),
701
+ path: joinObjectPath(path13, requiredKey),
701
702
  message: "is required"
702
703
  });
703
704
  }
@@ -706,13 +707,13 @@ function validateSchemaNode(schema, value, path11) {
706
707
  const propertySchema = properties[key];
707
708
  if (propertySchema !== void 0) {
708
709
  issues.push(
709
- ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path11, key))
710
+ ...validateSchemaNode(propertySchema, propertyValue, joinObjectPath(path13, key))
710
711
  );
711
712
  continue;
712
713
  }
713
714
  if (schema.additionalProperties === false) {
714
715
  issues.push({
715
- path: joinObjectPath(path11, key),
716
+ path: joinObjectPath(path13, key),
716
717
  message: "is not allowed"
717
718
  });
718
719
  continue;
@@ -722,7 +723,7 @@ function validateSchemaNode(schema, value, path11) {
722
723
  ...validateSchemaNode(
723
724
  schema.additionalProperties,
724
725
  propertyValue,
725
- joinObjectPath(path11, key)
726
+ joinObjectPath(path13, key)
726
727
  )
727
728
  );
728
729
  }
@@ -966,8 +967,8 @@ function matchesNetworkRecordFilters(record, filters) {
966
967
  }
967
968
  }
968
969
  if (filters.path !== void 0) {
969
- const path11 = getParsedUrl().pathname;
970
- if (!includesCaseInsensitive(path11, filters.path)) {
970
+ const path13 = getParsedUrl().pathname;
971
+ if (!includesCaseInsensitive(path13, filters.path)) {
971
972
  return false;
972
973
  }
973
974
  }
@@ -5682,6 +5683,65 @@ var opensteerComputerAnnotationNames = [
5682
5683
  "grid",
5683
5684
  "selected"
5684
5685
  ];
5686
+ var opensteerSemanticOperationNames = [
5687
+ "session.open",
5688
+ "page.list",
5689
+ "page.new",
5690
+ "page.activate",
5691
+ "page.close",
5692
+ "page.goto",
5693
+ "page.evaluate",
5694
+ "page.add-init-script",
5695
+ "page.snapshot",
5696
+ "dom.click",
5697
+ "dom.hover",
5698
+ "dom.input",
5699
+ "dom.scroll",
5700
+ "dom.extract",
5701
+ "network.query",
5702
+ "network.save",
5703
+ "network.clear",
5704
+ "network.minimize",
5705
+ "network.diff",
5706
+ "network.probe",
5707
+ "reverse.discover",
5708
+ "reverse.query",
5709
+ "reverse.package.create",
5710
+ "reverse.package.run",
5711
+ "reverse.export",
5712
+ "reverse.report",
5713
+ "reverse.package.get",
5714
+ "reverse.package.list",
5715
+ "reverse.package.patch",
5716
+ "interaction.capture",
5717
+ "interaction.get",
5718
+ "interaction.diff",
5719
+ "interaction.replay",
5720
+ "artifact.read",
5721
+ "inspect.cookies",
5722
+ "inspect.storage",
5723
+ "scripts.capture",
5724
+ "scripts.beautify",
5725
+ "scripts.deobfuscate",
5726
+ "scripts.sandbox",
5727
+ "captcha.solve",
5728
+ "request.raw",
5729
+ "request-plan.infer",
5730
+ "request-plan.write",
5731
+ "request-plan.get",
5732
+ "request-plan.list",
5733
+ "recipe.write",
5734
+ "recipe.get",
5735
+ "recipe.list",
5736
+ "recipe.run",
5737
+ "auth-recipe.write",
5738
+ "auth-recipe.get",
5739
+ "auth-recipe.list",
5740
+ "auth-recipe.run",
5741
+ "request.execute",
5742
+ "computer.execute",
5743
+ "session.close"
5744
+ ];
5685
5745
  function defineSemanticOperationSpec(spec) {
5686
5746
  return spec;
5687
5747
  }
@@ -6971,7 +7031,7 @@ function resolveComputerUseBridge(engine) {
6971
7031
  return isComputerUseBridgeFactory(candidate) ? candidate.call(engine) : void 0;
6972
7032
  }
6973
7033
 
6974
- // src/registry.ts
7034
+ // ../runtime-core/src/registry.ts
6975
7035
  function normalizeTags(tags) {
6976
7036
  if (tags === void 0) {
6977
7037
  return [];
@@ -8205,7 +8265,7 @@ function createTraceStore(rootPath, artifacts) {
8205
8265
  return new FilesystemTraceStore(rootPath, artifacts);
8206
8266
  }
8207
8267
 
8208
- // src/root.ts
8268
+ // ../runtime-core/src/root.ts
8209
8269
  var OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT = "opensteer-workspace";
8210
8270
  var OPENSTEER_FILESYSTEM_WORKSPACE_VERSION = 2;
8211
8271
  function normalizeWorkspaceId(workspace) {
@@ -8226,6 +8286,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8226
8286
  const browserManifestPath = path6.join(browserPath, "manifest.json");
8227
8287
  const browserUserDataDir = path6.join(browserPath, "user-data");
8228
8288
  const livePath = path6.join(options.rootPath, "live");
8289
+ const liveSessionPath = path6.join(livePath, "session.json");
8229
8290
  const liveBrowserPath = path6.join(livePath, "browser.json");
8230
8291
  const artifactsPath = path6.join(options.rootPath, "artifacts");
8231
8292
  const tracesPath = path6.join(options.rootPath, "traces");
@@ -8301,6 +8362,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8301
8362
  browserManifestPath,
8302
8363
  browserUserDataDir,
8303
8364
  livePath,
8365
+ liveSessionPath,
8304
8366
  liveBrowserPath,
8305
8367
  artifactsPath,
8306
8368
  tracesPath,
@@ -8325,7 +8387,7 @@ async function createFilesystemOpensteerWorkspace(options) {
8325
8387
  };
8326
8388
  }
8327
8389
 
8328
- // src/policy/defaults.ts
8390
+ // ../runtime-core/src/policy/defaults.ts
8329
8391
  var DEFAULT_TIMEOUTS = {
8330
8392
  "session.open": 3e4,
8331
8393
  "page.goto": 3e4,
@@ -8408,7 +8470,7 @@ function defaultPolicy() {
8408
8470
  return DEFAULT_POLICY;
8409
8471
  }
8410
8472
 
8411
- // src/policy/settle.ts
8473
+ // ../runtime-core/src/policy/settle.ts
8412
8474
  async function settleWithPolicy(policy, input) {
8413
8475
  for (const observer of policy.observers ?? []) {
8414
8476
  if (await observer.settle(input)) {
@@ -8447,7 +8509,7 @@ function abortError() {
8447
8509
  return error;
8448
8510
  }
8449
8511
 
8450
- // src/policy/timeout.ts
8512
+ // ../runtime-core/src/policy/timeout.ts
8451
8513
  var PolicyTimeoutController = class {
8452
8514
  constructor(input, budgetMs) {
8453
8515
  this.input = input;
@@ -8557,7 +8619,7 @@ function abortError2() {
8557
8619
  return error;
8558
8620
  }
8559
8621
 
8560
- // src/runtimes/dom/match-policy.ts
8622
+ // ../runtime-core/src/runtimes/dom/match-policy.ts
8561
8623
  var ATTRIBUTE_DENY_KEYS = /* @__PURE__ */ new Set([
8562
8624
  "style",
8563
8625
  "nonce",
@@ -8789,7 +8851,7 @@ function getClauseAttributeValue(node, clause) {
8789
8851
  return String(raw);
8790
8852
  }
8791
8853
 
8792
- // src/runtimes/dom/match-selectors.ts
8854
+ // ../runtime-core/src/runtimes/dom/match-selectors.ts
8793
8855
  function buildPathCandidates(domPath) {
8794
8856
  const nodes = Array.isArray(domPath) ? domPath : [];
8795
8857
  if (!nodes.length) {
@@ -8874,7 +8936,7 @@ function buildSuffixCandidates(segments) {
8874
8936
  return out;
8875
8937
  }
8876
8938
 
8877
- // src/runtimes/dom/extraction.ts
8939
+ // ../runtime-core/src/runtimes/dom/extraction.ts
8878
8940
  var URL_LIST_ATTRIBUTES = /* @__PURE__ */ new Set(["srcset", "imagesrcset", "ping"]);
8879
8941
  var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
8880
8942
  "href",
@@ -8886,9 +8948,9 @@ var IFRAME_URL_ATTRIBUTES = /* @__PURE__ */ new Set([
8886
8948
  "poster",
8887
8949
  "ping"
8888
8950
  ]);
8889
- function buildArrayFieldPathCandidates(path11) {
8890
- const strict = path11.nodes.length ? buildPathCandidates(path11.nodes) : [];
8891
- const relaxedNodes = stripPositionClauses(path11.nodes);
8951
+ function buildArrayFieldPathCandidates(path13) {
8952
+ const strict = path13.nodes.length ? buildPathCandidates(path13.nodes) : [];
8953
+ const relaxedNodes = stripPositionClauses(path13.nodes);
8892
8954
  const relaxed = relaxedNodes.length ? buildPathCandidates(relaxedNodes) : [];
8893
8955
  return dedupeSelectors([...strict, ...relaxed]);
8894
8956
  }
@@ -9121,7 +9183,7 @@ function readDescriptorToken(value, index) {
9121
9183
  };
9122
9184
  }
9123
9185
 
9124
- // src/runtimes/dom/errors.ts
9186
+ // ../runtime-core/src/runtimes/dom/errors.ts
9125
9187
  var ElementPathError = class extends Error {
9126
9188
  code;
9127
9189
  constructor(code, message) {
@@ -9197,6 +9259,45 @@ var selectorAdapter = {
9197
9259
  },
9198
9260
  equals(left, right) {
9199
9261
  return left === right;
9262
+ },
9263
+ existsOne(test, nodes) {
9264
+ return selectorAdapter.findOne(test, nodes) !== null;
9265
+ },
9266
+ findAll(test, nodes) {
9267
+ const matches = [];
9268
+ const visit = (node) => {
9269
+ if (selectorAdapter.isTag(node) && test(node)) {
9270
+ matches.push(node);
9271
+ }
9272
+ for (const child of selectorAdapter.getChildren(node)) {
9273
+ visit(child);
9274
+ }
9275
+ };
9276
+ for (const node of nodes) {
9277
+ visit(node);
9278
+ }
9279
+ return matches;
9280
+ },
9281
+ findOne(test, nodes) {
9282
+ const visit = (node) => {
9283
+ if (selectorAdapter.isTag(node) && test(node)) {
9284
+ return node;
9285
+ }
9286
+ for (const child of selectorAdapter.getChildren(node)) {
9287
+ const match = visit(child);
9288
+ if (match !== null) {
9289
+ return match;
9290
+ }
9291
+ }
9292
+ return null;
9293
+ };
9294
+ for (const node of nodes) {
9295
+ const match = visit(node);
9296
+ if (match !== null) {
9297
+ return match;
9298
+ }
9299
+ }
9300
+ return null;
9200
9301
  }
9201
9302
  };
9202
9303
  function createDomSnapshotIndex(snapshot) {
@@ -9380,7 +9481,7 @@ function sortNodes(nodes) {
9380
9481
  return [...nodes].sort((left, right) => left.snapshotNodeId - right.snapshotNodeId);
9381
9482
  }
9382
9483
 
9383
- // src/runtimes/dom/path.ts
9484
+ // ../runtime-core/src/runtimes/dom/path.ts
9384
9485
  var MAX_ATTRIBUTE_VALUE_LENGTH = 300;
9385
9486
  function cloneStructuralElementAnchor(anchor) {
9386
9487
  return {
@@ -9389,18 +9490,18 @@ function cloneStructuralElementAnchor(anchor) {
9389
9490
  nodes: anchor.nodes.map(clonePathNode)
9390
9491
  };
9391
9492
  }
9392
- function cloneReplayElementPath(path11) {
9493
+ function cloneReplayElementPath(path13) {
9393
9494
  return {
9394
9495
  resolution: "deterministic",
9395
- context: cloneContext(path11.context),
9396
- nodes: path11.nodes.map(clonePathNode)
9496
+ context: cloneContext(path13.context),
9497
+ nodes: path13.nodes.map(clonePathNode)
9397
9498
  };
9398
9499
  }
9399
- function cloneElementPath(path11) {
9400
- return cloneReplayElementPath(path11);
9500
+ function cloneElementPath(path13) {
9501
+ return cloneReplayElementPath(path13);
9401
9502
  }
9402
- function buildPathSelectorHint(path11) {
9403
- const nodes = path11?.nodes || [];
9503
+ function buildPathSelectorHint(path13) {
9504
+ const nodes = path13?.nodes || [];
9404
9505
  const last = nodes[nodes.length - 1];
9405
9506
  if (!last) {
9406
9507
  return "*";
@@ -9449,15 +9550,15 @@ function sanitizeStructuralElementAnchor(anchor) {
9449
9550
  nodes: sanitizeNodes(anchor.nodes)
9450
9551
  };
9451
9552
  }
9452
- function sanitizeReplayElementPath(path11) {
9553
+ function sanitizeReplayElementPath(path13) {
9453
9554
  return {
9454
9555
  resolution: "deterministic",
9455
- context: sanitizeContext(path11.context),
9456
- nodes: sanitizeNodes(path11.nodes)
9556
+ context: sanitizeContext(path13.context),
9557
+ nodes: sanitizeNodes(path13.nodes)
9457
9558
  };
9458
9559
  }
9459
- function sanitizeElementPath(path11) {
9460
- return sanitizeReplayElementPath(path11);
9560
+ function sanitizeElementPath(path13) {
9561
+ return sanitizeReplayElementPath(path13);
9461
9562
  }
9462
9563
  function buildLocalStructuralElementAnchor(index, rawTargetNode) {
9463
9564
  const targetNode = requireElementNode(index, rawTargetNode);
@@ -9580,8 +9681,8 @@ function buildTargetNotFoundMessage(domPath, diagnostics) {
9580
9681
  }
9581
9682
  return `${base} Target depth ${String(depth)}. Candidate counts: ${sample}.`;
9582
9683
  }
9583
- function buildArrayFieldCandidates(path11) {
9584
- return buildArrayFieldPathCandidates(path11);
9684
+ function buildArrayFieldCandidates(path13) {
9685
+ return buildArrayFieldPathCandidates(path13);
9585
9686
  }
9586
9687
  function firstDefinedAttribute(node, keys) {
9587
9688
  for (const key of keys) {
@@ -9997,7 +10098,7 @@ var MemoryDomDescriptorStore = class {
9997
10098
  }
9998
10099
  };
9999
10100
 
10000
- // src/runtimes/dom/executor.ts
10101
+ // ../runtime-core/src/runtimes/dom/executor.ts
10001
10102
  var MAX_DOM_ACTION_ATTEMPTS = 3;
10002
10103
  var DEFAULT_SCROLL_OPTIONS = {
10003
10104
  block: "center",
@@ -10561,7 +10662,7 @@ function pointFallsWithinQuads(point, quads) {
10561
10662
  return quads.some((quad) => rectContainsPoint(quadBounds(quad), point));
10562
10663
  }
10563
10664
 
10564
- // src/runtimes/dom/runtime.ts
10665
+ // ../runtime-core/src/runtimes/dom/runtime.ts
10565
10666
  var SnapshotSession = class {
10566
10667
  constructor(engine) {
10567
10668
  this.engine = engine;
@@ -10965,21 +11066,21 @@ var DefaultDomRuntime = class {
10965
11066
  return match;
10966
11067
  }
10967
11068
  async resolvePathTarget(session, pageRef, rawPath, source, description, descriptor) {
10968
- const path11 = sanitizeReplayElementPath(rawPath);
10969
- const context = await this.resolvePathContext(session, pageRef, path11.context);
10970
- const target = resolveDomPathInScope(context.index, path11.nodes, context.scope);
11069
+ const path13 = sanitizeReplayElementPath(rawPath);
11070
+ const context = await this.resolvePathContext(session, pageRef, path13.context);
11071
+ const target = resolveDomPathInScope(context.index, path13.nodes, context.scope);
10971
11072
  if (!target) {
10972
- throwTargetNotFound(context.index, path11.nodes, context.scope);
11073
+ throwTargetNotFound(context.index, path13.nodes, context.scope);
10973
11074
  }
10974
11075
  if (target.node.nodeRef === void 0) {
10975
11076
  throw new Error(
10976
- `resolved path "${buildPathSelectorHint(path11)}" does not point to a live element`
11077
+ `resolved path "${buildPathSelectorHint(path13)}" does not point to a live element`
10977
11078
  );
10978
11079
  }
10979
11080
  const anchor = await this.buildAnchorFromSnapshotNode(session, context.snapshot, target.node);
10980
11081
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
10981
11082
  ...description === void 0 ? {} : { description },
10982
- replayPath: path11,
11083
+ replayPath: path13,
10983
11084
  ...source === "path" || source === "descriptor" ? { selectorUsed: target.selector } : {},
10984
11085
  ...descriptor === void 0 ? {} : { descriptor }
10985
11086
  });
@@ -11000,9 +11101,9 @@ var DefaultDomRuntime = class {
11000
11101
  });
11001
11102
  }
11002
11103
  async queryAllByElementPath(session, pageRef, rawPath) {
11003
- const path11 = sanitizeReplayElementPath(rawPath);
11004
- const context = await this.resolvePathContext(session, pageRef, path11.context);
11005
- return queryAllDomPathInScope(context.index, path11.nodes, context.scope).filter(
11104
+ const path13 = sanitizeReplayElementPath(rawPath);
11105
+ const context = await this.resolvePathContext(session, pageRef, path13.context);
11106
+ return queryAllDomPathInScope(context.index, path13.nodes, context.scope).filter(
11006
11107
  (node) => node.nodeRef !== void 0
11007
11108
  ).map((node) => this.createSnapshotTarget(context.snapshot, node));
11008
11109
  }
@@ -11188,16 +11289,16 @@ var DefaultDomRuntime = class {
11188
11289
  const index = createSnapshotIndex(item.snapshot);
11189
11290
  return this.resolveFirstArrayFieldTargetInNode(index, item.node, field.path);
11190
11291
  }
11191
- resolveFirstArrayFieldTargetInNode(index, rootNode, path11) {
11192
- const normalizedPath = sanitizeElementPath(path11);
11292
+ resolveFirstArrayFieldTargetInNode(index, rootNode, path13) {
11293
+ const normalizedPath = sanitizeElementPath(path13);
11193
11294
  const selectors = buildArrayFieldCandidates(normalizedPath);
11194
11295
  if (!selectors.length) {
11195
11296
  return rootNode;
11196
11297
  }
11197
11298
  return resolveFirstWithinNodeBySelectors(index, rootNode, selectors);
11198
11299
  }
11199
- resolveUniqueArrayFieldTargetInNode(index, rootNode, path11) {
11200
- const normalizedPath = sanitizeElementPath(path11);
11300
+ resolveUniqueArrayFieldTargetInNode(index, rootNode, path13) {
11301
+ const normalizedPath = sanitizeElementPath(path13);
11201
11302
  const selectors = buildArrayFieldCandidates(normalizedPath);
11202
11303
  if (!selectors.length) {
11203
11304
  return rootNode;
@@ -12007,8 +12108,8 @@ function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
12007
12108
  const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
12008
12109
  return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
12009
12110
  }
12010
- function normalizeWebSocketPath(path11) {
12011
- return path11.startsWith("/") ? path11 : `/${path11}`;
12111
+ function normalizeWebSocketPath(path13) {
12112
+ return path13.startsWith("/") ? path13 : `/${path13}`;
12012
12113
  }
12013
12114
  function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
12014
12115
  try {
@@ -12025,8 +12126,115 @@ function readPort(url) {
12025
12126
  const port = Number.parseInt(url.port, 10);
12026
12127
  return Number.isInteger(port) && port > 0 ? port : void 0;
12027
12128
  }
12129
+ var OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
12130
+ var OPENSTEER_LIVE_SESSION_VERSION = 1;
12131
+ var LEGACY_CLOUD_SESSION_LAYOUT = "opensteer-cloud-session";
12132
+ var LEGACY_CLOUD_SESSION_VERSION = 1;
12133
+ function resolveLiveSessionRecordPath(rootPath) {
12134
+ return path6.join(rootPath, "live", "session.json");
12135
+ }
12136
+ function resolveLegacyLiveBrowserRecordPath(rootPath) {
12137
+ return path6.join(rootPath, "live", "browser.json");
12138
+ }
12139
+ function resolveLegacyCloudSessionRecordPath(rootPath) {
12140
+ return path6.join(rootPath, "live", "cloud-session.json");
12141
+ }
12142
+ async function readPersistedSessionRecord(rootPath) {
12143
+ const sessionPath = resolveLiveSessionRecordPath(rootPath);
12144
+ if (await pathExists(sessionPath)) {
12145
+ const parsed = await readJsonFile(sessionPath);
12146
+ if (isPersistedLocalBrowserSessionRecord(parsed)) {
12147
+ return parsed;
12148
+ }
12149
+ if (isPersistedCloudSessionRecord(parsed)) {
12150
+ return parsed;
12151
+ }
12152
+ }
12153
+ const legacyCloudPath = resolveLegacyCloudSessionRecordPath(rootPath);
12154
+ if (await pathExists(legacyCloudPath)) {
12155
+ const parsed = await readJsonFile(legacyCloudPath);
12156
+ if (isLegacyCloudSessionRecord(parsed)) {
12157
+ return {
12158
+ layout: OPENSTEER_LIVE_SESSION_LAYOUT,
12159
+ version: OPENSTEER_LIVE_SESSION_VERSION,
12160
+ provider: "cloud",
12161
+ mode: "cloud",
12162
+ ...parsed.workspace === void 0 ? {} : { workspace: parsed.workspace },
12163
+ sessionId: parsed.sessionId,
12164
+ baseUrl: parsed.baseUrl,
12165
+ startedAt: parsed.startedAt,
12166
+ updatedAt: parsed.updatedAt
12167
+ };
12168
+ }
12169
+ }
12170
+ const legacyBrowserPath = resolveLegacyLiveBrowserRecordPath(rootPath);
12171
+ if (await pathExists(legacyBrowserPath)) {
12172
+ const parsed = await readJsonFile(legacyBrowserPath);
12173
+ if (isLegacyLocalBrowserSessionRecord(parsed)) {
12174
+ return {
12175
+ layout: OPENSTEER_LIVE_SESSION_LAYOUT,
12176
+ version: OPENSTEER_LIVE_SESSION_VERSION,
12177
+ provider: "local",
12178
+ mode: "browser",
12179
+ engine: parsed.engine ?? "playwright",
12180
+ ...parsed.endpoint === void 0 ? {} : { endpoint: parsed.endpoint },
12181
+ ...parsed.baseUrl === void 0 ? {} : { baseUrl: parsed.baseUrl },
12182
+ ...parsed.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: parsed.remoteDebuggingUrl },
12183
+ ...parsed.sessionDir === void 0 ? {} : { sessionDir: parsed.sessionDir },
12184
+ pid: parsed.pid,
12185
+ startedAt: parsed.startedAt,
12186
+ updatedAt: parsed.startedAt,
12187
+ ...parsed.executablePath === void 0 ? {} : { executablePath: parsed.executablePath },
12188
+ userDataDir: parsed.userDataDir
12189
+ };
12190
+ }
12191
+ }
12192
+ return void 0;
12193
+ }
12194
+ async function readPersistedCloudSessionRecord(rootPath) {
12195
+ const record = await readPersistedSessionRecord(rootPath);
12196
+ return record?.provider === "cloud" ? record : void 0;
12197
+ }
12198
+ async function readPersistedLocalBrowserSessionRecord(rootPath) {
12199
+ const record = await readPersistedSessionRecord(rootPath);
12200
+ return record?.provider === "local" ? record : void 0;
12201
+ }
12202
+ async function hasPersistedCloudSession(rootPath) {
12203
+ return await readPersistedCloudSessionRecord(rootPath) !== void 0;
12204
+ }
12205
+ async function writePersistedSessionRecord(rootPath, record) {
12206
+ await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath), record);
12207
+ await clearLegacySessionRecordPaths(rootPath);
12208
+ }
12209
+ async function clearPersistedSessionRecord(rootPath) {
12210
+ await Promise.all([
12211
+ removeIfPresent(resolveLiveSessionRecordPath(rootPath)),
12212
+ clearLegacySessionRecordPaths(rootPath)
12213
+ ]);
12214
+ }
12215
+ function isPersistedCloudSessionRecord(value) {
12216
+ 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);
12217
+ }
12218
+ function isPersistedLocalBrowserSessionRecord(value) {
12219
+ 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;
12220
+ }
12221
+ function isLegacyCloudSessionRecord(value) {
12222
+ 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);
12223
+ }
12224
+ function isLegacyLocalBrowserSessionRecord(value) {
12225
+ 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;
12226
+ }
12227
+ async function clearLegacySessionRecordPaths(rootPath) {
12228
+ await Promise.all([
12229
+ removeIfPresent(resolveLegacyLiveBrowserRecordPath(rootPath)),
12230
+ removeIfPresent(resolveLegacyCloudSessionRecordPath(rootPath))
12231
+ ]);
12232
+ }
12233
+ async function removeIfPresent(filePath) {
12234
+ await rm(filePath, { force: true }).catch(() => void 0);
12235
+ }
12028
12236
 
12029
- // src/internal/engine-selection.ts
12237
+ // ../runtime-core/src/internal/engine-selection.ts
12030
12238
  var OPENSTEER_ENGINE_NAMES = ["playwright", "abp"];
12031
12239
  var DEFAULT_OPENSTEER_ENGINE = "playwright";
12032
12240
  function resolveOpensteerEngineName(input = {}) {
@@ -12146,6 +12354,32 @@ async function clearChromeSingletonEntries(userDataDir) {
12146
12354
  )
12147
12355
  );
12148
12356
  }
12357
+ async function sanitizeChromeProfile(userDataDir) {
12358
+ const entries = await readdir(userDataDir).catch(() => []);
12359
+ const profileDirs = entries.filter(
12360
+ (entry) => entry === "Default" || /^Profile \d+$/i.test(entry)
12361
+ );
12362
+ await Promise.all(profileDirs.map((dir) => sanitizeProfilePreferences(userDataDir, dir)));
12363
+ }
12364
+ async function sanitizeProfilePreferences(userDataDir, profileDir) {
12365
+ const prefsPath = join(userDataDir, profileDir, "Preferences");
12366
+ try {
12367
+ const raw = await readFile(prefsPath, "utf8");
12368
+ const prefs = JSON.parse(raw);
12369
+ const profile = prefs.profile ?? {};
12370
+ if (profile.exit_type === "Normal" && profile.exited_cleanly === true) {
12371
+ return;
12372
+ }
12373
+ profile.exit_type = "Normal";
12374
+ profile.exited_cleanly = true;
12375
+ prefs.profile = profile;
12376
+ await writeFile(prefsPath, JSON.stringify(prefs), "utf8");
12377
+ await rm(join(userDataDir, profileDir, "Secure Preferences"), { force: true }).catch(
12378
+ () => void 0
12379
+ );
12380
+ } catch {
12381
+ }
12382
+ }
12149
12383
  var CHROME_SINGLETON_ENTRIES = new Set(CHROME_SINGLETON_ARTIFACTS);
12150
12384
  var SKIPPED_ROOT_DIRECTORIES = /* @__PURE__ */ new Set([
12151
12385
  "Crash Reports",
@@ -12204,6 +12438,7 @@ async function createBrowserProfileSnapshot(input) {
12204
12438
  ...profileDirectory === void 0 ? {} : { selectedProfileDirectory: profileDirectory }
12205
12439
  });
12206
12440
  await clearChromeSingletonEntries(targetUserDataDir);
12441
+ await sanitizeChromeProfile(targetUserDataDir);
12207
12442
  }
12208
12443
  async function copyRootLevelEntries(input) {
12209
12444
  const entries = await readdir(input.sourceUserDataDir).catch(() => []);
@@ -12264,17 +12499,28 @@ function shouldCopyEntry(input) {
12264
12499
 
12265
12500
  // src/local-browser/stealth-init-script.ts
12266
12501
  function generateStealthInitScript(profile) {
12267
- const encodedProfile = JSON.stringify(profile);
12502
+ const encodedProfile = JSON.stringify({
12503
+ ...profile,
12504
+ platformString: getPlatformString(profile.platform),
12505
+ userAgentData: buildUserAgentData(profile)
12506
+ });
12268
12507
  return `(() => {
12269
12508
  const profile = ${encodedProfile};
12270
- const define = (target, key, value) => {
12509
+ var define = function(target, key, value) {
12271
12510
  Object.defineProperty(target, key, {
12272
12511
  configurable: true,
12273
- get: typeof value === "function" ? value : () => value,
12512
+ get: typeof value === 'function' ? value : function() { return value; },
12274
12513
  });
12275
12514
  };
12276
- define(Navigator.prototype, 'webdriver', false);
12277
- define(Navigator.prototype, 'platform', profile.platform === 'macos' ? 'MacIntel' : profile.platform === 'windows' ? 'Win32' : 'Linux x86_64');
12515
+
12516
+ // --- navigator / screen mirrors for future pages ---
12517
+ if (navigator.webdriver === true) {
12518
+ Object.defineProperty(Navigator.prototype, 'webdriver', {
12519
+ configurable: true,
12520
+ get: function() { return false; },
12521
+ });
12522
+ }
12523
+ define(Navigator.prototype, 'platform', profile.platformString);
12278
12524
  define(Navigator.prototype, 'userAgent', profile.userAgent);
12279
12525
  define(Navigator.prototype, 'language', profile.locale);
12280
12526
  define(Navigator.prototype, 'languages', [profile.locale, 'en']);
@@ -12284,53 +12530,191 @@ function generateStealthInitScript(profile) {
12284
12530
  define(window.screen, 'height', profile.screenResolution.height);
12285
12531
  define(window.screen, 'availWidth', profile.screenResolution.width);
12286
12532
  define(window.screen, 'availHeight', profile.screenResolution.height - 40);
12287
- if (document.fonts && typeof document.fonts.check === 'function') {
12288
- const originalCheck = document.fonts.check.bind(document.fonts);
12289
- document.fonts.check = function(font, text) {
12290
- const family = String(font).match(/["']([^"']+)["']/)?.[1] || String(font).split(/s+/).at(-1);
12291
- if (family && profile.fonts.includes(family)) {
12292
- return true;
12533
+ define(window.screen, 'colorDepth', 24);
12534
+ define(window.screen, 'pixelDepth', 24);
12535
+ define(Navigator.prototype, 'userAgentData', {
12536
+ brands: profile.userAgentData.brands,
12537
+ mobile: false,
12538
+ platform: profile.userAgentData.platform,
12539
+ toJSON: function() {
12540
+ return {
12541
+ brands: this.brands,
12542
+ mobile: this.mobile,
12543
+ platform: this.platform,
12544
+ };
12545
+ },
12546
+ getHighEntropyValues: async function(hints) {
12547
+ var source = {
12548
+ architecture: profile.userAgentData.architecture,
12549
+ bitness: profile.userAgentData.bitness,
12550
+ brands: profile.userAgentData.brands,
12551
+ fullVersionList: profile.userAgentData.fullVersionList,
12552
+ mobile: false,
12553
+ model: '',
12554
+ platform: profile.userAgentData.platform,
12555
+ platformVersion: profile.userAgentData.platformVersion,
12556
+ uaFullVersion: profile.browserVersion,
12557
+ wow64: false,
12558
+ };
12559
+ var values = {};
12560
+ for (var i = 0; i < hints.length; i++) {
12561
+ var hint = hints[i];
12562
+ if (Object.prototype.hasOwnProperty.call(source, hint)) {
12563
+ values[hint] = source[hint];
12564
+ }
12293
12565
  }
12294
- return originalCheck(font, text);
12566
+ return values;
12567
+ },
12568
+ });
12569
+
12570
+ if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {
12571
+ var originalResolvedOptions = Intl.DateTimeFormat.prototype.resolvedOptions;
12572
+ Intl.DateTimeFormat.prototype.resolvedOptions = function() {
12573
+ var options = originalResolvedOptions.call(this);
12574
+ options.timeZone = profile.timezoneId;
12575
+ return options;
12576
+ };
12577
+ }
12578
+
12579
+ if (Date.prototype.getTimezoneOffset) {
12580
+ var originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
12581
+ var calculateTimezoneOffset = function(date) {
12582
+ try {
12583
+ var formatter = new Intl.DateTimeFormat('en-US', {
12584
+ timeZone: profile.timezoneId,
12585
+ hour12: false,
12586
+ year: 'numeric',
12587
+ month: '2-digit',
12588
+ day: '2-digit',
12589
+ hour: '2-digit',
12590
+ minute: '2-digit',
12591
+ second: '2-digit',
12592
+ });
12593
+ var parts = formatter.formatToParts(date);
12594
+ var values = {};
12595
+ for (var i = 0; i < parts.length; i++) {
12596
+ if (parts[i].type !== 'literal') {
12597
+ values[parts[i].type] = Number(parts[i].value);
12598
+ }
12599
+ }
12600
+ var utcTime = Date.UTC(
12601
+ values.year,
12602
+ values.month - 1,
12603
+ values.day,
12604
+ values.hour,
12605
+ values.minute,
12606
+ values.second,
12607
+ );
12608
+ return Math.round((date.getTime() - utcTime) / 60000);
12609
+ } catch {
12610
+ return originalGetTimezoneOffset.call(date);
12611
+ }
12612
+ };
12613
+ Date.prototype.getTimezoneOffset = function() {
12614
+ return calculateTimezoneOffset(this);
12295
12615
  };
12296
12616
  }
12297
- const seedNoise = (seed, input) => {
12298
- const value = Math.sin(seed + input * 12.9898) * 43758.5453;
12617
+
12618
+ // --- CDP Runtime.enable leak defense ---
12619
+ var _wrap = function(name) {
12620
+ var orig = console[name];
12621
+ if (typeof orig !== 'function') return;
12622
+ console[name] = new Proxy(orig, {
12623
+ apply: function(target, thisArg, args) {
12624
+ for (var i = 0; i < args.length; i++) {
12625
+ if (args[i] instanceof Error) {
12626
+ var d = Object.getOwnPropertyDescriptor(args[i], 'stack');
12627
+ if (d && typeof d.get === 'function') return undefined;
12628
+ }
12629
+ }
12630
+ return Reflect.apply(target, thisArg, args);
12631
+ },
12632
+ });
12633
+ };
12634
+ ['debug', 'log', 'info', 'error', 'warn', 'trace', 'dir'].forEach(_wrap);
12635
+
12636
+ // --- Canvas fingerprint noise ---
12637
+ var seedNoise = function(seed, input) {
12638
+ var value = Math.sin(seed + input * 12.9898) * 43758.5453;
12299
12639
  return value - Math.floor(value);
12300
12640
  };
12301
12641
  if (HTMLCanvasElement.prototype.toDataURL) {
12302
- const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
12303
- HTMLCanvasElement.prototype.toDataURL = function(...args) {
12304
- const context = this.getContext('2d');
12642
+ var originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
12643
+ HTMLCanvasElement.prototype.toDataURL = function() {
12644
+ var context = this.getContext('2d');
12305
12645
  if (context) {
12306
- const x = Math.min(1, Math.max(0, this.width - 1));
12307
- const y = Math.min(1, Math.max(0, this.height - 1));
12308
- const imageData = context.getImageData(x, y, 1, 1);
12646
+ var x = Math.min(1, Math.max(0, this.width - 1));
12647
+ var y = Math.min(1, Math.max(0, this.height - 1));
12648
+ var imageData = context.getImageData(x, y, 1, 1);
12309
12649
  imageData.data[0] = Math.max(0, Math.min(255, imageData.data[0] + Math.floor(seedNoise(profile.canvasNoiseSeed, 1) * 2)));
12310
12650
  context.putImageData(imageData, x, y);
12311
12651
  }
12312
- return originalToDataURL.apply(this, args);
12652
+ return originalToDataURL.apply(this, arguments);
12313
12653
  };
12314
12654
  }
12655
+
12656
+ // --- WebGL vendor/renderer spoofing ---
12315
12657
  if (typeof WebGLRenderingContext !== 'undefined') {
12316
- const originalGetParameter = WebGLRenderingContext.prototype.getParameter;
12658
+ var originalGetParameter = WebGLRenderingContext.prototype.getParameter;
12317
12659
  WebGLRenderingContext.prototype.getParameter = function(parameter) {
12318
12660
  if (parameter === 37445) return profile.webglVendor;
12319
12661
  if (parameter === 37446) return profile.webglRenderer;
12320
12662
  return originalGetParameter.call(this, parameter);
12321
12663
  };
12322
12664
  }
12665
+
12666
+ // --- AudioBuffer fingerprint noise ---
12323
12667
  if (typeof AudioBuffer !== 'undefined' && typeof AnalyserNode !== 'undefined') {
12324
- const originalGetFloatFrequencyData = AnalyserNode.prototype.getFloatFrequencyData;
12668
+ var originalGetFloatFrequencyData = AnalyserNode.prototype.getFloatFrequencyData;
12325
12669
  AnalyserNode.prototype.getFloatFrequencyData = function(array) {
12326
12670
  originalGetFloatFrequencyData.call(this, array);
12327
- for (let index = 0; index < array.length; index += 16) {
12671
+ for (var index = 0; index < array.length; index += 16) {
12328
12672
  array[index] += (seedNoise(profile.audioNoiseSeed, index) - 0.5) * 0.0001;
12329
12673
  }
12330
12674
  };
12331
12675
  }
12676
+
12677
+ // --- Font availability spoofing ---
12678
+ if (document.fonts && typeof document.fonts.check === 'function') {
12679
+ var originalCheck = document.fonts.check.bind(document.fonts);
12680
+ document.fonts.check = function(font, text) {
12681
+ var family = String(font).match(/["']([^"']+)["']/)?.[1] || String(font).split(/\\s+/).at(-1);
12682
+ if (family && profile.fonts.includes(family)) {
12683
+ return true;
12684
+ }
12685
+ return originalCheck(font, text);
12686
+ };
12687
+ }
12332
12688
  })();`;
12333
12689
  }
12690
+ function buildUserAgentData(profile) {
12691
+ const majorVersion = profile.browserVersion.split(".")[0] ?? "136";
12692
+ const platformData = {
12693
+ macos: { platform: "macOS", platformVersion: "14.4.0", architecture: "arm" },
12694
+ windows: { platform: "Windows", platformVersion: "15.0.0", architecture: "x86" },
12695
+ linux: { platform: "Linux", platformVersion: "6.5.0", architecture: "x86" }
12696
+ };
12697
+ const platformInfo = platformData[profile.platform];
12698
+ return {
12699
+ architecture: platformInfo.architecture,
12700
+ bitness: "64",
12701
+ brands: [
12702
+ { brand: "Chromium", version: majorVersion },
12703
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: majorVersion }] : [{ brand: "Google Chrome", version: majorVersion }],
12704
+ { brand: "Not-A.Brand", version: "99" }
12705
+ ],
12706
+ fullVersionList: [
12707
+ { brand: "Chromium", version: profile.browserVersion },
12708
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: profile.browserVersion }] : [{ brand: "Google Chrome", version: profile.browserVersion }],
12709
+ { brand: "Not-A.Brand", version: "99.0.0.0" }
12710
+ ],
12711
+ platform: platformInfo.platform,
12712
+ platformVersion: platformInfo.platformVersion
12713
+ };
12714
+ }
12715
+ function getPlatformString(platform) {
12716
+ return platform === "macos" ? "MacIntel" : platform === "windows" ? "Win32" : "Linux x86_64";
12717
+ }
12334
12718
 
12335
12719
  // src/local-browser/stealth.ts
12336
12720
  var STEALTH_INIT_SCRIPT = `(() => {
@@ -12374,25 +12758,137 @@ var STEALTH_INIT_SCRIPT = `(() => {
12374
12758
  ['debug', 'log', 'info', 'error', 'warn', 'trace', 'dir'].forEach(_wrap);
12375
12759
  })();`;
12376
12760
  async function injectBrowserStealthScripts(context, input = {}) {
12761
+ if (input.profile !== void 0) {
12762
+ await installContextNetworkHeaders(context, input.profile);
12763
+ await installCdpStealthOverrides(context, input.profile, input.page);
12764
+ }
12377
12765
  if (typeof context.addInitScript === "function") {
12378
12766
  await context.addInitScript({
12379
12767
  content: input.profile === void 0 ? STEALTH_INIT_SCRIPT : generateStealthInitScript(input.profile)
12380
12768
  });
12381
12769
  }
12382
12770
  }
12771
+ function buildUserAgentMetadata(profile) {
12772
+ const majorVersion = profile.browserVersion.split(".")[0] ?? "136";
12773
+ const brands = [
12774
+ { brand: "Chromium", version: majorVersion },
12775
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: majorVersion }] : [{ brand: "Google Chrome", version: majorVersion }],
12776
+ { brand: "Not-A.Brand", version: "99" }
12777
+ ];
12778
+ const fullVersionList = [
12779
+ { brand: "Chromium", version: profile.browserVersion },
12780
+ ...profile.browserBrand === "edge" ? [{ brand: "Microsoft Edge", version: profile.browserVersion }] : [{ brand: "Google Chrome", version: profile.browserVersion }],
12781
+ { brand: "Not-A.Brand", version: "99.0.0.0" }
12782
+ ];
12783
+ const platformMap = {
12784
+ // Chromium keeps the reduced macOS UA token frozen to Intel even on Apple Silicon.
12785
+ macos: { platform: "macOS", platformVersion: "14.4.0", architecture: "arm" },
12786
+ windows: { platform: "Windows", platformVersion: "15.0.0", architecture: "x86" },
12787
+ linux: { platform: "Linux", platformVersion: "6.5.0", architecture: "x86" }
12788
+ };
12789
+ const platformInfo = platformMap[profile.platform];
12790
+ return {
12791
+ brands,
12792
+ fullVersionList,
12793
+ platform: platformInfo.platform,
12794
+ platformVersion: platformInfo.platformVersion,
12795
+ architecture: platformInfo.architecture,
12796
+ model: "",
12797
+ mobile: false,
12798
+ bitness: "64",
12799
+ wow64: false
12800
+ };
12801
+ }
12802
+ async function installCdpStealthOverrides(context, profile, initialPage) {
12803
+ const pages = initialPage === void 0 ? context.pages() : Array.from(/* @__PURE__ */ new Set([initialPage, ...context.pages()]));
12804
+ await Promise.all(pages.map((page) => applyPageOverrides(context, page, profile)));
12805
+ const appliedPages = /* @__PURE__ */ new WeakSet();
12806
+ const applyFuturePageOverrides = async (page) => {
12807
+ if (appliedPages.has(page)) {
12808
+ return;
12809
+ }
12810
+ appliedPages.add(page);
12811
+ await applyPageOverrides(context, page, profile);
12812
+ };
12813
+ if (typeof context.on === "function") {
12814
+ context.on("page", applyFuturePageOverrides);
12815
+ }
12816
+ }
12817
+ async function installContextNetworkHeaders(context, profile) {
12818
+ if (typeof context.setExtraHTTPHeaders !== "function") {
12819
+ return;
12820
+ }
12821
+ await context.setExtraHTTPHeaders(buildStealthRequestHeaders(profile)).catch(() => void 0);
12822
+ }
12823
+ async function applyPageOverrides(context, page, profile) {
12824
+ const contextWithCdp = context;
12825
+ if (typeof contextWithCdp.newCDPSession !== "function") {
12826
+ return;
12827
+ }
12828
+ let cdp;
12829
+ try {
12830
+ cdp = await contextWithCdp.newCDPSession(page);
12831
+ } catch {
12832
+ return;
12833
+ }
12834
+ try {
12835
+ await applyCdpStealthCommands((method, params) => cdp.send(method, params), profile);
12836
+ } catch {
12837
+ } finally {
12838
+ await cdp.detach().catch(() => void 0);
12839
+ }
12840
+ }
12841
+ async function applyCdpStealthCommands(send, profile) {
12842
+ await send("Network.setUserAgentOverride", {
12843
+ userAgent: profile.userAgent,
12844
+ acceptLanguage: `${profile.locale},en;q=0.9`,
12845
+ platform: getPlatformString2(profile.platform),
12846
+ userAgentMetadata: buildUserAgentMetadata(profile)
12847
+ });
12848
+ await send("Emulation.setDeviceMetricsOverride", {
12849
+ width: profile.viewport.width,
12850
+ height: profile.viewport.height,
12851
+ deviceScaleFactor: profile.devicePixelRatio,
12852
+ mobile: false,
12853
+ screenWidth: profile.screenResolution.width,
12854
+ screenHeight: profile.screenResolution.height
12855
+ });
12856
+ await send("Emulation.setLocaleOverride", {
12857
+ locale: profile.locale
12858
+ }).catch(() => void 0);
12859
+ await send("Emulation.setTimezoneOverride", {
12860
+ timezoneId: profile.timezoneId
12861
+ }).catch(() => void 0);
12862
+ }
12863
+ function buildStealthRequestHeaders(profile) {
12864
+ const metadata = buildUserAgentMetadata(profile);
12865
+ return {
12866
+ "Accept-Language": `${profile.locale},en;q=0.9`,
12867
+ "Sec-CH-UA": metadata.brands.map(formatClientHintBrand).join(", "),
12868
+ "Sec-CH-UA-Mobile": "?0",
12869
+ "Sec-CH-UA-Platform": `"${metadata.platform}"`,
12870
+ "User-Agent": profile.userAgent
12871
+ };
12872
+ }
12873
+ function formatClientHintBrand(brand2) {
12874
+ return `"${brand2.brand}";v="${brand2.version}"`;
12875
+ }
12876
+ function getPlatformString2(platform) {
12877
+ return platform === "macos" ? "MacIntel" : platform === "windows" ? "Win32" : "Linux x86_64";
12878
+ }
12383
12879
 
12384
12880
  // src/local-browser/stealth-profiles.ts
12385
12881
  var PROFILE_PRESETS = [
12386
12882
  {
12387
12883
  platform: "macos",
12388
12884
  browserBrand: "chrome",
12389
- browserVersion: "133.0.6943.99",
12390
- 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",
12885
+ browserVersion: "136.0.7103.93",
12886
+ 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",
12391
12887
  viewport: { width: 1440, height: 900 },
12392
12888
  screenResolution: { width: 1512, height: 982 },
12393
12889
  devicePixelRatio: 2,
12394
12890
  maxTouchPoints: 0,
12395
- webglVendor: "Apple Inc.",
12891
+ webglVendor: "Apple",
12396
12892
  webglRenderer: "Apple M2",
12397
12893
  fonts: ["SF Pro Text", "Helvetica Neue", "Arial", "Menlo", "Apple Color Emoji"],
12398
12894
  locale: "en-US",
@@ -12401,8 +12897,8 @@ var PROFILE_PRESETS = [
12401
12897
  {
12402
12898
  platform: "windows",
12403
12899
  browserBrand: "chrome",
12404
- browserVersion: "133.0.6943.99",
12405
- userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.99 Safari/537.36",
12900
+ browserVersion: "136.0.7103.93",
12901
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
12406
12902
  viewport: { width: 1536, height: 864 },
12407
12903
  screenResolution: { width: 1920, height: 1080 },
12408
12904
  devicePixelRatio: 1.25,
@@ -12416,8 +12912,8 @@ var PROFILE_PRESETS = [
12416
12912
  {
12417
12913
  platform: "windows",
12418
12914
  browserBrand: "edge",
12419
- browserVersion: "133.0.3065.82",
12420
- 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",
12915
+ browserVersion: "136.0.3240.50",
12916
+ 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",
12421
12917
  viewport: { width: 1536, height: 864 },
12422
12918
  screenResolution: { width: 1920, height: 1080 },
12423
12919
  devicePixelRatio: 1.25,
@@ -12431,8 +12927,8 @@ var PROFILE_PRESETS = [
12431
12927
  {
12432
12928
  platform: "linux",
12433
12929
  browserBrand: "chrome",
12434
- browserVersion: "133.0.6943.99",
12435
- userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.6943.99 Safari/537.36",
12930
+ browserVersion: "136.0.7103.93",
12931
+ userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
12436
12932
  viewport: { width: 1366, height: 768 },
12437
12933
  screenResolution: { width: 1366, height: 768 },
12438
12934
  devicePixelRatio: 1,
@@ -12586,7 +13082,7 @@ var OpensteerBrowserManager = class {
12586
13082
  await this.closePersistentBrowser(workspace);
12587
13083
  await rm(resolveAbpSessionDir(workspace), { recursive: true, force: true });
12588
13084
  await rm(workspace.browserPath, { recursive: true, force: true });
12589
- await rm(workspace.liveBrowserPath, { force: true });
13085
+ await clearPersistedSessionRecord(workspace.rootPath);
12590
13086
  await ensureDirectory(workspace.browserUserDataDir);
12591
13087
  });
12592
13088
  }
@@ -12597,7 +13093,7 @@ var OpensteerBrowserManager = class {
12597
13093
  await this.closePersistentBrowser(workspace);
12598
13094
  await rm(resolveAbpSessionDir(workspace), { recursive: true, force: true });
12599
13095
  await rm(workspace.browserPath, { recursive: true, force: true });
12600
- await rm(workspace.liveBrowserPath, { force: true });
13096
+ await clearPersistedSessionRecord(workspace.rootPath);
12601
13097
  });
12602
13098
  }
12603
13099
  async close() {
@@ -12675,12 +13171,12 @@ var OpensteerBrowserManager = class {
12675
13171
  sessionDir: resolveAbpSessionDir(workspace),
12676
13172
  ...launch?.browserExecutablePath === void 0 ? {} : { executablePath: launch.browserExecutablePath }
12677
13173
  };
12678
- await writeJsonFileAtomic(workspace.liveBrowserPath, liveRecord);
13174
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
12679
13175
  try {
12680
13176
  return await this.createAdoptedAbpEngine(liveRecord);
12681
13177
  } catch (error) {
12682
13178
  await terminateProcess(launched.process.pid ?? 0).catch(() => void 0);
12683
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13179
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12684
13180
  throw error;
12685
13181
  }
12686
13182
  });
@@ -12702,7 +13198,8 @@ var OpensteerBrowserManager = class {
12702
13198
  await clearChromeSingletonEntries(userDataDir);
12703
13199
  const launched = await launchOwnedBrowser({
12704
13200
  userDataDir,
12705
- ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions }
13201
+ ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions },
13202
+ ...this.contextOptions?.viewport === void 0 ? {} : { viewport: this.contextOptions.viewport }
12706
13203
  });
12707
13204
  try {
12708
13205
  return await this.createAttachedEngine({
@@ -12750,7 +13247,8 @@ var OpensteerBrowserManager = class {
12750
13247
  await this.ensurePersistentBrowserManifest(workspace);
12751
13248
  const launched = await launchOwnedBrowser({
12752
13249
  userDataDir: workspace.browserUserDataDir,
12753
- ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions }
13250
+ ...this.launchOptions === void 0 ? {} : { launch: this.launchOptions },
13251
+ ...this.contextOptions?.viewport === void 0 ? {} : { viewport: this.contextOptions.viewport }
12754
13252
  });
12755
13253
  const liveRecord = {
12756
13254
  mode: "persistent",
@@ -12761,7 +13259,7 @@ var OpensteerBrowserManager = class {
12761
13259
  executablePath: launched.executablePath,
12762
13260
  userDataDir: workspace.browserUserDataDir
12763
13261
  };
12764
- await writeJsonFileAtomic(workspace.liveBrowserPath, liveRecord);
13262
+ await this.writeLivePersistentBrowser(workspace, liveRecord);
12765
13263
  try {
12766
13264
  return await this.createAttachedEngine({
12767
13265
  endpoint: launched.endpoint,
@@ -12770,7 +13268,7 @@ var OpensteerBrowserManager = class {
12770
13268
  });
12771
13269
  } catch (error) {
12772
13270
  await terminateProcess(launched.pid).catch(() => void 0);
12773
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13271
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12774
13272
  throw error;
12775
13273
  }
12776
13274
  });
@@ -12785,13 +13283,16 @@ var OpensteerBrowserManager = class {
12785
13283
  if (!context) {
12786
13284
  throw new Error("Connected browser did not expose a Chromium browser context.");
12787
13285
  }
13286
+ const page = input.freshTab || context.pages()[0] === void 0 ? await context.newPage() : context.pages()[0];
13287
+ await page.bringToFront?.();
12788
13288
  const stealthProfile = resolveStealthProfile(this.contextOptions?.stealthProfile);
12789
13289
  await injectBrowserStealthScripts(
12790
13290
  context,
12791
- stealthProfile === void 0 ? {} : { profile: stealthProfile }
13291
+ stealthProfile === void 0 ? {} : {
13292
+ profile: stealthProfile,
13293
+ page
13294
+ }
12792
13295
  );
12793
- const page = input.freshTab || context.pages()[0] === void 0 ? await context.newPage() : context.pages()[0];
12794
- await page.bringToFront?.();
12795
13296
  const engine = await createPlaywrightBrowserCoreEngine({
12796
13297
  browser,
12797
13298
  attachedContext: context,
@@ -12873,7 +13374,7 @@ var OpensteerBrowserManager = class {
12873
13374
  return void 0;
12874
13375
  }
12875
13376
  if (!isProcessRunning(live.pid)) {
12876
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13377
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12877
13378
  return void 0;
12878
13379
  }
12879
13380
  if (live.engine === "playwright") {
@@ -12892,19 +13393,8 @@ var OpensteerBrowserManager = class {
12892
13393
  return live;
12893
13394
  }
12894
13395
  async readStoredLiveBrowser(workspace) {
12895
- if (!await pathExists(workspace.liveBrowserPath)) {
12896
- return void 0;
12897
- }
12898
- const live = await readJsonFile(
12899
- workspace.liveBrowserPath
12900
- );
12901
- if (live.engine === "abp") {
12902
- return live;
12903
- }
12904
- return {
12905
- ...live,
12906
- engine: "playwright"
12907
- };
13396
+ const live = await readPersistedLocalBrowserSessionRecord(workspace.rootPath);
13397
+ return live === void 0 ? void 0 : toWorkspaceLiveBrowserRecord(live);
12908
13398
  }
12909
13399
  async resolveLivePersistentEngineName() {
12910
13400
  if (this.mode !== "persistent") {
@@ -12924,7 +13414,7 @@ var OpensteerBrowserManager = class {
12924
13414
  async closePersistentBrowser(workspace) {
12925
13415
  const live = await this.readStoredLiveBrowser(workspace);
12926
13416
  if (!live) {
12927
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13417
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12928
13418
  return;
12929
13419
  }
12930
13420
  if (live.engine === "playwright") {
@@ -12932,15 +13422,21 @@ var OpensteerBrowserManager = class {
12932
13422
  await requestBrowserClose(live.endpoint).catch(() => void 0);
12933
13423
  }
12934
13424
  if (await waitForProcessExit(live.pid, BROWSER_CLOSE_TIMEOUT_MS)) {
12935
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13425
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12936
13426
  return;
12937
13427
  }
12938
13428
  await terminateProcess(live.pid).catch(() => void 0);
12939
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13429
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
12940
13430
  return;
12941
13431
  }
12942
13432
  await terminateProcess(live.pid).catch(() => void 0);
12943
- await rm(workspace.liveBrowserPath, { force: true }).catch(() => void 0);
13433
+ await clearPersistedSessionRecord(workspace.rootPath).catch(() => void 0);
13434
+ }
13435
+ async writeLivePersistentBrowser(workspace, live) {
13436
+ await writePersistedSessionRecord(
13437
+ workspace.rootPath,
13438
+ toPersistedLocalBrowserSessionRecord(this.workspace, live)
13439
+ );
12944
13440
  }
12945
13441
  requirePersistentMode(method) {
12946
13442
  if (this.mode !== "persistent" || this.workspace === void 0) {
@@ -12952,6 +13448,39 @@ function normalizeWorkspace(workspace) {
12952
13448
  const normalized = workspace?.trim();
12953
13449
  return normalized === void 0 || normalized.length === 0 ? void 0 : normalized;
12954
13450
  }
13451
+ function toPersistedLocalBrowserSessionRecord(workspace, live) {
13452
+ return {
13453
+ layout: "opensteer-session",
13454
+ version: 1,
13455
+ provider: "local",
13456
+ mode: "browser",
13457
+ ...workspace === void 0 ? {} : { workspace },
13458
+ engine: live.engine,
13459
+ ...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
13460
+ ...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
13461
+ ...live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: live.remoteDebuggingUrl },
13462
+ ...live.sessionDir === void 0 ? {} : { sessionDir: live.sessionDir },
13463
+ pid: live.pid,
13464
+ startedAt: live.startedAt,
13465
+ updatedAt: Date.now(),
13466
+ ...live.executablePath === void 0 ? {} : { executablePath: live.executablePath },
13467
+ userDataDir: live.userDataDir
13468
+ };
13469
+ }
13470
+ function toWorkspaceLiveBrowserRecord(record) {
13471
+ return {
13472
+ mode: "persistent",
13473
+ engine: record.engine,
13474
+ ...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
13475
+ ...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
13476
+ ...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl },
13477
+ ...record.sessionDir === void 0 ? {} : { sessionDir: record.sessionDir },
13478
+ pid: record.pid,
13479
+ startedAt: record.startedAt,
13480
+ ...record.executablePath === void 0 ? {} : { executablePath: record.executablePath },
13481
+ userDataDir: record.userDataDir
13482
+ };
13483
+ }
12955
13484
  function resolveBrowserMode(workspace, browser) {
12956
13485
  if (browser === void 0) {
12957
13486
  return workspace === void 0 ? "temporary" : "persistent";
@@ -12977,8 +13506,9 @@ async function resolveAttachEndpoint(browser) {
12977
13506
  async function launchOwnedBrowser(input) {
12978
13507
  await ensureDirectory(input.userDataDir);
12979
13508
  await clearChromeSingletonEntries(input.userDataDir);
13509
+ await sanitizeChromeProfile(input.userDataDir);
12980
13510
  const executablePath = resolveChromeExecutablePath(input.launch?.executablePath);
12981
- const args = buildChromeArgs(input.userDataDir, input.launch);
13511
+ const args = buildChromeArgs(input.userDataDir, input.launch, input.viewport);
12982
13512
  const child = spawn(executablePath, args, {
12983
13513
  stdio: ["ignore", "ignore", "pipe"],
12984
13514
  detached: process.platform !== "win32"
@@ -13005,7 +13535,8 @@ async function launchOwnedBrowser(input) {
13005
13535
  executablePath
13006
13536
  };
13007
13537
  }
13008
- function buildChromeArgs(userDataDir, launch) {
13538
+ function buildChromeArgs(userDataDir, launch, viewport) {
13539
+ const isHeadless = launch?.headless ?? true;
13009
13540
  const args = [
13010
13541
  "--remote-debugging-port=0",
13011
13542
  "--no-first-run",
@@ -13019,17 +13550,23 @@ function buildChromeArgs(userDataDir, launch) {
13019
13550
  "--disable-popup-blocking",
13020
13551
  "--disable-prompt-on-repost",
13021
13552
  "--disable-sync",
13553
+ "--disable-infobars",
13022
13554
  "--disable-features=Translate",
13023
13555
  "--enable-features=NetworkService,NetworkServiceInProcess",
13024
13556
  "--password-store=basic",
13025
13557
  "--use-mock-keychain",
13026
13558
  `--user-data-dir=${userDataDir}`
13027
13559
  ];
13028
- if (launch?.headless ?? true) {
13560
+ if (isHeadless) {
13029
13561
  args.push("--headless=new");
13030
- if (!(launch?.args ?? []).some((entry) => entry.startsWith("--window-size"))) {
13031
- args.push("--window-size=1280,800");
13032
- }
13562
+ }
13563
+ const hasUserWindowSize = (launch?.args ?? []).some((entry) => entry.startsWith("--window-size"));
13564
+ if (!hasUserWindowSize) {
13565
+ const width = viewport?.width ?? 1440;
13566
+ const height = viewport?.height ?? 900;
13567
+ args.push(
13568
+ isHeadless ? `--window-size=${String(width)},${String(height)}` : `--window-size=${String(width)},${String(height + 150)}`
13569
+ );
13033
13570
  }
13034
13571
  args.push(...launch?.args ?? []);
13035
13572
  return args;
@@ -13221,7 +13758,7 @@ async function sleep(ms) {
13221
13758
  await new Promise((resolve5) => setTimeout(resolve5, ms));
13222
13759
  }
13223
13760
 
13224
- // src/sdk/semantic-dispatch.ts
13761
+ // ../runtime-core/src/sdk/semantic-dispatch.ts
13225
13762
  async function dispatchSemanticOperation(runtime, operation, input, options = {}) {
13226
13763
  switch (operation) {
13227
13764
  case "session.open":
@@ -13482,7 +14019,14 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
13482
14019
  }
13483
14020
  }
13484
14021
 
13485
- // src/internal/errors.ts
14022
+ // ../runtime-core/package.json
14023
+ var package_default = {
14024
+ version: "0.1.0"};
14025
+
14026
+ // ../runtime-core/src/version.ts
14027
+ var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
14028
+
14029
+ // ../runtime-core/src/internal/errors.ts
13486
14030
  function normalizeThrownOpensteerError(error, fallbackMessage) {
13487
14031
  if (isOpensteerProtocolError(error)) {
13488
14032
  return toOpensteerError(error);
@@ -13493,15 +14037,6 @@ function normalizeThrownOpensteerError(error, fallbackMessage) {
13493
14037
  ...error.details === void 0 ? {} : { details: error.details }
13494
14038
  });
13495
14039
  }
13496
- if (error instanceof OpensteerAttachAmbiguousError) {
13497
- return createOpensteerError("conflict", error.message, {
13498
- details: {
13499
- candidates: error.candidates,
13500
- code: error.code,
13501
- name: error.name
13502
- }
13503
- });
13504
- }
13505
14040
  if (error instanceof Error) {
13506
14041
  return createOpensteerError("operation-failed", error.message, {
13507
14042
  details: {
@@ -13671,7 +14206,7 @@ function roundScale(value) {
13671
14206
  return Number(value.toFixed(6));
13672
14207
  }
13673
14208
 
13674
- // src/runtimes/computer-use/trace-enrichment.ts
14209
+ // ../runtime-core/src/runtimes/computer-use/trace-enrichment.ts
13675
14210
  async function enrichComputerUseTrace(input) {
13676
14211
  const tracePoints = toTracePoints(input.action);
13677
14212
  if (tracePoints.length === 0) {
@@ -13764,7 +14299,7 @@ function toOpensteerResolvedTarget(target) {
13764
14299
  };
13765
14300
  }
13766
14301
 
13767
- // src/runtimes/computer-use/runtime.ts
14302
+ // ../runtime-core/src/runtimes/computer-use/runtime.ts
13768
14303
  function createComputerUseRuntime(options) {
13769
14304
  return new DefaultComputerUseRuntime(options);
13770
14305
  }
@@ -13859,7 +14394,7 @@ function normalizeScreenshotOptions(input) {
13859
14394
  };
13860
14395
  }
13861
14396
 
13862
- // src/requests/shared.ts
14397
+ // ../runtime-core/src/requests/shared.ts
13863
14398
  var REDACTED_HEADER_VALUE = "[redacted]";
13864
14399
  var HTTP_HEADER_NAME_PATTERN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
13865
14400
  var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
@@ -14075,14 +14610,14 @@ function resolveTextEncoding(charset) {
14075
14610
  }
14076
14611
  }
14077
14612
 
14078
- // src/requests/errors.ts
14613
+ // ../runtime-core/src/requests/errors.ts
14079
14614
  function invalidRequestPlanError(message, details = {}) {
14080
14615
  return new OpensteerProtocolError("invalid-request", message, {
14081
14616
  details
14082
14617
  });
14083
14618
  }
14084
14619
 
14085
- // src/requests/plans/index.ts
14620
+ // ../runtime-core/src/requests/plans/index.ts
14086
14621
  var HTTP_METHOD_PATTERN = /^[A-Za-z]+$/;
14087
14622
  var URL_TEMPLATE_PLACEHOLDER_PATTERN = /\{([A-Za-z][A-Za-z0-9_-]*)\}/g;
14088
14623
  function assertValidRequestPlanPayload(payload) {
@@ -14490,7 +15025,7 @@ function normalizeTrimmedString(field, value) {
14490
15025
  return normalized;
14491
15026
  }
14492
15027
 
14493
- // src/requests/inference.ts
15028
+ // ../runtime-core/src/requests/inference.ts
14494
15029
  function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14495
15030
  const url = new URL(record.record.url);
14496
15031
  const defaultQuery = Array.from(url.searchParams.entries()).map(
@@ -14503,6 +15038,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14503
15038
  const responseContentType = headerValue(record.record.responseHeaders, "content-type") ?? record.record.responseBody?.mimeType;
14504
15039
  const defaultHeaders = inferDefaultHeaders(record);
14505
15040
  const auth = inferAuth(record.record.requestHeaders);
15041
+ const body = inferRequestPlanBody(record.record.requestBody, requestContentType);
14506
15042
  const payload = normalizeRequestPlanPayload({
14507
15043
  transport: {
14508
15044
  kind: "context-http"
@@ -14513,12 +15049,7 @@ function inferRequestPlanFromNetworkRecord(record, input, options = {}) {
14513
15049
  ...defaultQuery.length === 0 ? {} : { defaultQuery },
14514
15050
  ...defaultHeaders.length === 0 ? {} : { defaultHeaders }
14515
15051
  },
14516
- ...requestContentType === void 0 && record.record.requestBody === void 0 ? {} : {
14517
- body: {
14518
- ...requestContentType === void 0 ? {} : { contentType: requestContentType },
14519
- required: true
14520
- }
14521
- },
15052
+ ...body === void 0 ? {} : { body },
14522
15053
  ...record.record.status === void 0 ? {} : {
14523
15054
  response: {
14524
15055
  status: record.record.status,
@@ -14574,22 +15105,66 @@ function inferAuth(headers) {
14574
15105
  }
14575
15106
  return void 0;
14576
15107
  }
14577
-
14578
- // src/reverse/materialization.ts
14579
- var ALWAYS_OMIT_HEADER_NAMES = /* @__PURE__ */ new Set(["content-length", "host", "priority"]);
14580
- var BROWSER_OWNED_HEADER_PREFIXES = ["sec-"];
14581
- function isManagedRequestHeaderName(name, transport) {
14582
- const normalized = normalizeHeaderName(name);
14583
- if (!isValidHttpHeaderName(name)) {
14584
- return true;
14585
- }
14586
- if (normalized.startsWith(":")) {
14587
- return true;
14588
- }
14589
- if (ALWAYS_OMIT_HEADER_NAMES.has(normalized)) {
14590
- return true;
15108
+ function inferRequestPlanBody(body, contentType) {
15109
+ if (body === void 0) {
15110
+ return void 0;
14591
15111
  }
14592
- if (transport !== void 0 && (transport === "page-http" || transport === "session-http")) {
15112
+ const text = Buffer.from(body.data, "base64").toString("utf8");
15113
+ const normalizedContentType = contentType?.toLowerCase();
15114
+ const trimmedText = text.trim();
15115
+ const parsedJson = parseJsonBody(trimmedText);
15116
+ if (normalizedContentType?.includes("application/json") === true || normalizedContentType?.includes("+json") === true || parsedJson !== void 0) {
15117
+ return {
15118
+ kind: "json",
15119
+ required: true,
15120
+ ...contentType === void 0 ? {} : { contentType },
15121
+ template: parsedJson ?? text
15122
+ };
15123
+ }
15124
+ if (normalizedContentType?.includes("application/x-www-form-urlencoded") === true) {
15125
+ return {
15126
+ kind: "form",
15127
+ required: true,
15128
+ ...contentType === void 0 ? {} : { contentType },
15129
+ fields: Array.from(new URLSearchParams(text).entries()).map(([name, value]) => ({
15130
+ name,
15131
+ value
15132
+ }))
15133
+ };
15134
+ }
15135
+ return {
15136
+ kind: "text",
15137
+ required: true,
15138
+ ...contentType === void 0 ? {} : { contentType },
15139
+ template: text
15140
+ };
15141
+ }
15142
+ function parseJsonBody(value) {
15143
+ if (value.length === 0 || !value.startsWith("{") && !value.startsWith("[")) {
15144
+ return void 0;
15145
+ }
15146
+ try {
15147
+ return JSON.parse(value);
15148
+ } catch {
15149
+ return void 0;
15150
+ }
15151
+ }
15152
+
15153
+ // ../runtime-core/src/reverse/materialization.ts
15154
+ var ALWAYS_OMIT_HEADER_NAMES = /* @__PURE__ */ new Set(["content-length", "host", "priority"]);
15155
+ var BROWSER_OWNED_HEADER_PREFIXES = ["sec-"];
15156
+ function isManagedRequestHeaderName(name, transport) {
15157
+ const normalized = normalizeHeaderName(name);
15158
+ if (!isValidHttpHeaderName(name)) {
15159
+ return true;
15160
+ }
15161
+ if (normalized.startsWith(":")) {
15162
+ return true;
15163
+ }
15164
+ if (ALWAYS_OMIT_HEADER_NAMES.has(normalized)) {
15165
+ return true;
15166
+ }
15167
+ if (transport !== void 0 && (transport === "page-http" || transport === "session-http")) {
14593
15168
  if (BROWSER_OWNED_HEADER_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
14594
15169
  return true;
14595
15170
  }
@@ -14611,7 +15186,7 @@ function finalizeMaterializedTransportRequest(request, transport) {
14611
15186
  };
14612
15187
  }
14613
15188
 
14614
- // src/network/diff.ts
15189
+ // ../runtime-core/src/network/diff.ts
14615
15190
  function diffNetworkRecords(left, right, input) {
14616
15191
  const requestDiffs = [];
14617
15192
  const responseDiffs = [];
@@ -14771,7 +15346,7 @@ function diffStringMap(prefix, left, right, includeUnchanged, output) {
14771
15346
  diffScalarField(`${prefix}.${key}`, left[key], right[key], includeUnchanged, output);
14772
15347
  }
14773
15348
  }
14774
- function diffScalarField(path11, left, right, includeUnchanged, output) {
15349
+ function diffScalarField(path13, left, right, includeUnchanged, output) {
14775
15350
  const leftValue = stringifyFieldValue(left);
14776
15351
  const rightValue = stringifyFieldValue(right);
14777
15352
  const kind = leftValue === void 0 ? rightValue === void 0 ? "unchanged" : "added" : rightValue === void 0 ? "removed" : leftValue === rightValue ? "unchanged" : "changed";
@@ -14779,7 +15354,7 @@ function diffScalarField(path11, left, right, includeUnchanged, output) {
14779
15354
  return;
14780
15355
  }
14781
15356
  output.push({
14782
- path: path11,
15357
+ path: path13,
14783
15358
  kind,
14784
15359
  ...leftValue === void 0 ? {} : { leftValue },
14785
15360
  ...rightValue === void 0 ? {} : { rightValue },
@@ -14968,7 +15543,7 @@ var NetworkJournal = class {
14968
15543
  }
14969
15544
  };
14970
15545
 
14971
- // src/network/minimize.ts
15546
+ // ../runtime-core/src/network/minimize.ts
14972
15547
  function prepareMinimizationRequest(record, preserve = []) {
14973
15548
  const requestUrl = new URL(record.record.url);
14974
15549
  const preservedNames = new Set(
@@ -15381,7 +15956,7 @@ function resolveBodyEncoding2(charset) {
15381
15956
  }
15382
15957
  }
15383
15958
 
15384
- // src/network/probe.ts
15959
+ // ../runtime-core/src/network/probe.ts
15385
15960
  var TRANSPORT_PROBE_LADDER = [
15386
15961
  "direct-http",
15387
15962
  "matched-tls",
@@ -15393,7 +15968,7 @@ function selectTransportProbeRecommendation(results) {
15393
15968
  return results.find((entry) => entry.success)?.transport ?? results.at(-1)?.transport ?? "session-http";
15394
15969
  }
15395
15970
 
15396
- // src/reverse/analysis.ts
15971
+ // ../runtime-core/src/reverse/analysis.ts
15397
15972
  var TELEMETRY_HOST_PATTERNS = [
15398
15973
  "google-analytics",
15399
15974
  "doubleclick",
@@ -15733,9 +16308,9 @@ function matchReverseTargetHints(channel, codec, targetHints) {
15733
16308
  matches.add(`host:${host}`);
15734
16309
  }
15735
16310
  }
15736
- for (const path11 of targetHints.paths ?? []) {
15737
- if (url.pathname.includes(path11)) {
15738
- matches.add(`path:${path11}`);
16311
+ for (const path13 of targetHints.paths ?? []) {
16312
+ if (url.pathname.includes(path13)) {
16313
+ matches.add(`path:${path13}`);
15739
16314
  }
15740
16315
  }
15741
16316
  for (const operationName of targetHints.operationNames ?? []) {
@@ -16587,7 +17162,7 @@ function looksHighEntropy(value) {
16587
17162
  return uniqueCharacters >= Math.min(20, Math.floor(trimmed.length * 0.6));
16588
17163
  }
16589
17164
 
16590
- // src/reverse/discovery.ts
17165
+ // ../runtime-core/src/reverse/discovery.ts
16591
17166
  function clusterReverseObservationRecords(input) {
16592
17167
  const groups = /* @__PURE__ */ new Map();
16593
17168
  for (const item of sortClusterableRecords(input.records)) {
@@ -16887,14 +17462,14 @@ async function isExecutable(candidate) {
16887
17462
  }
16888
17463
  async function readDirSafe(directory) {
16889
17464
  try {
16890
- const { readdir: readdir3 } = await import('fs/promises');
16891
- return await readdir3(directory);
17465
+ const { readdir: readdir4 } = await import('fs/promises');
17466
+ return await readdir4(directory);
16892
17467
  } catch {
16893
17468
  return [];
16894
17469
  }
16895
17470
  }
16896
17471
 
16897
- // src/reverse/validation.ts
17472
+ // ../runtime-core/src/reverse/validation.ts
16898
17473
  function buildReverseValidationRules(input) {
16899
17474
  switch (input.channel.kind) {
16900
17475
  case "http":
@@ -17171,7 +17746,7 @@ function jsonStructureShape(value) {
17171
17746
  return typeof value;
17172
17747
  }
17173
17748
 
17174
- // src/reverse/workflows.ts
17749
+ // ../runtime-core/src/reverse/workflows.ts
17175
17750
  function buildReversePackageWorkflow(input) {
17176
17751
  if (input.template === void 0 && input.executeStepInput === void 0) {
17177
17752
  return [];
@@ -17540,7 +18115,7 @@ function dedupeSuggestedEdits(suggestions) {
17540
18115
  return [...new Map(suggestions.map((suggestion) => [suggestion.id, suggestion])).values()];
17541
18116
  }
17542
18117
 
17543
- // src/sdk/extraction-data-path.ts
18118
+ // ../runtime-core/src/sdk/extraction-data-path.ts
17544
18119
  function joinDataPath(base, key) {
17545
18120
  const normalizedBase = base.trim();
17546
18121
  const normalizedKey = key.trim();
@@ -17571,8 +18146,8 @@ function encodeDataPath(tokens) {
17571
18146
  }
17572
18147
  return out;
17573
18148
  }
17574
- function parseDataPath(path11) {
17575
- const input = path11.trim();
18149
+ function parseDataPath(path13) {
18150
+ const input = path13.trim();
17576
18151
  if (input.length === 0) {
17577
18152
  return [];
17578
18153
  }
@@ -17622,8 +18197,8 @@ function parseDataPath(path11) {
17622
18197
  function inflateDataPathObject(flat) {
17623
18198
  let root = {};
17624
18199
  let initialized = false;
17625
- for (const [path11, value] of Object.entries(flat)) {
17626
- const tokens = parseDataPath(path11);
18200
+ for (const [path13, value] of Object.entries(flat)) {
18201
+ const tokens = parseDataPath(path13);
17627
18202
  if (!tokens || tokens.length === 0) {
17628
18203
  continue;
17629
18204
  }
@@ -17681,7 +18256,7 @@ function assignDataPathValue(root, tokens, value) {
17681
18256
  }
17682
18257
  }
17683
18258
 
17684
- // src/sdk/extraction-consolidation.ts
18259
+ // ../runtime-core/src/sdk/extraction-consolidation.ts
17685
18260
  var STRUCTURAL_ATTR_KEYS = /* @__PURE__ */ new Set([
17686
18261
  "class",
17687
18262
  "role",
@@ -17955,8 +18530,8 @@ function buildVariantDescriptorFromCluster(descriptors) {
17955
18530
  fields: mergedFields
17956
18531
  };
17957
18532
  }
17958
- function minimizePathMatchClauses(path11, mode) {
17959
- const normalized = sanitizeElementPath(path11);
18533
+ function minimizePathMatchClauses(path13, mode) {
18534
+ const normalized = sanitizeElementPath(path13);
17960
18535
  const nodes = normalized.nodes.map((node, index) => {
17961
18536
  const isLast = index === normalized.nodes.length - 1;
17962
18537
  const attrs = node.attrs || {};
@@ -18060,8 +18635,8 @@ function seedMinimalAttrClause(attrs) {
18060
18635
  }
18061
18636
  return null;
18062
18637
  }
18063
- function relaxPathForSingleSample(path11, mode) {
18064
- const normalized = sanitizeElementPath(path11);
18638
+ function relaxPathForSingleSample(path13, mode) {
18639
+ const normalized = sanitizeElementPath(path13);
18065
18640
  const relaxedNodes = normalized.nodes.map((node, index) => {
18066
18641
  const isLast = index === normalized.nodes.length - 1;
18067
18642
  const attrs = normalizeAttrsForSingleSample(node.attrs || {});
@@ -18146,8 +18721,8 @@ function shouldKeepAttrForSingleSample(key) {
18146
18721
  }
18147
18722
  return true;
18148
18723
  }
18149
- function buildPathStructureKey(path11) {
18150
- const normalized = sanitizeElementPath(path11);
18724
+ function buildPathStructureKey(path13) {
18725
+ const normalized = sanitizeElementPath(path13);
18151
18726
  return canonicalJsonString({
18152
18727
  context: (normalized.context || []).map((hop) => ({
18153
18728
  kind: hop.kind,
@@ -18274,30 +18849,30 @@ function buildArrayItemNode(fields) {
18274
18849
  }
18275
18850
  return node;
18276
18851
  }
18277
- function insertNodeAtPath(root, path11, node) {
18278
- const tokens = parseDataPath(path11);
18852
+ function insertNodeAtPath(root, path13, node) {
18853
+ const tokens = parseDataPath(path13);
18279
18854
  if (!tokens || !tokens.length) {
18280
18855
  throw new Error(
18281
- `Invalid persisted extraction path "${path11}": expected a non-empty object path.`
18856
+ `Invalid persisted extraction path "${path13}": expected a non-empty object path.`
18282
18857
  );
18283
18858
  }
18284
18859
  if (tokens.some((token) => token.kind === "index")) {
18285
18860
  throw new Error(
18286
- `Invalid persisted extraction path "${path11}": nested array indices are not supported in cached descriptors.`
18861
+ `Invalid persisted extraction path "${path13}": nested array indices are not supported in cached descriptors.`
18287
18862
  );
18288
18863
  }
18289
18864
  let current = root;
18290
18865
  for (let index = 0; index < tokens.length; index += 1) {
18291
18866
  const token = tokens[index];
18292
18867
  if (!token || token.kind !== "prop") {
18293
- throw new Error(`Invalid persisted extraction path "${path11}": expected object segment.`);
18868
+ throw new Error(`Invalid persisted extraction path "${path13}": expected object segment.`);
18294
18869
  }
18295
18870
  const isLast = index === tokens.length - 1;
18296
18871
  if (isLast) {
18297
18872
  const existing = current[token.key];
18298
18873
  if (existing) {
18299
18874
  throw new Error(
18300
- `Conflicting persisted extraction path "${path11}" detected while building descriptor tree.`
18875
+ `Conflicting persisted extraction path "${path13}" detected while building descriptor tree.`
18301
18876
  );
18302
18877
  }
18303
18878
  current[token.key] = node;
@@ -18312,7 +18887,7 @@ function insertNodeAtPath(root, path11, node) {
18312
18887
  }
18313
18888
  if (!isPersistedObjectNode(next)) {
18314
18889
  throw new Error(
18315
- `Conflicting persisted extraction path "${path11}" detected at "${token.key}".`
18890
+ `Conflicting persisted extraction path "${path13}" detected at "${token.key}".`
18316
18891
  );
18317
18892
  }
18318
18893
  current = next;
@@ -18347,7 +18922,7 @@ function buildItemRootForArrayIndex(entries) {
18347
18922
  }
18348
18923
  const paths = entries.map(
18349
18924
  (entry) => isPersistablePathField(entry.source) ? sanitizeElementPath(entry.source.path) : null
18350
- ).filter((path11) => path11 !== null);
18925
+ ).filter((path13) => path13 !== null);
18351
18926
  if (!paths.length) {
18352
18927
  return null;
18353
18928
  }
@@ -18368,7 +18943,7 @@ function getCommonPathPrefixLength(paths) {
18368
18943
  if (!paths.length) {
18369
18944
  return 0;
18370
18945
  }
18371
- const nodeChains = paths.map((path11) => path11.nodes);
18946
+ const nodeChains = paths.map((path13) => path13.nodes);
18372
18947
  const minLength = Math.min(...nodeChains.map((nodes) => nodes.length));
18373
18948
  if (!Number.isFinite(minLength) || minLength <= 0) {
18374
18949
  return 0;
@@ -18437,30 +19012,30 @@ function mergeElementPathsByMajority(paths) {
18437
19012
  if (!paths.length) {
18438
19013
  return null;
18439
19014
  }
18440
- const normalized = paths.map((path11) => sanitizeElementPath(path11));
19015
+ const normalized = paths.map((path13) => sanitizeElementPath(path13));
18441
19016
  const contextKey = pickModeString(
18442
- normalized.map((path11) => canonicalJsonString(path11.context)),
19017
+ normalized.map((path13) => canonicalJsonString(path13.context)),
18443
19018
  1
18444
19019
  );
18445
19020
  if (!contextKey) {
18446
19021
  return null;
18447
19022
  }
18448
- const sameContext = normalized.filter((path11) => canonicalJsonString(path11.context) === contextKey);
19023
+ const sameContext = normalized.filter((path13) => canonicalJsonString(path13.context) === contextKey);
18449
19024
  if (!sameContext.length) {
18450
19025
  return null;
18451
19026
  }
18452
19027
  const targetLength = pickModeNumber(
18453
- sameContext.map((path11) => path11.nodes.length),
19028
+ sameContext.map((path13) => path13.nodes.length),
18454
19029
  1
18455
19030
  ) ?? sameContext[0]?.nodes.length ?? 0;
18456
- const aligned = sameContext.filter((path11) => path11.nodes.length === targetLength);
19031
+ const aligned = sameContext.filter((path13) => path13.nodes.length === targetLength);
18457
19032
  if (!aligned.length) {
18458
19033
  return null;
18459
19034
  }
18460
19035
  const threshold = majorityThreshold(aligned.length);
18461
19036
  const nodes = [];
18462
19037
  for (let index = 0; index < targetLength; index += 1) {
18463
- const nodesAtIndex = aligned.map((path11) => path11.nodes[index]).filter((node) => node !== void 0);
19038
+ const nodesAtIndex = aligned.map((path13) => path13.nodes[index]).filter((node) => node !== void 0);
18464
19039
  if (!nodesAtIndex.length) {
18465
19040
  return null;
18466
19041
  }
@@ -18706,8 +19281,8 @@ function clonePathContext(context) {
18706
19281
  function clonePathNodes(nodes) {
18707
19282
  return JSON.parse(JSON.stringify(nodes || []));
18708
19283
  }
18709
- function cloneElementPath2(path11) {
18710
- return JSON.parse(JSON.stringify(path11));
19284
+ function cloneElementPath2(path13) {
19285
+ return JSON.parse(JSON.stringify(path13));
18711
19286
  }
18712
19287
  function clonePersistedOpensteerExtractionNode(node) {
18713
19288
  return JSON.parse(JSON.stringify(node));
@@ -18728,7 +19303,7 @@ function isPersistedObjectNode(node) {
18728
19303
  return !!node && typeof node === "object" && !Array.isArray(node) && !isPersistedOpensteerExtractionValueNode(node) && !isPersistedOpensteerExtractionSourceNode(node) && !isPersistedOpensteerExtractionArrayNode(node);
18729
19304
  }
18730
19305
 
18731
- // src/sdk/extraction.ts
19306
+ // ../runtime-core/src/sdk/extraction.ts
18732
19307
  function assertValidOpensteerExtractionSchemaRoot(schema) {
18733
19308
  if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
18734
19309
  throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
@@ -18993,12 +19568,12 @@ async function resolvePersistableFieldTargets(options) {
18993
19568
  anchor: field.anchor
18994
19569
  }
18995
19570
  });
18996
- const path11 = resolved.replayPath ?? await options.dom.buildPath({
19571
+ const path13 = resolved.replayPath ?? await options.dom.buildPath({
18997
19572
  locator: resolved.locator
18998
19573
  });
18999
19574
  fields.push({
19000
19575
  key: field.key,
19001
- path: sanitizeElementPath(path11),
19576
+ path: sanitizeElementPath(path13),
19002
19577
  ...field.attribute === void 0 ? {} : { attribute: field.attribute }
19003
19578
  });
19004
19579
  }
@@ -19081,8 +19656,8 @@ function collectPersistedValueNodeRefs(node) {
19081
19656
  return [
19082
19657
  {
19083
19658
  path: sanitizeElementPath(node.$path),
19084
- replacePath: (path11) => {
19085
- node.$path = sanitizeElementPath(path11);
19659
+ replacePath: (path13) => {
19660
+ node.$path = sanitizeElementPath(path13);
19086
19661
  }
19087
19662
  }
19088
19663
  ];
@@ -19096,13 +19671,13 @@ function collectPersistedValueNodeRefs(node) {
19096
19671
  }
19097
19672
  return refs;
19098
19673
  }
19099
- function hasPositionClause(path11) {
19100
- return path11.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
19674
+ function hasPositionClause(path13) {
19675
+ return path13.nodes.some((node) => node.match.some((clause) => clause.kind === "position"));
19101
19676
  }
19102
- function stripPositionClauses2(path11) {
19677
+ function stripPositionClauses2(path13) {
19103
19678
  return sanitizeElementPath({
19104
- context: path11.context,
19105
- nodes: path11.nodes.map((node) => ({
19679
+ context: path13.context,
19680
+ nodes: path13.nodes.map((node) => ({
19106
19681
  ...node,
19107
19682
  match: node.match.filter((clause) => clause.kind !== "position")
19108
19683
  }))
@@ -19512,14 +20087,14 @@ function normalizeNonEmptyString2(name, value) {
19512
20087
  function normalizeKey(value) {
19513
20088
  return String(value ?? "").trim();
19514
20089
  }
19515
- function labelForPath(path11) {
19516
- return path11.trim().length === 0 ? "$" : path11;
20090
+ function labelForPath(path13) {
20091
+ return path13.trim().length === 0 ? "$" : path13;
19517
20092
  }
19518
20093
  function sha256Hex3(value) {
19519
20094
  return createHash("sha256").update(value).digest("hex");
19520
20095
  }
19521
20096
 
19522
- // src/sdk/snapshot/constants.ts
20097
+ // ../runtime-core/src/sdk/snapshot/constants.ts
19523
20098
  var OPENSTEER_INTERACTIVE_ATTR = "data-opensteer-interactive";
19524
20099
  var OPENSTEER_HIDDEN_ATTR = "data-opensteer-hidden";
19525
20100
  var OPENSTEER_SCROLLABLE_ATTR = "data-opensteer-scrollable";
@@ -19587,7 +20162,7 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
19587
20162
  "wbr"
19588
20163
  ]);
19589
20164
 
19590
- // src/sdk/snapshot/cleaner.ts
20165
+ // ../runtime-core/src/sdk/snapshot/cleaner.ts
19591
20166
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
19592
20167
  var TEXT_ATTR_MAX = 150;
19593
20168
  var URL_ATTR_MAX = 500;
@@ -20027,7 +20602,7 @@ var VOID_TAGS2 = /* @__PURE__ */ new Set([
20027
20602
  "wbr"
20028
20603
  ]);
20029
20604
 
20030
- // src/sdk/snapshot/compiler.ts
20605
+ // ../runtime-core/src/sdk/snapshot/compiler.ts
20031
20606
  async function compileOpensteerSnapshot(options) {
20032
20607
  const pageInfo = await options.engine.getPageInfo({ pageRef: options.pageRef });
20033
20608
  const mainSnapshot = await getMainDocumentSnapshot(options.engine, options.pageRef);
@@ -20578,7 +21153,7 @@ async function beautifyScriptContent(content) {
20578
21153
  });
20579
21154
  }
20580
21155
 
20581
- // src/scripts/deobfuscate.ts
21156
+ // ../runtime-core/src/scripts/deobfuscate.ts
20582
21157
  async function deobfuscateScriptContent(input) {
20583
21158
  const webcrack = await loadWebcrack();
20584
21159
  const result = await webcrack(input.content, {
@@ -20636,7 +21211,7 @@ function inferTransforms(original, deobfuscated) {
20636
21211
  return [...inferred];
20637
21212
  }
20638
21213
 
20639
- // src/scripts/sandbox-shims/minimal.ts
21214
+ // ../runtime-core/src/scripts/sandbox-shims/minimal.ts
20640
21215
  function createMinimalSandboxGlobals(options) {
20641
21216
  return {
20642
21217
  console: options.console,
@@ -20660,7 +21235,7 @@ function createMinimalSandboxGlobals(options) {
20660
21235
  };
20661
21236
  }
20662
21237
 
20663
- // src/scripts/sandbox-shims/standard.ts
21238
+ // ../runtime-core/src/scripts/sandbox-shims/standard.ts
20664
21239
  function createStandardSandboxGlobals(options) {
20665
21240
  const globals = createMinimalSandboxGlobals(options);
20666
21241
  const eventApi = createEventTargetApi();
@@ -20931,7 +21506,7 @@ function normalizeErrorMessage(error) {
20931
21506
  return error instanceof Error ? error.message : String(error);
20932
21507
  }
20933
21508
 
20934
- // src/scripts/sandbox-shims/full.ts
21509
+ // ../runtime-core/src/scripts/sandbox-shims/full.ts
20935
21510
  function createFullSandboxGlobals(options) {
20936
21511
  const globals = createStandardSandboxGlobals(options);
20937
21512
  const eventListeners = /* @__PURE__ */ new WeakMap();
@@ -21051,7 +21626,7 @@ function createFullSandboxGlobals(options) {
21051
21626
  };
21052
21627
  }
21053
21628
 
21054
- // src/scripts/sandbox.ts
21629
+ // ../runtime-core/src/scripts/sandbox.ts
21055
21630
  async function runScriptSandbox(input) {
21056
21631
  const startedAt = Date.now();
21057
21632
  const errors = [];
@@ -21275,7 +21850,7 @@ function normalizeErrorMessage2(error) {
21275
21850
  return error instanceof Error ? error.message : String(error);
21276
21851
  }
21277
21852
 
21278
- // src/captcha/solver-capsolver.ts
21853
+ // ../runtime-core/src/captcha/solver-capsolver.ts
21279
21854
  var CAPSOLVER_CREATE_TASK_URL = "https://api.capsolver.com/createTask";
21280
21855
  var CAPSOLVER_GET_TASK_RESULT_URL = "https://api.capsolver.com/getTaskResult";
21281
21856
  function createCapSolver(apiKey) {
@@ -21373,7 +21948,7 @@ function sleep2(ms, signal) {
21373
21948
  });
21374
21949
  }
21375
21950
 
21376
- // src/captcha/solver-2captcha.ts
21951
+ // ../runtime-core/src/captcha/solver-2captcha.ts
21377
21952
  var TWO_CAPTCHA_CREATE_TASK_URL = "https://api.2captcha.com/createTask";
21378
21953
  var TWO_CAPTCHA_GET_TASK_RESULT_URL = "https://api.2captcha.com/getTaskResult";
21379
21954
  function createTwoCaptchaSolver(apiKey) {
@@ -21471,7 +22046,7 @@ function sleep3(ms, signal) {
21471
22046
  });
21472
22047
  }
21473
22048
 
21474
- // src/captcha/detect.ts
22049
+ // ../runtime-core/src/captcha/detect.ts
21475
22050
  var CAPTCHA_DETECTION_SCRIPT = `(() => {
21476
22051
  const pageUrl = location.href;
21477
22052
  const findSiteKey = (selectors) => {
@@ -21533,7 +22108,7 @@ async function detectCaptchaOnPage(engine, pageRef) {
21533
22108
  return candidate;
21534
22109
  }
21535
22110
 
21536
- // src/captcha/inject.ts
22111
+ // ../runtime-core/src/captcha/inject.ts
21537
22112
  async function injectCaptchaToken(input) {
21538
22113
  const result = await input.engine.evaluatePage({
21539
22114
  pageRef: input.pageRef,
@@ -21589,7 +22164,7 @@ var CAPTCHA_INJECTION_SCRIPT = `(({ type, token }) => {
21589
22164
  return true;
21590
22165
  })`;
21591
22166
 
21592
- // src/interaction/diff.ts
22167
+ // ../runtime-core/src/interaction/diff.ts
21593
22168
  function diffInteractionTraces(left, right) {
21594
22169
  const eventSequenceMismatches = [];
21595
22170
  const eventPropertyMismatches = [];
@@ -21658,20 +22233,17 @@ function diffInteractionTraces(left, right) {
21658
22233
  };
21659
22234
  }
21660
22235
 
21661
- // src/sdk/runtime.ts
22236
+ // ../runtime-core/src/sdk/runtime.ts
21662
22237
  var requireForAuthRecipeHook = createRequire(import.meta.url);
21663
- var OpensteerRuntime = class {
22238
+ var OpensteerSessionRuntime = class {
21664
22239
  workspace;
21665
22240
  rootPath;
21666
- publicWorkspace;
21667
- configuredBrowser;
21668
- configuredLaunch;
21669
- configuredContext;
21670
- configuredEngineName;
22241
+ workspaceName;
21671
22242
  injectedEngine;
21672
22243
  engineFactory;
21673
22244
  policy;
21674
22245
  cleanupRootOnClose;
22246
+ sessionInfoBase;
21675
22247
  root;
21676
22248
  engine;
21677
22249
  dom;
@@ -21686,35 +22258,46 @@ var OpensteerRuntime = class {
21686
22258
  cookieJars = /* @__PURE__ */ new Map();
21687
22259
  recipeCache = /* @__PURE__ */ new Map();
21688
22260
  ownsEngine = false;
21689
- constructor(options = {}) {
21690
- this.publicWorkspace = options.workspace?.trim() === void 0 || options.workspace?.trim().length === 0 ? void 0 : options.workspace.trim();
21691
- this.workspace = normalizeNamespace3(options.workspace);
21692
- this.rootPath = options.rootPath ?? (this.publicWorkspace === void 0 ? path6.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : path6.resolve(
21693
- options.rootDir ?? process.cwd(),
21694
- ".opensteer",
21695
- "workspaces",
21696
- encodeURIComponent(this.publicWorkspace)
21697
- ));
21698
- this.configuredBrowser = options.browser;
21699
- this.configuredLaunch = options.launch;
21700
- this.configuredContext = options.context;
21701
- this.configuredEngineName = options.engineName;
22261
+ constructor(options) {
22262
+ this.workspace = normalizeNamespace3(options.name);
22263
+ this.workspaceName = options.workspaceName?.trim() === void 0 || options.workspaceName?.trim().length === 0 ? void 0 : options.workspaceName.trim();
22264
+ this.root = options.workspace;
22265
+ this.rootPath = options.workspace?.rootPath ?? options.rootPath ?? path6.resolve(process.cwd(), ".opensteer", "temporary", randomUUID());
21702
22266
  this.injectedEngine = options.engine;
21703
- this.engineFactory = options.engineFactory ?? ((factoryOptions) => {
21704
- const browser = factoryOptions.browser ?? this.configuredBrowser;
21705
- const launch = factoryOptions.launch ?? this.configuredLaunch;
21706
- const context = factoryOptions.context ?? this.configuredContext;
21707
- return new OpensteerBrowserManager({
21708
- rootPath: this.rootPath,
21709
- ...this.publicWorkspace === void 0 ? {} : { workspace: this.publicWorkspace },
21710
- ...this.configuredEngineName === void 0 ? {} : { engineName: this.configuredEngineName },
21711
- ...browser === void 0 ? {} : { browser },
21712
- ...launch === void 0 ? {} : { launch },
21713
- ...context === void 0 ? {} : { context }
21714
- }).createEngine();
21715
- });
22267
+ this.engineFactory = options.engineFactory;
21716
22268
  this.policy = options.policy ?? defaultPolicy();
21717
- this.cleanupRootOnClose = options.cleanupRootOnClose ?? this.publicWorkspace === void 0;
22269
+ this.cleanupRootOnClose = options.cleanupRootOnClose ?? options.workspace === void 0;
22270
+ this.sessionInfoBase = options.sessionInfo ?? {};
22271
+ if (this.injectedEngine === void 0 && this.engineFactory === void 0) {
22272
+ throw new Error("OpensteerSessionRuntime requires an engine or engineFactory.");
22273
+ }
22274
+ }
22275
+ async info() {
22276
+ const base = this.sessionInfoBase;
22277
+ return {
22278
+ provider: base.provider ?? {
22279
+ kind: "local",
22280
+ ownership: "owned",
22281
+ engine: "playwright"
22282
+ },
22283
+ ...base.workspace === void 0 ? {} : { workspace: base.workspace },
22284
+ ...this.sessionRef === void 0 ? {} : { sessionId: this.sessionRef },
22285
+ ...this.pageRef === void 0 ? {} : { activePageRef: this.pageRef },
22286
+ reconnectable: base.reconnectable ?? !this.cleanupRootOnClose,
22287
+ capabilities: base.capabilities ?? {
22288
+ semanticOperations: opensteerSemanticOperationNames,
22289
+ instrumentation: {
22290
+ route: true,
22291
+ interceptScript: true,
22292
+ networkStream: false
22293
+ }
22294
+ },
22295
+ ...base.grants === void 0 ? {} : { grants: base.grants },
22296
+ runtime: base.runtime ?? {
22297
+ protocolVersion: OPENSTEER_PROTOCOL_VERSION,
22298
+ runtimeCoreVersion: OPENSTEER_RUNTIME_CORE_VERSION
22299
+ }
22300
+ };
21718
22301
  }
21719
22302
  async open(input = {}, options = {}) {
21720
22303
  assertValidSemanticOperationInput("session.open", input);
@@ -26085,14 +26668,14 @@ var OpensteerRuntime = class {
26085
26668
  return saved;
26086
26669
  }
26087
26670
  resolveCurrentStateSource() {
26088
- const browser = this.configuredBrowser;
26089
- if (browser === void 0 || browser === "temporary") {
26090
- return "temporary";
26671
+ const ownership = this.sessionInfoBase.provider?.ownership;
26672
+ if (ownership === "attached") {
26673
+ return "attach";
26091
26674
  }
26092
- if (browser === "persistent") {
26675
+ if (this.workspaceName !== void 0 || this.cleanupRootOnClose === false) {
26093
26676
  return "persistent";
26094
26677
  }
26095
- return "attach";
26678
+ return "temporary";
26096
26679
  }
26097
26680
  async resolveReverseCaseById(caseId) {
26098
26681
  const record = await (await this.ensureRoot()).registry.reverseCases.getById(caseId);
@@ -27659,8 +28242,8 @@ var OpensteerRuntime = class {
27659
28242
  async ensureRoot() {
27660
28243
  this.root ??= await createFilesystemOpensteerWorkspace({
27661
28244
  rootPath: this.rootPath,
27662
- ...this.publicWorkspace === void 0 ? {} : { workspace: this.publicWorkspace },
27663
- scope: this.publicWorkspace === void 0 ? "temporary" : "workspace"
28245
+ ...this.workspaceName === void 0 ? {} : { workspace: this.workspaceName },
28246
+ scope: this.workspaceName === void 0 ? "temporary" : "workspace"
27664
28247
  });
27665
28248
  return this.root;
27666
28249
  }
@@ -27673,15 +28256,10 @@ var OpensteerRuntime = class {
27673
28256
  this.ownsEngine = false;
27674
28257
  return this.engine;
27675
28258
  }
27676
- const browser = overrides.browser ?? this.configuredBrowser;
27677
- const launch = overrides.launch ?? this.configuredLaunch;
27678
- const context = overrides.context ?? this.configuredContext;
27679
- const factoryOptions = {
27680
- ...browser === void 0 ? {} : { browser },
27681
- ...launch === void 0 ? {} : { launch },
27682
- ...context === void 0 ? {} : { context }
27683
- };
27684
- this.engine = await this.engineFactory(factoryOptions);
28259
+ if (this.engineFactory === void 0) {
28260
+ throw new Error("Opensteer engine factory is not initialized");
28261
+ }
28262
+ this.engine = await this.engineFactory(overrides);
27685
28263
  this.ownsEngine = true;
27686
28264
  return this.engine;
27687
28265
  }
@@ -28138,6 +28716,9 @@ function parseContentType2(contentType) {
28138
28716
  };
28139
28717
  }
28140
28718
  function toJsonValueOrNull(value) {
28719
+ if (value === void 0) {
28720
+ return null;
28721
+ }
28141
28722
  return toCanonicalJsonValue(value) ?? null;
28142
28723
  }
28143
28724
  function stringifyRecipeVariableValue(value) {
@@ -28935,12 +29516,12 @@ function extractReverseRuntimeValue(value, pointer) {
28935
29516
  }
28936
29517
  return readDotPath(value, pointer);
28937
29518
  }
28938
- function readDotPath(value, path11) {
28939
- if (path11.length === 0) {
29519
+ function readDotPath(value, path13) {
29520
+ if (path13.length === 0) {
28940
29521
  return value;
28941
29522
  }
28942
29523
  let current = value;
28943
- for (const segment of path11.split(".").filter((entry) => entry.length > 0)) {
29524
+ for (const segment of path13.split(".").filter((entry) => entry.length > 0)) {
28944
29525
  if (current === null || current === void 0) {
28945
29526
  return void 0;
28946
29527
  }
@@ -29413,7 +29994,7 @@ function parseSetCookieHeader(value, requestUrl) {
29413
29994
  }
29414
29995
  const url = new URL(requestUrl);
29415
29996
  let domain = url.hostname;
29416
- let path11 = defaultCookiePath(url.pathname);
29997
+ let path13 = defaultCookiePath(url.pathname);
29417
29998
  let secure = url.protocol === "https:";
29418
29999
  let expiresAt;
29419
30000
  const cookieValue = rawValueParts.join("=").trim();
@@ -29426,7 +30007,7 @@ function parseSetCookieHeader(value, requestUrl) {
29426
30007
  continue;
29427
30008
  }
29428
30009
  if (key === "path" && attributeValue.length > 0) {
29429
- path11 = attributeValue;
30010
+ path13 = attributeValue;
29430
30011
  continue;
29431
30012
  }
29432
30013
  if (key === "secure") {
@@ -29452,7 +30033,7 @@ function parseSetCookieHeader(value, requestUrl) {
29452
30033
  name,
29453
30034
  value: cookieValue,
29454
30035
  domain,
29455
- path: path11,
30036
+ path: path13,
29456
30037
  secure,
29457
30038
  ...expiresAt === void 0 ? {} : { expiresAt }
29458
30039
  }
@@ -30476,6 +31057,100 @@ function screenshotMediaType(format2) {
30476
31057
  }
30477
31058
  }
30478
31059
 
31060
+ // src/sdk/runtime.ts
31061
+ var OpensteerRuntime = class extends OpensteerSessionRuntime {
31062
+ constructor(options = {}) {
31063
+ const publicWorkspace = normalizeWorkspace2(options.workspace);
31064
+ const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path6.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
31065
+ rootDir: path6.resolve(options.rootDir ?? process.cwd()),
31066
+ workspace: publicWorkspace
31067
+ }));
31068
+ const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
31069
+ const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31070
+ assertSupportedEngineOptions({
31071
+ engineName,
31072
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31073
+ ...options.context === void 0 ? {} : { context: options.context }
31074
+ });
31075
+ super(buildSharedRuntimeOptions({
31076
+ name: publicWorkspace ?? "default",
31077
+ rootPath,
31078
+ ...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
31079
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31080
+ ...options.launch === void 0 ? {} : { launch: options.launch },
31081
+ ...options.context === void 0 ? {} : { context: options.context },
31082
+ engineName,
31083
+ ...options.engine === void 0 ? {} : { engine: options.engine },
31084
+ ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
31085
+ ...options.policy === void 0 ? {} : { policy: options.policy },
31086
+ cleanupRootOnClose
31087
+ }));
31088
+ }
31089
+ };
31090
+ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
31091
+ constructor(options) {
31092
+ const rootPath = options.rootPath ?? path6.resolve(options.rootDir ?? process.cwd());
31093
+ const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
31094
+ const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31095
+ assertSupportedEngineOptions({
31096
+ engineName,
31097
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31098
+ ...options.context === void 0 ? {} : { context: options.context }
31099
+ });
31100
+ super(buildSharedRuntimeOptions({
31101
+ name: options.name,
31102
+ rootPath,
31103
+ ...options.browser === void 0 ? {} : { browser: options.browser },
31104
+ ...options.launch === void 0 ? {} : { launch: options.launch },
31105
+ ...options.context === void 0 ? {} : { context: options.context },
31106
+ engineName,
31107
+ ...options.engine === void 0 ? {} : { engine: options.engine },
31108
+ ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
31109
+ ...options.policy === void 0 ? {} : { policy: options.policy },
31110
+ cleanupRootOnClose
31111
+ }));
31112
+ }
31113
+ };
31114
+ function buildSharedRuntimeOptions(input) {
31115
+ const ownership = resolveOwnership(input.browser);
31116
+ const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
31117
+ rootPath: input.rootPath,
31118
+ ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
31119
+ engineName: input.engineName,
31120
+ ...(factoryOptions.browser ?? input.browser) === void 0 ? {} : { browser: factoryOptions.browser ?? input.browser },
31121
+ ...(factoryOptions.launch ?? input.launch) === void 0 ? {} : { launch: factoryOptions.launch ?? input.launch },
31122
+ ...(factoryOptions.context ?? input.context) === void 0 ? {} : { context: factoryOptions.context ?? input.context }
31123
+ }).createEngine());
31124
+ return {
31125
+ name: input.name,
31126
+ ...input.workspaceName === void 0 ? {} : { workspaceName: input.workspaceName },
31127
+ rootPath: input.rootPath,
31128
+ ...input.engine === void 0 ? {} : { engine: input.engine },
31129
+ ...input.engine === void 0 ? { engineFactory } : {},
31130
+ ...input.policy === void 0 ? {} : { policy: input.policy },
31131
+ cleanupRootOnClose: input.cleanupRootOnClose,
31132
+ sessionInfo: {
31133
+ provider: {
31134
+ kind: "local",
31135
+ ownership,
31136
+ engine: input.engineName
31137
+ },
31138
+ ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
31139
+ reconnectable: !input.cleanupRootOnClose
31140
+ }
31141
+ };
31142
+ }
31143
+ function normalizeWorkspace2(workspace) {
31144
+ if (workspace === void 0) {
31145
+ return void 0;
31146
+ }
31147
+ const trimmed = workspace.trim();
31148
+ return trimmed.length === 0 ? void 0 : trimmed;
31149
+ }
31150
+ function resolveOwnership(browser) {
31151
+ return typeof browser === "object" && browser.mode === "attach" ? "attached" : "owned";
31152
+ }
31153
+
30479
31154
  // src/mode/config.ts
30480
31155
  var OPENSTEER_EXECUTION_MODES = ["local", "cloud"];
30481
31156
  function assertExecutionModeSupportsEngine(mode, engine) {
@@ -30911,14 +31586,14 @@ function toPortableBrowserProfileCookieRecord(cookie) {
30911
31586
  if (!name || !domain) {
30912
31587
  return null;
30913
31588
  }
30914
- const path11 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
31589
+ const path13 = typeof cookie.path === "string" && cookie.path.trim().length > 0 ? cookie.path : "/";
30915
31590
  const expiresAt = typeof cookie.expires === "number" && Number.isFinite(cookie.expires) && cookie.expires > 0 ? Math.floor(cookie.expires * 1e3) : null;
30916
31591
  const sameSite = normalizeSameSite(cookie.sameSite);
30917
31592
  return {
30918
31593
  name,
30919
31594
  value: cookie.value,
30920
31595
  domain,
30921
- path: path11,
31596
+ path: path13,
30922
31597
  secure: cookie.secure,
30923
31598
  httpOnly: cookie.httpOnly,
30924
31599
  ...sameSite === void 0 ? {} : { sameSite },
@@ -31095,6 +31770,18 @@ var OpensteerCloudClient = class {
31095
31770
  });
31096
31771
  return await response.json();
31097
31772
  }
31773
+ async issueAccess(sessionId, capabilities) {
31774
+ const response = await this.request(
31775
+ `/v1/sessions/${encodeURIComponent(sessionId)}/access`,
31776
+ {
31777
+ method: "POST",
31778
+ body: {
31779
+ capabilities
31780
+ }
31781
+ }
31782
+ );
31783
+ return await response.json();
31784
+ }
31098
31785
  async closeSession(sessionId) {
31099
31786
  const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
31100
31787
  method: "DELETE"
@@ -31300,58 +31987,367 @@ function isFetchFailure(error) {
31300
31987
  }
31301
31988
  return error.name === "TypeError" || /fetch failed/i.test(error.message);
31302
31989
  }
31303
-
31304
- // src/cloud/session-proxy.ts
31305
- var CLOUD_SESSION_LAYOUT = "opensteer-cloud-session";
31306
- var CLOUD_SESSION_VERSION = 1;
31307
- var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
31308
- var SUPPORTED_CLOUD_OPERATIONS = /* @__PURE__ */ new Set([
31309
- "session.open",
31310
- "page.goto",
31311
- "page.snapshot",
31312
- "dom.click",
31313
- "dom.hover",
31314
- "dom.input",
31315
- "dom.scroll",
31316
- "dom.extract",
31317
- "network.query",
31318
- "network.save",
31319
- "network.clear",
31320
- "request.raw",
31321
- "request-plan.infer",
31322
- "request-plan.write",
31323
- "request-plan.get",
31324
- "request-plan.list",
31325
- "request.execute",
31326
- "computer.execute",
31327
- "session.close"
31328
- ]);
31329
- function resolveCloudSessionRecordPath(rootPath) {
31330
- return path6.join(rootPath, "live", "cloud-session.json");
31990
+ var OpensteerCloudAutomationError = class extends Error {
31991
+ opensteerError;
31992
+ constructor(error) {
31993
+ super(error.message);
31994
+ this.name = "OpensteerCloudAutomationError";
31995
+ this.opensteerError = error;
31996
+ }
31997
+ };
31998
+ var OpensteerCloudAutomationClient = class {
31999
+ constructor(cloud, sessionId) {
32000
+ this.cloud = cloud;
32001
+ this.sessionId = sessionId;
32002
+ }
32003
+ socket;
32004
+ connectPromise;
32005
+ pending = /* @__PURE__ */ new Map();
32006
+ routes = /* @__PURE__ */ new Map();
32007
+ grant;
32008
+ async invoke(operation, input) {
32009
+ await this.ensureConnected();
32010
+ const requestId = `automation:${randomUUID()}`;
32011
+ const message = {
32012
+ protocol: OPENSTEER_PROTOCOL_NAME,
32013
+ version: OPENSTEER_PROTOCOL_VERSION,
32014
+ kind: "invoke",
32015
+ requestId,
32016
+ operation,
32017
+ sentAt: Date.now(),
32018
+ ...input === void 0 ? {} : { input }
32019
+ };
32020
+ return new Promise((resolve5, reject) => {
32021
+ this.pending.set(requestId, {
32022
+ resolve: (value) => resolve5(value),
32023
+ reject
32024
+ });
32025
+ try {
32026
+ this.requireSocket().send(JSON.stringify(message));
32027
+ } catch (error) {
32028
+ this.pending.delete(requestId);
32029
+ reject(error);
32030
+ }
32031
+ });
32032
+ }
32033
+ async getSessionInfo() {
32034
+ const result = await this.invoke("session.info", {});
32035
+ const sessionInfo = result;
32036
+ assertCompatibleRuntimeCoreVersion(sessionInfo);
32037
+ return sessionInfo;
32038
+ }
32039
+ async route(input) {
32040
+ const routeId = `route:${randomUUID()}`;
32041
+ const registration = await this.invoke("route.register", {
32042
+ routeId,
32043
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
32044
+ urlPattern: input.urlPattern,
32045
+ ...input.resourceTypes === void 0 ? {} : { resourceTypes: input.resourceTypes },
32046
+ ...input.times === void 0 ? {} : { times: input.times },
32047
+ includeOriginal: true
32048
+ });
32049
+ this.routes.set(routeId, {
32050
+ kind: "route",
32051
+ routeId,
32052
+ input
32053
+ });
32054
+ return registration;
32055
+ }
32056
+ async interceptScript(input) {
32057
+ const routeId = `route:${randomUUID()}`;
32058
+ const registration = await this.invoke("route.register", {
32059
+ routeId,
32060
+ ...input.pageRef === void 0 ? {} : { pageRef: input.pageRef },
32061
+ urlPattern: input.urlPattern,
32062
+ resourceTypes: ["script"],
32063
+ ...input.times === void 0 ? {} : { times: input.times },
32064
+ includeOriginal: true
32065
+ });
32066
+ this.routes.set(routeId, {
32067
+ kind: "intercept-script",
32068
+ routeId,
32069
+ input
32070
+ });
32071
+ return registration;
32072
+ }
32073
+ async close() {
32074
+ this.connectPromise = void 0;
32075
+ this.grant = void 0;
32076
+ if (!this.socket) {
32077
+ return;
32078
+ }
32079
+ const socket = this.socket;
32080
+ this.socket = void 0;
32081
+ for (const [requestId, pending] of this.pending) {
32082
+ pending.reject(new Error(`automation connection closed before ${requestId} completed`));
32083
+ }
32084
+ this.pending.clear();
32085
+ await new Promise((resolve5) => {
32086
+ socket.once("close", () => resolve5());
32087
+ socket.close();
32088
+ }).catch(() => void 0);
32089
+ }
32090
+ async ensureConnected() {
32091
+ if (this.socket?.readyState === WebSocket2.OPEN) {
32092
+ return;
32093
+ }
32094
+ if (this.connectPromise) {
32095
+ await this.connectPromise;
32096
+ return;
32097
+ }
32098
+ this.connectPromise = this.connect();
32099
+ try {
32100
+ await this.connectPromise;
32101
+ } finally {
32102
+ this.connectPromise = void 0;
32103
+ }
32104
+ }
32105
+ async connect() {
32106
+ const grant = await this.issueGrant("automation");
32107
+ const wsUrl = new URL(grant.wsUrl);
32108
+ wsUrl.searchParams.set("token", grant.token);
32109
+ const socket = new WebSocket2(wsUrl);
32110
+ this.socket = socket;
32111
+ socket.on("message", (data, isBinary) => {
32112
+ if (isBinary) {
32113
+ return;
32114
+ }
32115
+ this.handleMessage(data.toString());
32116
+ });
32117
+ socket.on("close", () => {
32118
+ if (this.socket === socket) {
32119
+ this.socket = void 0;
32120
+ }
32121
+ });
32122
+ socket.on("error", (error) => {
32123
+ for (const pending of this.pending.values()) {
32124
+ pending.reject(error);
32125
+ }
32126
+ this.pending.clear();
32127
+ });
32128
+ await new Promise((resolve5, reject) => {
32129
+ socket.once("open", () => resolve5());
32130
+ socket.once("error", reject);
32131
+ });
32132
+ this.send({
32133
+ protocol: OPENSTEER_PROTOCOL_NAME,
32134
+ version: OPENSTEER_PROTOCOL_VERSION,
32135
+ kind: "hello",
32136
+ sessionId: this.sessionId,
32137
+ grantKind: grant.kind
32138
+ });
32139
+ await this.restoreRoutes();
32140
+ }
32141
+ async restoreRoutes() {
32142
+ const stored = [...this.routes.values()];
32143
+ this.routes.clear();
32144
+ for (const registration of stored) {
32145
+ if (registration.kind === "route") {
32146
+ await this.route(registration.input);
32147
+ } else {
32148
+ await this.interceptScript(
32149
+ registration.input
32150
+ );
32151
+ }
32152
+ }
32153
+ }
32154
+ handleMessage(json) {
32155
+ const message = JSON.parse(json);
32156
+ if (message.protocol !== OPENSTEER_PROTOCOL_NAME) {
32157
+ return;
32158
+ }
32159
+ switch (message.kind) {
32160
+ case "result": {
32161
+ const pending = this.pending.get(message.requestId);
32162
+ if (!pending) {
32163
+ return;
32164
+ }
32165
+ this.pending.delete(message.requestId);
32166
+ pending.resolve(message.data);
32167
+ return;
32168
+ }
32169
+ case "error": {
32170
+ if (!message.requestId) {
32171
+ return;
32172
+ }
32173
+ const pending = this.pending.get(message.requestId);
32174
+ if (!pending) {
32175
+ return;
32176
+ }
32177
+ this.pending.delete(message.requestId);
32178
+ pending.reject(new OpensteerCloudAutomationError(message.error));
32179
+ return;
32180
+ }
32181
+ case "event":
32182
+ void this.handleEvent(message.event, message.data);
32183
+ return;
32184
+ case "pong":
32185
+ return;
32186
+ }
32187
+ }
32188
+ async handleEvent(event, payload) {
32189
+ if (event !== "route.request") {
32190
+ return;
32191
+ }
32192
+ const data = asRecord(payload);
32193
+ const request = asRecord(data.request);
32194
+ const original = asRecord(data.original);
32195
+ const routeId = typeof data.routeId === "string" ? data.routeId : "";
32196
+ const routeRequestId = typeof data.routeRequestId === "string" ? data.routeRequestId : "";
32197
+ if (!routeId || !routeRequestId) {
32198
+ return;
32199
+ }
32200
+ const registration = this.routes.get(routeId);
32201
+ if (!registration) {
32202
+ await this.invoke("route.resolve", {
32203
+ routeRequestId,
32204
+ decision: { kind: "continue" }
32205
+ }).catch(() => void 0);
32206
+ return;
32207
+ }
32208
+ try {
32209
+ const decision = registration.kind === "route" ? await registration.input.handler({
32210
+ request: toRouteRequest(request),
32211
+ fetchOriginal: async () => toFetchedRouteResponse(original)
32212
+ }) : {
32213
+ kind: "fulfill",
32214
+ body: await registration.input.handler({
32215
+ url: typeof request.url === "string" ? request.url : "",
32216
+ content: typeof original.body === "string" ? original.body : "",
32217
+ headers: Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32218
+ status: typeof original.status === "number" ? original.status : 200
32219
+ }),
32220
+ headers: Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32221
+ status: typeof original.status === "number" ? original.status : 200,
32222
+ contentType: findHeaderValue(
32223
+ Array.isArray(original.headers) ? original.headers.filter(isHeaderEntry) : [],
32224
+ "content-type"
32225
+ ) ?? "application/javascript; charset=utf-8"
32226
+ };
32227
+ await this.invoke("route.resolve", {
32228
+ routeRequestId,
32229
+ decision: serializeRouteDecision(decision)
32230
+ }).catch(() => void 0);
32231
+ } catch {
32232
+ await this.invoke("route.resolve", {
32233
+ routeRequestId,
32234
+ decision: { kind: "continue" }
32235
+ }).catch(() => void 0);
32236
+ }
32237
+ }
32238
+ async issueGrant(kind) {
32239
+ if (this.grant && this.grant.kind === kind && this.grant.expiresAt > Date.now() + 1e4) {
32240
+ return this.grant;
32241
+ }
32242
+ const issued = await this.cloud.issueAccess(this.sessionId, [kind]);
32243
+ const grant = issued.grants[kind];
32244
+ if (!grant) {
32245
+ throw new OpensteerCloudAutomationError(
32246
+ createOpensteerError(
32247
+ "permission-denied",
32248
+ `cloud did not issue an ${kind} automation grant`
32249
+ )
32250
+ );
32251
+ }
32252
+ this.grant = grant;
32253
+ return grant;
32254
+ }
32255
+ requireSocket() {
32256
+ if (!this.socket || this.socket.readyState !== WebSocket2.OPEN) {
32257
+ throw new Error("cloud automation socket is not connected");
32258
+ }
32259
+ return this.socket;
32260
+ }
32261
+ send(message) {
32262
+ this.requireSocket().send(JSON.stringify(message));
32263
+ }
32264
+ };
32265
+ function assertCompatibleRuntimeCoreVersion(sessionInfo) {
32266
+ const runtimeCoreVersion = sessionInfo.runtime?.runtimeCoreVersion;
32267
+ if (runtimeCoreVersion === void 0) {
32268
+ return;
32269
+ }
32270
+ const expectedMajor = parseMajorVersion(OPENSTEER_RUNTIME_CORE_VERSION);
32271
+ const actualMajor = parseMajorVersion(runtimeCoreVersion);
32272
+ if (expectedMajor === null || actualMajor === null || expectedMajor === actualMajor) {
32273
+ return;
32274
+ }
32275
+ throw new Error(
32276
+ `cloud runtime-core major version ${runtimeCoreVersion} is incompatible with local SDK runtime-core ${OPENSTEER_RUNTIME_CORE_VERSION}`
32277
+ );
31331
32278
  }
31332
- async function readPersistedCloudSessionRecord(rootPath) {
31333
- const sessionPath = resolveCloudSessionRecordPath(rootPath);
31334
- if (!await pathExists(sessionPath)) {
31335
- return void 0;
32279
+ function parseMajorVersion(version) {
32280
+ const major = Number.parseInt(version.split(".", 1)[0] ?? "", 10);
32281
+ return Number.isFinite(major) ? major : null;
32282
+ }
32283
+ function serializeRouteDecision(decision) {
32284
+ if (decision.kind === "continue") {
32285
+ return { kind: "continue" };
31336
32286
  }
31337
- const parsed = await readJsonFile(sessionPath);
31338
- 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)) {
31339
- return void 0;
32287
+ if (decision.kind === "abort") {
32288
+ return {
32289
+ kind: "abort",
32290
+ ...decision.errorCode === void 0 ? {} : { errorCode: decision.errorCode }
32291
+ };
31340
32292
  }
31341
32293
  return {
31342
- layout: CLOUD_SESSION_LAYOUT,
31343
- version: CLOUD_SESSION_VERSION,
31344
- mode: "cloud",
31345
- ...parsed.workspace === void 0 ? {} : { workspace: parsed.workspace },
31346
- sessionId: parsed.sessionId,
31347
- baseUrl: parsed.baseUrl,
31348
- startedAt: parsed.startedAt,
31349
- updatedAt: parsed.updatedAt
32294
+ kind: "fulfill",
32295
+ ...decision.status === void 0 ? {} : { status: decision.status },
32296
+ ...decision.headers === void 0 ? {} : { headers: decision.headers },
32297
+ ...decision.body === void 0 ? {} : typeof decision.body === "string" ? { body: decision.body } : { bodyBase64: Buffer.from(decision.body).toString("base64") },
32298
+ ...decision.contentType === void 0 ? {} : { contentType: decision.contentType }
31350
32299
  };
31351
32300
  }
31352
- async function hasPersistedCloudSession(rootPath) {
31353
- return await readPersistedCloudSessionRecord(rootPath) !== void 0;
32301
+ function toRouteRequest(record) {
32302
+ const pageRef = typeof record.pageRef === "string" ? record.pageRef : void 0;
32303
+ return {
32304
+ url: typeof record.url === "string" ? record.url : "",
32305
+ method: typeof record.method === "string" ? record.method : "GET",
32306
+ headers: Array.isArray(record.headers) ? record.headers.filter(isHeaderEntry) : [],
32307
+ resourceType: typeof record.resourceType === "string" ? record.resourceType : "other",
32308
+ ...pageRef === void 0 ? {} : { pageRef },
32309
+ ...typeof record.postData === "string" ? {
32310
+ postData: {
32311
+ bytes: Uint8Array.from(Buffer.from(record.postData)),
32312
+ encoding: "identity",
32313
+ truncated: false,
32314
+ capturedByteLength: Buffer.byteLength(record.postData)
32315
+ }
32316
+ } : {}
32317
+ };
31354
32318
  }
32319
+ function toFetchedRouteResponse(record) {
32320
+ return {
32321
+ url: typeof record.url === "string" ? record.url : "",
32322
+ status: typeof record.status === "number" ? record.status : 200,
32323
+ statusText: typeof record.statusText === "string" ? record.statusText : "OK",
32324
+ headers: Array.isArray(record.headers) ? record.headers.filter(isHeaderEntry) : [],
32325
+ ...typeof record.body === "string" ? {
32326
+ body: {
32327
+ bytes: Uint8Array.from(Buffer.from(record.body)),
32328
+ encoding: "identity",
32329
+ truncated: false,
32330
+ capturedByteLength: Buffer.byteLength(record.body)
32331
+ }
32332
+ } : {},
32333
+ redirected: Boolean(record.redirected)
32334
+ };
32335
+ }
32336
+ function findHeaderValue(headers, name) {
32337
+ return headers.find((header) => header.name.toLowerCase() === name)?.value;
32338
+ }
32339
+ function isHeaderEntry(value) {
32340
+ return value !== null && typeof value === "object" && typeof value.name === "string" && typeof value.value === "string";
32341
+ }
32342
+ function asRecord(value) {
32343
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
32344
+ return {};
32345
+ }
32346
+ return value;
32347
+ }
32348
+
32349
+ // src/cloud/session-proxy.ts
32350
+ var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
31355
32351
  var CloudSessionProxy = class {
31356
32352
  rootPath;
31357
32353
  workspace;
@@ -31360,6 +32356,7 @@ var CloudSessionProxy = class {
31360
32356
  sessionId;
31361
32357
  sessionBaseUrl;
31362
32358
  client;
32359
+ automation;
31363
32360
  workspaceStore;
31364
32361
  constructor(cloud, options = {}) {
31365
32362
  this.cloud = cloud;
@@ -31380,27 +32377,73 @@ var CloudSessionProxy = class {
31380
32377
  ...input.url === void 0 ? {} : { url: input.url }
31381
32378
  });
31382
32379
  }
32380
+ async info() {
32381
+ const persisted = this.client !== void 0 || this.sessionId !== void 0 ? void 0 : await this.loadPersistedSession();
32382
+ if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
32383
+ this.bindClient(persisted);
32384
+ }
32385
+ if (this.automation) {
32386
+ try {
32387
+ const sessionInfo = await this.automation.getSessionInfo();
32388
+ return {
32389
+ ...sessionInfo,
32390
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace }
32391
+ };
32392
+ } catch {
32393
+ }
32394
+ }
32395
+ return {
32396
+ provider: {
32397
+ kind: "cloud",
32398
+ ownership: "managed",
32399
+ engine: "playwright",
32400
+ baseUrl: this.cloud.getConfig().baseUrl
32401
+ },
32402
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace },
32403
+ ...this.sessionId === void 0 ? persisted?.sessionId === void 0 ? {} : { sessionId: persisted.sessionId } : { sessionId: this.sessionId },
32404
+ reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
32405
+ capabilities: {
32406
+ semanticOperations: opensteerSemanticOperationNames,
32407
+ sessionGrants: ["automation", "view", "cdp"],
32408
+ instrumentation: {
32409
+ route: true,
32410
+ interceptScript: true,
32411
+ networkStream: true
32412
+ }
32413
+ },
32414
+ runtime: {
32415
+ protocolVersion: OPENSTEER_PROTOCOL_VERSION,
32416
+ runtimeCoreVersion: OPENSTEER_RUNTIME_CORE_VERSION
32417
+ }
32418
+ };
32419
+ }
31383
32420
  async listPages(input = {}) {
31384
- throw unsupportedCloudOperation("page.list");
32421
+ await this.ensureSession();
32422
+ return this.requireClient().invoke("page.list", input);
31385
32423
  }
31386
32424
  async newPage(input = {}) {
31387
- throw unsupportedCloudOperation("page.new");
32425
+ await this.ensureSession();
32426
+ return this.requireAutomation().invoke("page.new", input);
31388
32427
  }
31389
32428
  async activatePage(input) {
31390
- throw unsupportedCloudOperation("page.activate");
32429
+ await this.ensureSession();
32430
+ return this.requireClient().invoke("page.activate", input);
31391
32431
  }
31392
32432
  async closePage(input = {}) {
31393
- throw unsupportedCloudOperation("page.close");
32433
+ await this.ensureSession();
32434
+ return this.requireClient().invoke("page.close", input);
31394
32435
  }
31395
32436
  async goto(input) {
31396
32437
  await this.ensureSession();
31397
32438
  return this.requireClient().invoke("page.goto", input);
31398
32439
  }
31399
32440
  async evaluate(input) {
31400
- throw unsupportedCloudOperation("page.evaluate");
32441
+ await this.ensureSession();
32442
+ return this.requireAutomation().invoke("page.evaluate", input);
31401
32443
  }
31402
32444
  async addInitScript(input) {
31403
- throw unsupportedCloudOperation("page.add-init-script");
32445
+ await this.ensureSession();
32446
+ return this.requireClient().invoke("page.add-init-script", input);
31404
32447
  }
31405
32448
  async snapshot(input = {}) {
31406
32449
  await this.ensureSession();
@@ -31435,80 +32478,112 @@ var CloudSessionProxy = class {
31435
32478
  return this.requireClient().invoke("network.save", input);
31436
32479
  }
31437
32480
  async minimizeNetwork(input) {
31438
- throw unsupportedCloudOperation("network.minimize");
32481
+ await this.ensureSession();
32482
+ return this.requireClient().invoke("network.minimize", input);
31439
32483
  }
31440
32484
  async diffNetwork(input) {
31441
- throw unsupportedCloudOperation("network.diff");
32485
+ await this.ensureSession();
32486
+ return this.requireClient().invoke("network.diff", input);
31442
32487
  }
31443
32488
  async probeNetwork(input) {
31444
- throw unsupportedCloudOperation("network.probe");
32489
+ await this.ensureSession();
32490
+ return this.requireClient().invoke("network.probe", input);
31445
32491
  }
31446
32492
  async discoverReverse(input) {
31447
- throw unsupportedCloudOperation("reverse.discover");
32493
+ await this.ensureSession();
32494
+ return this.requireClient().invoke("reverse.discover", input);
31448
32495
  }
31449
32496
  async queryReverse(input) {
31450
- throw unsupportedCloudOperation("reverse.query");
32497
+ await this.ensureSession();
32498
+ return this.requireClient().invoke("reverse.query", input);
31451
32499
  }
31452
32500
  async createReversePackage(input) {
31453
- throw unsupportedCloudOperation("reverse.package.create");
32501
+ await this.ensureSession();
32502
+ return this.requireClient().invoke("reverse.package.create", input);
31454
32503
  }
31455
32504
  async runReversePackage(input) {
31456
- throw unsupportedCloudOperation("reverse.package.run");
32505
+ await this.ensureSession();
32506
+ return this.requireClient().invoke("reverse.package.run", input);
31457
32507
  }
31458
32508
  async exportReverse(input) {
31459
- throw unsupportedCloudOperation("reverse.export");
32509
+ await this.ensureSession();
32510
+ return this.requireClient().invoke("reverse.export", input);
31460
32511
  }
31461
32512
  async getReverseReport(input) {
31462
- throw unsupportedCloudOperation("reverse.report");
32513
+ await this.ensureSession();
32514
+ return this.requireClient().invoke("reverse.report", input);
31463
32515
  }
31464
32516
  async getReversePackage(input) {
31465
- throw unsupportedCloudOperation("reverse.package.get");
32517
+ await this.ensureSession();
32518
+ return this.requireClient().invoke("reverse.package.get", input);
31466
32519
  }
31467
32520
  async listReversePackages(input = {}) {
31468
- throw unsupportedCloudOperation("reverse.package.list");
32521
+ await this.ensureSession();
32522
+ return this.requireClient().invoke("reverse.package.list", input);
31469
32523
  }
31470
32524
  async patchReversePackage(input) {
31471
- throw unsupportedCloudOperation("reverse.package.patch");
32525
+ await this.ensureSession();
32526
+ return this.requireClient().invoke("reverse.package.patch", input);
31472
32527
  }
31473
32528
  async captureInteraction(input) {
31474
- throw unsupportedCloudOperation("interaction.capture");
32529
+ await this.ensureSession();
32530
+ return this.requireClient().invoke("interaction.capture", input);
31475
32531
  }
31476
32532
  async getInteraction(input) {
31477
- throw unsupportedCloudOperation("interaction.get");
32533
+ await this.ensureSession();
32534
+ return this.requireClient().invoke("interaction.get", input);
31478
32535
  }
31479
32536
  async diffInteraction(input) {
31480
- throw unsupportedCloudOperation("interaction.diff");
32537
+ await this.ensureSession();
32538
+ return this.requireClient().invoke("interaction.diff", input);
31481
32539
  }
31482
32540
  async replayInteraction(input) {
31483
- throw unsupportedCloudOperation("interaction.replay");
32541
+ await this.ensureSession();
32542
+ return this.requireClient().invoke("interaction.replay", input);
31484
32543
  }
31485
32544
  async clearNetwork(input = {}) {
31486
32545
  await this.ensureSession();
31487
32546
  return this.requireClient().invoke("network.clear", input);
31488
32547
  }
31489
32548
  async captureScripts(input = {}) {
31490
- throw unsupportedCloudOperation("scripts.capture");
32549
+ await this.ensureSession();
32550
+ return this.requireClient().invoke("scripts.capture", input);
31491
32551
  }
31492
32552
  async readArtifact(input) {
31493
- throw unsupportedCloudOperation("artifact.read");
32553
+ await this.ensureSession();
32554
+ return this.requireClient().invoke("artifact.read", input);
31494
32555
  }
31495
32556
  async beautifyScript(input) {
31496
- throw unsupportedCloudOperation("scripts.beautify");
32557
+ await this.ensureSession();
32558
+ return this.requireClient().invoke("scripts.beautify", input);
31497
32559
  }
31498
32560
  async deobfuscateScript(input) {
31499
- throw unsupportedCloudOperation("scripts.deobfuscate");
32561
+ await this.ensureSession();
32562
+ return this.requireClient().invoke("scripts.deobfuscate", input);
31500
32563
  }
31501
32564
  async sandboxScript(input) {
31502
- throw unsupportedCloudOperation("scripts.sandbox");
32565
+ await this.ensureSession();
32566
+ return this.requireClient().invoke("scripts.sandbox", input);
31503
32567
  }
31504
32568
  async solveCaptcha(input) {
31505
- throw unsupportedCloudOperation("captcha.solve");
32569
+ await this.ensureSession();
32570
+ return this.requireClient().invoke("captcha.solve", input);
31506
32571
  }
31507
32572
  async getCookies(input = {}) {
31508
- throw unsupportedCloudOperation("inspect.cookies");
32573
+ await this.ensureSession();
32574
+ return this.requireAutomation().invoke("inspect.cookies", input);
32575
+ }
32576
+ async route(input) {
32577
+ await this.ensureSession();
32578
+ return this.requireAutomation().route(input);
32579
+ }
32580
+ async interceptScript(input) {
32581
+ await this.ensureSession();
32582
+ return this.requireAutomation().interceptScript(input);
31509
32583
  }
31510
32584
  async getStorageSnapshot(input = {}) {
31511
- throw unsupportedCloudOperation("inspect.storage");
32585
+ await this.ensureSession();
32586
+ return this.requireClient().invoke("inspect.storage", input);
31512
32587
  }
31513
32588
  async rawRequest(input) {
31514
32589
  await this.ensureSession();
@@ -31531,28 +32606,36 @@ var CloudSessionProxy = class {
31531
32606
  return this.requireClient().invoke("request-plan.list", input);
31532
32607
  }
31533
32608
  async writeAuthRecipe(input) {
31534
- throw unsupportedCloudOperation("auth-recipe.write");
32609
+ await this.ensureSession();
32610
+ return this.requireClient().invoke("auth-recipe.write", input);
31535
32611
  }
31536
32612
  async writeRecipe(input) {
31537
- throw unsupportedCloudOperation("recipe.write");
32613
+ await this.ensureSession();
32614
+ return this.requireClient().invoke("recipe.write", input);
31538
32615
  }
31539
32616
  async getAuthRecipe(input) {
31540
- throw unsupportedCloudOperation("auth-recipe.get");
32617
+ await this.ensureSession();
32618
+ return this.requireClient().invoke("auth-recipe.get", input);
31541
32619
  }
31542
32620
  async getRecipe(input) {
31543
- throw unsupportedCloudOperation("recipe.get");
32621
+ await this.ensureSession();
32622
+ return this.requireClient().invoke("recipe.get", input);
31544
32623
  }
31545
32624
  async listAuthRecipes(input = {}) {
31546
- throw unsupportedCloudOperation("auth-recipe.list");
32625
+ await this.ensureSession();
32626
+ return this.requireClient().invoke("auth-recipe.list", input);
31547
32627
  }
31548
32628
  async listRecipes(input = {}) {
31549
- throw unsupportedCloudOperation("recipe.list");
32629
+ await this.ensureSession();
32630
+ return this.requireClient().invoke("recipe.list", input);
31550
32631
  }
31551
32632
  async runAuthRecipe(input) {
31552
- throw unsupportedCloudOperation("auth-recipe.run");
32633
+ await this.ensureSession();
32634
+ return this.requireClient().invoke("auth-recipe.run", input);
31553
32635
  }
31554
32636
  async runRecipe(input) {
31555
- throw unsupportedCloudOperation("recipe.run");
32637
+ await this.ensureSession();
32638
+ return this.requireClient().invoke("recipe.run", input);
31556
32639
  }
31557
32640
  async request(input) {
31558
32641
  await this.ensureSession();
@@ -31564,8 +32647,9 @@ var CloudSessionProxy = class {
31564
32647
  }
31565
32648
  async close() {
31566
32649
  const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 || this.sessionBaseUrl === void 0 ? void 0 : {
31567
- layout: CLOUD_SESSION_LAYOUT,
31568
- version: CLOUD_SESSION_VERSION,
32650
+ layout: "opensteer-session",
32651
+ version: 1,
32652
+ provider: "cloud",
31569
32653
  mode: "cloud",
31570
32654
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
31571
32655
  sessionId: this.sessionId,
@@ -31583,7 +32667,9 @@ var CloudSessionProxy = class {
31583
32667
  });
31584
32668
  }
31585
32669
  } finally {
32670
+ await this.automation?.close().catch(() => void 0);
31586
32671
  await this.clearPersistedSession();
32672
+ this.automation = void 0;
31587
32673
  this.client = void 0;
31588
32674
  this.sessionId = void 0;
31589
32675
  this.sessionBaseUrl = void 0;
@@ -31599,6 +32685,8 @@ var CloudSessionProxy = class {
31599
32685
  return;
31600
32686
  }
31601
32687
  this.client = void 0;
32688
+ await this.automation?.close().catch(() => void 0);
32689
+ this.automation = void 0;
31602
32690
  this.sessionId = void 0;
31603
32691
  this.sessionBaseUrl = void 0;
31604
32692
  }
@@ -31619,8 +32707,9 @@ var CloudSessionProxy = class {
31619
32707
  ...resolveCloudBrowserProfile(this.cloud, input) === void 0 ? {} : { browserProfile: resolveCloudBrowserProfile(this.cloud, input) }
31620
32708
  });
31621
32709
  const record = {
31622
- layout: CLOUD_SESSION_LAYOUT,
31623
- version: CLOUD_SESSION_VERSION,
32710
+ layout: "opensteer-session",
32711
+ version: 1,
32712
+ provider: "cloud",
31624
32713
  mode: "cloud",
31625
32714
  ...this.workspace === void 0 ? {} : { workspace: this.workspace },
31626
32715
  sessionId: session.sessionId,
@@ -31638,6 +32727,7 @@ var CloudSessionProxy = class {
31638
32727
  baseUrl: record.baseUrl,
31639
32728
  getAuthorizationHeader: async () => this.cloud.buildAuthorizationHeader()
31640
32729
  });
32730
+ this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
31641
32731
  }
31642
32732
  async ensureWorkspaceStore() {
31643
32733
  if (this.workspaceStore !== void 0) {
@@ -31656,10 +32746,10 @@ var CloudSessionProxy = class {
31656
32746
  }
31657
32747
  async writePersistedSession(record) {
31658
32748
  const workspace = await this.ensureWorkspaceStore();
31659
- await writeJsonFileAtomic(resolveCloudSessionRecordPath(workspace.rootPath), record);
32749
+ await writePersistedSessionRecord(workspace.rootPath, record);
31660
32750
  }
31661
32751
  async clearPersistedSession() {
31662
- await rm(resolveCloudSessionRecordPath(this.rootPath), { force: true }).catch(() => void 0);
32752
+ await clearPersistedSessionRecord(this.rootPath).catch(() => void 0);
31663
32753
  }
31664
32754
  async isReusableCloudSession(sessionId) {
31665
32755
  try {
@@ -31678,6 +32768,12 @@ var CloudSessionProxy = class {
31678
32768
  }
31679
32769
  return this.client;
31680
32770
  }
32771
+ requireAutomation() {
32772
+ if (!this.automation) {
32773
+ throw new Error("Cloud automation session has not been initialized.");
32774
+ }
32775
+ return this.automation;
32776
+ }
31681
32777
  };
31682
32778
  function resolveCloudBrowserProfile(cloud, input) {
31683
32779
  return input.browserProfile ?? cloud.getConfig().browserProfile;
@@ -31693,22 +32789,24 @@ function assertSupportedCloudBrowserMode(browser) {
31693
32789
  function isMissingCloudSessionError(error) {
31694
32790
  return error instanceof Error && /\b404\b/.test(error.message);
31695
32791
  }
31696
- function unsupportedCloudOperation(operation) {
31697
- return new OpensteerProtocolError(
31698
- "unsupported-operation",
31699
- `Cloud mode does not currently support ${operation}.`,
31700
- {
31701
- details: {
31702
- mode: "cloud",
31703
- operation,
31704
- supportedOperations: [...SUPPORTED_CLOUD_OPERATIONS]
31705
- }
31706
- }
31707
- );
31708
- }
31709
32792
 
31710
32793
  // src/sdk/runtime-resolution.ts
31711
32794
  function resolveOpensteerRuntimeConfig(input = {}) {
32795
+ if (input.provider?.kind === "cloud") {
32796
+ return {
32797
+ mode: "cloud",
32798
+ cloud: resolveCloudConfig({
32799
+ enabled: true,
32800
+ ...input.provider,
32801
+ mode: "cloud"
32802
+ })
32803
+ };
32804
+ }
32805
+ if (input.provider?.kind === "local") {
32806
+ return {
32807
+ mode: "local"
32808
+ };
32809
+ }
31712
32810
  const mode = resolveOpensteerExecutionMode({
31713
32811
  ...input.mode === void 0 ? {} : { explicit: input.mode },
31714
32812
  cloud: input.cloud !== void 0 && input.cloud !== false,
@@ -31731,6 +32829,7 @@ function createOpensteerSemanticRuntime(input = {}) {
31731
32829
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
31732
32830
  const config = resolveOpensteerRuntimeConfig({
31733
32831
  ...input.cloud === void 0 ? {} : { cloud: input.cloud },
32832
+ ...input.provider === void 0 ? {} : { provider: input.provider },
31734
32833
  ...input.mode === void 0 ? {} : { mode: input.mode },
31735
32834
  ...process.env.OPENSTEER_MODE === void 0 ? {} : { environmentMode: process.env.OPENSTEER_MODE }
31736
32835
  });
@@ -31749,6 +32848,6 @@ function createOpensteerSemanticRuntime(input = {}) {
31749
32848
  });
31750
32849
  }
31751
32850
 
31752
- export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, STABLE_PRIMARY_ATTR_KEYS, assertExecutionModeSupportsEngine, buildArrayFieldPathCandidates, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomRuntime, createFilesystemOpensteerWorkspace, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hasPersistedCloudSession, inspectCdpEndpoint, isCurrentUrlField, isValidCssAttributeKey, listLocalChromeProfiles, normalizeExtractedValue, normalizeOpensteerEngineName, normalizeOpensteerExecutionMode, normalizeWorkspaceId, readPersistedCloudSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveOpensteerEngineName, resolveOpensteerExecutionMode, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
31753
- //# sourceMappingURL=chunk-2UMBR4XO.js.map
31754
- //# sourceMappingURL=chunk-2UMBR4XO.js.map
32851
+ export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertExecutionModeSupportsEngine, buildArrayFieldPathCandidates, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomRuntime, createFilesystemOpensteerWorkspace, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hasPersistedCloudSession, inspectCdpEndpoint, isCurrentUrlField, isValidCssAttributeKey, listLocalChromeProfiles, normalizeExtractedValue, normalizeOpensteerEngineName, normalizeOpensteerExecutionMode, normalizeWorkspaceId, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerExecutionMode, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
32852
+ //# sourceMappingURL=chunk-JK4NMMM2.js.map
32853
+ //# sourceMappingURL=chunk-JK4NMMM2.js.map