opensteer 0.4.5 → 0.4.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.
package/dist/index.cjs CHANGED
@@ -1026,11 +1026,33 @@ function parseMode(value, source) {
1026
1026
  `Invalid ${source} value "${value}". Use "local" or "remote".`
1027
1027
  );
1028
1028
  }
1029
+ function parseAuthScheme(value, source) {
1030
+ if (value == null) return void 0;
1031
+ if (typeof value !== "string") {
1032
+ throw new Error(
1033
+ `Invalid ${source} value "${String(value)}". Use "api-key" or "bearer".`
1034
+ );
1035
+ }
1036
+ const normalized = value.trim().toLowerCase();
1037
+ if (!normalized) return void 0;
1038
+ if (normalized === "api-key" || normalized === "bearer") {
1039
+ return normalized;
1040
+ }
1041
+ throw new Error(
1042
+ `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
1043
+ );
1044
+ }
1029
1045
  function resolveOpensteerApiKey() {
1030
1046
  const value = process.env.OPENSTEER_API_KEY?.trim();
1031
1047
  if (!value) return void 0;
1032
1048
  return value;
1033
1049
  }
1050
+ function resolveOpensteerAuthScheme() {
1051
+ return parseAuthScheme(
1052
+ process.env.OPENSTEER_AUTH_SCHEME,
1053
+ "OPENSTEER_AUTH_SCHEME"
1054
+ );
1055
+ }
1034
1056
  function normalizeRemoteOptions(value) {
1035
1057
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1036
1058
  return void 0;
@@ -1090,7 +1112,12 @@ function resolveConfig(input = {}) {
1090
1112
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1091
1113
  const resolved = mergeDeep(mergedWithEnv, input);
1092
1114
  const envApiKey = resolveOpensteerApiKey();
1115
+ const envAuthScheme = resolveOpensteerAuthScheme();
1093
1116
  const inputRemoteOptions = normalizeRemoteOptions(input.remote);
1117
+ const inputAuthScheme = parseAuthScheme(
1118
+ inputRemoteOptions?.authScheme,
1119
+ "remote.authScheme"
1120
+ );
1094
1121
  const inputHasRemoteApiKey = Boolean(
1095
1122
  inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
1096
1123
  );
@@ -1098,8 +1125,12 @@ function resolveConfig(input = {}) {
1098
1125
  mode: resolved.mode
1099
1126
  });
1100
1127
  if (modeSelection.mode === "remote") {
1101
- const resolvedRemote = normalizeRemoteOptions(resolved.remote);
1102
- resolved.remote = resolvedRemote ?? {};
1128
+ const resolvedRemote = normalizeRemoteOptions(resolved.remote) ?? {};
1129
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedRemote.authScheme, "remote.authScheme") ?? "api-key";
1130
+ resolved.remote = {
1131
+ ...resolvedRemote,
1132
+ authScheme
1133
+ };
1103
1134
  }
1104
1135
  if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
1105
1136
  resolved.remote = {
@@ -1146,15 +1177,52 @@ function getCallerFilePath() {
1146
1177
  // src/navigation.ts
1147
1178
  var DEFAULT_TIMEOUT = 3e4;
1148
1179
  var DEFAULT_SETTLE_MS = 750;
1180
+ var FRAME_EVALUATE_GRACE_MS = 200;
1181
+ var STEALTH_WORLD_NAME = "__opensteer_wait__";
1182
+ var StealthWaitUnavailableError = class extends Error {
1183
+ constructor(cause) {
1184
+ super("Stealth visual wait requires Chromium CDP support.", { cause });
1185
+ this.name = "StealthWaitUnavailableError";
1186
+ }
1187
+ };
1188
+ function isStealthWaitUnavailableError(error) {
1189
+ return error instanceof StealthWaitUnavailableError;
1190
+ }
1191
+ var FRAME_OWNER_VISIBILITY_FUNCTION = `function() {
1192
+ if (!(this instanceof HTMLElement)) return false;
1193
+
1194
+ var rect = this.getBoundingClientRect();
1195
+ if (rect.width <= 0 || rect.height <= 0) return false;
1196
+ if (
1197
+ rect.bottom <= 0 ||
1198
+ rect.right <= 0 ||
1199
+ rect.top >= window.innerHeight ||
1200
+ rect.left >= window.innerWidth
1201
+ ) {
1202
+ return false;
1203
+ }
1204
+
1205
+ var style = window.getComputedStyle(this);
1206
+ if (
1207
+ style.display === 'none' ||
1208
+ style.visibility === 'hidden' ||
1209
+ Number(style.opacity) === 0
1210
+ ) {
1211
+ return false;
1212
+ }
1213
+
1214
+ return true;
1215
+ }`;
1149
1216
  function buildStabilityScript(timeout, settleMs) {
1150
1217
  return `new Promise(function(resolve) {
1151
1218
  var deadline = Date.now() + ${timeout};
1152
- var timer = null;
1153
1219
  var resolved = false;
1220
+ var timer = null;
1154
1221
  var observers = [];
1155
1222
  var observedShadowRoots = [];
1156
1223
  var fonts = document.fonts;
1157
1224
  var fontsReady = !fonts || fonts.status === 'loaded';
1225
+ var lastRelevantMutationAt = Date.now();
1158
1226
 
1159
1227
  function clearObservers() {
1160
1228
  for (var i = 0; i < observers.length; i++) {
@@ -1172,9 +1240,87 @@ function buildStabilityScript(timeout, settleMs) {
1172
1240
  resolve();
1173
1241
  }
1174
1242
 
1243
+ function isElementVisiblyIntersectingViewport(element) {
1244
+ if (!(element instanceof Element)) return false;
1245
+
1246
+ var rect = element.getBoundingClientRect();
1247
+ var inViewport =
1248
+ rect.width > 0 &&
1249
+ rect.height > 0 &&
1250
+ rect.bottom > 0 &&
1251
+ rect.right > 0 &&
1252
+ rect.top < window.innerHeight &&
1253
+ rect.left < window.innerWidth;
1254
+
1255
+ if (!inViewport) return false;
1256
+
1257
+ var style = window.getComputedStyle(element);
1258
+ if (style.visibility === 'hidden' || style.display === 'none') {
1259
+ return false;
1260
+ }
1261
+ if (Number(style.opacity) === 0) {
1262
+ return false;
1263
+ }
1264
+
1265
+ return true;
1266
+ }
1267
+
1268
+ function resolveRelevantElement(node) {
1269
+ if (!node) return null;
1270
+ if (node instanceof Element) return node;
1271
+ if (typeof ShadowRoot !== 'undefined' && node instanceof ShadowRoot) {
1272
+ return node.host instanceof Element ? node.host : null;
1273
+ }
1274
+ var parentElement = node.parentElement;
1275
+ return parentElement instanceof Element ? parentElement : null;
1276
+ }
1277
+
1278
+ function isNodeVisiblyRelevant(node) {
1279
+ var element = resolveRelevantElement(node);
1280
+ if (!element) return false;
1281
+ return isElementVisiblyIntersectingViewport(element);
1282
+ }
1283
+
1284
+ function hasRelevantMutation(records) {
1285
+ for (var i = 0; i < records.length; i++) {
1286
+ var record = records[i];
1287
+ if (isNodeVisiblyRelevant(record.target)) return true;
1288
+
1289
+ var addedNodes = record.addedNodes;
1290
+ for (var j = 0; j < addedNodes.length; j++) {
1291
+ if (isNodeVisiblyRelevant(addedNodes[j])) return true;
1292
+ }
1293
+
1294
+ var removedNodes = record.removedNodes;
1295
+ for (var k = 0; k < removedNodes.length; k++) {
1296
+ if (isNodeVisiblyRelevant(removedNodes[k])) return true;
1297
+ }
1298
+ }
1299
+
1300
+ return false;
1301
+ }
1302
+
1303
+ function scheduleCheck() {
1304
+ if (resolved) return;
1305
+ if (timer) clearTimeout(timer);
1306
+
1307
+ var remaining = deadline - Date.now();
1308
+ if (remaining <= 0) {
1309
+ done();
1310
+ return;
1311
+ }
1312
+
1313
+ var checkDelay = Math.min(120, Math.max(16, ${settleMs}));
1314
+ timer = setTimeout(checkNow, checkDelay);
1315
+ }
1316
+
1175
1317
  function observeMutations(target) {
1176
1318
  if (!target) return;
1177
- var observer = new MutationObserver(function() { settle(); });
1319
+ var observer = new MutationObserver(function(records) {
1320
+ if (!hasRelevantMutation(records)) return;
1321
+ lastRelevantMutationAt = Date.now();
1322
+ scheduleCheck();
1323
+ });
1178
1324
  observer.observe(target, {
1179
1325
  childList: true,
1180
1326
  subtree: true,
@@ -1212,18 +1358,25 @@ function buildStabilityScript(timeout, settleMs) {
1212
1358
  var images = root.querySelectorAll('img');
1213
1359
  for (var i = 0; i < images.length; i++) {
1214
1360
  var img = images[i];
1215
- var rect = img.getBoundingClientRect();
1216
- var inViewport =
1217
- rect.bottom > 0 &&
1218
- rect.right > 0 &&
1219
- rect.top < window.innerHeight &&
1220
- rect.left < window.innerWidth;
1221
- if (inViewport && !img.complete) return false;
1361
+ if (!isElementVisiblyIntersectingViewport(img)) continue;
1362
+ if (!img.complete) return false;
1222
1363
  }
1223
1364
  return true;
1224
1365
  }
1225
1366
 
1226
- function hasRunningFiniteAnimations() {
1367
+ function getAnimationTarget(effect) {
1368
+ if (!effect) return null;
1369
+ var target = effect.target;
1370
+ if (target instanceof Element) return target;
1371
+
1372
+ if (target && target.element instanceof Element) {
1373
+ return target.element;
1374
+ }
1375
+
1376
+ return null;
1377
+ }
1378
+
1379
+ function hasRunningVisibleFiniteAnimations() {
1227
1380
  if (typeof document.getAnimations !== 'function') return false;
1228
1381
  var animations = document.getAnimations();
1229
1382
 
@@ -1237,6 +1390,9 @@ function buildStabilityScript(timeout, settleMs) {
1237
1390
  ? timing.endTime
1238
1391
  : Number.POSITIVE_INFINITY;
1239
1392
  if (Number.isFinite(endTime) && endTime > 0) {
1393
+ var target = getAnimationTarget(effect);
1394
+ if (!target) continue;
1395
+ if (!isElementVisiblyIntersectingViewport(target)) continue;
1240
1396
  return true;
1241
1397
  }
1242
1398
  }
@@ -1247,21 +1403,29 @@ function buildStabilityScript(timeout, settleMs) {
1247
1403
  function isVisuallyReady() {
1248
1404
  if (!fontsReady) return false;
1249
1405
  if (!checkViewportImages(document)) return false;
1250
- if (hasRunningFiniteAnimations()) return false;
1406
+ if (hasRunningVisibleFiniteAnimations()) return false;
1251
1407
  return true;
1252
1408
  }
1253
1409
 
1254
- function settle() {
1255
- if (Date.now() > deadline) { done(); return; }
1256
- if (timer) clearTimeout(timer);
1410
+ function checkNow() {
1411
+ if (Date.now() >= deadline) {
1412
+ done();
1413
+ return;
1414
+ }
1415
+
1257
1416
  observeOpenShadowRoots();
1258
- timer = setTimeout(function() {
1259
- if (isVisuallyReady()) {
1260
- done();
1261
- } else {
1262
- settle();
1263
- }
1264
- }, ${settleMs});
1417
+
1418
+ if (!isVisuallyReady()) {
1419
+ scheduleCheck();
1420
+ return;
1421
+ }
1422
+
1423
+ if (Date.now() - lastRelevantMutationAt >= ${settleMs}) {
1424
+ done();
1425
+ return;
1426
+ }
1427
+
1428
+ scheduleCheck();
1265
1429
  }
1266
1430
 
1267
1431
  observeMutations(document.documentElement);
@@ -1270,67 +1434,266 @@ function buildStabilityScript(timeout, settleMs) {
1270
1434
  if (fonts && fonts.ready && typeof fonts.ready.then === 'function') {
1271
1435
  fonts.ready.then(function() {
1272
1436
  fontsReady = true;
1273
- settle();
1437
+ scheduleCheck();
1438
+ }, function() {
1439
+ fontsReady = true;
1440
+ scheduleCheck();
1274
1441
  });
1275
1442
  }
1276
1443
 
1277
1444
  var safetyTimer = setTimeout(done, ${timeout});
1278
1445
 
1279
- settle();
1446
+ scheduleCheck();
1280
1447
  })`;
1281
1448
  }
1449
+ var StealthCdpRuntime = class _StealthCdpRuntime {
1450
+ constructor(session) {
1451
+ this.session = session;
1452
+ }
1453
+ contextsByFrame = /* @__PURE__ */ new Map();
1454
+ disposed = false;
1455
+ static async create(page) {
1456
+ let session;
1457
+ try {
1458
+ session = await page.context().newCDPSession(page);
1459
+ } catch (error) {
1460
+ throw new StealthWaitUnavailableError(error);
1461
+ }
1462
+ const runtime = new _StealthCdpRuntime(session);
1463
+ try {
1464
+ await runtime.initialize();
1465
+ return runtime;
1466
+ } catch (error) {
1467
+ await runtime.dispose();
1468
+ throw new StealthWaitUnavailableError(error);
1469
+ }
1470
+ }
1471
+ async dispose() {
1472
+ if (this.disposed) return;
1473
+ this.disposed = true;
1474
+ this.contextsByFrame.clear();
1475
+ await this.session.detach().catch(() => void 0);
1476
+ }
1477
+ async waitForMainFrameVisualStability(options) {
1478
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1479
+ const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1480
+ if (timeout <= 0) return;
1481
+ const frameRecords = await this.getFrameRecords();
1482
+ const mainFrame = frameRecords[0];
1483
+ if (!mainFrame) return;
1484
+ await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
1485
+ }
1486
+ async collectVisibleFrameIds() {
1487
+ const frameRecords = await this.getFrameRecords();
1488
+ if (frameRecords.length === 0) return [];
1489
+ const visibleFrameIds = [];
1490
+ for (const frameRecord of frameRecords) {
1491
+ if (!frameRecord.parentFrameId) {
1492
+ visibleFrameIds.push(frameRecord.frameId);
1493
+ continue;
1494
+ }
1495
+ try {
1496
+ const parentContextId = await this.ensureFrameContextId(
1497
+ frameRecord.parentFrameId
1498
+ );
1499
+ const visible = await this.isFrameOwnerVisible(
1500
+ frameRecord.frameId,
1501
+ parentContextId
1502
+ );
1503
+ if (visible) {
1504
+ visibleFrameIds.push(frameRecord.frameId);
1505
+ }
1506
+ } catch (error) {
1507
+ if (isIgnorableFrameError(error)) continue;
1508
+ throw error;
1509
+ }
1510
+ }
1511
+ return visibleFrameIds;
1512
+ }
1513
+ async waitForFrameVisualStability(frameId, timeout, settleMs) {
1514
+ if (timeout <= 0) return;
1515
+ const script = buildStabilityScript(timeout, settleMs);
1516
+ let contextId = await this.ensureFrameContextId(frameId);
1517
+ try {
1518
+ await this.evaluateWithGuard(contextId, script, timeout);
1519
+ } catch (error) {
1520
+ if (!isMissingExecutionContextError(error)) {
1521
+ throw error;
1522
+ }
1523
+ this.contextsByFrame.delete(frameId);
1524
+ contextId = await this.ensureFrameContextId(frameId);
1525
+ await this.evaluateWithGuard(contextId, script, timeout);
1526
+ }
1527
+ }
1528
+ async initialize() {
1529
+ await this.session.send("Page.enable");
1530
+ await this.session.send("Runtime.enable");
1531
+ await this.session.send("DOM.enable");
1532
+ }
1533
+ async getFrameRecords() {
1534
+ const treeResult = await this.session.send("Page.getFrameTree");
1535
+ const records = [];
1536
+ walkFrameTree(treeResult.frameTree, null, records);
1537
+ return records;
1538
+ }
1539
+ async ensureFrameContextId(frameId) {
1540
+ const cached = this.contextsByFrame.get(frameId);
1541
+ if (cached != null) {
1542
+ return cached;
1543
+ }
1544
+ const world = await this.session.send("Page.createIsolatedWorld", {
1545
+ frameId,
1546
+ worldName: STEALTH_WORLD_NAME
1547
+ });
1548
+ this.contextsByFrame.set(frameId, world.executionContextId);
1549
+ return world.executionContextId;
1550
+ }
1551
+ async evaluateWithGuard(contextId, script, timeout) {
1552
+ const evaluationPromise = this.evaluateScript(contextId, script);
1553
+ const settledPromise = evaluationPromise.then(
1554
+ () => ({ kind: "resolved" }),
1555
+ (error) => ({ kind: "rejected", error })
1556
+ );
1557
+ const timeoutPromise = sleep(
1558
+ timeout + FRAME_EVALUATE_GRACE_MS
1559
+ ).then(() => ({ kind: "timeout" }));
1560
+ const result = await Promise.race([
1561
+ settledPromise,
1562
+ timeoutPromise
1563
+ ]);
1564
+ if (result.kind === "rejected") {
1565
+ throw result.error;
1566
+ }
1567
+ }
1568
+ async evaluateScript(contextId, expression) {
1569
+ const result = await this.session.send("Runtime.evaluate", {
1570
+ contextId,
1571
+ expression,
1572
+ awaitPromise: true,
1573
+ returnByValue: true
1574
+ });
1575
+ if (result.exceptionDetails) {
1576
+ throw new Error(formatCdpException(result.exceptionDetails));
1577
+ }
1578
+ }
1579
+ async isFrameOwnerVisible(frameId, parentContextId) {
1580
+ const owner = await this.session.send("DOM.getFrameOwner", {
1581
+ frameId
1582
+ });
1583
+ const resolveParams = {
1584
+ executionContextId: parentContextId
1585
+ };
1586
+ if (typeof owner.backendNodeId === "number") {
1587
+ resolveParams.backendNodeId = owner.backendNodeId;
1588
+ } else if (typeof owner.nodeId === "number") {
1589
+ resolveParams.nodeId = owner.nodeId;
1590
+ } else {
1591
+ return false;
1592
+ }
1593
+ const resolved = await this.session.send(
1594
+ "DOM.resolveNode",
1595
+ resolveParams
1596
+ );
1597
+ const objectId = resolved.object?.objectId;
1598
+ if (!objectId) return false;
1599
+ try {
1600
+ const callResult = await this.session.send("Runtime.callFunctionOn", {
1601
+ objectId,
1602
+ functionDeclaration: FRAME_OWNER_VISIBILITY_FUNCTION,
1603
+ returnByValue: true
1604
+ });
1605
+ if (callResult.exceptionDetails) {
1606
+ throw new Error(formatCdpException(callResult.exceptionDetails));
1607
+ }
1608
+ return callResult.result.value === true;
1609
+ } finally {
1610
+ await this.releaseObject(objectId);
1611
+ }
1612
+ }
1613
+ async releaseObject(objectId) {
1614
+ await this.session.send("Runtime.releaseObject", {
1615
+ objectId
1616
+ }).catch(() => void 0);
1617
+ }
1618
+ };
1282
1619
  async function waitForVisualStability(page, options = {}) {
1283
- const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1284
- const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1285
- await waitForFrameVisualStability(page.mainFrame(), {
1286
- timeout,
1287
- settleMs
1288
- });
1620
+ const runtime = await StealthCdpRuntime.create(page);
1621
+ try {
1622
+ await runtime.waitForMainFrameVisualStability(options);
1623
+ } finally {
1624
+ await runtime.dispose();
1625
+ }
1289
1626
  }
1290
1627
  async function waitForVisualStabilityAcrossFrames(page, options = {}) {
1291
1628
  const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1292
1629
  const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1630
+ if (timeout <= 0) return;
1293
1631
  const deadline = Date.now() + timeout;
1294
- while (true) {
1295
- const remaining = Math.max(0, deadline - Date.now());
1296
- if (remaining === 0) return;
1297
- const frames = page.frames();
1298
- await Promise.all(
1299
- frames.map(async (frame) => {
1300
- try {
1301
- await waitForFrameVisualStability(frame, {
1302
- timeout: remaining,
1303
- settleMs
1304
- });
1305
- } catch (error) {
1306
- if (isIgnorableFrameError(error)) return;
1307
- throw error;
1308
- }
1309
- })
1310
- );
1311
- const currentFrames = page.frames();
1312
- if (sameFrames(frames, currentFrames)) {
1313
- return;
1632
+ const runtime = await StealthCdpRuntime.create(page);
1633
+ try {
1634
+ while (true) {
1635
+ const remaining = Math.max(0, deadline - Date.now());
1636
+ if (remaining === 0) return;
1637
+ const frameIds = await runtime.collectVisibleFrameIds();
1638
+ if (frameIds.length === 0) return;
1639
+ await Promise.all(
1640
+ frameIds.map(async (frameId) => {
1641
+ try {
1642
+ await runtime.waitForFrameVisualStability(
1643
+ frameId,
1644
+ remaining,
1645
+ settleMs
1646
+ );
1647
+ } catch (error) {
1648
+ if (isIgnorableFrameError(error)) return;
1649
+ throw error;
1650
+ }
1651
+ })
1652
+ );
1653
+ const currentFrameIds = await runtime.collectVisibleFrameIds();
1654
+ if (sameFrameIds(frameIds, currentFrameIds)) {
1655
+ return;
1656
+ }
1314
1657
  }
1658
+ } finally {
1659
+ await runtime.dispose();
1315
1660
  }
1316
1661
  }
1317
- async function waitForFrameVisualStability(frame, options) {
1318
- const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1319
- const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1320
- if (timeout <= 0) return;
1321
- await frame.evaluate(buildStabilityScript(timeout, settleMs));
1662
+ function walkFrameTree(node, parentFrameId, records) {
1663
+ const frameId = node.frame?.id;
1664
+ if (!frameId) return;
1665
+ records.push({
1666
+ frameId,
1667
+ parentFrameId
1668
+ });
1669
+ for (const child of node.childFrames ?? []) {
1670
+ walkFrameTree(child, frameId, records);
1671
+ }
1322
1672
  }
1323
- function sameFrames(before, after) {
1673
+ function sameFrameIds(before, after) {
1324
1674
  if (before.length !== after.length) return false;
1325
- for (const frame of before) {
1326
- if (!after.includes(frame)) return false;
1675
+ for (const frameId of before) {
1676
+ if (!after.includes(frameId)) return false;
1327
1677
  }
1328
1678
  return true;
1329
1679
  }
1680
+ function formatCdpException(details) {
1681
+ return details.exception?.description || details.text || "CDP runtime evaluation failed.";
1682
+ }
1683
+ function isMissingExecutionContextError(error) {
1684
+ if (!(error instanceof Error)) return false;
1685
+ const message = error.message;
1686
+ return message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
1687
+ }
1330
1688
  function isIgnorableFrameError(error) {
1331
1689
  if (!(error instanceof Error)) return false;
1332
1690
  const message = error.message;
1333
- return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed");
1691
+ return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed") || message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context") || message.includes("No frame for given id found");
1692
+ }
1693
+ function sleep(ms) {
1694
+ return new Promise((resolve) => {
1695
+ setTimeout(resolve, ms);
1696
+ });
1334
1697
  }
1335
1698
 
1336
1699
  // src/storage/local.ts
@@ -2231,6 +2594,66 @@ var OS_BOUNDARY_ATTR = "data-os-boundary";
2231
2594
  var OS_UNAVAILABLE_ATTR = "data-os-unavailable";
2232
2595
  var OS_IFRAME_BOUNDARY_TAG = "os-iframe-root";
2233
2596
  var OS_SHADOW_BOUNDARY_TAG = "os-shadow-root";
2597
+ function decodeSerializedNodeTableEntry(nodeTable, rawIndex, label) {
2598
+ if (typeof rawIndex !== "number" || !Number.isInteger(rawIndex) || rawIndex < 0 || rawIndex >= nodeTable.length) {
2599
+ throw new Error(
2600
+ `Invalid serialized path node index at "${label}": expected a valid table index.`
2601
+ );
2602
+ }
2603
+ const node = nodeTable[rawIndex];
2604
+ if (!node || typeof node !== "object") {
2605
+ throw new Error(
2606
+ `Invalid serialized path node at "${label}": table entry is missing.`
2607
+ );
2608
+ }
2609
+ return node;
2610
+ }
2611
+ function decodeSerializedDomPath(nodeTable, rawPath, label) {
2612
+ if (!Array.isArray(rawPath)) {
2613
+ throw new Error(
2614
+ `Invalid serialized path at "${label}": expected an array of node indexes.`
2615
+ );
2616
+ }
2617
+ return rawPath.map(
2618
+ (value, index) => decodeSerializedNodeTableEntry(nodeTable, value, `${label}[${index}]`)
2619
+ );
2620
+ }
2621
+ function decodeSerializedElementPath(nodeTable, rawPath, label) {
2622
+ if (!rawPath || typeof rawPath !== "object") {
2623
+ throw new Error(
2624
+ `Invalid serialized element path at "${label}": expected an object.`
2625
+ );
2626
+ }
2627
+ if (rawPath.context !== void 0 && !Array.isArray(rawPath.context)) {
2628
+ throw new Error(
2629
+ `Invalid serialized context at "${label}.context": expected an array.`
2630
+ );
2631
+ }
2632
+ const contextRaw = Array.isArray(rawPath.context) ? rawPath.context : [];
2633
+ const context = contextRaw.map((hop, hopIndex) => {
2634
+ if (!hop || typeof hop !== "object" || hop.kind !== "shadow") {
2635
+ throw new Error(
2636
+ `Invalid serialized context hop at "${label}.context[${hopIndex}]": expected a shadow hop.`
2637
+ );
2638
+ }
2639
+ return {
2640
+ kind: "shadow",
2641
+ host: decodeSerializedDomPath(
2642
+ nodeTable,
2643
+ hop.host,
2644
+ `${label}.context[${hopIndex}].host`
2645
+ )
2646
+ };
2647
+ });
2648
+ return {
2649
+ context,
2650
+ nodes: decodeSerializedDomPath(
2651
+ nodeTable,
2652
+ rawPath.nodes,
2653
+ `${label}.nodes`
2654
+ )
2655
+ };
2656
+ }
2234
2657
  async function serializePageHTML(page, _options = {}) {
2235
2658
  return serializeFrameRecursive(page.mainFrame(), [], "f0");
2236
2659
  }
@@ -2287,6 +2710,8 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2287
2710
  (Array.isArray(deferredMatchAttrKeys) ? deferredMatchAttrKeys : []).map((key) => String(key))
2288
2711
  );
2289
2712
  let counter = 1;
2713
+ const nodeTable = [];
2714
+ const nodeTableIndexByKey = /* @__PURE__ */ new Map();
2290
2715
  const entries = [];
2291
2716
  const helpers = {
2292
2717
  nextToken() {
@@ -2488,6 +2913,47 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2488
2913
  nodes: target
2489
2914
  };
2490
2915
  },
2916
+ buildPathNodeKey(node) {
2917
+ const attrs = Object.entries(node.attrs || {}).sort(
2918
+ ([a], [b]) => a.localeCompare(b)
2919
+ );
2920
+ const match = (node.match || []).map(
2921
+ (clause) => clause.kind === "attr" ? [
2922
+ "attr",
2923
+ clause.key,
2924
+ clause.op || "exact",
2925
+ clause.value ?? null
2926
+ ] : ["position", clause.axis]
2927
+ );
2928
+ return JSON.stringify([
2929
+ node.tag,
2930
+ node.position.nthChild,
2931
+ node.position.nthOfType,
2932
+ attrs,
2933
+ match
2934
+ ]);
2935
+ },
2936
+ internPathNode(node) {
2937
+ const key = helpers.buildPathNodeKey(node);
2938
+ const existing = nodeTableIndexByKey.get(key);
2939
+ if (existing != null) return existing;
2940
+ const index = nodeTable.length;
2941
+ nodeTable.push(node);
2942
+ nodeTableIndexByKey.set(key, index);
2943
+ return index;
2944
+ },
2945
+ packDomPath(path5) {
2946
+ return path5.map((node) => helpers.internPathNode(node));
2947
+ },
2948
+ packElementPath(path5) {
2949
+ return {
2950
+ context: (path5.context || []).map((hop) => ({
2951
+ kind: "shadow",
2952
+ host: helpers.packDomPath(hop.host)
2953
+ })),
2954
+ nodes: helpers.packDomPath(path5.nodes)
2955
+ };
2956
+ },
2491
2957
  ensureNodeId(el) {
2492
2958
  const next = `${frameKey2}_${counter++}`;
2493
2959
  el.setAttribute(nodeAttr, next);
@@ -2517,9 +2983,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2517
2983
  serializeElement(el) {
2518
2984
  const nodeId = helpers.ensureNodeId(el);
2519
2985
  const instanceToken = helpers.setInstanceToken(el);
2986
+ const packedPath = helpers.packElementPath(
2987
+ helpers.buildElementPath(el)
2988
+ );
2520
2989
  entries.push({
2521
2990
  nodeId,
2522
- path: helpers.buildElementPath(el),
2991
+ path: packedPath,
2523
2992
  instanceToken
2524
2993
  });
2525
2994
  const tag = el.tagName.toLowerCase();
@@ -2547,11 +3016,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2547
3016
  win[frameTokenKey] = frameToken;
2548
3017
  const root = document.documentElement;
2549
3018
  if (!root) {
2550
- return { html: "", frameToken, entries };
3019
+ return { html: "", frameToken, nodeTable, entries };
2551
3020
  }
2552
3021
  return {
2553
3022
  html: helpers.serializeElement(root),
2554
3023
  frameToken,
3024
+ nodeTable,
2555
3025
  entries
2556
3026
  };
2557
3027
  },
@@ -2569,13 +3039,18 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2569
3039
  );
2570
3040
  const nodePaths = /* @__PURE__ */ new Map();
2571
3041
  const nodeMeta = /* @__PURE__ */ new Map();
2572
- for (const entry of frameSnapshot.entries) {
3042
+ for (const [index, entry] of frameSnapshot.entries.entries()) {
3043
+ const path5 = decodeSerializedElementPath(
3044
+ frameSnapshot.nodeTable,
3045
+ entry.path,
3046
+ `entries[${index}].path`
3047
+ );
2573
3048
  nodePaths.set(entry.nodeId, {
2574
3049
  context: [
2575
3050
  ...baseContext,
2576
- ...entry.path.context || []
3051
+ ...path5.context || []
2577
3052
  ],
2578
- nodes: entry.path.nodes
3053
+ nodes: path5.nodes
2579
3054
  });
2580
3055
  nodeMeta.set(entry.nodeId, {
2581
3056
  frameToken: frameSnapshot.frameToken,
@@ -4969,7 +5444,7 @@ async function performInput(page, path5, options) {
4969
5444
  await resolved.element.type(options.text);
4970
5445
  }
4971
5446
  if (options.pressEnter) {
4972
- await resolved.element.press("Enter");
5447
+ await resolved.element.press("Enter", { noWaitAfter: true });
4973
5448
  }
4974
5449
  return {
4975
5450
  ok: true,
@@ -5645,7 +6120,26 @@ var ACTION_WAIT_PROFILES = {
5645
6120
  type: ROBUST_PROFILE
5646
6121
  };
5647
6122
  var NETWORK_POLL_MS = 50;
5648
- var IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["websocket", "eventsource"]);
6123
+ var NETWORK_RELAX_AFTER_MS = 1800;
6124
+ var RELAXED_ALLOWED_PENDING = 2;
6125
+ var HEAVY_VISUAL_REQUEST_WINDOW_MS = 5e3;
6126
+ var TRACKED_RESOURCE_TYPES = /* @__PURE__ */ new Set([
6127
+ "document",
6128
+ "fetch",
6129
+ "xhr",
6130
+ "stylesheet",
6131
+ "image",
6132
+ "font",
6133
+ "media"
6134
+ ]);
6135
+ var HEAVY_RESOURCE_TYPES = /* @__PURE__ */ new Set(["document", "fetch", "xhr"]);
6136
+ var HEAVY_VISUAL_RESOURCE_TYPES = /* @__PURE__ */ new Set([
6137
+ "stylesheet",
6138
+ "image",
6139
+ "font",
6140
+ "media"
6141
+ ]);
6142
+ var IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["websocket", "eventsource", "manifest"]);
5649
6143
  var NOOP_SESSION = {
5650
6144
  async wait() {
5651
6145
  },
@@ -5655,7 +6149,7 @@ var NOOP_SESSION = {
5655
6149
  function createPostActionWaitSession(page, action, override) {
5656
6150
  const profile = resolveActionWaitProfile(action, override);
5657
6151
  if (!profile.enabled) return NOOP_SESSION;
5658
- const tracker = profile.includeNetwork ? new ScopedNetworkTracker(page) : null;
6152
+ const tracker = profile.includeNetwork ? new AdaptiveNetworkTracker(page) : null;
5659
6153
  tracker?.start();
5660
6154
  let settled = false;
5661
6155
  return {
@@ -5663,19 +6157,32 @@ function createPostActionWaitSession(page, action, override) {
5663
6157
  if (settled) return;
5664
6158
  settled = true;
5665
6159
  const deadline = Date.now() + profile.timeout;
5666
- const networkWait = tracker ? tracker.waitForQuiet({
5667
- deadline,
5668
- quietMs: profile.networkQuietMs
5669
- }) : Promise.resolve();
6160
+ const visualTimeout = profile.includeNetwork ? Math.min(
6161
+ profile.timeout,
6162
+ resolveNetworkBackedVisualTimeout(profile.settleMs)
6163
+ ) : profile.timeout;
5670
6164
  try {
5671
- await Promise.all([
5672
- waitForVisualStabilityAcrossFrames(page, {
5673
- timeout: profile.timeout,
6165
+ try {
6166
+ await waitForVisualStabilityAcrossFrames(page, {
6167
+ timeout: visualTimeout,
5674
6168
  settleMs: profile.settleMs
5675
- }),
5676
- networkWait
5677
- ]);
5678
- } catch {
6169
+ });
6170
+ } catch (error) {
6171
+ if (isStealthWaitUnavailableError(error)) {
6172
+ throw error;
6173
+ }
6174
+ } finally {
6175
+ tracker?.freezeCollection();
6176
+ }
6177
+ if (tracker) {
6178
+ try {
6179
+ await tracker.waitForQuiet({
6180
+ deadline,
6181
+ quietMs: profile.networkQuietMs
6182
+ });
6183
+ } catch {
6184
+ }
6185
+ }
5679
6186
  } finally {
5680
6187
  tracker?.stop();
5681
6188
  }
@@ -5715,53 +6222,70 @@ function normalizeMs(value, fallback) {
5715
6222
  }
5716
6223
  return Math.max(0, Math.floor(value));
5717
6224
  }
5718
- var ScopedNetworkTracker = class {
6225
+ function resolveNetworkBackedVisualTimeout(settleMs) {
6226
+ const derived = settleMs * 3 + 300;
6227
+ return Math.max(1200, Math.min(2500, derived));
6228
+ }
6229
+ var AdaptiveNetworkTracker = class {
5719
6230
  constructor(page) {
5720
6231
  this.page = page;
5721
6232
  }
5722
- pending = /* @__PURE__ */ new Set();
6233
+ pending = /* @__PURE__ */ new Map();
5723
6234
  started = false;
6235
+ collecting = false;
6236
+ startedAt = 0;
5724
6237
  idleSince = Date.now();
5725
6238
  start() {
5726
6239
  if (this.started) return;
5727
6240
  this.started = true;
6241
+ this.collecting = true;
6242
+ this.startedAt = Date.now();
6243
+ this.idleSince = this.startedAt;
5728
6244
  this.page.on("request", this.handleRequestStarted);
5729
6245
  this.page.on("requestfinished", this.handleRequestFinished);
5730
6246
  this.page.on("requestfailed", this.handleRequestFinished);
5731
6247
  }
6248
+ freezeCollection() {
6249
+ if (!this.started) return;
6250
+ this.collecting = false;
6251
+ }
5732
6252
  stop() {
5733
6253
  if (!this.started) return;
5734
6254
  this.started = false;
6255
+ this.collecting = false;
5735
6256
  this.page.off("request", this.handleRequestStarted);
5736
6257
  this.page.off("requestfinished", this.handleRequestFinished);
5737
6258
  this.page.off("requestfailed", this.handleRequestFinished);
5738
6259
  this.pending.clear();
6260
+ this.startedAt = 0;
5739
6261
  this.idleSince = Date.now();
5740
6262
  }
5741
6263
  async waitForQuiet(options) {
5742
6264
  const quietMs = Math.max(0, options.quietMs);
5743
6265
  if (quietMs === 0) return;
5744
6266
  while (Date.now() < options.deadline) {
5745
- if (this.pending.size === 0) {
6267
+ const now = Date.now();
6268
+ const allowedPending = this.resolveAllowedPending(now);
6269
+ if (this.pending.size <= allowedPending) {
5746
6270
  if (this.idleSince === 0) {
5747
- this.idleSince = Date.now();
6271
+ this.idleSince = now;
5748
6272
  }
5749
- const idleFor = Date.now() - this.idleSince;
6273
+ const idleFor = now - this.idleSince;
5750
6274
  if (idleFor >= quietMs) {
5751
6275
  return;
5752
6276
  }
5753
6277
  } else {
5754
6278
  this.idleSince = 0;
5755
6279
  }
5756
- const remaining = Math.max(1, options.deadline - Date.now());
5757
- await sleep(Math.min(NETWORK_POLL_MS, remaining));
6280
+ const remaining = Math.max(1, options.deadline - now);
6281
+ await sleep2(Math.min(NETWORK_POLL_MS, remaining));
5758
6282
  }
5759
6283
  }
5760
6284
  handleRequestStarted = (request) => {
5761
- if (!this.started) return;
5762
- const resourceType = request.resourceType();
5763
- if (IGNORED_RESOURCE_TYPES.has(resourceType)) return;
5764
- this.pending.add(request);
6285
+ if (!this.started || !this.collecting) return;
6286
+ const trackedRequest = this.classifyRequest(request);
6287
+ if (!trackedRequest) return;
6288
+ this.pending.set(request, trackedRequest);
5765
6289
  this.idleSince = 0;
5766
6290
  };
5767
6291
  handleRequestFinished = (request) => {
@@ -5771,8 +6295,35 @@ var ScopedNetworkTracker = class {
5771
6295
  this.idleSince = Date.now();
5772
6296
  }
5773
6297
  };
6298
+ classifyRequest(request) {
6299
+ const resourceType = request.resourceType().toLowerCase();
6300
+ if (IGNORED_RESOURCE_TYPES.has(resourceType)) return null;
6301
+ if (!TRACKED_RESOURCE_TYPES.has(resourceType)) return null;
6302
+ const frame = request.frame();
6303
+ if (!frame || frame !== this.page.mainFrame()) return null;
6304
+ return {
6305
+ resourceType,
6306
+ startedAt: Date.now()
6307
+ };
6308
+ }
6309
+ resolveAllowedPending(now) {
6310
+ const relaxed = now - this.startedAt >= NETWORK_RELAX_AFTER_MS ? RELAXED_ALLOWED_PENDING : 0;
6311
+ if (this.hasHeavyPending(now)) return 0;
6312
+ return relaxed;
6313
+ }
6314
+ hasHeavyPending(now) {
6315
+ for (const trackedRequest of this.pending.values()) {
6316
+ if (HEAVY_RESOURCE_TYPES.has(trackedRequest.resourceType)) {
6317
+ return true;
6318
+ }
6319
+ if (HEAVY_VISUAL_RESOURCE_TYPES.has(trackedRequest.resourceType) && now - trackedRequest.startedAt < HEAVY_VISUAL_REQUEST_WINDOW_MS) {
6320
+ return true;
6321
+ }
6322
+ }
6323
+ return false;
6324
+ }
5774
6325
  };
5775
- async function sleep(ms) {
6326
+ async function sleep2(ms) {
5776
6327
  await new Promise((resolve) => {
5777
6328
  setTimeout(resolve, ms);
5778
6329
  });
@@ -7212,16 +7763,18 @@ var CACHE_IMPORT_BATCH_SIZE = 200;
7212
7763
  var RemoteSessionClient = class {
7213
7764
  baseUrl;
7214
7765
  key;
7215
- constructor(baseUrl, key) {
7766
+ authScheme;
7767
+ constructor(baseUrl, key, authScheme = "api-key") {
7216
7768
  this.baseUrl = normalizeBaseUrl(baseUrl);
7217
7769
  this.key = key;
7770
+ this.authScheme = authScheme;
7218
7771
  }
7219
7772
  async create(request) {
7220
7773
  const response = await fetch(`${this.baseUrl}/sessions`, {
7221
7774
  method: "POST",
7222
7775
  headers: {
7223
7776
  "content-type": "application/json",
7224
- "x-api-key": this.key
7777
+ ...this.authHeaders()
7225
7778
  },
7226
7779
  body: JSON.stringify(request)
7227
7780
  });
@@ -7234,7 +7787,7 @@ var RemoteSessionClient = class {
7234
7787
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
7235
7788
  method: "DELETE",
7236
7789
  headers: {
7237
- "x-api-key": this.key
7790
+ ...this.authHeaders()
7238
7791
  }
7239
7792
  });
7240
7793
  if (response.status === 204) {
@@ -7264,7 +7817,7 @@ var RemoteSessionClient = class {
7264
7817
  method: "POST",
7265
7818
  headers: {
7266
7819
  "content-type": "application/json",
7267
- "x-api-key": this.key
7820
+ ...this.authHeaders()
7268
7821
  },
7269
7822
  body: JSON.stringify({ entries })
7270
7823
  });
@@ -7273,6 +7826,16 @@ var RemoteSessionClient = class {
7273
7826
  }
7274
7827
  return await response.json();
7275
7828
  }
7829
+ authHeaders() {
7830
+ if (this.authScheme === "bearer") {
7831
+ return {
7832
+ authorization: `Bearer ${this.key}`
7833
+ };
7834
+ }
7835
+ return {
7836
+ "x-api-key": this.key
7837
+ };
7838
+ }
7276
7839
  };
7277
7840
  function normalizeBaseUrl(baseUrl) {
7278
7841
  return baseUrl.replace(/\/+$/, "");
@@ -7313,9 +7876,9 @@ function toRemoteErrorCode(code) {
7313
7876
 
7314
7877
  // src/remote/runtime.ts
7315
7878
  var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7316
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl()) {
7879
+ function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
7317
7880
  return {
7318
- sessionClient: new RemoteSessionClient(baseUrl, key),
7881
+ sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7319
7882
  cdpClient: new RemoteCdpClient(),
7320
7883
  actionClient: null,
7321
7884
  sessionId: null
@@ -7380,7 +7943,8 @@ var Opensteer = class _Opensteer {
7380
7943
  }
7381
7944
  this.remote = createRemoteRuntimeState(
7382
7945
  apiKey,
7383
- remoteConfig?.baseUrl
7946
+ remoteConfig?.baseUrl,
7947
+ remoteConfig?.authScheme
7384
7948
  );
7385
7949
  } else {
7386
7950
  this.remote = null;
@@ -7836,7 +8400,7 @@ var Opensteer = class _Opensteer {
7836
8400
  await handle.type(options.text);
7837
8401
  }
7838
8402
  if (options.pressEnter) {
7839
- await handle.press("Enter");
8403
+ await handle.press("Enter", { noWaitAfter: true });
7840
8404
  }
7841
8405
  });
7842
8406
  } catch (err) {