opensteer 0.9.5 → 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.
@@ -1,4 +1,4 @@
1
- import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext, OpensteerBrowserManager } from './chunk-GSCQQKZZ.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
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';
@@ -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 },
@@ -333,7 +315,6 @@ var defaultNavigationSettleObserver = {
333
315
  };
334
316
  Object.freeze(defaultNavigationSettleObserver);
335
317
  var DEFAULT_SETTLE_OBSERVERS = Object.freeze([
336
- defaultSnapshotSettleObserver,
337
318
  defaultDomActionSettleObserver,
338
319
  defaultNavigationSettleObserver
339
320
  ]);
@@ -412,72 +393,6 @@ function abortError() {
412
393
  return error;
413
394
  }
414
395
 
415
- // ../protocol/src/json.ts
416
- var JSON_SCHEMA_DRAFT_2020_12 = "https://json-schema.org/draft/2020-12/schema";
417
- function defineSchema(schema) {
418
- return schema;
419
- }
420
- function stringSchema(options = {}) {
421
- return defineSchema({
422
- type: "string",
423
- ...options
424
- });
425
- }
426
- function numberSchema(options = {}) {
427
- return defineSchema({
428
- type: "number",
429
- ...options
430
- });
431
- }
432
- function integerSchema(options = {}) {
433
- return defineSchema({
434
- type: "integer",
435
- ...options
436
- });
437
- }
438
- function literalSchema(value, options = {}) {
439
- return defineSchema({
440
- const: value,
441
- ...options
442
- });
443
- }
444
- function enumSchema(values, options = {}) {
445
- return defineSchema({
446
- enum: values,
447
- ...options
448
- });
449
- }
450
- function arraySchema(items, options = {}) {
451
- return defineSchema({
452
- type: "array",
453
- items,
454
- ...options
455
- });
456
- }
457
- function objectSchema(properties, options = {}) {
458
- const { required, additionalProperties, ...rest } = options;
459
- return defineSchema({
460
- type: "object",
461
- properties,
462
- ...rest,
463
- ...required === void 0 ? {} : { required },
464
- ...additionalProperties === void 0 ? { additionalProperties: false } : { additionalProperties }
465
- });
466
- }
467
- function recordSchema(valueSchema, options = {}) {
468
- return defineSchema({
469
- type: "object",
470
- additionalProperties: valueSchema,
471
- ...options
472
- });
473
- }
474
- function oneOfSchema(members, options = {}) {
475
- return defineSchema({
476
- oneOf: members,
477
- ...options
478
- });
479
- }
480
-
481
396
  // ../protocol/src/validation.ts
482
397
  function validateJsonSchema(schema, value, path6 = "$") {
483
398
  return validateSchemaNode(schema, value, path6);
@@ -2387,152 +2302,6 @@ var hitTestResultSchema = objectSchema(
2387
2302
  }
2388
2303
  );
2389
2304
 
