opensteer 0.6.6 → 0.6.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/dist/{browser-profile-client-OUHaODro.d.ts → browser-profile-client-AGTsLQxT.d.ts} +1 -1
- package/dist/{browser-profile-client-D6PuRefA.d.cts → browser-profile-client-Biu6DyT6.d.cts} +1 -1
- package/dist/{chunk-H27BERRF.js → chunk-ACRSRDRN.js} +7825 -7631
- package/dist/cli/auth.d.cts +1 -1
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/profile.cjs +349 -168
- package/dist/cli/profile.d.cts +2 -2
- package/dist/cli/profile.d.ts +2 -2
- package/dist/cli/profile.js +1 -1
- package/dist/cli/server.cjs +330 -149
- package/dist/cli/server.js +1 -1
- package/dist/index.cjs +340 -147
- package/dist/index.d.cts +48 -5
- package/dist/index.d.ts +48 -5
- package/dist/index.js +15 -2
- package/dist/{types-BWItZPl_.d.ts → types-Cr10igF3.d.cts} +1 -1
- package/dist/{types-BWItZPl_.d.cts → types-Cr10igF3.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -421,6 +421,7 @@ var init_extractor = __esm({
|
|
|
421
421
|
var index_exports = {};
|
|
422
422
|
__export(index_exports, {
|
|
423
423
|
ActionWsClient: () => ActionWsClient,
|
|
424
|
+
BrowserPool: () => BrowserPool,
|
|
424
425
|
BrowserProfileClient: () => BrowserProfileClient,
|
|
425
426
|
CdpOverlayCursorRenderer: () => CdpOverlayCursorRenderer,
|
|
426
427
|
CloudCdpClient: () => CloudCdpClient,
|
|
@@ -459,6 +460,7 @@ __export(index_exports, {
|
|
|
459
460
|
cleanForFull: () => cleanForFull,
|
|
460
461
|
cleanForScrollable: () => cleanForScrollable,
|
|
461
462
|
clearCookies: () => clearCookies,
|
|
463
|
+
clearPersistentProfileSingletons: () => clearPersistentProfileSingletons,
|
|
462
464
|
cloneElementPath: () => cloneElementPath,
|
|
463
465
|
closeTab: () => closeTab,
|
|
464
466
|
cloudNotLaunchedError: () => cloudNotLaunchedError,
|
|
@@ -471,6 +473,8 @@ __export(index_exports, {
|
|
|
471
473
|
createExtractCallback: () => createExtractCallback,
|
|
472
474
|
createResolveCallback: () => createResolveCallback,
|
|
473
475
|
createTab: () => createTab,
|
|
476
|
+
detectChromePaths: () => detectChromePaths,
|
|
477
|
+
expandHome: () => expandHome,
|
|
474
478
|
exportCookies: () => exportCookies,
|
|
475
479
|
extractArrayRowsWithPaths: () => extractArrayRowsWithPaths,
|
|
476
480
|
extractArrayWithPaths: () => extractArrayWithPaths,
|
|
@@ -481,9 +485,11 @@ __export(index_exports, {
|
|
|
481
485
|
getElementText: () => getElementText,
|
|
482
486
|
getElementValue: () => getElementValue,
|
|
483
487
|
getModelProvider: () => getModelProvider,
|
|
488
|
+
getOrCreatePersistentProfile: () => getOrCreatePersistentProfile,
|
|
484
489
|
getPageHtml: () => getPageHtml,
|
|
485
490
|
getPageTitle: () => getPageTitle,
|
|
486
491
|
importCookies: () => importCookies,
|
|
492
|
+
listLocalChromeProfiles: () => listLocalChromeProfiles,
|
|
487
493
|
listTabs: () => listTabs,
|
|
488
494
|
markInteractiveElements: () => markInteractiveElements,
|
|
489
495
|
normalizeNamespace: () => normalizeNamespace,
|
|
@@ -516,11 +522,8 @@ var import_crypto = require("crypto");
|
|
|
516
522
|
|
|
517
523
|
// src/browser/pool.ts
|
|
518
524
|
var import_node_child_process = require("child_process");
|
|
519
|
-
var
|
|
520
|
-
var import_promises = require("fs/promises");
|
|
525
|
+
var import_promises2 = require("fs/promises");
|
|
521
526
|
var import_node_net = require("net");
|
|
522
|
-
var import_node_os = require("os");
|
|
523
|
-
var import_node_path = require("path");
|
|
524
527
|
var import_playwright = require("playwright");
|
|
525
528
|
|
|
526
529
|
// src/browser/cdp-proxy.ts
|
|
@@ -1013,18 +1016,282 @@ function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDa
|
|
|
1013
1016
|
}
|
|
1014
1017
|
}
|
|
1015
1018
|
|
|
1019
|
+
// src/browser/persistent-profile.ts
|
|
1020
|
+
var import_node_crypto = require("crypto");
|
|
1021
|
+
var import_node_fs = require("fs");
|
|
1022
|
+
var import_promises = require("fs/promises");
|
|
1023
|
+
var import_node_os = require("os");
|
|
1024
|
+
var import_node_path = require("path");
|
|
1025
|
+
var OPENSTEER_META_FILE = ".opensteer-meta.json";
|
|
1026
|
+
var PROCESS_STARTED_AT_MS = Math.floor(Date.now() - process.uptime() * 1e3);
|
|
1027
|
+
var PROCESS_START_TIME_TOLERANCE_MS = 1e3;
|
|
1028
|
+
var CHROME_SINGLETON_ENTRIES = /* @__PURE__ */ new Set([
|
|
1029
|
+
"SingletonCookie",
|
|
1030
|
+
"SingletonLock",
|
|
1031
|
+
"SingletonSocket",
|
|
1032
|
+
"DevToolsActivePort",
|
|
1033
|
+
"lockfile"
|
|
1034
|
+
]);
|
|
1035
|
+
var COPY_SKIP_ENTRIES = /* @__PURE__ */ new Set([
|
|
1036
|
+
...CHROME_SINGLETON_ENTRIES,
|
|
1037
|
+
OPENSTEER_META_FILE
|
|
1038
|
+
]);
|
|
1039
|
+
var SKIPPED_ROOT_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1040
|
+
"Crash Reports",
|
|
1041
|
+
"Crashpad",
|
|
1042
|
+
"BrowserMetrics",
|
|
1043
|
+
"GrShaderCache",
|
|
1044
|
+
"ShaderCache",
|
|
1045
|
+
"GraphiteDawnCache",
|
|
1046
|
+
"component_crx_cache",
|
|
1047
|
+
"Crowd Deny",
|
|
1048
|
+
"hyphen-data",
|
|
1049
|
+
"OnDeviceHeadSuggestModel",
|
|
1050
|
+
"OptimizationGuidePredictionModels",
|
|
1051
|
+
"Segmentation Platform",
|
|
1052
|
+
"SmartCardDeviceNames",
|
|
1053
|
+
"WidevineCdm",
|
|
1054
|
+
"pnacl"
|
|
1055
|
+
]);
|
|
1056
|
+
async function getOrCreatePersistentProfile(sourceUserDataDir, profileDirectory, profilesRootDir = defaultPersistentProfilesRootDir()) {
|
|
1057
|
+
const resolvedSourceUserDataDir = expandHome(sourceUserDataDir);
|
|
1058
|
+
const targetUserDataDir = (0, import_node_path.join)(
|
|
1059
|
+
expandHome(profilesRootDir),
|
|
1060
|
+
buildPersistentProfileKey(resolvedSourceUserDataDir, profileDirectory)
|
|
1061
|
+
);
|
|
1062
|
+
const sourceProfileDir = (0, import_node_path.join)(resolvedSourceUserDataDir, profileDirectory);
|
|
1063
|
+
const metadata = buildPersistentProfileMetadata(
|
|
1064
|
+
resolvedSourceUserDataDir,
|
|
1065
|
+
profileDirectory
|
|
1066
|
+
);
|
|
1067
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(targetUserDataDir), { recursive: true });
|
|
1068
|
+
await cleanOrphanedTempDirs(
|
|
1069
|
+
(0, import_node_path.dirname)(targetUserDataDir),
|
|
1070
|
+
(0, import_node_path.basename)(targetUserDataDir)
|
|
1071
|
+
);
|
|
1072
|
+
if (!(0, import_node_fs.existsSync)(sourceProfileDir)) {
|
|
1073
|
+
throw new Error(
|
|
1074
|
+
`Chrome profile "${profileDirectory}" was not found in "${resolvedSourceUserDataDir}".`
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
const created = await createPersistentProfileClone(
|
|
1078
|
+
resolvedSourceUserDataDir,
|
|
1079
|
+
sourceProfileDir,
|
|
1080
|
+
targetUserDataDir,
|
|
1081
|
+
profileDirectory,
|
|
1082
|
+
metadata
|
|
1083
|
+
);
|
|
1084
|
+
await ensurePersistentProfileMetadata(targetUserDataDir, metadata);
|
|
1085
|
+
return {
|
|
1086
|
+
created,
|
|
1087
|
+
userDataDir: targetUserDataDir
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
async function clearPersistentProfileSingletons(userDataDir) {
|
|
1091
|
+
await Promise.all(
|
|
1092
|
+
[...CHROME_SINGLETON_ENTRIES].map(
|
|
1093
|
+
(entry) => (0, import_promises.rm)((0, import_node_path.join)(userDataDir, entry), {
|
|
1094
|
+
force: true,
|
|
1095
|
+
recursive: true
|
|
1096
|
+
}).catch(() => void 0)
|
|
1097
|
+
)
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
function buildPersistentProfileKey(sourceUserDataDir, profileDirectory) {
|
|
1101
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(`${sourceUserDataDir}\0${profileDirectory}`).digest("hex").slice(0, 16);
|
|
1102
|
+
const sourceLabel = sanitizePathSegment((0, import_node_path.basename)(sourceUserDataDir) || "user-data");
|
|
1103
|
+
const profileLabel = sanitizePathSegment(profileDirectory || "Default");
|
|
1104
|
+
return `${sourceLabel}-${profileLabel}-${hash}`;
|
|
1105
|
+
}
|
|
1106
|
+
function defaultPersistentProfilesRootDir() {
|
|
1107
|
+
return (0, import_node_path.join)((0, import_node_os.homedir)(), ".opensteer", "real-browser-profiles");
|
|
1108
|
+
}
|
|
1109
|
+
function sanitizePathSegment(value) {
|
|
1110
|
+
const sanitized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-");
|
|
1111
|
+
return sanitized.replace(/^-|-$/g, "") || "profile";
|
|
1112
|
+
}
|
|
1113
|
+
function isProfileDirectory(userDataDir, entry) {
|
|
1114
|
+
return (0, import_node_fs.existsSync)((0, import_node_path.join)(userDataDir, entry, "Preferences"));
|
|
1115
|
+
}
|
|
1116
|
+
async function copyRootLevelEntries(sourceUserDataDir, targetUserDataDir, targetProfileDirectory) {
|
|
1117
|
+
let entries;
|
|
1118
|
+
try {
|
|
1119
|
+
entries = await (0, import_promises.readdir)(sourceUserDataDir);
|
|
1120
|
+
} catch {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const copyTasks = [];
|
|
1124
|
+
for (const entry of entries) {
|
|
1125
|
+
if (COPY_SKIP_ENTRIES.has(entry)) continue;
|
|
1126
|
+
if (entry === targetProfileDirectory) continue;
|
|
1127
|
+
const sourcePath = (0, import_node_path.join)(sourceUserDataDir, entry);
|
|
1128
|
+
const targetPath = (0, import_node_path.join)(targetUserDataDir, entry);
|
|
1129
|
+
if ((0, import_node_fs.existsSync)(targetPath)) continue;
|
|
1130
|
+
let entryStat;
|
|
1131
|
+
try {
|
|
1132
|
+
entryStat = await (0, import_promises.stat)(sourcePath);
|
|
1133
|
+
} catch {
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (entryStat.isFile()) {
|
|
1137
|
+
copyTasks.push((0, import_promises.copyFile)(sourcePath, targetPath).catch(() => void 0));
|
|
1138
|
+
} else if (entryStat.isDirectory()) {
|
|
1139
|
+
if (isProfileDirectory(sourceUserDataDir, entry)) continue;
|
|
1140
|
+
if (SKIPPED_ROOT_DIRECTORIES.has(entry)) continue;
|
|
1141
|
+
copyTasks.push(
|
|
1142
|
+
(0, import_promises.cp)(sourcePath, targetPath, { recursive: true }).catch(
|
|
1143
|
+
() => void 0
|
|
1144
|
+
)
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
await Promise.all(copyTasks);
|
|
1149
|
+
}
|
|
1150
|
+
async function writePersistentProfileMetadata(userDataDir, metadata) {
|
|
1151
|
+
await (0, import_promises.writeFile)(
|
|
1152
|
+
(0, import_node_path.join)(userDataDir, OPENSTEER_META_FILE),
|
|
1153
|
+
JSON.stringify(metadata, null, 2)
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
function buildPersistentProfileMetadata(sourceUserDataDir, profileDirectory) {
|
|
1157
|
+
return {
|
|
1158
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1159
|
+
profileDirectory,
|
|
1160
|
+
source: sourceUserDataDir
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
async function createPersistentProfileClone(sourceUserDataDir, sourceProfileDir, targetUserDataDir, profileDirectory, metadata) {
|
|
1164
|
+
if ((0, import_node_fs.existsSync)(targetUserDataDir)) {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
const tempUserDataDir = await (0, import_promises.mkdtemp)(
|
|
1168
|
+
buildPersistentProfileTempDirPrefix(targetUserDataDir)
|
|
1169
|
+
);
|
|
1170
|
+
let published = false;
|
|
1171
|
+
try {
|
|
1172
|
+
await (0, import_promises.cp)(sourceProfileDir, (0, import_node_path.join)(tempUserDataDir, profileDirectory), {
|
|
1173
|
+
recursive: true
|
|
1174
|
+
});
|
|
1175
|
+
await copyRootLevelEntries(
|
|
1176
|
+
sourceUserDataDir,
|
|
1177
|
+
tempUserDataDir,
|
|
1178
|
+
profileDirectory
|
|
1179
|
+
);
|
|
1180
|
+
await writePersistentProfileMetadata(tempUserDataDir, metadata);
|
|
1181
|
+
try {
|
|
1182
|
+
await (0, import_promises.rename)(tempUserDataDir, targetUserDataDir);
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
if (wasProfilePublishedByAnotherProcess(error, targetUserDataDir)) {
|
|
1185
|
+
return false;
|
|
1186
|
+
}
|
|
1187
|
+
throw error;
|
|
1188
|
+
}
|
|
1189
|
+
published = true;
|
|
1190
|
+
return true;
|
|
1191
|
+
} finally {
|
|
1192
|
+
if (!published) {
|
|
1193
|
+
await (0, import_promises.rm)(tempUserDataDir, {
|
|
1194
|
+
recursive: true,
|
|
1195
|
+
force: true
|
|
1196
|
+
}).catch(() => void 0);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
async function ensurePersistentProfileMetadata(userDataDir, metadata) {
|
|
1201
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(userDataDir, OPENSTEER_META_FILE))) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
await writePersistentProfileMetadata(userDataDir, metadata);
|
|
1205
|
+
}
|
|
1206
|
+
function wasProfilePublishedByAnotherProcess(error, targetUserDataDir) {
|
|
1207
|
+
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
1208
|
+
return (0, import_node_fs.existsSync)(targetUserDataDir) && (code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM");
|
|
1209
|
+
}
|
|
1210
|
+
function buildPersistentProfileTempDirPrefix(targetUserDataDir) {
|
|
1211
|
+
return (0, import_node_path.join)(
|
|
1212
|
+
(0, import_node_path.dirname)(targetUserDataDir),
|
|
1213
|
+
`${(0, import_node_path.basename)(targetUserDataDir)}-tmp-${process.pid}-${PROCESS_STARTED_AT_MS}-`
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
async function cleanOrphanedTempDirs(profilesDir, targetBaseName) {
|
|
1217
|
+
let entries;
|
|
1218
|
+
try {
|
|
1219
|
+
entries = await (0, import_promises.readdir)(profilesDir, {
|
|
1220
|
+
encoding: "utf8",
|
|
1221
|
+
withFileTypes: true
|
|
1222
|
+
});
|
|
1223
|
+
} catch {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const tempDirPrefix = `${targetBaseName}-tmp-`;
|
|
1227
|
+
await Promise.all(
|
|
1228
|
+
entries.map(async (entry) => {
|
|
1229
|
+
if (!entry.isDirectory() || !entry.name.startsWith(tempDirPrefix)) {
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
if (isTempDirOwnedByLiveProcess(entry.name, tempDirPrefix)) {
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
await (0, import_promises.rm)((0, import_node_path.join)(profilesDir, entry.name), {
|
|
1236
|
+
recursive: true,
|
|
1237
|
+
force: true
|
|
1238
|
+
}).catch(() => void 0);
|
|
1239
|
+
})
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
function isTempDirOwnedByLiveProcess(tempDirName, tempDirPrefix) {
|
|
1243
|
+
const owner = parseTempDirOwner(tempDirName, tempDirPrefix);
|
|
1244
|
+
if (!owner) {
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
if (owner.pid === process.pid && Math.abs(owner.processStartedAtMs - PROCESS_STARTED_AT_MS) <= PROCESS_START_TIME_TOLERANCE_MS) {
|
|
1248
|
+
return true;
|
|
1249
|
+
}
|
|
1250
|
+
return isProcessRunning(owner.pid);
|
|
1251
|
+
}
|
|
1252
|
+
function parseTempDirOwner(tempDirName, tempDirPrefix) {
|
|
1253
|
+
const remainder = tempDirName.slice(tempDirPrefix.length);
|
|
1254
|
+
const firstDashIndex = remainder.indexOf("-");
|
|
1255
|
+
const secondDashIndex = firstDashIndex === -1 ? -1 : remainder.indexOf("-", firstDashIndex + 1);
|
|
1256
|
+
if (firstDashIndex === -1 || secondDashIndex === -1) {
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
const pid = Number.parseInt(remainder.slice(0, firstDashIndex), 10);
|
|
1260
|
+
const processStartedAtMs = Number.parseInt(
|
|
1261
|
+
remainder.slice(firstDashIndex + 1, secondDashIndex),
|
|
1262
|
+
10
|
|
1263
|
+
);
|
|
1264
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
if (!Number.isInteger(processStartedAtMs) || processStartedAtMs <= 0) {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
return { pid, processStartedAtMs };
|
|
1271
|
+
}
|
|
1272
|
+
function isProcessRunning(pid) {
|
|
1273
|
+
try {
|
|
1274
|
+
process.kill(pid, 0);
|
|
1275
|
+
return true;
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
1278
|
+
return code !== "ESRCH";
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1016
1282
|
// src/browser/pool.ts
|
|
1017
1283
|
var BrowserPool = class {
|
|
1018
1284
|
browser = null;
|
|
1019
1285
|
cdpProxy = null;
|
|
1020
1286
|
launchedProcess = null;
|
|
1021
|
-
|
|
1287
|
+
managedUserDataDir = null;
|
|
1288
|
+
persistentProfile = false;
|
|
1022
1289
|
defaults;
|
|
1023
1290
|
constructor(defaults = {}) {
|
|
1024
1291
|
this.defaults = defaults;
|
|
1025
1292
|
}
|
|
1026
1293
|
async launch(options = {}) {
|
|
1027
|
-
if (this.browser || this.cdpProxy || this.launchedProcess || this.
|
|
1294
|
+
if (this.browser || this.cdpProxy || this.launchedProcess || this.managedUserDataDir) {
|
|
1028
1295
|
await this.close();
|
|
1029
1296
|
}
|
|
1030
1297
|
const mode = options.mode ?? this.defaults.mode ?? "chromium";
|
|
@@ -1074,11 +1341,13 @@ var BrowserPool = class {
|
|
|
1074
1341
|
const browser = this.browser;
|
|
1075
1342
|
const cdpProxy = this.cdpProxy;
|
|
1076
1343
|
const launchedProcess = this.launchedProcess;
|
|
1077
|
-
const
|
|
1344
|
+
const managedUserDataDir = this.managedUserDataDir;
|
|
1345
|
+
const persistentProfile = this.persistentProfile;
|
|
1078
1346
|
this.browser = null;
|
|
1079
1347
|
this.cdpProxy = null;
|
|
1080
1348
|
this.launchedProcess = null;
|
|
1081
|
-
this.
|
|
1349
|
+
this.managedUserDataDir = null;
|
|
1350
|
+
this.persistentProfile = false;
|
|
1082
1351
|
try {
|
|
1083
1352
|
if (browser) {
|
|
1084
1353
|
await browser.close().catch(() => void 0);
|
|
@@ -1086,8 +1355,8 @@ var BrowserPool = class {
|
|
|
1086
1355
|
} finally {
|
|
1087
1356
|
cdpProxy?.close();
|
|
1088
1357
|
await killProcessTree(launchedProcess);
|
|
1089
|
-
if (
|
|
1090
|
-
await (0,
|
|
1358
|
+
if (managedUserDataDir && !persistentProfile) {
|
|
1359
|
+
await (0, import_promises2.rm)(managedUserDataDir, {
|
|
1091
1360
|
recursive: true,
|
|
1092
1361
|
force: true
|
|
1093
1362
|
}).catch(() => void 0);
|
|
@@ -1136,10 +1405,11 @@ var BrowserPool = class {
|
|
|
1136
1405
|
options.userDataDir ?? chromePaths.defaultUserDataDir
|
|
1137
1406
|
);
|
|
1138
1407
|
const profileDirectory = options.profileDirectory ?? "Default";
|
|
1139
|
-
const
|
|
1408
|
+
const persistentProfile = await getOrCreatePersistentProfile(
|
|
1140
1409
|
sourceUserDataDir,
|
|
1141
1410
|
profileDirectory
|
|
1142
1411
|
);
|
|
1412
|
+
await clearPersistentProfileSingletons(persistentProfile.userDataDir);
|
|
1143
1413
|
const debugPort = await reserveDebugPort();
|
|
1144
1414
|
const headless = resolveLaunchHeadless(
|
|
1145
1415
|
"real",
|
|
@@ -1147,7 +1417,7 @@ var BrowserPool = class {
|
|
|
1147
1417
|
this.defaults.headless
|
|
1148
1418
|
);
|
|
1149
1419
|
const launchArgs = buildRealBrowserLaunchArgs({
|
|
1150
|
-
userDataDir:
|
|
1420
|
+
userDataDir: persistentProfile.userDataDir,
|
|
1151
1421
|
profileDirectory,
|
|
1152
1422
|
debugPort,
|
|
1153
1423
|
headless
|
|
@@ -1167,24 +1437,22 @@ var BrowserPool = class {
|
|
|
1167
1437
|
timeout: options.timeout ?? 3e4
|
|
1168
1438
|
});
|
|
1169
1439
|
const { context, page } = await createOwnedBrowserContextAndPage(
|
|
1170
|
-
browser
|
|
1171
|
-
{
|
|
1172
|
-
headless,
|
|
1173
|
-
initialUrl: options.initialUrl,
|
|
1174
|
-
timeoutMs: options.timeout ?? 3e4
|
|
1175
|
-
}
|
|
1440
|
+
browser
|
|
1176
1441
|
);
|
|
1442
|
+
if (options.initialUrl) {
|
|
1443
|
+
await page.goto(options.initialUrl, {
|
|
1444
|
+
waitUntil: "domcontentloaded",
|
|
1445
|
+
timeout: options.timeout ?? 3e4
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1177
1448
|
this.browser = browser;
|
|
1178
1449
|
this.launchedProcess = processHandle;
|
|
1179
|
-
this.
|
|
1450
|
+
this.managedUserDataDir = persistentProfile.userDataDir;
|
|
1451
|
+
this.persistentProfile = true;
|
|
1180
1452
|
return { browser, context, page, isExternal: false };
|
|
1181
1453
|
} catch (error) {
|
|
1182
1454
|
await browser?.close().catch(() => void 0);
|
|
1183
1455
|
await killProcessTree(processHandle);
|
|
1184
|
-
await (0, import_promises.rm)(tempUserDataDir, {
|
|
1185
|
-
recursive: true,
|
|
1186
|
-
force: true
|
|
1187
|
-
}).catch(() => void 0);
|
|
1188
1456
|
throw error;
|
|
1189
1457
|
}
|
|
1190
1458
|
}
|
|
@@ -1207,8 +1475,7 @@ var BrowserPool = class {
|
|
|
1207
1475
|
};
|
|
1208
1476
|
async function pickBrowserContextAndPage(browser) {
|
|
1209
1477
|
const context = getPrimaryBrowserContext(browser);
|
|
1210
|
-
const
|
|
1211
|
-
const page = pages.find((candidate) => isInspectablePageUrl2(candidate.url())) || pages[0] || await context.newPage();
|
|
1478
|
+
const page = await getAttachedPageOrCreate(context);
|
|
1212
1479
|
return { context, page };
|
|
1213
1480
|
}
|
|
1214
1481
|
function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
|
|
@@ -1220,77 +1487,31 @@ function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
|
|
|
1220
1487
|
}
|
|
1221
1488
|
return mode === "real";
|
|
1222
1489
|
}
|
|
1223
|
-
async function createOwnedBrowserContextAndPage(browser
|
|
1490
|
+
async function createOwnedBrowserContextAndPage(browser) {
|
|
1224
1491
|
const context = getPrimaryBrowserContext(browser);
|
|
1225
|
-
const page = await
|
|
1492
|
+
const page = await getExistingPageOrCreate(context);
|
|
1226
1493
|
return { context, page };
|
|
1227
1494
|
}
|
|
1228
|
-
async function
|
|
1229
|
-
const
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
newWindow: !options.headless
|
|
1236
|
-
});
|
|
1237
|
-
await browserSession.send("Target.activateTarget", { targetId }).catch(() => void 0);
|
|
1238
|
-
const page = await waitForOwnedBrowserPage(context, {
|
|
1239
|
-
existingPages,
|
|
1240
|
-
targetUrl,
|
|
1241
|
-
timeoutMs: options.timeoutMs
|
|
1242
|
-
});
|
|
1243
|
-
if (targetUrl !== "about:blank") {
|
|
1244
|
-
await page.waitForLoadState("domcontentloaded", {
|
|
1245
|
-
timeout: options.timeoutMs
|
|
1246
|
-
});
|
|
1247
|
-
}
|
|
1248
|
-
await closeDisposableStartupTargets(browserSession, targetId);
|
|
1249
|
-
return page;
|
|
1250
|
-
} finally {
|
|
1251
|
-
await browserSession.detach().catch(() => void 0);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
async function closeDisposableStartupTargets(browserSession, preservedTargetId) {
|
|
1255
|
-
const response = await browserSession.send("Target.getTargets").catch(() => null);
|
|
1256
|
-
if (!response) {
|
|
1257
|
-
return;
|
|
1495
|
+
async function getAttachedPageOrCreate(context) {
|
|
1496
|
+
const pages = context.pages();
|
|
1497
|
+
const inspectablePage = pages.find(
|
|
1498
|
+
(candidate) => isInspectablePageUrl2(safePageUrl(candidate))
|
|
1499
|
+
);
|
|
1500
|
+
if (inspectablePage) {
|
|
1501
|
+
return inspectablePage;
|
|
1258
1502
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
await browserSession.send("Target.closeTarget", { targetId: targetInfo.targetId }).catch(() => void 0);
|
|
1503
|
+
const attachedPage = pages[0];
|
|
1504
|
+
if (attachedPage) {
|
|
1505
|
+
return attachedPage;
|
|
1264
1506
|
}
|
|
1507
|
+
return await context.newPage();
|
|
1265
1508
|
}
|
|
1266
|
-
async function
|
|
1267
|
-
const
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
for (const candidate of context.pages()) {
|
|
1271
|
-
if (options.existingPages.has(candidate)) {
|
|
1272
|
-
continue;
|
|
1273
|
-
}
|
|
1274
|
-
const url = candidate.url();
|
|
1275
|
-
if (!isInspectablePageUrl2(url)) {
|
|
1276
|
-
continue;
|
|
1277
|
-
}
|
|
1278
|
-
fallbackPage ??= candidate;
|
|
1279
|
-
if (options.targetUrl === "about:blank") {
|
|
1280
|
-
return candidate;
|
|
1281
|
-
}
|
|
1282
|
-
if (pageLooselyMatchesUrl(url, options.targetUrl)) {
|
|
1283
|
-
return candidate;
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
await sleep(100);
|
|
1287
|
-
}
|
|
1288
|
-
if (fallbackPage) {
|
|
1289
|
-
return fallbackPage;
|
|
1509
|
+
async function getExistingPageOrCreate(context) {
|
|
1510
|
+
const existingPage = context.pages()[0];
|
|
1511
|
+
if (existingPage) {
|
|
1512
|
+
return existingPage;
|
|
1290
1513
|
}
|
|
1291
|
-
|
|
1292
|
-
`Chrome created a target for ${options.targetUrl}, but Playwright did not expose the page in time.`
|
|
1293
|
-
);
|
|
1514
|
+
return await context.newPage();
|
|
1294
1515
|
}
|
|
1295
1516
|
function getPrimaryBrowserContext(browser) {
|
|
1296
1517
|
const contexts = browser.contexts();
|
|
@@ -1304,19 +1525,11 @@ function getPrimaryBrowserContext(browser) {
|
|
|
1304
1525
|
function isInspectablePageUrl2(url) {
|
|
1305
1526
|
return url === "about:blank" || url.startsWith("http://") || url.startsWith("https://");
|
|
1306
1527
|
}
|
|
1307
|
-
function
|
|
1308
|
-
return url === "about:blank" || url === "chrome://newtab/" || url === "chrome://new-tab-page/";
|
|
1309
|
-
}
|
|
1310
|
-
function pageLooselyMatchesUrl(currentUrl, initialUrl) {
|
|
1528
|
+
function safePageUrl(page) {
|
|
1311
1529
|
try {
|
|
1312
|
-
|
|
1313
|
-
const requested = new URL(initialUrl);
|
|
1314
|
-
if (current.href === requested.href) {
|
|
1315
|
-
return true;
|
|
1316
|
-
}
|
|
1317
|
-
return current.hostname === requested.hostname && current.pathname === requested.pathname;
|
|
1530
|
+
return page.url();
|
|
1318
1531
|
} catch {
|
|
1319
|
-
return
|
|
1532
|
+
return "";
|
|
1320
1533
|
}
|
|
1321
1534
|
}
|
|
1322
1535
|
function normalizeDiscoveryUrl(cdpUrl) {
|
|
@@ -1396,38 +1609,12 @@ async function reserveDebugPort() {
|
|
|
1396
1609
|
});
|
|
1397
1610
|
});
|
|
1398
1611
|
}
|
|
1399
|
-
async function cloneProfileToTempDir(userDataDir, profileDirectory) {
|
|
1400
|
-
const resolvedUserDataDir = expandHome(userDataDir);
|
|
1401
|
-
const tempUserDataDir = await (0, import_promises.mkdtemp)(
|
|
1402
|
-
(0, import_node_path.join)((0, import_node_os.tmpdir)(), "opensteer-real-browser-")
|
|
1403
|
-
);
|
|
1404
|
-
const sourceProfileDir = (0, import_node_path.join)(resolvedUserDataDir, profileDirectory);
|
|
1405
|
-
const targetProfileDir = (0, import_node_path.join)(tempUserDataDir, profileDirectory);
|
|
1406
|
-
if ((0, import_node_fs.existsSync)(sourceProfileDir)) {
|
|
1407
|
-
await (0, import_promises.cp)(sourceProfileDir, targetProfileDir, {
|
|
1408
|
-
recursive: true
|
|
1409
|
-
});
|
|
1410
|
-
} else {
|
|
1411
|
-
await (0, import_promises.mkdir)(targetProfileDir, {
|
|
1412
|
-
recursive: true
|
|
1413
|
-
});
|
|
1414
|
-
}
|
|
1415
|
-
const localStatePath = (0, import_node_path.join)(resolvedUserDataDir, "Local State");
|
|
1416
|
-
if ((0, import_node_fs.existsSync)(localStatePath)) {
|
|
1417
|
-
await (0, import_promises.copyFile)(localStatePath, (0, import_node_path.join)(tempUserDataDir, "Local State"));
|
|
1418
|
-
}
|
|
1419
|
-
return tempUserDataDir;
|
|
1420
|
-
}
|
|
1421
1612
|
function buildRealBrowserLaunchArgs(options) {
|
|
1422
1613
|
const args = [
|
|
1423
1614
|
`--user-data-dir=${options.userDataDir}`,
|
|
1424
1615
|
`--profile-directory=${options.profileDirectory}`,
|
|
1425
1616
|
`--remote-debugging-port=${options.debugPort}`,
|
|
1426
|
-
"--
|
|
1427
|
-
"--no-default-browser-check",
|
|
1428
|
-
"--disable-background-networking",
|
|
1429
|
-
"--disable-sync",
|
|
1430
|
-
"--disable-popup-blocking"
|
|
1617
|
+
"--disable-blink-features=AutomationControlled"
|
|
1431
1618
|
];
|
|
1432
1619
|
if (options.headless) {
|
|
1433
1620
|
args.push("--headless=new");
|
|
@@ -7147,7 +7334,7 @@ async function closeTab(context, activePage, index) {
|
|
|
7147
7334
|
}
|
|
7148
7335
|
|
|
7149
7336
|
// src/actions/cookies.ts
|
|
7150
|
-
var
|
|
7337
|
+
var import_promises3 = require("fs/promises");
|
|
7151
7338
|
async function getCookies(context, url) {
|
|
7152
7339
|
return context.cookies(url ? [url] : void 0);
|
|
7153
7340
|
}
|
|
@@ -7159,10 +7346,10 @@ async function clearCookies(context) {
|
|
|
7159
7346
|
}
|
|
7160
7347
|
async function exportCookies(context, filePath, url) {
|
|
7161
7348
|
const cookies = await context.cookies(url ? [url] : void 0);
|
|
7162
|
-
await (0,
|
|
7349
|
+
await (0, import_promises3.writeFile)(filePath, JSON.stringify(cookies, null, 2), "utf-8");
|
|
7163
7350
|
}
|
|
7164
7351
|
async function importCookies(context, filePath) {
|
|
7165
|
-
const raw = await (0,
|
|
7352
|
+
const raw = await (0, import_promises3.readFile)(filePath, "utf-8");
|
|
7166
7353
|
const cookies = JSON.parse(raw);
|
|
7167
7354
|
await context.addCookies(cookies);
|
|
7168
7355
|
}
|
|
@@ -7913,31 +8100,31 @@ function buildVariantDescriptorFromCluster(descriptors) {
|
|
|
7913
8100
|
const keyStats = /* @__PURE__ */ new Map();
|
|
7914
8101
|
for (const descriptor of descriptors) {
|
|
7915
8102
|
for (const field of descriptor.fields) {
|
|
7916
|
-
const
|
|
8103
|
+
const stat2 = keyStats.get(field.path) || {
|
|
7917
8104
|
indices: /* @__PURE__ */ new Set(),
|
|
7918
8105
|
pathNodes: [],
|
|
7919
8106
|
attributes: [],
|
|
7920
8107
|
sources: []
|
|
7921
8108
|
};
|
|
7922
|
-
|
|
8109
|
+
stat2.indices.add(descriptor.index);
|
|
7923
8110
|
if (isPersistedValueNode(field.node)) {
|
|
7924
|
-
|
|
7925
|
-
|
|
8111
|
+
stat2.pathNodes.push(field.node.$path);
|
|
8112
|
+
stat2.attributes.push(field.node.attribute);
|
|
7926
8113
|
} else if (isPersistedSourceNode(field.node)) {
|
|
7927
|
-
|
|
8114
|
+
stat2.sources.push("current_url");
|
|
7928
8115
|
}
|
|
7929
|
-
keyStats.set(field.path,
|
|
8116
|
+
keyStats.set(field.path, stat2);
|
|
7930
8117
|
}
|
|
7931
8118
|
}
|
|
7932
8119
|
const mergedFields = [];
|
|
7933
|
-
for (const [fieldPath,
|
|
7934
|
-
if (
|
|
7935
|
-
if (
|
|
8120
|
+
for (const [fieldPath, stat2] of keyStats) {
|
|
8121
|
+
if (stat2.indices.size < threshold) continue;
|
|
8122
|
+
if (stat2.pathNodes.length >= threshold) {
|
|
7936
8123
|
let mergedFieldPath = null;
|
|
7937
|
-
if (
|
|
7938
|
-
mergedFieldPath = sanitizeElementPath(
|
|
8124
|
+
if (stat2.pathNodes.length === 1) {
|
|
8125
|
+
mergedFieldPath = sanitizeElementPath(stat2.pathNodes[0]);
|
|
7939
8126
|
} else {
|
|
7940
|
-
mergedFieldPath = mergeElementPathsByMajority(
|
|
8127
|
+
mergedFieldPath = mergeElementPathsByMajority(stat2.pathNodes);
|
|
7941
8128
|
}
|
|
7942
8129
|
if (!mergedFieldPath) continue;
|
|
7943
8130
|
if (clusterSize === 1) {
|
|
@@ -7950,17 +8137,17 @@ function buildVariantDescriptorFromCluster(descriptors) {
|
|
|
7950
8137
|
mergedFieldPath,
|
|
7951
8138
|
"field"
|
|
7952
8139
|
);
|
|
7953
|
-
const attrThreshold =
|
|
8140
|
+
const attrThreshold = stat2.pathNodes.length === 1 ? 1 : majorityThreshold(stat2.pathNodes.length);
|
|
7954
8141
|
mergedFields.push({
|
|
7955
8142
|
path: fieldPath,
|
|
7956
8143
|
node: createValueNode({
|
|
7957
8144
|
elementPath: mergedFieldPath,
|
|
7958
|
-
attribute: pickModeString(
|
|
8145
|
+
attribute: pickModeString(stat2.attributes, attrThreshold)
|
|
7959
8146
|
})
|
|
7960
8147
|
});
|
|
7961
8148
|
continue;
|
|
7962
8149
|
}
|
|
7963
|
-
const dominantSource = pickModeString(
|
|
8150
|
+
const dominantSource = pickModeString(stat2.sources, threshold);
|
|
7964
8151
|
if (dominantSource === "current_url") {
|
|
7965
8152
|
mergedFields.push({
|
|
7966
8153
|
path: fieldPath,
|
|
@@ -9161,7 +9348,7 @@ function selectPreferredContextPage(browser, contexts) {
|
|
|
9161
9348
|
let aboutBlankCandidate = null;
|
|
9162
9349
|
for (const context of contexts) {
|
|
9163
9350
|
for (const page of context.pages()) {
|
|
9164
|
-
const url =
|
|
9351
|
+
const url = safePageUrl2(page);
|
|
9165
9352
|
if (!isInternalOrEmptyUrl(url)) {
|
|
9166
9353
|
return { browser, context, page };
|
|
9167
9354
|
}
|
|
@@ -9172,7 +9359,7 @@ function selectPreferredContextPage(browser, contexts) {
|
|
|
9172
9359
|
}
|
|
9173
9360
|
return aboutBlankCandidate;
|
|
9174
9361
|
}
|
|
9175
|
-
function
|
|
9362
|
+
function safePageUrl2(page) {
|
|
9176
9363
|
try {
|
|
9177
9364
|
return page.url();
|
|
9178
9365
|
} catch {
|
|
@@ -14846,6 +15033,7 @@ function sleep7(ms) {
|
|
|
14846
15033
|
// Annotate the CommonJS export names for ESM import in node:
|
|
14847
15034
|
0 && (module.exports = {
|
|
14848
15035
|
ActionWsClient,
|
|
15036
|
+
BrowserPool,
|
|
14849
15037
|
BrowserProfileClient,
|
|
14850
15038
|
CdpOverlayCursorRenderer,
|
|
14851
15039
|
CloudCdpClient,
|
|
@@ -14884,6 +15072,7 @@ function sleep7(ms) {
|
|
|
14884
15072
|
cleanForFull,
|
|
14885
15073
|
cleanForScrollable,
|
|
14886
15074
|
clearCookies,
|
|
15075
|
+
clearPersistentProfileSingletons,
|
|
14887
15076
|
cloneElementPath,
|
|
14888
15077
|
closeTab,
|
|
14889
15078
|
cloudNotLaunchedError,
|
|
@@ -14896,6 +15085,8 @@ function sleep7(ms) {
|
|
|
14896
15085
|
createExtractCallback,
|
|
14897
15086
|
createResolveCallback,
|
|
14898
15087
|
createTab,
|
|
15088
|
+
detectChromePaths,
|
|
15089
|
+
expandHome,
|
|
14899
15090
|
exportCookies,
|
|
14900
15091
|
extractArrayRowsWithPaths,
|
|
14901
15092
|
extractArrayWithPaths,
|
|
@@ -14906,9 +15097,11 @@ function sleep7(ms) {
|
|
|
14906
15097
|
getElementText,
|
|
14907
15098
|
getElementValue,
|
|
14908
15099
|
getModelProvider,
|
|
15100
|
+
getOrCreatePersistentProfile,
|
|
14909
15101
|
getPageHtml,
|
|
14910
15102
|
getPageTitle,
|
|
14911
15103
|
importCookies,
|
|
15104
|
+
listLocalChromeProfiles,
|
|
14912
15105
|
listTabs,
|
|
14913
15106
|
markInteractiveElements,
|
|
14914
15107
|
normalizeNamespace,
|