opensteer 0.4.5 → 0.4.7
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/CHANGELOG.md +4 -3
- package/README.md +4 -4
- package/bin/opensteer.mjs +5 -6
- package/dist/{chunk-2NKR4JZ6.js → chunk-PIJI7FBH.js} +1121 -308
- package/dist/cli/server.cjs +1117 -304
- package/dist/cli/server.js +3 -2
- package/dist/index.cjs +1127 -313
- package/dist/index.d.cts +69 -42
- package/dist/index.d.ts +69 -42
- package/dist/index.js +13 -11
- package/package.json +73 -80
package/dist/index.cjs
CHANGED
|
@@ -338,6 +338,8 @@ var init_extractor = __esm({
|
|
|
338
338
|
var index_exports = {};
|
|
339
339
|
__export(index_exports, {
|
|
340
340
|
ActionWsClient: () => ActionWsClient,
|
|
341
|
+
CloudCdpClient: () => CloudCdpClient,
|
|
342
|
+
CloudSessionClient: () => CloudSessionClient,
|
|
341
343
|
CounterResolutionError: () => CounterResolutionError,
|
|
342
344
|
ElementPathError: () => ElementPathError,
|
|
343
345
|
LocalSelectorStorage: () => LocalSelectorStorage,
|
|
@@ -351,9 +353,7 @@ __export(index_exports, {
|
|
|
351
353
|
OS_UNAVAILABLE_ATTR: () => OS_UNAVAILABLE_ATTR,
|
|
352
354
|
Opensteer: () => Opensteer,
|
|
353
355
|
OpensteerActionError: () => OpensteerActionError,
|
|
354
|
-
|
|
355
|
-
RemoteCdpClient: () => RemoteCdpClient,
|
|
356
|
-
RemoteSessionClient: () => RemoteSessionClient,
|
|
356
|
+
OpensteerCloudError: () => OpensteerCloudError,
|
|
357
357
|
buildElementPathFromHandle: () => buildElementPathFromHandle,
|
|
358
358
|
buildElementPathFromSelector: () => buildElementPathFromSelector,
|
|
359
359
|
buildPathSelectorHint: () => buildPathSelectorHint,
|
|
@@ -365,6 +365,9 @@ __export(index_exports, {
|
|
|
365
365
|
clearCookies: () => clearCookies,
|
|
366
366
|
cloneElementPath: () => cloneElementPath,
|
|
367
367
|
closeTab: () => closeTab,
|
|
368
|
+
cloudNotLaunchedError: () => cloudNotLaunchedError,
|
|
369
|
+
cloudSessionContractVersion: () => cloudSessionContractVersion,
|
|
370
|
+
cloudUnsupportedMethodError: () => cloudUnsupportedMethodError,
|
|
368
371
|
collectLocalSelectorCacheEntries: () => collectLocalSelectorCacheEntries,
|
|
369
372
|
countArrayItemsWithPath: () => countArrayItemsWithPath,
|
|
370
373
|
createEmptyRegistry: () => createEmptyRegistry,
|
|
@@ -396,8 +399,6 @@ __export(index_exports, {
|
|
|
396
399
|
performSelect: () => performSelect,
|
|
397
400
|
prepareSnapshot: () => prepareSnapshot,
|
|
398
401
|
pressKey: () => pressKey,
|
|
399
|
-
remoteNotLaunchedError: () => remoteNotLaunchedError,
|
|
400
|
-
remoteUnsupportedMethodError: () => remoteUnsupportedMethodError,
|
|
401
402
|
resolveCounterElement: () => resolveCounterElement,
|
|
402
403
|
resolveCountersBatch: () => resolveCountersBatch,
|
|
403
404
|
resolveElementPath: () => resolveElementPath,
|
|
@@ -826,7 +827,7 @@ var BrowserPool = class {
|
|
|
826
827
|
const context = contexts[0];
|
|
827
828
|
const pages = context.pages();
|
|
828
829
|
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
829
|
-
return { browser, context, page,
|
|
830
|
+
return { browser, context, page, isExternal: true };
|
|
830
831
|
} catch (error) {
|
|
831
832
|
if (browser) {
|
|
832
833
|
await browser.close().catch(() => void 0);
|
|
@@ -861,7 +862,7 @@ var BrowserPool = class {
|
|
|
861
862
|
context = await browser.newContext(options.context || {});
|
|
862
863
|
page = await context.newPage();
|
|
863
864
|
}
|
|
864
|
-
return { browser, context, page,
|
|
865
|
+
return { browser, context, page, isExternal: false };
|
|
865
866
|
}
|
|
866
867
|
async launchSandbox(options) {
|
|
867
868
|
const browser = await import_playwright.chromium.launch({
|
|
@@ -872,7 +873,7 @@ var BrowserPool = class {
|
|
|
872
873
|
const context = await browser.newContext(options.context || {});
|
|
873
874
|
const page = await context.newPage();
|
|
874
875
|
this.browser = browser;
|
|
875
|
-
return { browser, context, page,
|
|
876
|
+
return { browser, context, page, isExternal: false };
|
|
876
877
|
}
|
|
877
878
|
};
|
|
878
879
|
|
|
@@ -943,28 +944,27 @@ function assertNoLegacyAiConfig(source, config) {
|
|
|
943
944
|
);
|
|
944
945
|
}
|
|
945
946
|
}
|
|
946
|
-
function
|
|
947
|
+
function assertNoLegacyRuntimeConfig(source, config) {
|
|
947
948
|
if (!config || typeof config !== "object") return;
|
|
948
949
|
const configRecord = config;
|
|
949
950
|
if (hasOwn(configRecord, "runtime")) {
|
|
950
951
|
throw new Error(
|
|
951
|
-
`Legacy "runtime" config is no longer supported in ${source}. Use top-level "
|
|
952
|
+
`Legacy "runtime" config is no longer supported in ${source}. Use top-level "cloud" instead.`
|
|
952
953
|
);
|
|
953
954
|
}
|
|
954
|
-
if (hasOwn(configRecord, "
|
|
955
|
+
if (hasOwn(configRecord, "mode")) {
|
|
955
956
|
throw new Error(
|
|
956
|
-
`Top-level "
|
|
957
|
+
`Top-level "mode" config is no longer supported in ${source}. Use "cloud: true" to enable cloud mode.`
|
|
957
958
|
);
|
|
958
959
|
}
|
|
959
|
-
|
|
960
|
-
if (typeof remoteValue === "boolean") {
|
|
960
|
+
if (hasOwn(configRecord, "remote")) {
|
|
961
961
|
throw new Error(
|
|
962
|
-
`
|
|
962
|
+
`Top-level "remote" config is no longer supported in ${source}. Use "cloud" options instead.`
|
|
963
963
|
);
|
|
964
964
|
}
|
|
965
|
-
if (
|
|
965
|
+
if (hasOwn(configRecord, "apiKey")) {
|
|
966
966
|
throw new Error(
|
|
967
|
-
`
|
|
967
|
+
`Top-level "apiKey" config is not supported in ${source}. Use "cloud.apiKey" instead.`
|
|
968
968
|
);
|
|
969
969
|
}
|
|
970
970
|
}
|
|
@@ -1010,20 +1010,52 @@ function parseNumber(value) {
|
|
|
1010
1010
|
if (!Number.isFinite(parsed)) return void 0;
|
|
1011
1011
|
return parsed;
|
|
1012
1012
|
}
|
|
1013
|
-
function
|
|
1013
|
+
function parseRuntimeMode(value, source) {
|
|
1014
1014
|
if (value == null) return void 0;
|
|
1015
1015
|
if (typeof value !== "string") {
|
|
1016
1016
|
throw new Error(
|
|
1017
|
-
`Invalid ${source} value "${String(value)}". Use "local" or "
|
|
1017
|
+
`Invalid ${source} value "${String(value)}". Use "local" or "cloud".`
|
|
1018
1018
|
);
|
|
1019
1019
|
}
|
|
1020
1020
|
const normalized = value.trim().toLowerCase();
|
|
1021
1021
|
if (!normalized) return void 0;
|
|
1022
|
-
if (normalized === "local" || normalized === "
|
|
1022
|
+
if (normalized === "local" || normalized === "cloud") {
|
|
1023
1023
|
return normalized;
|
|
1024
1024
|
}
|
|
1025
1025
|
throw new Error(
|
|
1026
|
-
`Invalid ${source} value "${value}". Use "local" or "
|
|
1026
|
+
`Invalid ${source} value "${value}". Use "local" or "cloud".`
|
|
1027
|
+
);
|
|
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
|
+
}
|
|
1045
|
+
function parseCloudAnnounce(value, source) {
|
|
1046
|
+
if (value == null) return void 0;
|
|
1047
|
+
if (typeof value !== "string") {
|
|
1048
|
+
throw new Error(
|
|
1049
|
+
`Invalid ${source} value "${String(value)}". Use "always", "off", or "tty".`
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
const normalized = value.trim().toLowerCase();
|
|
1053
|
+
if (!normalized) return void 0;
|
|
1054
|
+
if (normalized === "always" || normalized === "off" || normalized === "tty") {
|
|
1055
|
+
return normalized;
|
|
1056
|
+
}
|
|
1057
|
+
throw new Error(
|
|
1058
|
+
`Invalid ${source} value "${value}". Use "always", "off", or "tty".`
|
|
1027
1059
|
);
|
|
1028
1060
|
}
|
|
1029
1061
|
function resolveOpensteerApiKey() {
|
|
@@ -1031,29 +1063,46 @@ function resolveOpensteerApiKey() {
|
|
|
1031
1063
|
if (!value) return void 0;
|
|
1032
1064
|
return value;
|
|
1033
1065
|
}
|
|
1034
|
-
function
|
|
1066
|
+
function resolveOpensteerAuthScheme() {
|
|
1067
|
+
return parseAuthScheme(
|
|
1068
|
+
process.env.OPENSTEER_AUTH_SCHEME,
|
|
1069
|
+
"OPENSTEER_AUTH_SCHEME"
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
function normalizeCloudOptions(value) {
|
|
1035
1073
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1036
1074
|
return void 0;
|
|
1037
1075
|
}
|
|
1038
1076
|
return value;
|
|
1039
1077
|
}
|
|
1040
|
-
function
|
|
1041
|
-
|
|
1042
|
-
if (
|
|
1078
|
+
function parseCloudEnabled(value, source) {
|
|
1079
|
+
if (value == null) return void 0;
|
|
1080
|
+
if (typeof value === "boolean") return value;
|
|
1081
|
+
if (typeof value === "object" && !Array.isArray(value)) return true;
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
`Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
function resolveCloudSelection(config) {
|
|
1087
|
+
const configCloud = parseCloudEnabled(config.cloud, "cloud");
|
|
1088
|
+
if (configCloud !== void 0) {
|
|
1043
1089
|
return {
|
|
1044
|
-
|
|
1045
|
-
source: "config.
|
|
1090
|
+
cloud: configCloud,
|
|
1091
|
+
source: "config.cloud"
|
|
1046
1092
|
};
|
|
1047
1093
|
}
|
|
1048
|
-
const envMode =
|
|
1094
|
+
const envMode = parseRuntimeMode(
|
|
1095
|
+
process.env.OPENSTEER_MODE,
|
|
1096
|
+
"OPENSTEER_MODE"
|
|
1097
|
+
);
|
|
1049
1098
|
if (envMode) {
|
|
1050
1099
|
return {
|
|
1051
|
-
|
|
1100
|
+
cloud: envMode === "cloud",
|
|
1052
1101
|
source: "env.OPENSTEER_MODE"
|
|
1053
1102
|
};
|
|
1054
1103
|
}
|
|
1055
1104
|
return {
|
|
1056
|
-
|
|
1105
|
+
cloud: false,
|
|
1057
1106
|
source: "default"
|
|
1058
1107
|
};
|
|
1059
1108
|
}
|
|
@@ -1069,11 +1118,11 @@ function resolveConfig(input = {}) {
|
|
|
1069
1118
|
);
|
|
1070
1119
|
}
|
|
1071
1120
|
assertNoLegacyAiConfig("Opensteer constructor config", input);
|
|
1072
|
-
|
|
1121
|
+
assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
|
|
1073
1122
|
const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
|
|
1074
1123
|
const fileConfig = loadConfigFile(rootDir);
|
|
1075
1124
|
assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
|
|
1076
|
-
|
|
1125
|
+
assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
|
|
1077
1126
|
const envConfig = {
|
|
1078
1127
|
browser: {
|
|
1079
1128
|
headless: parseBool(process.env.OPENSTEER_HEADLESS),
|
|
@@ -1090,20 +1139,39 @@ function resolveConfig(input = {}) {
|
|
|
1090
1139
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
1091
1140
|
const resolved = mergeDeep(mergedWithEnv, input);
|
|
1092
1141
|
const envApiKey = resolveOpensteerApiKey();
|
|
1093
|
-
const
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1142
|
+
const envAuthScheme = resolveOpensteerAuthScheme();
|
|
1143
|
+
const envCloudAnnounce = parseCloudAnnounce(
|
|
1144
|
+
process.env.OPENSTEER_REMOTE_ANNOUNCE,
|
|
1145
|
+
"OPENSTEER_REMOTE_ANNOUNCE"
|
|
1146
|
+
);
|
|
1147
|
+
const inputCloudOptions = normalizeCloudOptions(input.cloud);
|
|
1148
|
+
const inputAuthScheme = parseAuthScheme(
|
|
1149
|
+
inputCloudOptions?.authScheme,
|
|
1150
|
+
"cloud.authScheme"
|
|
1151
|
+
);
|
|
1152
|
+
const inputCloudAnnounce = parseCloudAnnounce(
|
|
1153
|
+
inputCloudOptions?.announce,
|
|
1154
|
+
"cloud.announce"
|
|
1155
|
+
);
|
|
1156
|
+
const inputHasCloudApiKey = Boolean(
|
|
1157
|
+
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "apiKey")
|
|
1096
1158
|
);
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1159
|
+
const cloudSelection = resolveCloudSelection({
|
|
1160
|
+
cloud: resolved.cloud
|
|
1099
1161
|
});
|
|
1100
|
-
if (
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1162
|
+
if (cloudSelection.cloud) {
|
|
1163
|
+
const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
|
|
1164
|
+
const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
|
|
1165
|
+
const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
|
|
1166
|
+
resolved.cloud = {
|
|
1167
|
+
...resolvedCloud,
|
|
1168
|
+
authScheme,
|
|
1169
|
+
announce
|
|
1170
|
+
};
|
|
1103
1171
|
}
|
|
1104
|
-
if (envApiKey &&
|
|
1105
|
-
resolved.
|
|
1106
|
-
...
|
|
1172
|
+
if (envApiKey && cloudSelection.cloud && !inputHasCloudApiKey) {
|
|
1173
|
+
resolved.cloud = {
|
|
1174
|
+
...normalizeCloudOptions(resolved.cloud) ?? {},
|
|
1107
1175
|
apiKey: envApiKey
|
|
1108
1176
|
};
|
|
1109
1177
|
}
|
|
@@ -1146,15 +1214,52 @@ function getCallerFilePath() {
|
|
|
1146
1214
|
// src/navigation.ts
|
|
1147
1215
|
var DEFAULT_TIMEOUT = 3e4;
|
|
1148
1216
|
var DEFAULT_SETTLE_MS = 750;
|
|
1217
|
+
var FRAME_EVALUATE_GRACE_MS = 200;
|
|
1218
|
+
var STEALTH_WORLD_NAME = "__opensteer_wait__";
|
|
1219
|
+
var StealthWaitUnavailableError = class extends Error {
|
|
1220
|
+
constructor(cause) {
|
|
1221
|
+
super("Stealth visual wait requires Chromium CDP support.", { cause });
|
|
1222
|
+
this.name = "StealthWaitUnavailableError";
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
function isStealthWaitUnavailableError(error) {
|
|
1226
|
+
return error instanceof StealthWaitUnavailableError;
|
|
1227
|
+
}
|
|
1228
|
+
var FRAME_OWNER_VISIBILITY_FUNCTION = `function() {
|
|
1229
|
+
if (!(this instanceof HTMLElement)) return false;
|
|
1230
|
+
|
|
1231
|
+
var rect = this.getBoundingClientRect();
|
|
1232
|
+
if (rect.width <= 0 || rect.height <= 0) return false;
|
|
1233
|
+
if (
|
|
1234
|
+
rect.bottom <= 0 ||
|
|
1235
|
+
rect.right <= 0 ||
|
|
1236
|
+
rect.top >= window.innerHeight ||
|
|
1237
|
+
rect.left >= window.innerWidth
|
|
1238
|
+
) {
|
|
1239
|
+
return false;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
var style = window.getComputedStyle(this);
|
|
1243
|
+
if (
|
|
1244
|
+
style.display === 'none' ||
|
|
1245
|
+
style.visibility === 'hidden' ||
|
|
1246
|
+
Number(style.opacity) === 0
|
|
1247
|
+
) {
|
|
1248
|
+
return false;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
return true;
|
|
1252
|
+
}`;
|
|
1149
1253
|
function buildStabilityScript(timeout, settleMs) {
|
|
1150
1254
|
return `new Promise(function(resolve) {
|
|
1151
1255
|
var deadline = Date.now() + ${timeout};
|
|
1152
|
-
var timer = null;
|
|
1153
1256
|
var resolved = false;
|
|
1257
|
+
var timer = null;
|
|
1154
1258
|
var observers = [];
|
|
1155
1259
|
var observedShadowRoots = [];
|
|
1156
1260
|
var fonts = document.fonts;
|
|
1157
1261
|
var fontsReady = !fonts || fonts.status === 'loaded';
|
|
1262
|
+
var lastRelevantMutationAt = Date.now();
|
|
1158
1263
|
|
|
1159
1264
|
function clearObservers() {
|
|
1160
1265
|
for (var i = 0; i < observers.length; i++) {
|
|
@@ -1172,9 +1277,87 @@ function buildStabilityScript(timeout, settleMs) {
|
|
|
1172
1277
|
resolve();
|
|
1173
1278
|
}
|
|
1174
1279
|
|
|
1280
|
+
function isElementVisiblyIntersectingViewport(element) {
|
|
1281
|
+
if (!(element instanceof Element)) return false;
|
|
1282
|
+
|
|
1283
|
+
var rect = element.getBoundingClientRect();
|
|
1284
|
+
var inViewport =
|
|
1285
|
+
rect.width > 0 &&
|
|
1286
|
+
rect.height > 0 &&
|
|
1287
|
+
rect.bottom > 0 &&
|
|
1288
|
+
rect.right > 0 &&
|
|
1289
|
+
rect.top < window.innerHeight &&
|
|
1290
|
+
rect.left < window.innerWidth;
|
|
1291
|
+
|
|
1292
|
+
if (!inViewport) return false;
|
|
1293
|
+
|
|
1294
|
+
var style = window.getComputedStyle(element);
|
|
1295
|
+
if (style.visibility === 'hidden' || style.display === 'none') {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
if (Number(style.opacity) === 0) {
|
|
1299
|
+
return false;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
function resolveRelevantElement(node) {
|
|
1306
|
+
if (!node) return null;
|
|
1307
|
+
if (node instanceof Element) return node;
|
|
1308
|
+
if (typeof ShadowRoot !== 'undefined' && node instanceof ShadowRoot) {
|
|
1309
|
+
return node.host instanceof Element ? node.host : null;
|
|
1310
|
+
}
|
|
1311
|
+
var parentElement = node.parentElement;
|
|
1312
|
+
return parentElement instanceof Element ? parentElement : null;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function isNodeVisiblyRelevant(node) {
|
|
1316
|
+
var element = resolveRelevantElement(node);
|
|
1317
|
+
if (!element) return false;
|
|
1318
|
+
return isElementVisiblyIntersectingViewport(element);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function hasRelevantMutation(records) {
|
|
1322
|
+
for (var i = 0; i < records.length; i++) {
|
|
1323
|
+
var record = records[i];
|
|
1324
|
+
if (isNodeVisiblyRelevant(record.target)) return true;
|
|
1325
|
+
|
|
1326
|
+
var addedNodes = record.addedNodes;
|
|
1327
|
+
for (var j = 0; j < addedNodes.length; j++) {
|
|
1328
|
+
if (isNodeVisiblyRelevant(addedNodes[j])) return true;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
var removedNodes = record.removedNodes;
|
|
1332
|
+
for (var k = 0; k < removedNodes.length; k++) {
|
|
1333
|
+
if (isNodeVisiblyRelevant(removedNodes[k])) return true;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
function scheduleCheck() {
|
|
1341
|
+
if (resolved) return;
|
|
1342
|
+
if (timer) clearTimeout(timer);
|
|
1343
|
+
|
|
1344
|
+
var remaining = deadline - Date.now();
|
|
1345
|
+
if (remaining <= 0) {
|
|
1346
|
+
done();
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
var checkDelay = Math.min(120, Math.max(16, ${settleMs}));
|
|
1351
|
+
timer = setTimeout(checkNow, checkDelay);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1175
1354
|
function observeMutations(target) {
|
|
1176
1355
|
if (!target) return;
|
|
1177
|
-
var observer = new MutationObserver(function() {
|
|
1356
|
+
var observer = new MutationObserver(function(records) {
|
|
1357
|
+
if (!hasRelevantMutation(records)) return;
|
|
1358
|
+
lastRelevantMutationAt = Date.now();
|
|
1359
|
+
scheduleCheck();
|
|
1360
|
+
});
|
|
1178
1361
|
observer.observe(target, {
|
|
1179
1362
|
childList: true,
|
|
1180
1363
|
subtree: true,
|
|
@@ -1212,18 +1395,25 @@ function buildStabilityScript(timeout, settleMs) {
|
|
|
1212
1395
|
var images = root.querySelectorAll('img');
|
|
1213
1396
|
for (var i = 0; i < images.length; i++) {
|
|
1214
1397
|
var img = images[i];
|
|
1215
|
-
|
|
1216
|
-
|
|
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;
|
|
1398
|
+
if (!isElementVisiblyIntersectingViewport(img)) continue;
|
|
1399
|
+
if (!img.complete) return false;
|
|
1222
1400
|
}
|
|
1223
1401
|
return true;
|
|
1224
1402
|
}
|
|
1225
1403
|
|
|
1226
|
-
function
|
|
1404
|
+
function getAnimationTarget(effect) {
|
|
1405
|
+
if (!effect) return null;
|
|
1406
|
+
var target = effect.target;
|
|
1407
|
+
if (target instanceof Element) return target;
|
|
1408
|
+
|
|
1409
|
+
if (target && target.element instanceof Element) {
|
|
1410
|
+
return target.element;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
return null;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
function hasRunningVisibleFiniteAnimations() {
|
|
1227
1417
|
if (typeof document.getAnimations !== 'function') return false;
|
|
1228
1418
|
var animations = document.getAnimations();
|
|
1229
1419
|
|
|
@@ -1237,6 +1427,9 @@ function buildStabilityScript(timeout, settleMs) {
|
|
|
1237
1427
|
? timing.endTime
|
|
1238
1428
|
: Number.POSITIVE_INFINITY;
|
|
1239
1429
|
if (Number.isFinite(endTime) && endTime > 0) {
|
|
1430
|
+
var target = getAnimationTarget(effect);
|
|
1431
|
+
if (!target) continue;
|
|
1432
|
+
if (!isElementVisiblyIntersectingViewport(target)) continue;
|
|
1240
1433
|
return true;
|
|
1241
1434
|
}
|
|
1242
1435
|
}
|
|
@@ -1247,21 +1440,29 @@ function buildStabilityScript(timeout, settleMs) {
|
|
|
1247
1440
|
function isVisuallyReady() {
|
|
1248
1441
|
if (!fontsReady) return false;
|
|
1249
1442
|
if (!checkViewportImages(document)) return false;
|
|
1250
|
-
if (
|
|
1443
|
+
if (hasRunningVisibleFiniteAnimations()) return false;
|
|
1251
1444
|
return true;
|
|
1252
1445
|
}
|
|
1253
1446
|
|
|
1254
|
-
function
|
|
1255
|
-
if (Date.now()
|
|
1256
|
-
|
|
1447
|
+
function checkNow() {
|
|
1448
|
+
if (Date.now() >= deadline) {
|
|
1449
|
+
done();
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1257
1453
|
observeOpenShadowRoots();
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1454
|
+
|
|
1455
|
+
if (!isVisuallyReady()) {
|
|
1456
|
+
scheduleCheck();
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
if (Date.now() - lastRelevantMutationAt >= ${settleMs}) {
|
|
1461
|
+
done();
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
scheduleCheck();
|
|
1265
1466
|
}
|
|
1266
1467
|
|
|
1267
1468
|
observeMutations(document.documentElement);
|
|
@@ -1270,67 +1471,266 @@ function buildStabilityScript(timeout, settleMs) {
|
|
|
1270
1471
|
if (fonts && fonts.ready && typeof fonts.ready.then === 'function') {
|
|
1271
1472
|
fonts.ready.then(function() {
|
|
1272
1473
|
fontsReady = true;
|
|
1273
|
-
|
|
1474
|
+
scheduleCheck();
|
|
1475
|
+
}, function() {
|
|
1476
|
+
fontsReady = true;
|
|
1477
|
+
scheduleCheck();
|
|
1274
1478
|
});
|
|
1275
1479
|
}
|
|
1276
1480
|
|
|
1277
1481
|
var safetyTimer = setTimeout(done, ${timeout});
|
|
1278
1482
|
|
|
1279
|
-
|
|
1483
|
+
scheduleCheck();
|
|
1280
1484
|
})`;
|
|
1281
1485
|
}
|
|
1486
|
+
var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
1487
|
+
constructor(session) {
|
|
1488
|
+
this.session = session;
|
|
1489
|
+
}
|
|
1490
|
+
contextsByFrame = /* @__PURE__ */ new Map();
|
|
1491
|
+
disposed = false;
|
|
1492
|
+
static async create(page) {
|
|
1493
|
+
let session;
|
|
1494
|
+
try {
|
|
1495
|
+
session = await page.context().newCDPSession(page);
|
|
1496
|
+
} catch (error) {
|
|
1497
|
+
throw new StealthWaitUnavailableError(error);
|
|
1498
|
+
}
|
|
1499
|
+
const runtime = new _StealthCdpRuntime(session);
|
|
1500
|
+
try {
|
|
1501
|
+
await runtime.initialize();
|
|
1502
|
+
return runtime;
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
await runtime.dispose();
|
|
1505
|
+
throw new StealthWaitUnavailableError(error);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
async dispose() {
|
|
1509
|
+
if (this.disposed) return;
|
|
1510
|
+
this.disposed = true;
|
|
1511
|
+
this.contextsByFrame.clear();
|
|
1512
|
+
await this.session.detach().catch(() => void 0);
|
|
1513
|
+
}
|
|
1514
|
+
async waitForMainFrameVisualStability(options) {
|
|
1515
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
1516
|
+
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
1517
|
+
if (timeout <= 0) return;
|
|
1518
|
+
const frameRecords = await this.getFrameRecords();
|
|
1519
|
+
const mainFrame = frameRecords[0];
|
|
1520
|
+
if (!mainFrame) return;
|
|
1521
|
+
await this.waitForFrameVisualStability(mainFrame.frameId, timeout, settleMs);
|
|
1522
|
+
}
|
|
1523
|
+
async collectVisibleFrameIds() {
|
|
1524
|
+
const frameRecords = await this.getFrameRecords();
|
|
1525
|
+
if (frameRecords.length === 0) return [];
|
|
1526
|
+
const visibleFrameIds = [];
|
|
1527
|
+
for (const frameRecord of frameRecords) {
|
|
1528
|
+
if (!frameRecord.parentFrameId) {
|
|
1529
|
+
visibleFrameIds.push(frameRecord.frameId);
|
|
1530
|
+
continue;
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
const parentContextId = await this.ensureFrameContextId(
|
|
1534
|
+
frameRecord.parentFrameId
|
|
1535
|
+
);
|
|
1536
|
+
const visible = await this.isFrameOwnerVisible(
|
|
1537
|
+
frameRecord.frameId,
|
|
1538
|
+
parentContextId
|
|
1539
|
+
);
|
|
1540
|
+
if (visible) {
|
|
1541
|
+
visibleFrameIds.push(frameRecord.frameId);
|
|
1542
|
+
}
|
|
1543
|
+
} catch (error) {
|
|
1544
|
+
if (isIgnorableFrameError(error)) continue;
|
|
1545
|
+
throw error;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return visibleFrameIds;
|
|
1549
|
+
}
|
|
1550
|
+
async waitForFrameVisualStability(frameId, timeout, settleMs) {
|
|
1551
|
+
if (timeout <= 0) return;
|
|
1552
|
+
const script = buildStabilityScript(timeout, settleMs);
|
|
1553
|
+
let contextId = await this.ensureFrameContextId(frameId);
|
|
1554
|
+
try {
|
|
1555
|
+
await this.evaluateWithGuard(contextId, script, timeout);
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
if (!isMissingExecutionContextError(error)) {
|
|
1558
|
+
throw error;
|
|
1559
|
+
}
|
|
1560
|
+
this.contextsByFrame.delete(frameId);
|
|
1561
|
+
contextId = await this.ensureFrameContextId(frameId);
|
|
1562
|
+
await this.evaluateWithGuard(contextId, script, timeout);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
async initialize() {
|
|
1566
|
+
await this.session.send("Page.enable");
|
|
1567
|
+
await this.session.send("Runtime.enable");
|
|
1568
|
+
await this.session.send("DOM.enable");
|
|
1569
|
+
}
|
|
1570
|
+
async getFrameRecords() {
|
|
1571
|
+
const treeResult = await this.session.send("Page.getFrameTree");
|
|
1572
|
+
const records = [];
|
|
1573
|
+
walkFrameTree(treeResult.frameTree, null, records);
|
|
1574
|
+
return records;
|
|
1575
|
+
}
|
|
1576
|
+
async ensureFrameContextId(frameId) {
|
|
1577
|
+
const cached = this.contextsByFrame.get(frameId);
|
|
1578
|
+
if (cached != null) {
|
|
1579
|
+
return cached;
|
|
1580
|
+
}
|
|
1581
|
+
const world = await this.session.send("Page.createIsolatedWorld", {
|
|
1582
|
+
frameId,
|
|
1583
|
+
worldName: STEALTH_WORLD_NAME
|
|
1584
|
+
});
|
|
1585
|
+
this.contextsByFrame.set(frameId, world.executionContextId);
|
|
1586
|
+
return world.executionContextId;
|
|
1587
|
+
}
|
|
1588
|
+
async evaluateWithGuard(contextId, script, timeout) {
|
|
1589
|
+
const evaluationPromise = this.evaluateScript(contextId, script);
|
|
1590
|
+
const settledPromise = evaluationPromise.then(
|
|
1591
|
+
() => ({ kind: "resolved" }),
|
|
1592
|
+
(error) => ({ kind: "rejected", error })
|
|
1593
|
+
);
|
|
1594
|
+
const timeoutPromise = sleep(
|
|
1595
|
+
timeout + FRAME_EVALUATE_GRACE_MS
|
|
1596
|
+
).then(() => ({ kind: "timeout" }));
|
|
1597
|
+
const result = await Promise.race([
|
|
1598
|
+
settledPromise,
|
|
1599
|
+
timeoutPromise
|
|
1600
|
+
]);
|
|
1601
|
+
if (result.kind === "rejected") {
|
|
1602
|
+
throw result.error;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
async evaluateScript(contextId, expression) {
|
|
1606
|
+
const result = await this.session.send("Runtime.evaluate", {
|
|
1607
|
+
contextId,
|
|
1608
|
+
expression,
|
|
1609
|
+
awaitPromise: true,
|
|
1610
|
+
returnByValue: true
|
|
1611
|
+
});
|
|
1612
|
+
if (result.exceptionDetails) {
|
|
1613
|
+
throw new Error(formatCdpException(result.exceptionDetails));
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
async isFrameOwnerVisible(frameId, parentContextId) {
|
|
1617
|
+
const owner = await this.session.send("DOM.getFrameOwner", {
|
|
1618
|
+
frameId
|
|
1619
|
+
});
|
|
1620
|
+
const resolveParams = {
|
|
1621
|
+
executionContextId: parentContextId
|
|
1622
|
+
};
|
|
1623
|
+
if (typeof owner.backendNodeId === "number") {
|
|
1624
|
+
resolveParams.backendNodeId = owner.backendNodeId;
|
|
1625
|
+
} else if (typeof owner.nodeId === "number") {
|
|
1626
|
+
resolveParams.nodeId = owner.nodeId;
|
|
1627
|
+
} else {
|
|
1628
|
+
return false;
|
|
1629
|
+
}
|
|
1630
|
+
const resolved = await this.session.send(
|
|
1631
|
+
"DOM.resolveNode",
|
|
1632
|
+
resolveParams
|
|
1633
|
+
);
|
|
1634
|
+
const objectId = resolved.object?.objectId;
|
|
1635
|
+
if (!objectId) return false;
|
|
1636
|
+
try {
|
|
1637
|
+
const callResult = await this.session.send("Runtime.callFunctionOn", {
|
|
1638
|
+
objectId,
|
|
1639
|
+
functionDeclaration: FRAME_OWNER_VISIBILITY_FUNCTION,
|
|
1640
|
+
returnByValue: true
|
|
1641
|
+
});
|
|
1642
|
+
if (callResult.exceptionDetails) {
|
|
1643
|
+
throw new Error(formatCdpException(callResult.exceptionDetails));
|
|
1644
|
+
}
|
|
1645
|
+
return callResult.result.value === true;
|
|
1646
|
+
} finally {
|
|
1647
|
+
await this.releaseObject(objectId);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
async releaseObject(objectId) {
|
|
1651
|
+
await this.session.send("Runtime.releaseObject", {
|
|
1652
|
+
objectId
|
|
1653
|
+
}).catch(() => void 0);
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1282
1656
|
async function waitForVisualStability(page, options = {}) {
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1657
|
+
const runtime = await StealthCdpRuntime.create(page);
|
|
1658
|
+
try {
|
|
1659
|
+
await runtime.waitForMainFrameVisualStability(options);
|
|
1660
|
+
} finally {
|
|
1661
|
+
await runtime.dispose();
|
|
1662
|
+
}
|
|
1289
1663
|
}
|
|
1290
1664
|
async function waitForVisualStabilityAcrossFrames(page, options = {}) {
|
|
1291
1665
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
1292
1666
|
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
1667
|
+
if (timeout <= 0) return;
|
|
1293
1668
|
const deadline = Date.now() + timeout;
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1669
|
+
const runtime = await StealthCdpRuntime.create(page);
|
|
1670
|
+
try {
|
|
1671
|
+
while (true) {
|
|
1672
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
1673
|
+
if (remaining === 0) return;
|
|
1674
|
+
const frameIds = await runtime.collectVisibleFrameIds();
|
|
1675
|
+
if (frameIds.length === 0) return;
|
|
1676
|
+
await Promise.all(
|
|
1677
|
+
frameIds.map(async (frameId) => {
|
|
1678
|
+
try {
|
|
1679
|
+
await runtime.waitForFrameVisualStability(
|
|
1680
|
+
frameId,
|
|
1681
|
+
remaining,
|
|
1682
|
+
settleMs
|
|
1683
|
+
);
|
|
1684
|
+
} catch (error) {
|
|
1685
|
+
if (isIgnorableFrameError(error)) return;
|
|
1686
|
+
throw error;
|
|
1687
|
+
}
|
|
1688
|
+
})
|
|
1689
|
+
);
|
|
1690
|
+
const currentFrameIds = await runtime.collectVisibleFrameIds();
|
|
1691
|
+
if (sameFrameIds(frameIds, currentFrameIds)) {
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1314
1694
|
}
|
|
1695
|
+
} finally {
|
|
1696
|
+
await runtime.dispose();
|
|
1315
1697
|
}
|
|
1316
1698
|
}
|
|
1317
|
-
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1699
|
+
function walkFrameTree(node, parentFrameId, records) {
|
|
1700
|
+
const frameId = node.frame?.id;
|
|
1701
|
+
if (!frameId) return;
|
|
1702
|
+
records.push({
|
|
1703
|
+
frameId,
|
|
1704
|
+
parentFrameId
|
|
1705
|
+
});
|
|
1706
|
+
for (const child of node.childFrames ?? []) {
|
|
1707
|
+
walkFrameTree(child, frameId, records);
|
|
1708
|
+
}
|
|
1322
1709
|
}
|
|
1323
|
-
function
|
|
1710
|
+
function sameFrameIds(before, after) {
|
|
1324
1711
|
if (before.length !== after.length) return false;
|
|
1325
|
-
for (const
|
|
1326
|
-
if (!after.includes(
|
|
1712
|
+
for (const frameId of before) {
|
|
1713
|
+
if (!after.includes(frameId)) return false;
|
|
1327
1714
|
}
|
|
1328
1715
|
return true;
|
|
1329
1716
|
}
|
|
1717
|
+
function formatCdpException(details) {
|
|
1718
|
+
return details.exception?.description || details.text || "CDP runtime evaluation failed.";
|
|
1719
|
+
}
|
|
1720
|
+
function isMissingExecutionContextError(error) {
|
|
1721
|
+
if (!(error instanceof Error)) return false;
|
|
1722
|
+
const message = error.message;
|
|
1723
|
+
return message.includes("Cannot find context with specified id") || message.includes("Cannot find execution context");
|
|
1724
|
+
}
|
|
1330
1725
|
function isIgnorableFrameError(error) {
|
|
1331
1726
|
if (!(error instanceof Error)) return false;
|
|
1332
1727
|
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");
|
|
1728
|
+
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");
|
|
1729
|
+
}
|
|
1730
|
+
function sleep(ms) {
|
|
1731
|
+
return new Promise((resolve) => {
|
|
1732
|
+
setTimeout(resolve, ms);
|
|
1733
|
+
});
|
|
1334
1734
|
}
|
|
1335
1735
|
|
|
1336
1736
|
// src/storage/local.ts
|
|
@@ -2231,6 +2631,66 @@ var OS_BOUNDARY_ATTR = "data-os-boundary";
|
|
|
2231
2631
|
var OS_UNAVAILABLE_ATTR = "data-os-unavailable";
|
|
2232
2632
|
var OS_IFRAME_BOUNDARY_TAG = "os-iframe-root";
|
|
2233
2633
|
var OS_SHADOW_BOUNDARY_TAG = "os-shadow-root";
|
|
2634
|
+
function decodeSerializedNodeTableEntry(nodeTable, rawIndex, label) {
|
|
2635
|
+
if (typeof rawIndex !== "number" || !Number.isInteger(rawIndex) || rawIndex < 0 || rawIndex >= nodeTable.length) {
|
|
2636
|
+
throw new Error(
|
|
2637
|
+
`Invalid serialized path node index at "${label}": expected a valid table index.`
|
|
2638
|
+
);
|
|
2639
|
+
}
|
|
2640
|
+
const node = nodeTable[rawIndex];
|
|
2641
|
+
if (!node || typeof node !== "object") {
|
|
2642
|
+
throw new Error(
|
|
2643
|
+
`Invalid serialized path node at "${label}": table entry is missing.`
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
return node;
|
|
2647
|
+
}
|
|
2648
|
+
function decodeSerializedDomPath(nodeTable, rawPath, label) {
|
|
2649
|
+
if (!Array.isArray(rawPath)) {
|
|
2650
|
+
throw new Error(
|
|
2651
|
+
`Invalid serialized path at "${label}": expected an array of node indexes.`
|
|
2652
|
+
);
|
|
2653
|
+
}
|
|
2654
|
+
return rawPath.map(
|
|
2655
|
+
(value, index) => decodeSerializedNodeTableEntry(nodeTable, value, `${label}[${index}]`)
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
function decodeSerializedElementPath(nodeTable, rawPath, label) {
|
|
2659
|
+
if (!rawPath || typeof rawPath !== "object") {
|
|
2660
|
+
throw new Error(
|
|
2661
|
+
`Invalid serialized element path at "${label}": expected an object.`
|
|
2662
|
+
);
|
|
2663
|
+
}
|
|
2664
|
+
if (rawPath.context !== void 0 && !Array.isArray(rawPath.context)) {
|
|
2665
|
+
throw new Error(
|
|
2666
|
+
`Invalid serialized context at "${label}.context": expected an array.`
|
|
2667
|
+
);
|
|
2668
|
+
}
|
|
2669
|
+
const contextRaw = Array.isArray(rawPath.context) ? rawPath.context : [];
|
|
2670
|
+
const context = contextRaw.map((hop, hopIndex) => {
|
|
2671
|
+
if (!hop || typeof hop !== "object" || hop.kind !== "shadow") {
|
|
2672
|
+
throw new Error(
|
|
2673
|
+
`Invalid serialized context hop at "${label}.context[${hopIndex}]": expected a shadow hop.`
|
|
2674
|
+
);
|
|
2675
|
+
}
|
|
2676
|
+
return {
|
|
2677
|
+
kind: "shadow",
|
|
2678
|
+
host: decodeSerializedDomPath(
|
|
2679
|
+
nodeTable,
|
|
2680
|
+
hop.host,
|
|
2681
|
+
`${label}.context[${hopIndex}].host`
|
|
2682
|
+
)
|
|
2683
|
+
};
|
|
2684
|
+
});
|
|
2685
|
+
return {
|
|
2686
|
+
context,
|
|
2687
|
+
nodes: decodeSerializedDomPath(
|
|
2688
|
+
nodeTable,
|
|
2689
|
+
rawPath.nodes,
|
|
2690
|
+
`${label}.nodes`
|
|
2691
|
+
)
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2234
2694
|
async function serializePageHTML(page, _options = {}) {
|
|
2235
2695
|
return serializeFrameRecursive(page.mainFrame(), [], "f0");
|
|
2236
2696
|
}
|
|
@@ -2287,6 +2747,8 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
|
|
|
2287
2747
|
(Array.isArray(deferredMatchAttrKeys) ? deferredMatchAttrKeys : []).map((key) => String(key))
|
|
2288
2748
|
);
|
|
2289
2749
|
let counter = 1;
|
|
2750
|
+
const nodeTable = [];
|
|
2751
|
+
const nodeTableIndexByKey = /* @__PURE__ */ new Map();
|
|
2290
2752
|
const entries = [];
|
|
2291
2753
|
const helpers = {
|
|
2292
2754
|
nextToken() {
|
|
@@ -2488,6 +2950,47 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
|
|
|
2488
2950
|
nodes: target
|
|
2489
2951
|
};
|
|
2490
2952
|
},
|
|
2953
|
+
buildPathNodeKey(node) {
|
|
2954
|
+
const attrs = Object.entries(node.attrs || {}).sort(
|
|
2955
|
+
([a], [b]) => a.localeCompare(b)
|
|
2956
|
+
);
|
|
2957
|
+
const match = (node.match || []).map(
|
|
2958
|
+
(clause) => clause.kind === "attr" ? [
|
|
2959
|
+
"attr",
|
|
2960
|
+
clause.key,
|
|
2961
|
+
clause.op || "exact",
|
|
2962
|
+
clause.value ?? null
|
|
2963
|
+
] : ["position", clause.axis]
|
|
2964
|
+
);
|
|
2965
|
+
return JSON.stringify([
|
|
2966
|
+
node.tag,
|
|
2967
|
+
node.position.nthChild,
|
|
2968
|
+
node.position.nthOfType,
|
|
2969
|
+
attrs,
|
|
2970
|
+
match
|
|
2971
|
+
]);
|
|
2972
|
+
},
|
|
2973
|
+
internPathNode(node) {
|
|
2974
|
+
const key = helpers.buildPathNodeKey(node);
|
|
2975
|
+
const existing = nodeTableIndexByKey.get(key);
|
|
2976
|
+
if (existing != null) return existing;
|
|
2977
|
+
const index = nodeTable.length;
|
|
2978
|
+
nodeTable.push(node);
|
|
2979
|
+
nodeTableIndexByKey.set(key, index);
|
|
2980
|
+
return index;
|
|
2981
|
+
},
|
|
2982
|
+
packDomPath(path5) {
|
|
2983
|
+
return path5.map((node) => helpers.internPathNode(node));
|
|
2984
|
+
},
|
|
2985
|
+
packElementPath(path5) {
|
|
2986
|
+
return {
|
|
2987
|
+
context: (path5.context || []).map((hop) => ({
|
|
2988
|
+
kind: "shadow",
|
|
2989
|
+
host: helpers.packDomPath(hop.host)
|
|
2990
|
+
})),
|
|
2991
|
+
nodes: helpers.packDomPath(path5.nodes)
|
|
2992
|
+
};
|
|
2993
|
+
},
|
|
2491
2994
|
ensureNodeId(el) {
|
|
2492
2995
|
const next = `${frameKey2}_${counter++}`;
|
|
2493
2996
|
el.setAttribute(nodeAttr, next);
|
|
@@ -2517,9 +3020,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
|
|
|
2517
3020
|
serializeElement(el) {
|
|
2518
3021
|
const nodeId = helpers.ensureNodeId(el);
|
|
2519
3022
|
const instanceToken = helpers.setInstanceToken(el);
|
|
3023
|
+
const packedPath = helpers.packElementPath(
|
|
3024
|
+
helpers.buildElementPath(el)
|
|
3025
|
+
);
|
|
2520
3026
|
entries.push({
|
|
2521
3027
|
nodeId,
|
|
2522
|
-
path:
|
|
3028
|
+
path: packedPath,
|
|
2523
3029
|
instanceToken
|
|
2524
3030
|
});
|
|
2525
3031
|
const tag = el.tagName.toLowerCase();
|
|
@@ -2547,11 +3053,12 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
|
|
|
2547
3053
|
win[frameTokenKey] = frameToken;
|
|
2548
3054
|
const root = document.documentElement;
|
|
2549
3055
|
if (!root) {
|
|
2550
|
-
return { html: "", frameToken, entries };
|
|
3056
|
+
return { html: "", frameToken, nodeTable, entries };
|
|
2551
3057
|
}
|
|
2552
3058
|
return {
|
|
2553
3059
|
html: helpers.serializeElement(root),
|
|
2554
3060
|
frameToken,
|
|
3061
|
+
nodeTable,
|
|
2555
3062
|
entries
|
|
2556
3063
|
};
|
|
2557
3064
|
},
|
|
@@ -2569,13 +3076,18 @@ async function serializeFrameRecursive(frame, baseContext, frameKey) {
|
|
|
2569
3076
|
);
|
|
2570
3077
|
const nodePaths = /* @__PURE__ */ new Map();
|
|
2571
3078
|
const nodeMeta = /* @__PURE__ */ new Map();
|
|
2572
|
-
for (const entry of frameSnapshot.entries) {
|
|
3079
|
+
for (const [index, entry] of frameSnapshot.entries.entries()) {
|
|
3080
|
+
const path5 = decodeSerializedElementPath(
|
|
3081
|
+
frameSnapshot.nodeTable,
|
|
3082
|
+
entry.path,
|
|
3083
|
+
`entries[${index}].path`
|
|
3084
|
+
);
|
|
2573
3085
|
nodePaths.set(entry.nodeId, {
|
|
2574
3086
|
context: [
|
|
2575
3087
|
...baseContext,
|
|
2576
|
-
...
|
|
3088
|
+
...path5.context || []
|
|
2577
3089
|
],
|
|
2578
|
-
nodes:
|
|
3090
|
+
nodes: path5.nodes
|
|
2579
3091
|
});
|
|
2580
3092
|
nodeMeta.set(entry.nodeId, {
|
|
2581
3093
|
frameToken: frameSnapshot.frameToken,
|
|
@@ -4969,7 +5481,7 @@ async function performInput(page, path5, options) {
|
|
|
4969
5481
|
await resolved.element.type(options.text);
|
|
4970
5482
|
}
|
|
4971
5483
|
if (options.pressEnter) {
|
|
4972
|
-
await resolved.element.press("Enter");
|
|
5484
|
+
await resolved.element.press("Enter", { noWaitAfter: true });
|
|
4973
5485
|
}
|
|
4974
5486
|
return {
|
|
4975
5487
|
ok: true,
|
|
@@ -5645,7 +6157,26 @@ var ACTION_WAIT_PROFILES = {
|
|
|
5645
6157
|
type: ROBUST_PROFILE
|
|
5646
6158
|
};
|
|
5647
6159
|
var NETWORK_POLL_MS = 50;
|
|
5648
|
-
var
|
|
6160
|
+
var NETWORK_RELAX_AFTER_MS = 1800;
|
|
6161
|
+
var RELAXED_ALLOWED_PENDING = 2;
|
|
6162
|
+
var HEAVY_VISUAL_REQUEST_WINDOW_MS = 5e3;
|
|
6163
|
+
var TRACKED_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
6164
|
+
"document",
|
|
6165
|
+
"fetch",
|
|
6166
|
+
"xhr",
|
|
6167
|
+
"stylesheet",
|
|
6168
|
+
"image",
|
|
6169
|
+
"font",
|
|
6170
|
+
"media"
|
|
6171
|
+
]);
|
|
6172
|
+
var HEAVY_RESOURCE_TYPES = /* @__PURE__ */ new Set(["document", "fetch", "xhr"]);
|
|
6173
|
+
var HEAVY_VISUAL_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
6174
|
+
"stylesheet",
|
|
6175
|
+
"image",
|
|
6176
|
+
"font",
|
|
6177
|
+
"media"
|
|
6178
|
+
]);
|
|
6179
|
+
var IGNORED_RESOURCE_TYPES = /* @__PURE__ */ new Set(["websocket", "eventsource", "manifest"]);
|
|
5649
6180
|
var NOOP_SESSION = {
|
|
5650
6181
|
async wait() {
|
|
5651
6182
|
},
|
|
@@ -5655,7 +6186,7 @@ var NOOP_SESSION = {
|
|
|
5655
6186
|
function createPostActionWaitSession(page, action, override) {
|
|
5656
6187
|
const profile = resolveActionWaitProfile(action, override);
|
|
5657
6188
|
if (!profile.enabled) return NOOP_SESSION;
|
|
5658
|
-
const tracker = profile.includeNetwork ? new
|
|
6189
|
+
const tracker = profile.includeNetwork ? new AdaptiveNetworkTracker(page) : null;
|
|
5659
6190
|
tracker?.start();
|
|
5660
6191
|
let settled = false;
|
|
5661
6192
|
return {
|
|
@@ -5663,19 +6194,32 @@ function createPostActionWaitSession(page, action, override) {
|
|
|
5663
6194
|
if (settled) return;
|
|
5664
6195
|
settled = true;
|
|
5665
6196
|
const deadline = Date.now() + profile.timeout;
|
|
5666
|
-
const
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
6197
|
+
const visualTimeout = profile.includeNetwork ? Math.min(
|
|
6198
|
+
profile.timeout,
|
|
6199
|
+
resolveNetworkBackedVisualTimeout(profile.settleMs)
|
|
6200
|
+
) : profile.timeout;
|
|
5670
6201
|
try {
|
|
5671
|
-
|
|
5672
|
-
waitForVisualStabilityAcrossFrames(page, {
|
|
5673
|
-
timeout:
|
|
6202
|
+
try {
|
|
6203
|
+
await waitForVisualStabilityAcrossFrames(page, {
|
|
6204
|
+
timeout: visualTimeout,
|
|
5674
6205
|
settleMs: profile.settleMs
|
|
5675
|
-
})
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
6206
|
+
});
|
|
6207
|
+
} catch (error) {
|
|
6208
|
+
if (isStealthWaitUnavailableError(error)) {
|
|
6209
|
+
throw error;
|
|
6210
|
+
}
|
|
6211
|
+
} finally {
|
|
6212
|
+
tracker?.freezeCollection();
|
|
6213
|
+
}
|
|
6214
|
+
if (tracker) {
|
|
6215
|
+
try {
|
|
6216
|
+
await tracker.waitForQuiet({
|
|
6217
|
+
deadline,
|
|
6218
|
+
quietMs: profile.networkQuietMs
|
|
6219
|
+
});
|
|
6220
|
+
} catch {
|
|
6221
|
+
}
|
|
6222
|
+
}
|
|
5679
6223
|
} finally {
|
|
5680
6224
|
tracker?.stop();
|
|
5681
6225
|
}
|
|
@@ -5715,53 +6259,70 @@ function normalizeMs(value, fallback) {
|
|
|
5715
6259
|
}
|
|
5716
6260
|
return Math.max(0, Math.floor(value));
|
|
5717
6261
|
}
|
|
5718
|
-
|
|
6262
|
+
function resolveNetworkBackedVisualTimeout(settleMs) {
|
|
6263
|
+
const derived = settleMs * 3 + 300;
|
|
6264
|
+
return Math.max(1200, Math.min(2500, derived));
|
|
6265
|
+
}
|
|
6266
|
+
var AdaptiveNetworkTracker = class {
|
|
5719
6267
|
constructor(page) {
|
|
5720
6268
|
this.page = page;
|
|
5721
6269
|
}
|
|
5722
|
-
pending = /* @__PURE__ */ new
|
|
6270
|
+
pending = /* @__PURE__ */ new Map();
|
|
5723
6271
|
started = false;
|
|
6272
|
+
collecting = false;
|
|
6273
|
+
startedAt = 0;
|
|
5724
6274
|
idleSince = Date.now();
|
|
5725
6275
|
start() {
|
|
5726
6276
|
if (this.started) return;
|
|
5727
6277
|
this.started = true;
|
|
6278
|
+
this.collecting = true;
|
|
6279
|
+
this.startedAt = Date.now();
|
|
6280
|
+
this.idleSince = this.startedAt;
|
|
5728
6281
|
this.page.on("request", this.handleRequestStarted);
|
|
5729
6282
|
this.page.on("requestfinished", this.handleRequestFinished);
|
|
5730
6283
|
this.page.on("requestfailed", this.handleRequestFinished);
|
|
5731
6284
|
}
|
|
6285
|
+
freezeCollection() {
|
|
6286
|
+
if (!this.started) return;
|
|
6287
|
+
this.collecting = false;
|
|
6288
|
+
}
|
|
5732
6289
|
stop() {
|
|
5733
6290
|
if (!this.started) return;
|
|
5734
6291
|
this.started = false;
|
|
6292
|
+
this.collecting = false;
|
|
5735
6293
|
this.page.off("request", this.handleRequestStarted);
|
|
5736
6294
|
this.page.off("requestfinished", this.handleRequestFinished);
|
|
5737
6295
|
this.page.off("requestfailed", this.handleRequestFinished);
|
|
5738
6296
|
this.pending.clear();
|
|
6297
|
+
this.startedAt = 0;
|
|
5739
6298
|
this.idleSince = Date.now();
|
|
5740
6299
|
}
|
|
5741
6300
|
async waitForQuiet(options) {
|
|
5742
6301
|
const quietMs = Math.max(0, options.quietMs);
|
|
5743
6302
|
if (quietMs === 0) return;
|
|
5744
6303
|
while (Date.now() < options.deadline) {
|
|
5745
|
-
|
|
6304
|
+
const now = Date.now();
|
|
6305
|
+
const allowedPending = this.resolveAllowedPending(now);
|
|
6306
|
+
if (this.pending.size <= allowedPending) {
|
|
5746
6307
|
if (this.idleSince === 0) {
|
|
5747
|
-
this.idleSince =
|
|
6308
|
+
this.idleSince = now;
|
|
5748
6309
|
}
|
|
5749
|
-
const idleFor =
|
|
6310
|
+
const idleFor = now - this.idleSince;
|
|
5750
6311
|
if (idleFor >= quietMs) {
|
|
5751
6312
|
return;
|
|
5752
6313
|
}
|
|
5753
6314
|
} else {
|
|
5754
6315
|
this.idleSince = 0;
|
|
5755
6316
|
}
|
|
5756
|
-
const remaining = Math.max(1, options.deadline -
|
|
5757
|
-
await
|
|
6317
|
+
const remaining = Math.max(1, options.deadline - now);
|
|
6318
|
+
await sleep2(Math.min(NETWORK_POLL_MS, remaining));
|
|
5758
6319
|
}
|
|
5759
6320
|
}
|
|
5760
6321
|
handleRequestStarted = (request) => {
|
|
5761
|
-
if (!this.started) return;
|
|
5762
|
-
const
|
|
5763
|
-
if (
|
|
5764
|
-
this.pending.
|
|
6322
|
+
if (!this.started || !this.collecting) return;
|
|
6323
|
+
const trackedRequest = this.classifyRequest(request);
|
|
6324
|
+
if (!trackedRequest) return;
|
|
6325
|
+
this.pending.set(request, trackedRequest);
|
|
5765
6326
|
this.idleSince = 0;
|
|
5766
6327
|
};
|
|
5767
6328
|
handleRequestFinished = (request) => {
|
|
@@ -5771,8 +6332,35 @@ var ScopedNetworkTracker = class {
|
|
|
5771
6332
|
this.idleSince = Date.now();
|
|
5772
6333
|
}
|
|
5773
6334
|
};
|
|
6335
|
+
classifyRequest(request) {
|
|
6336
|
+
const resourceType = request.resourceType().toLowerCase();
|
|
6337
|
+
if (IGNORED_RESOURCE_TYPES.has(resourceType)) return null;
|
|
6338
|
+
if (!TRACKED_RESOURCE_TYPES.has(resourceType)) return null;
|
|
6339
|
+
const frame = request.frame();
|
|
6340
|
+
if (!frame || frame !== this.page.mainFrame()) return null;
|
|
6341
|
+
return {
|
|
6342
|
+
resourceType,
|
|
6343
|
+
startedAt: Date.now()
|
|
6344
|
+
};
|
|
6345
|
+
}
|
|
6346
|
+
resolveAllowedPending(now) {
|
|
6347
|
+
const relaxed = now - this.startedAt >= NETWORK_RELAX_AFTER_MS ? RELAXED_ALLOWED_PENDING : 0;
|
|
6348
|
+
if (this.hasHeavyPending(now)) return 0;
|
|
6349
|
+
return relaxed;
|
|
6350
|
+
}
|
|
6351
|
+
hasHeavyPending(now) {
|
|
6352
|
+
for (const trackedRequest of this.pending.values()) {
|
|
6353
|
+
if (HEAVY_RESOURCE_TYPES.has(trackedRequest.resourceType)) {
|
|
6354
|
+
return true;
|
|
6355
|
+
}
|
|
6356
|
+
if (HEAVY_VISUAL_RESOURCE_TYPES.has(trackedRequest.resourceType) && now - trackedRequest.startedAt < HEAVY_VISUAL_REQUEST_WINDOW_MS) {
|
|
6357
|
+
return true;
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
return false;
|
|
6361
|
+
}
|
|
5774
6362
|
};
|
|
5775
|
-
async function
|
|
6363
|
+
async function sleep2(ms) {
|
|
5776
6364
|
await new Promise((resolve) => {
|
|
5777
6365
|
setTimeout(resolve, ms);
|
|
5778
6366
|
});
|
|
@@ -6890,36 +7478,39 @@ function clonePersistedExtractNode(node) {
|
|
|
6890
7478
|
return JSON.parse(JSON.stringify(node));
|
|
6891
7479
|
}
|
|
6892
7480
|
|
|
6893
|
-
// src/
|
|
7481
|
+
// src/cloud/contracts.ts
|
|
7482
|
+
var cloudSessionContractVersion = "v3";
|
|
7483
|
+
|
|
7484
|
+
// src/cloud/action-ws-client.ts
|
|
6894
7485
|
var import_ws2 = __toESM(require("ws"), 1);
|
|
6895
7486
|
|
|
6896
|
-
// src/
|
|
6897
|
-
var
|
|
7487
|
+
// src/cloud/errors.ts
|
|
7488
|
+
var OpensteerCloudError = class extends Error {
|
|
6898
7489
|
code;
|
|
6899
7490
|
status;
|
|
6900
7491
|
details;
|
|
6901
7492
|
constructor(code, message, status, details) {
|
|
6902
7493
|
super(message);
|
|
6903
|
-
this.name = "
|
|
7494
|
+
this.name = "OpensteerCloudError";
|
|
6904
7495
|
this.code = code;
|
|
6905
7496
|
this.status = status;
|
|
6906
7497
|
this.details = details;
|
|
6907
7498
|
}
|
|
6908
7499
|
};
|
|
6909
|
-
function
|
|
6910
|
-
return new
|
|
6911
|
-
"
|
|
6912
|
-
message || `${method} is not supported in
|
|
7500
|
+
function cloudUnsupportedMethodError(method, message) {
|
|
7501
|
+
return new OpensteerCloudError(
|
|
7502
|
+
"CLOUD_UNSUPPORTED_METHOD",
|
|
7503
|
+
message || `${method} is not supported in cloud mode.`
|
|
6913
7504
|
);
|
|
6914
7505
|
}
|
|
6915
|
-
function
|
|
6916
|
-
return new
|
|
6917
|
-
"
|
|
6918
|
-
"
|
|
7506
|
+
function cloudNotLaunchedError() {
|
|
7507
|
+
return new OpensteerCloudError(
|
|
7508
|
+
"CLOUD_SESSION_NOT_FOUND",
|
|
7509
|
+
"Cloud session is not connected. Call launch() first."
|
|
6919
7510
|
);
|
|
6920
7511
|
}
|
|
6921
7512
|
|
|
6922
|
-
// src/
|
|
7513
|
+
// src/cloud/action-ws-client.ts
|
|
6923
7514
|
var ActionWsClient = class _ActionWsClient {
|
|
6924
7515
|
ws;
|
|
6925
7516
|
sessionId;
|
|
@@ -6936,18 +7527,18 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
6936
7527
|
});
|
|
6937
7528
|
ws.on("error", (error) => {
|
|
6938
7529
|
this.rejectAll(
|
|
6939
|
-
new
|
|
6940
|
-
"
|
|
6941
|
-
`
|
|
7530
|
+
new OpensteerCloudError(
|
|
7531
|
+
"CLOUD_TRANSPORT_ERROR",
|
|
7532
|
+
`Cloud action websocket error: ${error.message}`
|
|
6942
7533
|
)
|
|
6943
7534
|
);
|
|
6944
7535
|
});
|
|
6945
7536
|
ws.on("close", () => {
|
|
6946
7537
|
this.closed = true;
|
|
6947
7538
|
this.rejectAll(
|
|
6948
|
-
new
|
|
6949
|
-
"
|
|
6950
|
-
"
|
|
7539
|
+
new OpensteerCloudError(
|
|
7540
|
+
"CLOUD_SESSION_CLOSED",
|
|
7541
|
+
"Cloud action websocket closed."
|
|
6951
7542
|
)
|
|
6952
7543
|
);
|
|
6953
7544
|
});
|
|
@@ -6959,8 +7550,8 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
6959
7550
|
ws.once("open", () => resolve());
|
|
6960
7551
|
ws.once("error", (error) => {
|
|
6961
7552
|
reject(
|
|
6962
|
-
new
|
|
6963
|
-
"
|
|
7553
|
+
new OpensteerCloudError(
|
|
7554
|
+
"CLOUD_TRANSPORT_ERROR",
|
|
6964
7555
|
`Failed to connect action websocket: ${error.message}`
|
|
6965
7556
|
)
|
|
6966
7557
|
);
|
|
@@ -6970,9 +7561,9 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
6970
7561
|
}
|
|
6971
7562
|
async request(method, args) {
|
|
6972
7563
|
if (this.closed || this.ws.readyState !== import_ws2.default.OPEN) {
|
|
6973
|
-
throw new
|
|
6974
|
-
"
|
|
6975
|
-
"
|
|
7564
|
+
throw new OpensteerCloudError(
|
|
7565
|
+
"CLOUD_SESSION_CLOSED",
|
|
7566
|
+
"Cloud action websocket is closed."
|
|
6976
7567
|
);
|
|
6977
7568
|
}
|
|
6978
7569
|
const id = this.nextRequestId;
|
|
@@ -6991,8 +7582,8 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
6991
7582
|
this.ws.send(JSON.stringify(payload));
|
|
6992
7583
|
} catch (error) {
|
|
6993
7584
|
this.pending.delete(id);
|
|
6994
|
-
const message = error instanceof Error ? error.message : "Failed to send
|
|
6995
|
-
throw new
|
|
7585
|
+
const message = error instanceof Error ? error.message : "Failed to send cloud action request.";
|
|
7586
|
+
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
6996
7587
|
}
|
|
6997
7588
|
return await resultPromise;
|
|
6998
7589
|
}
|
|
@@ -7010,9 +7601,9 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
7010
7601
|
parsed = JSON.parse(rawDataToUtf8(raw));
|
|
7011
7602
|
} catch {
|
|
7012
7603
|
this.rejectAll(
|
|
7013
|
-
new
|
|
7014
|
-
"
|
|
7015
|
-
"Invalid
|
|
7604
|
+
new OpensteerCloudError(
|
|
7605
|
+
"CLOUD_TRANSPORT_ERROR",
|
|
7606
|
+
"Invalid cloud action response payload."
|
|
7016
7607
|
)
|
|
7017
7608
|
);
|
|
7018
7609
|
return;
|
|
@@ -7025,7 +7616,7 @@ var ActionWsClient = class _ActionWsClient {
|
|
|
7025
7616
|
return;
|
|
7026
7617
|
}
|
|
7027
7618
|
pending.reject(
|
|
7028
|
-
new
|
|
7619
|
+
new OpensteerCloudError(
|
|
7029
7620
|
parsed.code,
|
|
7030
7621
|
parsed.error,
|
|
7031
7622
|
void 0,
|
|
@@ -7053,7 +7644,7 @@ function withTokenQuery(wsUrl, token) {
|
|
|
7053
7644
|
return url.toString();
|
|
7054
7645
|
}
|
|
7055
7646
|
|
|
7056
|
-
// src/
|
|
7647
|
+
// src/cloud/local-cache-sync.ts
|
|
7057
7648
|
var import_fs3 = __toESM(require("fs"), 1);
|
|
7058
7649
|
var import_path5 = __toESM(require("path"), 1);
|
|
7059
7650
|
function collectLocalSelectorCacheEntries(storage) {
|
|
@@ -7177,24 +7768,24 @@ function dedupeNewest(entries) {
|
|
|
7177
7768
|
return [...byKey.values()];
|
|
7178
7769
|
}
|
|
7179
7770
|
|
|
7180
|
-
// src/
|
|
7771
|
+
// src/cloud/cdp-client.ts
|
|
7181
7772
|
var import_playwright2 = require("playwright");
|
|
7182
|
-
var
|
|
7773
|
+
var CloudCdpClient = class {
|
|
7183
7774
|
async connect(args) {
|
|
7184
7775
|
const endpoint = withTokenQuery2(args.wsUrl, args.token);
|
|
7185
7776
|
let browser;
|
|
7186
7777
|
try {
|
|
7187
7778
|
browser = await import_playwright2.chromium.connectOverCDP(endpoint);
|
|
7188
7779
|
} catch (error) {
|
|
7189
|
-
const message = error instanceof Error ? error.message : "Failed to connect to
|
|
7190
|
-
throw new
|
|
7780
|
+
const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
|
|
7781
|
+
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
7191
7782
|
}
|
|
7192
7783
|
const context = browser.contexts()[0];
|
|
7193
7784
|
if (!context) {
|
|
7194
7785
|
await browser.close();
|
|
7195
|
-
throw new
|
|
7196
|
-
"
|
|
7197
|
-
"
|
|
7786
|
+
throw new OpensteerCloudError(
|
|
7787
|
+
"CLOUD_INTERNAL",
|
|
7788
|
+
"Cloud browser returned no context."
|
|
7198
7789
|
);
|
|
7199
7790
|
}
|
|
7200
7791
|
const page = context.pages()[0] || await context.newPage();
|
|
@@ -7207,34 +7798,46 @@ function withTokenQuery2(wsUrl, token) {
|
|
|
7207
7798
|
return url.toString();
|
|
7208
7799
|
}
|
|
7209
7800
|
|
|
7210
|
-
// src/
|
|
7801
|
+
// src/cloud/session-client.ts
|
|
7211
7802
|
var CACHE_IMPORT_BATCH_SIZE = 200;
|
|
7212
|
-
var
|
|
7803
|
+
var CloudSessionClient = class {
|
|
7213
7804
|
baseUrl;
|
|
7214
7805
|
key;
|
|
7215
|
-
|
|
7806
|
+
authScheme;
|
|
7807
|
+
constructor(baseUrl, key, authScheme = "api-key") {
|
|
7216
7808
|
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
7217
7809
|
this.key = key;
|
|
7810
|
+
this.authScheme = authScheme;
|
|
7218
7811
|
}
|
|
7219
7812
|
async create(request) {
|
|
7220
7813
|
const response = await fetch(`${this.baseUrl}/sessions`, {
|
|
7221
7814
|
method: "POST",
|
|
7222
7815
|
headers: {
|
|
7223
7816
|
"content-type": "application/json",
|
|
7224
|
-
|
|
7817
|
+
...this.authHeaders()
|
|
7225
7818
|
},
|
|
7226
7819
|
body: JSON.stringify(request)
|
|
7227
7820
|
});
|
|
7228
7821
|
if (!response.ok) {
|
|
7229
7822
|
throw await parseHttpError(response);
|
|
7230
7823
|
}
|
|
7231
|
-
|
|
7824
|
+
let body;
|
|
7825
|
+
try {
|
|
7826
|
+
body = await response.json();
|
|
7827
|
+
} catch {
|
|
7828
|
+
throw new OpensteerCloudError(
|
|
7829
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
7830
|
+
"Invalid cloud session create response: expected a JSON object.",
|
|
7831
|
+
response.status
|
|
7832
|
+
);
|
|
7833
|
+
}
|
|
7834
|
+
return parseCreateResponse(body, response.status);
|
|
7232
7835
|
}
|
|
7233
7836
|
async close(sessionId) {
|
|
7234
7837
|
const response = await fetch(`${this.baseUrl}/sessions/${sessionId}`, {
|
|
7235
7838
|
method: "DELETE",
|
|
7236
7839
|
headers: {
|
|
7237
|
-
|
|
7840
|
+
...this.authHeaders()
|
|
7238
7841
|
}
|
|
7239
7842
|
});
|
|
7240
7843
|
if (response.status === 204) {
|
|
@@ -7264,7 +7867,7 @@ var RemoteSessionClient = class {
|
|
|
7264
7867
|
method: "POST",
|
|
7265
7868
|
headers: {
|
|
7266
7869
|
"content-type": "application/json",
|
|
7267
|
-
|
|
7870
|
+
...this.authHeaders()
|
|
7268
7871
|
},
|
|
7269
7872
|
body: JSON.stringify({ entries })
|
|
7270
7873
|
});
|
|
@@ -7273,10 +7876,148 @@ var RemoteSessionClient = class {
|
|
|
7273
7876
|
}
|
|
7274
7877
|
return await response.json();
|
|
7275
7878
|
}
|
|
7879
|
+
authHeaders() {
|
|
7880
|
+
if (this.authScheme === "bearer") {
|
|
7881
|
+
return {
|
|
7882
|
+
authorization: `Bearer ${this.key}`
|
|
7883
|
+
};
|
|
7884
|
+
}
|
|
7885
|
+
return {
|
|
7886
|
+
"x-api-key": this.key
|
|
7887
|
+
};
|
|
7888
|
+
}
|
|
7276
7889
|
};
|
|
7277
7890
|
function normalizeBaseUrl(baseUrl) {
|
|
7278
7891
|
return baseUrl.replace(/\/+$/, "");
|
|
7279
7892
|
}
|
|
7893
|
+
function parseCreateResponse(body, status) {
|
|
7894
|
+
const root = requireObject(
|
|
7895
|
+
body,
|
|
7896
|
+
"Invalid cloud session create response: expected a JSON object.",
|
|
7897
|
+
status
|
|
7898
|
+
);
|
|
7899
|
+
const sessionId = requireString(root, "sessionId", status);
|
|
7900
|
+
const actionWsUrl = requireString(root, "actionWsUrl", status);
|
|
7901
|
+
const cdpWsUrl = requireString(root, "cdpWsUrl", status);
|
|
7902
|
+
const actionToken = requireString(root, "actionToken", status);
|
|
7903
|
+
const cdpToken = requireString(root, "cdpToken", status);
|
|
7904
|
+
const cloudSessionUrl = requireString(root, "cloudSessionUrl", status);
|
|
7905
|
+
const cloudSessionRoot = requireObject(
|
|
7906
|
+
root.cloudSession,
|
|
7907
|
+
"Invalid cloud session create response: cloudSession must be an object.",
|
|
7908
|
+
status
|
|
7909
|
+
);
|
|
7910
|
+
const cloudSession = {
|
|
7911
|
+
sessionId: requireString(cloudSessionRoot, "sessionId", status, "cloudSession"),
|
|
7912
|
+
workspaceId: requireString(
|
|
7913
|
+
cloudSessionRoot,
|
|
7914
|
+
"workspaceId",
|
|
7915
|
+
status,
|
|
7916
|
+
"cloudSession"
|
|
7917
|
+
),
|
|
7918
|
+
state: requireString(cloudSessionRoot, "state", status, "cloudSession"),
|
|
7919
|
+
createdAt: requireNumber(cloudSessionRoot, "createdAt", status, "cloudSession"),
|
|
7920
|
+
sourceType: requireSourceType(cloudSessionRoot, "sourceType", status, "cloudSession"),
|
|
7921
|
+
sourceRef: optionalString(cloudSessionRoot, "sourceRef", status, "cloudSession"),
|
|
7922
|
+
label: optionalString(cloudSessionRoot, "label", status, "cloudSession")
|
|
7923
|
+
};
|
|
7924
|
+
const expiresAt = optionalNumber(root, "expiresAt", status);
|
|
7925
|
+
return {
|
|
7926
|
+
sessionId,
|
|
7927
|
+
actionWsUrl,
|
|
7928
|
+
cdpWsUrl,
|
|
7929
|
+
actionToken,
|
|
7930
|
+
cdpToken,
|
|
7931
|
+
expiresAt,
|
|
7932
|
+
cloudSessionUrl,
|
|
7933
|
+
cloudSession
|
|
7934
|
+
};
|
|
7935
|
+
}
|
|
7936
|
+
function requireObject(value, message, status) {
|
|
7937
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7938
|
+
throw new OpensteerCloudError("CLOUD_CONTRACT_MISMATCH", message, status);
|
|
7939
|
+
}
|
|
7940
|
+
return value;
|
|
7941
|
+
}
|
|
7942
|
+
function requireString(source, field, status, parent) {
|
|
7943
|
+
const value = source[field];
|
|
7944
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
7945
|
+
throw new OpensteerCloudError(
|
|
7946
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
7947
|
+
`Invalid cloud session create response: ${formatFieldPath(
|
|
7948
|
+
field,
|
|
7949
|
+
parent
|
|
7950
|
+
)} must be a non-empty string.`,
|
|
7951
|
+
status
|
|
7952
|
+
);
|
|
7953
|
+
}
|
|
7954
|
+
return value;
|
|
7955
|
+
}
|
|
7956
|
+
function requireNumber(source, field, status, parent) {
|
|
7957
|
+
const value = source[field];
|
|
7958
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
7959
|
+
throw new OpensteerCloudError(
|
|
7960
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
7961
|
+
`Invalid cloud session create response: ${formatFieldPath(
|
|
7962
|
+
field,
|
|
7963
|
+
parent
|
|
7964
|
+
)} must be a finite number.`,
|
|
7965
|
+
status
|
|
7966
|
+
);
|
|
7967
|
+
}
|
|
7968
|
+
return value;
|
|
7969
|
+
}
|
|
7970
|
+
function optionalString(source, field, status, parent) {
|
|
7971
|
+
const value = source[field];
|
|
7972
|
+
if (value == null) {
|
|
7973
|
+
return void 0;
|
|
7974
|
+
}
|
|
7975
|
+
if (typeof value !== "string") {
|
|
7976
|
+
throw new OpensteerCloudError(
|
|
7977
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
7978
|
+
`Invalid cloud session create response: ${formatFieldPath(
|
|
7979
|
+
field,
|
|
7980
|
+
parent
|
|
7981
|
+
)} must be a string when present.`,
|
|
7982
|
+
status
|
|
7983
|
+
);
|
|
7984
|
+
}
|
|
7985
|
+
return value;
|
|
7986
|
+
}
|
|
7987
|
+
function optionalNumber(source, field, status, parent) {
|
|
7988
|
+
const value = source[field];
|
|
7989
|
+
if (value == null) {
|
|
7990
|
+
return void 0;
|
|
7991
|
+
}
|
|
7992
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
7993
|
+
throw new OpensteerCloudError(
|
|
7994
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
7995
|
+
`Invalid cloud session create response: ${formatFieldPath(
|
|
7996
|
+
field,
|
|
7997
|
+
parent
|
|
7998
|
+
)} must be a finite number when present.`,
|
|
7999
|
+
status
|
|
8000
|
+
);
|
|
8001
|
+
}
|
|
8002
|
+
return value;
|
|
8003
|
+
}
|
|
8004
|
+
function requireSourceType(source, field, status, parent) {
|
|
8005
|
+
const value = source[field];
|
|
8006
|
+
if (value === "agent-thread" || value === "agent-run" || value === "local-cloud" || value === "manual") {
|
|
8007
|
+
return value;
|
|
8008
|
+
}
|
|
8009
|
+
throw new OpensteerCloudError(
|
|
8010
|
+
"CLOUD_CONTRACT_MISMATCH",
|
|
8011
|
+
`Invalid cloud session create response: ${formatFieldPath(
|
|
8012
|
+
field,
|
|
8013
|
+
parent
|
|
8014
|
+
)} must be one of "agent-thread", "agent-run", "local-cloud", or "manual".`,
|
|
8015
|
+
status
|
|
8016
|
+
);
|
|
8017
|
+
}
|
|
8018
|
+
function formatFieldPath(field, parent) {
|
|
8019
|
+
return parent ? `"${parent}.${field}"` : `"${field}"`;
|
|
8020
|
+
}
|
|
7280
8021
|
function zeroImportResponse() {
|
|
7281
8022
|
return {
|
|
7282
8023
|
imported: 0,
|
|
@@ -7300,33 +8041,46 @@ async function parseHttpError(response) {
|
|
|
7300
8041
|
} catch {
|
|
7301
8042
|
body = null;
|
|
7302
8043
|
}
|
|
7303
|
-
const code = typeof body?.code === "string" ?
|
|
7304
|
-
const message = typeof body?.error === "string" ? body.error : `
|
|
7305
|
-
return new
|
|
8044
|
+
const code = typeof body?.code === "string" ? toCloudErrorCode(body.code) : "CLOUD_TRANSPORT_ERROR";
|
|
8045
|
+
const message = typeof body?.error === "string" ? body.error : `Cloud request failed with status ${response.status}.`;
|
|
8046
|
+
return new OpensteerCloudError(code, message, response.status, body?.details);
|
|
7306
8047
|
}
|
|
7307
|
-
function
|
|
7308
|
-
if (code === "
|
|
8048
|
+
function toCloudErrorCode(code) {
|
|
8049
|
+
if (code === "CLOUD_AUTH_FAILED" || code === "CLOUD_SESSION_NOT_FOUND" || code === "CLOUD_SESSION_CLOSED" || code === "CLOUD_UNSUPPORTED_METHOD" || code === "CLOUD_INVALID_REQUEST" || code === "CLOUD_MODEL_NOT_ALLOWED" || code === "CLOUD_ACTION_FAILED" || code === "CLOUD_INTERNAL" || code === "CLOUD_CAPACITY_EXHAUSTED" || code === "CLOUD_RUNTIME_UNAVAILABLE" || code === "CLOUD_RUNTIME_MISMATCH" || code === "CLOUD_SESSION_STALE" || code === "CLOUD_CONTRACT_MISMATCH" || code === "CLOUD_CONTROL_PLANE_ERROR") {
|
|
7309
8050
|
return code;
|
|
7310
8051
|
}
|
|
7311
|
-
return "
|
|
8052
|
+
return "CLOUD_TRANSPORT_ERROR";
|
|
7312
8053
|
}
|
|
7313
8054
|
|
|
7314
|
-
// src/
|
|
7315
|
-
var
|
|
7316
|
-
|
|
8055
|
+
// src/cloud/runtime.ts
|
|
8056
|
+
var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
|
|
8057
|
+
var DEFAULT_CLOUD_APP_URL = "https://opensteer.com";
|
|
8058
|
+
function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key", appUrl = resolveCloudAppUrl()) {
|
|
7317
8059
|
return {
|
|
7318
|
-
sessionClient: new
|
|
7319
|
-
cdpClient: new
|
|
8060
|
+
sessionClient: new CloudSessionClient(baseUrl, key, authScheme),
|
|
8061
|
+
cdpClient: new CloudCdpClient(),
|
|
8062
|
+
appUrl: normalizeCloudAppUrl(appUrl),
|
|
7320
8063
|
actionClient: null,
|
|
7321
|
-
sessionId: null
|
|
8064
|
+
sessionId: null,
|
|
8065
|
+
localRunId: null,
|
|
8066
|
+
cloudSessionUrl: null
|
|
7322
8067
|
};
|
|
7323
8068
|
}
|
|
7324
|
-
function
|
|
8069
|
+
function resolveCloudBaseUrl() {
|
|
7325
8070
|
const value = process.env.OPENSTEER_BASE_URL?.trim();
|
|
7326
|
-
if (!value) return
|
|
8071
|
+
if (!value) return DEFAULT_CLOUD_BASE_URL;
|
|
8072
|
+
return value.replace(/\/+$/, "");
|
|
8073
|
+
}
|
|
8074
|
+
function resolveCloudAppUrl() {
|
|
8075
|
+
const value = process.env.OPENSTEER_APP_URL?.trim();
|
|
8076
|
+
if (!value) return DEFAULT_CLOUD_APP_URL;
|
|
8077
|
+
return normalizeCloudAppUrl(value);
|
|
8078
|
+
}
|
|
8079
|
+
function normalizeCloudAppUrl(value) {
|
|
8080
|
+
if (!value) return null;
|
|
7327
8081
|
return value.replace(/\/+$/, "");
|
|
7328
8082
|
}
|
|
7329
|
-
function
|
|
8083
|
+
function readCloudActionDescription(payload) {
|
|
7330
8084
|
const description = payload.description;
|
|
7331
8085
|
if (typeof description !== "string") return void 0;
|
|
7332
8086
|
const normalized = description.trim();
|
|
@@ -7334,7 +8088,7 @@ function readRemoteActionDescription(payload) {
|
|
|
7334
8088
|
}
|
|
7335
8089
|
|
|
7336
8090
|
// src/opensteer.ts
|
|
7337
|
-
var
|
|
8091
|
+
var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
|
|
7338
8092
|
"click",
|
|
7339
8093
|
"dblclick",
|
|
7340
8094
|
"rightclick",
|
|
@@ -7351,7 +8105,7 @@ var Opensteer = class _Opensteer {
|
|
|
7351
8105
|
namespace;
|
|
7352
8106
|
storage;
|
|
7353
8107
|
pool;
|
|
7354
|
-
|
|
8108
|
+
cloud;
|
|
7355
8109
|
browser = null;
|
|
7356
8110
|
pageRef = null;
|
|
7357
8111
|
contextRef = null;
|
|
@@ -7359,8 +8113,8 @@ var Opensteer = class _Opensteer {
|
|
|
7359
8113
|
snapshotCache = null;
|
|
7360
8114
|
constructor(config = {}) {
|
|
7361
8115
|
const resolved = resolveConfig(config);
|
|
7362
|
-
const
|
|
7363
|
-
|
|
8116
|
+
const cloudSelection = resolveCloudSelection({
|
|
8117
|
+
cloud: resolved.cloud
|
|
7364
8118
|
});
|
|
7365
8119
|
const model = resolved.model;
|
|
7366
8120
|
this.config = resolved;
|
|
@@ -7370,20 +8124,22 @@ var Opensteer = class _Opensteer {
|
|
|
7370
8124
|
this.namespace = resolveNamespace(resolved, rootDir);
|
|
7371
8125
|
this.storage = new LocalSelectorStorage(rootDir, this.namespace);
|
|
7372
8126
|
this.pool = new BrowserPool(resolved.browser || {});
|
|
7373
|
-
if (
|
|
7374
|
-
const
|
|
7375
|
-
const apiKey =
|
|
8127
|
+
if (cloudSelection.cloud) {
|
|
8128
|
+
const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
|
|
8129
|
+
const apiKey = cloudConfig?.apiKey?.trim();
|
|
7376
8130
|
if (!apiKey) {
|
|
7377
8131
|
throw new Error(
|
|
7378
|
-
"
|
|
8132
|
+
"Cloud mode requires a non-empty API key via cloud.apiKey or OPENSTEER_API_KEY."
|
|
7379
8133
|
);
|
|
7380
8134
|
}
|
|
7381
|
-
this.
|
|
8135
|
+
this.cloud = createCloudRuntimeState(
|
|
7382
8136
|
apiKey,
|
|
7383
|
-
|
|
8137
|
+
cloudConfig?.baseUrl,
|
|
8138
|
+
cloudConfig?.authScheme,
|
|
8139
|
+
cloudConfig?.appUrl
|
|
7384
8140
|
);
|
|
7385
8141
|
} else {
|
|
7386
|
-
this.
|
|
8142
|
+
this.cloud = null;
|
|
7387
8143
|
}
|
|
7388
8144
|
}
|
|
7389
8145
|
createLazyResolveCallback(model) {
|
|
@@ -7421,32 +8177,32 @@ var Opensteer = class _Opensteer {
|
|
|
7421
8177
|
};
|
|
7422
8178
|
return extract;
|
|
7423
8179
|
}
|
|
7424
|
-
async
|
|
7425
|
-
const result = await this.
|
|
8180
|
+
async invokeCloudActionAndResetCache(method, args) {
|
|
8181
|
+
const result = await this.invokeCloudAction(method, args);
|
|
7426
8182
|
this.snapshotCache = null;
|
|
7427
8183
|
return result;
|
|
7428
8184
|
}
|
|
7429
|
-
async
|
|
7430
|
-
const actionClient = this.
|
|
7431
|
-
const sessionId = this.
|
|
8185
|
+
async invokeCloudAction(method, args) {
|
|
8186
|
+
const actionClient = this.cloud?.actionClient;
|
|
8187
|
+
const sessionId = this.cloud?.sessionId;
|
|
7432
8188
|
if (!actionClient || !sessionId) {
|
|
7433
|
-
throw
|
|
8189
|
+
throw cloudNotLaunchedError();
|
|
7434
8190
|
}
|
|
7435
8191
|
const payload = args && typeof args === "object" ? args : {};
|
|
7436
8192
|
try {
|
|
7437
8193
|
return await actionClient.request(method, payload);
|
|
7438
8194
|
} catch (err) {
|
|
7439
|
-
if (err instanceof
|
|
8195
|
+
if (err instanceof OpensteerCloudError && err.code === "CLOUD_ACTION_FAILED" && CLOUD_INTERACTION_METHODS.has(method)) {
|
|
7440
8196
|
const detailsRecord = err.details && typeof err.details === "object" ? err.details : null;
|
|
7441
|
-
const
|
|
8197
|
+
const cloudFailure = normalizeActionFailure(
|
|
7442
8198
|
detailsRecord?.actionFailure
|
|
7443
8199
|
);
|
|
7444
|
-
const failure =
|
|
8200
|
+
const failure = cloudFailure || classifyActionFailure({
|
|
7445
8201
|
action: method,
|
|
7446
8202
|
error: err,
|
|
7447
8203
|
fallbackMessage: defaultActionFailureMessage(method)
|
|
7448
8204
|
});
|
|
7449
|
-
const description =
|
|
8205
|
+
const description = readCloudActionDescription(payload);
|
|
7450
8206
|
throw this.buildActionError(
|
|
7451
8207
|
method,
|
|
7452
8208
|
description,
|
|
@@ -7487,8 +8243,36 @@ var Opensteer = class _Opensteer {
|
|
|
7487
8243
|
}
|
|
7488
8244
|
return this.contextRef;
|
|
7489
8245
|
}
|
|
7490
|
-
|
|
7491
|
-
return this.
|
|
8246
|
+
getCloudSessionId() {
|
|
8247
|
+
return this.cloud?.sessionId ?? null;
|
|
8248
|
+
}
|
|
8249
|
+
getCloudSessionUrl() {
|
|
8250
|
+
return this.cloud?.cloudSessionUrl ?? null;
|
|
8251
|
+
}
|
|
8252
|
+
announceCloudSession(args) {
|
|
8253
|
+
if (!this.shouldAnnounceCloudSession()) {
|
|
8254
|
+
return;
|
|
8255
|
+
}
|
|
8256
|
+
const fields = [
|
|
8257
|
+
`sessionId=${args.sessionId}`,
|
|
8258
|
+
`workspaceId=${args.workspaceId}`
|
|
8259
|
+
];
|
|
8260
|
+
if (args.cloudSessionUrl) {
|
|
8261
|
+
fields.push(`url=${args.cloudSessionUrl}`);
|
|
8262
|
+
}
|
|
8263
|
+
process.stderr.write(`[opensteer] cloud session ready ${fields.join(" ")}
|
|
8264
|
+
`);
|
|
8265
|
+
}
|
|
8266
|
+
shouldAnnounceCloudSession() {
|
|
8267
|
+
const cloudConfig = this.config.cloud && typeof this.config.cloud === "object" ? this.config.cloud : null;
|
|
8268
|
+
const announce = cloudConfig?.announce ?? "always";
|
|
8269
|
+
if (announce === "off") {
|
|
8270
|
+
return false;
|
|
8271
|
+
}
|
|
8272
|
+
if (announce === "tty") {
|
|
8273
|
+
return Boolean(process.stderr.isTTY);
|
|
8274
|
+
}
|
|
8275
|
+
return true;
|
|
7492
8276
|
}
|
|
7493
8277
|
async launch(options = {}) {
|
|
7494
8278
|
if (this.pageRef && !this.ownsBrowser) {
|
|
@@ -7499,22 +8283,29 @@ var Opensteer = class _Opensteer {
|
|
|
7499
8283
|
if (this.pageRef && this.ownsBrowser) {
|
|
7500
8284
|
return;
|
|
7501
8285
|
}
|
|
7502
|
-
if (this.
|
|
8286
|
+
if (this.cloud) {
|
|
7503
8287
|
let actionClient = null;
|
|
7504
8288
|
let browser = null;
|
|
7505
8289
|
let sessionId = null;
|
|
8290
|
+
let localRunId = null;
|
|
7506
8291
|
try {
|
|
7507
8292
|
try {
|
|
7508
|
-
await this.
|
|
8293
|
+
await this.syncLocalSelectorCacheToCloud();
|
|
7509
8294
|
} catch (error) {
|
|
7510
8295
|
if (this.config.debug) {
|
|
7511
8296
|
const message = error instanceof Error ? error.message : String(error);
|
|
7512
8297
|
console.warn(
|
|
7513
|
-
`[opensteer]
|
|
8298
|
+
`[opensteer] cloud selector cache sync failed: ${message}`
|
|
7514
8299
|
);
|
|
7515
8300
|
}
|
|
7516
8301
|
}
|
|
7517
|
-
|
|
8302
|
+
localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
|
|
8303
|
+
this.cloud.localRunId = localRunId;
|
|
8304
|
+
const session2 = await this.cloud.sessionClient.create({
|
|
8305
|
+
cloudSessionContractVersion,
|
|
8306
|
+
sourceType: "local-cloud",
|
|
8307
|
+
clientSessionHint: this.namespace,
|
|
8308
|
+
localRunId,
|
|
7518
8309
|
name: this.namespace,
|
|
7519
8310
|
model: this.config.model,
|
|
7520
8311
|
launchContext: options.context || void 0
|
|
@@ -7525,7 +8316,7 @@ var Opensteer = class _Opensteer {
|
|
|
7525
8316
|
token: session2.actionToken,
|
|
7526
8317
|
sessionId: session2.sessionId
|
|
7527
8318
|
});
|
|
7528
|
-
const cdpConnection = await this.
|
|
8319
|
+
const cdpConnection = await this.cloud.cdpClient.connect({
|
|
7529
8320
|
wsUrl: session2.cdpWsUrl,
|
|
7530
8321
|
token: session2.cdpToken
|
|
7531
8322
|
});
|
|
@@ -7535,8 +8326,17 @@ var Opensteer = class _Opensteer {
|
|
|
7535
8326
|
this.pageRef = cdpConnection.page;
|
|
7536
8327
|
this.ownsBrowser = true;
|
|
7537
8328
|
this.snapshotCache = null;
|
|
7538
|
-
this.
|
|
7539
|
-
this.
|
|
8329
|
+
this.cloud.actionClient = actionClient;
|
|
8330
|
+
this.cloud.sessionId = sessionId;
|
|
8331
|
+
this.cloud.cloudSessionUrl = buildCloudSessionUrl(
|
|
8332
|
+
this.cloud.appUrl,
|
|
8333
|
+
session2.cloudSession.sessionId
|
|
8334
|
+
);
|
|
8335
|
+
this.announceCloudSession({
|
|
8336
|
+
sessionId: session2.sessionId,
|
|
8337
|
+
workspaceId: session2.cloudSession.workspaceId,
|
|
8338
|
+
cloudSessionUrl: this.cloud.cloudSessionUrl
|
|
8339
|
+
});
|
|
7540
8340
|
return;
|
|
7541
8341
|
} catch (error) {
|
|
7542
8342
|
if (actionClient) {
|
|
@@ -7546,8 +8346,9 @@ var Opensteer = class _Opensteer {
|
|
|
7546
8346
|
await browser.close().catch(() => void 0);
|
|
7547
8347
|
}
|
|
7548
8348
|
if (sessionId) {
|
|
7549
|
-
await this.
|
|
8349
|
+
await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
|
|
7550
8350
|
}
|
|
8351
|
+
this.cloud.cloudSessionUrl = null;
|
|
7551
8352
|
throw error;
|
|
7552
8353
|
}
|
|
7553
8354
|
}
|
|
@@ -7565,13 +8366,13 @@ var Opensteer = class _Opensteer {
|
|
|
7565
8366
|
}
|
|
7566
8367
|
static from(page, config = {}) {
|
|
7567
8368
|
const resolvedConfig = resolveConfig(config);
|
|
7568
|
-
const
|
|
7569
|
-
|
|
8369
|
+
const cloudSelection = resolveCloudSelection({
|
|
8370
|
+
cloud: resolvedConfig.cloud
|
|
7570
8371
|
});
|
|
7571
|
-
if (
|
|
7572
|
-
throw
|
|
8372
|
+
if (cloudSelection.cloud) {
|
|
8373
|
+
throw cloudUnsupportedMethodError(
|
|
7573
8374
|
"Opensteer.from(page)",
|
|
7574
|
-
"Opensteer.from(page) is not supported in
|
|
8375
|
+
"Opensteer.from(page) is not supported in cloud mode."
|
|
7575
8376
|
);
|
|
7576
8377
|
}
|
|
7577
8378
|
const instance = new _Opensteer(config);
|
|
@@ -7584,12 +8385,14 @@ var Opensteer = class _Opensteer {
|
|
|
7584
8385
|
}
|
|
7585
8386
|
async close() {
|
|
7586
8387
|
this.snapshotCache = null;
|
|
7587
|
-
if (this.
|
|
7588
|
-
const actionClient = this.
|
|
7589
|
-
const sessionId = this.
|
|
8388
|
+
if (this.cloud) {
|
|
8389
|
+
const actionClient = this.cloud.actionClient;
|
|
8390
|
+
const sessionId = this.cloud.sessionId;
|
|
7590
8391
|
const browser = this.browser;
|
|
7591
|
-
this.
|
|
7592
|
-
this.
|
|
8392
|
+
this.cloud.actionClient = null;
|
|
8393
|
+
this.cloud.sessionId = null;
|
|
8394
|
+
this.cloud.localRunId = null;
|
|
8395
|
+
this.cloud.cloudSessionUrl = null;
|
|
7593
8396
|
this.browser = null;
|
|
7594
8397
|
this.pageRef = null;
|
|
7595
8398
|
this.contextRef = null;
|
|
@@ -7601,7 +8404,7 @@ var Opensteer = class _Opensteer {
|
|
|
7601
8404
|
await browser.close().catch(() => void 0);
|
|
7602
8405
|
}
|
|
7603
8406
|
if (sessionId) {
|
|
7604
|
-
await this.
|
|
8407
|
+
await this.cloud.sessionClient.close(sessionId).catch(() => void 0);
|
|
7605
8408
|
}
|
|
7606
8409
|
return;
|
|
7607
8410
|
}
|
|
@@ -7613,17 +8416,17 @@ var Opensteer = class _Opensteer {
|
|
|
7613
8416
|
this.contextRef = null;
|
|
7614
8417
|
this.ownsBrowser = false;
|
|
7615
8418
|
}
|
|
7616
|
-
async
|
|
7617
|
-
if (!this.
|
|
8419
|
+
async syncLocalSelectorCacheToCloud() {
|
|
8420
|
+
if (!this.cloud) return;
|
|
7618
8421
|
const entries = collectLocalSelectorCacheEntries(this.storage);
|
|
7619
8422
|
if (!entries.length) return;
|
|
7620
|
-
await this.
|
|
8423
|
+
await this.cloud.sessionClient.importSelectorCache({
|
|
7621
8424
|
entries
|
|
7622
8425
|
});
|
|
7623
8426
|
}
|
|
7624
8427
|
async goto(url, options) {
|
|
7625
|
-
if (this.
|
|
7626
|
-
await this.
|
|
8428
|
+
if (this.cloud) {
|
|
8429
|
+
await this.invokeCloudActionAndResetCache("goto", { url, options });
|
|
7627
8430
|
return;
|
|
7628
8431
|
}
|
|
7629
8432
|
const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
|
|
@@ -7632,8 +8435,8 @@ var Opensteer = class _Opensteer {
|
|
|
7632
8435
|
this.snapshotCache = null;
|
|
7633
8436
|
}
|
|
7634
8437
|
async snapshot(options = {}) {
|
|
7635
|
-
if (this.
|
|
7636
|
-
return await this.
|
|
8438
|
+
if (this.cloud) {
|
|
8439
|
+
return await this.invokeCloudActionAndResetCache("snapshot", {
|
|
7637
8440
|
options
|
|
7638
8441
|
});
|
|
7639
8442
|
}
|
|
@@ -7642,8 +8445,8 @@ var Opensteer = class _Opensteer {
|
|
|
7642
8445
|
return prepared.cleanedHtml;
|
|
7643
8446
|
}
|
|
7644
8447
|
async state() {
|
|
7645
|
-
if (this.
|
|
7646
|
-
return await this.
|
|
8448
|
+
if (this.cloud) {
|
|
8449
|
+
return await this.invokeCloudAction("state", {});
|
|
7647
8450
|
}
|
|
7648
8451
|
const html = await this.snapshot({ mode: "action" });
|
|
7649
8452
|
return {
|
|
@@ -7653,8 +8456,8 @@ var Opensteer = class _Opensteer {
|
|
|
7653
8456
|
};
|
|
7654
8457
|
}
|
|
7655
8458
|
async screenshot(options = {}) {
|
|
7656
|
-
if (this.
|
|
7657
|
-
const b64 = await this.
|
|
8459
|
+
if (this.cloud) {
|
|
8460
|
+
const b64 = await this.invokeCloudAction(
|
|
7658
8461
|
"screenshot",
|
|
7659
8462
|
options
|
|
7660
8463
|
);
|
|
@@ -7668,8 +8471,8 @@ var Opensteer = class _Opensteer {
|
|
|
7668
8471
|
});
|
|
7669
8472
|
}
|
|
7670
8473
|
async click(options) {
|
|
7671
|
-
if (this.
|
|
7672
|
-
return await this.
|
|
8474
|
+
if (this.cloud) {
|
|
8475
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7673
8476
|
"click",
|
|
7674
8477
|
options
|
|
7675
8478
|
);
|
|
@@ -7681,8 +8484,8 @@ var Opensteer = class _Opensteer {
|
|
|
7681
8484
|
});
|
|
7682
8485
|
}
|
|
7683
8486
|
async dblclick(options) {
|
|
7684
|
-
if (this.
|
|
7685
|
-
return await this.
|
|
8487
|
+
if (this.cloud) {
|
|
8488
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7686
8489
|
"dblclick",
|
|
7687
8490
|
options
|
|
7688
8491
|
);
|
|
@@ -7694,8 +8497,8 @@ var Opensteer = class _Opensteer {
|
|
|
7694
8497
|
});
|
|
7695
8498
|
}
|
|
7696
8499
|
async rightclick(options) {
|
|
7697
|
-
if (this.
|
|
7698
|
-
return await this.
|
|
8500
|
+
if (this.cloud) {
|
|
8501
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7699
8502
|
"rightclick",
|
|
7700
8503
|
options
|
|
7701
8504
|
);
|
|
@@ -7707,8 +8510,8 @@ var Opensteer = class _Opensteer {
|
|
|
7707
8510
|
});
|
|
7708
8511
|
}
|
|
7709
8512
|
async hover(options) {
|
|
7710
|
-
if (this.
|
|
7711
|
-
return await this.
|
|
8513
|
+
if (this.cloud) {
|
|
8514
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7712
8515
|
"hover",
|
|
7713
8516
|
options
|
|
7714
8517
|
);
|
|
@@ -7806,8 +8609,8 @@ var Opensteer = class _Opensteer {
|
|
|
7806
8609
|
);
|
|
7807
8610
|
}
|
|
7808
8611
|
async input(options) {
|
|
7809
|
-
if (this.
|
|
7810
|
-
return await this.
|
|
8612
|
+
if (this.cloud) {
|
|
8613
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7811
8614
|
"input",
|
|
7812
8615
|
options
|
|
7813
8616
|
);
|
|
@@ -7836,7 +8639,7 @@ var Opensteer = class _Opensteer {
|
|
|
7836
8639
|
await handle.type(options.text);
|
|
7837
8640
|
}
|
|
7838
8641
|
if (options.pressEnter) {
|
|
7839
|
-
await handle.press("Enter");
|
|
8642
|
+
await handle.press("Enter", { noWaitAfter: true });
|
|
7840
8643
|
}
|
|
7841
8644
|
});
|
|
7842
8645
|
} catch (err) {
|
|
@@ -7909,8 +8712,8 @@ var Opensteer = class _Opensteer {
|
|
|
7909
8712
|
);
|
|
7910
8713
|
}
|
|
7911
8714
|
async select(options) {
|
|
7912
|
-
if (this.
|
|
7913
|
-
return await this.
|
|
8715
|
+
if (this.cloud) {
|
|
8716
|
+
return await this.invokeCloudActionAndResetCache(
|
|
7914
8717
|
"select",
|
|
7915
8718
|
options
|
|
7916
8719
|
);
|
|
@@ -8019,8 +8822,8 @@ var Opensteer = class _Opensteer {
|
|
|
8019
8822
|
);
|
|
8020
8823
|
}
|
|
8021
8824
|
async scroll(options = {}) {
|
|
8022
|
-
if (this.
|
|
8023
|
-
return await this.
|
|
8825
|
+
if (this.cloud) {
|
|
8826
|
+
return await this.invokeCloudActionAndResetCache(
|
|
8024
8827
|
"scroll",
|
|
8025
8828
|
options
|
|
8026
8829
|
);
|
|
@@ -8121,14 +8924,14 @@ var Opensteer = class _Opensteer {
|
|
|
8121
8924
|
}
|
|
8122
8925
|
// --- Tab Management ---
|
|
8123
8926
|
async tabs() {
|
|
8124
|
-
if (this.
|
|
8125
|
-
return await this.
|
|
8927
|
+
if (this.cloud) {
|
|
8928
|
+
return await this.invokeCloudAction("tabs", {});
|
|
8126
8929
|
}
|
|
8127
8930
|
return listTabs(this.context, this.page);
|
|
8128
8931
|
}
|
|
8129
8932
|
async newTab(url) {
|
|
8130
|
-
if (this.
|
|
8131
|
-
return await this.
|
|
8933
|
+
if (this.cloud) {
|
|
8934
|
+
return await this.invokeCloudActionAndResetCache("newTab", {
|
|
8132
8935
|
url
|
|
8133
8936
|
});
|
|
8134
8937
|
}
|
|
@@ -8138,8 +8941,8 @@ var Opensteer = class _Opensteer {
|
|
|
8138
8941
|
return info;
|
|
8139
8942
|
}
|
|
8140
8943
|
async switchTab(index) {
|
|
8141
|
-
if (this.
|
|
8142
|
-
await this.
|
|
8944
|
+
if (this.cloud) {
|
|
8945
|
+
await this.invokeCloudActionAndResetCache("switchTab", { index });
|
|
8143
8946
|
return;
|
|
8144
8947
|
}
|
|
8145
8948
|
const page = await switchTab(this.context, index);
|
|
@@ -8147,8 +8950,8 @@ var Opensteer = class _Opensteer {
|
|
|
8147
8950
|
this.snapshotCache = null;
|
|
8148
8951
|
}
|
|
8149
8952
|
async closeTab(index) {
|
|
8150
|
-
if (this.
|
|
8151
|
-
await this.
|
|
8953
|
+
if (this.cloud) {
|
|
8954
|
+
await this.invokeCloudActionAndResetCache("closeTab", { index });
|
|
8152
8955
|
return;
|
|
8153
8956
|
}
|
|
8154
8957
|
const newPage = await closeTab(this.context, this.page, index);
|
|
@@ -8159,8 +8962,8 @@ var Opensteer = class _Opensteer {
|
|
|
8159
8962
|
}
|
|
8160
8963
|
// --- Cookie Management ---
|
|
8161
8964
|
async getCookies(url) {
|
|
8162
|
-
if (this.
|
|
8163
|
-
return await this.
|
|
8965
|
+
if (this.cloud) {
|
|
8966
|
+
return await this.invokeCloudAction(
|
|
8164
8967
|
"getCookies",
|
|
8165
8968
|
{ url }
|
|
8166
8969
|
);
|
|
@@ -8168,41 +8971,41 @@ var Opensteer = class _Opensteer {
|
|
|
8168
8971
|
return getCookies(this.context, url);
|
|
8169
8972
|
}
|
|
8170
8973
|
async setCookie(cookie) {
|
|
8171
|
-
if (this.
|
|
8172
|
-
await this.
|
|
8974
|
+
if (this.cloud) {
|
|
8975
|
+
await this.invokeCloudAction("setCookie", cookie);
|
|
8173
8976
|
return;
|
|
8174
8977
|
}
|
|
8175
8978
|
return setCookie(this.context, cookie);
|
|
8176
8979
|
}
|
|
8177
8980
|
async clearCookies() {
|
|
8178
|
-
if (this.
|
|
8179
|
-
await this.
|
|
8981
|
+
if (this.cloud) {
|
|
8982
|
+
await this.invokeCloudAction("clearCookies", {});
|
|
8180
8983
|
return;
|
|
8181
8984
|
}
|
|
8182
8985
|
return clearCookies(this.context);
|
|
8183
8986
|
}
|
|
8184
8987
|
async exportCookies(filePath, url) {
|
|
8185
|
-
if (this.
|
|
8186
|
-
throw
|
|
8988
|
+
if (this.cloud) {
|
|
8989
|
+
throw cloudUnsupportedMethodError(
|
|
8187
8990
|
"exportCookies",
|
|
8188
|
-
"exportCookies() is not supported in
|
|
8991
|
+
"exportCookies() is not supported in cloud mode because it depends on local filesystem paths."
|
|
8189
8992
|
);
|
|
8190
8993
|
}
|
|
8191
8994
|
return exportCookies(this.context, filePath, url);
|
|
8192
8995
|
}
|
|
8193
8996
|
async importCookies(filePath) {
|
|
8194
|
-
if (this.
|
|
8195
|
-
throw
|
|
8997
|
+
if (this.cloud) {
|
|
8998
|
+
throw cloudUnsupportedMethodError(
|
|
8196
8999
|
"importCookies",
|
|
8197
|
-
"importCookies() is not supported in
|
|
9000
|
+
"importCookies() is not supported in cloud mode because it depends on local filesystem paths."
|
|
8198
9001
|
);
|
|
8199
9002
|
}
|
|
8200
9003
|
return importCookies(this.context, filePath);
|
|
8201
9004
|
}
|
|
8202
9005
|
// --- Keyboard Input ---
|
|
8203
9006
|
async pressKey(key) {
|
|
8204
|
-
if (this.
|
|
8205
|
-
await this.
|
|
9007
|
+
if (this.cloud) {
|
|
9008
|
+
await this.invokeCloudActionAndResetCache("pressKey", { key });
|
|
8206
9009
|
return;
|
|
8207
9010
|
}
|
|
8208
9011
|
await this.runWithPostActionWait("pressKey", void 0, async () => {
|
|
@@ -8211,8 +9014,8 @@ var Opensteer = class _Opensteer {
|
|
|
8211
9014
|
this.snapshotCache = null;
|
|
8212
9015
|
}
|
|
8213
9016
|
async type(text) {
|
|
8214
|
-
if (this.
|
|
8215
|
-
await this.
|
|
9017
|
+
if (this.cloud) {
|
|
9018
|
+
await this.invokeCloudActionAndResetCache("type", { text });
|
|
8216
9019
|
return;
|
|
8217
9020
|
}
|
|
8218
9021
|
await this.runWithPostActionWait("type", void 0, async () => {
|
|
@@ -8222,8 +9025,8 @@ var Opensteer = class _Opensteer {
|
|
|
8222
9025
|
}
|
|
8223
9026
|
// --- Element Info ---
|
|
8224
9027
|
async getElementText(options) {
|
|
8225
|
-
if (this.
|
|
8226
|
-
return await this.
|
|
9028
|
+
if (this.cloud) {
|
|
9029
|
+
return await this.invokeCloudAction("getElementText", options);
|
|
8227
9030
|
}
|
|
8228
9031
|
return this.executeElementInfoAction(
|
|
8229
9032
|
"getElementText",
|
|
@@ -8236,8 +9039,8 @@ var Opensteer = class _Opensteer {
|
|
|
8236
9039
|
);
|
|
8237
9040
|
}
|
|
8238
9041
|
async getElementValue(options) {
|
|
8239
|
-
if (this.
|
|
8240
|
-
return await this.
|
|
9042
|
+
if (this.cloud) {
|
|
9043
|
+
return await this.invokeCloudAction(
|
|
8241
9044
|
"getElementValue",
|
|
8242
9045
|
options
|
|
8243
9046
|
);
|
|
@@ -8252,8 +9055,8 @@ var Opensteer = class _Opensteer {
|
|
|
8252
9055
|
);
|
|
8253
9056
|
}
|
|
8254
9057
|
async getElementAttributes(options) {
|
|
8255
|
-
if (this.
|
|
8256
|
-
return await this.
|
|
9058
|
+
if (this.cloud) {
|
|
9059
|
+
return await this.invokeCloudAction(
|
|
8257
9060
|
"getElementAttributes",
|
|
8258
9061
|
options
|
|
8259
9062
|
);
|
|
@@ -8274,8 +9077,8 @@ var Opensteer = class _Opensteer {
|
|
|
8274
9077
|
);
|
|
8275
9078
|
}
|
|
8276
9079
|
async getElementBoundingBox(options) {
|
|
8277
|
-
if (this.
|
|
8278
|
-
return await this.
|
|
9080
|
+
if (this.cloud) {
|
|
9081
|
+
return await this.invokeCloudAction(
|
|
8279
9082
|
"getElementBoundingBox",
|
|
8280
9083
|
options
|
|
8281
9084
|
);
|
|
@@ -8290,14 +9093,14 @@ var Opensteer = class _Opensteer {
|
|
|
8290
9093
|
);
|
|
8291
9094
|
}
|
|
8292
9095
|
async getHtml(selector) {
|
|
8293
|
-
if (this.
|
|
8294
|
-
return await this.
|
|
9096
|
+
if (this.cloud) {
|
|
9097
|
+
return await this.invokeCloudAction("getHtml", { selector });
|
|
8295
9098
|
}
|
|
8296
9099
|
return getPageHtml(this.page, selector);
|
|
8297
9100
|
}
|
|
8298
9101
|
async getTitle() {
|
|
8299
|
-
if (this.
|
|
8300
|
-
return await this.
|
|
9102
|
+
if (this.cloud) {
|
|
9103
|
+
return await this.invokeCloudAction("getTitle", {});
|
|
8301
9104
|
}
|
|
8302
9105
|
return getPageTitle(this.page);
|
|
8303
9106
|
}
|
|
@@ -8335,10 +9138,10 @@ var Opensteer = class _Opensteer {
|
|
|
8335
9138
|
}
|
|
8336
9139
|
// --- File Upload ---
|
|
8337
9140
|
async uploadFile(options) {
|
|
8338
|
-
if (this.
|
|
8339
|
-
throw
|
|
9141
|
+
if (this.cloud) {
|
|
9142
|
+
throw cloudUnsupportedMethodError(
|
|
8340
9143
|
"uploadFile",
|
|
8341
|
-
"uploadFile() is not supported in
|
|
9144
|
+
"uploadFile() is not supported in cloud mode because file paths must be accessible on the cloud runtime."
|
|
8342
9145
|
);
|
|
8343
9146
|
}
|
|
8344
9147
|
const storageKey = this.resolveStorageKey(options.description);
|
|
@@ -8440,15 +9243,15 @@ var Opensteer = class _Opensteer {
|
|
|
8440
9243
|
}
|
|
8441
9244
|
// --- Wait for Text ---
|
|
8442
9245
|
async waitForText(text, options) {
|
|
8443
|
-
if (this.
|
|
8444
|
-
await this.
|
|
9246
|
+
if (this.cloud) {
|
|
9247
|
+
await this.invokeCloudAction("waitForText", { text, options });
|
|
8445
9248
|
return;
|
|
8446
9249
|
}
|
|
8447
9250
|
await this.page.getByText(text).first().waitFor({ timeout: options?.timeout ?? 3e4 });
|
|
8448
9251
|
}
|
|
8449
9252
|
async extract(options) {
|
|
8450
|
-
if (this.
|
|
8451
|
-
return await this.
|
|
9253
|
+
if (this.cloud) {
|
|
9254
|
+
return await this.invokeCloudAction("extract", options);
|
|
8452
9255
|
}
|
|
8453
9256
|
const storageKey = this.resolveStorageKey(options.description);
|
|
8454
9257
|
const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
|
|
@@ -8500,8 +9303,8 @@ var Opensteer = class _Opensteer {
|
|
|
8500
9303
|
return inflateDataPathObject(data);
|
|
8501
9304
|
}
|
|
8502
9305
|
async extractFromPlan(options) {
|
|
8503
|
-
if (this.
|
|
8504
|
-
return await this.
|
|
9306
|
+
if (this.cloud) {
|
|
9307
|
+
return await this.invokeCloudAction(
|
|
8505
9308
|
"extractFromPlan",
|
|
8506
9309
|
options
|
|
8507
9310
|
);
|
|
@@ -8550,10 +9353,10 @@ var Opensteer = class _Opensteer {
|
|
|
8550
9353
|
return this.storage;
|
|
8551
9354
|
}
|
|
8552
9355
|
clearCache() {
|
|
8553
|
-
if (this.
|
|
9356
|
+
if (this.cloud) {
|
|
8554
9357
|
this.snapshotCache = null;
|
|
8555
|
-
if (!this.
|
|
8556
|
-
void this.
|
|
9358
|
+
if (!this.cloud.actionClient) return;
|
|
9359
|
+
void this.invokeCloudAction("clearCache", {});
|
|
8557
9360
|
return;
|
|
8558
9361
|
}
|
|
8559
9362
|
this.storage.clearNamespace();
|
|
@@ -9581,6 +10384,16 @@ function getScrollDelta2(options) {
|
|
|
9581
10384
|
return { x: 0, y: absoluteAmount };
|
|
9582
10385
|
}
|
|
9583
10386
|
}
|
|
10387
|
+
function buildLocalRunId(namespace) {
|
|
10388
|
+
const normalized = namespace.trim() || "default";
|
|
10389
|
+
return `${normalized}-${Date.now().toString(36)}-${(0, import_crypto2.randomUUID)().slice(0, 8)}`;
|
|
10390
|
+
}
|
|
10391
|
+
function buildCloudSessionUrl(appUrl, sessionId) {
|
|
10392
|
+
if (!appUrl) {
|
|
10393
|
+
return null;
|
|
10394
|
+
}
|
|
10395
|
+
return `${appUrl}/browser/${encodeURIComponent(sessionId)}`;
|
|
10396
|
+
}
|
|
9584
10397
|
|
|
9585
10398
|
// src/ai/index.ts
|
|
9586
10399
|
init_resolver();
|
|
@@ -9589,6 +10402,8 @@ init_model();
|
|
|
9589
10402
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9590
10403
|
0 && (module.exports = {
|
|
9591
10404
|
ActionWsClient,
|
|
10405
|
+
CloudCdpClient,
|
|
10406
|
+
CloudSessionClient,
|
|
9592
10407
|
CounterResolutionError,
|
|
9593
10408
|
ElementPathError,
|
|
9594
10409
|
LocalSelectorStorage,
|
|
@@ -9602,9 +10417,7 @@ init_model();
|
|
|
9602
10417
|
OS_UNAVAILABLE_ATTR,
|
|
9603
10418
|
Opensteer,
|
|
9604
10419
|
OpensteerActionError,
|
|
9605
|
-
|
|
9606
|
-
RemoteCdpClient,
|
|
9607
|
-
RemoteSessionClient,
|
|
10420
|
+
OpensteerCloudError,
|
|
9608
10421
|
buildElementPathFromHandle,
|
|
9609
10422
|
buildElementPathFromSelector,
|
|
9610
10423
|
buildPathSelectorHint,
|
|
@@ -9616,6 +10429,9 @@ init_model();
|
|
|
9616
10429
|
clearCookies,
|
|
9617
10430
|
cloneElementPath,
|
|
9618
10431
|
closeTab,
|
|
10432
|
+
cloudNotLaunchedError,
|
|
10433
|
+
cloudSessionContractVersion,
|
|
10434
|
+
cloudUnsupportedMethodError,
|
|
9619
10435
|
collectLocalSelectorCacheEntries,
|
|
9620
10436
|
countArrayItemsWithPath,
|
|
9621
10437
|
createEmptyRegistry,
|
|
@@ -9647,8 +10463,6 @@ init_model();
|
|
|
9647
10463
|
performSelect,
|
|
9648
10464
|
prepareSnapshot,
|
|
9649
10465
|
pressKey,
|
|
9650
|
-
remoteNotLaunchedError,
|
|
9651
|
-
remoteUnsupportedMethodError,
|
|
9652
10466
|
resolveCounterElement,
|
|
9653
10467
|
resolveCountersBatch,
|
|
9654
10468
|
resolveElementPath,
|