2390
- // ../protocol/src/capabilities.ts
2391
- var opensteerCapabilities = [
2392
- "sessions.manage",
2393
- "pages.manage",
2394
- "pages.navigate",
2395
- "input.pointer",
2396
- "input.keyboard",
2397
- "input.touch",
2398
- "artifacts.screenshot",
2399
- "execution.pause",
2400
- "execution.resume",
2401
- "execution.freeze",
2402
- "inspect.pages",
2403
- "inspect.frames",
2404
- "inspect.html",
2405
- "inspect.domSnapshot",
2406
- "inspect.text",
2407
- "inspect.attributes",
2408
- "inspect.hitTest",
2409
- "inspect.viewportMetrics",
2410
- "inspect.network",
2411
- "inspect.networkBodies",
2412
- "inspect.cookies",
2413
- "inspect.localStorage",
2414
- "inspect.sessionStorage",
2415
- "inspect.indexedDb",
2416
- "transport.sessionHttp",
2417
- "instrumentation.initScripts",
2418
- "instrumentation.routing",
2419
- "events.pageLifecycle",
2420
- "events.dialog",
2421
- "events.download",
2422
- "events.chooser",
2423
- "events.worker",
2424
- "events.console",
2425
- "events.pageError",
2426
- "events.websocket",
2427
- "events.eventStream",
2428
- "events.executionState",
2429
- "surface.rest",
2430
- "surface.mcp"
2431
- ];
2432
- var opensteerCapabilitySchema = enumSchema(opensteerCapabilities, {
2433
- title: "OpensteerCapability"
2434
- });
2435
- var opensteerCapabilitySetSchema = arraySchema(opensteerCapabilitySchema, {
2436
- title: "OpensteerCapabilitySet",
2437
- uniqueItems: true
2438
- });
2439
- var opensteerCapabilityDescriptorSchema = objectSchema(
2440
- {
2441
- key: opensteerCapabilitySchema,
2442
- description: stringSchema(),
2443
- stability: enumSchema(["stable", "experimental"])
2444
- },
2445
- {
2446
- title: "OpensteerCapabilityDescriptor",
2447
- required: ["key", "description", "stability"]
2448
- }
2449
- );
2450
- arraySchema(
2451
- opensteerCapabilityDescriptorSchema,
2452
- {
2453
- title: "OpensteerCapabilityDescriptorList",
2454
- uniqueItems: true
2455
- }
2456
- );
2457
-
2458
- // ../protocol/src/errors.ts
2459
- var opensteerErrorCodes = [
2460
- "invalid-request",
2461
- "invalid-argument",
2462
- "invalid-ref",
2463
- "unsupported-version",
2464
- "unsupported-operation",
2465
- "unsupported-capability",
2466
- "not-found",
2467
- "stale-node-ref",
2468
- "session-closed",
2469
- "page-closed",
2470
- "frame-detached",
2471
- "timeout",
2472
- "navigation-failed",
2473
- "permission-denied",
2474
- "conflict",
2475
- "profile-unavailable",
2476
- "auth-failure",
2477
- "auth-recovery-failed",
2478
- "browser-required",
2479
- "rate-limited",
2480
- "operation-failed",
2481
- "internal"
2482
- ];
2483
- var OpensteerProtocolError = class extends Error {
2484
- code;
2485
- retriable;
2486
- capability;
2487
- details;
2488
- constructor(code, message, options = {}) {
2489
- super(message, { cause: options.cause });
2490
- this.name = "OpensteerProtocolError";
2491
- this.code = code;
2492
- this.retriable = options.retriable ?? false;
2493
- this.capability = options.capability;
2494
- this.details = options.details;
2495
- }
2496
- };
2497
- function createOpensteerError(code, message, options = {}) {
2498
- return {
2499
- code,
2500
- message,
2501
- retriable: options.retriable ?? false,
2502
- ...options.capability === void 0 ? {} : { capability: options.capability },
2503
- ...options.details === void 0 ? {} : { details: options.details }
2504
- };
2505
- }
2506
- function isOpensteerProtocolError(value) {
2507
- return value instanceof OpensteerProtocolError;
2508
- }
2509
- function toOpensteerError(error) {
2510
- return createOpensteerError(error.code, error.message, {
2511
- retriable: error.retriable,
2512
- ...error.capability === void 0 ? {} : { capability: error.capability },
2513
- ...error.details === void 0 ? {} : { details: error.details }
2514
- });
2515
- }
2516
- var opensteerErrorCodeSchema = {
2517
- title: "OpensteerErrorCode",
2518
- enum: opensteerErrorCodes
2519
- };
2520
- var opensteerErrorSchema = objectSchema(
2521
- {
2522
- code: opensteerErrorCodeSchema,
2523
- message: stringSchema(),
2524
- retriable: {
2525
- type: "boolean"
2526
- },
2527
- capability: opensteerCapabilitySchema,
2528
- details: recordSchema({})
2529
- },
2530
- {
2531
- title: "OpensteerError",
2532
- required: ["code", "message", "retriable"]
2533
- }
2534
- );
2535
-
2536
2305
  // ../protocol/src/events.ts
