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
@@ -1,5 +1,5 @@
1
- import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, OpensteerBrowserManager, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext } from './chunk-GSCQQKZZ.js';
2
- import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, sha256Hex, resolveBrandUserDataDir, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-T5P2QGZ3.js';
1
+ import { objectSchema, stringSchema, literalSchema, JSON_SCHEMA_DRAFT_2020_12, integerSchema, enumSchema, oneOfSchema, numberSchema, arraySchema, recordSchema, defineSchema, opensteerCapabilitySetSchema, opensteerErrorSchema, OpensteerProtocolError, resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, createOpensteerError, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext, OpensteerBrowserManager, isOpensteerProtocolError, toOpensteerError } from './chunk-3XBQRZZC.js';
2
+ import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, resolveBrandUserDataDir, sha256Hex, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-T5P2QGZ3.js';
3
3
  import { selectAll } from 'css-select';
4
4
  import { createHash, randomUUID, pbkdf2Sync, createDecipheriv } from 'crypto';
5
5
  import { existsSync, readFileSync } from 'fs';
@@ -7,7 +7,7 @@ import path2, { join } from 'path';
7
7
  import { promisify } from 'util';
8
8
  import { gzip as gzip$1 } from 'zlib';
9
9
  import { execFile as execFile$1, spawn } from 'child_process';
10
- import { rm, mkdtemp, writeFile, readFile, copyFile } from 'fs/promises';
10
+ import { rm, mkdtemp, copyFile, writeFile, readFile } from 'fs/promises';
11
11
  import { tmpdir } from 'os';
12
12
  import { AsyncLocalStorage } from 'async_hooks';
13
13
  import sharp from 'sharp';
@@ -208,9 +208,6 @@ function isBrowserCoreError(value) {
208
208
  return value instanceof BrowserCoreError;
209
209
  }
210
210
 
211
- // ../browser-core/src/cdp-visual-stability.ts
212
- var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
213
-
214
211
  // ../browser-core/src/post-load-tracker.ts
215
212
  var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
216
213
 
@@ -240,21 +237,6 @@ var DEFAULT_SETTLE_DELAYS = {
240
237
  "dom-action": 100,
241
238
  snapshot: 0
242
239
  };
243
- var defaultSnapshotSettleObserver = {
244
- async settle(input) {
245
- if (input.trigger !== "snapshot") {
246
- return false;
247
- }
248
- await input.engine.waitForVisualStability({
249
- pageRef: input.pageRef,
250
- ...input.remainingMs === void 0 ? {} : { timeoutMs: input.remainingMs },
251
- settleMs: DEFAULT_VISUAL_STABILITY_SETTLE_MS,
252
- scope: "visible-frames"
253
- });
254
- return true;
255
- }
256
- };
257
- Object.freeze(defaultSnapshotSettleObserver);
258
240
  var DOM_ACTION_VISUAL_STABILITY_PROFILES = {
259
241
  "dom.click": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
260
242
  "dom.input": { settleMs: 750, scope: "visible-frames", timeoutMs: 7e3 },
@@ -307,6 +289,7 @@ var defaultNavigationSettleObserver = {
307
289
  return false;
308
290
  }
309
291
  try {
292
+ const startedAt = Date.now();
310
293
  await input.engine.waitForPostLoadQuiet({
311
294
  pageRef: input.pageRef,
312
295
  timeoutMs: effectiveTimeout,
@@ -314,9 +297,13 @@ var defaultNavigationSettleObserver = {
314
297
  captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
315
298
  signal: input.signal
316
299
  });
300
+ const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
301
+ if (visualTimeout <= 0) {
302
+ return true;
303
+ }
317
304
  await input.engine.waitForVisualStability({
318
305
  pageRef: input.pageRef,
319
- timeoutMs: effectiveTimeout,
306
+ timeoutMs: visualTimeout,
320
307
  settleMs: profile.settleMs,
321
308
  scope: profile.scope
322
309
  });
@@ -328,7 +315,6 @@ var defaultNavigationSettleObserver = {
328
315
  };
329
316
  Object.freeze(defaultNavigationSettleObserver);
330
317
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
331
- defaultSnapshotSettleObserver,
332
318
  defaultDomActionSettleObserver,
333
319
  defaultNavigationSettleObserver
334
320
  ]);
@@ -407,72 +393,6 @@ function abortError() {
407
393
  return error;
408
394
  }
409
395
 
410
- // ../protocol/src/json.ts
411
- var JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
412
- function defineSchema(schema) {
413
- return schema;
414
- }
415
- function stringSchema(options = {}) {
416
- return defineSchema({
417
- type: "string",
418
- ...options
419
- });
420
- }
421
- function numberSchema(options = {}) {
422
- return defineSchema({
423
- type: "number",
424
- ...options
425
- });
426
- }
427
- function integerSchema(options = {}) {
428
- return defineSchema({
429
- type: "integer",
430
- ...options
431
- });
432
- }
433
- function literalSchema(value, options = {}) {
434
- return defineSchema({
435
- const: value,
436
- ...options
437
- });
438
- }
439
- function enumSchema(values, options = {}) {
440
- return defineSchema({
441
- enum: values,
442
- ...options
443
- });
444
- }
445
- function arraySchema(items, options = {}) {
446
- return defineSchema({
447
- type: "array",
448
- items,
449
- ...options
450
- });
451
- }
452
- function objectSchema(properties, options = {}) {
453
- const { required, additionalProperties, ...rest } = options;
454
- return defineSchema({
455
- type: "object",
456
- properties,
457
- ...rest,
458
- ...required === void 0 ? {} : { required },
459
- ...additionalProperties === void 0 ? { additionalProperties: false } : { additionalProperties }
460
- });
461
- }
462
- function recordSchema(valueSchema, options = {}) {
463
- return defineSchema({
464
- type: "object",
465
- additionalProperties: valueSchema,
466
- ...options
467
- });
468
- }
469
- function oneOfSchema(members, options = {}) {
470
- return defineSchema({
471
- oneOf: members,
472
- ...options
473
- });
474
- }
475
-
476
396
  // ../protocol/src/validation.ts
477
397
  function validateJsonSchema(schema, value, path6 = "$") {
478
398
  return validateSchemaNode(schema, value, path6);
@@ -2382,152 +2302,6 @@ var hitTestResultSchema = objectSchema(
2382
2302
  }
2383
2303
  );
2384
2304
 
2385
- // ../protocol/src/capabilities.ts
2386
- var opensteerCapabilities = [
2387
- "sessions.manage",
2388
- "pages.manage",
2389
- "pages.navigate",
2390
- "input.pointer",
2391
- "input.keyboard",
2392
- "input.touch",
2393
- "artifacts.screenshot",
2394
- "execution.pause",
2395
- "execution.resume",
2396
- "execution.freeze",
2397
- "inspect.pages",
2398
- "inspect.frames",
2399
- "inspect.html",
2400
- "inspect.domSnapshot",
2401
- "inspect.text",
2402
- "inspect.attributes",
2403
- "inspect.hitTest",
2404
- "inspect.viewportMetrics",
2405
- "inspect.network",
2406
- "inspect.networkBodies",
2407
- "inspect.cookies",
2408
- "inspect.localStorage",
2409
- "inspect.sessionStorage",
2410
- "inspect.indexedDb",
2411
- "transport.sessionHttp",
2412
- "instrumentation.initScripts",
2413
- "instrumentation.routing",
2414
- "events.pageLifecycle",
2415
- "events.dialog",
2416
- "events.download",
2417
- "events.chooser",
2418
- "events.worker",
2419
- "events.console",
2420
- "events.pageError",
2421
- "events.websocket",
2422
- "events.eventStream",
2423
- "events.executionState",
2424
- "surface.rest",
2425
- "surface.mcp"
2426
- ];
2427
- var opensteerCapabilitySchema = enumSchema(opensteerCapabilities, {
2428
- title: "OpensteerCapability"
2429
- });
2430
- var opensteerCapabilitySetSchema = arraySchema(opensteerCapabilitySchema, {
2431
- title: "OpensteerCapabilitySet",
2432
- uniqueItems: true
2433
- });
2434
- var opensteerCapabilityDescriptorSchema = objectSchema(
2435
- {
2436
- key: opensteerCapabilitySchema,
2437
- description: stringSchema(),
2438
- stability: enumSchema(["stable", "experimental"])
2439
- },
2440
- {
2441
- title: "OpensteerCapabilityDescriptor",
2442
- required: ["key", "description", "stability"]
2443
- }
2444
- );
2445
- arraySchema(
2446
- opensteerCapabilityDescriptorSchema,
2447
- {
2448
- title: "OpensteerCapabilityDescriptorList",
2449
- uniqueItems: true
2450
- }
2451
- );
2452
-
2453
- // ../protocol/src/errors.ts
2454
- var opensteerErrorCodes = [
2455
- "invalid-request",
2456
- "invalid-argument",
2457
- "invalid-ref",
2458
- "unsupported-version",
2459
- "unsupported-operation",
2460
- "unsupported-capability",
2461
- "not-found",
2462
- "stale-node-ref",
2463
- "session-closed",
2464
- "page-closed",
2465
- "frame-detached",
2466
- "timeout",
2467
- "navigation-failed",
2468
- "permission-denied",
2469
- "conflict",
2470
- "profile-unavailable",
2471
- "auth-failure",
2472
- "auth-recovery-failed",
2473
- "browser-required",
2474
- "rate-limited",
2475
- "operation-failed",
2476
- "internal"
2477
- ];
2478
- var OpensteerProtocolError = class extends Error {
2479
- code;
2480
- retriable;
2481
- capability;
2482
- details;
2483
- constructor(code, message, options = {}) {
2484
- super(message, { cause: options.cause });
2485
- this.name = "OpensteerProtocolError";
2486
- this.code = code;
2487
- this.retriable = options.retriable ?? false;
2488
- this.capability = options.capability;
2489
- this.details = options.details;
2490
- }
2491
- };
2492
- function createOpensteerError(code, message, options = {}) {
2493
- return {
2494
- code,
2495
- message,
2496
- retriable: options.retriable ?? false,
2497
- ...options.capability === void 0 ? {} : { capability: options.capability },
2498
- ...options.details === void 0 ? {} : { details: options.details }
2499
- };
2500
- }
2501
- function isOpensteerProtocolError(value) {
2502
- return value instanceof OpensteerProtocolError;
2503
- }
2504
- function toOpensteerError(error) {
2505
- return createOpensteerError(error.code, error.message, {
2506
- retriable: error.retriable,
2507
- ...error.capability === void 0 ? {} : { capability: error.capability },
2508
- ...error.details === void 0 ? {} : { details: error.details }
2509
- });
2510
- }
2511
- var opensteerErrorCodeSchema = {
2512
- title: "OpensteerErrorCode",
2513
- enum: opensteerErrorCodes
2514
- };
2515
- var opensteerErrorSchema = objectSchema(
2516
- {
2517
- code: opensteerErrorCodeSchema,
2518
- message: stringSchema(),
2519
- retriable: {
2520
- type: "boolean"
2521
- },
2522
- capability: opensteerCapabilitySchema,
2523
- details: recordSchema({})
2524
- },
2525
- {
2526
- title: "OpensteerError",
2527
- required: ["code", "message", "retriable"]
2528
- }
2529
- );
2530
-
2531
2305
  // ../protocol/src/events.ts
2532
2306
  function eventBaseSchema(kind) {
2533
2307
  return {
@@ -4149,10 +3923,10 @@ var opensteerDomScrollInputSchema = objectSchema(
4149
3923
  required: ["target", "direction", "amount"]
4150
3924
  }
4151
3925
  );
4152
- var opensteerExtractSchemaSchema = objectSchema(
3926
+ var opensteerExtractTemplateSchema = objectSchema(
4153
3927
  {},
4154
3928
  {
4155
- title: "OpensteerExtractSchema",
3929
+ title: "OpensteerExtractTemplate",
4156
3930
  additionalProperties: true
4157
3931
  }
4158
3932
  );
@@ -4160,13 +3934,13 @@ var opensteerDomExtractInputSchema = defineSchema({
4160
3934
  ...objectSchema(
4161
3935
  {
4162
3936
  persist: stringSchema(),
4163
- schema: opensteerExtractSchemaSchema
3937
+ template: opensteerExtractTemplateSchema
4164
3938
  },
4165
3939
  {
4166
3940
  title: "OpensteerDomExtractInput"
4167
3941
  }
4168
3942
  ),
4169
- anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["schema"] })]
3943
+ anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["template"] })]
4170
3944
  });
