opensteer 0.9.4 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{chunk-ZRF7WMS3.js → chunk-3I3A5OLB.js} +3 -3
  2. package/dist/{chunk-ZRF7WMS3.js.map → chunk-3I3A5OLB.js.map} +1 -1
  3. package/dist/{chunk-GSCQQKZZ.js → chunk-3XBQRZZC.js} +221 -6
  4. package/dist/chunk-3XBQRZZC.js.map +1 -0
  5. package/dist/{chunk-GEUHKPC2.js → chunk-BVRIPCWA.js} +878 -572
  6. package/dist/chunk-BVRIPCWA.js.map +1 -0
  7. package/dist/chunk-L4NF74KI.js +458 -0
  8. package/dist/chunk-L4NF74KI.js.map +1 -0
  9. package/dist/cli/bin.cjs +1313 -494
  10. package/dist/cli/bin.cjs.map +1 -1
  11. package/dist/cli/bin.js +235 -108
  12. package/dist/cli/bin.js.map +1 -1
  13. package/dist/index.cjs +1152 -647
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +37 -460
  16. package/dist/index.d.ts +37 -460
  17. package/dist/index.js +3 -4
  18. package/dist/local-view/serve-entry.cjs +5354 -4
  19. package/dist/local-view/serve-entry.cjs.map +1 -1
  20. package/dist/local-view/serve-entry.js +1 -1
  21. package/dist/opensteer-UGA6YBRN.js +6 -0
  22. package/dist/{opensteer-PJI7VUIT.js.map → opensteer-UGA6YBRN.js.map} +1 -1
  23. package/dist/{session-control-M3JD7ZKA.js → session-control-U3L5H2ZI.js} +3 -3
  24. package/dist/{session-control-M3JD7ZKA.js.map → session-control-U3L5H2ZI.js.map} +1 -1
  25. package/package.json +7 -7
  26. package/skills/opensteer/SKILL.md +134 -94
  27. package/dist/chunk-GEUHKPC2.js.map +0 -1
  28. package/dist/chunk-GSCQQKZZ.js.map +0 -1
  29. package/dist/chunk-HQCMXRBE.js +0 -335
  30. package/dist/chunk-HQCMXRBE.js.map +0 -1
  31. package/dist/chunk-KCINASQC.js +0 -3
  32. package/dist/chunk-KCINASQC.js.map +0 -1
  33. package/dist/opensteer-PJI7VUIT.js +0 -6
package/dist/index.cjs CHANGED
@@ -1037,9 +1037,6 @@ function isBrowserCoreError(value) {
1037
1037
  return value instanceof BrowserCoreError;
1038
1038
  }
1039
1039
 
1040
- // ../browser-core/src/cdp-visual-stability.ts
1041
- var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
1042
-
1043
1040
  // ../browser-core/src/post-load-tracker.ts
1044
1041
  var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
1045
1042
 
@@ -4445,10 +4442,10 @@ var opensteerDomScrollInputSchema = objectSchema(
4445
4442
  required: ["target", "direction", "amount"]
4446
4443
  }
4447
4444
  );
4448
- var opensteerExtractSchemaSchema = objectSchema(
4445
+ var opensteerExtractTemplateSchema = objectSchema(
4449
4446
  {},
4450
4447
  {
4451
- title: "OpensteerExtractSchema",
4448
+ title: "OpensteerExtractTemplate",
4452
4449
  additionalProperties: true
4453
4450
  }
4454
4451
  );
@@ -4456,13 +4453,13 @@ var opensteerDomExtractInputSchema = defineSchema({
4456
4453
  ...objectSchema(
4457
4454
  {
4458
4455
  persist: stringSchema(),
4459
- schema: opensteerExtractSchemaSchema
4456
+ template: opensteerExtractTemplateSchema
4460
4457
  },
4461
4458
  {
4462
4459
  title: "OpensteerDomExtractInput"
4463
4460
  }
4464
4461
  ),
4465
- anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["schema"] })]
4462
+ anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["template"] })]
4466
4463
  });
4467
4464
  var jsonValueSchema2 = recordSchema({}, { title: "JsonValueRecord" });