2537
2306
  function eventBaseSchema(kind) {
2538
2307
  return {
@@ -5110,6 +4879,9 @@ function buildClauseSelector(node, clause) {
5110
4879
  if (!clause || typeof clause !== "object") {
5111
4880
  return "";
5112
4881
  }
4882
+ if (clause.kind === "text") {
4883
+ return "";
4884
+ }
5113
4885
  if (clause.kind === "position") {
5114
4886
  if (clause.axis === "nthOfType") {
5115
4887
  return `:nth-of-type(${Math.max(1, Number(node.position?.nthOfType || 1))})`;
@@ -5234,7 +5006,7 @@ function resolveExtractedValueInContext(normalizedValue, options) {
5234
5006
  function stripPositionClauses(nodes) {
5235
5007
  return (nodes || []).map((node) => ({
5236
5008
  ...node,
5237
- match: (node.match || []).filter((clause) => clause.kind !== "position")
5009
+ match: (node.match || []).filter((clause) => clause.kind !== "position" && clause.kind !== "text")
5238
5010
  }));
5239
5011
  }
5240
5012
  function dedupeSelectors(selectors) {
@@ -5811,9 +5583,17 @@ function resolveDomPathInScope(index, domPath, scope) {
5811
5583
  if (!candidates.length) {
5812
5584
  return null;
5813
5585
  }
5586
+ const lastNode = domPath[domPath.length - 1];
5587
+ const textClauses = lastNode?.match.filter((c) => c.kind === "text") ?? [];
5814
5588
  let fallback = null;
5815
5589
  for (const selector of candidates) {
5816
- 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
+ }
5817
5597
  if (matches.length === 1) {
5818
5598
  return {
5819
5599
  node: matches[0],
@@ -5833,6 +5613,10 @@ function resolveDomPathInScope(index, domPath, scope) {
5833
5613
  }
5834
5614
  return fallback;
5835
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
+ }
5836
5620
  function queryAllDomPathInScope(index, domPath, scope) {
5837
5621
  const selectors = buildPathCandidates(domPath);
5838
5622
  for (const selector of selectors) {
@@ -5991,7 +5775,13 @@ function clonePathNode(node) {
5991
5775
  };
5992
5776
  }
5993
5777
  function cloneMatchClause(clause) {
5994
- 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 {
5995
5785
  kind: "attr",
5996
5786
  key: clause.key,
5997
5787
  ...clause.op === void 0 ? {} : { op: clause.op },
@@ -6046,6 +5836,13 @@ function normalizeMatch(rawMatch, attrs, position, tag) {
6046
5836
  op,
6047
5837
  ...value === void 0 ? {} : { value }
6048
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
+ }
6049
5846
  }
6050
5847
  }
6051
5848
  }
@@ -6411,7 +6208,7 @@ function isTimeoutError(error) {
6411
6208
  }
6412
6209
 
6413
6210
  // ../runtime-core/src/runtimes/dom/executor.ts
6414
- var MAX_DOM_ACTION_ATTEMPTS = 3;
6211
+ var MAX_DOM_ACTION_ATTEMPTS = 2;
6415
6212
  var DEFAULT_SCROLL_OPTIONS = {
6416
6213
  block: "center",
6417
6214
  inline: "center"
@@ -6898,11 +6695,12 @@ var DomActionExecutor = class {
6898
6695
  throw this.createActionabilityError(
6899
6696
  operation,
6900
6697
  "obscured",
6901
- `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`,
6902
6699
  {
6903
6700
  ...details,
6904
6701
  hitRelation: assessment.relation,
6905
6702
  ...assessment.ambiguous === void 0 ? {} : { hitAmbiguous: assessment.ambiguous },
6703
+ ...assessment.blockingDescription === void 0 ? {} : { blockingDescription: assessment.blockingDescription },
6906
6704
  ...assessment.canonicalTarget === void 0 ? {} : {
6907
6705
  canonicalNodeRef: assessment.canonicalTarget.nodeRef,
6908
6706
  canonicalDocumentRef: assessment.canonicalTarget.documentRef,
@@ -6916,8 +6714,7 @@ var DomActionExecutor = class {
6916
6714
  hitMissingFromSnapshot: !resolved.snapshot.nodes.some(
6917
6715
  (node) => node.nodeRef === hit.nodeRef
6918
6716
  )
6919
- },
6920
- true
6717
+ }
6921
6718
  );
6922
6719
  }
6923
6720
  async resolveActionablePointerTarget(session, operation, resolved) {
@@ -7129,7 +6926,12 @@ var DefaultDomRuntime = class {
7129
6926
  });
7130
6927
  }
7131
6928
  async buildPath(input) {
7132
- 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
+ );
7133
6935
  }
7134
6936
  async resolveTarget(input) {
7135
6937
  return this.withSnapshotSession((session) => this.resolveTargetWithSession(session, input));
@@ -7349,7 +7151,7 @@ var DefaultDomRuntime = class {
7349
7151
  if (resolvedByLocator) {
7350
7152
  const { snapshot, node } = resolvedByLocator;
7351
7153
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7352
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7154
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch: true });
7353
7155
  return this.createResolvedTarget("live", snapshot, node, anchor, {
7354
7156
  ...target.persist === void 0 ? {} : { persist: target.persist },
7355
7157
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7367,11 +7169,12 @@ var DefaultDomRuntime = class {
7367
7169
  const { snapshot, node } = resolved;
7368
7170
  const anchor = await this.buildAnchorFromSnapshotNode(session, snapshot, node);
7369
7171
  const writeDescriptor = descriptorWriter ?? ((input) => this.descriptors.write(input));
7370
- const replayPath = await this.tryBuildPathFromNode(snapshot, node);
7172
+ const enableTextMatch = method !== "extract";
7173
+ const replayPath = await this.tryBuildPathFromNode(snapshot, node, { enableTextMatch });
7371
7174
  const descriptor = target.persist === void 0 ? void 0 : await writeDescriptor({
7372
7175
  method,
7373
7176
  persist: target.persist,
7374
- path: replayPath ?? await this.buildPathForNode(snapshot, node),
7177
+ path: replayPath ?? await this.buildPathForNode(snapshot, node, { enableTextMatch }),
7375
7178
  sourceUrl: snapshot.url
7376
7179
  });
7377
7180
  return this.createResolvedTarget("selector", snapshot, node, anchor, {
@@ -7471,7 +7274,7 @@ var DefaultDomRuntime = class {
7471
7274
  `Unable to resolve structural anchor "${buildPathSelectorHint(anchor)}" in the current session`
7472
7275
  );
7473
7276
  }
7474
- const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node);
7277
+ const replayPath = await this.tryBuildPathFromNode(context.snapshot, target.node, { enableTextMatch: true });
7475
7278
  return this.createResolvedTarget(source, context.snapshot, target.node, anchor, {
7476
7279
  ...persist === void 0 ? {} : { persist },
7477
7280
  ...replayPath === void 0 ? {} : { replayPath }
@@ -7642,19 +7445,20 @@ var DefaultDomRuntime = class {
7642
7445
  }
7643
7446
  return this.bridge;
7644
7447
  }
7645
- async buildPathForNode(snapshot, node) {
7448
+ async buildPathForNode(snapshot, node, options) {
7646
7449
  if (node.nodeRef === void 0) {
7647
7450
  throw new Error(
7648
7451
  `snapshot node ${String(node.snapshotNodeId)} does not expose a live node reference`
7649
7452
  );
7650
7453
  }
7651
7454
  return this.buildPath({
7652
- locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef)
7455
+ locator: createNodeLocator(snapshot.documentRef, snapshot.documentEpoch, node.nodeRef),
7456
+ ...options?.enableTextMatch ? { enableTextMatch: true } : {}
7653
7457
  });
7654
7458
  }
7655
- async tryBuildPathFromNode(snapshot, node) {
7459
+ async tryBuildPathFromNode(snapshot, node, options) {
7656
7460
  try {
7657
- return await this.buildPathForNode(snapshot, node);
7461
+ return await this.buildPathForNode(snapshot, node, options);
7658
7462
  } catch {
7659
7463
  return void 0;
7660
7464
  }
@@ -8268,6 +8072,9 @@ function relaxPathForSingleSample(path6, mode) {
8268
8072
  }
8269
8073
  return !isLast;
8270
8074
  }
8075
+ if (clause.kind === "text") {
8076
+ return false;
8077
+ }
8271
8078
  const key = String(clause.key || "").trim().toLowerCase();
8272
8079
  if (!key || !shouldKeepAttrForSingleSample(key)) {
8273
8080
  return false;
@@ -8370,9 +8177,11 @@ function buildNodeStructure(node) {
8370
8177
  }
8371
8178
  structuralAttrs[key] = value;
8372
8179
  }
8373
- const matchClauses = (node.match || []).map(
8374
- (clause) => clause.kind === "position" ? `position:${clause.axis}` : `attr:${String(clause.key || "").trim().toLowerCase()}`
8375
- ).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();
8376
8185
  return {
8377
8186
  tag,
8378
8187
  attrs: structuralAttrs,
@@ -8778,6 +8587,9 @@ function mergeMatchByMajority(matchLists, attrs, threshold, positionFlags = {
8778
8587
  });
8779
8588
  continue;
8780
8589
  }
8590
+ if (clause.kind === "text") {
8591
+ continue;
8592
+ }
8781
8593
  if (clause.axis === "nthOfType") {
8782
8594
  if (positionFlags.hasNthOfType) {
8783
8595
  merged.push({ kind: "position", axis: "nthOfType" });
@@ -9773,7 +9585,8 @@ function assertProviderSupportsEngine(provider, engine) {
9773
9585
  return;
9774
9586
  }
9775
9587
  if (provider === "cloud") {
9776
- throw new Error(
9588
+ throw new OpensteerProtocolError(
9589
+ "invalid-argument",
9777
9590
  "ABP is not supported for provider=cloud. Cloud provider currently requires Playwright."
9778
9591
  );
9779
9592
  }
@@ -9783,7 +9596,8 @@ function normalizeOpensteerProviderMode(value, source = "OPENSTEER_PROVIDER") {
9783
9596
  if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
9784
9597
  return normalized;
9785
9598
  }
9786
- throw new Error(
9599
+ throw new OpensteerProtocolError(
9600
+ "invalid-argument",
9787
9601
  `${source} must be one of ${OPENSTEER_PROVIDER_MODES.join(", ")}; received "${value}".`
9788
9602
  );
9789
9603
  }
@@ -10223,6 +10037,24 @@ async function sleep(ms) {
10223
10037
  // src/cloud/client.ts
10224
10038
  var CLOUD_CLOSE_TIMEOUT_MS = 6e4;
10225
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
+ };
10226
10058
  var OpensteerCloudClient = class {
10227
10059
  constructor(config) {
10228
10060
  this.config = config;
@@ -10398,7 +10230,11 @@ var OpensteerCloudClient = class {
10398
10230
  });
10399
10231
  }
10400
10232
  if (!response.ok) {
10401
- 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
+ });
10402
10238
  }
10403
10239
  return response;
10404
10240
  }
@@ -10446,6 +10282,36 @@ function wrapCloudFetchError(error, input) {
10446
10282
  wrapped.name = error.name;
10447
10283
  return wrapped;
10448
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
+ }
10449
10315
 
10450
10316
  // src/cloud/config.ts
10451
10317
  function resolveCloudConfig(input = {}) {
@@ -10621,7 +10487,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10621
10487
 
10622
10488
  // ../runtime-core/package.json
10623
10489
  var package_default = {
10624
- version: "0.2.4"};
10490
+ version: "0.2.5"};
10625
10491
 
10626
10492
  // ../runtime-core/src/version.ts
10627
10493
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -22224,6 +22090,7 @@ function payloadByteLength(value) {
22224
22090
 
22225
22091
  // src/cloud/session-proxy.ts
22226
22092
  var TEMPORARY_CLOUD_WORKSPACE_PREFIX = "opensteer-cloud-workspace-";
22093
+ var CLOUD_SESSION_REUSE_EXPIRY_SKEW_MS = 1e4;
22227
22094
  var CloudSessionProxy = class {
22228
22095
  rootPath;
22229
22096
  workspace;
@@ -22237,6 +22104,8 @@ var CloudSessionProxy = class {
22237
22104
  automation;
22238
22105
  workspaceStore;
22239
22106
  syncWorkspaceOnClose = false;
22107
+ liveSessionStateEstablished = false;
22108
+ storedInstrumentation = [];
22240
22109
  constructor(cloud, options = {}) {
22241
22110
  this.cloud = cloud;
22242
22111
  this.workspace = options.workspace;
@@ -22266,15 +22135,12 @@ var CloudSessionProxy = class {
22266
22135
  if (this.client === void 0 && this.sessionId === void 0 && persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
22267
22136
  this.bindClient(persisted);
22268
22137
  }
22269
- if (this.automation) {
22270
- try {
22271
- const sessionInfo = await this.automation.getSessionInfo();
22272
- return {
22273
- ...sessionInfo,
22274
- ...this.workspace === void 0 ? {} : { workspace: this.workspace }
22275
- };
22276
- } catch {
22277
- }
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
+ };
22278
22144
  }
22279
22145
  return {
22280
22146
  provider: {
@@ -22386,12 +22252,26 @@ var CloudSessionProxy = class {
22386
22252
  return this.invokeSemanticOperation("session.cookies", input);
22387
22253
  }
22388
22254
  async route(input) {
22389
- await this.ensureSession();
22390
- 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;
22391
22264
  }
22392
22265
  async interceptScript(input) {
22393
- await this.ensureSession();
22394
- 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;
22395
22275
  }
22396
22276
  async getStorageSnapshot(input = {}) {
22397
22277
  return this.invokeSemanticOperation("session.storage", input);
@@ -22439,6 +22319,8 @@ var CloudSessionProxy = class {
22439
22319
  this.client = void 0;
22440
22320
  this.sessionId = void 0;
22441
22321
  this.semanticGrant = void 0;
22322
+ this.liveSessionStateEstablished = false;
22323
+ this.storedInstrumentation.length = 0;
22442
22324
  if (this.cleanupRootOnClose) {
22443
22325
  await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
22444
22326
  }
@@ -22466,6 +22348,8 @@ var CloudSessionProxy = class {
22466
22348
  this.automation = void 0;
22467
22349
  this.sessionId = void 0;
22468
22350
  this.semanticGrant = void 0;
22351
+ this.liveSessionStateEstablished = false;
22352
+ this.storedInstrumentation.length = 0;
22469
22353
  if (syncError !== void 0) {
22470
22354
  throw syncError;
22471
22355
  }
@@ -22513,6 +22397,7 @@ var CloudSessionProxy = class {
22513
22397
  };
22514
22398
  await this.writePersistedSession(record);
22515
22399
  this.bindClient(record, session.initialGrants?.semantic);
22400
+ await this.restoreStoredInstrumentation();
22516
22401
  }
22517
22402
  async syncWorkspaceToCloud() {
22518
22403
  if (this.workspace === void 0) {
@@ -22524,6 +22409,7 @@ var CloudSessionProxy = class {
22524
22409
  bindClient(record, initialSemanticGrant) {
22525
22410
  this.sessionId = record.sessionId;
22526
22411
  this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
22412
+ this.liveSessionStateEstablished = false;
22527
22413
  this.client = new OpensteerSemanticRestClient({
22528
22414
  getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
22529
22415
  getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
@@ -22531,6 +22417,19 @@ var CloudSessionProxy = class {
22531
22417
  });
22532
22418
  this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
22533
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
+ }
22534
22433
  async ensureWorkspaceStore() {
22535
22434
  if (this.workspaceStore !== void 0) {
22536
22435
  return this.workspaceStore;
@@ -22553,13 +22452,22 @@ var CloudSessionProxy = class {
22553
22452
  async clearPersistedSession() {
22554
22453
  await clearPersistedSessionRecord(this.rootPath, "cloud").catch(() => void 0);
22555
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
+ }
22556
22464
  async isReusableCloudSession(sessionId, timeout) {
22557
22465
  try {
22558
22466
  const session = await this.cloud.getSession(sessionId, {
22559
22467
  signal: timeout?.signal,
22560
22468
  timeoutMs: timeout?.remainingMs()
22561
22469
  });
22562
- return session.status !== "closed" && session.status !== "failed";
22470
+ return isReusableCloudSessionState(session);
22563
22471
  } catch (error) {
22564
22472
  if (isMissingCloudSessionError(error)) {
22565
22473
  return false;
@@ -22608,26 +22516,79 @@ var CloudSessionProxy = class {
22608
22516
  try {
22609
22517
  await this.ensureSemanticGrant(true);
22610
22518
  return true;
22611
- } catch {
22519
+ } catch (refreshError) {
22520
+ if (await this.resetStaleSession(refreshError)) {
22521
+ throw refreshError;
22522
+ }
22612
22523
  return false;
22613
22524
  }
22614
22525
  }
22615
22526
  async invokeSemanticOperation(operation, input, sessionInit = {}) {
22616
- return this.runOperationWithPolicy(operation, async (timeout) => {
22527
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22617
22528
  await this.ensureSession(sessionInit, timeout);
22618
22529
  await this.ensureSemanticGrant(false, timeout);
22619
- return this.requireClient().invoke(operation, input, {
22530
+ const output = await this.requireClient().invoke(operation, input, {
22620
22531
  signal: timeout.signal,
22621
22532
  timeoutMs: timeout.remainingMs()
22622
22533
  });
22534
+ this.noteSuccessfulLiveOperation(operation);
22535
+ return output;
22623
22536
  });
22624
22537
  }
22625
22538
  async invokeAutomationOperation(operation, invoke, sessionInit = {}) {
22626
- return this.runOperationWithPolicy(operation, async (timeout) => {
22539
+ return this.runOperationWithSessionRecovery(operation, async (timeout) => {
22627
22540
  await this.ensureSession(sessionInit, timeout);
22628
- return invoke(this.requireAutomation());
22541
+ const output = await invoke(this.requireAutomation());
22542
+ this.noteSuccessfulLiveOperation(operation);
22543
+ return output;
22544
+ });
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
+ }
22629
22575
  });
22630
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
+ }
22631
22592
  async runOperationWithPolicy(operation, invoke) {
22632
22593
  return runWithPolicyTimeout(this.policy.timeout, { operation }, invoke);
22633
22594
  }
@@ -22651,8 +22612,29 @@ function assertSupportedCloudBrowserMode(browser) {
22651
22612
  }
22652
22613
  }
22653
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
+ }
22654
22618
  return error instanceof Error && /\b404\b/.test(error.message);
22655
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
+ }
22656
22638
  function isLoopbackBaseUrl(baseUrl) {
22657
22639
  let url;
22658
22640
  try {
@@ -22791,6 +22773,6 @@ function createOpensteerSemanticRuntime(input = {}) {
22791
22773
  });
22792
22774
  }
22793
22775
 
22794
- 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, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22795
- //# sourceMappingURL=chunk-7LQL5YUR.js.map
22796
- //# sourceMappingURL=chunk-7LQL5YUR.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