4171
3945
  var jsonValueSchema2 = recordSchema({}, { title: "JsonValueRecord" });
4172
3946
  var opensteerDomExtractOutputSchema = objectSchema(
@@ -5105,6 +4879,9 @@ function buildClauseSelector(node, clause) {
5105
4879
  if (!clause || typeof clause !== "object") {
5106
4880
  return "";
5107
4881
  }
4882
+ if (clause.kind === "text") {
4883
+ return "";
4884
+ }
5108
4885
  if (clause.kind === "position") {
5109
4886
  if (clause.axis === "nthOfType") {
5110
4887
  return `:nth-of-type(${Math.max(1, Number(node.position?.nthOfType || 1))})`;
@@ -5229,7 +5006,7 @@ function resolveExtractedValueInContext(normalizedValue, options) {
5229
5006
  function stripPositionClauses(nodes) {
5230
5007
  return (nodes || []).map((node) => ({
5231
5008
  ...node,
5232
- match: (node.match || []).filter((clause) => clause.kind !== "position")
5009
+ match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
5233
5010
  }));
5234
5011
  }
5235
5012
  function dedupeSelectors(selectors) {
@@ -5806,9 +5583,17 @@ function resolveDomPathInScope(index, domPath, scope) {
5806
5583
  if (!candidates.length) {
5807
5584
  return null;
5808
5585
  }
5586
+ const lastNode = domPath[domPath.length - 1];
5587
+ const textClauses = lastNode?.match.filter((c) => c.kind === "text") ?? [];
5809
5588
  let fallback = null;
5810
5589
  for (const selector of candidates) {
5811
- const matches = querySelectorAllInScope(index, selector, scope);
5590
+ let matches = querySelectorAllInScope(index, selector, scope);
5591
+ if (textClauses.length > 0 && matches.length > 1) {
5592
+ const filtered = matches.filter((node) => matchesTextClauses(node, textClauses));
5593
+ if (filtered.length > 0) {
5594
+ matches = filtered;
5595
+ }
5596
+ }
5812
5597
  if (matches.length === 1) {
5813
5598
  return {
5814
5599
  node: matches[0],
@@ -5828,6 +5613,10 @@ function resolveDomPathInScope(index, domPath, scope) {
5828
5613
  }
5829
5614
  return fallback;
5830
5615
  }
5616
+ function matchesTextClauses(node, clauses) {
5617
+ const text = (node.textContent ?? "").replace(/\s+/g, " ").trim();
5618
+ return clauses.every((clause) => text.includes(clause.value));
5619
+ }
5831
5620
  function queryAllDomPathInScope(index, domPath, scope) {
5832
5621
  const selectors = buildPathCandidates(domPath);
5833
5622
  for (const selector of selectors) {
@@ -5986,7 +5775,13 @@ function clonePathNode(node) {
5986
5775
  };
5987
5776
  }
5988
5777
  function cloneMatchClause(clause) {
5989
- return clause.kind === "position" ? { kind: "position", axis: clause.axis } : {
5778
+ if (clause.kind === "position") {
5779
+ return { kind: "position", axis: clause.axis };
5780
+ }
5781
+ if (clause.kind === "text") {
5782
+ return { kind: "text", value: clause.value };
5783
+ }
5784
+ return {
5990
5785
  kind: "attr",
5991
5786
  key: clause.key,
5992
5787
  ...clause.op === void 0 ? {} : { op: clause.op },
@@ -6041,6 +5836,13 @@ function normalizeMatch(rawMatch, attrs, position, tag) {
6041
5836
  op,
6042
5837
  ...value === void 0 ? {} : { value }
6043
5838
  });
5839
+ continue;
5840
+ }
5841
+ if (record.kind === "text") {
5842
+ const textValue = typeof record.value === "string" ? record.value.trim() : "";
5843
+ if (textValue) {
5844
+ push({ kind: "text", value: textValue.slice(0, 80) });
5845
+ }
6044
5846
  }
6045
5847
  }
6046
5848
  }
@@ -6406,7 +6208,7 @@ function isTimeoutError(error) {
6406
6208
  }
6407
6209
 
6408
6210
  // ../runtime-core/src/runtimes/dom/executor.ts
6409
- var MAX_DOM_ACTION_ATTEMPTS = 3;
6211
+ var MAX_DOM_ACTION_ATTEMPTS = 2;
6410
6212
  var DEFAULT_SCROLL_OPTIONS = {
6411
6213
  block: "center",
6412
6214
  inline: "center"
@@ -6893,11 +6695,12 @@ var DomActionExecutor = class {
6893
6695
  throw this.createActionabilityError(
6894
6696
  operation,
6895
6697
  "obscured",
6896
- `hit test resolved ${hit.nodeRef} outside the target subtree rooted at ${resolved.nodeRef}`,
6698
+ `target is obscured by ${assessment.blockingDescription ?? "another element"} at the click point`,
6897
6699
  {
6898
6700
  ...details,
6899
6701
  hitRelation: assessment.relation,
6900
6702
  ...assessment.ambiguous === void 0 ? {} : { hitAmbiguous: assessment.ambiguous },
6703
+ ...assessment.blockingDescription === void 0 ? {} : { blockingDescription: assessment.blockingDescription },
6901
6704
  ...assessment.canonicalTarget === void 0 ? {} : {
6902
6705
  canonicalNodeRef: assessment.canonicalTarget.nodeRef,
6903
6706
  canonicalDocumentRef: assessment.canonicalTarget.documentRef,
@@ -6911,8 +6714,7 @@ var DomActionExecutor = class {
6911
6714
  hitMissingFromSnapshot: !resolved.snapshot.nodes.some(
6912
6715
  (node) => node.nodeRef === hit.nodeRef
6913
6716
  )
6914
- },
6915
- true
6717
+ }
6916
6718
  );
6917
6719
  }
6918
6720
  async resolveActionablePointerTarget(session, operation, resolved) {
@@ -7124,7 +6926,12 @@ var DefaultDomRuntime = class {
7124
6926
  });
7125
6927
  }
7126
6928
  async buildPath(input) {
7127
- return sanitizeReplayElementPath(await this.requireBridge().buildReplayPath(input.locator));
6929
+ return sanitizeReplayElementPath(
6930
+ await this.requireBridge().buildReplayPath(
6931
+ input.locator,
6932
+ input.enableTextMatch ? { enableTextMatch: true } : void 0
6933
+ )
6934
+ );
7128
6935
  }
7129
6936
  async resolveTarget(input) {
7130
6937
  return this.withSnapshotSession((session) => this.resolveTargetWithSession(session, input));
@@ -7344,7 +7151,7 @@ var DefaultDomRuntime = class {
7344
7151
  if (resolvedByLocator) {
7345
7152
  const { snapshot, node } = resolvedByLocator;
7346
7153
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7347
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7154
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch: true });
7348
7155
  return this.createResolvedTarget("live", snapshot, node, anchor, {
7349
7156
  ...target.persist === void 0 ? {} : { persist: target.persist },
7350
7157
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7362,11 +7169,12 @@ var DefaultDomRuntime = class {
7362
7169
  const { snapshot, node } = resolved;
7363
7170
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7364
7171
  const writeDescriptor = descriptorWriter ?? ((input) => this.descriptors.write(input));
7365
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7172
+ const enableTextMatch = method !== "extract";
7173
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch });
7366
7174
  const descriptor = target.persist === void 0 ? void 0 : await writeDescriptor({
7367
7175
  method,
7368
7176
  persist: target.persist,
7369
- path: replayPath ?? await this.buildPathForNode(snapshot, node),
7177
+ path: replayPath ?? await this.buildPathForNode(snapshot, node, { enableTextMatch }),
7370
7178
  sourceUrl: snapshot.url
7371
7179
  });
7372
7180
  return this.createResolvedTarget("selector", snapshot, node, anchor, {
@@ -7466,7 +7274,7 @@ var DefaultDomRuntime = class {
7466
7274
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
7467
7275
  );
7468
7276
  }
7469
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node);
7277
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
7470
7278
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
7471
7279
  ...persist === void 0 ? {} : { persist },
7472
7280
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7637,19 +7445,20 @@ var DefaultDomRuntime = class {
7637
7445
  }
7638
7446
  return this.bridge;
7639
7447
  }
7640
- async buildPathForNode(snapshot, node) {
7448
+ async buildPathForNode(snapshot, node, options) {
7641
7449
  if (node.nodeRef === void 0) {
7642
7450
  throw new Error(
7643
7451
  `snapshot node ${String(node.snapshotNodeId)} does not expose a live node reference`
7644
7452
  );
7645
7453
  }
7646
7454
  return this.buildPath({
7647
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef)
7455
+ locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
7456
+ ...options?.enableTextMatch ? { enableTextMatch: true } : {}
7648
7457
  });
7649
7458
  }
7650
- async tryBuildPathFromNode(snapshot, node) {
7459
+ async tryBuildPathFromNode(snapshot, node, options) {
7651
7460
  try {
7652
- return await this.buildPathForNode(snapshot, node);
7461
+ return await this.buildPathForNode(snapshot, node, options);
7653
7462
  } catch {
7654
7463
  return void 0;
7655
7464
  }
@@ -8263,6 +8072,9 @@ function relaxPathForSingleSample(path6, mode) {
8263
8072
  }
8264
8073
  return !isLast;
8265
8074
  }
8075
+ if (clause.kind === "text") {
8076
+ return false;
8077
+ }
8266
8078
  const key = String(clause.key || "").trim().toLowerCase();
8267
8079
  if (!key || !shouldKeepAttrForSingleSample(key)) {
8268
8080
  return false;
@@ -8365,9 +8177,11 @@ function buildNodeStructure(node) {
8365
8177
  }
8366
8178
  structuralAttrs[key] = value;
8367
8179
  }
8368
- const matchClauses = (node.match || []).map(
8369
- (clause) => clause.kind === "position" ? `position:${clause.axis}` : `attr:${String(clause.key || "").trim().toLowerCase()}`
8370
- ).sort();
8180
+ const matchClauses = (node.match || []).map((clause) => {
8181
+ if (clause.kind === "position") return `position:${clause.axis}`;
8182
+ if (clause.kind === "text") return `text:${clause.value}`;
8183
+ return `attr:${String(clause.key || "").trim().toLowerCase()}`;
8184
+ }).sort();
8371
8185
  return {
8372
8186
  tag,
8373
8187
  attrs: structuralAttrs,
@@ -8773,6 +8587,9 @@ function mergeMatchByMajority(matchLists, attrs, threshold, positionFlags = {
8773
8587
  });
8774
8588
  continue;
8775
8589
  }
8590
+ if (clause.kind === "text") {
8591
+ continue;
8592
+ }
8776
8593
  if (clause.axis === "nthOfType") {
8777
8594
  if (positionFlags.hasNthOfType) {
8778
8595
  merged.push({ kind: "position", axis: "nthOfType" });
@@ -8917,9 +8734,9 @@ function isPersistedObjectNode(node) {
8917
8734
  }
8918
8735
 
8919
8736
  // ../runtime-core/src/sdk/extraction.ts
8920
- function assertValidOpensteerExtractionSchemaRoot(schema) {
8921
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
8922
- throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
8737
+ function assertValidOpensteerExtractionTemplateRoot(template) {
8738
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
8739
+ throw new Error("Invalid extraction template: expected a JSON object at the top level.");
8923
8740
  }
8924
8741
  }
8925
8742
  function isPersistedOpensteerExtractionValueNode2(value) {
@@ -8941,12 +8758,12 @@ function isPersistedOpensteerExtractionArrayNode2(value) {
8941
8758
  return "$array" in value;
8942
8759
  }
8943
8760
  async function compileOpensteerExtractionFieldTargets(options) {
8944
- assertValidOpensteerExtractionSchemaRoot(options.schema);
8761
+ assertValidOpensteerExtractionTemplateRoot(options.template);
8945
8762
  const fields = [];
8946
- await collectFieldTargetsFromSchemaObject({
8763
+ await collectFieldTargetsFromTemplateObject({
8947
8764
  dom: options.dom,
8948
8765
  pageRef: options.pageRef,
8949
- value: options.schema,
8766
+ value: options.template,
8950
8767
  path: "",
8951
8768
  fields,
8952
8769
  insideArray: false
@@ -8998,13 +8815,13 @@ function createOpensteerExtractionDescriptorStore(options) {
8998
8815
  }
8999
8816
  return new MemoryOpensteerExtractionDescriptorStore(namespace);
9000
8817
  }
9001
- async function collectFieldTargetsFromSchemaObject(options) {
8818
+ async function collectFieldTargetsFromTemplateObject(options) {
9002
8819
  for (const [key, childValue] of Object.entries(options.value)) {
9003
8820
  const normalizedKey = normalizeKey(key);
9004
8821
  if (!normalizedKey) {
9005
8822
  continue;
9006
8823
  }
9007
- await collectFieldTargetsFromSchemaValue({
8824
+ await collectFieldTargetsFromTemplateValue({
9008
8825
  dom: options.dom,
9009
8826
  pageRef: options.pageRef,
9010
8827
  value: childValue,
@@ -9014,8 +8831,8 @@ async function collectFieldTargetsFromSchemaObject(options) {
9014
8831
  });
9015
8832
  }
9016
8833
  }
9017
- async function collectFieldTargetsFromSchemaValue(options) {
9018
- const normalizedField = normalizeSchemaField(options.value);
8834
+ async function collectFieldTargetsFromTemplateValue(options) {
8835
+ const normalizedField = normalizeTemplateField(options.value);
9019
8836
  if (normalizedField !== null) {
9020
8837
  options.fields.push(
9021
8838
  await compileFieldTarget({
@@ -9030,12 +8847,12 @@ async function collectFieldTargetsFromSchemaValue(options) {
9030
8847
  if (Array.isArray(options.value)) {
9031
8848
  if (options.insideArray) {
9032
8849
  throw new Error(
9033
- `Nested arrays are not supported in extraction schema at "${labelForPath(options.path)}".`
8850
+ `Nested arrays are not supported in extraction template at "${labelForPath(options.path)}".`
9034
8851
  );
9035
8852
  }
9036
8853
  if (options.value.length === 0) {
9037
8854
  throw new Error(
9038
- `Extraction array "${labelForPath(options.path)}" must include at least one representative item.`
8855
+ `Extraction array "${labelForPath(options.path)}" must include at least one representative template item.`
9039
8856
  );
9040
8857
  }
9041
8858
  for (let index = 0; index < options.value.length; index += 1) {
@@ -9046,7 +8863,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9046
8863
  );
9047
8864
  }
9048
8865
  const fieldCountBeforeItem = options.fields.length;
9049
- await collectFieldTargetsFromSchemaObject({
8866
+ await collectFieldTargetsFromTemplateObject({
9050
8867
  dom: options.dom,
9051
8868
  pageRef: options.pageRef,
9052
8869
  value: itemValue,
@@ -9057,7 +8874,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9057
8874
  const itemFields = options.fields.slice(fieldCountBeforeItem);
9058
8875
  if (!itemFields.some((field) => !("source" in field))) {
9059
8876
  throw new Error(
9060
- `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element- or selector-backed field.`
8877
+ `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element number or selector field.`
9061
8878
  );
9062
8879
  }
9063
8880
  }
@@ -9065,10 +8882,10 @@ async function collectFieldTargetsFromSchemaValue(options) {
9065
8882
  }
9066
8883
  if (!options.value || typeof options.value !== "object") {
9067
8884
  throw new Error(
9068
- `Invalid extraction schema value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
8885
+ `Invalid extraction template value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
9069
8886
  );
9070
8887
  }
9071
- await collectFieldTargetsFromSchemaObject({
8888
+ await collectFieldTargetsFromTemplateObject({
9072
8889
  dom: options.dom,
9073
8890
  pageRef: options.pageRef,
9074
8891
  value: options.value,
@@ -9100,7 +8917,7 @@ async function compileFieldTarget(options) {
9100
8917
  path: await resolveSelectorFieldPath({
9101
8918
  dom: options.dom,
9102
8919
  pageRef: options.pageRef,
9103
- selector: `[c="${String(options.field.element)}"]`
8920
+ selector: `[c="${String(options.field.c)}"]`
9104
8921
  }),
9105
8922
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
9106
8923
  };
@@ -9401,24 +9218,29 @@ function countNonNullLeaves(value) {
9401
9218
  }
9402
9219
  return Object.values(value).reduce((sum, item) => sum + countNonNullLeaves(item), 0);
9403
9220
  }
9404
- function normalizeSchemaField(value) {
9221
+ function normalizeTemplateField(value) {
9222
+ if (typeof value === "number") {
9223
+ return {
9224
+ c: normalizeExtractionCounter(value)
9225
+ };
9226
+ }
9405
9227
  if (!value || typeof value !== "object" || Array.isArray(value)) {
9406
9228
  return null;
9407
9229
  }
9408
9230
  const raw = value;
9409
- const hasElement = raw.element !== void 0;
9231
+ const hasCounter = raw.c !== void 0 || raw.element !== void 0;
9410
9232
  const hasSelector = raw.selector !== void 0;
9411
9233
  const hasSource = raw.source !== void 0;
9412
- const targetCount = Number(hasElement) + Number(hasSelector) + Number(hasSource);
9234
+ const targetCount = Number(hasCounter) + Number(hasSelector) + Number(hasSource);
9413
9235
  if (targetCount === 0) {
9414
9236
  return null;
9415
9237
  }
9416
9238
  if (targetCount !== 1) {
9417
9239
  throw new Error(
9418
- "Extraction field descriptors must specify exactly one of element, selector, or source."
9240
+ "Extraction field descriptors must specify exactly one of c/element, selector, or source."
9419
9241
  );
9420
9242
  }
9421
- const attribute = raw.attribute === void 0 ? void 0 : normalizeNonEmptyString("attribute", raw.attribute);
9243
+ const attribute = raw.attr !== void 0 ? normalizeNonEmptyString("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString("attribute", raw.attribute);
9422
9244
  if (hasSource) {
9423
9245
  if (raw.source !== "current_url") {
9424
9246
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -9433,17 +9255,20 @@ function normalizeSchemaField(value) {
9433
9255
  ...attribute === void 0 ? {} : { attribute }
9434
9256
  };
9435
9257
  }
9436
- const element = Number(raw.element);
9437
- if (!Number.isInteger(element) || element < 1) {
9438
- throw new Error(
9439
- `Extraction field element must be a positive integer, received ${String(raw.element)}.`
9440
- );
9441
- }
9442
9258
  return {
9443
- element,
9259
+ c: normalizeExtractionCounter(raw.c ?? raw.element),
9444
9260
  ...attribute === void 0 ? {} : { attribute }
9445
9261
  };
9446
9262
  }
9263
+ function normalizeExtractionCounter(value) {
9264
+ const counter = Number(value);
9265
+ if (!Number.isInteger(counter) || counter < 1) {
9266
+ throw new Error(
9267
+ `Extraction element number must be a positive integer, received ${String(value)}.`
9268
+ );
9269
+ }
9270
+ return counter;
9271
+ }
9447
9272
  function normalizeNamespace(namespace) {
9448
9273
  const normalized = String(namespace ?? "default").trim();
9449
9274
  return normalized.length === 0 ? "default" : normalized;
@@ -9474,7 +9299,7 @@ function parseExtractionDescriptorRecord(record) {
9474
9299
  kind: "dom-extraction",
9475
9300
  persist: raw.persist,
9476
9301
  root,
9477
- ...typeof raw.schemaHash === "string" ? { schemaHash: raw.schemaHash } : {},
9302
+ ...typeof raw.templateHash === "string" ? { templateHash: raw.templateHash } : typeof raw.schemaHash === "string" ? { templateHash: raw.schemaHash } : {},
9478
9303
  ...typeof raw.sourceUrl === "string" ? { sourceUrl: raw.sourceUrl } : {}
9479
9304
  }
9480
9305
  };
@@ -9560,7 +9385,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
9560
9385
  kind: "dom-extraction",
9561
9386
  persist: input.persist,
9562
9387
  root: input.root,
9563
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9388
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9564
9389
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9565
9390
  };
9566
9391
  const key = persistKey(this.namespace, input.persist);
@@ -9609,7 +9434,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
9609
9434
  kind: "dom-extraction",
9610
9435
  persist: input.persist,
9611
9436
  root: input.root,
9612
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9437
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9613
9438
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9614
9439
  };
9615
9440
  const key = persistKey(this.namespace, input.persist);
@@ -9760,7 +9585,8 @@ function assertProviderSupportsEngine(provider, engine) {
9760
9585
  return;
9761
9586
  }
9762
9587
  if (provider === "cloud") {
9763
- throw new Error(
9588
+ throw new OpensteerProtocolError(
9589
+ "invalid-argument",
9764
9590
  "ABP is not supported for provider=cloud. Cloud provider currently requires Playwright."
9765
9591
  );
9766
9592
  }
@@ -9770,7 +9596,8 @@ function normalizeOpensteerProviderMode(value, source = "OPENSTEER_PROVIDER") {
9770
9596
  if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
9771
9597
  return normalized;
9772
9598
  }
9773
- throw new Error(
9599
+ throw new OpensteerProtocolError(
9600
+ "invalid-argument",
9774
9601
  `${source} must be one of ${OPENSTEER_PROVIDER_MODES.join(", ")}; received "${value}".`
9775
9602
  );
9776
9603
  }
@@ -10210,6 +10037,24 @@ async function sleep(ms) {
10210
10037
  // src/cloud/client.ts
10211
10038
  var CLOUD_CLOSE_TIMEOUT_MS = 6e4;
10212
10039
  var CLOUD_CLOSE_POLL_INTERVAL_MS = 250;
10040
+ var OpensteerCloudRequestError = class extends Error {
10041
+ statusCode;
10042
+ code;
10043
+ details;
10044
+ method;
10045
+ pathname;
10046
+ url;
10047
+ constructor(args) {
10048
+ super(args.message);
10049
+ this.name = "OpensteerCloudRequestError";
10050
+ this.statusCode = args.statusCode;
10051
+ this.code = args.code;
10052
+ this.details = args.details;
10053
+ this.method = args.method;
10054
+ this.pathname = args.pathname;
10055
+ this.url = args.url;
10056
+ }
10057
+ };
10213
10058
  var OpensteerCloudClient = class {
10214
10059
  constructor(config) {
10215
10060
  this.config = config;
@@ -10385,7 +10230,11 @@ var OpensteerCloudClient = class {
10385
10230
  });
10386
10231
  }
10387
10232
  if (!response.ok) {
10388
- throw new Error(`${init.method} ${pathname} failed with ${String(response.status)}.`);
10233
+ throw await createCloudRequestError(response, {
10234
+ method: init.method,
10235
+ pathname,
10236
+ url
10237
+ });
10389
10238
  }
10390
10239
  return response;
10391
10240
  }
@@ -10433,6 +10282,36 @@ function wrapCloudFetchError(error, input) {
10433
10282
  wrapped.name = error.name;
10434
10283
  return wrapped;
10435
10284
  }
10285
+ async function createCloudRequestError(response, input) {
10286
+ const payload = await readCloudErrorPayload(response);
10287
+ return new OpensteerCloudRequestError({
10288
+ statusCode: response.status,
10289
+ method: input.method,
10290
+ pathname: input.pathname,
10291
+ url: input.url,
10292
+ message: payload?.error ?? `${input.method} ${input.pathname} failed with ${String(response.status)}.`,
10293
+ ...payload?.code === void 0 ? {} : { code: payload.code },
10294
+ ...payload?.details === void 0 ? {} : { details: payload.details }
10295
+ });
10296
+ }
10297
+ async function readCloudErrorPayload(response) {
10298
+ try {
10299
+ return asCloudErrorPayload(await response.json());
10300
+ } catch {
10301
+ return void 0;
10302
+ }
10303
+ }
10304
+ function asCloudErrorPayload(value) {
10305
+ if (value === null || typeof value !== "object") {
10306
+ return void 0;
10307
+ }
10308
+ const payload = value;
10309
+ return {
10310
+ ...typeof payload.error === "string" ? { error: payload.error } : {},
10311
+ ...typeof payload.code === "string" ? { code: payload.code } : {},
10312
+ ..."details" in payload ? { details: payload.details } : {}
10313
+ };
10314
+ }
10436
10315
 
10437
10316
  // src/cloud/config.ts
10438
10317
  function resolveCloudConfig(input = {}) {
@@ -10608,7 +10487,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10608
10487
 
10609
10488
  // ../runtime-core/package.json
10610
10489
  var package_default = {
10611
- version: "0.2.3"};
10490
+ version: "0.2.5"};
10612
10491
 
10613
10492
  // ../runtime-core/src/version.ts
10614
10493
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -11654,10 +11533,19 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
11654
11533
  // ../runtime-core/src/sdk/snapshot/cleaner.ts
11655
11534
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
11656
11535
  var TEXT_ATTR_MAX = 150;
11657
- var URL_ATTR_MAX = 500;
11658
- var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "srcset"]);
11536
+ var SRCSET_ATTR_MAX = 160;
11537
+ var MIDDLE_TRUNCATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
11659
11538
  var TEXT_ATTRS = /* @__PURE__ */ new Set(["alt", "title", "aria-label", "placeholder", "value"]);
11660
- var TRUNCATION_SUFFIX = " [truncated]";
11539
+ var TRUNCATION_SUFFIX = "...";
11540
+ var MIDDLE_TRUNCATION_MARKER = "...";
11541
+ var MIDDLE_TRUNCATION_HEAD_MAX = 40;
11542
+ var MIDDLE_TRUNCATION_TAIL_MAX = 20;
11543
+ var SRCSET_CANDIDATE_HEAD_MAX = 36;
11544
+ var SRCSET_CANDIDATE_TAIL_MAX = 12;
11545
+ var SRCSET_COMPACT_CANDIDATE_HEAD_MAX = 20;
11546
+ var SRCSET_COMPACT_CANDIDATE_TAIL_MAX = 8;
11547
+ var SRCSET_FALLBACK_HEAD_MAX = 56;
11548
+ var SRCSET_FALLBACK_TAIL_MAX = 20;
11661
11549
  var NOISE_SELECTORS = [
11662
11550
  `[${OPENSTEER_HIDDEN_ATTR}]`,
11663
11551
  "[hidden]",
@@ -11720,184 +11608,554 @@ function truncateValue(value, max) {
11720
11608
  }
11721
11609
  return `${head}${TRUNCATION_SUFFIX}`;
11722
11610
  }
11611
+ function takeValueWithinSerializedLengthFromEnd(value, max) {
11612
+ let serializedLength = 0;
11613
+ const chars = [];
11614
+ for (let index = value.length - 1; index >= 0; index -= 1) {
11615
+ const char = value[index];
11616
+ let nextLength = 1;
11617
+ if (char === "&") {
11618
+ nextLength = 5;
11619
+ } else if (char === "<" || char === ">") {
11620
+ nextLength = 4;
11621
+ } else if (char === '"') {
11622
+ nextLength = 6;
11623
+ }
11624
+ if (serializedLength + nextLength > max) {
11625
+ break;
11626
+ }
11627
+ chars.push(char);
11628
+ serializedLength += nextLength;
11629
+ }
11630
+ return chars.reverse().join("");
11631
+ }
11632
+ function truncateValueInMiddle(value, headMax, tailMax, marker = MIDDLE_TRUNCATION_MARKER) {
11633
+ const markerLength = getSerializedLength(marker);
11634
+ const max = headMax + markerLength + tailMax;
11635
+ if (getSerializedLength(value) <= max) {
11636
+ return value;
11637
+ }
11638
+ const head = takeValueWithinSerializedLength(value, headMax).replace(/\s+$/u, "");
11639
+ const tail = takeValueWithinSerializedLengthFromEnd(value, tailMax).replace(/^\s+/u, "");
11640
+ if (head.length === 0) {
11641
+ return tail.length === 0 ? marker : `${marker}${tail}`;
11642
+ }
11643
+ if (tail.length === 0) {
11644
+ return `${head}${marker}`;
11645
+ }
11646
+ return `${head}${marker}${tail}`;
11647
+ }
11723
11648
  function getAttrLimit(attr) {
11724
- if (URL_ATTRS.has(attr)) {
11725
- return URL_ATTR_MAX;
11649
+ if (attr === "srcset") {
11650
+ return SRCSET_ATTR_MAX;
11726
11651
  }
11727
11652
  if (TEXT_ATTRS.has(attr)) {
11728
11653
  return TEXT_ATTR_MAX;
11729
11654
  }
11730
11655
  return void 0;
11731
11656
  }
11657
+ function shouldBoundAttr(attr) {
11658
+ return MIDDLE_TRUNCATED_URL_ATTRS.has(attr) || getAttrLimit(attr) !== void 0;
11659
+ }
11732
11660
  function setBoundedAttr(el, attr, value) {
11661
+ if (MIDDLE_TRUNCATED_URL_ATTRS.has(attr)) {
11662
+ el.attr(
11663
+ attr,
11664
+ truncateValueInMiddle(value, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX)
11665
+ );
11666
+ return;
11667
+ }
11733
11668
  const limit = getAttrLimit(attr);
11669
+ if (attr === "srcset" && limit !== void 0) {
11670
+ el.attr(attr, truncateSrcsetValue(value, limit));
11671
+ return;
11672
+ }
11734
11673
  el.attr(attr, limit === void 0 ? value : truncateValue(value, limit));
11735
11674
  }
11736
- function removeNoise($) {
11737
- for (const tag of STRIP_TAGS) {
11738
- $(tag).remove();
11675
+ function truncateSrcsetValue(value, max) {
11676
+ if (getSerializedLength(value) <= max) {
11677
+ return value;
11739
11678
  }
11740
- $(NOISE_SELECTORS.join(", ")).remove();
11741
- }
11742
- function removeComments($) {
11743
- $("*").contents().each(function removeComment() {
11744
- if (this.type === "comment") {
11745
- $(this).remove();
11746
- }
11747
- });
11748
- }
11749
- function markInlineSelfHiddenFallback($) {
11750
- $(
11751
- "[style*='visibility: hidden'], [style*='visibility:hidden'], [style*='visibility: collapse'], [style*='visibility:collapse']"
11752
- ).each(function markInlineVisibilityHidden() {
11753
- const el = $(this);
11754
- if (el.attr(OPENSTEER_HIDDEN_ATTR) === void 0) {
11755
- el.attr(OPENSTEER_SELF_HIDDEN_ATTR, "1");
11756
- }
11757
- });
11758
- }
11759
- function pruneSelfHiddenNodes($) {
11760
- const nodes = [];
11761
- $(`[${OPENSTEER_SELF_HIDDEN_ATTR}]`).each(function collectSelfHiddenNodes() {
11762
- nodes.push($(this));
11763
- });
11764
- nodes.sort((left, right) => right.parents().length - left.parents().length);
11765
- for (const el of nodes) {
11766
- if (!el[0]) {
11767
- continue;
11768
- }
11769
- el.contents().each(function removeSelfHiddenText() {
11770
- if (this.type === "text") {
11771
- $(this).remove();
11772
- }
11773
- });
11774
- if (el.children().length === 0) {
11775
- el.remove();
11776
- }
11679
+ const candidates = parseSrcsetCandidates2(value);
11680
+ if (candidates.length === 0) {
11681
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
11777
11682
  }
11778
- }
11779
- function hasDirectText($, el) {
11780
- return el.contents().filter(function hasDirectNodeText() {
11781
- return this.type === "text" && $(this).text().trim() !== "";
11782
- }).length > 0;
11783
- }
11784
- function hasTextDeep(el) {
11785
- return el.text().trim().length > 0;
11786
- }
11787
- function hasActionLabel(attrs) {
11788
- 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() !== "";
11789
- }
11790
- function unwrapActionNode($, el) {
11791
- if (hasTextDeep(el)) {
11792
- if (el.prev().length > 0) {
11793
- el.before(" ");
11794
- }
11795
- if (el.next().length > 0) {
11796
- el.after(" ");
11683
+ for (const [headMax, tailMax, includeBest] of [
11684
+ [SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, true],
11685
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, true],
11686
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, false]
11687
+ ]) {
11688
+ const compact = buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest);
11689
+ if (getSerializedLength(compact) <= max) {
11690
+ return compact;
11797
11691
  }
11798
11692
  }
11799
- el.replaceWith(el.contents());
11693
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
11800
11694
  }
11801
- function stripToAttrs(el, keep) {
11802
- const attrs = el.attr() || {};
11803
- for (const attr of Object.keys(attrs)) {
11804
- if (!keep.has(attr)) {
11805
- el.removeAttr(attr);
11806
- continue;
11807
- }
11808
- const value = el.attr(attr);
11809
- if (typeof value !== "string") {
11810
- continue;
11811
- }
11812
- if (getAttrLimit(attr) !== void 0) {
11813
- setBoundedAttr(el, attr, value);
11695
+ function buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest) {
11696
+ const kept = getPreferredSrcsetCandidateIndices(candidates, includeBest);
11697
+ const parts = [];
11698
+ let previousIndex;
11699
+ for (const candidateIndex of kept) {
11700
+ if (previousIndex !== void 0 && candidateIndex - previousIndex > 1) {
11701
+ parts.push(MIDDLE_TRUNCATION_MARKER);
11814
11702
  }
11703
+ parts.push(formatSrcsetCandidate(candidates[candidateIndex], headMax, tailMax));
11704
+ previousIndex = candidateIndex;
11815
11705
  }
11706
+ return parts.join(", ");
11816
11707
  }
11817
- function restoreBoundedAttr(el, attr, value) {
11818
- if (typeof value !== "string") {
11819
- return;
11708
+ function getPreferredSrcsetCandidateIndices(candidates, includeBest) {
11709
+ if (candidates.length === 0) {
11710
+ return [];
11820
11711
  }
11821
- const trimmed = value.trim();
11822
- if (trimmed === "") {
11823
- return;
11712
+ const kept = /* @__PURE__ */ new Set([0, candidates.length - 1]);
11713
+ if (includeBest) {
11714
+ kept.add(pickBestSrcsetCandidateIndex(candidates));
11824
11715
  }
11825
- setBoundedAttr(el, attr, value);
11716
+ return [...kept].filter((index) => index >= 0 && index < candidates.length).sort((a, b) => a - b);
11826
11717
  }
11827
- function deduplicateImages(html) {
11828
- const seen = /* @__PURE__ */ new Set();
11829
- return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
11830
- const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
11831
- const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
11832
- let src = null;
11833
- if (srcMatch && srcMatch[2]) {
11834
- src = srcMatch[2].trim();
11835
- } else if (srcsetMatch && srcsetMatch[2]) {
11836
- src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
11837
- }
11838
- if (!src) {
11839
- return full;
11718
+ function pickBestSrcsetCandidateIndex(candidates) {
11719
+ let bestWidthIndex = -1;
11720
+ let bestWidth = -1;
11721
+ let bestDensityIndex = -1;
11722
+ let bestDensity = -1;
11723
+ for (let index = 0; index < candidates.length; index += 1) {
11724
+ const candidate = candidates[index];
11725
+ if (typeof candidate.width === "number" && Number.isFinite(candidate.width) && candidate.width > bestWidth) {
11726
+ bestWidth = candidate.width;
11727
+ bestWidthIndex = index;
11840
11728
  }
11841
- if (seen.has(src)) {
11842
- return "";
11729
+ if (typeof candidate.density === "number" && Number.isFinite(candidate.density) && candidate.density > bestDensity) {
11730
+ bestDensity = candidate.density;
11731
+ bestDensityIndex = index;
11843
11732
  }
11844
- seen.add(src);
11845
- return full;
11846
- });
11847
- }
11848
- function isPreservedImageElement($, el) {
11849
- const tag = (el[0]?.tagName || "").toLowerCase();
11850
- if (tag === "img") {
11851
- return true;
11852
11733
  }
11853
- if (tag === "picture") {
11854
- const hasImg = el.find("img").length > 0;
11855
- const hasSource = el.find("source[src], source[srcset]").length > 0;
11856
- return hasImg || hasSource;
11734
+ if (bestWidthIndex >= 0) {
11735
+ return bestWidthIndex;
11857
11736
  }
11858
- if (tag === "source") {
11859
- const inPicture = el.parents("picture").length > 0;
11860
- const hasSrc = el.attr("src") != null && el.attr("src").trim() !== "" || el.attr("srcset") != null && el.attr("srcset").trim() !== "";
11861
- return inPicture && hasSrc;
11737
+ if (bestDensityIndex >= 0) {
11738
+ return bestDensityIndex;
11862
11739
  }
11863
- return false;
11740
+ return candidates.length - 1;
11864
11741
  }
11865
- function flattenExtractionTree($) {
11866
- const flatten = (root) => {
11867
- root.find("*").each(function flattenNode() {
11868
- const el = $(this);
11869
- const node = el[0];
11870
- if (!node) {
11871
- return;
11872
- }
11873
- const tag = (node.tagName || "").toLowerCase();
11874
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
11875
- return;
11742
+ function formatSrcsetCandidate(candidate, headMax, tailMax) {
11743
+ const url = truncateValueInMiddle(candidate.url, headMax, tailMax);
11744
+ return candidate.descriptorText ? `${url} ${candidate.descriptorText}` : url;
11745
+ }
11746
+ function parseSrcsetCandidates2(raw) {
11747
+ const text = raw.trim();
11748
+ if (!text) {
11749
+ return [];
11750
+ }
11751
+ const out = [];
11752
+ let index = 0;
11753
+ while (index < text.length) {
11754
+ index = skipSrcsetSeparators(text, index);
11755
+ if (index >= text.length) {
11756
+ break;
11757
+ }
11758
+ const urlToken = readSrcsetUrlToken(text, index);
11759
+ index = urlToken.nextIndex;
11760
+ const url = urlToken.value.trim();
11761
+ if (!url) {
11762
+ continue;
11763
+ }
11764
+ index = skipSrcsetWhitespace(text, index);
11765
+ const descriptors = [];
11766
+ while (index < text.length && text[index] !== ",") {
11767
+ const descriptorToken = readSrcsetDescriptorToken(text, index);
11768
+ if (!descriptorToken.value) {
11769
+ index = descriptorToken.nextIndex;
11770
+ continue;
11876
11771
  }
11877
- if (isPreservedImageElement($, el)) {
11878
- return;
11772
+ descriptors.push(descriptorToken.value);
11773
+ index = descriptorToken.nextIndex;
11774
+ index = skipSrcsetWhitespace(text, index);
11775
+ }
11776
+ if (index < text.length && text[index] === ",") {
11777
+ index += 1;
11778
+ }
11779
+ let width = null;
11780
+ let density = null;
11781
+ for (const descriptor of descriptors) {
11782
+ const token = descriptor.trim().toLowerCase();
11783
+ if (!token) {
11784
+ continue;
11879
11785
  }
11880
- if (tag === "a") {
11881
- el.children().each(function flattenAnchorChild() {
11882
- flatten($(this));
11883
- });
11884
- return;
11786
+ const widthMatch = token.match(/^(\d+)w$/);
11787
+ if (widthMatch) {
11788
+ const parsed = Number.parseInt(widthMatch[1], 10);
11789
+ if (Number.isFinite(parsed)) {
11790
+ width = parsed;
11791
+ }
11792
+ continue;
11885
11793
  }
11886
- const hasText = hasDirectText($, el);
11887
- if (hasText) {
11888
- return;
11794
+ const densityMatch = token.match(/^(\d*\.?\d+)x$/);
11795
+ if (densityMatch) {
11796
+ const parsed = Number.parseFloat(densityMatch[1]);
11797
+ if (Number.isFinite(parsed)) {
11798
+ density = parsed;
11799
+ }
11889
11800
  }
11890
- if (el.children().length === 0) {
11891
- el.remove();
11892
- return;
11801
+ }
11802
+ out.push({
11803
+ url,
11804
+ descriptorText: descriptors.join(" "),
11805
+ width,
11806
+ density
11807
+ });
11808
+ }
11809
+ return out;
11810
+ }
11811
+ function skipSrcsetWhitespace(value, index) {
11812
+ let cursor = index;
11813
+ while (cursor < value.length && /\s/u.test(value[cursor])) {
11814
+ cursor += 1;
11815
+ }
11816
+ return cursor;
11817
+ }
11818
+ function skipSrcsetSeparators(value, index) {
11819
+ let cursor = skipSrcsetWhitespace(value, index);
11820
+ while (cursor < value.length && value[cursor] === ",") {
11821
+ cursor += 1;
11822
+ cursor = skipSrcsetWhitespace(value, cursor);
11823
+ }
11824
+ return cursor;
11825
+ }
11826
+ function readSrcsetUrlToken(value, index) {
11827
+ let cursor = index;
11828
+ let out = "";
11829
+ const isDataUrl = value.slice(index, index + 5).toLowerCase().startsWith("data:");
11830
+ while (cursor < value.length) {
11831
+ const char = value[cursor];
11832
+ if (/\s/u.test(char)) {
11833
+ break;
11834
+ }
11835
+ if (char === "," && !isDataUrl) {
11836
+ break;
11837
+ }
11838
+ out += char;
11839
+ cursor += 1;
11840
+ }
11841
+ if (isDataUrl && out.endsWith(",") && cursor < value.length) {
11842
+ out = out.slice(0, -1);
11843
+ }
11844
+ return {
11845
+ value: out,
11846
+ nextIndex: cursor
11847
+ };
11848
+ }
11849
+ function readSrcsetDescriptorToken(value, index) {
11850
+ let cursor = skipSrcsetWhitespace(value, index);
11851
+ let out = "";
11852
+ while (cursor < value.length) {
11853
+ const char = value[cursor];
11854
+ if (char === "," || /\s/u.test(char)) {
11855
+ break;
11856
+ }
11857
+ out += char;
11858
+ cursor += 1;
11859
+ }
11860
+ return {
11861
+ value: out.trim(),
11862
+ nextIndex: cursor
11863
+ };
11864
+ }
11865
+ function removeNoise($) {
11866
+ for (const tag of STRIP_TAGS) {
11867
+ $(tag).remove();
11868
+ }
11869
+ $(NOISE_SELECTORS.join(", ")).remove();
11870
+ }
11871
+ function removeComments($) {
11872
+ $("*").contents().each(function removeComment() {
11873
+ if (this.type === "comment") {
11874
+ $(this).remove();
11875
+ }
11876
+ });
11877
+ }
11878
+ function markInlineSelfHiddenFallback($) {
11879
+ $(
11880
+ "[style*='visibility: hidden'], [style*='visibility:hidden'], [style*='visibility: collapse'], [style*='visibility:collapse']"
11881
+ ).each(function markInlineVisibilityHidden() {
11882
+ const el = $(this);
11883
+ if (el.attr(OPENSTEER_HIDDEN_ATTR) === void 0) {
11884
+ el.attr(OPENSTEER_SELF_HIDDEN_ATTR, "1");
11885
+ }
11886
+ });
11887
+ }
11888
+ function pruneSelfHiddenNodes($) {
11889
+ for (const node of getElementsInReverseDocumentOrder($)) {
11890
+ if (node.attribs?.[OPENSTEER_SELF_HIDDEN_ATTR] === void 0) {
11891
+ continue;
11892
+ }
11893
+ const el = $(node);
11894
+ el.contents().each(function removeSelfHiddenText() {
11895
+ if (this.type === "text") {
11896
+ $(this).remove();
11893
11897
  }
11894
- el.children().each(function flattenChild() {
11895
- flatten($(this));
11896
- });
11897
- el.replaceWith(el.contents());
11898
11898
  });
11899
+ if (!hasElementChildren(node)) {
11900
+ el.remove();
11901
+ }
11902
+ }
11903
+ }
11904
+ function getChildNodes(node) {
11905
+ return node?.children ?? [];
11906
+ }
11907
+ function isElementLikeNode(node) {
11908
+ return node?.type === "tag" || node?.type === "script" || node?.type === "style";
11909
+ }
11910
+ function hasDirectText(node) {
11911
+ if (!node) {
11912
+ return false;
11913
+ }
11914
+ for (const child of getChildNodes(node)) {
11915
+ if (child.type === "text" && (child.data || "").trim() !== "") {
11916
+ return true;
11917
+ }
11918
+ }
11919
+ return false;
11920
+ }
11921
+ function hasElementChildren(node) {
11922
+ if (!node) {
11923
+ return false;
11924
+ }
11925
+ for (const child of getChildNodes(node)) {
11926
+ if (isElementLikeNode(child)) {
11927
+ return true;
11928
+ }
11929
+ }
11930
+ return false;
11931
+ }
11932
+ function hasTextDeepNode(node) {
11933
+ if (!node) {
11934
+ return false;
11935
+ }
11936
+ if (node.type === "text") {
11937
+ return (node.data || "").trim() !== "";
11938
+ }
11939
+ for (const child of getChildNodes(node)) {
11940
+ if (hasTextDeepNode(child)) {
11941
+ return true;
11942
+ }
11943
+ }
11944
+ return false;
11945
+ }
11946
+ function hasActionLabel(attrs) {
11947
+ 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() !== "";
11948
+ }
11949
+ function unwrapActionNode($, el) {
11950
+ if (hasTextDeepNode(el[0])) {
11951
+ if (el.prev().length > 0) {
11952
+ el.before(" ");
11953
+ }
11954
+ if (el.next().length > 0) {
11955
+ el.after(" ");
11956
+ }
11957
+ }
11958
+ el.replaceWith(el.contents());
11959
+ }
11960
+ function stripToAttrs(el, keep) {
11961
+ const attrs = el.attr() || {};
11962
+ for (const attr of Object.keys(attrs)) {
11963
+ if (!keep.has(attr)) {
11964
+ el.removeAttr(attr);
11965
+ continue;
11966
+ }
11967
+ const value = el.attr(attr);
11968
+ if (typeof value !== "string") {
11969
+ continue;
11970
+ }
11971
+ if (shouldBoundAttr(attr)) {
11972
+ setBoundedAttr(el, attr, value);
11973
+ }
11974
+ }
11975
+ }
11976
+ function restoreBoundedAttr(el, attr, value) {
11977
+ if (typeof value !== "string") {
11978
+ return;
11979
+ }
11980
+ const trimmed = value.trim();
11981
+ if (trimmed === "") {
11982
+ return;
11983
+ }
11984
+ setBoundedAttr(el, attr, value);
11985
+ }
11986
+ function deduplicateImages(html) {
11987
+ const seen = /* @__PURE__ */ new Set();
11988
+ return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
11989
+ if (/\bc\s*=/.test(attrContent)) {
11990
+ return full;
11991
+ }
11992
+ const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
11993
+ const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
11994
+ let src = null;
11995
+ if (srcMatch && srcMatch[2]) {
11996
+ src = srcMatch[2].trim();
11997
+ } else if (srcsetMatch && srcsetMatch[2]) {
11998
+ src = srcsetMatch[2].split(",")[0]?.trim().split(" ")[0] ?? null;
11999
+ }
12000
+ if (!src) {
12001
+ return full;
12002
+ }
12003
+ if (seen.has(src)) {
12004
+ return "";
12005
+ }
12006
+ seen.add(src);
12007
+ return full;
12008
+ });
12009
+ }
12010
+ function hasAttribute2(node, attr) {
12011
+ return node?.attribs?.[attr] !== void 0;
12012
+ }
12013
+ function hasPictureAncestor(node) {
12014
+ let current = node?.parent;
12015
+ while (current) {
12016
+ if (isElementLikeNode(current) && (current.tagName || "").toLowerCase() === "picture") {
12017
+ return true;
12018
+ }
12019
+ current = current.parent;
12020
+ }
12021
+ return false;
12022
+ }
12023
+ function pictureHasPreservedDescendant(node) {
12024
+ if (!node) {
12025
+ return false;
12026
+ }
12027
+ for (const child of getChildNodes(node)) {
12028
+ if (!isElementLikeNode(child)) {
12029
+ continue;
12030
+ }
12031
+ const tag = (child.tagName || "").toLowerCase();
12032
+ if (tag === "img") {
12033
+ return true;
12034
+ }
12035
+ if (tag === "source" && typeof child.attribs?.src === "string" && child.attribs.src.trim() !== "") {
12036
+ return true;
12037
+ }
12038
+ if (tag === "source" && typeof child.attribs?.srcset === "string" && child.attribs.srcset.trim() !== "") {
12039
+ return true;
12040
+ }
12041
+ if (pictureHasPreservedDescendant(child)) {
12042
+ return true;
12043
+ }
12044
+ }
12045
+ return false;
12046
+ }
12047
+ function isPreservedImageElement(node) {
12048
+ const tag = (node?.tagName || "").toLowerCase();
12049
+ if (tag === "img") {
12050
+ return true;
12051
+ }
12052
+ if (tag === "picture") {
12053
+ return pictureHasPreservedDescendant(node);
12054
+ }
12055
+ if (tag === "source") {
12056
+ const inPicture = hasPictureAncestor(node);
12057
+ const hasSrc = typeof node?.attribs?.src === "string" && node.attribs.src.trim() !== "" || typeof node?.attribs?.srcset === "string" && node.attribs.srcset.trim() !== "";
12058
+ return inPicture && hasSrc;
12059
+ }
12060
+ return false;
12061
+ }
12062
+ function getElementsInReverseDocumentOrder($) {
12063
+ return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
12064
+ }
12065
+ function getNodeDepth(node) {
12066
+ let depth = 0;
12067
+ let current = node.parent;
12068
+ while (current) {
12069
+ depth++;
12070
+ current = current.parent;
12071
+ }
12072
+ return depth;
12073
+ }
12074
+ function getElementsByDepthDescending($) {
12075
+ const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
12076
+ const depths = /* @__PURE__ */ new Map();
12077
+ for (const el of elements) {
12078
+ depths.set(el, getNodeDepth(el));
12079
+ }
12080
+ return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
12081
+ }
12082
+ function flattenExtractionTree($) {
12083
+ for (const node of getElementsInReverseDocumentOrder($)) {
12084
+ const el = $(node);
12085
+ const tag = (node.tagName || "").toLowerCase();
12086
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag) || isPreservedImageElement(node)) {
12087
+ continue;
12088
+ }
12089
+ if (tag === "a" || hasDirectText(node)) {
12090
+ continue;
12091
+ }
12092
+ if (!hasElementChildren(node)) {
12093
+ el.remove();
12094
+ continue;
12095
+ }
12096
+ el.replaceWith(el.contents());
12097
+ }
12098
+ }
12099
+ function hasMarkedAncestor(el, attr) {
12100
+ let current = el[0]?.parent;
12101
+ while (current) {
12102
+ if (!isElementLikeNode(current)) {
12103
+ return false;
12104
+ }
12105
+ if (current.attribs?.[attr] !== void 0) {
12106
+ return true;
12107
+ }
12108
+ current = current.parent;
12109
+ }
12110
+ return false;
12111
+ }
12112
+ function isIndicatorImage(node) {
12113
+ return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12114
+ }
12115
+ function isIndicatorPictureSource(node) {
12116
+ return (node?.tagName || "").toLowerCase() === "source" && hasPictureAncestor(node) && (hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12117
+ }
12118
+ function isSemanticIndicator(node) {
12119
+ const tag = (node?.tagName || "").toLowerCase();
12120
+ if (tag === "svg") {
12121
+ return true;
12122
+ }
12123
+ return hasAttribute2(node, "aria-label") || hasAttribute2(node, "title") || hasAttribute2(node, "data-icon") || node?.attribs?.role === "img";
12124
+ }
12125
+ function findIndicatorDescendant(root) {
12126
+ if (!root) {
12127
+ return void 0;
12128
+ }
12129
+ let firstImage;
12130
+ let firstSource;
12131
+ let firstSemantic;
12132
+ const visit = (node) => {
12133
+ if (!isElementLikeNode(node)) {
12134
+ return false;
12135
+ }
12136
+ if (isIndicatorImage(node)) {
12137
+ firstImage = node;
12138
+ return true;
12139
+ }
12140
+ if (firstSource === void 0 && isIndicatorPictureSource(node)) {
12141
+ firstSource = node;
12142
+ }
12143
+ if (firstSemantic === void 0 && isSemanticIndicator(node)) {
12144
+ firstSemantic = node;
12145
+ }
12146
+ for (const child of getChildNodes(node)) {
12147
+ if (visit(child)) {
12148
+ return true;
12149
+ }
12150
+ }
12151
+ return false;
11899
12152
  };
11900
- flatten($.root());
12153
+ for (const child of getChildNodes(root)) {
12154
+ if (visit(child)) {
12155
+ return firstImage;
12156
+ }
12157
+ }
12158
+ return firstImage ?? firstSource ?? firstSemantic;
11901
12159
  }
11902
12160
  function serializeForExtraction($, root) {
11903
12161
  const lines = [];
@@ -11966,7 +12224,7 @@ function serializeForExtraction($, root) {
11966
12224
  lines.push(`${" ".repeat(depth)}</${tagName}>`);
11967
12225
  }
11968
12226
  traverse(root, 0);
11969
- return lines.join("\n");
12227
+ return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
11970
12228
  }
11971
12229
  function isClickable($, el, context) {
11972
12230
  if (context.hasPreMarked) {
@@ -12058,7 +12316,11 @@ function cleanForExtraction(html) {
12058
12316
  }
12059
12317
  });
12060
12318
  flattenExtractionTree($clean);
12061
- return deduplicateImages(serializeForExtraction($clean, $clean.root()[0]));
12319
+ const root = $clean.root()[0];
12320
+ if (root === void 0) {
12321
+ return "";
12322
+ }
12323
+ return deduplicateImages(serializeForExtraction($clean, root));
12062
12324
  }
12063
12325
  function cleanForAction(html) {
12064
12326
  if (!html.trim()) {
@@ -12084,22 +12346,12 @@ function cleanForAction(html) {
12084
12346
  $(`[${clickableMark}]`).each(function markIndicators() {
12085
12347
  const el = $(this);
12086
12348
  const wrapperAttrs = el.attr() || {};
12087
- if (hasTextDeep(el) || hasActionLabel(wrapperAttrs)) {
12349
+ if (hasTextDeepNode(el[0]) || hasActionLabel(wrapperAttrs)) {
12088
12350
  return;
12089
12351
  }
12090
- const imageIndicator = el.find("img[alt], img[src], img[srcset]").first();
12091
- if (imageIndicator.length) {
12092
- imageIndicator.attr(indicatorMark, "1");
12093
- return;
12094
- }
12095
- const pictureSourceIndicator = el.find("picture source[src], picture source[srcset]").first();
12096
- if (pictureSourceIndicator.length) {
12097
- pictureSourceIndicator.attr(indicatorMark, "1");
12098
- return;
12099
- }
12100
- const semanticIndicator = el.find('[aria-label], [title], [data-icon], [role="img"], svg').first();
12101
- if (semanticIndicator.length) {
12102
- semanticIndicator.attr(indicatorMark, "1");
12352
+ const indicatorNode = findIndicatorDescendant(el[0]);
12353
+ if (indicatorNode !== void 0) {
12354
+ $(indicatorNode).attr(indicatorMark, "1");
12103
12355
  }
12104
12356
  });
12105
12357
  $(`[${clickableMark}]`).each(function removeEmptyClickable() {
@@ -12109,7 +12361,7 @@ function cleanForAction(html) {
12109
12361
  if (NATIVE_INTERACTIVE_TAGS.has(tag) || tag === "a") {
12110
12362
  return;
12111
12363
  }
12112
- if (el.children().length > 0 || hasDirectText($, el)) {
12364
+ if (hasElementChildren(node) || hasDirectText(node)) {
12113
12365
  return;
12114
12366
  }
12115
12367
  const wrapperAttrs = el.attr() || {};
@@ -12135,46 +12387,31 @@ function cleanForAction(html) {
12135
12387
  current = ancestor.parent();
12136
12388
  }
12137
12389
  });
12138
- let changed = true;
12139
- while (changed) {
12140
- changed = false;
12141
- const nodes = [];
12142
- $("*").each(function collectNodes() {
12143
- nodes.push($(this));
12144
- });
12145
- nodes.sort((left, right) => right.parents().length - left.parents().length);
12146
- for (const el of nodes) {
12147
- const node = el[0];
12148
- if (!node) {
12149
- continue;
12150
- }
12151
- const tag = (node.tagName || "").toLowerCase();
12152
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
12153
- continue;
12154
- }
12155
- if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12156
- continue;
12157
- }
12158
- const insideClickable = el.parents(`[${clickableMark}]`).length > 0;
12159
- const preserveBranch = el.attr(branchMark) !== void 0;
12160
- const hasContent = el.children().length > 0 || hasDirectText($, el);
12161
- if (insideClickable || preserveBranch) {
12162
- if (!hasContent) {
12163
- el.remove();
12164
- } else {
12165
- unwrapActionNode($, el);
12166
- }
12167
- changed = true;
12168
- continue;
12169
- }
12390
+ for (const node of getElementsByDepthDescending($)) {
12391
+ const el = $(node);
12392
+ const tag = (node.tagName || "").toLowerCase();
12393
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
12394
+ continue;
12395
+ }
12396
+ if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12397
+ continue;
12398
+ }
12399
+ const insideClickable = hasMarkedAncestor(el, clickableMark);
12400
+ const preserveBranch = el.attr(branchMark) !== void 0;
12401
+ const hasContent = hasElementChildren(node) || hasDirectText(node);
12402
+ if (insideClickable || preserveBranch) {
12170
12403
  if (!hasContent) {
12171
12404
  el.remove();
12172
- changed = true;
12173
- continue;
12405
+ } else {
12406
+ unwrapActionNode($, el);
12174
12407
  }
12175
- unwrapActionNode($, el);
12176
- changed = true;
12408
+ continue;
12177
12409
  }
12410
+ if (!hasContent) {
12411
+ el.remove();
12412
+ continue;
12413
+ }
12414
+ unwrapActionNode($, el);
12178
12415
  }
12179
12416
  $.root().find("*").contents().each(function normalizeActionTextNodes() {
12180
12417
  if (this.type !== "text") {
@@ -12254,21 +12491,7 @@ function cleanForAction(html) {
12254
12491
  OPENSTEER_SPARSE_COUNTER_ATTR
12255
12492
  ]);
12256
12493
  if (clickable) {
12257
- for (const attr of [
12258
- "href",
12259
- "role",
12260
- "type",
12261
- "title",
12262
- "placeholder",
12263
- "value",
12264
- "aria-label",
12265
- "aria-labelledby",
12266
- "aria-describedby",
12267
- "aria-expanded",
12268
- "aria-pressed",
12269
- "aria-selected",
12270
- "aria-haspopup"
12271
- ]) {
12494
+ for (const attr of ["href", "role", "type", "title", "placeholder", "value", "aria-label"]) {
12272
12495
  keep.add(attr);
12273
12496
  }
12274
12497
  }
@@ -12888,9 +13111,9 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
12888
13111
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
12889
13112
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
12890
13113
  const attributes = [...authoredAttributes];
12891
- const subtreeHidden = hasAttribute2(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
12892
- const selfHidden = !subtreeHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
12893
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
13114
+ const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13115
+ const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13116
+ const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
12894
13117
  if (interactive) {
12895
13118
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
12896
13119
  }
@@ -13208,7 +13431,7 @@ function parseOpacity(value) {
13208
13431
  const parsed = Number.parseFloat(value);
13209
13432
  return Number.isFinite(parsed) ? parsed : Number.NaN;
13210
13433
  }
13211
- function hasAttribute2(attributes, name) {
13434
+ function hasAttribute3(attributes, name) {
13212
13435
  const normalizedName = name.toLowerCase();
13213
13436
  return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
13214
13437
  }
@@ -15169,12 +15392,12 @@ var OpensteerSessionRuntime = class {
15169
15392
  async (timeout) => {
15170
15393
  let descriptor2;
15171
15394
  let data;
15172
- if (input.schema !== void 0) {
15173
- assertValidOpensteerExtractionSchemaRoot(input.schema);
15395
+ if (input.template !== void 0) {
15396
+ assertValidOpensteerExtractionTemplateRoot(input.template);
15174
15397
  const fieldTargets = await timeout.runStep(
15175
15398
  () => compileOpensteerExtractionFieldTargets({
15176
15399
  pageRef,
15177
- schema: input.schema,
15400
+ template: input.template,
15178
15401
  dom: this.requireDom()
15179
15402
  })
15180
15403
  );
@@ -15206,7 +15429,7 @@ var OpensteerSessionRuntime = class {
15206
15429
  () => descriptors.write({
15207
15430
  persist,
15208
15431
  root: payload,
15209
- schemaHash: canonicalJsonString(input.schema),
15432
+ templateHash: canonicalJsonString(input.template),
15210
15433
  sourceUrl: pageInfo.url
15211
15434
  })
15212
15435
  );
@@ -15266,7 +15489,7 @@ var OpensteerSessionRuntime = class {
15266
15489
  artifacts,
15267
15490
  data: {
15268
15491
  ...input.persist === void 0 ? {} : { persist: input.persist },
15269
- ...descriptor?.payload.schemaHash === void 0 ? {} : { schemaHash: descriptor.payload.schemaHash },
15492
+ ...descriptor?.payload.templateHash === void 0 ? {} : { templateHash: descriptor.payload.templateHash },
15270
15493
  data: output.data
15271
15494
  },
15272
15495
  context: buildRuntimeTraceContext({
@@ -21867,6 +22090,7 @@ function payloadByteLength(value) {
21867
22090
 
21868
22091
  // src/cloud/session-proxy.ts
21869
22092
  var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
22093
+ var CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS = 1e4;
21870
22094
  var CloudSessionProxy = class {
21871
22095
  rootPath;
21872
22096
  workspace;
@@ -21880,6 +22104,8 @@ var CloudSessionProxy = class {
21880
22104
  automation;
21881
22105
  workspaceStore;
21882
22106
  syncWorkspaceOnClose = false;
22107
+ liveSessionStateEstablished = false;
22108
+ storedInstrumentation = [];
21883
22109
  constructor(cloud, options = {}) {
21884
22110
  this.cloud = cloud;
21885
22111
  this.workspace = options.workspace;
@@ -21909,15 +22135,12 @@ var CloudSessionProxy = class {
21909
22135
  if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
21910
22136
  this.bindClient(persisted);
21911
22137
  }
21912
- if (this.automation) {
21913
- try {
21914
- const sessionInfo = await this.automation.getSessionInfo();
21915
- return {
21916
- ...sessionInfo,
21917
- ...this.workspace === void 0 ? {} : { workspace: this.workspace }
21918
- };
21919
- } catch {
21920
- }
22138
+ const sessionInfo = this.automation ? await this.automation.getSessionInfo().catch(() => void 0) : void 0;
22139
+ if (sessionInfo !== void 0) {
22140
+ return {
22141
+ ...sessionInfo,
22142
+ ...this.workspace === void 0 ? {} : { workspace: this.workspace }
22143
+ };
21921
22144
  }
21922
22145
  return {
21923
22146
  provider: {
@@ -22029,12 +22252,26 @@ var CloudSessionProxy = class {
22029
22252
  return this.invokeSemanticOperation("session.cookies", input);
22030
22253
  }
22031
22254
  async route(input) {
22032
- await this.ensureSession();
22033
- return this.requireAutomation().route(input);
22255
+ const registration = await this.invokeBootstrapInstrumentationOperation(
22256
+ "instrumentation.route",
22257
+ (automation) => automation.route(input)
22258
+ );
22259
+ this.storedInstrumentation.push({
22260
+ kind: "route",
22261
+ input
22262
+ });
22263
+ return registration;
22034
22264
  }
22035
22265
  async interceptScript(input) {
22036
- await this.ensureSession();
22037
- return this.requireAutomation().interceptScript(input);
22266
+ const registration = await this.invokeBootstrapInstrumentationOperation(
22267
+ "instrumentation.intercept-script",
22268
+ (automation) => automation.interceptScript(input)
22269
+ );
22270
+ this.storedInstrumentation.push({
22271
+ kind: "intercept-script",
22272
+ input
22273
+ });
22274
+ return registration;
22038
22275
  }
22039
22276
  async getStorageSnapshot(input = {}) {
22040
22277
  return this.invokeSemanticOperation("session.storage", input);
@@ -22082,6 +22319,8 @@ var CloudSessionProxy = class {
22082
22319
  this.client = void 0;
22083
22320
  this.sessionId = void 0;
22084
22321
  this.semanticGrant = void 0;
22322
+ this.liveSessionStateEstablished = false;
22323
+ this.storedInstrumentation.length = 0;
22085
22324
  if (this.cleanupRootOnClose) {
22086
22325
  await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
22087
22326
  }
@@ -22109,6 +22348,8 @@ var CloudSessionProxy = class {
22109
22348
  this.automation = void 0;
22110
22349
  this.sessionId = void 0;
22111
22350
  this.semanticGrant = void 0;
22351
+ this.liveSessionStateEstablished = false;
22352
+ this.storedInstrumentation.length = 0;
22112
22353
  if (syncError !== void 0) {
22113
22354
  throw syncError;
22114
22355
  }
@@ -22156,6 +22397,7 @@ var CloudSessionProxy = class {
22156
22397
  };
22157
22398
  await this.writePersistedSession(record);
22158
22399
  this.bindClient(record, session.initialGrants?.semantic);
22400
+ await this.restoreStoredInstrumentation();
22159
22401
  }
22160
22402
  async syncWorkspaceToCloud() {
22161
22403
  if (this.workspace === void 0) {
@@ -22167,6 +22409,7 @@ var CloudSessionProxy = class {
22167
22409
  bindClient(record, initialSemanticGrant) {
22168
22410
  this.sessionId = record.sessionId;
22169
22411
  this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
22412
+ this.liveSessionStateEstablished = false;
22170
22413
  this.client = new OpensteerSemanticRestClient({
22171
22414
  getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
22172
22415
  getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
@@ -22174,6 +22417,19 @@ var CloudSessionProxy = class {
22174
22417
  });
22175
22418
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
22176
22419
  }
22420
+ async restoreStoredInstrumentation() {
22421
+ if (this.storedInstrumentation.length === 0) {
22422
+ return;
22423
+ }
22424
+ const automation = this.requireAutomation();
22425
+ for (const registration of this.storedInstrumentation) {
22426
+ if (registration.kind === "route") {
22427
+ await automation.route(registration.input);
22428
+ } else {
22429
+ await automation.interceptScript(registration.input);
22430
+ }
22431
+ }
22432
+ }
22177
22433
  async ensureWorkspaceStore() {
22178
22434
  if (this.workspaceStore !== void 0) {
22179
22435
  return this.workspaceStore;
@@ -22196,13 +22452,22 @@ var CloudSessionProxy = class {
22196
22452
  async clearPersistedSession() {
22197
22453
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
22198
22454
  }
22455
+ async invalidateSession() {
22456
+ await this.automation?.close().catch(() => void 0);
22457
+ this.automation = void 0;
22458
+ this.client = void 0;
22459
+ this.sessionId = void 0;
22460
+ this.semanticGrant = void 0;
22461
+ this.liveSessionStateEstablished = false;
22462
+ await this.clearPersistedSession();
22463
+ }
22199
22464
  async isReusableCloudSession(sessionId, timeout) {
22200
22465
  try {
22201
22466
  const session = await this.cloud.getSession(sessionId, {
22202
22467
  signal: timeout?.signal,
22203
22468
  timeoutMs: timeout?.remainingMs()
22204
22469
  });
22205
- return session.status !== "closed" && session.status !== "failed";
22470
+ return isReusableCloudSessionState(session);
22206
22471
  } catch (error) {
22207
22472
  if (isMissingCloudSessionError(error)) {
22208
22473
  return false;
@@ -22251,26 +22516,79 @@ var CloudSessionProxy = class {
22251
22516
  try {
22252
22517
  await this.ensureSemanticGrant(true);
22253
22518
  return true;
22254
- } catch {
22519
+ } catch (refreshError) {
22520
+ if (await this.resetStaleSession(refreshError)) {
22521
+ throw refreshError;
22522
+ }
22255
22523
  return false;
22256
22524
  }
22257
22525
  }
22258
22526
  async invokeSemanticOperation(operation, input, sessionInit = {}) {
22259
- return this.runOperationWithPolicy(operation, async (timeout) => {
22527
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22260
22528
  await this.ensureSession(sessionInit, timeout);
22261
22529
  await this.ensureSemanticGrant(false, timeout);
22262
- return this.requireClient().invoke(operation, input, {
22530
+ const output = await this.requireClient().invoke(operation, input, {
22263
22531
  signal: timeout.signal,
22264
22532
  timeoutMs: timeout.remainingMs()
22265
22533
  });
22534
+ this.noteSuccessfulLiveOperation(operation);
22535
+ return output;
22266
22536
  });
22267
22537
  }
22268
22538
  async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
22269
- return this.runOperationWithPolicy(operation, async (timeout) => {
22539
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22270
22540
  await this.ensureSession(sessionInit, timeout);
22271
- return invoke(this.requireAutomation());
22541
+ const output = await invoke(this.requireAutomation());
22542
+ this.noteSuccessfulLiveOperation(operation);
22543
+ return output;
22272
22544
  });
22273
22545
  }
22546
+ async invokeBootstrapInstrumentationOperation(operation, invoke) {
22547
+ let recovered = false;
22548
+ while (true) {
22549
+ try {
22550
+ await this.ensureSession();
22551
+ return await invoke(this.requireAutomation());
22552
+ } catch (error) {
22553
+ const stale = await this.resetStaleSession(error);
22554
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
22555
+ throw error;
22556
+ }
22557
+ recovered = true;
22558
+ }
22559
+ }
22560
+ }
22561
+ async runOperationWithSessionRecovery(operation, invoke) {
22562
+ return this.runOperationWithPolicy(operation, async (timeout) => {
22563
+ let recovered = false;
22564
+ while (true) {
22565
+ try {
22566
+ return await invoke(timeout);
22567
+ } catch (error) {
22568
+ const stale = await this.resetStaleSession(error);
22569
+ if (!stale || recovered || !this.canRecoverWithFreshSession(operation)) {
22570
+ throw error;
22571
+ }
22572
+ recovered = true;
22573
+ }
22574
+ }
22575
+ });
22576
+ }
22577
+ async resetStaleSession(error) {
22578
+ if (!isRecoverableCloudSessionError(error)) {
22579
+ return false;
22580
+ }
22581
+ await this.invalidateSession();
22582
+ return true;
22583
+ }
22584
+ canRecoverWithFreshSession(operation) {
22585
+ return !this.liveSessionStateEstablished && isBootstrapRecoveryOperation(operation);
22586
+ }
22587
+ noteSuccessfulLiveOperation(operation) {
22588
+ 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") {
22589
+ this.liveSessionStateEstablished = true;
22590
+ }
22591
+ }
22274
22592
  async runOperationWithPolicy(operation, invoke) {
22275
22593
  return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
22276
22594
  }
@@ -22294,8 +22612,29 @@ function assertSupportedCloudBrowserMode(browser) {
22294
22612
  }
22295
22613
  }
22296
22614
  function isMissingCloudSessionError(error) {
22615
+ if (error instanceof OpensteerCloudRequestError) {
22616
+ return error.statusCode === 404 && (error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND");
22617
+ }
22297
22618
  return error instanceof Error && /\b404\b/.test(error.message);
22298
22619
  }
22620
+ function isRecoverableCloudSessionError(error) {
22621
+ if (!(error instanceof OpensteerCloudRequestError)) {
22622
+ return false;
22623
+ }
22624
+ if (error.statusCode === 404) {
22625
+ return error.code === void 0 || error.code === "CLOUD_SESSION_NOT_FOUND";
22626
+ }
22627
+ return error.statusCode === 409 && error.code === "CLOUD_SESSION_STALE";
22628
+ }
22629
+ function isBootstrapRecoveryOperation(operation) {
22630
+ return operation === "session.open" || operation === "instrumentation.route" || operation === "instrumentation.intercept-script";
22631
+ }
22632
+ function isReusableCloudSessionState(session) {
22633
+ if (session.status === "closing" || session.status === "closed" || session.status === "failed") {
22634
+ return false;
22635
+ }
22636
+ return !(typeof session.expiresAt === "number" && session.expiresAt <= Date.now() + CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS);
22637
+ }
22299
22638
  function isLoopbackBaseUrl(baseUrl) {
22300
22639
  let url;
22301
22640
  try {
@@ -22343,39 +22682,6 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
22343
22682
  );
22344
22683
  }
22345
22684
  };
22346
- var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
22347
- constructor(options) {
22348
- const rootPath = options.rootPath ?? path2.resolve(options.rootDir ?? process.cwd());
22349
- const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
22350
- const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
22351
- assertSupportedEngineOptions({
22352
- engineName,
22353
- ...options.browser === void 0 ? {} : { browser: options.browser },
22354
- ...options.context === void 0 ? {} : { context: options.context }
22355
- });
22356
- super(
22357
- buildSharedRuntimeOptions({
22358
- name: options.name,
22359
- ...options.rootDir === void 0 ? {} : { rootDir: options.rootDir },
22360
- rootPath,
22361
- ...options.environment === void 0 ? {} : { environment: options.environment },
22362
- ...options.browser === void 0 ? {} : { browser: options.browser },
22363
- ...options.launch === void 0 ? {} : { launch: options.launch },
22364
- ...options.context === void 0 ? {} : { context: options.context },
22365
- engineName,
22366
- ...options.engine === void 0 ? {} : { engine: options.engine },
22367
- ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
22368
- ...options.policy === void 0 ? {} : { policy: options.policy },
22369
- ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
22370
- ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
22371
- cleanupRootOnClose,
22372
- ...options.observability === void 0 ? {} : { observability: options.observability },
22373
- ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
22374
- ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
22375
- })
22376
- );
22377
- }
22378
- };
22379
22685
  function buildSharedRuntimeOptions(input) {
22380
22686
  const ownership = resolveOwnership(input.browser);
22381
22687
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
@@ -22467,6 +22773,6 @@ function createOpensteerSemanticRuntime(input = {}) {
22467
22773
  });
22468
22774
  }
22469
22775
 
22470
- export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22471
- //# sourceMappingURL=chunk-GEUHKPC2.js.map
22472
- //# sourceMappingURL=chunk-GEUHKPC2.js.map
22776
+ export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isBrowserCoreError, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22777
+ //# sourceMappingURL=chunk-BVRIPCWA.js.map
22778
+ //# sourceMappingURL=chunk-BVRIPCWA.js.map