4468
4465
  var opensteerDomExtractOutputSchema = objectSchema(
@@ -6845,21 +6842,6 @@ var DEFAULT_SETTLE_DELAYS = {
6845
6842
  "dom-action": 100,
6846
6843
  snapshot: 0
6847
6844
  };
6848
- var defaultSnapshotSettleObserver = {
6849
- async settle(input) {
6850
- if (input.trigger !== "snapshot") {
6851
- return false;
6852
- }
6853
- await input.engine.waitForVisualStability({
6854
- pageRef: input.pageRef,
6855
- ...input.remainingMs === void 0 ? {} : { timeoutMs: input.remainingMs },
6856
- settleMs: DEFAULT_VISUAL_STABILITY_SETTLE_MS,
6857
- scope: "visible-frames"
6858
- });
6859
- return true;
6860
- }
6861
- };
6862
- Object.freeze(defaultSnapshotSettleObserver);
6863
6845
  var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
6864
6846
  "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
6865
6847
  "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
@@ -6912,6 +6894,7 @@ var defaultNavigationSettleObserver = {
6912
6894
  return false;
6913
6895
  }
6914
6896
  try {
6897
+ const startedAt = Date.now();
6915
6898
  await input.engine.waitForPostLoadQuiet({
6916
6899
  pageRef: input.pageRef,
6917
6900
  timeoutMs: effectiveTimeout,
@@ -6919,9 +6902,13 @@ var defaultNavigationSettleObserver = {
6919
6902
  captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
6920
6903
  signal: input.signal
6921
6904
  });
6905
+ const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
6906
+ if (visualTimeout <= 0) {
6907
+ return true;
6908
+ }
6922
6909
  await input.engine.waitForVisualStability({
6923
6910
  pageRef: input.pageRef,
6924
- timeoutMs: effectiveTimeout,
6911
+ timeoutMs: visualTimeout,
6925
6912
  settleMs: profile.settleMs,
6926
6913
  scope: profile.scope
6927
6914
  });
@@ -6933,7 +6920,6 @@ var defaultNavigationSettleObserver = {
6933
6920
  };
6934
6921
  Object.freeze(defaultNavigationSettleObserver);
6935
6922
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
6936
- defaultSnapshotSettleObserver,
6937
6923
  defaultDomActionSettleObserver,
6938
6924
  defaultNavigationSettleObserver
6939
6925
  ]);
@@ -7386,6 +7372,9 @@ function buildClauseSelector(node, clause) {
7386
7372
  if (!clause || typeof clause !== "object") {
7387
7373
  return "";
7388
7374
  }
7375
+ if (clause.kind === "text") {
7376
+ return "";
7377
+ }
7389
7378
  if (clause.kind === "position") {
7390
7379
  if (clause.axis === "nthOfType") {
7391
7380
  return `:nth-of-type(${Math.max(1, Number(node.position?.nthOfType || 1))})`;
@@ -7510,7 +7499,7 @@ function resolveExtractedValueInContext(normalizedValue, options) {
7510
7499
  function stripPositionClauses(nodes) {
7511
7500
  return (nodes || []).map((node) => ({
7512
7501
  ...node,
7513
- match: (node.match || []).filter((clause) => clause.kind !== "position")
7502
+ match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
7514
7503
  }));
7515
7504
  }
7516
7505
  function dedupeSelectors(selectors) {
@@ -8087,9 +8076,17 @@ function resolveDomPathInScope(index, domPath, scope) {
8087
8076
  if (!candidates.length) {
8088
8077
  return null;
8089
8078
  }
8079
+ const lastNode = domPath[domPath.length - 1];
8080
+ const textClauses = lastNode?.match.filter((c) => c.kind === "text") ?? [];
8090
8081
  let fallback = null;
8091
8082
  for (const selector of candidates) {
8092
- const matches = querySelectorAllInScope(index, selector, scope);
8083
+ let matches = querySelectorAllInScope(index, selector, scope);
8084
+ if (textClauses.length > 0 && matches.length > 1) {
8085
+ const filtered = matches.filter((node) => matchesTextClauses(node, textClauses));
8086
+ if (filtered.length > 0) {
8087
+ matches = filtered;
8088
+ }
8089
+ }
8093
8090
  if (matches.length === 1) {
8094
8091
  return {
8095
8092
  node: matches[0],
@@ -8109,6 +8106,10 @@ function resolveDomPathInScope(index, domPath, scope) {
8109
8106
  }
8110
8107
  return fallback;
8111
8108
  }
8109
+ function matchesTextClauses(node, clauses) {
8110
+ const text = (node.textContent ?? "").replace(/\s+/g, " ").trim();
8111
+ return clauses.every((clause) => text.includes(clause.value));
8112
+ }
8112
8113
  function queryAllDomPathInScope(index, domPath, scope) {
8113
8114
  const selectors = buildPathCandidates(domPath);
8114
8115
  for (const selector of selectors) {
@@ -8267,7 +8268,13 @@ function clonePathNode(node) {
8267
8268
  };
8268
8269
  }
8269
8270
  function cloneMatchClause(clause) {
8270
- return clause.kind === "position" ? { kind: "position", axis: clause.axis } : {
8271
+ if (clause.kind === "position") {
8272
+ return { kind: "position", axis: clause.axis };
8273
+ }
8274
+ if (clause.kind === "text") {
8275
+ return { kind: "text", value: clause.value };
8276
+ }
8277
+ return {
8271
8278
  kind: "attr",
8272
8279
  key: clause.key,
8273
8280
  ...clause.op === void 0 ? {} : { op: clause.op },
@@ -8322,6 +8329,13 @@ function normalizeMatch(rawMatch, attrs, position, tag) {
8322
8329
  op,
8323
8330
  ...value === void 0 ? {} : { value }
8324
8331
  });
8332
+ continue;
8333
+ }
8334
+ if (record.kind === "text") {
8335
+ const textValue = typeof record.value === "string" ? record.value.trim() : "";
8336
+ if (textValue) {
8337
+ push({ kind: "text", value: textValue.slice(0, 80) });
8338
+ }
8325
8339
  }
8326
8340
  }
8327
8341
  }
@@ -8689,7 +8703,7 @@ function isTimeoutError(error) {
8689
8703
  }
8690
8704
 
8691
8705
  // ../runtime-core/src/runtimes/dom/executor.ts
8692
- var MAX_DOM_ACTION_ATTEMPTS = 3;
8706
+ var MAX_DOM_ACTION_ATTEMPTS = 2;
8693
8707
  var DEFAULT_SCROLL_OPTIONS = {
8694
8708
  block: "center",
8695
8709
  inline: "center"
@@ -9176,11 +9190,12 @@ var DomActionExecutor = class {
9176
9190
  throw this.createActionabilityError(
9177
9191
  operation,
9178
9192
  "obscured",
9179
- `hit test resolved ${hit.nodeRef} outside the target subtree rooted at ${resolved.nodeRef}`,
9193
+ `target is obscured by ${assessment.blockingDescription ?? "another element"} at the click point`,
9180
9194
  {
9181
9195
  ...details,
9182
9196
  hitRelation: assessment.relation,
9183
9197
  ...assessment.ambiguous === void 0 ? {} : { hitAmbiguous: assessment.ambiguous },
9198
+ ...assessment.blockingDescription === void 0 ? {} : { blockingDescription: assessment.blockingDescription },
9184
9199
  ...assessment.canonicalTarget === void 0 ? {} : {
9185
9200
  canonicalNodeRef: assessment.canonicalTarget.nodeRef,
9186
9201
  canonicalDocumentRef: assessment.canonicalTarget.documentRef,
@@ -9194,8 +9209,7 @@ var DomActionExecutor = class {
9194
9209
  hitMissingFromSnapshot: !resolved.snapshot.nodes.some(
9195
9210
  (node) => node.nodeRef === hit.nodeRef
9196
9211
  )
9197
- },
9198
- true
9212
+ }
9199
9213
  );
9200
9214
  }
9201
9215
  async resolveActionablePointerTarget(session, operation, resolved) {
@@ -9407,7 +9421,12 @@ var DefaultDomRuntime = class {
9407
9421
  });
9408
9422
  }
9409
9423
  async buildPath(input) {
9410
- return sanitizeReplayElementPath(await this.requireBridge().buildReplayPath(input.locator));
9424
+ return sanitizeReplayElementPath(
9425
+ await this.requireBridge().buildReplayPath(
9426
+ input.locator,
9427
+ input.enableTextMatch ? { enableTextMatch: true } : void 0
9428
+ )
9429
+ );
9411
9430
  }
9412
9431
  async resolveTarget(input) {
9413
9432
  return this.withSnapshotSession((session) => this.resolveTargetWithSession(session, input));
@@ -9627,7 +9646,7 @@ var DefaultDomRuntime = class {
9627
9646
  if (resolvedByLocator) {
9628
9647
  const { snapshot, node } = resolvedByLocator;
9629
9648
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
9630
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
9649
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch: true });
9631
9650
  return this.createResolvedTarget("live", snapshot, node, anchor, {
9632
9651
  ...target.persist === void 0 ? {} : { persist: target.persist },
9633
9652
  ...replayPath === void 0 ? {} : { replayPath }
@@ -9645,11 +9664,12 @@ var DefaultDomRuntime = class {
9645
9664
  const { snapshot, node } = resolved;
9646
9665
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
9647
9666
  const writeDescriptor = descriptorWriter ?? ((input) => this.descriptors.write(input));
9648
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
9667
+ const enableTextMatch = method !== "extract";
9668
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch });
9649
9669
  const descriptor = target.persist === void 0 ? void 0 : await writeDescriptor({
9650
9670
  method,
9651
9671
  persist: target.persist,
9652
- path: replayPath ?? await this.buildPathForNode(snapshot, node),
9672
+ path: replayPath ?? await this.buildPathForNode(snapshot, node, { enableTextMatch }),
9653
9673
  sourceUrl: snapshot.url
9654
9674
  });
9655
9675
  return this.createResolvedTarget("selector", snapshot, node, anchor, {
@@ -9749,7 +9769,7 @@ var DefaultDomRuntime = class {
9749
9769
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
9750
9770
  );
9751
9771
  }
9752
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node);
9772
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
9753
9773
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
9754
9774
  ...persist === void 0 ? {} : { persist },
9755
9775
  ...replayPath === void 0 ? {} : { replayPath }
@@ -9920,19 +9940,20 @@ var DefaultDomRuntime = class {
9920
9940
  }
9921
9941
  return this.bridge;
9922
9942
  }
9923
- async buildPathForNode(snapshot, node) {
9943
+ async buildPathForNode(snapshot, node, options) {
9924
9944
  if (node.nodeRef === void 0) {
9925
9945
  throw new Error(
9926
9946
  `snapshot node ${String(node.snapshotNodeId)} does not expose a live node reference`
9927
9947
  );
9928
9948
  }
9929
9949
  return this.buildPath({
9930
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef)
9950
+ locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
9951
+ ...options?.enableTextMatch ? { enableTextMatch: true } : {}
9931
9952
  });
9932
9953
  }
9933
- async tryBuildPathFromNode(snapshot, node) {
9954
+ async tryBuildPathFromNode(snapshot, node, options) {
9934
9955
  try {
9935
- return await this.buildPathForNode(snapshot, node);
9956
+ return await this.buildPathForNode(snapshot, node, options);
9936
9957
  } catch {
9937
9958
  return void 0;
9938
9959
  }
@@ -10546,6 +10567,9 @@ function relaxPathForSingleSample(path18, mode) {
10546
10567
  }
10547
10568
  return !isLast;
10548
10569
  }
10570
+ if (clause.kind === "text") {
10571
+ return false;
10572
+ }
10549
10573
  const key = String(clause.key || "").trim().toLowerCase();
10550
10574
  if (!key || !shouldKeepAttrForSingleSample(key)) {
10551
10575
  return false;
@@ -10648,9 +10672,11 @@ function buildNodeStructure(node) {
10648
10672
  }
10649
10673
  structuralAttrs[key] = value;
10650
10674
  }
10651
- const matchClauses = (node.match || []).map(
10652
- (clause) => clause.kind === "position" ? `position:${clause.axis}` : `attr:${String(clause.key || "").trim().toLowerCase()}`
10653
- ).sort();
10675
+ const matchClauses = (node.match || []).map((clause) => {
10676
+ if (clause.kind === "position") return `position:${clause.axis}`;
10677
+ if (clause.kind === "text") return `text:${clause.value}`;
10678
+ return `attr:${String(clause.key || "").trim().toLowerCase()}`;
10679
+ }).sort();
10654
10680
  return {
10655
10681
  tag,
10656
10682
  attrs: structuralAttrs,
@@ -11056,6 +11082,9 @@ function mergeMatchByMajority(matchLists, attrs, threshold, positionFlags = {
11056
11082
  });
11057
11083
  continue;
11058
11084
  }
11085
+ if (clause.kind === "text") {
11086
+ continue;
11087
+ }
11059
11088
  if (clause.axis === "nthOfType") {
11060
11089
  if (positionFlags.hasNthOfType) {
11061
11090
  merged.push({ kind: "position", axis: "nthOfType" });
@@ -11200,9 +11229,9 @@ function isPersistedObjectNode(node) {
11200
11229
  }
11201
11230
 
11202
11231
  // ../runtime-core/src/sdk/extraction.ts
11203
- function assertValidOpensteerExtractionSchemaRoot(schema) {
11204
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
11205
- throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
11232
+ function assertValidOpensteerExtractionTemplateRoot(template) {
11233
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
11234
+ throw new Error("Invalid extraction template: expected a JSON object at the top level.");
11206
11235
  }
11207
11236
  }
11208
11237
  function isPersistedOpensteerExtractionValueNode2(value) {
@@ -11224,12 +11253,12 @@ function isPersistedOpensteerExtractionArrayNode2(value) {
11224
11253
  return "$array" in value;
11225
11254
  }
11226
11255
  async function compileOpensteerExtractionFieldTargets(options) {
11227
- assertValidOpensteerExtractionSchemaRoot(options.schema);
11256
+ assertValidOpensteerExtractionTemplateRoot(options.template);
11228
11257
  const fields = [];
11229
- await collectFieldTargetsFromSchemaObject({
11258
+ await collectFieldTargetsFromTemplateObject({
11230
11259
  dom: options.dom,
11231
11260
  pageRef: options.pageRef,
11232
- value: options.schema,
11261
+ value: options.template,
11233
11262
  path: "",
11234
11263
  fields,
11235
11264
  insideArray: false
@@ -11281,13 +11310,13 @@ function createOpensteerExtractionDescriptorStore(options) {
11281
11310
  }
11282
11311
  return new MemoryOpensteerExtractionDescriptorStore(namespace);
11283
11312
  }
11284
- async function collectFieldTargetsFromSchemaObject(options) {
11313
+ async function collectFieldTargetsFromTemplateObject(options) {
11285
11314
  for (const [key, childValue] of Object.entries(options.value)) {
11286
11315
  const normalizedKey = normalizeKey(key);
11287
11316
  if (!normalizedKey) {
11288
11317
  continue;
11289
11318
  }
11290
- await collectFieldTargetsFromSchemaValue({
11319
+ await collectFieldTargetsFromTemplateValue({
11291
11320
  dom: options.dom,
11292
11321
  pageRef: options.pageRef,
11293
11322
  value: childValue,
@@ -11297,8 +11326,8 @@ async function collectFieldTargetsFromSchemaObject(options) {
11297
11326
  });
11298
11327
  }
11299
11328
  }
11300
- async function collectFieldTargetsFromSchemaValue(options) {
11301
- const normalizedField = normalizeSchemaField(options.value);
11329
+ async function collectFieldTargetsFromTemplateValue(options) {
11330
+ const normalizedField = normalizeTemplateField(options.value);
11302
11331
  if (normalizedField !== null) {
11303
11332
  options.fields.push(
11304
11333
  await compileFieldTarget({
@@ -11313,12 +11342,12 @@ async function collectFieldTargetsFromSchemaValue(options) {
11313
11342
  if (Array.isArray(options.value)) {
11314
11343
  if (options.insideArray) {
11315
11344
  throw new Error(
11316
- `Nested arrays are not supported in extraction schema at "${labelForPath(options.path)}".`
11345
+ `Nested arrays are not supported in extraction template at "${labelForPath(options.path)}".`
11317
11346
  );
11318
11347
  }
11319
11348
  if (options.value.length === 0) {
11320
11349
  throw new Error(
11321
- `Extraction array "${labelForPath(options.path)}" must include at least one representative item.`
11350
+ `Extraction array "${labelForPath(options.path)}" must include at least one representative template item.`
11322
11351
  );
11323
11352
  }
11324
11353
  for (let index = 0; index < options.value.length; index += 1) {
@@ -11329,7 +11358,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
11329
11358
  );
11330
11359
  }
11331
11360
  const fieldCountBeforeItem = options.fields.length;
11332
- await collectFieldTargetsFromSchemaObject({
11361
+ await collectFieldTargetsFromTemplateObject({
11333
11362
  dom: options.dom,
11334
11363
  pageRef: options.pageRef,
11335
11364
  value: itemValue,
@@ -11340,7 +11369,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
11340
11369
  const itemFields = options.fields.slice(fieldCountBeforeItem);
11341
11370
  if (!itemFields.some((field) => !("source" in field))) {
11342
11371
  throw new Error(
11343
- `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element- or selector-backed field.`
11372
+ `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element number or selector field.`
11344
11373
  );
11345
11374
  }
11346
11375
  }
@@ -11348,10 +11377,10 @@ async function collectFieldTargetsFromSchemaValue(options) {
11348
11377
  }
11349
11378
  if (!options.value || typeof options.value !== "object") {
11350
11379
  throw new Error(
11351
- `Invalid extraction schema value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
11380
+ `Invalid extraction template value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
11352
11381
  );
11353
11382
  }
11354
- await collectFieldTargetsFromSchemaObject({
11383
+ await collectFieldTargetsFromTemplateObject({
11355
11384
  dom: options.dom,
11356
11385
  pageRef: options.pageRef,
11357
11386
  value: options.value,
@@ -11383,7 +11412,7 @@ async function compileFieldTarget(options) {
11383
11412
  path: await resolveSelectorFieldPath({
11384
11413
  dom: options.dom,
11385
11414
  pageRef: options.pageRef,
11386
- selector: `[c="${String(options.field.element)}"]`
11415
+ selector: `[c="${String(options.field.c)}"]`
11387
11416
  }),
11388
11417
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
11389
11418
  };
@@ -11684,24 +11713,29 @@ function countNonNullLeaves(value) {
11684
11713
  }
11685
11714
  return Object.values(value).reduce((sum, item) => sum + countNonNullLeaves(item), 0);
11686
11715
  }
11687
- function normalizeSchemaField(value) {
11716
+ function normalizeTemplateField(value) {
11717
+ if (typeof value === "number") {
11718
+ return {
11719
+ c: normalizeExtractionCounter(value)
11720
+ };
11721
+ }
11688
11722
  if (!value || typeof value !== "object" || Array.isArray(value)) {
11689
11723
  return null;
11690
11724
  }
11691
11725
  const raw = value;
11692
- const hasElement = raw.element !== void 0;
11726
+ const hasCounter = raw.c !== void 0 || raw.element !== void 0;
11693
11727
  const hasSelector = raw.selector !== void 0;
11694
11728
  const hasSource = raw.source !== void 0;
11695
- const targetCount = Number(hasElement) + Number(hasSelector) + Number(hasSource);
11729
+ const targetCount = Number(hasCounter) + Number(hasSelector) + Number(hasSource);
11696
11730
  if (targetCount === 0) {
11697
11731
  return null;
11698
11732
  }
11699
11733
  if (targetCount !== 1) {
11700
11734
  throw new Error(
11701
- "Extraction field descriptors must specify exactly one of element, selector, or source."
11735
+ "Extraction field descriptors must specify exactly one of c/element, selector, or source."
11702
11736
  );
11703
11737
  }
11704
- const attribute = raw.attribute === void 0 ? void 0 : normalizeNonEmptyString2("attribute", raw.attribute);
11738
+ const attribute = raw.attr !== void 0 ? normalizeNonEmptyString2("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString2("attribute", raw.attribute);
11705
11739
  if (hasSource) {
11706
11740
  if (raw.source !== "current_url") {
11707
11741
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -11716,17 +11750,20 @@ function normalizeSchemaField(value) {
11716
11750
  ...attribute === void 0 ? {} : { attribute }
11717
11751
  };
11718
11752
  }
11719
- const element = Number(raw.element);
11720
- if (!Number.isInteger(element) || element < 1) {
11721
- throw new Error(
11722
- `Extraction field element must be a positive integer, received ${String(raw.element)}.`
11723
- );
11724
- }
11725
11753
  return {
11726
- element,
11754
+ c: normalizeExtractionCounter(raw.c ?? raw.element),
11727
11755
  ...attribute === void 0 ? {} : { attribute }
11728
11756
  };
11729
11757
  }
11758
+ function normalizeExtractionCounter(value) {
11759
+ const counter = Number(value);
11760
+ if (!Number.isInteger(counter) || counter < 1) {
11761
+ throw new Error(
11762
+ `Extraction element number must be a positive integer, received ${String(value)}.`
11763
+ );
11764
+ }
11765
+ return counter;
11766
+ }
11730
11767
  function normalizeNamespace(namespace) {
11731
11768
  const normalized = String(namespace ?? "default").trim();
11732
11769
  return normalized.length === 0 ? "default" : normalized;
@@ -11757,7 +11794,7 @@ function parseExtractionDescriptorRecord(record) {
11757
11794
  kind: "dom-extraction",
11758
11795
  persist: raw.persist,
11759
11796
  root,
11760
- ...typeof raw.schemaHash === "string" ? { schemaHash: raw.schemaHash } : {},
11797
+ ...typeof raw.templateHash === "string" ? { templateHash: raw.templateHash } : typeof raw.schemaHash === "string" ? { templateHash: raw.schemaHash } : {},
11761
11798
  ...typeof raw.sourceUrl === "string" ? { sourceUrl: raw.sourceUrl } : {}
11762
11799
  }
11763
11800
  };
@@ -11843,7 +11880,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
11843
11880
  kind: "dom-extraction",
11844
11881
  persist: input.persist,
11845
11882
  root: input.root,
11846
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
11883
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
11847
11884
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
11848
11885
  };
11849
11886
  const key = persistKey(this.namespace, input.persist);
@@ -11892,7 +11929,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
11892
11929
  kind: "dom-extraction",
11893
11930
  persist: input.persist,
11894
11931
  root: input.root,
11895
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
11932
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
11896
11933
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
11897
11934
  };
11898
11935
  const key = persistKey(this.namespace, input.persist);
@@ -13862,7 +13899,8 @@ function normalizeOpensteerEngineName(value, source = "engine") {
13862
13899
  if (normalized === "playwright" || normalized === "abp") {
13863
13900
  return normalized;
13864
13901
  }
13865
- throw new Error(
13902
+ throw new OpensteerProtocolError(
13903
+ "invalid-argument",
13866
13904
  `${source} must be one of ${OPENSTEER_ENGINE_NAMES.join(", ")}; received "${value}".`
13867
13905
  );
13868
13906
  }
@@ -13871,7 +13909,8 @@ function assertSupportedEngineOptions(input) {
13871
13909
  return;
13872
13910
  }
13873
13911
  if (typeof input.browser === "object" && input.browser !== null && input.browser.mode === "attach") {
13874
- throw new Error(
13912
+ throw new OpensteerProtocolError(
13913
+ "invalid-argument",
13875
13914
  'ABP engine does not support browser.mode="attach". Use the Playwright engine for attach flows.'
13876
13915
  );
13877
13916
  }
@@ -13879,7 +13918,8 @@ function assertSupportedEngineOptions(input) {
13879
13918
  if (unsupportedContextOptionNames.length === 0) {
13880
13919
  return;
13881
13920
  }
13882
- throw new Error(
13921
+ throw new OpensteerProtocolError(
13922
+ "invalid-argument",
13883
13923
  `ABP engine does not support ${unsupportedContextOptionNames.join(", ")}. Supported ABP context options: context.viewport.`
13884
13924
  );
13885
13925
  }
@@ -14981,6 +15021,48 @@ function isOpensteerEnvironmentKey(key) {
14981
15021
  return key.startsWith(OPENSTEER_ENV_PREFIX);
14982
15022
  }
14983
15023
 
15024
+ // src/internal/errors.ts
15025
+ function normalizeThrownOpensteerError(error, fallbackMessage) {
15026
+ if (isOpensteerProtocolError(error)) {
15027
+ return toOpensteerError(error);
15028
+ }
15029
+ if (isBrowserCoreError(error)) {
15030
+ return createOpensteerError(error.code, error.message, {
15031
+ retriable: error.retriable,
15032
+ ...error.details === void 0 ? {} : { details: error.details }
15033
+ });
15034
+ }
15035
+ if (error instanceof OpensteerAttachAmbiguousError) {
15036
+ return createOpensteerError("conflict", error.message, {
15037
+ details: {
15038
+ candidates: error.candidates,
15039
+ code: error.code,
15040
+ name: error.name
15041
+ }
15042
+ });
15043
+ }
15044
+ if (error instanceof Error && "opensteerError" in error && typeof error.opensteerError === "object" && error.opensteerError !== null) {
15045
+ const oe = error.opensteerError;
15046
+ return createOpensteerError(oe.code, oe.message, {
15047
+ retriable: oe.retriable,
15048
+ ...oe.capability === void 0 ? {} : { capability: oe.capability },
15049
+ ...oe.details === void 0 ? {} : { details: oe.details }
15050
+ });
15051
+ }
15052
+ if (error instanceof Error) {
15053
+ return createOpensteerError("operation-failed", error.message, {
15054
+ details: {
15055
+ name: error.name
15056
+ }
15057
+ });
15058
+ }
15059
+ return createOpensteerError("internal", fallbackMessage, {
15060
+ details: {
15061
+ value: error
15062
+ }
15063
+ });
15064
+ }
15065
+
14984
15066
  // src/provider/config.ts
14985
15067
  var OPENSTEER_PROVIDER_MODES = ["local", "cloud"];
14986
15068
  function assertProviderSupportsEngine(provider, engine) {
@@ -14988,7 +15070,8 @@ function assertProviderSupportsEngine(provider, engine) {
14988
15070
  return;
14989
15071
  }
14990
15072
  if (provider === "cloud") {
14991
- throw new Error(
15073
+ throw new OpensteerProtocolError(
15074
+ "invalid-argument",
14992
15075
  "ABP is not supported for provider=cloud. Cloud provider currently requires Playwright."
14993
15076
  );
14994
15077
  }
@@ -14998,7 +15081,8 @@ function normalizeOpensteerProviderMode(value, source = "OPENSTEER_PROVIDER") {
14998
15081
  if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
14999
15082
  return normalized;
15000
15083
  }
15001
- throw new Error(
15084
+ throw new OpensteerProtocolError(
15085
+ "invalid-argument",
15002
15086
  `${source} must be one of ${OPENSTEER_PROVIDER_MODES.join(", ")}; received "${value}".`
15003
15087
  );
15004
15088
  }
@@ -15438,6 +15522,24 @@ async function sleep3(ms) {
15438
15522
  // src/cloud/client.ts
15439
15523
  var CLOUD_CLOSE_TIMEOUT_MS = 6e4;
15440
15524
  var CLOUD_CLOSE_POLL_INTERVAL_MS = 250;
15525
+ var OpensteerCloudRequestError = class extends Error {
15526
+ statusCode;
15527
+ code;
15528
+ details;
15529
+ method;
15530
+ pathname;
15531
+ url;
15532
+ constructor(args) {
15533
+ super(args.message);
15534
+ this.name = "OpensteerCloudRequestError";
15535
+ this.statusCode = args.statusCode;
15536
+ this.code = args.code;
15537
+ this.details = args.details;
15538
+ this.method = args.method;
15539
+ this.pathname = args.pathname;
15540
+ this.url = args.url;
15541
+ }
15542
+ };
15441
15543
  var OpensteerCloudClient = class {
15442
15544
  constructor(config) {
15443
15545
  this.config = config;
@@ -15613,7 +15715,11 @@ var OpensteerCloudClient = class {
15613
15715
  });
15614
15716
  }
15615
15717
  if (!response.ok) {
15616
- throw new Error(`${init.method} ${pathname} failed with ${String(response.status)}.`);
15718
+ throw await createCloudRequestError(response, {
15719
+ method: init.method,
15720
+ pathname,
15721
+ url
15722
+ });
15617
15723
  }
15618
15724
  return response;
15619
15725
  }
@@ -15661,6 +15767,36 @@ function wrapCloudFetchError(error, input) {
15661
15767
  wrapped.name = error.name;
15662
15768
  return wrapped;
15663
15769
  }
15770
+ async function createCloudRequestError(response, input) {
15771
+ const payload = await readCloudErrorPayload(response);
15772
+ return new OpensteerCloudRequestError({
15773
+ statusCode: response.status,
15774
+ method: input.method,
15775
+ pathname: input.pathname,
15776
+ url: input.url,
15777
+ message: payload?.error ?? `${input.method} ${input.pathname} failed with ${String(response.status)}.`,
15778
+ ...payload?.code === void 0 ? {} : { code: payload.code },
15779
+ ...payload?.details === void 0 ? {} : { details: payload.details }
15780
+ });
15781
+ }
15782
+ async function readCloudErrorPayload(response) {
15783
+ try {
15784
+ return asCloudErrorPayload(await response.json());
15785
+ } catch {
15786
+ return void 0;
15787
+ }
15788
+ }
15789
+ function asCloudErrorPayload(value) {
15790
+ if (value === null || typeof value !== "object") {
15791
+ return void 0;
15792
+ }
15793
+ const payload = value;
15794
+ return {
15795
+ ...typeof payload.error === "string" ? { error: payload.error } : {},
15796
+ ...typeof payload.code === "string" ? { code: payload.code } : {},
15797
+ ..."details" in payload ? { details: payload.details } : {}
15798
+ };
15799
+ }
15664
15800
 
15665
15801
  // src/cloud/config.ts
15666
15802
  function resolveCloudConfig(input = {}) {
@@ -15691,13 +15827,13 @@ function resolveCloudConfig(input = {}) {
15691
15827
 
15692
15828
  // ../runtime-core/package.json
15693
15829
  var package_default = {
15694
- version: "0.2.3"};
15830
+ version: "0.2.5"};
15695
15831
 
15696
15832
  // ../runtime-core/src/version.ts
15697
15833
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
15698
15834
 
15699
15835
  // ../runtime-core/src/internal/errors.ts
15700
- function normalizeThrownOpensteerError(error, fallbackMessage) {
15836
+ function normalizeThrownOpensteerError2(error, fallbackMessage) {
15701
15837
  if (isOpensteerProtocolError(error)) {
15702
15838
  return toOpensteerError(error);
15703
15839
  }
@@ -16737,10 +16873,19 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
16737
16873
  // ../runtime-core/src/sdk/snapshot/cleaner.ts
16738
16874
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
16739
16875
  var TEXT_ATTR_MAX = 150;
16740
- var URL_ATTR_MAX = 500;
16741
- var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "srcset"]);
16876
+ var SRCSET_ATTR_MAX = 160;
16877
+ var MIDDLE_TRUNCATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
16742
16878
  var TEXT_ATTRS = /* @__PURE__ */ new Set(["alt", "title", "aria-label", "placeholder", "value"]);
16743
- var TRUNCATION_SUFFIX = " [truncated]";
16879
+ var TRUNCATION_SUFFIX = "...";
16880
+ var MIDDLE_TRUNCATION_MARKER = "...";
16881
+ var MIDDLE_TRUNCATION_HEAD_MAX = 40;
16882
+ var MIDDLE_TRUNCATION_TAIL_MAX = 20;
16883
+ var SRCSET_CANDIDATE_HEAD_MAX = 36;
16884
+ var SRCSET_CANDIDATE_TAIL_MAX = 12;
16885
+ var SRCSET_COMPACT_CANDIDATE_HEAD_MAX = 20;
16886
+ var SRCSET_COMPACT_CANDIDATE_TAIL_MAX = 8;
16887
+ var SRCSET_FALLBACK_HEAD_MAX = 56;
16888
+ var SRCSET_FALLBACK_TAIL_MAX = 20;
16744
16889
  var NOISE_SELECTORS = [
16745
16890
  `[${OPENSTEER_HIDDEN_ATTR}]`,
16746
16891
  "[hidden]",
@@ -16803,184 +16948,554 @@ function truncateValue(value, max) {
16803
16948
  }
16804
16949
  return `${head}${TRUNCATION_SUFFIX}`;
16805
16950
  }
16951
+ function takeValueWithinSerializedLengthFromEnd(value, max) {
16952
+ let serializedLength = 0;
16953
+ const chars = [];
16954
+ for (let index = value.length - 1; index >= 0; index -= 1) {
16955
+ const char = value[index];
16956
+ let nextLength = 1;
16957
+ if (char === "&") {
16958
+ nextLength = 5;
16959
+ } else if (char === "<" || char === ">") {
16960
+ nextLength = 4;
16961
+ } else if (char === '"') {
16962
+ nextLength = 6;
16963
+ }
16964
+ if (serializedLength + nextLength > max) {
16965
+ break;
16966
+ }
16967
+ chars.push(char);
16968
+ serializedLength += nextLength;
16969
+ }
16970
+ return chars.reverse().join("");
16971
+ }
16972
+ function truncateValueInMiddle(value, headMax, tailMax, marker = MIDDLE_TRUNCATION_MARKER) {
16973
+ const markerLength = getSerializedLength(marker);
16974
+ const max = headMax + markerLength + tailMax;
16975
+ if (getSerializedLength(value) <= max) {
16976
+ return value;
16977
+ }
16978
+ const head = takeValueWithinSerializedLength(value, headMax).replace(/\s+$/u, "");
16979
+ const tail = takeValueWithinSerializedLengthFromEnd(value, tailMax).replace(/^\s+/u, "");
16980
+ if (head.length === 0) {
16981
+ return tail.length === 0 ? marker : `${marker}${tail}`;
16982
+ }
16983
+ if (tail.length === 0) {
16984
+ return `${head}${marker}`;
16985
+ }
16986
+ return `${head}${marker}${tail}`;
16987
+ }
16806
16988
  function getAttrLimit(attr) {
16807
- if (URL_ATTRS.has(attr)) {
16808
- return URL_ATTR_MAX;
16989
+ if (attr === "srcset") {
16990
+ return SRCSET_ATTR_MAX;
16809
16991
  }
16810
16992
  if (TEXT_ATTRS.has(attr)) {
16811
16993
  return TEXT_ATTR_MAX;
16812
16994
  }
16813
16995
  return void 0;
16814
16996
  }
16997
+ function shouldBoundAttr(attr) {
16998
+ return MIDDLE_TRUNCATED_URL_ATTRS.has(attr) || getAttrLimit(attr) !== void 0;
16999
+ }
16815
17000
  function setBoundedAttr(el, attr, value) {
17001
+ if (MIDDLE_TRUNCATED_URL_ATTRS.has(attr)) {
17002
+ el.attr(
17003
+ attr,
17004
+ truncateValueInMiddle(value, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX)
17005
+ );
17006
+ return;
17007
+ }
16816
17008
  const limit = getAttrLimit(attr);
17009
+ if (attr === "srcset" && limit !== void 0) {
17010
+ el.attr(attr, truncateSrcsetValue(value, limit));
17011
+ return;
17012
+ }
16817
17013
  el.attr(attr, limit === void 0 ? value : truncateValue(value, limit));
16818
17014
  }
16819
- function removeNoise($) {
16820
- for (const tag of STRIP_TAGS) {
16821
- $(tag).remove();
17015
+ function truncateSrcsetValue(value, max) {
17016
+ if (getSerializedLength(value) <= max) {
17017
+ return value;
16822
17018
  }
16823
- $(NOISE_SELECTORS.join(", ")).remove();
16824
- }
16825
- function removeComments($) {
16826
- $("*").contents().each(function removeComment() {
16827
- if (this.type === "comment") {
16828
- $(this).remove();
16829
- }
16830
- });
16831
- }
16832
- function markInlineSelfHiddenFallback($) {
16833
- $(
16834
- "[style*='visibility: hidden'], [style*='visibility:hidden'], [style*='visibility: collapse'], [style*='visibility:collapse']"
16835
- ).each(function markInlineVisibilityHidden() {
16836
- const el = $(this);
16837
- if (el.attr(OPENSTEER_HIDDEN_ATTR) === void 0) {
16838
- el.attr(OPENSTEER_SELF_HIDDEN_ATTR, "1");
16839
- }
16840
- });
16841
- }
16842
- function pruneSelfHiddenNodes($) {
16843
- const nodes = [];
16844
- $(`[${OPENSTEER_SELF_HIDDEN_ATTR}]`).each(function collectSelfHiddenNodes() {
16845
- nodes.push($(this));
16846
- });
16847
- nodes.sort((left, right) => right.parents().length - left.parents().length);
16848
- for (const el of nodes) {
16849
- if (!el[0]) {
16850
- continue;
16851
- }
16852
- el.contents().each(function removeSelfHiddenText() {
16853
- if (this.type === "text") {
16854
- $(this).remove();
16855
- }
16856
- });
16857
- if (el.children().length === 0) {
16858
- el.remove();
16859
- }
17019
+ const candidates = parseSrcsetCandidates2(value);
17020
+ if (candidates.length === 0) {
17021
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
16860
17022
  }
16861
- }
16862
- function hasDirectText($, el) {
16863
- return el.contents().filter(function hasDirectNodeText() {
16864
- return this.type === "text" && $(this).text().trim() !== "";
16865
- }).length > 0;
16866
- }
16867
- function hasTextDeep(el) {
16868
- return el.text().trim().length > 0;
16869
- }
16870
- function hasActionLabel(attrs) {
16871
- return typeof attrs["aria-label"] === "string" && attrs["aria-label"].trim() !== "" || typeof attrs["aria-labelledby"] === "string" && attrs["aria-labelledby"].trim() !== "" || typeof attrs["aria-describedby"] === "string" && attrs["aria-describedby"].trim() !== "" || typeof attrs.title === "string" && attrs.title.trim() !== "" || typeof attrs.placeholder === "string" && attrs.placeholder.trim() !== "" || typeof attrs.value === "string" && attrs.value.trim() !== "";
16872
- }
16873
- function unwrapActionNode($, el) {
16874
- if (hasTextDeep(el)) {
16875
- if (el.prev().length > 0) {
16876
- el.before(" ");
16877
- }
16878
- if (el.next().length > 0) {
16879
- el.after(" ");
17023
+ for (const [headMax, tailMax, includeBest] of [
17024
+ [SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, true],
17025
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, true],
17026
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, false]
17027
+ ]) {
17028
+ const compact = buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest);
17029
+ if (getSerializedLength(compact) <= max) {
17030
+ return compact;
16880
17031
  }
16881
17032
  }
16882
- el.replaceWith(el.contents());
17033
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
16883
17034
  }
16884
- function stripToAttrs(el, keep) {
16885
- const attrs = el.attr() || {};
16886
- for (const attr of Object.keys(attrs)) {
16887
- if (!keep.has(attr)) {
16888
- el.removeAttr(attr);
16889
- continue;
16890
- }
16891
- const value = el.attr(attr);
16892
- if (typeof value !== "string") {
16893
- continue;
16894
- }
16895
- if (getAttrLimit(attr) !== void 0) {
16896
- setBoundedAttr(el, attr, value);
17035
+ function buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest) {
17036
+ const kept = getPreferredSrcsetCandidateIndices(candidates, includeBest);
17037
+ const parts = [];
17038
+ let previousIndex;
17039
+ for (const candidateIndex of kept) {
17040
+ if (previousIndex !== void 0 && candidateIndex - previousIndex > 1) {
17041
+ parts.push(MIDDLE_TRUNCATION_MARKER);
16897
17042
  }
17043
+ parts.push(formatSrcsetCandidate(candidates[candidateIndex], headMax, tailMax));
17044
+ previousIndex = candidateIndex;
16898
17045
  }
17046
+ return parts.join(", ");
16899
17047
  }
16900
- function restoreBoundedAttr(el, attr, value) {
16901
- if (typeof value !== "string") {
16902
- return;
17048
+ function getPreferredSrcsetCandidateIndices(candidates, includeBest) {
17049
+ if (candidates.length === 0) {
17050
+ return [];
16903
17051
  }
16904
- const trimmed = value.trim();
16905
- if (trimmed === "") {
16906
- return;
17052
+ const kept = /* @__PURE__ */ new Set([0, candidates.length - 1]);
17053
+ if (includeBest) {
17054
+ kept.add(pickBestSrcsetCandidateIndex(candidates));
16907
17055
  }
16908
- setBoundedAttr(el, attr, value);
17056
+ return [...kept].filter((index) => index >= 0 && index < candidates.length).sort((a, b) => a - b);
16909
17057
  }
16910
- function deduplicateImages(html) {
16911
- const seen = /* @__PURE__ */ new Set();
16912
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
16913
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
16914
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
16915
- let src = null;
16916
- if (srcMatch && srcMatch[2]) {
16917
- src = srcMatch[2].trim();
16918
- } else if (srcsetMatch && srcsetMatch[2]) {
16919
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
16920
- }
16921
- if (!src) {
16922
- return full;
17058
+ function pickBestSrcsetCandidateIndex(candidates) {
17059
+ let bestWidthIndex = -1;
17060
+ let bestWidth = -1;
17061
+ let bestDensityIndex = -1;
17062
+ let bestDensity = -1;
17063
+ for (let index = 0; index < candidates.length; index += 1) {
17064
+ const candidate = candidates[index];
17065
+ if (typeof candidate.width === "number" && Number.isFinite(candidate.width) && candidate.width > bestWidth) {
17066
+ bestWidth = candidate.width;
17067
+ bestWidthIndex = index;
16923
17068
  }
16924
- if (seen.has(src)) {
16925
- return "";
17069
+ if (typeof candidate.density === "number" && Number.isFinite(candidate.density) && candidate.density > bestDensity) {
17070
+ bestDensity = candidate.density;
17071
+ bestDensityIndex = index;
16926
17072
  }
16927
- seen.add(src);
16928
- return full;
16929
- });
16930
- }
16931
- function isPreservedImageElement($, el) {
16932
- const tag = (el[0]?.tagName || "").toLowerCase();
16933
- if (tag === "img") {
16934
- return true;
16935
17073
  }
16936
- if (tag === "picture") {
16937
- const hasImg = el.find("img").length > 0;
16938
- const hasSource = el.find("source[src], source[srcset]").length > 0;
16939
- return hasImg || hasSource;
17074
+ if (bestWidthIndex >= 0) {
17075
+ return bestWidthIndex;
16940
17076
  }
16941
- if (tag === "source") {
16942
- const inPicture = el.parents("picture").length > 0;
16943
- const hasSrc = el.attr("src") != null && el.attr("src").trim() !== "" || el.attr("srcset") != null && el.attr("srcset").trim() !== "";
16944
- return inPicture && hasSrc;
17077
+ if (bestDensityIndex >= 0) {
17078
+ return bestDensityIndex;
16945
17079
  }
16946
- return false;
17080
+ return candidates.length - 1;
16947
17081
  }
16948
- function flattenExtractionTree($) {
16949
- const flatten = (root) => {
16950
- root.find("*").each(function flattenNode() {
16951
- const el = $(this);
16952
- const node = el[0];
16953
- if (!node) {
16954
- return;
16955
- }
16956
- const tag = (node.tagName || "").toLowerCase();
16957
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
16958
- return;
16959
- }
16960
- if (isPreservedImageElement($, el)) {
16961
- return;
16962
- }
16963
- if (tag === "a") {
16964
- el.children().each(function flattenAnchorChild() {
16965
- flatten($(this));
16966
- });
16967
- return;
16968
- }
16969
- const hasText = hasDirectText($, el);
16970
- if (hasText) {
16971
- return;
16972
- }
16973
- if (el.children().length === 0) {
16974
- el.remove();
16975
- return;
16976
- }
16977
- el.children().each(function flattenChild() {
16978
- flatten($(this));
16979
- });
16980
- el.replaceWith(el.contents());
17082
+ function formatSrcsetCandidate(candidate, headMax, tailMax) {
17083
+ const url = truncateValueInMiddle(candidate.url, headMax, tailMax);
17084
+ return candidate.descriptorText ? `${url} ${candidate.descriptorText}` : url;
17085
+ }
17086
+ function parseSrcsetCandidates2(raw) {
17087
+ const text = raw.trim();
17088
+ if (!text) {
17089
+ return [];
17090
+ }
17091
+ const out = [];
17092
+ let index = 0;
17093
+ while (index < text.length) {
17094
+ index = skipSrcsetSeparators(text, index);
17095
+ if (index >= text.length) {
17096
+ break;
17097
+ }
17098
+ const urlToken = readSrcsetUrlToken(text, index);
17099
+ index = urlToken.nextIndex;
17100
+ const url = urlToken.value.trim();
17101
+ if (!url) {
17102
+ continue;
17103
+ }
17104
+ index = skipSrcsetWhitespace(text, index);
17105
+ const descriptors = [];
17106
+ while (index < text.length && text[index] !== ",") {
17107
+ const descriptorToken = readSrcsetDescriptorToken(text, index);
17108
+ if (!descriptorToken.value) {
17109
+ index = descriptorToken.nextIndex;
17110
+ continue;
17111
+ }
17112
+ descriptors.push(descriptorToken.value);
17113
+ index = descriptorToken.nextIndex;
17114
+ index = skipSrcsetWhitespace(text, index);
17115
+ }
17116
+ if (index < text.length && text[index] === ",") {
17117
+ index += 1;
17118
+ }
17119
+ let width = null;
17120
+ let density = null;
17121
+ for (const descriptor of descriptors) {
17122
+ const token = descriptor.trim().toLowerCase();
17123
+ if (!token) {
17124
+ continue;
17125
+ }
17126
+ const widthMatch = token.match(/^(\d+)w$/);
17127
+ if (widthMatch) {
17128
+ const parsed = Number.parseInt(widthMatch[1], 10);
17129
+ if (Number.isFinite(parsed)) {
17130
+ width = parsed;
17131
+ }
17132
+ continue;
17133
+ }
17134
+ const densityMatch = token.match(/^(\d*\.?\d+)x$/);
17135
+ if (densityMatch) {
17136
+ const parsed = Number.parseFloat(densityMatch[1]);
17137
+ if (Number.isFinite(parsed)) {
17138
+ density = parsed;
17139
+ }
17140
+ }
17141
+ }
17142
+ out.push({
17143
+ url,
17144
+ descriptorText: descriptors.join(" "),
17145
+ width,
17146
+ density
16981
17147
  });
17148
+ }
17149
+ return out;
17150
+ }
17151
+ function skipSrcsetWhitespace(value, index) {
17152
+ let cursor = index;
17153
+ while (cursor < value.length && /\s/u.test(value[cursor])) {
17154
+ cursor += 1;
17155
+ }
17156
+ return cursor;
17157
+ }
17158
+ function skipSrcsetSeparators(value, index) {
17159
+ let cursor = skipSrcsetWhitespace(value, index);
17160
+ while (cursor < value.length && value[cursor] === ",") {
17161
+ cursor += 1;
17162
+ cursor = skipSrcsetWhitespace(value, cursor);
17163
+ }
17164
+ return cursor;
17165
+ }
17166
+ function readSrcsetUrlToken(value, index) {
17167
+ let cursor = index;
17168
+ let out = "";
17169
+ const isDataUrl = value.slice(index, index + 5).toLowerCase().startsWith("data:");
17170
+ while (cursor < value.length) {
17171
+ const char = value[cursor];
17172
+ if (/\s/u.test(char)) {
17173
+ break;
17174
+ }
17175
+ if (char === "," && !isDataUrl) {
17176
+ break;
17177
+ }
17178
+ out += char;
17179
+ cursor += 1;
17180
+ }
17181
+ if (isDataUrl && out.endsWith(",") && cursor < value.length) {
17182
+ out = out.slice(0, -1);
17183
+ }
17184
+ return {
17185
+ value: out,
17186
+ nextIndex: cursor
17187
+ };
17188
+ }
17189
+ function readSrcsetDescriptorToken(value, index) {
17190
+ let cursor = skipSrcsetWhitespace(value, index);
17191
+ let out = "";
17192
+ while (cursor < value.length) {
17193
+ const char = value[cursor];
17194
+ if (char === "," || /\s/u.test(char)) {
17195
+ break;
17196
+ }
17197
+ out += char;
17198
+ cursor += 1;
17199
+ }
17200
+ return {
17201
+ value: out.trim(),
17202
+ nextIndex: cursor
17203
+ };
17204
+ }
17205
+ function removeNoise($) {
17206
+ for (const tag of STRIP_TAGS) {
17207
+ $(tag).remove();
17208
+ }
17209
+ $(NOISE_SELECTORS.join(", ")).remove();
17210
+ }
17211
+ function removeComments($) {
17212
+ $("*").contents().each(function removeComment() {
17213
+ if (this.type === "comment") {
17214
+ $(this).remove();
17215
+ }
17216
+ });
17217
+ }
17218
+ function markInlineSelfHiddenFallback($) {
17219
+ $(
17220
+ "[style*='visibility: hidden'], [style*='visibility:hidden'], [style*='visibility: collapse'], [style*='visibility:collapse']"
17221
+ ).each(function markInlineVisibilityHidden() {
17222
+ const el = $(this);
17223
+ if (el.attr(OPENSTEER_HIDDEN_ATTR) === void 0) {
17224
+ el.attr(OPENSTEER_SELF_HIDDEN_ATTR, "1");
17225
+ }
17226
+ });
17227
+ }
17228
+ function pruneSelfHiddenNodes($) {
17229
+ for (const node of getElementsInReverseDocumentOrder($)) {
17230
+ if (node.attribs?.[OPENSTEER_SELF_HIDDEN_ATTR] === void 0) {
17231
+ continue;
17232
+ }
17233
+ const el = $(node);
17234
+ el.contents().each(function removeSelfHiddenText() {
17235
+ if (this.type === "text") {
17236
+ $(this).remove();
17237
+ }
17238
+ });
17239
+ if (!hasElementChildren(node)) {
17240
+ el.remove();
17241
+ }
17242
+ }
17243
+ }
17244
+ function getChildNodes(node) {
17245
+ return node?.children ?? [];
17246
+ }
17247
+ function isElementLikeNode(node) {
17248
+ return node?.type === "tag" || node?.type === "script" || node?.type === "style";
17249
+ }
17250
+ function hasDirectText(node) {
17251
+ if (!node) {
17252
+ return false;
17253
+ }
17254
+ for (const child of getChildNodes(node)) {
17255
+ if (child.type === "text" && (child.data || "").trim() !== "") {
17256
+ return true;
17257
+ }
17258
+ }
17259
+ return false;
17260
+ }
17261
+ function hasElementChildren(node) {
17262
+ if (!node) {
17263
+ return false;
17264
+ }
17265
+ for (const child of getChildNodes(node)) {
17266
+ if (isElementLikeNode(child)) {
17267
+ return true;
17268
+ }
17269
+ }
17270
+ return false;
17271
+ }
17272
+ function hasTextDeepNode(node) {
17273
+ if (!node) {
17274
+ return false;
17275
+ }
17276
+ if (node.type === "text") {
17277
+ return (node.data || "").trim() !== "";
17278
+ }
17279
+ for (const child of getChildNodes(node)) {
17280
+ if (hasTextDeepNode(child)) {
17281
+ return true;
17282
+ }
17283
+ }
17284
+ return false;
17285
+ }
17286
+ function hasActionLabel(attrs) {
17287
+ return typeof attrs["aria-label"] === "string" && attrs["aria-label"].trim() !== "" || typeof attrs["aria-labelledby"] === "string" && attrs["aria-labelledby"].trim() !== "" || typeof attrs["aria-describedby"] === "string" && attrs["aria-describedby"].trim() !== "" || typeof attrs.title === "string" && attrs.title.trim() !== "" || typeof attrs.placeholder === "string" && attrs.placeholder.trim() !== "" || typeof attrs.value === "string" && attrs.value.trim() !== "";
17288
+ }
17289
+ function unwrapActionNode($, el) {
17290
+ if (hasTextDeepNode(el[0])) {
17291
+ if (el.prev().length > 0) {
17292
+ el.before(" ");
17293
+ }
17294
+ if (el.next().length > 0) {
17295
+ el.after(" ");
17296
+ }
17297
+ }
17298
+ el.replaceWith(el.contents());
17299
+ }
17300
+ function stripToAttrs(el, keep) {
17301
+ const attrs = el.attr() || {};
17302
+ for (const attr of Object.keys(attrs)) {
17303
+ if (!keep.has(attr)) {
17304
+ el.removeAttr(attr);
17305
+ continue;
17306
+ }
17307
+ const value = el.attr(attr);
17308
+ if (typeof value !== "string") {
17309
+ continue;
17310
+ }
17311
+ if (shouldBoundAttr(attr)) {
17312
+ setBoundedAttr(el, attr, value);
17313
+ }
17314
+ }
17315
+ }
17316
+ function restoreBoundedAttr(el, attr, value) {
17317
+ if (typeof value !== "string") {
17318
+ return;
17319
+ }
17320
+ const trimmed = value.trim();
17321
+ if (trimmed === "") {
17322
+ return;
17323
+ }
17324
+ setBoundedAttr(el, attr, value);
17325
+ }
17326
+ function deduplicateImages(html) {
17327
+ const seen = /* @__PURE__ */ new Set();
17328
+ return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
17329
+ if (/\bc\s*=/.test(attrContent)) {
17330
+ return full;
17331
+ }
17332
+ const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
17333
+ const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
17334
+ let src = null;
17335
+ if (srcMatch && srcMatch[2]) {
17336
+ src = srcMatch[2].trim();
17337
+ } else if (srcsetMatch && srcsetMatch[2]) {
17338
+ src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
17339
+ }
17340
+ if (!src) {
17341
+ return full;
17342
+ }
17343
+ if (seen.has(src)) {
17344
+ return "";
17345
+ }
17346
+ seen.add(src);
17347
+ return full;
17348
+ });
17349
+ }
17350
+ function hasAttribute2(node, attr) {
17351
+ return node?.attribs?.[attr] !== void 0;
17352
+ }
17353
+ function hasPictureAncestor(node) {
17354
+ let current = node?.parent;
17355
+ while (current) {
17356
+ if (isElementLikeNode(current) && (current.tagName || "").toLowerCase() === "picture") {
17357
+ return true;
17358
+ }
17359
+ current = current.parent;
17360
+ }
17361
+ return false;
17362
+ }
17363
+ function pictureHasPreservedDescendant(node) {
17364
+ if (!node) {
17365
+ return false;
17366
+ }
17367
+ for (const child of getChildNodes(node)) {
17368
+ if (!isElementLikeNode(child)) {
17369
+ continue;
17370
+ }
17371
+ const tag = (child.tagName || "").toLowerCase();
17372
+ if (tag === "img") {
17373
+ return true;
17374
+ }
17375
+ if (tag === "source" && typeof child.attribs?.src === "string" && child.attribs.src.trim() !== "") {
17376
+ return true;
17377
+ }
17378
+ if (tag === "source" && typeof child.attribs?.srcset === "string" && child.attribs.srcset.trim() !== "") {
17379
+ return true;
17380
+ }
17381
+ if (pictureHasPreservedDescendant(child)) {
17382
+ return true;
17383
+ }
17384
+ }
17385
+ return false;
17386
+ }
17387
+ function isPreservedImageElement(node) {
17388
+ const tag = (node?.tagName || "").toLowerCase();
17389
+ if (tag === "img") {
17390
+ return true;
17391
+ }
17392
+ if (tag === "picture") {
17393
+ return pictureHasPreservedDescendant(node);
17394
+ }
17395
+ if (tag === "source") {
17396
+ const inPicture = hasPictureAncestor(node);
17397
+ const hasSrc = typeof node?.attribs?.src === "string" && node.attribs.src.trim() !== "" || typeof node?.attribs?.srcset === "string" && node.attribs.srcset.trim() !== "";
17398
+ return inPicture && hasSrc;
17399
+ }
17400
+ return false;
17401
+ }
17402
+ function getElementsInReverseDocumentOrder($) {
17403
+ return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
17404
+ }
17405
+ function getNodeDepth(node) {
17406
+ let depth = 0;
17407
+ let current = node.parent;
17408
+ while (current) {
17409
+ depth++;
17410
+ current = current.parent;
17411
+ }
17412
+ return depth;
17413
+ }
17414
+ function getElementsByDepthDescending($) {
17415
+ const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
17416
+ const depths = /* @__PURE__ */ new Map();
17417
+ for (const el of elements) {
17418
+ depths.set(el, getNodeDepth(el));
17419
+ }
17420
+ return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
17421
+ }
17422
+ function flattenExtractionTree($) {
17423
+ for (const node of getElementsInReverseDocumentOrder($)) {
17424
+ const el = $(node);
17425
+ const tag = (node.tagName || "").toLowerCase();
17426
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag) || isPreservedImageElement(node)) {
17427
+ continue;
17428
+ }
17429
+ if (tag === "a" || hasDirectText(node)) {
17430
+ continue;
17431
+ }
17432
+ if (!hasElementChildren(node)) {
17433
+ el.remove();
17434
+ continue;
17435
+ }
17436
+ el.replaceWith(el.contents());
17437
+ }
17438
+ }
17439
+ function hasMarkedAncestor(el, attr) {
17440
+ let current = el[0]?.parent;
17441
+ while (current) {
17442
+ if (!isElementLikeNode(current)) {
17443
+ return false;
17444
+ }
17445
+ if (current.attribs?.[attr] !== void 0) {
17446
+ return true;
17447
+ }
17448
+ current = current.parent;
17449
+ }
17450
+ return false;
17451
+ }
17452
+ function isIndicatorImage(node) {
17453
+ return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
17454
+ }
17455
+ function isIndicatorPictureSource(node) {
17456
+ return (node?.tagName || "").toLowerCase() === "source" && hasPictureAncestor(node) && (hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
17457
+ }
17458
+ function isSemanticIndicator(node) {
17459
+ const tag = (node?.tagName || "").toLowerCase();
17460
+ if (tag === "svg") {
17461
+ return true;
17462
+ }
17463
+ return hasAttribute2(node, "aria-label") || hasAttribute2(node, "title") || hasAttribute2(node, "data-icon") || node?.attribs?.role === "img";
17464
+ }
17465
+ function findIndicatorDescendant(root) {
17466
+ if (!root) {
17467
+ return void 0;
17468
+ }
17469
+ let firstImage;
17470
+ let firstSource;
17471
+ let firstSemantic;
17472
+ const visit = (node) => {
17473
+ if (!isElementLikeNode(node)) {
17474
+ return false;
17475
+ }
17476
+ if (isIndicatorImage(node)) {
17477
+ firstImage = node;
17478
+ return true;
17479
+ }
17480
+ if (firstSource === void 0 && isIndicatorPictureSource(node)) {
17481
+ firstSource = node;
17482
+ }
17483
+ if (firstSemantic === void 0 && isSemanticIndicator(node)) {
17484
+ firstSemantic = node;
17485
+ }
17486
+ for (const child of getChildNodes(node)) {
17487
+ if (visit(child)) {
17488
+ return true;
17489
+ }
17490
+ }
17491
+ return false;
16982
17492
  };
16983
- flatten($.root());
17493
+ for (const child of getChildNodes(root)) {
17494
+ if (visit(child)) {
17495
+ return firstImage;
17496
+ }
17497
+ }
17498
+ return firstImage ?? firstSource ?? firstSemantic;
16984
17499
  }
16985
17500
  function serializeForExtraction($, root) {
16986
17501
  const lines = [];
@@ -17049,7 +17564,7 @@ function serializeForExtraction($, root) {
17049
17564
  lines.push(`${" ".repeat(depth)}</${tagName}>`);
17050
17565
  }
17051
17566
  traverse(root, 0);
17052
- return lines.join("\n");
17567
+ return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
17053
17568
  }
17054
17569
  function isClickable($, el, context) {
17055
17570
  if (context.hasPreMarked) {
@@ -17141,7 +17656,11 @@ function cleanForExtraction(html) {
17141
17656
  }
17142
17657
  });
17143
17658
  flattenExtractionTree($clean);
17144
- return deduplicateImages(serializeForExtraction($clean, $clean.root()[0]));
17659
+ const root = $clean.root()[0];
17660
+ if (root === void 0) {
17661
+ return "";
17662
+ }
17663
+ return deduplicateImages(serializeForExtraction($clean, root));
17145
17664
  }
17146
17665
  function cleanForAction(html) {
17147
17666
  if (!html.trim()) {
@@ -17167,22 +17686,12 @@ function cleanForAction(html) {
17167
17686
  $(`[${clickableMark}]`).each(function markIndicators() {
17168
17687
  const el = $(this);
17169
17688
  const wrapperAttrs = el.attr() || {};
17170
- if (hasTextDeep(el) || hasActionLabel(wrapperAttrs)) {
17689
+ if (hasTextDeepNode(el[0]) || hasActionLabel(wrapperAttrs)) {
17171
17690
  return;
17172
17691
  }
17173
- const imageIndicator = el.find("img[alt], img[src], img[srcset]").first();
17174
- if (imageIndicator.length) {
17175
- imageIndicator.attr(indicatorMark, "1");
17176
- return;
17177
- }
17178
- const pictureSourceIndicator = el.find("picture source[src], picture source[srcset]").first();
17179
- if (pictureSourceIndicator.length) {
17180
- pictureSourceIndicator.attr(indicatorMark, "1");
17181
- return;
17182
- }
17183
- const semanticIndicator = el.find('[aria-label], [title], [data-icon], [role="img"], svg').first();
17184
- if (semanticIndicator.length) {
17185
- semanticIndicator.attr(indicatorMark, "1");
17692
+ const indicatorNode = findIndicatorDescendant(el[0]);
17693
+ if (indicatorNode !== void 0) {
17694
+ $(indicatorNode).attr(indicatorMark, "1");
17186
17695
  }
17187
17696
  });
17188
17697
  $(`[${clickableMark}]`).each(function removeEmptyClickable() {
@@ -17192,7 +17701,7 @@ function cleanForAction(html) {
17192
17701
  if (NATIVE_INTERACTIVE_TAGS.has(tag) || tag === "a") {
17193
17702
  return;
17194
17703
  }
17195
- if (el.children().length > 0 || hasDirectText($, el)) {
17704
+ if (hasElementChildren(node) || hasDirectText(node)) {
17196
17705
  return;
17197
17706
  }
17198
17707
  const wrapperAttrs = el.attr() || {};
@@ -17215,49 +17724,34 @@ function cleanForAction(html) {
17215
17724
  if (!isBoundaryTag(tag)) {
17216
17725
  ancestor.attr(branchMark, "1");
17217
17726
  }
17218
- current = ancestor.parent();
17219
- }
17220
- });
17221
- let changed = true;
17222
- while (changed) {
17223
- changed = false;
17224
- const nodes = [];
17225
- $("*").each(function collectNodes() {
17226
- nodes.push($(this));
17227
- });
17228
- nodes.sort((left, right) => right.parents().length - left.parents().length);
17229
- for (const el of nodes) {
17230
- const node = el[0];
17231
- if (!node) {
17232
- continue;
17233
- }
17234
- const tag = (node.tagName || "").toLowerCase();
17235
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
17236
- continue;
17237
- }
17238
- if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
17239
- continue;
17240
- }
17241
- const insideClickable = el.parents(`[${clickableMark}]`).length > 0;
17242
- const preserveBranch = el.attr(branchMark) !== void 0;
17243
- const hasContent = el.children().length > 0 || hasDirectText($, el);
17244
- if (insideClickable || preserveBranch) {
17245
- if (!hasContent) {
17246
- el.remove();
17247
- } else {
17248
- unwrapActionNode($, el);
17249
- }
17250
- changed = true;
17251
- continue;
17252
- }
17727
+ current = ancestor.parent();
17728
+ }
17729
+ });
17730
+ for (const node of getElementsByDepthDescending($)) {
17731
+ const el = $(node);
17732
+ const tag = (node.tagName || "").toLowerCase();
17733
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
17734
+ continue;
17735
+ }
17736
+ if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
17737
+ continue;
17738
+ }
17739
+ const insideClickable = hasMarkedAncestor(el, clickableMark);
17740
+ const preserveBranch = el.attr(branchMark) !== void 0;
17741
+ const hasContent = hasElementChildren(node) || hasDirectText(node);
17742
+ if (insideClickable || preserveBranch) {
17253
17743
  if (!hasContent) {
17254
17744
  el.remove();
17255
- changed = true;
17256
- continue;
17745
+ } else {
17746
+ unwrapActionNode($, el);
17257
17747
  }
17258
- unwrapActionNode($, el);
17259
- changed = true;
17748
+ continue;
17749
+ }
17750
+ if (!hasContent) {
17751
+ el.remove();
17752
+ continue;
17260
17753
  }
17754
+ unwrapActionNode($, el);
17261
17755
  }
17262
17756
  $.root().find("*").contents().each(function normalizeActionTextNodes() {
17263
17757
  if (this.type !== "text") {
@@ -17337,21 +17831,7 @@ function cleanForAction(html) {
17337
17831
  OPENSTEER_SPARSE_COUNTER_ATTR
17338
17832
  ]);
17339
17833
  if (clickable) {
17340
- for (const attr of [
17341
- "href",
17342
- "role",
17343
- "type",
17344
- "title",
17345
- "placeholder",
17346
- "value",
17347
- "aria-label",
17348
- "aria-labelledby",
17349
- "aria-describedby",
17350
- "aria-expanded",
17351
- "aria-pressed",
17352
- "aria-selected",
17353
- "aria-haspopup"
17354
- ]) {
17834
+ for (const attr of ["href", "role", "type", "title", "placeholder", "value", "aria-label"]) {
17355
17835
  keep.add(attr);
17356
17836
  }
17357
17837
  }
@@ -17971,9 +18451,9 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
17971
18451
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
17972
18452
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
17973
18453
  const attributes = [...authoredAttributes];
17974
- const subtreeHidden = hasAttribute2(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
17975
- const selfHidden = !subtreeHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
17976
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
18454
+ const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
18455
+ const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
18456
+ const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
17977
18457
  if (interactive) {
17978
18458
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
17979
18459
  }
@@ -18291,7 +18771,7 @@ function parseOpacity(value) {
18291
18771
  const parsed = Number.parseFloat(value);
18292
18772
  return Number.isFinite(parsed) ? parsed : Number.NaN;
18293
18773
  }
18294
- function hasAttribute2(attributes, name) {
18774
+ function hasAttribute3(attributes, name) {
18295
18775
  const normalizedName = name.toLowerCase();
18296
18776
  return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
18297
18777
  }
@@ -20252,12 +20732,12 @@ var OpensteerSessionRuntime = class {
20252
20732
  async (timeout) => {
20253
20733
  let descriptor2;
20254
20734
  let data;
20255
- if (input.schema !== void 0) {
20256
- assertValidOpensteerExtractionSchemaRoot(input.schema);
20735
+ if (input.template !== void 0) {
20736
+ assertValidOpensteerExtractionTemplateRoot(input.template);
20257
20737
  const fieldTargets = await timeout.runStep(
20258
20738
  () => compileOpensteerExtractionFieldTargets({
20259
20739
  pageRef,
20260
- schema: input.schema,
20740
+ template: input.template,
20261
20741
  dom: this.requireDom()
20262
20742
  })
20263
20743
  );
@@ -20289,7 +20769,7 @@ var OpensteerSessionRuntime = class {
20289
20769
  () => descriptors.write({
20290
20770
  persist,
20291
20771
  root: payload,
20292
- schemaHash: canonicalJsonString(input.schema),
20772
+ templateHash: canonicalJsonString(input.template),
20293
20773
  sourceUrl: pageInfo.url
20294
20774
  })
20295
20775
  );
@@ -20349,7 +20829,7 @@ var OpensteerSessionRuntime = class {
20349
20829
  artifacts,
20350
20830
  data: {
20351
20831
  ...input.persist === void 0 ? {} : { persist: input.persist },
20352
- ...descriptor?.payload.schemaHash === void 0 ? {} : { schemaHash: descriptor.payload.schemaHash },
20832
+ ...descriptor?.payload.templateHash === void 0 ? {} : { templateHash: descriptor.payload.templateHash },
20353
20833
  data: output.data
20354
20834
  },
20355
20835
  context: buildRuntimeTraceContext({
@@ -24486,7 +24966,7 @@ function toOpensteerResolvedTarget2(target) {
24486
24966
  };
24487
24967
  }
24488
24968
  function normalizeOpensteerError(error) {
24489
- return normalizeThrownOpensteerError(error, "Unknown Opensteer runtime failure");
24969
+ return normalizeThrownOpensteerError2(error, "Unknown Opensteer runtime failure");
24490
24970
  }
24491
24971
  function observationArtifactKindFromManifest(kind) {
24492
24972
  switch (kind) {
@@ -24659,142 +25139,6 @@ function screenshotMediaType(format2) {
24659
25139
  return "image/webp";
24660
25140
  }
24661
25141
  }
24662
-
24663
- // ../runtime-core/src/sdk/semantic-dispatch.ts
24664
- async function dispatchSemanticOperation(runtime, operation, input, options = {}) {
24665
- switch (operation) {
24666
- case "session.open":
24667
- return runtime.open(input, options);
24668
- case "page.list":
24669
- return runtime.listPages(
24670
- input,
24671
- options
24672
- );
24673
- case "page.new":
24674
- return runtime.newPage(input, options);
24675
- case "page.activate":
24676
- return runtime.activatePage(
24677
- input,
24678
- options
24679
- );
24680
- case "page.close":
24681
- return runtime.closePage(
24682
- input,
24683
- options
24684
- );
24685
- case "page.goto":
24686
- return runtime.goto(input, options);
24687
- case "page.evaluate":
24688
- return runtime.evaluate(
24689
- input,
24690
- options
24691
- );
24692
- case "page.add-init-script":
24693
- return runtime.addInitScript(
24694
- input,
24695
- options
24696
- );
24697
- case "page.snapshot":
24698
- return runtime.snapshot(
24699
- input,
24700
- options
24701
- );
24702
- case "dom.click":
24703
- return runtime.click(input, options);
24704
- case "dom.hover":
24705
- return runtime.hover(input, options);
24706
- case "dom.input":
24707
- return runtime.input(input, options);
24708
- case "dom.scroll":
24709
- return runtime.scroll(input, options);
24710
- case "dom.extract":
24711
- return runtime.extract(input, options);
24712
- case "network.query":
24713
- return runtime.queryNetwork(
24714
- input,
24715
- options
24716
- );
24717
- case "network.detail":
24718
- return runtime.getNetworkDetail(
24719
- input,
24720
- options
24721
- );
24722
- case "interaction.capture":
24723
- return runtime.captureInteraction(
24724
- input,
24725
- options
24726
- );
24727
- case "interaction.get":
24728
- return runtime.getInteraction(
24729
- input,
24730
- options
24731
- );
24732
- case "interaction.diff":
24733
- return runtime.diffInteraction(
24734
- input,
24735
- options
24736
- );
24737
- case "interaction.replay":
24738
- return runtime.replayInteraction(
24739
- input,
24740
- options
24741
- );
24742
- case "artifact.read":
24743
- return runtime.readArtifact(
24744
- input,
24745
- options
24746
- );
24747
- case "session.cookies":
24748
- return runtime.getCookies(
24749
- input,
24750
- options
24751
- );
24752
- case "session.storage":
24753
- return runtime.getStorageSnapshot(
24754
- input,
24755
- options
24756
- );
24757
- case "session.state":
24758
- return runtime.getBrowserState(
24759
- input,
24760
- options
24761
- );
24762
- case "session.fetch":
24763
- return runtime.fetch(input, options);
24764
- case "scripts.capture":
24765
- return runtime.captureScripts(
24766
- input,
24767
- options
24768
- );
24769
- case "scripts.beautify":
24770
- return runtime.beautifyScript(
24771
- input,
24772
- options
24773
- );
24774
- case "scripts.deobfuscate":
24775
- return runtime.deobfuscateScript(
24776
- input,
24777
- options
24778
- );
24779
- case "scripts.sandbox":
24780
- return runtime.sandboxScript(
24781
- input,
24782
- options
24783
- );
24784
- case "captcha.solve":
24785
- return runtime.solveCaptcha(
24786
- input,
24787
- options
24788
- );
24789
- case "computer.execute":
24790
- return runtime.computerExecute(
24791
- input,
24792
- options
24793
- );
24794
- case "session.close":
24795
- return runtime.close(options);
24796
- }
24797
- }
24798
25142
  var OpensteerSemanticRestError = class extends Error {
24799
25143
  opensteerError;
24800
25144
  statusCode;
@@ -25322,6 +25666,7 @@ function payloadByteLength(value) {
25322
25666
 
25323
25667
  // src/cloud/session-proxy.ts
25324
25668
  var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
25669
+ var CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS = 1e4;
25325
25670
  var CloudSessionProxy = class {
25326
25671
  rootPath;
25327
25672
  workspace;
@@ -25335,6 +25680,8 @@ var CloudSessionProxy = class {
25335
25680
  automation;
25336
25681
  workspaceStore;
25337
25682
  syncWorkspaceOnClose = false;
25683
+ liveSessionStateEstablished = false;
25684
+ storedInstrumentation = [];
25338
25685
  constructor(cloud, options = {}) {
25339
25686
  this.cloud = cloud;
25340
25687
  this.workspace = options.workspace;
@@ -25364,15 +25711,12 @@ var CloudSessionProxy = class {
25364
25711
  if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
25365
25712
  this.bindClient(persisted);
25366
25713
  }
25367
- if (this.automation) {
25368
- try {
25369
- const sessionInfo = await this.automation.getSessionInfo();
25370
- return {
25371
- ...sessionInfo,
25372
- ...this.workspace === void 0 ? {} : { workspace: this.workspace }
25373
- };
25374
- } catch {
25375
- }
25714
+ const sessionInfo = this.automation ? await this.automation.getSessionInfo().catch(() => void 0) : void 0;
25715
+ if (sessionInfo !== void 0) {
25716
+ return {
25717
+ ...sessionInfo,
25718
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace }
25719
+ };
25376
25720
  }
25377
25721
  return {
25378
25722
  provider: {
@@ -25484,12 +25828,26 @@ var CloudSessionProxy = class {
25484
25828
  return this.invokeSemanticOperation("session.cookies", input);
25485
25829
  }
25486
25830
  async route(input) {
25487
- await this.ensureSession();
25488
- return this.requireAutomation().route(input);
25831
+ const registration = await this.invokeBootstrapInstrumentationOperation(
25832
+ "instrumentation.route",
25833
+ (automation) => automation.route(input)
25834
+ );
25835
+ this.storedInstrumentation.push({
25836
+ kind: "route",
25837
+ input
25838
+ });
25839
+ return registration;
25489
25840
  }
25490
25841
  async interceptScript(input) {
25491
- await this.ensureSession();
25492
- return this.requireAutomation().interceptScript(input);
25842
+ const registration = await this.invokeBootstrapInstrumentationOperation(
25843
+ "instrumentation.intercept-script",
25844
+ (automation) => automation.interceptScript(input)
25845
+ );
25846
+ this.storedInstrumentation.push({
25847
+ kind: "intercept-script",
25848
+ input
25849
+ });
25850
+ return registration;
25493
25851
  }
25494
25852
  async getStorageSnapshot(input = {}) {
25495
25853
  return this.invokeSemanticOperation("session.storage", input);
@@ -25537,6 +25895,8 @@ var CloudSessionProxy = class {
25537
25895
  this.client = void 0;
25538
25896
  this.sessionId = void 0;
25539
25897
  this.semanticGrant = void 0;
25898
+ this.liveSessionStateEstablished = false;
25899
+ this.storedInstrumentation.length = 0;
25540
25900
  if (this.cleanupRootOnClose) {
25541
25901
  await promises.rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
25542
25902
  }
@@ -25564,6 +25924,8 @@ var CloudSessionProxy = class {
25564
25924
  this.automation = void 0;
25565
25925
  this.sessionId = void 0;
25566
25926
  this.semanticGrant = void 0;
25927
+ this.liveSessionStateEstablished = false;
25928
+ this.storedInstrumentation.length = 0;
25567
25929
  if (syncError !== void 0) {
25568
25930
  throw syncError;
25569
25931
  }
@@ -25611,6 +25973,7 @@ var CloudSessionProxy = class {
25611
25973
  };
25612
25974
  await this.writePersistedSession(record);
25613
25975
  this.bindClient(record, session.initialGrants?.semantic);
25976
+ await this.restoreStoredInstrumentation();
25614
25977
  }
25615
25978
  async syncWorkspaceToCloud() {
25616
25979
  if (this.workspace === void 0) {
@@ -25622,6 +25985,7 @@ var CloudSessionProxy = class {
25622
25985
  bindClient(record, initialSemanticGrant) {
25623
25986
  this.sessionId = record.sessionId;
25624
25987
  this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
25988
+ this.liveSessionStateEstablished = false;
25625
25989
  this.client = new OpensteerSemanticRestClient({
25626
25990
  getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
25627
25991
  getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
@@ -25629,6 +25993,19 @@ var CloudSessionProxy = class {
25629
25993
  });
25630
25994
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
25631
25995
  }
25996
+ async restoreStoredInstrumentation() {
25997
+ if (this.storedInstrumentation.length === 0) {
25998
+ return;
25999
+ }
26000
+ const automation = this.requireAutomation();
26001
+ for (const registration of this.storedInstrumentation) {
26002
+ if (registration.kind === "route") {
26003
+ await automation.route(registration.input);
26004
+ } else {
26005
+ await automation.interceptScript(registration.input);
26006
+ }
26007
+ }
26008
+ }
25632
26009
  async ensureWorkspaceStore() {
25633
26010
  if (this.workspaceStore !== void 0) {
25634
26011
  return this.workspaceStore;
@@ -25651,13 +26028,22 @@ var CloudSessionProxy = class {
25651
26028
  async clearPersistedSession() {
25652
26029
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
25653
26030
  }
26031
+ async invalidateSession() {
26032
+ await this.automation?.close().catch(() => void 0);
26033
+ this.automation = void 0;
26034
+ this.client = void 0;
26035
+ this.sessionId = void 0;
26036
+ this.semanticGrant = void 0;
26037
+ this.liveSessionStateEstablished = false;
26038
+ await this.clearPersistedSession();
26039
+ }
25654
26040
  async isReusableCloudSession(sessionId, timeout) {
25655
26041
  try {
25656
26042
  const session = await this.cloud.getSession(sessionId, {
25657
26043
  signal: timeout?.signal,
25658
26044
  timeoutMs: timeout?.remainingMs()
25659
26045
  });
25660
- return session.status !== "closed" && session.status !== "failed";
26046
+ return isReusableCloudSessionState(session);
25661
26047
  } catch (error) {
25662
26048
  if (isMissingCloudSessionError(error)) {
25663
26049
  return false;
@@ -25706,26 +26092,79 @@ var CloudSessionProxy = class {
25706
26092
  try {
25707
26093
  await this.ensureSemanticGrant(true);
25708
26094
  return true;
25709
- } catch {
26095
+ } catch (refreshError) {
26096
+ if (await this.resetStaleSession(refreshError)) {
26097
+ throw refreshError;
26098
+ }
25710
26099
  return false;
25711
26100
  }
25712
26101
  }
25713
26102
  async invokeSemanticOperation(operation, input, sessionInit = {}) {
25714
- return this.runOperationWithPolicy(operation, async (timeout) => {
26103
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
25715
26104
  await this.ensureSession(sessionInit, timeout);
25716
26105
  await this.ensureSemanticGrant(false, timeout);
25717
- return this.requireClient().invoke(operation, input, {
26106
+ const output = await this.requireClient().invoke(operation, input, {
25718
26107
  signal: timeout.signal,
25719
26108
  timeoutMs: timeout.remainingMs()
25720
26109
  });
26110
+ this.noteSuccessfulLiveOperation(operation);
26111
+ return output;
25721
26112
  });
25722
26113
  }
25723
26114
  async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
25724
- return this.runOperationWithPolicy(operation, async (timeout) => {
26115
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
25725
26116
  await this.ensureSession(sessionInit, timeout);
25726
- return invoke(this.requireAutomation());
26117
+ const output = await invoke(this.requireAutomation());
26118
+ this.noteSuccessfulLiveOperation(operation);
26119
+ return output;
26120
+ });
26121
+ }
26122
+ async invokeBootstrapInstrumentationOperation(operation, invoke) {
26123
+ let recovered = false;
26124
+ while (true) {
26125
+ try {
26126
+ await this.ensureSession();
26127
+ return await invoke(this.requireAutomation());
26128
+ } catch (error) {
26129
+ const stale = await this.resetStaleSession(error);
26130
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
26131
+ throw error;
26132
+ }
26133
+ recovered = true;
26134
+ }
26135
+ }
26136
+ }
26137
+ async runOperationWithSessionRecovery(operation, invoke) {
26138
+ return this.runOperationWithPolicy(operation, async (timeout) => {
26139
+ let recovered = false;
26140
+ while (true) {
26141
+ try {
26142
+ return await invoke(timeout);
26143
+ } catch (error) {
26144
+ const stale = await this.resetStaleSession(error);
26145
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
26146
+ throw error;
26147
+ }
26148
+ recovered = true;
26149
+ }
26150
+ }
25727
26151
  });
25728
26152
  }
26153
+ async resetStaleSession(error) {
26154
+ if (!isRecoverableCloudSessionError(error)) {
26155
+ return false;
26156
+ }
26157
+ await this.invalidateSession();
26158
+ return true;
26159
+ }
26160
+ canRecoverWithFreshSession(operation) {
26161
+ return !this.liveSessionStateEstablished && isBootstrapRecoveryOperation(operation);
26162
+ }
26163
+ noteSuccessfulLiveOperation(operation) {
26164
+ if (operation === "session.open" || operation === "page.list" || operation === "page.new" || operation === "page.activate" || operation === "page.close" || operation === "page.goto" || operation === "page.evaluate" || operation === "page.add-init-script" || operation === "page.snapshot" || operation === "dom.click" || operation === "dom.hover" || operation === "dom.input" || operation === "dom.scroll" || operation === "dom.extract" || operation === "network.query" || operation === "network.detail" || operation === "interaction.capture" || operation === "interaction.get" || operation === "interaction.diff" || operation === "interaction.replay" || operation === "scripts.capture" || operation === "artifact.read" || operation === "scripts.beautify" || operation === "scripts.deobfuscate" || operation === "scripts.sandbox" || operation === "captcha.solve" || operation === "session.cookies" || operation === "session.storage" || operation === "session.state" || operation === "session.fetch" || operation === "computer.execute") {
26165
+ this.liveSessionStateEstablished = true;
26166
+ }
26167
+ }
25729
26168
  async runOperationWithPolicy(operation, invoke) {
25730
26169
  return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
25731
26170
  }
@@ -25749,8 +26188,29 @@ function assertSupportedCloudBrowserMode(browser) {
25749
26188
  }
25750
26189
  }
25751
26190
  function isMissingCloudSessionError(error) {
26191
+ if (error instanceof OpensteerCloudRequestError) {
26192
+ return error.statusCode === 404 && (error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND");
26193
+ }
25752
26194
  return error instanceof Error && /\b404\b/.test(error.message);
25753
26195
  }
26196
+ function isRecoverableCloudSessionError(error) {
26197
+ if (!(error instanceof OpensteerCloudRequestError)) {
26198
+ return false;
26199
+ }
26200
+ if (error.statusCode === 404) {
26201
+ return error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND";
26202
+ }
26203
+ return error.statusCode === 409 && error.code === "CLOUD_SESSION_STALE";
26204
+ }
26205
+ function isBootstrapRecoveryOperation(operation) {
26206
+ return operation === "session.open" || operation === "instrumentation.route" || operation === "instrumentation.intercept-script";
26207
+ }
26208
+ function isReusableCloudSessionState(session) {
26209
+ if (session.status === "closing" || session.status === "closed" || session.status === "failed") {
26210
+ return false;
26211
+ }
26212
+ return !(typeof session.expiresAt === "number" && session.expiresAt <= Date.now() + CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS);
26213
+ }
25754
26214
  function isLoopbackBaseUrl(baseUrl) {
25755
26215
  let url;
25756
26216
  try {
@@ -25798,39 +26258,6 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
25798
26258
  );
25799
26259
  }
25800
26260
  };
25801
- var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
25802
- constructor(options) {
25803
- const rootPath = options.rootPath ?? path10__default.default.resolve(options.rootDir ?? process.cwd());
25804
- const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
25805
- const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
25806
- assertSupportedEngineOptions({
25807
- engineName,
25808
- ...options.browser === void 0 ? {} : { browser: options.browser },
25809
- ...options.context === void 0 ? {} : { context: options.context }
25810
- });
25811
- super(
25812
- buildSharedRuntimeOptions({
25813
- name: options.name,
25814
- ...options.rootDir === void 0 ? {} : { rootDir: options.rootDir },
25815
- rootPath,
25816
- ...options.environment === void 0 ? {} : { environment: options.environment },
25817
- ...options.browser === void 0 ? {} : { browser: options.browser },
25818
- ...options.launch === void 0 ? {} : { launch: options.launch },
25819
- ...options.context === void 0 ? {} : { context: options.context },
25820
- engineName,
25821
- ...options.engine === void 0 ? {} : { engine: options.engine },
25822
- ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
25823
- ...options.policy === void 0 ? {} : { policy: options.policy },
25824
- ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
25825
- ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
25826
- cleanupRootOnClose,
25827
- ...options.observability === void 0 ? {} : { observability: options.observability },
25828
- ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
25829
- ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
25830
- })
25831
- );
25832
- }
25833
- };
25834
26261
  function buildSharedRuntimeOptions(input) {
25835
26262
  const ownership = resolveOwnership(input.browser);
25836
26263
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
@@ -25945,6 +26372,25 @@ var SessionCookieJar = class {
25945
26372
  return this.cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
25946
26373
  }
25947
26374
  };
26375
+ function createSdkProtocolError(error, fallbackMessage) {
26376
+ const normalized = normalizeThrownOpensteerError(error, fallbackMessage);
26377
+ return new OpensteerProtocolError(normalized.code, normalized.message, {
26378
+ cause: error,
26379
+ retriable: normalized.retriable,
26380
+ ...normalized.capability === void 0 ? {} : { capability: normalized.capability },
26381
+ ...normalized.details === void 0 ? {} : { details: normalized.details }
26382
+ });
26383
+ }
26384
+ async function wrapSdkError(operation, fn) {
26385
+ try {
26386
+ return await fn();
26387
+ } catch (error) {
26388
+ if (isOpensteerProtocolError(error)) {
26389
+ throw error;
26390
+ }
26391
+ throw createSdkProtocolError(error, `${operation} failed`);
26392
+ }
26393
+ }
25948
26394
  var Opensteer = class {
25949
26395
  runtime;
25950
26396
  browserManager;
@@ -25952,210 +26398,265 @@ var Opensteer = class {
25952
26398
  dom;
25953
26399
  network;
25954
26400
  constructor(options = {}) {
25955
- const environment = resolveOpensteerEnvironment(options.rootDir);
25956
- const { provider, engineName, ...runtimeOptions } = options;
25957
- const runtimeConfig = resolveOpensteerRuntimeConfig({
25958
- ...provider === void 0 ? {} : { provider },
25959
- environment
25960
- });
25961
- if (runtimeConfig.provider.mode === "cloud") {
25962
- this.browserManager = void 0;
25963
- this.runtime = createOpensteerSemanticRuntime({
25964
- ...provider === void 0 ? {} : { provider },
25965
- ...engineName === void 0 ? {} : { engine: engineName },
25966
- environment,
25967
- runtimeOptions
25968
- });
25969
- this.browser = createUnsupportedBrowserController();
25970
- } else {
25971
- this.browserManager = new OpensteerBrowserManager({
25972
- ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
25973
- ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
25974
- ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
25975
- ...engineName === void 0 ? {} : { engineName },
25976
- environment,
25977
- ...runtimeOptions.browser === void 0 ? {} : { browser: runtimeOptions.browser },
25978
- ...runtimeOptions.launch === void 0 ? {} : { launch: runtimeOptions.launch },
25979
- ...runtimeOptions.context === void 0 ? {} : { context: runtimeOptions.context }
25980
- });
25981
- this.runtime = createOpensteerSemanticRuntime({
26401
+ try {
26402
+ const environment = resolveOpensteerEnvironment(options.rootDir);
26403
+ const { provider, engineName, ...runtimeOptions } = options;
26404
+ const runtimeConfig = resolveOpensteerRuntimeConfig({
25982
26405
  ...provider === void 0 ? {} : { provider },
25983
- ...engineName === void 0 ? {} : { engine: engineName },
25984
- environment,
25985
- runtimeOptions: {
25986
- ...runtimeOptions,
25987
- rootPath: this.browserManager.rootPath,
25988
- cleanupRootOnClose: this.browserManager.cleanupRootOnDisconnect
25989
- }
26406
+ environment
25990
26407
  });
25991
- this.browser = {
25992
- status: () => this.browserManager.status(),
25993
- clone: (input) => this.browserManager.clonePersistentBrowser(input),
25994
- reset: () => this.browserManager.reset(),
25995
- delete: () => this.browserManager.delete()
26408
+ if (runtimeConfig.provider.mode === "cloud") {
26409
+ this.browserManager = void 0;
26410
+ this.runtime = createOpensteerSemanticRuntime({
26411
+ ...provider === void 0 ? {} : { provider },
26412
+ ...engineName === void 0 ? {} : { engine: engineName },
26413
+ environment,
26414
+ runtimeOptions
26415
+ });
26416
+ this.browser = createUnsupportedBrowserController();
26417
+ } else {
26418
+ this.browserManager = new OpensteerBrowserManager({
26419
+ ...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
26420
+ ...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
26421
+ ...runtimeOptions.workspace === void 0 ? {} : { workspace: runtimeOptions.workspace },
26422
+ ...engineName === void 0 ? {} : { engineName },
26423
+ environment,
26424
+ ...runtimeOptions.browser === void 0 ? {} : { browser: runtimeOptions.browser },
26425
+ ...runtimeOptions.launch === void 0 ? {} : { launch: runtimeOptions.launch },
26426
+ ...runtimeOptions.context === void 0 ? {} : { context: runtimeOptions.context }
26427
+ });
26428
+ this.runtime = createOpensteerSemanticRuntime({
26429
+ ...provider === void 0 ? {} : { provider },
26430
+ ...engineName === void 0 ? {} : { engine: engineName },
26431
+ environment,
26432
+ runtimeOptions: {
26433
+ ...runtimeOptions,
26434
+ rootPath: this.browserManager.rootPath,
26435
+ cleanupRootOnClose: this.browserManager.cleanupRootOnDisconnect
26436
+ }
26437
+ });
26438
+ this.browser = {
26439
+ status: () => wrapSdkError("browser.status", () => this.browserManager.status()),
26440
+ clone: (input) => wrapSdkError("browser.clone", () => this.browserManager.clonePersistentBrowser(input)),
26441
+ reset: () => wrapSdkError("browser.reset", () => this.browserManager.reset()),
26442
+ delete: () => wrapSdkError("browser.delete", () => this.browserManager.delete())
26443
+ };
26444
+ }
26445
+ this.dom = {
26446
+ click: (input) => this.click(input),
26447
+ hover: (input) => this.hover(input),
26448
+ input: (input) => this.input(input),
26449
+ scroll: (input) => this.scroll(input)
26450
+ };
26451
+ this.network = {
26452
+ query: (input = {}) => wrapSdkError("network.query", () => this.runtime.queryNetwork(input)),
26453
+ detail: (recordId, options2) => wrapSdkError(
26454
+ "network.detail",
26455
+ () => this.runtime.getNetworkDetail({ recordId, ...options2 })
26456
+ )
25996
26457
  };
26458
+ } catch (error) {
26459
+ if (isOpensteerProtocolError(error)) {
26460
+ throw error;
26461
+ }
26462
+ throw createSdkProtocolError(error, "Failed to initialize Opensteer");
25997
26463
  }
25998
- this.dom = {
25999
- click: (input) => this.click(input),
26000
- hover: (input) => this.hover(input),
26001
- input: (input) => this.input(input),
26002
- scroll: (input) => this.scroll(input)
26003
- };
26004
- this.network = {
26005
- query: (input = {}) => this.runtime.queryNetwork(input),
26006
- detail: (recordId, options2) => this.runtime.getNetworkDetail({ recordId, ...options2 })
26007
- };
26008
26464
  }
26009
26465
  async open(input = {}) {
26010
- return this.runtime.open(typeof input === "string" ? { url: input } : input);
26466
+ return wrapSdkError(
26467
+ "session.open",
26468
+ () => this.runtime.open(typeof input === "string" ? { url: input } : input)
26469
+ );
26011
26470
  }
26012
26471
  async info() {
26013
- return this.runtime.info();
26472
+ return wrapSdkError("session.info", () => this.runtime.info());
26014
26473
  }
26015
26474
  async listPages(input = {}) {
26016
- return this.runtime.listPages(input);
26475
+ return wrapSdkError("page.list", () => this.runtime.listPages(input));
26017
26476
  }
26018
26477
  async newPage(input = {}) {
26019
- return this.runtime.newPage(input);
26478
+ return wrapSdkError("page.new", () => this.runtime.newPage(input));
26020
26479
  }
26021
26480
  async activatePage(input) {
26022
- return this.runtime.activatePage(input);
26481
+ return wrapSdkError("page.activate", () => this.runtime.activatePage(input));
26023
26482
  }
26024
26483
  async closePage(input = {}) {
26025
- return this.runtime.closePage(input);
26484
+ return wrapSdkError("page.close", () => this.runtime.closePage(input));
26026
26485
  }
26027
26486
  async goto(url, options = {}) {
26028
- return this.runtime.goto({
26029
- url,
26030
- ...options
26031
- });
26487
+ return wrapSdkError(
26488
+ "page.goto",
26489
+ () => this.runtime.goto({
26490
+ url,
26491
+ ...options
26492
+ })
26493
+ );
26032
26494
  }
26033
26495
  async evaluate(input) {
26034
- const normalized = typeof input === "string" ? {
26035
- script: input
26036
- } : input;
26037
- return (await this.runtime.evaluate(normalized)).value;
26496
+ return wrapSdkError("page.evaluate", async () => {
26497
+ const normalized = typeof input === "string" ? {
26498
+ script: input
26499
+ } : input;
26500
+ return (await this.runtime.evaluate(normalized)).value;
26501
+ });
26038
26502
  }
26039
26503
  async addInitScript(input) {
26040
- return this.runtime.addInitScript(
26041
- typeof input === "string" ? {
26042
- script: input
26043
- } : input
26504
+ return wrapSdkError(
26505
+ "page.addInitScript",
26506
+ () => this.runtime.addInitScript(
26507
+ typeof input === "string" ? {
26508
+ script: input
26509
+ } : input
26510
+ )
26044
26511
  );
26045
26512
  }
26046
26513
  async click(input) {
26047
- const { button, clickCount, modifiers, ...target } = input;
26048
- return this.runtime.click({
26049
- ...normalizeTargetOptions(target),
26050
- ...button === void 0 ? {} : { button },
26051
- ...clickCount === void 0 ? {} : { clickCount },
26052
- ...modifiers === void 0 ? {} : { modifiers }
26514
+ return wrapSdkError("dom.click", () => {
26515
+ const { button, clickCount, modifiers, ...target } = input;
26516
+ return this.runtime.click({
26517
+ ...normalizeTargetOptions(target),
26518
+ ...button === void 0 ? {} : { button },
26519
+ ...clickCount === void 0 ? {} : { clickCount },
26520
+ ...modifiers === void 0 ? {} : { modifiers }
26521
+ });
26053
26522
  });
26054
26523
  }
26055
26524
  async hover(input) {
26056
- return this.runtime.hover(normalizeTargetOptions(input));
26525
+ return wrapSdkError("dom.hover", () => this.runtime.hover(normalizeTargetOptions(input)));
26057
26526
  }
26058
26527
  async input(input) {
26059
- return this.runtime.input({
26060
- ...normalizeTargetOptions(input),
26061
- text: input.text,
26062
- ...input.pressEnter === void 0 ? {} : { pressEnter: input.pressEnter }
26063
- });
26528
+ return wrapSdkError(
26529
+ "dom.input",
26530
+ () => this.runtime.input({
26531
+ ...normalizeTargetOptions(input),
26532
+ text: input.text,
26533
+ ...input.pressEnter === void 0 ? {} : { pressEnter: input.pressEnter }
26534
+ })
26535
+ );
26064
26536
  }
26065
26537
  async scroll(input) {
26066
- return this.runtime.scroll({
26067
- ...normalizeTargetOptions(input),
26068
- direction: input.direction,
26069
- amount: input.amount
26070
- });
26538
+ return wrapSdkError(
26539
+ "dom.scroll",
26540
+ () => this.runtime.scroll({
26541
+ ...normalizeTargetOptions(input),
26542
+ direction: input.direction,
26543
+ amount: input.amount
26544
+ })
26545
+ );
26071
26546
  }
26072
26547
  async extract(input) {
26073
- return (await this.runtime.extract(input)).data;
26548
+ return wrapSdkError("extract", async () => (await this.runtime.extract(input)).data);
26074
26549
  }
26075
26550
  async waitForPage(input = {}) {
26076
- const baseline = new Set((await this.runtime.listPages()).pages.map((page) => page.pageRef));
26077
- const timeoutAt = Date.now() + (input.timeoutMs ?? 3e4);
26078
- const pollIntervalMs = input.pollIntervalMs ?? 100;
26079
- while (true) {
26080
- const match = (await this.runtime.listPages()).pages.find((page) => {
26081
- if (baseline.has(page.pageRef)) {
26082
- return false;
26083
- }
26084
- if (input.openerPageRef !== void 0 && page.openerPageRef !== input.openerPageRef) {
26085
- return false;
26551
+ return wrapSdkError("page.waitForPage", async () => {
26552
+ const baseline = new Set((await this.runtime.listPages()).pages.map((page) => page.pageRef));
26553
+ const timeoutAt = Date.now() + (input.timeoutMs ?? 3e4);
26554
+ const pollIntervalMs = input.pollIntervalMs ?? 100;
26555
+ while (true) {
26556
+ const match = (await this.runtime.listPages()).pages.find((page) => {
26557
+ if (baseline.has(page.pageRef)) {
26558
+ return false;
26559
+ }
26560
+ if (input.openerPageRef !== void 0 && page.openerPageRef !== input.openerPageRef) {
26561
+ return false;
26562
+ }
26563
+ if (input.urlIncludes !== void 0 && !page.url.includes(input.urlIncludes)) {
26564
+ return false;
26565
+ }
26566
+ return true;
26567
+ });
26568
+ if (match !== void 0) {
26569
+ return match;
26086
26570
  }
26087
- if (input.urlIncludes !== void 0 && !page.url.includes(input.urlIncludes)) {
26088
- return false;
26571
+ if (Date.now() >= timeoutAt) {
26572
+ throw new OpensteerProtocolError("timeout", "waitForPage timed out");
26089
26573
  }
26090
- return true;
26091
- });
26092
- if (match !== void 0) {
26093
- return match;
26574
+ await delay3(pollIntervalMs);
26094
26575
  }
26095
- if (Date.now() >= timeoutAt) {
26096
- throw new Error("waitForPage timed out");
26097
- }
26098
- await delay3(pollIntervalMs);
26099
- }
26100
- }
26101
- async snapshot(mode = "action") {
26102
- return (await this.runtime.snapshot({ mode })).html;
26576
+ });
26103
26577
  }
26104
26578
  async cookies(domain) {
26105
- return new SessionCookieJar(
26106
- await this.runtime.getCookies(domain === void 0 ? {} : { domain })
26579
+ return wrapSdkError(
26580
+ "session.cookies",
26581
+ async () => new SessionCookieJar(await this.runtime.getCookies(domain === void 0 ? {} : { domain }))
26107
26582
  );
26108
26583
  }
26109
26584
  async storage(domain, type = "local") {
26110
- const snapshot = await this.runtime.getStorageSnapshot(domain === void 0 ? {} : { domain });
26111
- const domainSnapshot = pickStorageDomainSnapshot(snapshot, domain);
26112
- if (domainSnapshot === void 0) {
26113
- return {};
26114
- }
26115
- const entries = type === "local" ? domainSnapshot.localStorage : domainSnapshot.sessionStorage;
26116
- return Object.fromEntries(entries.map((entry) => [entry.key, entry.value]));
26585
+ return wrapSdkError("session.storage", async () => {
26586
+ const snapshot = await this.runtime.getStorageSnapshot(
26587
+ domain === void 0 ? {} : { domain }
26588
+ );
26589
+ const domainSnapshot = pickStorageDomainSnapshot(snapshot, domain);
26590
+ if (domainSnapshot === void 0) {
26591
+ return {};
26592
+ }
26593
+ const entries = type === "local" ? domainSnapshot.localStorage : domainSnapshot.sessionStorage;
26594
+ return Object.fromEntries(entries.map((entry) => [entry.key, entry.value]));
26595
+ });
26117
26596
  }
26118
26597
  async state(domain) {
26119
- return this.runtime.getBrowserState(domain === void 0 ? {} : { domain });
26598
+ return wrapSdkError(
26599
+ "session.state",
26600
+ () => this.runtime.getBrowserState(domain === void 0 ? {} : { domain })
26601
+ );
26120
26602
  }
26121
26603
  async fetch(url, options = {}) {
26122
- const input = buildFetchInput(url, options);
26123
- const result = await this.runtime.fetch(input);
26124
- if (result.response === void 0) {
26125
- throw new Error(result.note ?? `session.fetch did not produce a response for ${url}`);
26126
- }
26127
- return toResponse(result.response);
26604
+ return wrapSdkError("session.fetch", async () => {
26605
+ const input = buildFetchInput(url, options);
26606
+ const result = await this.runtime.fetch(input);
26607
+ if (result.response === void 0) {
26608
+ throw new OpensteerProtocolError(
26609
+ "operation-failed",
26610
+ result.note ?? `session.fetch did not produce a response for ${url}`
26611
+ );
26612
+ }
26613
+ return toResponse(result.response);
26614
+ });
26128
26615
  }
26129
26616
  async computerExecute(input) {
26130
- return this.runtime.computerExecute(input);
26617
+ return wrapSdkError("session.computerExecute", () => this.runtime.computerExecute(input));
26131
26618
  }
26132
26619
  async route(input) {
26133
- return this.requireOwnedInstrumentationRuntime("route").route(input);
26620
+ return wrapSdkError(
26621
+ "session.route",
26622
+ () => this.requireOwnedInstrumentationRuntime("route").route(input)
26623
+ );
26134
26624
  }
26135
26625
  async interceptScript(input) {
26136
- return this.requireOwnedInstrumentationRuntime("interceptScript").interceptScript(input);
26626
+ return wrapSdkError(
26627
+ "session.interceptScript",
26628
+ () => this.requireOwnedInstrumentationRuntime("interceptScript").interceptScript(input)
26629
+ );
26137
26630
  }
26138
26631
  async close() {
26139
- if (this.browserManager === void 0 || this.browserManager.mode === "temporary") {
26140
- return this.runtime.close();
26141
- }
26142
- const output = await this.runtime.close();
26143
- await this.browserManager.close();
26144
- return output;
26632
+ return wrapSdkError("session.close", async () => {
26633
+ if (this.browserManager === void 0 || this.browserManager.mode === "temporary") {
26634
+ return this.runtime.close();
26635
+ }
26636
+ const output = await this.runtime.close();
26637
+ await this.browserManager.close();
26638
+ return output;
26639
+ });
26145
26640
  }
26146
26641
  async disconnect() {
26147
- await this.runtime.disconnect();
26642
+ return wrapSdkError("session.disconnect", () => this.runtime.disconnect());
26148
26643
  }
26149
26644
  requireOwnedInstrumentationRuntime(method) {
26150
26645
  if (typeof this.runtime.route === "function" && typeof this.runtime.interceptScript === "function") {
26151
26646
  return this.runtime;
26152
26647
  }
26153
- throw new Error(`${method}() is not available for this session runtime.`);
26648
+ throw new OpensteerProtocolError(
26649
+ "unsupported-operation",
26650
+ `${method}() is not available for this session runtime.`
26651
+ );
26154
26652
  }
26155
26653
  };
26156
26654
  function createUnsupportedBrowserController() {
26157
26655
  const fail = async () => {
26158
- throw new Error("browser.* helpers are only available in local mode.");
26656
+ throw new OpensteerProtocolError(
26657
+ "unsupported-operation",
26658
+ "browser.* helpers are only available in local mode."
26659
+ );
26159
26660
  };
26160
26661
  return {
26161
26662
  status: fail,
@@ -26168,7 +26669,10 @@ function normalizeTargetOptions(input) {
26168
26669
  const hasElement = input.element !== void 0;
26169
26670
  const hasSelector = input.selector !== void 0;
26170
26671
  if (hasElement && hasSelector) {
26171
- throw new Error("Specify exactly one of element, selector, or persist.");
26672
+ throw new OpensteerProtocolError(
26673
+ "invalid-argument",
26674
+ "Specify exactly one of element, selector, or persist."
26675
+ );
26172
26676
  }
26173
26677
  if (hasElement) {
26174
26678
  return {
@@ -26191,7 +26695,10 @@ function normalizeTargetOptions(input) {
26191
26695
  };
26192
26696
  }
26193
26697
  if (input.persist === void 0) {
26194
- throw new Error("Specify exactly one of element, selector, or persist.");
26698
+ throw new OpensteerProtocolError(
26699
+ "invalid-argument",
26700
+ "Specify exactly one of element, selector, or persist."
26701
+ );
26195
26702
  }
26196
26703
  return {
26197
26704
  target: {
@@ -26251,7 +26758,6 @@ function delay3(ms) {
26251
26758
  return new Promise((resolve4) => setTimeout(resolve4, ms));
26252
26759
  }
26253
26760
 
26254
- exports.CloudSessionProxy = CloudSessionProxy;
26255
26761
  exports.DEFAULT_OPENSTEER_ENGINE = DEFAULT_OPENSTEER_ENGINE;
26256
26762
  exports.DEFERRED_MATCH_ATTR_KEYS = DEFERRED_MATCH_ATTR_KEYS;
26257
26763
  exports.ElementPathError = ElementPathError;
@@ -26264,8 +26770,7 @@ exports.Opensteer = Opensteer;
26264
26770
  exports.OpensteerAttachAmbiguousError = OpensteerAttachAmbiguousError;
26265
26771
  exports.OpensteerBrowserManager = OpensteerBrowserManager;
26266
26772
  exports.OpensteerCloudClient = OpensteerCloudClient;
26267
- exports.OpensteerRuntime = OpensteerRuntime;
26268
- exports.OpensteerSessionRuntime = OpensteerSessionRuntime2;
26773
+ exports.OpensteerProtocolError = OpensteerProtocolError;
26269
26774
  exports.STABLE_PRIMARY_ATTR_KEYS = STABLE_PRIMARY_ATTR_KEYS;
26270
26775
  exports.assertProviderSupportsEngine = assertProviderSupportsEngine;
26271
26776
  exports.buildArrayFieldPathCandidates = buildArrayFieldPathCandidates;
@@ -26285,7 +26790,6 @@ exports.createDomRuntime = createDomRuntime;
26285
26790
  exports.createFilesystemOpensteerWorkspace = createFilesystemOpensteerWorkspace;
26286
26791
  exports.createObservationStore = createObservationStore;
26287
26792
  exports.createOpensteerExtractionDescriptorStore = createOpensteerExtractionDescriptorStore;
26288
- exports.createOpensteerSemanticRuntime = createOpensteerSemanticRuntime;
26289
26793
  exports.defaultFallbackPolicy = defaultFallbackPolicy;
26290
26794
  exports.defaultPolicy = defaultPolicy;
26291
26795
  exports.defaultRetryPolicy = defaultRetryPolicy;
@@ -26293,10 +26797,10 @@ exports.defaultSettlePolicy = defaultSettlePolicy;
26293
26797
  exports.defaultTimeoutPolicy = defaultTimeoutPolicy;
26294
26798
  exports.delayWithSignal = delayWithSignal;
26295
26799
  exports.discoverLocalCdpBrowsers = discoverLocalCdpBrowsers;
26296
- exports.dispatchSemanticOperation = dispatchSemanticOperation;
26297
26800
  exports.hashDomDescriptorPersist = hashDomDescriptorPersist;
26298
26801
  exports.inspectCdpEndpoint = inspectCdpEndpoint;
26299
26802
  exports.isCurrentUrlField = isCurrentUrlField;
26803
+ exports.isOpensteerProtocolError = isOpensteerProtocolError;
26300
26804
  exports.isValidCssAttributeKey = isValidCssAttributeKey;
26301
26805
  exports.listLocalChromeProfiles = listLocalChromeProfiles;
26302
26806
  exports.manifestToExternalBinaryLocation = manifestToExternalBinaryLocation;
@@ -26305,6 +26809,7 @@ exports.normalizeObservabilityConfig = normalizeObservabilityConfig;
26305
26809
  exports.normalizeOpensteerEngineName = normalizeOpensteerEngineName;
26306
26810
  exports.normalizeOpensteerProviderMode = normalizeOpensteerProviderMode;
26307
26811
  exports.normalizeWorkspaceId = normalizeWorkspaceId;
26812
+ exports.opensteerErrorCodes = opensteerErrorCodes;
26308
26813
  exports.parseDomDescriptorRecord = parseDomDescriptorRecord;
26309
26814
  exports.parseExtractionDescriptorRecord = parseExtractionDescriptorRecord;
26310
26815
  exports.readPersistedCloudSessionRecord = readPersistedCloudSessionRecord;