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.
@@ -952,11 +952,33 @@ function parseMode(value, source) {
952
952
  `Invalid ${source} value "${value}". Use "local" or "remote".`
953
953
  );
954
954
  }
955
+ function parseAuthScheme(value, source) {
956
+ if (value == null) return void 0;
957
+ if (typeof value !== "string") {
958
+ throw new Error(
959
+ `Invalid ${source} value "${String(value)}". Use "api-key" or "bearer".`
960
+ );
961
+ }
962
+ const normalized = value.trim().toLowerCase();
963
+ if (!normalized) return void 0;
964
+ if (normalized === "api-key" || normalized === "bearer") {
965
+ return normalized;
966
+ }
967
+ throw new Error(
968
+ `Invalid ${source} value "${value}". Use "api-key" or "bearer".`
969
+ );
970
+ }
955
971
  function resolveOpensteerApiKey() {
956
972
  const value = process.env.OPENSTEER_API_KEY?.trim();
957
973
  if (!value) return void 0;
958
974
  return value;
959
975
  }
976
+ function resolveOpensteerAuthScheme() {
977
+ return parseAuthScheme(
978
+ process.env.OPENSTEER_AUTH_SCHEME,
979
+ "OPENSTEER_AUTH_SCHEME"
980
+ );
981
+ }
960
982
  function normalizeRemoteOptions(value) {
961
983
  if (!value || typeof value !== "object" || Array.isArray(value)) {
962
984
  return void 0;
@@ -1016,7 +1038,12 @@ function resolveConfig(input = {}) {
1016
1038
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1017
1039
  const resolved = mergeDeep(mergedWithEnv, input);
1018
1040
  const envApiKey = resolveOpensteerApiKey();
1041
+ const envAuthScheme = resolveOpensteerAuthScheme();
1019
1042
  const inputRemoteOptions = normalizeRemoteOptions(input.remote);
1043
+ const inputAuthScheme = parseAuthScheme(
1044
+ inputRemoteOptions?.authScheme,
1045
+ "remote.authScheme"
1046
+ );
1020
1047
  const inputHasRemoteApiKey = Boolean(
1021
1048
  inputRemoteOptions && Object.prototype.hasOwnProperty.call(inputRemoteOptions, "apiKey")
1022
1049
  );
@@ -1024,8 +1051,12 @@ function resolveConfig(input = {}) {
1024
1051
  mode: resolved.mode
1025
1052
  });
1026
1053
  if (modeSelection.mode === "remote") {
1027
- const resolvedRemote = normalizeRemoteOptions(resolved.remote);
1028
- resolved.remote = resolvedRemote ?? {};
1054
+ const resolvedRemote = normalizeRemoteOptions(resolved.remote) ?? {};
1055
+ const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedRemote.authScheme, "remote.authScheme") ?? "api-key";
1056
+ resolved.remote = {
1057
+ ...resolvedRemote,
1058
+ authScheme
1059
+ };
1029
1060
  }
1030
1061
  if (envApiKey && modeSelection.mode === "remote" && !inputHasRemoteApiKey) {
1031
1062
  resolved.remote = {
@@ -1072,15 +1103,52 @@ function getCallerFilePath() {
1072
1103
  // src/navigation.ts
1073
1104
  var DEFAULT_TIMEOUT = 3e4;
1074
1105
  var DEFAULT_SETTLE_MS = 750;
1106
+ var FRAME_EVALUATE_GRACE_MS = 200;
1107
+ var STEALTH_WORLD_NAME = "__opensteer_wait__";
1108
+ var StealthWaitUnavailableError = class extends Error {
1109
+ constructor(cause) {
1110
+ super("Stealth visual wait requires Chromium CDP support.", { cause });
1111
+ this.name = "StealthWaitUnavailableError";
1112
+ }
1113
+ };
1114
+ function isStealthWaitUnavailableError(error) {
1115
+ return error instanceof StealthWaitUnavailableError;
1116
+ }
1117
+ var FRAME_OWNER_VISIBILITY_FUNCTION = `function() {
1118
+ if (!(this instanceof HTMLElement)) return false;
1119
+
1120
+ var rect = this.getBoundingClientRect();
1121
+ if (rect.width <= 0 || rect.height <= 0) return false;
1122
+ if (
1123
+ rect.bottom <= 0 ||
1124
+ rect.right <= 0 ||
1125
+ rect.top >= window.innerHeight ||
1126
+ rect.left >= window.innerWidth
1127
+ ) {
1128
+ return false;
1129
+ }
1130
+
1131
+ var style = window.getComputedStyle(this);
1132
+ if (
1133
+ style.display === 'none' ||
1134
+ style.visibility === 'hidden' ||
1135
+ Number(style.opacity) === 0
1136
+ ) {
1137
+ return false;
1138
+ }
1139
+
1140
+ return true;
1141
+ }`;
1075
1142
  function buildStabilityScript(timeout, settleMs) {
1076
1143
  return `new Promise(function(resolve) {
1077
1144
  var deadline = Date.now() + ${timeout};
1078
- var timer = null;
1079
1145
  var resolved = false;
1146
+ var timer = null;
1080
1147
  var observers = [];
1081
1148
  var observedShadowRoots = [];
1082
1149
  var fonts = document.fonts;
1083
1150
  var fontsReady = !fonts || fonts.status === 'loaded';
1151
+ var lastRelevantMutationAt = Date.now();
1084
1152
 
1085
1153
  function clearObservers() {
1086
1154
  for (var i = 0; i < observers.length; i++) {
@@ -1098,9 +1166,87 @@ function buildStabilityScript(timeout, settleMs) {
1098
1166
  resolve();
1099
1167
  }
1100
1168
 
1169
+ function isElementVisiblyIntersectingViewport(element) {
1170
+ if (!(element instanceof Element)) return false;
1171
+
1172
+ var rect = element.getBoundingClientRect();
1173
+ var inViewport =
1174
+ rect.width > 0 &&
1175
+ rect.height > 0 &&
1176
+ rect.bottom > 0 &&
1177
+ rect.right > 0 &&
1178
+ rect.top < window.innerHeight &&
1179
+ rect.left < window.innerWidth;
1180
+
1181
+ if (!inViewport) return false;
1182
+
1183
+ var style = window.getComputedStyle(element);
1184
+ if (style.visibility === 'hidden' || style.display === 'none') {
1185
+ return false;
1186
+ }
1187
+ if (Number(style.opacity) === 0) {
1188
+ return false;
1189
+ }
1190
+
1191
+ return true;
1192
+ }
1193
+
1194
+ function resolveRelevantElement(node) {
1195
+ if (!node) return null;
1196
+ if (node instanceof Element) return node;
1197
+ if (typeof ShadowRoot !== 'undefined' && node instanceof ShadowRoot) {
1198
+ return node.host instanceof Element ? node.host : null;
1199
+ }
1200
+ var parentElement = node.parentElement;
1201
+ return parentElement instanceof Element ? parentElement : null;
1202
+ }
1203
+
1204
+ function isNodeVisiblyRelevant(node) {
1205
+ var element = resolveRelevantElement(node);
1206
+ if (!element) return false;
1207
+ return isElementVisiblyIntersectingViewport(element);
1208
+ }
1209
+
1210
+ function hasRelevantMutation(records) {
1211
+ for (var i = 0; i < records.length; i++) {
1212
+ var record = records[i];
1213
+ if (isNodeVisiblyRelevant(record.target)) return true;
1214
+
1215
+ var addedNodes = record.addedNodes;
1216
+ for (var j = 0; j < addedNodes.length; j++) {
1217
+ if (isNodeVisiblyRelevant(addedNodes[j])) return true;
1218
+ }
1219
+
1220
+ var removedNodes = record.removedNodes;
1221
+ for (var k = 0; k < removedNodes.length; k++) {
1222
+ if (isNodeVisiblyRelevant(removedNodes[k])) return true;
1223
+ }
1224
+ }
1225
+
1226
+ return false;
1227
+ }
1228
+
1229
+ function scheduleCheck() {
1230
+ if (resolved) return;
1231
+ if (timer) clearTimeout(timer);
1232
+
1233
+ var remaining = deadline - Date.now();
1234
+ if (remaining <= 0) {
1235
+ done();
1236
+ return;
1237
+ }
1238
+
1239
+ var checkDelay = Math.min(120, Math.max(16, ${settleMs}));
1240
+ timer = setTimeout(checkNow, checkDelay);
1241
+ }
1242
+
1101
1243
  function observeMutations(target) {
1102
1244
  if (!target) return;
1103
- var observer = new MutationObserver(function() { settle(); });
1245
+ var observer = new MutationObserver(function(records) {
1246
+ if (!hasRelevantMutation(records)) return;
1247
+ lastRelevantMutationAt = Date.now();
1248
+ scheduleCheck();
1249
+ });
1104
1250
  observer.observe(target, {
1105
1251
  childList: true,
1106
1252
  subtree: true,
@@ -1138,18 +1284,25 @@ function buildStabilityScript(timeout, settleMs) {
1138
1284
  var images = root.querySelectorAll('img');
1139
1285
  for (var i = 0; i < images.length; i++) {
1140
1286
  var img = images[i];
1141
- var rect = img.getBoundingClientRect();
1142
- var inViewport =
1143
- rect.bottom > 0 &&
1144
- rect.right > 0 &&
1145
- rect.top < window.innerHeight &&
1146
- rect.left < window.innerWidth;
1147
- if (inViewport && !img.complete) return false;
1287
+ if (!isElementVisiblyIntersectingViewport(img)) continue;
1288
+ if (!img.complete) return false;
1148
1289
  }
1149
1290
  return true;
1150
1291
  }
1151
1292
 
1152
- function hasRunningFiniteAnimations() {
1293
+ function getAnimationTarget(effect) {
1294
+ if (!effect) return null;
1295
+ var target = effect.target;
1296
+ if (target instanceof Element) return target;
1297
+
1298
+ if (target && target.element instanceof Element) {
1299
+ return target.element;
1300
+ }
1301
+
1302
+ return null;
1303
+ }
1304
+
1305
+ function hasRunningVisibleFiniteAnimations() {
1153
1306
  if (typeof document.getAnimations !== 'function') return false;
1154
1307
  var animations = document.getAnimations();
1155
1308
 
@@ -1163,6 +1316,9 @@ function buildStabilityScript(timeout, settleMs) {
1163
1316
  ? timing.endTime
1164
1317
  : Number.POSITIVE_INFINITY;
1165
1318
  if (Number.isFinite(endTime) && endTime > 0) {
1319
+ var target = getAnimationTarget(effect);
1320
+ if (!target) continue;
1321
+ if (!isElementVisiblyIntersectingViewport(target)) continue;
1166
1322
  return true;
1167
1323
  }
1168
1324
  }
@@ -1173,21 +1329,29 @@ function buildStabilityScript(timeout, settleMs) {
1173
1329
  function isVisuallyReady() {
1174
1330
  if (!fontsReady) return false;
1175
1331
  if (!checkViewportImages(document)) return false;
1176
- if (hasRunningFiniteAnimations()) return false;
1332
+ if (hasRunningVisibleFiniteAnimations()) return false;
1177
1333
  return true;
1178
1334
  }
1179
1335
 
1180
- function settle() {
1181
- if (Date.now() > deadline) { done(); return; }
1182
- if (timer) clearTimeout(timer);
1336
+ function checkNow() {
1337
+ if (Date.now() >= deadline) {
1338
+ done();
1339
+ return;
1340
+ }
1341
+
1183
1342
  observeOpenShadowRoots();
1184
- timer = setTimeout(function() {
1185
- if (isVisuallyReady()) {
1186
- done();
1187
- } else {
1188
- settle();
1189
- }
1190
- }, ${settleMs});
1343
+
1344
+ if (!isVisuallyReady()) {
1345
+ scheduleCheck();
1346
+ return;
1347
+ }
1348
+
1349
+ if (Date.now() - lastRelevantMutationAt >= ${settleMs}) {
1350
+ done();
1351
+ return;
1352
+ }
1353
+
1354
+ scheduleCheck();
1191
1355
  }
1192
1356
 
1193
1357
  observeMutations(document.documentElement);
@@ -1196,67 +1360,266 @@ function buildStabilityScript(timeout, settleMs) {
1196
1360
  if (fonts && fonts.ready && typeof fonts.ready.then === 'function') {
1197
1361
  fonts.ready.then(function() {
1198
1362
  fontsReady = true;
1199
- settle();
1363
+ scheduleCheck();
1364
+ }, function() {
1365
+ fontsReady = true;
1366
+ scheduleCheck();
1200
1367
  });
1201
1368
  }
1202
1369
 
1203
1370
  var safetyTimer = setTimeout(done, ${timeout});
1204
1371
 
1205
- settle();
1372
+ scheduleCheck();
1206
1373
  })`;
1207
1374
  }
1375
+ var StealthCdpRuntime = class _StealthCdpRuntime {
1376
+ constructor(session2) {
1377
+ this.session = session2;
1378
+ }
1379
+ contextsByFrame = /* @__PURE__ */ new Map();
1380
+ disposed = false;
1381
+ static async create(page) {
1382
+ let session2;
1383
+ try {
1384
+ session2 = await page.context().newCDPSession(page);
1385
+ } catch (error) {
1386
+ throw new StealthWaitUnavailableError(error);
1387
+ }
1388
+ const runtime = new _StealthCdpRuntime(session2);
1389
+ try {
1390
+ await runtime.initialize();
1391
+ return runtime;
1392
+ } catch (error) {
1393
+ await runtime.dispose();
1394
+ throw new StealthWaitUnavailableError(error);
1395
+ }
1396
+ }
1397
+ async dispose() {
1398
+ if (this.disposed) return;
1399
+ this.disposed = true;
1400
+ this.contextsByFrame.clear();
1401
+ await this.session.detach().catch(() => void 0);
1402
+ }
1403
+ async waitForMainFrameVisualStability(options) {
1404
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1405
+ const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1406
+ if (timeout <= 0) return;
1407
+ const frameRecords = await this.getFrameRecords();
1408
+ const mainFrame = frameRecords[0];
1409
+ if (!mainFrame) return;
1410
+ await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
1411
+ }
1412
+ async collectVisibleFrameIds() {
1413
+ const frameRecords = await this.getFrameRecords();
1414
+ if (frameRecords.length === 0) return [];
1415
+ const visibleFrameIds = [];
1416
+ for (const frameRecord of frameRecords) {
1417
+ if (!frameRecord.parentFrameId) {
1418
+ visibleFrameIds.push(frameRecord.frameId);
1419
+ continue;
1420
+ }
1421
+ try {
1422
+ const parentContextId = await this.ensureFrameContextId(
1423
+ frameRecord.parentFrameId
1424
+ );
1425
+ const visible = await this.isFrameOwnerVisible(
1426
+ frameRecord.frameId,
1427
+ parentContextId
1428
+ );
1429
+ if (visible) {
1430
+ visibleFrameIds.push(frameRecord.frameId);
1431
+ }
1432
+ } catch (error) {
1433
+ if (isIgnorableFrameError(error)) continue;
1434
+ throw error;
1435
+ }
1436
+ }
1437
+ return visibleFrameIds;
1438
+ }
1439
+ async waitForFrameVisualStability(frameId, timeout, settleMs) {
1440
+ if (timeout <= 0) return;
1441
+ const script = buildStabilityScript(timeout, settleMs);
1442
+ let contextId = await this.ensureFrameContextId(frameId);
1443
+ try {
1444
+ await this.evaluateWithGuard(contextId, script, timeout);
1445
+ } catch (error) {
1446
+ if (!isMissingExecutionContextError(error)) {
1447
+ throw error;
1448
+ }
1449
+ this.contextsByFrame.delete(frameId);
1450
+ contextId = await this.ensureFrameContextId(frameId);
1451
+ await this.evaluateWithGuard(contextId, script, timeout);
1452
+ }
1453
+ }
1454
+ async initialize() {
1455
+ await this.session.send("Page.enable");
1456
+ await this.session.send("Runtime.enable");
1457
+ await this.session.send("DOM.enable");
1458
+ }
1459
+ async getFrameRecords() {
1460
+ const treeResult = await this.session.send("Page.getFrameTree");
1461
+ const records = [];
1462
+ walkFrameTree(treeResult.frameTree, null, records);
1463
+ return records;
1464
+ }
1465
+ async ensureFrameContextId(frameId) {
1466
+ const cached = this.contextsByFrame.get(frameId);
1467
+ if (cached != null) {
1468
+ return cached;
1469
+ }
1470
+ const world = await this.session.send("Page.createIsolatedWorld", {
1471
+ frameId,
1472
+ worldName: STEALTH_WORLD_NAME
1473
+ });
1474
+ this.contextsByFrame.set(frameId, world.executionContextId);
1475
+ return world.executionContextId;
1476
+ }
1477
+ async evaluateWithGuard(contextId, script, timeout) {
1478
+ const evaluationPromise = this.evaluateScript(contextId, script);
1479
+ const settledPromise = evaluationPromise.then(
1480
+ () => ({ kind: "resolved" }),
1481
+ (error) => ({ kind: "rejected", error })
1482
+ );
1483
+ const timeoutPromise = sleep(
1484
+ timeout + FRAME_EVALUATE_GRACE_MS
1485
+ ).then(() => ({ kind: "timeout" }));
1486
+ const result = await Promise.race([
1487
+ settledPromise,
1488
+ timeoutPromise
1489
+ ]);
1490
+ if (result.kind === "rejected") {
1491
+ throw result.error;
1492
+ }
1493
+ }
1494
+ async evaluateScript(contextId, expression) {
1495
+ const result = await this.session.send("Runtime.evaluate", {
1496
+ contextId,
1497
+ expression,
1498
+ awaitPromise: true,
1499
+ returnByValue: true
1500
+ });
1501
+ if (result.exceptionDetails) {
1502
+ throw new Error(formatCdpException(result.exceptionDetails));
1503
+ }
1504
+ }
1505
+ async isFrameOwnerVisible(frameId, parentContextId) {
1506
+ const owner = await this.session.send("DOM.getFrameOwner", {
1507
+ frameId
1508
+ });
1509
+ const resolveParams = {
1510
+ executionContextId: parentContextId
1511
+ };
1512
+ if (typeof owner.backendNodeId === "number") {
1513
+ resolveParams.backendNodeId = owner.backendNodeId;
1514
+ } else if (typeof owner.nodeId === "number") {
1515
+ resolveParams.nodeId = owner.nodeId;
1516
+ } else {
1517
+ return false;
1518
+ }
1519
+ const resolved = await this.session.send(
1520
+ "DOM.resolveNode",
1521
+ resolveParams
1522
+ );
1523
+ const objectId = resolved.object?.objectId;
1524
+ if (!objectId) return false;
1525
+ try {
1526
+ const callResult = await this.session.send("Runtime.callFunctionOn", {
1527
+ objectId,
1528
+ functionDeclaration: FRAME_OWNER_VISIBILITY_FUNCTION,
1529
+ returnByValue: true
1530
+ });
1531
+ if (callResult.exceptionDetails) {
1532
+ throw new Error(formatCdpException(callResult.exceptionDetails));
1533
+ }
1534
+ return callResult.result.value === true;
1535
+ } finally {
1536
+ await this.releaseObject(objectId);
1537
+ }
1538
+ }
1539
+ async releaseObject(objectId) {
1540
+ await this.session.send("Runtime.releaseObject", {
1541
+ objectId
1542
+ }).catch(() => void 0);
1543
+ }
1544
+ };
1208
1545
  async function waitForVisualStability(page, options = {}) {
1209
- const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1210
- const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1211
- await waitForFrameVisualStability(page.mainFrame(), {
1212
- timeout,
1213
- settleMs
1214
- });
1546
+ const runtime = await StealthCdpRuntime.create(page);
1547
+ try {
1548
+ await runtime.waitForMainFrameVisualStability(options);
1549
+ } finally {
1550
+ await runtime.dispose();
1551
+ }
1215
1552
  }
1216
1553
  async function waitForVisualStabilityAcrossFrames(page, options = {}) {
1217
1554
  const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1218
1555
  const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1556
+ if (timeout <= 0) return;
1219
1557
  const deadline = Date.now() + timeout;
1220
- while (true) {
1221
- const remaining = Math.max(0, deadline - Date.now());
1222
- if (remaining === 0) return;
1223
- const frames = page.frames();
1224
- await Promise.all(
1225
- frames.map(async (frame) => {
1226
- try {
1227
- await waitForFrameVisualStability(frame, {
1228
- timeout: remaining,
1229
- settleMs
1230
- });
1231
- } catch (error) {
1232
- if (isIgnorableFrameError(error)) return;
1233
- throw error;
1234
- }
1235
- })
1236
- );
1237
- const currentFrames = page.frames();
1238
- if (sameFrames(frames, currentFrames)) {
1239
- return;
1558
+ const runtime = await StealthCdpRuntime.create(page);
1559
+ try {
1560
+ while (true) {
1561
+ const remaining = Math.max(0, deadline - Date.now());
1562
+ if (remaining === 0) return;
1563
+ const frameIds = await runtime.collectVisibleFrameIds();
1564
+ if (frameIds.length === 0) return;
1565
+ await Promise.all(
1566
+ frameIds.map(async (frameId) => {
1567
+ try {
1568
+ await runtime.waitForFrameVisualStability(
1569
+ frameId,
1570
+ remaining,
1571
+ settleMs
1572
+ );
1573
+ } catch (error) {
1574
+ if (isIgnorableFrameError(error)) return;
1575
+ throw error;
1576
+ }
1577
+ })
1578
+ );
1579
+ const currentFrameIds = await runtime.collectVisibleFrameIds();
1580
+ if (sameFrameIds(frameIds, currentFrameIds)) {
1581
+ return;
1582
+ }
1240
1583
  }
1584
+ } finally {
1585
+ await runtime.dispose();
1241
1586
  }
1242
1587
  }
1243
- async function waitForFrameVisualStability(frame, options) {
1244
- const timeout = options.timeout ?? DEFAULT_TIMEOUT;
1245
- const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
1246
- if (timeout <= 0) return;
1247
- await frame.evaluate(buildStabilityScript(timeout, settleMs));
1588
+ function walkFrameTree(node, parentFrameId, records) {
1589
+ const frameId = node.frame?.id;
1590
+ if (!frameId) return;
1591
+ records.push({
1592
+ frameId,
1593
+ parentFrameId
1594
+ });
1595
+ for (const child of node.childFrames ?? []) {
1596
+ walkFrameTree(child, frameId, records);
1597
+ }
1248
1598
  }
1249
- function sameFrames(before, after) {
1599
+ function sameFrameIds(before, after) {
1250
1600
  if (before.length !== after.length) return false;
1251
- for (const frame of before) {
1252
- if (!after.includes(frame)) return false;
1601
+ for (const frameId of before) {
1602
+ if (!after.includes(frameId)) return false;
1253
1603
  }
1254
1604
  return true;
1255
1605
  }
1606
+ function formatCdpException(details) {
1607
+ return details.exception?.description || details.text || "CDP runtime evaluation failed.";
1608
+ }
1609
+ function isMissingExecutionContextError(error) {
1610
+ if (!(error instanceof Error)) return false;
1611
+ const message = error.message;
1612
+ return message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
1613
+ }
1256
1614
  function isIgnorableFrameError(error) {
1257
1615
  if (!(error instanceof Error)) return false;
1258
1616
  const message = error.message;
1259
- return message.includes("Frame was detached") || message.includes("Execution context was destroyed") || message.includes("Target page, context or browser has been closed");
1617
+ 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");
1618
+ }
1619
+ function sleep(ms) {
1620
+ return new Promise((resolve) => {
1621
+ setTimeout(resolve, ms);
1622
+ });
1260
1623
  }
1261
1624
 
1262
1625
  // src/storage/local.ts
@@ -2157,6 +2520,66 @@ var OS_BOUNDARY_ATTR = "data-os-boundary";
2157
2520
  var OS_UNAVAILABLE_ATTR = "data-os-unavailable";
2158
2521
  var OS_IFRAME_BOUNDARY_TAG = "os-iframe-root";
2159
2522
  var OS_SHADOW_BOUNDARY_TAG = "os-shadow-root";
2523
+ function decodeSerializedNodeTableEntry(nodeTable, rawIndex, label) {
2524
+ if (typeof rawIndex !== "number" || !Number.isInteger(rawIndex) || rawIndex < 0 || rawIndex >= nodeTable.length) {
2525
+ throw new Error(
2526
+ `Invalid serialized path node index at "${label}": expected a valid table index.`
2527
+ );
2528
+ }
2529
+ const node = nodeTable[rawIndex];
2530
+ if (!node || typeof node !== "object") {
2531
+ throw new Error(
2532
+ `Invalid serialized path node at "${label}": table entry is missing.`
2533
+ );
2534
+ }
2535
+ return node;
2536
+ }
2537
+ function decodeSerializedDomPath(nodeTable, rawPath, label) {
2538
+ if (!Array.isArray(rawPath)) {
2539
+ throw new Error(
2540
+ `Invalid serialized path at "${label}": expected an array of node indexes.`
2541
+ );
2542
+ }
2543
+ return rawPath.map(
2544
+ (value, index) => decodeSerializedNodeTableEntry(nodeTable, value, `${label}[${index}]`)
2545
+ );
2546
+ }
2547
+ function decodeSerializedElementPath(nodeTable, rawPath, label) {
2548
+ if (!rawPath || typeof rawPath !== "object") {
2549
+ throw new Error(
2550
+ `Invalid serialized element path at "${label}": expected an object.`
2551
+ );
2552
+ }
2553
+ if (rawPath.context !== void 0 && !Array.isArray(rawPath.context)) {
2554
+ throw new Error(
2555
+ `Invalid serialized context at "${label}.context": expected an array.`
2556
+ );
2557
+ }
2558
+ const contextRaw = Array.isArray(rawPath.context) ? rawPath.context : [];
2559
+ const context = contextRaw.map((hop, hopIndex) => {
2560
+ if (!hop || typeof hop !== "object" || hop.kind !== "shadow") {
2561
+ throw new Error(
2562
+ `Invalid serialized context hop at "${label}.context[${hopIndex}]": expected a shadow hop.`
2563
+ );
2564
+ }
2565
+ return {
2566
+ kind: "shadow",
2567
+ host: decodeSerializedDomPath(
2568
+ nodeTable,
2569
+ hop.host,
2570
+ `${label}.context[${hopIndex}].host`
2571
+ )
2572
+ };
2573
+ });
2574
+ return {
2575
+ context,
2576
+ nodes: decodeSerializedDomPath(
2577
+ nodeTable,
2578
+ rawPath.nodes,
2579
+ `${label}.nodes`
2580
+ )
2581
+ };
2582
+ }
2160
2583
  async function serializePageHTML(page, _options = {}) {
2161
2584
  return serializeFrameRecursive(page.mainFrame(), [], "f0");
2162
2585
  }
@@ -2213,6 +2636,8 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2213
2636
  (Array.isArray(deferredMatchAttrKeys) ? deferredMatchAttrKeys : []).map((key) => String(key))
2214
2637
  );
2215
2638
  let counter = 1;
2639
+ const nodeTable = [];
2640
+ const nodeTableIndexByKey = /* @__PURE__ */ new Map();
2216
2641
  const entries = [];
2217
2642
  const helpers = {
2218
2643
  nextToken() {
@@ -2414,6 +2839,47 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2414
2839
  nodes: target
2415
2840
  };
2416
2841
  },
2842
+ buildPathNodeKey(node) {
2843
+ const attrs = Object.entries(node.attrs || {}).sort(
2844
+ ([a], [b]) => a.localeCompare(b)
2845
+ );
2846
+ const match = (node.match || []).map(
2847
+ (clause) => clause.kind === "attr" ? [
2848
+ "attr",
2849
+ clause.key,
2850
+ clause.op || "exact",
2851
+ clause.value ?? null
2852
+ ] : ["position", clause.axis]
2853
+ );
2854
+ return JSON.stringify([
2855
+ node.tag,
2856
+ node.position.nthChild,
2857
+ node.position.nthOfType,
2858
+ attrs,
2859
+ match
2860
+ ]);
2861
+ },
2862
+ internPathNode(node) {
2863
+ const key = helpers.buildPathNodeKey(node);
2864
+ const existing = nodeTableIndexByKey.get(key);
2865
+ if (existing != null) return existing;
2866
+ const index = nodeTable.length;
2867
+ nodeTable.push(node);
2868
+ nodeTableIndexByKey.set(key, index);
2869
+ return index;
2870
+ },
2871
+ packDomPath(path5) {
2872
+ return path5.map((node) => helpers.internPathNode(node));
2873
+ },
2874
+ packElementPath(path5) {
2875
+ return {
2876
+ context: (path5.context || []).map((hop) => ({
2877
+ kind: "shadow",
2878
+ host: helpers.packDomPath(hop.host)
2879
+ })),
2880
+ nodes: helpers.packDomPath(path5.nodes)
2881
+ };
2882
+ },
2417
2883
  ensureNodeId(el) {
2418
2884
  const next = `${frameKey2}_${counter++}`;
2419
2885
  el.setAttribute(nodeAttr, next);
@@ -2443,9 +2909,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2443
2909
  serializeElement(el) {
2444
2910
  const nodeId = helpers.ensureNodeId(el);
2445
2911
  const instanceToken = helpers.setInstanceToken(el);
2912
+ const packedPath = helpers.packElementPath(
2913
+ helpers.buildElementPath(el)
2914
+ );
2446
2915
  entries.push({
2447
2916
  nodeId,
2448
- path: helpers.buildElementPath(el),
2917
+ path: packedPath,
2449
2918
  instanceToken
2450
2919
  });
2451
2920
  const tag = el.tagName.toLowerCase();
@@ -2473,11 +2942,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2473
2942
  win[frameTokenKey] = frameToken;
2474
2943
  const root = document.documentElement;
2475
2944
  if (!root) {
2476
- return { html: "", frameToken, entries };
2945
+ return { html: "", frameToken, nodeTable, entries };
2477
2946
  }
2478
2947
  return {
2479
2948
  html: helpers.serializeElement(root),
2480
2949
  frameToken,
2950
+ nodeTable,
2481
2951
  entries
2482
2952
  };
2483
2953
  },
@@ -2495,13 +2965,18 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
2495
2965
  );
2496
2966
  const nodePaths = /* @__PURE__ */ new Map();
2497
2967
  const nodeMeta = /* @__PURE__ */ new Map();
2498
- for (const entry of frameSnapshot.entries) {
2968
+ for (const [index, entry] of frameSnapshot.entries.entries()) {
2969
+ const path5 = decodeSerializedElementPath(
2970
+ frameSnapshot.nodeTable,
2971
+ entry.path,
2972
+ `entries[${index}].path`
2973
+ );
2499
2974
  nodePaths.set(entry.nodeId, {
2500
2975
  context: [
2501
2976
  ...baseContext,
2502
- ...entry.path.context || []
2977
+ ...path5.context || []
2503
2978
  ],
2504
- nodes: entry.path.nodes
2979
+ nodes: path5.nodes
2505
2980
  });
2506
2981
  nodeMeta.set(entry.nodeId, {
2507
2982
  frameToken: frameSnapshot.frameToken,
@@ -4895,7 +5370,7 @@ async function performInput(page, path5, options) {
4895
5370
  await resolved.element.type(options.text);
4896
5371
  }
4897
5372
  if (options.pressEnter) {
4898
- await resolved.element.press("Enter");
5373
+ await resolved.element.press("Enter", { noWaitAfter: true });
4899
5374
  }
4900
5375
  return {
4901
5376
  ok: true,
@@ -5561,7 +6036,26 @@ var ACTION_WAIT_PROFILES = {
5561
6036
  type: ROBUST_PROFILE
5562
6037
  };
5563
6038
  var NETWORK_POLL_MS = 50;
5564
- var IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["websocket", "eventsource"]);
6039
+ var NETWORK_RELAX_AFTER_MS = 1800;
6040
+ var RELAXED_ALLOWED_PENDING = 2;
6041
+ var HEAVY_VISUAL_REQUEST_WINDOW_MS = 5e3;
6042
+ var TRACKED_RESOURCE_TYPES = /* @__PURE__ */ new Set([
6043
+ "document",
6044
+ "fetch",
6045
+ "xhr",
6046
+ "stylesheet",
6047
+ "image",
6048
+ "font",
6049
+ "media"
6050
+ ]);
6051
+ var HEAVY_RESOURCE_TYPES = /* @__PURE__ */ new Set(["document", "fetch", "xhr"]);
6052
+ var HEAVY_VISUAL_RESOURCE_TYPES = /* @__PURE__ */ new Set([
6053
+ "stylesheet",
6054
+ "image",
6055
+ "font",
6056
+ "media"
6057
+ ]);
6058
+ var IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["websocket", "eventsource", "manifest"]);
5565
6059
  var NOOP_SESSION = {
5566
6060
  async wait() {
5567
6061
  },
@@ -5571,7 +6065,7 @@ var NOOP_SESSION = {
5571
6065
  function createPostActionWaitSession(page, action, override) {
5572
6066
  const profile = resolveActionWaitProfile(action, override);
5573
6067
  if (!profile.enabled) return NOOP_SESSION;
5574
- const tracker = profile.includeNetwork ? new ScopedNetworkTracker(page) : null;
6068
+ const tracker = profile.includeNetwork ? new AdaptiveNetworkTracker(page) : null;
5575
6069
  tracker?.start();
5576
6070
  let settled = false;
5577
6071
  return {
@@ -5579,19 +6073,32 @@ function createPostActionWaitSession(page, action, override) {
5579
6073
  if (settled) return;
5580
6074
  settled = true;
5581
6075
  const deadline = Date.now() + profile.timeout;
5582
- const networkWait = tracker ? tracker.waitForQuiet({
5583
- deadline,
5584
- quietMs: profile.networkQuietMs
5585
- }) : Promise.resolve();
6076
+ const visualTimeout = profile.includeNetwork ? Math.min(
6077
+ profile.timeout,
6078
+ resolveNetworkBackedVisualTimeout(profile.settleMs)
6079
+ ) : profile.timeout;
5586
6080
  try {
5587
- await Promise.all([
5588
- waitForVisualStabilityAcrossFrames(page, {
5589
- timeout: profile.timeout,
6081
+ try {
6082
+ await waitForVisualStabilityAcrossFrames(page, {
6083
+ timeout: visualTimeout,
5590
6084
  settleMs: profile.settleMs
5591
- }),
5592
- networkWait
5593
- ]);
5594
- } catch {
6085
+ });
6086
+ } catch (error) {
6087
+ if (isStealthWaitUnavailableError(error)) {
6088
+ throw error;
6089
+ }
6090
+ } finally {
6091
+ tracker?.freezeCollection();
6092
+ }
6093
+ if (tracker) {
6094
+ try {
6095
+ await tracker.waitForQuiet({
6096
+ deadline,
6097
+ quietMs: profile.networkQuietMs
6098
+ });
6099
+ } catch {
6100
+ }
6101
+ }
5595
6102
  } finally {
5596
6103
  tracker?.stop();
5597
6104
  }
@@ -5631,53 +6138,70 @@ function normalizeMs(value, fallback) {
5631
6138
  }
5632
6139
  return Math.max(0, Math.floor(value));
5633
6140
  }
5634
- var ScopedNetworkTracker = class {
6141
+ function resolveNetworkBackedVisualTimeout(settleMs) {
6142
+ const derived = settleMs * 3 + 300;
6143
+ return Math.max(1200, Math.min(2500, derived));
6144
+ }
6145
+ var AdaptiveNetworkTracker = class {
5635
6146
  constructor(page) {
5636
6147
  this.page = page;
5637
6148
  }
5638
- pending = /* @__PURE__ */ new Set();
6149
+ pending = /* @__PURE__ */ new Map();
5639
6150
  started = false;
6151
+ collecting = false;
6152
+ startedAt = 0;
5640
6153
  idleSince = Date.now();
5641
6154
  start() {
5642
6155
  if (this.started) return;
5643
6156
  this.started = true;
6157
+ this.collecting = true;
6158
+ this.startedAt = Date.now();
6159
+ this.idleSince = this.startedAt;
5644
6160
  this.page.on("request", this.handleRequestStarted);
5645
6161
  this.page.on("requestfinished", this.handleRequestFinished);
5646
6162
  this.page.on("requestfailed", this.handleRequestFinished);
5647
6163
  }
6164
+ freezeCollection() {
6165
+ if (!this.started) return;
6166
+ this.collecting = false;
6167
+ }
5648
6168
  stop() {
5649
6169
  if (!this.started) return;
5650
6170
  this.started = false;
6171
+ this.collecting = false;
5651
6172
  this.page.off("request", this.handleRequestStarted);
5652
6173
  this.page.off("requestfinished", this.handleRequestFinished);
5653
6174
  this.page.off("requestfailed", this.handleRequestFinished);
5654
6175
  this.pending.clear();
6176
+ this.startedAt = 0;
5655
6177
  this.idleSince = Date.now();
5656
6178
  }
5657
6179
  async waitForQuiet(options) {
5658
6180
  const quietMs = Math.max(0, options.quietMs);
5659
6181
  if (quietMs === 0) return;
5660
6182
  while (Date.now() < options.deadline) {
5661
- if (this.pending.size === 0) {
6183
+ const now = Date.now();
6184
+ const allowedPending = this.resolveAllowedPending(now);
6185
+ if (this.pending.size <= allowedPending) {
5662
6186
  if (this.idleSince === 0) {
5663
- this.idleSince = Date.now();
6187
+ this.idleSince = now;
5664
6188
  }
5665
- const idleFor = Date.now() - this.idleSince;
6189
+ const idleFor = now - this.idleSince;
5666
6190
  if (idleFor >= quietMs) {
5667
6191
  return;
5668
6192
  }
5669
6193
  } else {
5670
6194
  this.idleSince = 0;
5671
6195
  }
5672
- const remaining = Math.max(1, options.deadline - Date.now());
5673
- await sleep(Math.min(NETWORK_POLL_MS, remaining));
6196
+ const remaining = Math.max(1, options.deadline - now);
6197
+ await sleep2(Math.min(NETWORK_POLL_MS, remaining));
5674
6198
  }
5675
6199
  }
5676
6200
  handleRequestStarted = (request) => {
5677
- if (!this.started) return;
5678
- const resourceType = request.resourceType();
5679
- if (IGNORED_RESOURCE_TYPES.has(resourceType)) return;
5680
- this.pending.add(request);
6201
+ if (!this.started || !this.collecting) return;
6202
+ const trackedRequest = this.classifyRequest(request);
6203
+ if (!trackedRequest) return;
6204
+ this.pending.set(request, trackedRequest);
5681
6205
  this.idleSince = 0;
5682
6206
  };
5683
6207
  handleRequestFinished = (request) => {
@@ -5687,8 +6211,35 @@ var ScopedNetworkTracker = class {
5687
6211
  this.idleSince = Date.now();
5688
6212
  }
5689
6213
  };
6214
+ classifyRequest(request) {
6215
+ const resourceType = request.resourceType().toLowerCase();
6216
+ if (IGNORED_RESOURCE_TYPES.has(resourceType)) return null;
6217
+ if (!TRACKED_RESOURCE_TYPES.has(resourceType)) return null;
6218
+ const frame = request.frame();
6219
+ if (!frame || frame !== this.page.mainFrame()) return null;
6220
+ return {
6221
+ resourceType,
6222
+ startedAt: Date.now()
6223
+ };
6224
+ }
6225
+ resolveAllowedPending(now) {
6226
+ const relaxed = now - this.startedAt >= NETWORK_RELAX_AFTER_MS ? RELAXED_ALLOWED_PENDING : 0;
6227
+ if (this.hasHeavyPending(now)) return 0;
6228
+ return relaxed;
6229
+ }
6230
+ hasHeavyPending(now) {
6231
+ for (const trackedRequest of this.pending.values()) {
6232
+ if (HEAVY_RESOURCE_TYPES.has(trackedRequest.resourceType)) {
6233
+ return true;
6234
+ }
6235
+ if (HEAVY_VISUAL_RESOURCE_TYPES.has(trackedRequest.resourceType) && now - trackedRequest.startedAt < HEAVY_VISUAL_REQUEST_WINDOW_MS) {
6236
+ return true;
6237
+ }
6238
+ }
6239
+ return false;
6240
+ }
5690
6241
  };
5691
- async function sleep(ms) {
6242
+ async function sleep2(ms) {
5692
6243
  await new Promise((resolve) => {
5693
6244
  setTimeout(resolve, ms);
5694
6245
  });
@@ -7128,16 +7679,18 @@ var CACHE_IMPORT_BATCH_SIZE = 200;
7128
7679
  var RemoteSessionClient = class {
7129
7680
  baseUrl;
7130
7681
  key;
7131
- constructor(baseUrl, key) {
7682
+ authScheme;
7683
+ constructor(baseUrl, key, authScheme = "api-key") {
7132
7684
  this.baseUrl = normalizeBaseUrl(baseUrl);
7133
7685
  this.key = key;
7686
+ this.authScheme = authScheme;
7134
7687
  }
7135
7688
  async create(request) {
7136
7689
  const response = await fetch(`${this.baseUrl}/sessions`, {
7137
7690
  method: "POST",
7138
7691
  headers: {
7139
7692
  "content-type": "application/json",
7140
- "x-api-key": this.key
7693
+ ...this.authHeaders()
7141
7694
  },
7142
7695
  body: JSON.stringify(request)
7143
7696
  });
@@ -7150,7 +7703,7 @@ var RemoteSessionClient = class {
7150
7703
  const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
7151
7704
  method: "DELETE",
7152
7705
  headers: {
7153
- "x-api-key": this.key
7706
+ ...this.authHeaders()
7154
7707
  }
7155
7708
  });
7156
7709
  if (response.status === 204) {
@@ -7180,7 +7733,7 @@ var RemoteSessionClient = class {
7180
7733
  method: "POST",
7181
7734
  headers: {
7182
7735
  "content-type": "application/json",
7183
- "x-api-key": this.key
7736
+ ...this.authHeaders()
7184
7737
  },
7185
7738
  body: JSON.stringify({ entries })
7186
7739
  });
@@ -7189,6 +7742,16 @@ var RemoteSessionClient = class {
7189
7742
  }
7190
7743
  return await response.json();
7191
7744
  }
7745
+ authHeaders() {
7746
+ if (this.authScheme === "bearer") {
7747
+ return {
7748
+ authorization: `Bearer ${this.key}`
7749
+ };
7750
+ }
7751
+ return {
7752
+ "x-api-key": this.key
7753
+ };
7754
+ }
7192
7755
  };
7193
7756
  function normalizeBaseUrl(baseUrl) {
7194
7757
  return baseUrl.replace(/\/+$/, "");
@@ -7229,9 +7792,9 @@ function toRemoteErrorCode(code) {
7229
7792
 
7230
7793
  // src/remote/runtime.ts
7231
7794
  var DEFAULT_REMOTE_BASE_URL = "https://remote.opensteer.com";
7232
- function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl()) {
7795
+ function createRemoteRuntimeState(key, baseUrl = resolveRemoteBaseUrl(), authScheme = "api-key") {
7233
7796
  return {
7234
- sessionClient: new RemoteSessionClient(baseUrl, key),
7797
+ sessionClient: new RemoteSessionClient(baseUrl, key, authScheme),
7235
7798
  cdpClient: new RemoteCdpClient(),
7236
7799
  actionClient: null,
7237
7800
  sessionId: null
@@ -7296,7 +7859,8 @@ var Opensteer = class _Opensteer {
7296
7859
  }
7297
7860
  this.remote = createRemoteRuntimeState(
7298
7861
  apiKey,
7299
- remoteConfig?.baseUrl
7862
+ remoteConfig?.baseUrl,
7863
+ remoteConfig?.authScheme
7300
7864
  );
7301
7865
  } else {
7302
7866
  this.remote = null;
@@ -7752,7 +8316,7 @@ var Opensteer = class _Opensteer {
7752
8316
  await handle.type(options.text);
7753
8317
  }
7754
8318
  if (options.pressEnter) {
7755
- await handle.press("Enter");
8319
+ await handle.press("Enter", { noWaitAfter: true });
7756
8320
  }
7757
8321
  });
7758
8322
  } catch (err) {