opensteer 0.9.6 → 0.9.8
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/README.md +2 -2
- package/dist/{chunk-3I3A5OLB.js → chunk-BPGXP3RF.js} +257 -24
- package/dist/chunk-BPGXP3RF.js.map +1 -0
- package/dist/{chunk-3XBQRZZC.js → chunk-EXXRLPLI.js} +158 -46
- package/dist/chunk-EXXRLPLI.js.map +1 -0
- package/dist/{chunk-T5P2QGZ3.js → chunk-GKYBP3KD.js} +154 -13
- package/dist/chunk-GKYBP3KD.js.map +1 -0
- package/dist/{chunk-BVRIPCWA.js → chunk-LFWP5RXF.js} +500 -513
- package/dist/chunk-LFWP5RXF.js.map +1 -0
- package/dist/{chunk-L4NF74KI.js → chunk-SOJEWKSW.js} +5 -5
- package/dist/{chunk-L4NF74KI.js.map → chunk-SOJEWKSW.js.map} +1 -1
- package/dist/cli/bin.cjs +1230 -660
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +166 -72
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +793 -565
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -51
- package/dist/index.d.ts +36 -51
- package/dist/index.js +4 -4
- package/dist/local-view/public/assets/app.js +10 -1
- package/dist/local-view/serve-entry.cjs +1022 -591
- package/dist/local-view/serve-entry.cjs.map +1 -1
- package/dist/local-view/serve-entry.js +2 -2
- package/dist/opensteer-XLPY343Y.js +6 -0
- package/dist/{opensteer-UGA6YBRN.js.map → opensteer-XLPY343Y.js.map} +1 -1
- package/dist/{session-control-U3L5H2ZI.js → session-control-FVKKD45R.js} +4 -4
- package/dist/{session-control-U3L5H2ZI.js.map → session-control-FVKKD45R.js.map} +1 -1
- package/package.json +5 -5
- package/skills/recorder/SKILL.md +2 -2
- package/dist/chunk-3I3A5OLB.js.map +0 -1
- package/dist/chunk-3XBQRZZC.js.map +0 -1
- package/dist/chunk-BVRIPCWA.js.map +0 -1
- package/dist/chunk-T5P2QGZ3.js.map +0 -1
- package/dist/opensteer-UGA6YBRN.js +0 -6
|
@@ -6,9 +6,9 @@ var crypto = require('crypto');
|
|
|
6
6
|
var path12 = require('path');
|
|
7
7
|
var url = require('url');
|
|
8
8
|
var child_process = require('child_process');
|
|
9
|
-
var util = require('util');
|
|
10
|
-
var os = require('os');
|
|
11
9
|
var fs = require('fs');
|
|
10
|
+
var os = require('os');
|
|
11
|
+
var util = require('util');
|
|
12
12
|
var module$1 = require('module');
|
|
13
13
|
var enginePlaywright = require('@opensteer/engine-playwright');
|
|
14
14
|
var http = require('http');
|
|
@@ -203,388 +203,174 @@ function isAlreadyExistsError(error) {
|
|
|
203
203
|
}
|
|
204
204
|
async function withFilesystemLock(lockPath, task) {
|
|
205
205
|
await ensureDirectory(path12__default.default.dirname(lockPath));
|
|
206
|
+
const ownerToken = crypto.randomUUID();
|
|
206
207
|
let attempt = 0;
|
|
207
208
|
while (true) {
|
|
208
209
|
try {
|
|
209
210
|
await promises.mkdir(lockPath);
|
|
211
|
+
const acquiredAt = Date.now();
|
|
212
|
+
await writeLockMetadata(lockPath, {
|
|
213
|
+
version: LOCK_METADATA_VERSION,
|
|
214
|
+
ownerToken,
|
|
215
|
+
pid: process.pid,
|
|
216
|
+
acquiredAt,
|
|
217
|
+
heartbeatAt: acquiredAt
|
|
218
|
+
});
|
|
210
219
|
break;
|
|
211
220
|
} catch (error) {
|
|
212
221
|
if (!isAlreadyExistsError(error)) {
|
|
213
222
|
throw error;
|
|
214
223
|
}
|
|
224
|
+
if (await tryRecoverFilesystemLock(lockPath)) {
|
|
225
|
+
attempt = 0;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
215
228
|
const delayMs = LOCK_RETRY_DELAYS_MS[Math.min(attempt, LOCK_RETRY_DELAYS_MS.length - 1)];
|
|
216
229
|
attempt += 1;
|
|
217
230
|
await new Promise((resolve4) => setTimeout(resolve4, delayMs));
|
|
218
231
|
}
|
|
219
232
|
}
|
|
233
|
+
const heartbeatTimer = setInterval(() => {
|
|
234
|
+
void touchLockMetadata(lockPath, ownerToken);
|
|
235
|
+
}, LOCK_HEARTBEAT_INTERVAL_MS);
|
|
236
|
+
heartbeatTimer.unref?.();
|
|
220
237
|
try {
|
|
221
238
|
return await task();
|
|
222
239
|
} finally {
|
|
223
|
-
|
|
240
|
+
clearInterval(heartbeatTimer);
|
|
241
|
+
const metadata = await readLockMetadata(lockPath);
|
|
242
|
+
if (metadata?.ownerToken === ownerToken) {
|
|
243
|
+
await promises.rm(lockPath, { recursive: true, force: true });
|
|
244
|
+
}
|
|
224
245
|
}
|
|
225
246
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
init_json();
|
|
230
|
-
LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// src/internal/filesystem.ts
|
|
235
|
-
var init_filesystem2 = __esm({
|
|
236
|
-
"src/internal/filesystem.ts"() {
|
|
237
|
-
init_filesystem();
|
|
247
|
+
async function tryRecoverFilesystemLock(lockPath) {
|
|
248
|
+
if (!await shouldRecoverFilesystemLock(lockPath)) {
|
|
249
|
+
return false;
|
|
238
250
|
}
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
return path12__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
|
|
251
|
+
await promises.rm(lockPath, { recursive: true, force: true });
|
|
252
|
+
return true;
|
|
242
253
|
}
|
|
243
|
-
async function
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
|
|
254
|
+
async function shouldRecoverFilesystemLock(lockPath) {
|
|
255
|
+
const metadata = await readLockMetadata(lockPath);
|
|
256
|
+
if (metadata !== void 0) {
|
|
257
|
+
if (isProcessRunning(metadata.pid)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return Date.now() - metadata.heartbeatAt >= LOCK_ORPHAN_GRACE_MS;
|
|
247
261
|
}
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
-
return
|
|
262
|
+
const lockStat = await promises.stat(lockPath).catch(() => void 0);
|
|
263
|
+
if (lockStat === void 0) {
|
|
264
|
+
return false;
|
|
251
265
|
}
|
|
252
|
-
return
|
|
253
|
-
}
|
|
254
|
-
async function readPersistedLocalBrowserSessionRecord(rootPath) {
|
|
255
|
-
const record = await readPersistedSessionRecord(rootPath, "local");
|
|
256
|
-
return record?.provider === "local" ? record : void 0;
|
|
257
|
-
}
|
|
258
|
-
async function writePersistedSessionRecord(rootPath, record) {
|
|
259
|
-
await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath, record.provider), record);
|
|
260
|
-
}
|
|
261
|
-
async function clearPersistedSessionRecord(rootPath, provider) {
|
|
262
|
-
await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
|
|
266
|
+
return Date.now() - lockStat.mtimeMs >= LOCK_METADATALESS_STALE_MS;
|
|
263
267
|
}
|
|
264
|
-
function
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
var init_live_session = __esm({
|
|
269
|
-
"src/live-session.ts"() {
|
|
270
|
-
init_filesystem2();
|
|
271
|
-
OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
|
|
272
|
-
OPENSTEER_LIVE_SESSION_VERSION = 1;
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
function parseProcessOwner(value) {
|
|
276
|
-
if (!value || typeof value !== "object") {
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
const parsed = value;
|
|
280
|
-
const pid = Number(parsed.pid);
|
|
281
|
-
const processStartedAtMs = Number(parsed.processStartedAtMs);
|
|
282
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
283
|
-
return null;
|
|
268
|
+
async function readLockMetadata(lockPath) {
|
|
269
|
+
const metadataPath = path12__default.default.join(lockPath, LOCK_METADATA_FILENAME);
|
|
270
|
+
if (!await pathExists(metadataPath)) {
|
|
271
|
+
return void 0;
|
|
284
272
|
}
|
|
285
|
-
|
|
286
|
-
|
|
273
|
+
try {
|
|
274
|
+
const parsed = await readJsonFile(metadataPath);
|
|
275
|
+
const pid = parsed.pid;
|
|
276
|
+
const acquiredAt = parsed.acquiredAt;
|
|
277
|
+
const heartbeatAt = parsed.heartbeatAt;
|
|
278
|
+
if (parsed.version !== LOCK_METADATA_VERSION || typeof parsed.ownerToken !== "string" || parsed.ownerToken.length === 0 || typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || typeof acquiredAt !== "number" || !Number.isFinite(acquiredAt) || typeof heartbeatAt !== "number" || !Number.isFinite(heartbeatAt)) {
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
version: LOCK_METADATA_VERSION,
|
|
283
|
+
ownerToken: parsed.ownerToken,
|
|
284
|
+
pid,
|
|
285
|
+
acquiredAt,
|
|
286
|
+
heartbeatAt
|
|
287
|
+
};
|
|
288
|
+
} catch {
|
|
289
|
+
return void 0;
|
|
287
290
|
}
|
|
288
|
-
return {
|
|
289
|
-
pid,
|
|
290
|
-
processStartedAtMs
|
|
291
|
-
};
|
|
292
291
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
292
|
+
async function writeLockMetadata(lockPath, metadata) {
|
|
293
|
+
try {
|
|
294
|
+
await writeJsonFileAtomic(path12__default.default.join(lockPath, LOCK_METADATA_FILENAME), metadata);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
await promises.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
|
|
297
|
+
throw error;
|
|
296
298
|
}
|
|
297
|
-
return left.pid === right.pid && left.processStartedAtMs === right.processStartedAtMs;
|
|
298
299
|
}
|
|
299
|
-
async function
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const startedAtMs = await readProcessStartedAtMs(owner.pid);
|
|
304
|
-
if (typeof startedAtMs === "number") {
|
|
305
|
-
return hasMatchingProcessStartTime(owner.processStartedAtMs, startedAtMs) ? "live" : "dead";
|
|
300
|
+
async function touchLockMetadata(lockPath, ownerToken) {
|
|
301
|
+
const metadata = await readLockMetadata(lockPath);
|
|
302
|
+
if (metadata === void 0 || metadata.ownerToken !== ownerToken) {
|
|
303
|
+
return;
|
|
306
304
|
}
|
|
307
|
-
|
|
305
|
+
await writeJsonFileAtomic(path12__default.default.join(lockPath, LOCK_METADATA_FILENAME), {
|
|
306
|
+
...metadata,
|
|
307
|
+
heartbeatAt: Date.now()
|
|
308
|
+
}).catch(() => void 0);
|
|
308
309
|
}
|
|
309
310
|
function isProcessRunning(pid) {
|
|
311
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
310
314
|
try {
|
|
311
315
|
process.kill(pid, 0);
|
|
312
316
|
return true;
|
|
313
317
|
} catch (error) {
|
|
314
|
-
|
|
315
|
-
return code !== "ESRCH";
|
|
318
|
+
return error?.code === "EPERM";
|
|
316
319
|
}
|
|
317
320
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (process.platform === "win32") {
|
|
329
|
-
return readWindowsProcessStartedAtMs(pid);
|
|
321
|
+
var LOCK_RETRY_DELAYS_MS, LOCK_METADATA_FILENAME, LOCK_METADATA_VERSION, LOCK_HEARTBEAT_INTERVAL_MS, LOCK_ORPHAN_GRACE_MS, LOCK_METADATALESS_STALE_MS;
|
|
322
|
+
var init_filesystem = __esm({
|
|
323
|
+
"../runtime-core/src/internal/filesystem.ts"() {
|
|
324
|
+
init_json();
|
|
325
|
+
LOCK_RETRY_DELAYS_MS = [1, 2, 5, 10, 20, 50];
|
|
326
|
+
LOCK_METADATA_FILENAME = "owner.json";
|
|
327
|
+
LOCK_METADATA_VERSION = 1;
|
|
328
|
+
LOCK_HEARTBEAT_INTERVAL_MS = 1e3;
|
|
329
|
+
LOCK_ORPHAN_GRACE_MS = 2e3;
|
|
330
|
+
LOCK_METADATALESS_STALE_MS = 3e4;
|
|
330
331
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
} catch {
|
|
338
|
-
return null;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// src/internal/filesystem.ts
|
|
335
|
+
var init_filesystem2 = __esm({
|
|
336
|
+
"src/internal/filesystem.ts"() {
|
|
337
|
+
init_filesystem();
|
|
339
338
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
});
|
|
340
|
+
function resolveBrandPlatformConfig(brand, platform = process.platform) {
|
|
341
|
+
if (platform === "darwin") {
|
|
342
|
+
return brand.darwin;
|
|
343
343
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
readLinuxClockTicksPerSecond()
|
|
347
|
-
]);
|
|
348
|
-
if (bootTimeMs === null || clockTicksPerSecond === null) {
|
|
349
|
-
return null;
|
|
344
|
+
if (platform === "win32") {
|
|
345
|
+
return brand.win32;
|
|
350
346
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
function parseLinuxProcessStartTicks(statRaw) {
|
|
354
|
-
const closingParenIndex = statRaw.lastIndexOf(")");
|
|
355
|
-
if (closingParenIndex === -1) {
|
|
356
|
-
return null;
|
|
347
|
+
if (platform === "linux") {
|
|
348
|
+
return brand.linux;
|
|
357
349
|
}
|
|
358
|
-
|
|
359
|
-
const startTicks = Number(fields[LINUX_STAT_START_TIME_FIELD_INDEX]);
|
|
360
|
-
return Number.isFinite(startTicks) && startTicks >= 0 ? startTicks : null;
|
|
350
|
+
return void 0;
|
|
361
351
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
if (!
|
|
367
|
-
|
|
352
|
+
function detectInstalledBrowserBrands() {
|
|
353
|
+
const installations = [];
|
|
354
|
+
for (const brand of BROWSER_BRANDS) {
|
|
355
|
+
const platformConfig = resolveBrandPlatformConfig(brand);
|
|
356
|
+
if (!platformConfig) {
|
|
357
|
+
continue;
|
|
368
358
|
}
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
} catch {
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
async function readLinuxClockTicksPerSecond() {
|
|
376
|
-
if (!linuxClockTicksPerSecondPromise) {
|
|
377
|
-
linuxClockTicksPerSecondPromise = execFileAsync("getconf", ["CLK_TCK"], {
|
|
378
|
-
encoding: "utf8",
|
|
379
|
-
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
|
|
380
|
-
}).then(({ stdout }) => {
|
|
381
|
-
const value = Number.parseInt(stdout.trim(), 10);
|
|
382
|
-
return Number.isFinite(value) && value > 0 ? value : null;
|
|
383
|
-
}).catch(() => null);
|
|
384
|
-
}
|
|
385
|
-
return linuxClockTicksPerSecondPromise;
|
|
386
|
-
}
|
|
387
|
-
async function readWindowsProcessStartedAtMs(pid) {
|
|
388
|
-
try {
|
|
389
|
-
const { stdout } = await execFileAsync(
|
|
390
|
-
"powershell.exe",
|
|
391
|
-
[
|
|
392
|
-
"-NoProfile",
|
|
393
|
-
"-Command",
|
|
394
|
-
`(Get-Process -Id ${String(pid)}).StartTime.ToUniversalTime().ToString("o")`
|
|
395
|
-
],
|
|
396
|
-
{
|
|
397
|
-
encoding: "utf8",
|
|
398
|
-
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
|
|
399
|
-
}
|
|
359
|
+
const executablePath = firstExistingPath(
|
|
360
|
+
resolveExecutableCandidates(platformConfig.executableCandidates)
|
|
400
361
|
);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
return null;
|
|
362
|
+
if (!executablePath) {
|
|
363
|
+
continue;
|
|
404
364
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
async function readPsProcessStartedAtMs(pid) {
|
|
412
|
-
try {
|
|
413
|
-
const { stdout } = await execFileAsync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
414
|
-
encoding: "utf8",
|
|
415
|
-
env: PS_COMMAND_ENV,
|
|
416
|
-
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES
|
|
365
|
+
installations.push({
|
|
366
|
+
brand,
|
|
367
|
+
brandId: brand.id,
|
|
368
|
+
displayName: brand.displayName,
|
|
369
|
+
executablePath,
|
|
370
|
+
userDataDir: path12.resolve(expandHome(platformConfig.userDataDir))
|
|
417
371
|
});
|
|
418
|
-
const startedAt = stdout.trim();
|
|
419
|
-
if (!startedAt) {
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
const startedAtMs = Date.parse(startedAt.replace(/\s+/g, " "));
|
|
423
|
-
return Number.isFinite(startedAtMs) ? startedAtMs : null;
|
|
424
|
-
} catch {
|
|
425
|
-
return null;
|
|
426
372
|
}
|
|
427
|
-
|
|
428
|
-
var execFileAsync, PROCESS_STARTED_AT_MS, PROCESS_START_TIME_TOLERANCE_MS, PROCESS_LIST_MAX_BUFFER_BYTES, PS_COMMAND_ENV, LINUX_STAT_START_TIME_FIELD_INDEX, CURRENT_PROCESS_OWNER, linuxClockTicksPerSecondPromise;
|
|
429
|
-
var init_process_owner = __esm({
|
|
430
|
-
"src/local-browser/process-owner.ts"() {
|
|
431
|
-
execFileAsync = util.promisify(child_process.execFile);
|
|
432
|
-
PROCESS_STARTED_AT_MS = Math.floor(Date.now() - process.uptime() * 1e3);
|
|
433
|
-
PROCESS_START_TIME_TOLERANCE_MS = 1e3;
|
|
434
|
-
PROCESS_LIST_MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
435
|
-
PS_COMMAND_ENV = { ...process.env, LC_ALL: "C" };
|
|
436
|
-
LINUX_STAT_START_TIME_FIELD_INDEX = 19;
|
|
437
|
-
CURRENT_PROCESS_OWNER = {
|
|
438
|
-
pid: process.pid,
|
|
439
|
-
processStartedAtMs: PROCESS_STARTED_AT_MS
|
|
440
|
-
};
|
|
441
|
-
linuxClockTicksPerSecondPromise = null;
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
function resolveOpensteerStateDir() {
|
|
445
|
-
const explicit = process.env.OPENSTEER_HOME?.trim();
|
|
446
|
-
if (explicit) {
|
|
447
|
-
return path12__default.default.resolve(explicit);
|
|
448
|
-
}
|
|
449
|
-
if (process.platform === "win32") {
|
|
450
|
-
return path12__default.default.join(
|
|
451
|
-
process.env.LOCALAPPDATA ?? path12__default.default.join(os.homedir(), "AppData", "Local"),
|
|
452
|
-
"Opensteer"
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
if (process.platform === "darwin") {
|
|
456
|
-
return path12__default.default.join(os.homedir(), "Library", "Application Support", "Opensteer");
|
|
457
|
-
}
|
|
458
|
-
return path12__default.default.join(
|
|
459
|
-
process.env.XDG_STATE_HOME ?? path12__default.default.join(os.homedir(), ".local", "state"),
|
|
460
|
-
"opensteer"
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
function resolveLocalViewRootDir() {
|
|
464
|
-
return path12__default.default.join(resolveOpensteerStateDir(), "local-view");
|
|
465
|
-
}
|
|
466
|
-
function resolveLocalViewPreferencesPath() {
|
|
467
|
-
return path12__default.default.join(resolveLocalViewRootDir(), "preferences.json");
|
|
468
|
-
}
|
|
469
|
-
function resolveLocalViewServiceDir() {
|
|
470
|
-
return path12__default.default.join(resolveLocalViewRootDir(), "service");
|
|
471
|
-
}
|
|
472
|
-
function resolveLocalViewSessionsDir() {
|
|
473
|
-
return path12__default.default.join(resolveLocalViewRootDir(), "sessions");
|
|
474
|
-
}
|
|
475
|
-
function resolveLocalViewServiceLockDir() {
|
|
476
|
-
return path12__default.default.join(resolveLocalViewServiceDir(), "startup.lock");
|
|
477
|
-
}
|
|
478
|
-
function resolveLocalViewServiceStatePath() {
|
|
479
|
-
return path12__default.default.join(resolveLocalViewServiceDir(), "state.json");
|
|
480
|
-
}
|
|
481
|
-
var init_runtime_dir = __esm({
|
|
482
|
-
"src/local-view/runtime-dir.ts"() {
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
function buildLocalViewSessionId(input) {
|
|
486
|
-
const hash = crypto.createHash("sha256").update(`${input.rootPath}
|
|
487
|
-
${String(input.pid)}
|
|
488
|
-
${String(input.startedAt)}`).digest("hex");
|
|
489
|
-
return `local_${hash.slice(0, 24)}`;
|
|
490
|
-
}
|
|
491
|
-
function createLocalViewSessionManifest(input) {
|
|
492
|
-
return {
|
|
493
|
-
layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
|
|
494
|
-
version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
|
|
495
|
-
sessionId: buildLocalViewSessionId({
|
|
496
|
-
rootPath: input.rootPath,
|
|
497
|
-
pid: input.live.pid,
|
|
498
|
-
startedAt: input.live.startedAt
|
|
499
|
-
}),
|
|
500
|
-
rootPath: input.rootPath,
|
|
501
|
-
...input.workspace === void 0 ? {} : { workspace: input.workspace },
|
|
502
|
-
engine: input.live.engine,
|
|
503
|
-
ownership: input.ownership,
|
|
504
|
-
pid: input.live.pid,
|
|
505
|
-
startedAt: input.live.startedAt,
|
|
506
|
-
updatedAt: Date.now()
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
async function writeLocalViewSessionManifest(manifest) {
|
|
510
|
-
await ensureDirectory(resolveLocalViewSessionsDir());
|
|
511
|
-
await writeJsonFileAtomic(resolveLocalViewSessionManifestPath(manifest.sessionId), manifest);
|
|
512
|
-
}
|
|
513
|
-
async function deleteLocalViewSessionManifest(sessionId) {
|
|
514
|
-
await promises.rm(resolveLocalViewSessionManifestPath(sessionId), { force: true }).catch(() => void 0);
|
|
515
|
-
}
|
|
516
|
-
async function readLocalViewSessionManifest(sessionId) {
|
|
517
|
-
const manifestPath = resolveLocalViewSessionManifestPath(sessionId);
|
|
518
|
-
if (!await pathExists(manifestPath)) {
|
|
519
|
-
return void 0;
|
|
520
|
-
}
|
|
521
|
-
const parsed = await readJsonFile(manifestPath);
|
|
522
|
-
return isPersistedLocalViewSessionManifest(parsed) ? parsed : void 0;
|
|
523
|
-
}
|
|
524
|
-
async function listLocalViewSessionManifests() {
|
|
525
|
-
const directoryPath = resolveLocalViewSessionsDir();
|
|
526
|
-
const fileNames = await listJsonFiles(directoryPath);
|
|
527
|
-
const manifests = await Promise.all(
|
|
528
|
-
fileNames.map(async (fileName) => {
|
|
529
|
-
const parsed = await readJsonFile(
|
|
530
|
-
path12__default.default.join(directoryPath, fileName)
|
|
531
|
-
).catch(() => void 0);
|
|
532
|
-
return isPersistedLocalViewSessionManifest(parsed) ? parsed : void 0;
|
|
533
|
-
})
|
|
534
|
-
);
|
|
535
|
-
return manifests.filter((manifest) => manifest !== void 0).sort(
|
|
536
|
-
(left, right) => left.startedAt - right.startedAt || left.sessionId.localeCompare(right.sessionId)
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
function resolveLocalViewSessionManifestPath(sessionId) {
|
|
540
|
-
return path12__default.default.join(resolveLocalViewSessionsDir(), `${sessionId}.json`);
|
|
541
|
-
}
|
|
542
|
-
function isPersistedLocalViewSessionManifest(value) {
|
|
543
|
-
return value?.layout === OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT && value.version === OPENSTEER_LOCAL_VIEW_SESSION_VERSION && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.rootPath === "string" && value.rootPath.length > 0 && (value.engine === "playwright" || value.engine === "abp") && (value.ownership === "owned" || value.ownership === "attached" || value.ownership === "managed") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
|
|
544
|
-
}
|
|
545
|
-
var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT, OPENSTEER_LOCAL_VIEW_SESSION_VERSION;
|
|
546
|
-
var init_session_manifest = __esm({
|
|
547
|
-
"src/local-view/session-manifest.ts"() {
|
|
548
|
-
init_filesystem2();
|
|
549
|
-
init_runtime_dir();
|
|
550
|
-
OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
|
|
551
|
-
OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
function resolveBrandPlatformConfig(brand, platform = process.platform) {
|
|
555
|
-
if (platform === "darwin") {
|
|
556
|
-
return brand.darwin;
|
|
557
|
-
}
|
|
558
|
-
if (platform === "win32") {
|
|
559
|
-
return brand.win32;
|
|
560
|
-
}
|
|
561
|
-
if (platform === "linux") {
|
|
562
|
-
return brand.linux;
|
|
563
|
-
}
|
|
564
|
-
return void 0;
|
|
565
|
-
}
|
|
566
|
-
function detectInstalledBrowserBrands() {
|
|
567
|
-
const installations = [];
|
|
568
|
-
for (const brand of BROWSER_BRANDS) {
|
|
569
|
-
const platformConfig = resolveBrandPlatformConfig(brand);
|
|
570
|
-
if (!platformConfig) {
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
const executablePath = firstExistingPath(
|
|
574
|
-
resolveExecutableCandidates(platformConfig.executableCandidates)
|
|
575
|
-
);
|
|
576
|
-
if (!executablePath) {
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
installations.push({
|
|
580
|
-
brand,
|
|
581
|
-
brandId: brand.id,
|
|
582
|
-
displayName: brand.displayName,
|
|
583
|
-
executablePath,
|
|
584
|
-
userDataDir: path12.resolve(expandHome(platformConfig.userDataDir))
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
return installations;
|
|
373
|
+
return installations;
|
|
588
374
|
}
|
|
589
375
|
function resolveExecutableCandidates(candidates) {
|
|
590
376
|
return candidates.map((candidate) => candidate ? path12.resolve(expandHome(candidate)) : null);
|
|
@@ -1033,116 +819,473 @@ async function probeCdpEndpoint(input) {
|
|
|
1033
819
|
httpUrl: target.httpUrl
|
|
1034
820
|
});
|
|
1035
821
|
}
|
|
1036
|
-
if (target.fallbackBrowserWebSocketUrl !== void 0 && await isHttpEndpointReachable(target.httpUrl, timeoutMs)) {
|
|
1037
|
-
return createInspectedCdpEndpoint({
|
|
1038
|
-
browserWebSocketUrl: target.fallbackBrowserWebSocketUrl,
|
|
1039
|
-
httpUrl: target.httpUrl
|
|
822
|
+
if (target.fallbackBrowserWebSocketUrl !== void 0 && await isHttpEndpointReachable(target.httpUrl, timeoutMs)) {
|
|
823
|
+
return createInspectedCdpEndpoint({
|
|
824
|
+
browserWebSocketUrl: target.fallbackBrowserWebSocketUrl,
|
|
825
|
+
httpUrl: target.httpUrl
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
function dedupeAndSortCandidates(candidates) {
|
|
831
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
832
|
+
for (const candidate of [...candidates].sort(compareCandidates)) {
|
|
833
|
+
const existing = deduped.get(candidate.endpoint);
|
|
834
|
+
if (!existing || compareCandidates(candidate, existing) < 0) {
|
|
835
|
+
deduped.set(candidate.endpoint, candidate);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return [...deduped.values()].sort(compareCandidates);
|
|
839
|
+
}
|
|
840
|
+
function compareCandidates(left, right) {
|
|
841
|
+
return getAttachCandidatePriority(right) - getAttachCandidatePriority(left) || left.endpoint.localeCompare(right.endpoint) || (left.userDataDir ?? "").localeCompare(right.userDataDir ?? "");
|
|
842
|
+
}
|
|
843
|
+
function getAttachCandidatePriority(candidate) {
|
|
844
|
+
return candidate.source === "devtools-active-port" ? 2 : 1;
|
|
845
|
+
}
|
|
846
|
+
function createInspectedCdpEndpoint(input) {
|
|
847
|
+
const port = readPort(input.httpUrl);
|
|
848
|
+
return {
|
|
849
|
+
endpoint: input.browserWebSocketUrl,
|
|
850
|
+
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
851
|
+
...input.protocolVersion === void 0 ? {} : { protocolVersion: input.protocolVersion },
|
|
852
|
+
httpUrl: input.httpUrl.toString(),
|
|
853
|
+
...port === void 0 ? {} : { port }
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
function normalizeProbeTarget(endpoint) {
|
|
857
|
+
if (/^\d+$/.test(endpoint)) {
|
|
858
|
+
return {
|
|
859
|
+
httpUrl: new URL(`http://127.0.0.1:${endpoint}`)
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
if (endpoint.startsWith("ws://") || endpoint.startsWith("wss://")) {
|
|
863
|
+
const url = new URL(endpoint);
|
|
864
|
+
return {
|
|
865
|
+
fallbackBrowserWebSocketUrl: url.toString(),
|
|
866
|
+
httpUrl: new URL(`${url.protocol === "wss:" ? "https:" : "http:"}//${url.host}`)
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
try {
|
|
870
|
+
const url = endpoint.startsWith("http://") || endpoint.startsWith("https://") ? new URL(endpoint) : new URL(`http://${endpoint}`);
|
|
871
|
+
return {
|
|
872
|
+
httpUrl: new URL(`${url.protocol}//${url.host}`)
|
|
873
|
+
};
|
|
874
|
+
} catch {
|
|
875
|
+
throw new Error(`Invalid CDP endpoint "${endpoint}".`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async function fetchJson(url, headers, timeoutMs) {
|
|
879
|
+
const response = await fetch(url, {
|
|
880
|
+
...headers === void 0 ? {} : { headers },
|
|
881
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
882
|
+
}).catch(() => null);
|
|
883
|
+
if (!response?.ok) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
return await response.json();
|
|
887
|
+
}
|
|
888
|
+
async function isHttpEndpointReachable(url, timeoutMs) {
|
|
889
|
+
const response = await fetch(url, {
|
|
890
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
891
|
+
}).catch(() => null);
|
|
892
|
+
return response !== null;
|
|
893
|
+
}
|
|
894
|
+
function buildBrowserWebSocketUrl(httpUrl, webSocketPath) {
|
|
895
|
+
const protocol = httpUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
896
|
+
return `${protocol}//${httpUrl.host}${normalizeWebSocketPath(webSocketPath)}`;
|
|
897
|
+
}
|
|
898
|
+
function normalizeWebSocketPath(path15) {
|
|
899
|
+
return path15.startsWith("/") ? path15 : `/${path15}`;
|
|
900
|
+
}
|
|
901
|
+
function rewriteBrowserWebSocketHost(browserWsUrl, requestedUrl) {
|
|
902
|
+
try {
|
|
903
|
+
const parsed = new URL(browserWsUrl);
|
|
904
|
+
parsed.protocol = requestedUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
905
|
+
parsed.hostname = requestedUrl.hostname;
|
|
906
|
+
parsed.port = requestedUrl.port;
|
|
907
|
+
return parsed.toString();
|
|
908
|
+
} catch {
|
|
909
|
+
return browserWsUrl;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function readPort(url) {
|
|
913
|
+
const port = Number.parseInt(url.port, 10);
|
|
914
|
+
return Number.isInteger(port) && port > 0 ? port : void 0;
|
|
915
|
+
}
|
|
916
|
+
var DEFAULT_DISCOVERY_TIMEOUT_MS, DISCOVERY_FALLBACK_PORT, OpensteerAttachAmbiguousError;
|
|
917
|
+
var init_cdp_discovery = __esm({
|
|
918
|
+
"src/local-browser/cdp-discovery.ts"() {
|
|
919
|
+
init_chrome_discovery();
|
|
920
|
+
DEFAULT_DISCOVERY_TIMEOUT_MS = 2e3;
|
|
921
|
+
DISCOVERY_FALLBACK_PORT = 9222;
|
|
922
|
+
OpensteerAttachAmbiguousError = class extends Error {
|
|
923
|
+
constructor(candidates) {
|
|
924
|
+
super(
|
|
925
|
+
"Multiple running Chromium DevTools endpoints were discovered. Specify the desired endpoint explicitly."
|
|
926
|
+
);
|
|
927
|
+
this.candidates = candidates;
|
|
928
|
+
this.name = "OpensteerAttachAmbiguousError";
|
|
929
|
+
}
|
|
930
|
+
code = "attach-ambiguous";
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
function resolveLiveSessionRecordPath(rootPath, provider) {
|
|
935
|
+
return path12__default.default.join(rootPath, "live", provider === "local" ? "local.json" : "cloud.json");
|
|
936
|
+
}
|
|
937
|
+
async function readPersistedSessionRecord(rootPath, provider) {
|
|
938
|
+
const sessionPath = resolveLiveSessionRecordPath(rootPath, provider);
|
|
939
|
+
if (!await pathExists(sessionPath)) {
|
|
940
|
+
return void 0;
|
|
941
|
+
}
|
|
942
|
+
const parsed = await readJsonFile(sessionPath);
|
|
943
|
+
if (isPersistedLocalBrowserSessionRecord(parsed)) {
|
|
944
|
+
return parsed;
|
|
945
|
+
}
|
|
946
|
+
return void 0;
|
|
947
|
+
}
|
|
948
|
+
async function readPersistedLocalBrowserSessionRecord(rootPath) {
|
|
949
|
+
const record = await readPersistedSessionRecord(rootPath, "local");
|
|
950
|
+
return record?.provider === "local" ? record : void 0;
|
|
951
|
+
}
|
|
952
|
+
async function writePersistedSessionRecord(rootPath, record) {
|
|
953
|
+
await writeJsonFileAtomic(resolveLiveSessionRecordPath(rootPath, record.provider), record);
|
|
954
|
+
}
|
|
955
|
+
async function clearPersistedSessionRecord(rootPath, provider) {
|
|
956
|
+
await promises.rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
|
|
957
|
+
}
|
|
958
|
+
function getPersistedLocalBrowserSessionOwnership(record) {
|
|
959
|
+
return record.ownership === "attached" ? "attached" : "owned";
|
|
960
|
+
}
|
|
961
|
+
async function isAttachedLocalBrowserSessionReachable(record) {
|
|
962
|
+
if (getPersistedLocalBrowserSessionOwnership(record) !== "attached") {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
if (record.engine !== "playwright" || record.endpoint === void 0) {
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
try {
|
|
969
|
+
await inspectCdpEndpoint({
|
|
970
|
+
endpoint: record.endpoint,
|
|
971
|
+
timeoutMs: 1500
|
|
972
|
+
});
|
|
973
|
+
return true;
|
|
974
|
+
} catch {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function isPersistedLocalBrowserSessionRecord(value) {
|
|
979
|
+
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && (value.ownership === void 0 || value.ownership === "owned" || value.ownership === "attached") && (value.activePageUrl === void 0 || typeof value.activePageUrl === "string") && (value.activePageTitle === void 0 || typeof value.activePageTitle === "string") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
|
|
980
|
+
}
|
|
981
|
+
var OPENSTEER_LIVE_SESSION_LAYOUT, OPENSTEER_LIVE_SESSION_VERSION;
|
|
982
|
+
var init_live_session = __esm({
|
|
983
|
+
"src/live-session.ts"() {
|
|
984
|
+
init_filesystem2();
|
|
985
|
+
init_cdp_discovery();
|
|
986
|
+
OPENSTEER_LIVE_SESSION_LAYOUT = "opensteer-session";
|
|
987
|
+
OPENSTEER_LIVE_SESSION_VERSION = 1;
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
function parseProcessOwner(value) {
|
|
991
|
+
if (!value || typeof value !== "object") {
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
const parsed = value;
|
|
995
|
+
const pid = Number(parsed.pid);
|
|
996
|
+
const processStartedAtMs = Number(parsed.processStartedAtMs);
|
|
997
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
if (!Number.isInteger(processStartedAtMs) || processStartedAtMs <= 0) {
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
return {
|
|
1004
|
+
pid,
|
|
1005
|
+
processStartedAtMs
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function processOwnersEqual(left, right) {
|
|
1009
|
+
if (!left || !right) {
|
|
1010
|
+
return left === right;
|
|
1011
|
+
}
|
|
1012
|
+
return left.pid === right.pid && left.processStartedAtMs === right.processStartedAtMs;
|
|
1013
|
+
}
|
|
1014
|
+
async function getProcessLiveness(owner) {
|
|
1015
|
+
if (owner.pid === process.pid && hasMatchingProcessStartTime(owner.processStartedAtMs, PROCESS_STARTED_AT_MS)) {
|
|
1016
|
+
return "live";
|
|
1017
|
+
}
|
|
1018
|
+
const startedAtMs = await readProcessStartedAtMs(owner.pid);
|
|
1019
|
+
if (typeof startedAtMs === "number") {
|
|
1020
|
+
return hasMatchingProcessStartTime(owner.processStartedAtMs, startedAtMs) ? "live" : "dead";
|
|
1021
|
+
}
|
|
1022
|
+
return isProcessRunning2(owner.pid) ? "unknown" : "dead";
|
|
1023
|
+
}
|
|
1024
|
+
function isProcessRunning2(pid) {
|
|
1025
|
+
try {
|
|
1026
|
+
process.kill(pid, 0);
|
|
1027
|
+
return true;
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
1030
|
+
return code !== "ESRCH";
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function hasMatchingProcessStartTime(expectedStartedAtMs, actualStartedAtMs) {
|
|
1034
|
+
return Math.abs(expectedStartedAtMs - actualStartedAtMs) <= PROCESS_START_TIME_TOLERANCE_MS;
|
|
1035
|
+
}
|
|
1036
|
+
async function readProcessStartedAtMs(pid) {
|
|
1037
|
+
if (pid <= 0) {
|
|
1038
|
+
return null;
|
|
1039
|
+
}
|
|
1040
|
+
if (process.platform === "linux") {
|
|
1041
|
+
return readLinuxProcessStartedAtMs(pid);
|
|
1042
|
+
}
|
|
1043
|
+
if (process.platform === "win32") {
|
|
1044
|
+
return readWindowsProcessStartedAtMs(pid);
|
|
1045
|
+
}
|
|
1046
|
+
return readPsProcessStartedAtMs(pid);
|
|
1047
|
+
}
|
|
1048
|
+
async function readLinuxProcessStartedAtMs(pid) {
|
|
1049
|
+
let statRaw;
|
|
1050
|
+
try {
|
|
1051
|
+
statRaw = await promises.readFile(`/proc/${String(pid)}/stat`, "utf8");
|
|
1052
|
+
} catch {
|
|
1053
|
+
return null;
|
|
1054
|
+
}
|
|
1055
|
+
const startTicks = parseLinuxProcessStartTicks(statRaw);
|
|
1056
|
+
if (startTicks === null) {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1059
|
+
const [bootTimeMs, clockTicksPerSecond] = await Promise.all([
|
|
1060
|
+
readLinuxBootTimeMs(),
|
|
1061
|
+
readLinuxClockTicksPerSecond()
|
|
1062
|
+
]);
|
|
1063
|
+
if (bootTimeMs === null || clockTicksPerSecond === null) {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
return Math.floor(bootTimeMs + startTicks * 1e3 / clockTicksPerSecond);
|
|
1067
|
+
}
|
|
1068
|
+
function parseLinuxProcessStartTicks(statRaw) {
|
|
1069
|
+
const closingParenIndex = statRaw.lastIndexOf(")");
|
|
1070
|
+
if (closingParenIndex === -1) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
const fields = statRaw.slice(closingParenIndex + 2).trim().split(/\s+/);
|
|
1074
|
+
const startTicks = Number(fields[LINUX_STAT_START_TIME_FIELD_INDEX]);
|
|
1075
|
+
return Number.isFinite(startTicks) && startTicks >= 0 ? startTicks : null;
|
|
1076
|
+
}
|
|
1077
|
+
async function readLinuxBootTimeMs() {
|
|
1078
|
+
try {
|
|
1079
|
+
const statRaw = await promises.readFile("/proc/stat", "utf8");
|
|
1080
|
+
const bootTimeLine = statRaw.split("\n").find((line) => line.startsWith("btime "));
|
|
1081
|
+
if (!bootTimeLine) {
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
const bootTimeSeconds = Number.parseInt(bootTimeLine.slice("btime ".length), 10);
|
|
1085
|
+
return Number.isFinite(bootTimeSeconds) ? bootTimeSeconds * 1e3 : null;
|
|
1086
|
+
} catch {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
async function readLinuxClockTicksPerSecond() {
|
|
1091
|
+
if (!linuxClockTicksPerSecondPromise) {
|
|
1092
|
+
linuxClockTicksPerSecondPromise = execFileAsync("getconf", ["CLK_TCK"], {
|
|
1093
|
+
encoding: "utf8",
|
|
1094
|
+
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES2
|
|
1095
|
+
}).then(({ stdout }) => {
|
|
1096
|
+
const value = Number.parseInt(stdout.trim(), 10);
|
|
1097
|
+
return Number.isFinite(value) && value > 0 ? value : null;
|
|
1098
|
+
}).catch(() => null);
|
|
1099
|
+
}
|
|
1100
|
+
return linuxClockTicksPerSecondPromise;
|
|
1101
|
+
}
|
|
1102
|
+
async function readWindowsProcessStartedAtMs(pid) {
|
|
1103
|
+
try {
|
|
1104
|
+
const { stdout } = await execFileAsync(
|
|
1105
|
+
"powershell.exe",
|
|
1106
|
+
[
|
|
1107
|
+
"-NoProfile",
|
|
1108
|
+
"-Command",
|
|
1109
|
+
`(Get-Process -Id ${String(pid)}).StartTime.ToUniversalTime().ToString("o")`
|
|
1110
|
+
],
|
|
1111
|
+
{
|
|
1112
|
+
encoding: "utf8",
|
|
1113
|
+
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES2
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
const isoTimestamp = stdout.trim();
|
|
1117
|
+
if (!isoTimestamp) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
const startedAtMs = Date.parse(isoTimestamp);
|
|
1121
|
+
return Number.isFinite(startedAtMs) ? startedAtMs : null;
|
|
1122
|
+
} catch {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
async function readPsProcessStartedAtMs(pid) {
|
|
1127
|
+
try {
|
|
1128
|
+
const { stdout } = await execFileAsync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
1129
|
+
encoding: "utf8",
|
|
1130
|
+
env: PS_COMMAND_ENV2,
|
|
1131
|
+
maxBuffer: PROCESS_LIST_MAX_BUFFER_BYTES2
|
|
1040
1132
|
});
|
|
1133
|
+
const startedAt = stdout.trim();
|
|
1134
|
+
if (!startedAt) {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
const startedAtMs = Date.parse(startedAt.replace(/\s+/g, " "));
|
|
1138
|
+
return Number.isFinite(startedAtMs) ? startedAtMs : null;
|
|
1139
|
+
} catch {
|
|
1140
|
+
return null;
|
|
1041
1141
|
}
|
|
1042
|
-
return null;
|
|
1043
1142
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1143
|
+
var execFileAsync, PROCESS_STARTED_AT_MS, PROCESS_START_TIME_TOLERANCE_MS, PROCESS_LIST_MAX_BUFFER_BYTES2, PS_COMMAND_ENV2, LINUX_STAT_START_TIME_FIELD_INDEX, CURRENT_PROCESS_OWNER, linuxClockTicksPerSecondPromise;
|
|
1144
|
+
var init_process_owner = __esm({
|
|
1145
|
+
"src/local-browser/process-owner.ts"() {
|
|
1146
|
+
execFileAsync = util.promisify(child_process.execFile);
|
|
1147
|
+
PROCESS_STARTED_AT_MS = Math.floor(Date.now() - process.uptime() * 1e3);
|
|
1148
|
+
PROCESS_START_TIME_TOLERANCE_MS = 1e3;
|
|
1149
|
+
PROCESS_LIST_MAX_BUFFER_BYTES2 = 16 * 1024 * 1024;
|
|
1150
|
+
PS_COMMAND_ENV2 = { ...process.env, LC_ALL: "C" };
|
|
1151
|
+
LINUX_STAT_START_TIME_FIELD_INDEX = 19;
|
|
1152
|
+
CURRENT_PROCESS_OWNER = {
|
|
1153
|
+
pid: process.pid,
|
|
1154
|
+
processStartedAtMs: PROCESS_STARTED_AT_MS
|
|
1155
|
+
};
|
|
1156
|
+
linuxClockTicksPerSecondPromise = null;
|
|
1051
1157
|
}
|
|
1052
|
-
|
|
1158
|
+
});
|
|
1159
|
+
function resolveOpensteerStateDir() {
|
|
1160
|
+
const explicit = process.env.OPENSTEER_HOME?.trim();
|
|
1161
|
+
if (explicit) {
|
|
1162
|
+
return path12__default.default.resolve(explicit);
|
|
1163
|
+
}
|
|
1164
|
+
if (process.platform === "win32") {
|
|
1165
|
+
return path12__default.default.join(
|
|
1166
|
+
process.env.LOCALAPPDATA ?? path12__default.default.join(os.homedir(), "AppData", "Local"),
|
|
1167
|
+
"Opensteer"
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
if (process.platform === "darwin") {
|
|
1171
|
+
return path12__default.default.join(os.homedir(), "Library", "Application Support", "Opensteer");
|
|
1172
|
+
}
|
|
1173
|
+
return path12__default.default.join(
|
|
1174
|
+
process.env.XDG_STATE_HOME ?? path12__default.default.join(os.homedir(), ".local", "state"),
|
|
1175
|
+
"opensteer"
|
|
1176
|
+
);
|
|
1053
1177
|
}
|
|
1054
|
-
function
|
|
1055
|
-
return
|
|
1178
|
+
function resolveLocalViewRootDir() {
|
|
1179
|
+
return path12__default.default.join(resolveOpensteerStateDir(), "local-view");
|
|
1056
1180
|
}
|
|
1057
|
-
function
|
|
1058
|
-
return
|
|
1181
|
+
function resolveLocalViewPreferencesPath() {
|
|
1182
|
+
return path12__default.default.join(resolveLocalViewRootDir(), "preferences.json");
|
|
1059
1183
|
}
|
|
1060
|
-
function
|
|
1061
|
-
|
|
1062
|
-
return {
|
|
1063
|
-
endpoint: input.browserWebSocketUrl,
|
|
1064
|
-
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
1065
|
-
...input.protocolVersion === void 0 ? {} : { protocolVersion: input.protocolVersion },
|
|
1066
|
-
httpUrl: input.httpUrl.toString(),
|
|
1067
|
-
...port === void 0 ? {} : { port }
|
|
1068
|
-
};
|
|
1184
|
+
function resolveLocalViewServiceDir() {
|
|
1185
|
+
return path12__default.default.join(resolveLocalViewRootDir(), "service");
|
|
1069
1186
|
}
|
|
1070
|
-
function
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
try {
|
|
1084
|
-
const url = endpoint.startsWith("http://") || endpoint.startsWith("https://") ? new URL(endpoint) : new URL(`http://${endpoint}`);
|
|
1085
|
-
return {
|
|
1086
|
-
httpUrl: new URL(`${url.protocol}//${url.host}`)
|
|
1087
|
-
};
|
|
1088
|
-
} catch {
|
|
1089
|
-
throw new Error(`Invalid CDP endpoint "${endpoint}".`);
|
|
1187
|
+
function resolveLocalViewSessionsDir() {
|
|
1188
|
+
return path12__default.default.join(resolveLocalViewRootDir(), "sessions");
|
|
1189
|
+
}
|
|
1190
|
+
function resolveLocalViewServiceLockDir() {
|
|
1191
|
+
return path12__default.default.join(resolveLocalViewServiceDir(), "startup.lock");
|
|
1192
|
+
}
|
|
1193
|
+
function resolveLocalViewServiceStatePath() {
|
|
1194
|
+
return path12__default.default.join(resolveLocalViewServiceDir(), "state.json");
|
|
1195
|
+
}
|
|
1196
|
+
var init_runtime_dir = __esm({
|
|
1197
|
+
"src/local-view/runtime-dir.ts"() {
|
|
1090
1198
|
}
|
|
1199
|
+
});
|
|
1200
|
+
function buildLocalViewSessionId(input) {
|
|
1201
|
+
const ownership = input.ownership ?? "owned";
|
|
1202
|
+
const identity = ownership === "attached" ? input.endpoint ?? input.remoteDebuggingUrl ?? input.baseUrl ?? "attached" : `pid:${String(input.pid ?? 0)}`;
|
|
1203
|
+
const hash = crypto.createHash("sha256").update(`${input.rootPath}
|
|
1204
|
+
${ownership}
|
|
1205
|
+
${identity}
|
|
1206
|
+
${String(input.startedAt)}`).digest("hex");
|
|
1207
|
+
return `local_${hash.slice(0, 24)}`;
|
|
1091
1208
|
}
|
|
1092
|
-
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1209
|
+
function buildLocalViewSessionIdForRecord(input) {
|
|
1210
|
+
const ownership = getPersistedLocalBrowserSessionOwnership(input.live);
|
|
1211
|
+
if (ownership === "attached") {
|
|
1212
|
+
return buildLocalViewSessionId({
|
|
1213
|
+
rootPath: input.rootPath,
|
|
1214
|
+
ownership,
|
|
1215
|
+
startedAt: input.live.startedAt,
|
|
1216
|
+
...input.live.endpoint === void 0 ? {} : { endpoint: input.live.endpoint },
|
|
1217
|
+
...input.live.baseUrl === void 0 ? {} : { baseUrl: input.live.baseUrl },
|
|
1218
|
+
...input.live.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: input.live.remoteDebuggingUrl }
|
|
1219
|
+
});
|
|
1099
1220
|
}
|
|
1100
|
-
return
|
|
1221
|
+
return buildLocalViewSessionId({
|
|
1222
|
+
rootPath: input.rootPath,
|
|
1223
|
+
ownership,
|
|
1224
|
+
startedAt: input.live.startedAt,
|
|
1225
|
+
pid: input.live.pid
|
|
1226
|
+
});
|
|
1101
1227
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1228
|
+
function createLocalViewSessionManifest(input) {
|
|
1229
|
+
return {
|
|
1230
|
+
layout: OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT,
|
|
1231
|
+
version: OPENSTEER_LOCAL_VIEW_SESSION_VERSION,
|
|
1232
|
+
sessionId: buildLocalViewSessionIdForRecord({
|
|
1233
|
+
rootPath: input.rootPath,
|
|
1234
|
+
live: input.live
|
|
1235
|
+
}),
|
|
1236
|
+
rootPath: input.rootPath,
|
|
1237
|
+
...input.workspace === void 0 ? {} : { workspace: input.workspace },
|
|
1238
|
+
engine: input.live.engine,
|
|
1239
|
+
ownership: input.ownership,
|
|
1240
|
+
pid: input.live.pid,
|
|
1241
|
+
startedAt: input.live.startedAt,
|
|
1242
|
+
updatedAt: Date.now()
|
|
1243
|
+
};
|
|
1107
1244
|
}
|
|
1108
|
-
function
|
|
1109
|
-
|
|
1110
|
-
|
|
1245
|
+
async function writeLocalViewSessionManifest(manifest) {
|
|
1246
|
+
await ensureDirectory(resolveLocalViewSessionsDir());
|
|
1247
|
+
await writeJsonFileAtomic(resolveLocalViewSessionManifestPath(manifest.sessionId), manifest);
|
|
1111
1248
|
}
|
|
1112
|
-
function
|
|
1113
|
-
|
|
1249
|
+
async function deleteLocalViewSessionManifest(sessionId) {
|
|
1250
|
+
await promises.rm(resolveLocalViewSessionManifestPath(sessionId), { force: true }).catch(() => void 0);
|
|
1114
1251
|
}
|
|
1115
|
-
function
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
parsed.hostname = requestedUrl.hostname;
|
|
1120
|
-
parsed.port = requestedUrl.port;
|
|
1121
|
-
return parsed.toString();
|
|
1122
|
-
} catch {
|
|
1123
|
-
return browserWsUrl;
|
|
1252
|
+
async function readLocalViewSessionManifest(sessionId) {
|
|
1253
|
+
const manifestPath = resolveLocalViewSessionManifestPath(sessionId);
|
|
1254
|
+
if (!await pathExists(manifestPath)) {
|
|
1255
|
+
return void 0;
|
|
1124
1256
|
}
|
|
1257
|
+
const parsed = await readJsonFile(manifestPath);
|
|
1258
|
+
return isPersistedLocalViewSessionManifest(parsed) ? parsed : void 0;
|
|
1125
1259
|
}
|
|
1126
|
-
function
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1260
|
+
async function listLocalViewSessionManifests() {
|
|
1261
|
+
const directoryPath = resolveLocalViewSessionsDir();
|
|
1262
|
+
const fileNames = await listJsonFiles(directoryPath);
|
|
1263
|
+
const manifests = await Promise.all(
|
|
1264
|
+
fileNames.map(async (fileName) => {
|
|
1265
|
+
const parsed = await readJsonFile(
|
|
1266
|
+
path12__default.default.join(directoryPath, fileName)
|
|
1267
|
+
).catch(() => void 0);
|
|
1268
|
+
return isPersistedLocalViewSessionManifest(parsed) ? parsed : void 0;
|
|
1269
|
+
})
|
|
1270
|
+
);
|
|
1271
|
+
return manifests.filter((manifest) => manifest !== void 0).sort(
|
|
1272
|
+
(left, right) => left.startedAt - right.startedAt || left.sessionId.localeCompare(right.sessionId)
|
|
1273
|
+
);
|
|
1129
1274
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
code = "attach-ambiguous";
|
|
1145
|
-
};
|
|
1275
|
+
function resolveLocalViewSessionManifestPath(sessionId) {
|
|
1276
|
+
return path12__default.default.join(resolveLocalViewSessionsDir(), `${sessionId}.json`);
|
|
1277
|
+
}
|
|
1278
|
+
function isPersistedLocalViewSessionManifest(value) {
|
|
1279
|
+
return value?.layout === OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT && value.version === OPENSTEER_LOCAL_VIEW_SESSION_VERSION && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.rootPath === "string" && value.rootPath.length > 0 && (value.engine === "playwright" || value.engine === "abp") && (value.ownership === "owned" || value.ownership === "attached" || value.ownership === "managed") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
|
|
1280
|
+
}
|
|
1281
|
+
var OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT, OPENSTEER_LOCAL_VIEW_SESSION_VERSION;
|
|
1282
|
+
var init_session_manifest = __esm({
|
|
1283
|
+
"src/local-view/session-manifest.ts"() {
|
|
1284
|
+
init_filesystem2();
|
|
1285
|
+
init_live_session();
|
|
1286
|
+
init_runtime_dir();
|
|
1287
|
+
OPENSTEER_LOCAL_VIEW_SESSION_LAYOUT = "opensteer-local-view-session";
|
|
1288
|
+
OPENSTEER_LOCAL_VIEW_SESSION_VERSION = 1;
|
|
1146
1289
|
}
|
|
1147
1290
|
});
|
|
1148
1291
|
async function readLocalViewServiceState() {
|
|
@@ -2228,7 +2371,7 @@ var init_version = __esm({
|
|
|
2228
2371
|
"../protocol/src/version.ts"() {
|
|
2229
2372
|
init_json2();
|
|
2230
2373
|
OPENSTEER_PROTOCOL_NAME = "opensteer";
|
|
2231
|
-
OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION =
|
|
2374
|
+
OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION = 3;
|
|
2232
2375
|
OPENSTEER_PROTOCOL_VERSION = `0.${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}.0`;
|
|
2233
2376
|
OPENSTEER_PROTOCOL_MEDIA_TYPE = `application/vnd.${OPENSTEER_PROTOCOL_NAME}+json;version=${OPENSTEER_PROTOCOL_VERSION}`;
|
|
2234
2377
|
OPENSTEER_PROTOCOL_REST_BASE_PATH = `/api/v${OPENSTEER_PROTOCOL_COMPATIBILITY_REVISION}`;
|
|
@@ -2478,6 +2621,9 @@ var init_metadata = __esm({
|
|
|
2478
2621
|
{
|
|
2479
2622
|
pageRef: pageRefSchema,
|
|
2480
2623
|
sessionRef: sessionRefSchema,
|
|
2624
|
+
targetId: stringSchema({
|
|
2625
|
+
description: "Underlying browser target identifier when available."
|
|
2626
|
+
}),
|
|
2481
2627
|
openerPageRef: pageRefSchema,
|
|
2482
2628
|
url: stringSchema({
|
|
2483
2629
|
description: "Current main-frame URL."
|
|
@@ -6252,14 +6398,14 @@ var init_interaction = __esm({
|
|
|
6252
6398
|
function defineSemanticOperationSpec(spec) {
|
|
6253
6399
|
return spec;
|
|
6254
6400
|
}
|
|
6255
|
-
var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema,
|
|
6401
|
+
var opensteerComputerAnnotationNames, opensteerExposedSemanticOperationNames, opensteerPackageRunnableSemanticOperationNames, snapshotModeSchema, viewportSchema, opensteerBrowserLaunchOptionsSchema, attachBrowserOptionsSchema, opensteerBrowserOptionsSchema, opensteerBrowserContextOptionsSchema, targetByElementSchema2, targetByPersistSchema2, targetBySelectorSchema2, opensteerTargetInputSchema, opensteerActionResultSchema, opensteerSnapshotCounterSchema, opensteerNavigationSummarySchema, opensteerOpenInputSchema, opensteerPageListInputSchema, opensteerPageListOutputSchema, opensteerPageNewInputSchema, opensteerPageActivateInputSchema, opensteerPageCloseInputSchema, opensteerPageCloseOutputSchema, opensteerPageNewOutputSchema, opensteerPageGotoInputSchema, opensteerPageEvaluateInputSchema, opensteerPageEvaluateOutputSchema, opensteerAddInitScriptInputSchema, opensteerAddInitScriptOutputSchema, opensteerCapturedScriptSchema, opensteerCaptureScriptsInputSchema, opensteerCaptureScriptsOutputSchema, opensteerPageSnapshotInputSchema, opensteerPageSnapshotOutputSchema, opensteerComputerMouseButtonSchema, opensteerComputerKeyModifierSchema, opensteerDomClickInputSchema, opensteerDomHoverInputSchema, opensteerDomInputInputSchema, opensteerDomScrollInputSchema, opensteerExtractTemplateSchema, opensteerDomExtractInputSchema, jsonValueSchema2, opensteerDomExtractOutputSchema, opensteerSessionCloseInputSchema, opensteerSessionCloseOutputSchema, opensteerComputerAnnotationSchema, opensteerComputerClickActionSchema, opensteerComputerMoveActionSchema, opensteerComputerScrollActionSchema, opensteerComputerTypeActionSchema, opensteerComputerKeyActionSchema, opensteerComputerDragActionSchema, opensteerComputerScreenshotActionSchema, opensteerComputerWaitActionSchema, opensteerComputerActionSchema, opensteerComputerScreenshotOptionsSchema, opensteerComputerExecuteInputSchema, opensteerScreenshotSummarySchema, opensteerComputerExecuteOutputSchema, opensteerSemanticOperationSpecificationsBase, exposedSemanticOperationNameSet, opensteerSemanticOperationSpecificationsInternal, opensteerSemanticOperationSpecifications, semanticRestBasePath;
|
|
6256
6402
|
var init_semantic = __esm({
|
|
6257
6403
|
"../protocol/src/semantic.ts"() {
|
|
6258
6404
|
init_json2();
|
|
6405
|
+
init_binary_location();
|
|
6259
6406
|
init_identity();
|
|
6260
6407
|
init_geometry();
|
|
6261
6408
|
init_metadata();
|
|
6262
|
-
init_events();
|
|
6263
6409
|
init_envelopes();
|
|
6264
6410
|
init_snapshots();
|
|
6265
6411
|
init_artifacts2();
|
|
@@ -6482,39 +6628,14 @@ var init_semantic = __esm({
|
|
|
6482
6628
|
title: "OpensteerTargetInput"
|
|
6483
6629
|
}
|
|
6484
6630
|
);
|
|
6485
|
-
opensteerResolvedTargetSchema = objectSchema(
|
|
6486
|
-
{
|
|
6487
|
-
pageRef: pageRefSchema,
|
|
6488
|
-
frameRef: frameRefSchema,
|
|
6489
|
-
documentRef: documentRefSchema,
|
|
6490
|
-
documentEpoch: documentEpochSchema,
|
|
6491
|
-
nodeRef: nodeRefSchema,
|
|
6492
|
-
tagName: stringSchema(),
|
|
6493
|
-
pathHint: stringSchema(),
|
|
6494
|
-
persist: stringSchema(),
|
|
6495
|
-
selectorUsed: stringSchema()
|
|
6496
|
-
},
|
|
6497
|
-
{
|
|
6498
|
-
title: "OpensteerResolvedTarget",
|
|
6499
|
-
required: [
|
|
6500
|
-
"pageRef",
|
|
6501
|
-
"frameRef",
|
|
6502
|
-
"documentRef",
|
|
6503
|
-
"documentEpoch",
|
|
6504
|
-
"nodeRef",
|
|
6505
|
-
"tagName",
|
|
6506
|
-
"pathHint"
|
|
6507
|
-
]
|
|
6508
|
-
}
|
|
6509
|
-
);
|
|
6510
6631
|
opensteerActionResultSchema = objectSchema(
|
|
6511
6632
|
{
|
|
6512
|
-
|
|
6513
|
-
|
|
6633
|
+
tagName: stringSchema({ minLength: 1 }),
|
|
6634
|
+
persist: stringSchema({ minLength: 1 })
|
|
6514
6635
|
},
|
|
6515
6636
|
{
|
|
6516
6637
|
title: "OpensteerActionResult",
|
|
6517
|
-
required: ["
|
|
6638
|
+
required: ["tagName"]
|
|
6518
6639
|
}
|
|
6519
6640
|
);
|
|
6520
6641
|
opensteerSnapshotCounterSchema = objectSchema(
|
|
@@ -6560,16 +6681,14 @@ var init_semantic = __esm({
|
|
|
6560
6681
|
]
|
|
6561
6682
|
}
|
|
6562
6683
|
);
|
|
6563
|
-
|
|
6684
|
+
opensteerNavigationSummarySchema = objectSchema(
|
|
6564
6685
|
{
|
|
6565
|
-
sessionRef: sessionRefSchema,
|
|
6566
|
-
pageRef: pageRefSchema,
|
|
6567
6686
|
url: stringSchema(),
|
|
6568
6687
|
title: stringSchema()
|
|
6569
6688
|
},
|
|
6570
6689
|
{
|
|
6571
|
-
title: "
|
|
6572
|
-
required: ["
|
|
6690
|
+
title: "OpensteerNavigationSummary",
|
|
6691
|
+
required: ["url", "title"]
|
|
6573
6692
|
}
|
|
6574
6693
|
);
|
|
6575
6694
|
opensteerOpenInputSchema = objectSchema(
|
|
@@ -6633,8 +6752,19 @@ var init_semantic = __esm({
|
|
|
6633
6752
|
pages: arraySchema(pageInfoSchema)
|
|
6634
6753
|
},
|
|
6635
6754
|
{
|
|
6636
|
-
title: "OpensteerPageCloseOutput",
|
|
6637
|
-
required: ["closedPageRef", "pages"]
|
|
6755
|
+
title: "OpensteerPageCloseOutput",
|
|
6756
|
+
required: ["closedPageRef", "pages"]
|
|
6757
|
+
}
|
|
6758
|
+
);
|
|
6759
|
+
opensteerPageNewOutputSchema = objectSchema(
|
|
6760
|
+
{
|
|
6761
|
+
pageRef: pageRefSchema,
|
|
6762
|
+
url: stringSchema(),
|
|
6763
|
+
title: stringSchema()
|
|
6764
|
+
},
|
|
6765
|
+
{
|
|
6766
|
+
title: "OpensteerPageNewOutput",
|
|
6767
|
+
required: ["pageRef", "url", "title"]
|
|
6638
6768
|
}
|
|
6639
6769
|
);
|
|
6640
6770
|
opensteerPageGotoInputSchema = objectSchema(
|
|
@@ -7023,72 +7153,28 @@ var init_semantic = __esm({
|
|
|
7023
7153
|
required: ["action"]
|
|
7024
7154
|
}
|
|
7025
7155
|
);
|
|
7026
|
-
|
|
7027
|
-
{
|
|
7028
|
-
role: enumSchema(["point", "start", "end"]),
|
|
7029
|
-
point: pointSchema,
|
|
7030
|
-
hitTest: hitTestResultSchema,
|
|
7031
|
-
target: opensteerResolvedTargetSchema
|
|
7032
|
-
},
|
|
7033
|
-
{
|
|
7034
|
-
title: "OpensteerComputerTracePoint",
|
|
7035
|
-
required: ["role", "point"]
|
|
7036
|
-
}
|
|
7037
|
-
);
|
|
7038
|
-
opensteerComputerTraceEnrichmentSchema = objectSchema(
|
|
7039
|
-
{
|
|
7040
|
-
points: arraySchema(opensteerComputerTracePointSchema)
|
|
7041
|
-
},
|
|
7042
|
-
{
|
|
7043
|
-
title: "OpensteerComputerTraceEnrichment",
|
|
7044
|
-
required: ["points"]
|
|
7045
|
-
}
|
|
7046
|
-
);
|
|
7047
|
-
opensteerComputerExecuteTimingSchema = objectSchema(
|
|
7156
|
+
opensteerScreenshotSummarySchema = objectSchema(
|
|
7048
7157
|
{
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
title: "OpensteerComputerExecuteTiming",
|
|
7055
|
-
required: ["actionMs", "waitMs", "totalMs"]
|
|
7056
|
-
}
|
|
7057
|
-
);
|
|
7058
|
-
opensteerComputerDisplayScaleSchema = objectSchema(
|
|
7059
|
-
{
|
|
7060
|
-
x: numberSchema({ exclusiveMinimum: 0 }),
|
|
7061
|
-
y: numberSchema({ exclusiveMinimum: 0 })
|
|
7158
|
+
payload: externalBinaryLocationSchema,
|
|
7159
|
+
format: screenshotFormatSchema,
|
|
7160
|
+
size: sizeSchema,
|
|
7161
|
+
coordinateSpace: coordinateSpaceSchema,
|
|
7162
|
+
clip: rectSchema
|
|
7062
7163
|
},
|
|
7063
7164
|
{
|
|
7064
|
-
title: "
|
|
7065
|
-
required: ["
|
|
7165
|
+
title: "OpensteerScreenshotSummary",
|
|
7166
|
+
required: ["payload", "format", "size", "coordinateSpace"]
|
|
7066
7167
|
}
|
|
7067
7168
|
);
|
|
7068
7169
|
opensteerComputerExecuteOutputSchema = objectSchema(
|
|
7069
7170
|
{
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
screenshot:
|
|
7073
|
-
displayViewport: viewportMetricsSchema,
|
|
7074
|
-
nativeViewport: viewportMetricsSchema,
|
|
7075
|
-
displayScale: opensteerComputerDisplayScaleSchema,
|
|
7076
|
-
events: arraySchema(opensteerEventSchema),
|
|
7077
|
-
timing: opensteerComputerExecuteTimingSchema,
|
|
7078
|
-
trace: opensteerComputerTraceEnrichmentSchema
|
|
7171
|
+
url: stringSchema(),
|
|
7172
|
+
title: stringSchema(),
|
|
7173
|
+
screenshot: opensteerScreenshotSummarySchema
|
|
7079
7174
|
},
|
|
7080
7175
|
{
|
|
7081
7176
|
title: "OpensteerComputerExecuteOutput",
|
|
7082
|
-
required: [
|
|
7083
|
-
"action",
|
|
7084
|
-
"pageRef",
|
|
7085
|
-
"screenshot",
|
|
7086
|
-
"displayViewport",
|
|
7087
|
-
"nativeViewport",
|
|
7088
|
-
"displayScale",
|
|
7089
|
-
"events",
|
|
7090
|
-
"timing"
|
|
7091
|
-
]
|
|
7177
|
+
required: ["url", "title", "screenshot"]
|
|
7092
7178
|
}
|
|
7093
7179
|
);
|
|
7094
7180
|
opensteerSemanticOperationSpecificationsBase = [
|
|
@@ -7096,7 +7182,7 @@ var init_semantic = __esm({
|
|
|
7096
7182
|
name: "session.open",
|
|
7097
7183
|
description: "Open or resume the current Opensteer session and primary page.",
|
|
7098
7184
|
inputSchema: opensteerOpenInputSchema,
|
|
7099
|
-
outputSchema:
|
|
7185
|
+
outputSchema: opensteerNavigationSummarySchema,
|
|
7100
7186
|
requiredCapabilities: ["sessions.manage", "pages.manage"],
|
|
7101
7187
|
resolveRequiredCapabilities: (input) => input.url === void 0 ? ["sessions.manage", "pages.manage"] : ["sessions.manage", "pages.manage", "pages.navigate"]
|
|
7102
7188
|
}),
|
|
@@ -7111,7 +7197,7 @@ var init_semantic = __esm({
|
|
|
7111
7197
|
name: "page.new",
|
|
7112
7198
|
description: "Create and optionally navigate a new top-level page in the current session.",
|
|
7113
7199
|
inputSchema: opensteerPageNewInputSchema,
|
|
7114
|
-
outputSchema:
|
|
7200
|
+
outputSchema: opensteerPageNewOutputSchema,
|
|
7115
7201
|
requiredCapabilities: ["pages.manage"],
|
|
7116
7202
|
resolveRequiredCapabilities: (input) => input.url === void 0 ? ["pages.manage"] : ["pages.manage", "pages.navigate"]
|
|
7117
7203
|
}),
|
|
@@ -7119,7 +7205,7 @@ var init_semantic = __esm({
|
|
|
7119
7205
|
name: "page.activate",
|
|
7120
7206
|
description: "Activate an existing top-level page in the current session.",
|
|
7121
7207
|
inputSchema: opensteerPageActivateInputSchema,
|
|
7122
|
-
outputSchema:
|
|
7208
|
+
outputSchema: opensteerNavigationSummarySchema,
|
|
7123
7209
|
requiredCapabilities: ["pages.manage", "inspect.pages"]
|
|
7124
7210
|
}),
|
|
7125
7211
|
defineSemanticOperationSpec({
|
|
@@ -7133,7 +7219,7 @@ var init_semantic = __esm({
|
|
|
7133
7219
|
name: "page.goto",
|
|
7134
7220
|
description: "Navigate the current Opensteer page to a new URL.",
|
|
7135
7221
|
inputSchema: opensteerPageGotoInputSchema,
|
|
7136
|
-
outputSchema:
|
|
7222
|
+
outputSchema: opensteerNavigationSummarySchema,
|
|
7137
7223
|
requiredCapabilities: ["pages.navigate"]
|
|
7138
7224
|
}),
|
|
7139
7225
|
defineSemanticOperationSpec({
|
|
@@ -8658,6 +8744,9 @@ var init_observations = __esm({
|
|
|
8658
8744
|
this.store = store;
|
|
8659
8745
|
this.sessionId = sessionId;
|
|
8660
8746
|
}
|
|
8747
|
+
configure(input) {
|
|
8748
|
+
return this.store.configureSession(this.sessionId, input);
|
|
8749
|
+
}
|
|
8661
8750
|
append(input) {
|
|
8662
8751
|
return this.store.appendEvent(this.sessionId, input);
|
|
8663
8752
|
}
|
|
@@ -8688,40 +8777,14 @@ var init_observations = __esm({
|
|
|
8688
8777
|
const sessionId = normalizeNonEmptyString("sessionId", input.sessionId);
|
|
8689
8778
|
const openedAt = normalizeTimestamp("openedAt", input.openedAt ?? Date.now());
|
|
8690
8779
|
const config = normalizeObservabilityConfig(input.config);
|
|
8691
|
-
|
|
8692
|
-
this.redactors.set(sessionId, redactor);
|
|
8693
|
-
const redactedLabels = redactor.redactLabels(config.labels);
|
|
8694
|
-
const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
|
|
8695
|
-
await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
|
|
8696
|
-
const existing = await this.reconcileSessionManifest(sessionId);
|
|
8697
|
-
if (existing === void 0) {
|
|
8698
|
-
await ensureDirectory(this.sessionEventsDirectory(sessionId));
|
|
8699
|
-
await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
|
|
8700
|
-
const session = {
|
|
8701
|
-
sessionId,
|
|
8702
|
-
profile: config.profile,
|
|
8703
|
-
...redactedLabels === void 0 ? {} : { labels: redactedLabels },
|
|
8704
|
-
...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
|
|
8705
|
-
openedAt,
|
|
8706
|
-
updatedAt: openedAt,
|
|
8707
|
-
currentSequence: 0,
|
|
8708
|
-
eventCount: 0,
|
|
8709
|
-
artifactCount: 0
|
|
8710
|
-
};
|
|
8711
|
-
await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
|
|
8712
|
-
return;
|
|
8713
|
-
}
|
|
8714
|
-
const patched = {
|
|
8715
|
-
...existing,
|
|
8716
|
-
profile: config.profile,
|
|
8717
|
-
...redactedLabels === void 0 ? {} : { labels: redactedLabels },
|
|
8718
|
-
...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
|
|
8719
|
-
updatedAt: Math.max(existing.updatedAt, openedAt)
|
|
8720
|
-
};
|
|
8721
|
-
await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
|
|
8722
|
-
});
|
|
8780
|
+
await this.applySessionConfiguration(sessionId, config, openedAt);
|
|
8723
8781
|
return new FilesystemSessionSink(this, sessionId);
|
|
8724
8782
|
}
|
|
8783
|
+
async configureSession(sessionId, input) {
|
|
8784
|
+
const updatedAt = normalizeTimestamp("updatedAt", input.updatedAt ?? Date.now());
|
|
8785
|
+
const config = normalizeObservabilityConfig(input.config);
|
|
8786
|
+
await this.applySessionConfiguration(sessionId, config, updatedAt);
|
|
8787
|
+
}
|
|
8725
8788
|
async getSession(sessionId) {
|
|
8726
8789
|
const manifestPath = this.sessionManifestPath(sessionId);
|
|
8727
8790
|
if (!await pathExists(manifestPath)) {
|
|
@@ -8942,6 +9005,40 @@ var init_observations = __esm({
|
|
|
8942
9005
|
sessionLockPath(sessionId) {
|
|
8943
9006
|
return path12__default.default.join(this.sessionDirectory(sessionId), ".lock");
|
|
8944
9007
|
}
|
|
9008
|
+
async applySessionConfiguration(sessionId, config, timestamp) {
|
|
9009
|
+
const redactor = createObservationRedactor(config);
|
|
9010
|
+
this.redactors.set(sessionId, redactor);
|
|
9011
|
+
const redactedLabels = redactor.redactLabels(config.labels);
|
|
9012
|
+
const redactedTraceContext = redactor.redactTraceContext(config.traceContext);
|
|
9013
|
+
await withFilesystemLock(this.sessionLockPath(sessionId), async () => {
|
|
9014
|
+
const existing = await this.reconcileSessionManifest(sessionId);
|
|
9015
|
+
if (existing === void 0) {
|
|
9016
|
+
await ensureDirectory(this.sessionEventsDirectory(sessionId));
|
|
9017
|
+
await ensureDirectory(this.sessionArtifactsDirectory(sessionId));
|
|
9018
|
+
const session = {
|
|
9019
|
+
sessionId,
|
|
9020
|
+
profile: config.profile,
|
|
9021
|
+
...redactedLabels === void 0 ? {} : { labels: redactedLabels },
|
|
9022
|
+
...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
|
|
9023
|
+
openedAt: timestamp,
|
|
9024
|
+
updatedAt: timestamp,
|
|
9025
|
+
currentSequence: 0,
|
|
9026
|
+
eventCount: 0,
|
|
9027
|
+
artifactCount: 0
|
|
9028
|
+
};
|
|
9029
|
+
await writeJsonFileExclusive(this.sessionManifestPath(sessionId), session);
|
|
9030
|
+
return;
|
|
9031
|
+
}
|
|
9032
|
+
const patched = {
|
|
9033
|
+
...existing,
|
|
9034
|
+
profile: config.profile,
|
|
9035
|
+
...redactedLabels === void 0 ? {} : { labels: redactedLabels },
|
|
9036
|
+
...redactedTraceContext === void 0 ? {} : { traceContext: redactedTraceContext },
|
|
9037
|
+
updatedAt: Math.max(existing.updatedAt, timestamp)
|
|
9038
|
+
};
|
|
9039
|
+
await writeJsonFileAtomic(this.sessionManifestPath(sessionId), patched);
|
|
9040
|
+
});
|
|
9041
|
+
}
|
|
8945
9042
|
async reconcileSessionManifest(sessionId) {
|
|
8946
9043
|
const session = await this.getSession(sessionId);
|
|
8947
9044
|
if (session === void 0) {
|
|
@@ -9800,6 +9897,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
|
|
|
9800
9897
|
version: 1,
|
|
9801
9898
|
provider: "local",
|
|
9802
9899
|
...workspace === void 0 ? {} : { workspace },
|
|
9900
|
+
ownership: live.ownership,
|
|
9803
9901
|
engine: live.engine,
|
|
9804
9902
|
...live.endpoint === void 0 ? {} : { endpoint: live.endpoint },
|
|
9805
9903
|
...live.baseUrl === void 0 ? {} : { baseUrl: live.baseUrl },
|
|
@@ -9815,6 +9913,7 @@ function toPersistedLocalBrowserSessionRecord(workspace, live) {
|
|
|
9815
9913
|
function toWorkspaceLiveBrowserRecord(record) {
|
|
9816
9914
|
return {
|
|
9817
9915
|
mode: "persistent",
|
|
9916
|
+
ownership: getPersistedLocalBrowserSessionOwnership(record),
|
|
9818
9917
|
engine: record.engine,
|
|
9819
9918
|
...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
|
|
9820
9919
|
...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
|
|
@@ -9841,7 +9940,12 @@ function isAttachBrowserOptions(browser) {
|
|
|
9841
9940
|
async function resolveAttachEndpoint(browser) {
|
|
9842
9941
|
const endpoint = browser?.endpoint?.trim();
|
|
9843
9942
|
if (endpoint && endpoint.length > 0) {
|
|
9844
|
-
|
|
9943
|
+
const inspected = await inspectCdpEndpoint({
|
|
9944
|
+
endpoint,
|
|
9945
|
+
...browser?.headers === void 0 ? {} : { headers: browser.headers },
|
|
9946
|
+
timeoutMs: DEFAULT_TIMEOUT_MS
|
|
9947
|
+
});
|
|
9948
|
+
return inspected.endpoint;
|
|
9845
9949
|
}
|
|
9846
9950
|
const selection = await selectAttachBrowserCandidate({
|
|
9847
9951
|
timeoutMs: DEFAULT_TIMEOUT_MS
|
|
@@ -10105,12 +10209,12 @@ async function waitForProcessExit(pid, timeoutMs) {
|
|
|
10105
10209
|
}
|
|
10106
10210
|
const deadline = Date.now() + timeoutMs;
|
|
10107
10211
|
while (Date.now() < deadline) {
|
|
10108
|
-
if (!
|
|
10212
|
+
if (!isProcessRunning2(pid)) {
|
|
10109
10213
|
return true;
|
|
10110
10214
|
}
|
|
10111
10215
|
await sleep2(50);
|
|
10112
10216
|
}
|
|
10113
|
-
return !
|
|
10217
|
+
return !isProcessRunning2(pid);
|
|
10114
10218
|
}
|
|
10115
10219
|
function resolveAbpSessionDir(workspace) {
|
|
10116
10220
|
return path12__default.default.join(workspace.livePath, "abp-session");
|
|
@@ -10267,7 +10371,7 @@ var init_browser_manager = __esm({
|
|
|
10267
10371
|
}
|
|
10268
10372
|
const liveRecord = await this.readLivePersistentBrowser(await this.ensureWorkspaceStore());
|
|
10269
10373
|
return {
|
|
10270
|
-
mode: this.mode,
|
|
10374
|
+
mode: liveRecord?.ownership === "attached" ? "attach" : this.mode,
|
|
10271
10375
|
engine: liveRecord?.engine ?? this.engineName,
|
|
10272
10376
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
10273
10377
|
live: liveRecord !== void 0
|
|
@@ -10395,6 +10499,7 @@ var init_browser_manager = __esm({
|
|
|
10395
10499
|
});
|
|
10396
10500
|
const liveRecord = {
|
|
10397
10501
|
mode: "persistent",
|
|
10502
|
+
ownership: "owned",
|
|
10398
10503
|
engine: "abp",
|
|
10399
10504
|
baseUrl: launched.baseUrl,
|
|
10400
10505
|
remoteDebuggingUrl: launched.remoteDebuggingUrl,
|
|
@@ -10481,11 +10586,78 @@ var init_browser_manager = __esm({
|
|
|
10481
10586
|
}
|
|
10482
10587
|
async createAttachEngine() {
|
|
10483
10588
|
const endpoint = await resolveAttachEndpoint(this.browserOptions);
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10589
|
+
if (this.workspace === void 0) {
|
|
10590
|
+
return this.createAttachedEngine({
|
|
10591
|
+
endpoint,
|
|
10592
|
+
...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
|
|
10593
|
+
freshTab: this.browserOptions?.freshTab ?? true,
|
|
10594
|
+
onDispose: async () => void 0
|
|
10595
|
+
});
|
|
10596
|
+
}
|
|
10597
|
+
const workspace = await this.ensureWorkspaceStore();
|
|
10598
|
+
return workspace.lock(async () => {
|
|
10599
|
+
const live = await this.readLivePersistentBrowser(workspace);
|
|
10600
|
+
if (live) {
|
|
10601
|
+
if (live.engine !== "playwright") {
|
|
10602
|
+
throw new Error(
|
|
10603
|
+
`workspace "${this.workspace}" already has a live ${live.engine} browser. Close it before attaching a Playwright browser.`
|
|
10604
|
+
);
|
|
10605
|
+
}
|
|
10606
|
+
if (live.ownership !== "attached") {
|
|
10607
|
+
throw new Error(
|
|
10608
|
+
`workspace "${this.workspace}" already has a live Opensteer-owned browser. Close it before attaching another browser.`
|
|
10609
|
+
);
|
|
10610
|
+
}
|
|
10611
|
+
if (live.endpoint === void 0) {
|
|
10612
|
+
throw new Error("workspace live browser record is missing a DevTools endpoint.");
|
|
10613
|
+
}
|
|
10614
|
+
if (live.endpoint !== endpoint) {
|
|
10615
|
+
throw new Error(
|
|
10616
|
+
`workspace "${this.workspace}" is already attached to a different browser endpoint. Close it before reattaching.`
|
|
10617
|
+
);
|
|
10618
|
+
}
|
|
10619
|
+
await bestEffortRegisterLocalViewSession({
|
|
10620
|
+
rootPath: workspace.rootPath,
|
|
10621
|
+
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
10622
|
+
live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
|
|
10623
|
+
ownership: "attached"
|
|
10624
|
+
});
|
|
10625
|
+
return this.createAttachedEngine({
|
|
10626
|
+
endpoint: live.endpoint,
|
|
10627
|
+
...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
|
|
10628
|
+
freshTab: this.browserOptions?.freshTab ?? true,
|
|
10629
|
+
onDispose: async () => void 0
|
|
10630
|
+
});
|
|
10631
|
+
}
|
|
10632
|
+
const liveRecord = {
|
|
10633
|
+
mode: "persistent",
|
|
10634
|
+
ownership: "attached",
|
|
10635
|
+
engine: "playwright",
|
|
10636
|
+
endpoint,
|
|
10637
|
+
pid: 0,
|
|
10638
|
+
startedAt: Date.now(),
|
|
10639
|
+
userDataDir: workspace.browserUserDataDir
|
|
10640
|
+
};
|
|
10641
|
+
await this.writeLivePersistentBrowser(workspace, liveRecord);
|
|
10642
|
+
const persistedLiveRecord = toPersistedLocalBrowserSessionRecord(this.workspace, liveRecord);
|
|
10643
|
+
await bestEffortRegisterLocalViewSession({
|
|
10644
|
+
rootPath: workspace.rootPath,
|
|
10645
|
+
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
10646
|
+
live: persistedLiveRecord,
|
|
10647
|
+
ownership: "attached"
|
|
10648
|
+
});
|
|
10649
|
+
try {
|
|
10650
|
+
return await this.createAttachedEngine({
|
|
10651
|
+
endpoint,
|
|
10652
|
+
...this.browserOptions?.headers === void 0 ? {} : { headers: this.browserOptions.headers },
|
|
10653
|
+
freshTab: this.browserOptions?.freshTab ?? true,
|
|
10654
|
+
onDispose: async () => void 0
|
|
10655
|
+
});
|
|
10656
|
+
} catch (error) {
|
|
10657
|
+
await this.unregisterLocalViewSessionForRecord(workspace.rootPath, persistedLiveRecord);
|
|
10658
|
+
await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
|
|
10659
|
+
throw error;
|
|
10660
|
+
}
|
|
10489
10661
|
});
|
|
10490
10662
|
}
|
|
10491
10663
|
async createPersistentEngine() {
|
|
@@ -10505,7 +10677,7 @@ var init_browser_manager = __esm({
|
|
|
10505
10677
|
rootPath: workspace.rootPath,
|
|
10506
10678
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
10507
10679
|
live: toPersistedLocalBrowserSessionRecord(this.workspace, live),
|
|
10508
|
-
ownership:
|
|
10680
|
+
ownership: live.ownership
|
|
10509
10681
|
});
|
|
10510
10682
|
return this.createAttachedEngine({
|
|
10511
10683
|
endpoint: live.endpoint,
|
|
@@ -10521,6 +10693,7 @@ var init_browser_manager = __esm({
|
|
|
10521
10693
|
});
|
|
10522
10694
|
const liveRecord = {
|
|
10523
10695
|
mode: "persistent",
|
|
10696
|
+
ownership: "owned",
|
|
10524
10697
|
engine: "playwright",
|
|
10525
10698
|
endpoint: launched.endpoint,
|
|
10526
10699
|
pid: launched.pid,
|
|
@@ -10650,7 +10823,20 @@ var init_browser_manager = __esm({
|
|
|
10650
10823
|
if (live === void 0) {
|
|
10651
10824
|
return void 0;
|
|
10652
10825
|
}
|
|
10653
|
-
if (
|
|
10826
|
+
if (live.ownership === "attached") {
|
|
10827
|
+
const attachedRecord = toPersistedLocalBrowserSessionRecord(this.workspace, live);
|
|
10828
|
+
if (!await isAttachedLocalBrowserSessionReachable(attachedRecord)) {
|
|
10829
|
+
await this.unregisterLocalViewSessionForRecord(workspace.rootPath, attachedRecord);
|
|
10830
|
+
await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
|
|
10831
|
+
return void 0;
|
|
10832
|
+
}
|
|
10833
|
+
return live;
|
|
10834
|
+
}
|
|
10835
|
+
if (!isProcessRunning2(live.pid)) {
|
|
10836
|
+
await this.unregisterLocalViewSessionForRecord(
|
|
10837
|
+
workspace.rootPath,
|
|
10838
|
+
toPersistedLocalBrowserSessionRecord(this.workspace, live)
|
|
10839
|
+
);
|
|
10654
10840
|
await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
|
|
10655
10841
|
return void 0;
|
|
10656
10842
|
}
|
|
@@ -10698,6 +10884,10 @@ var init_browser_manager = __esm({
|
|
|
10698
10884
|
workspace.rootPath,
|
|
10699
10885
|
toPersistedLocalBrowserSessionRecord(this.workspace, live)
|
|
10700
10886
|
);
|
|
10887
|
+
if (live.ownership === "attached") {
|
|
10888
|
+
await clearPersistedSessionRecord(workspace.rootPath, "local").catch(() => void 0);
|
|
10889
|
+
return;
|
|
10890
|
+
}
|
|
10701
10891
|
if (live.engine === "playwright") {
|
|
10702
10892
|
if (live.endpoint !== void 0) {
|
|
10703
10893
|
await requestBrowserClose(live.endpoint).catch(() => void 0);
|
|
@@ -10726,10 +10916,18 @@ var init_browser_manager = __esm({
|
|
|
10726
10916
|
}
|
|
10727
10917
|
async unregisterLocalViewSessionForRecord(rootPath, record) {
|
|
10728
10918
|
await bestEffortUnregisterLocalViewSession(
|
|
10729
|
-
buildLocalViewSessionId({
|
|
10919
|
+
getPersistedLocalBrowserSessionOwnership(record) === "attached" ? buildLocalViewSessionId({
|
|
10730
10920
|
rootPath,
|
|
10731
|
-
|
|
10732
|
-
|
|
10921
|
+
startedAt: record.startedAt,
|
|
10922
|
+
ownership: "attached",
|
|
10923
|
+
...record.endpoint === void 0 ? {} : { endpoint: record.endpoint },
|
|
10924
|
+
...record.baseUrl === void 0 ? {} : { baseUrl: record.baseUrl },
|
|
10925
|
+
...record.remoteDebuggingUrl === void 0 ? {} : { remoteDebuggingUrl: record.remoteDebuggingUrl }
|
|
10926
|
+
}) : buildLocalViewSessionId({
|
|
10927
|
+
rootPath,
|
|
10928
|
+
startedAt: record.startedAt,
|
|
10929
|
+
ownership: "owned",
|
|
10930
|
+
pid: record.pid
|
|
10733
10931
|
})
|
|
10734
10932
|
);
|
|
10735
10933
|
}
|
|
@@ -10833,13 +11031,13 @@ async function resolveSessionSummary(manifest) {
|
|
|
10833
11031
|
return {
|
|
10834
11032
|
sessionId: manifest.sessionId,
|
|
10835
11033
|
label: manifest.workspace ?? (path12__default.default.basename(manifest.rootPath) || manifest.sessionId),
|
|
10836
|
-
status:
|
|
11034
|
+
status: getPersistedLocalBrowserSessionOwnership(record) === "attached" || isProcessRunning2(record.pid) ? "live" : "stale",
|
|
10837
11035
|
...manifest.workspace === void 0 ? {} : { workspace: manifest.workspace },
|
|
10838
11036
|
rootPath: manifest.rootPath,
|
|
10839
11037
|
engine: record.engine,
|
|
10840
11038
|
ownership: manifest.ownership,
|
|
10841
|
-
pid: record.pid,
|
|
10842
11039
|
startedAt: record.startedAt,
|
|
11040
|
+
...record.pid > 0 ? { pid: record.pid } : {},
|
|
10843
11041
|
...browserName === void 0 ? {} : { browserName }
|
|
10844
11042
|
};
|
|
10845
11043
|
}
|
|
@@ -10867,7 +11065,16 @@ async function readLiveRecord(manifest) {
|
|
|
10867
11065
|
if (!record) {
|
|
10868
11066
|
return void 0;
|
|
10869
11067
|
}
|
|
10870
|
-
if (
|
|
11068
|
+
if (buildLocalViewSessionIdForRecord({
|
|
11069
|
+
rootPath: manifest.rootPath,
|
|
11070
|
+
live: record
|
|
11071
|
+
}) !== manifest.sessionId || record.startedAt !== manifest.startedAt || record.engine !== manifest.engine || getPersistedLocalBrowserSessionOwnership(record) !== manifest.ownership) {
|
|
11072
|
+
return void 0;
|
|
11073
|
+
}
|
|
11074
|
+
if (getPersistedLocalBrowserSessionOwnership(record) === "attached") {
|
|
11075
|
+
return await isAttachedLocalBrowserSessionReachable(record) ? record : void 0;
|
|
11076
|
+
}
|
|
11077
|
+
if (record.pid !== manifest.pid || !isProcessRunning2(record.pid)) {
|
|
10871
11078
|
return void 0;
|
|
10872
11079
|
}
|
|
10873
11080
|
return record;
|
|
@@ -11161,6 +11368,172 @@ init_process_owner();
|
|
|
11161
11368
|
init_service_state();
|
|
11162
11369
|
init_service_state();
|
|
11163
11370
|
|
|
11371
|
+
// src/local-view/browser-target-order.ts
|
|
11372
|
+
async function readPageTargetId(page) {
|
|
11373
|
+
const cdp = await page.context().newCDPSession(page);
|
|
11374
|
+
try {
|
|
11375
|
+
const result = await cdp.send("Target.getTargetInfo");
|
|
11376
|
+
const targetId = result?.targetInfo?.targetId;
|
|
11377
|
+
return typeof targetId === "string" && targetId.length > 0 ? targetId : null;
|
|
11378
|
+
} finally {
|
|
11379
|
+
await cdp.detach().catch(() => void 0);
|
|
11380
|
+
}
|
|
11381
|
+
}
|
|
11382
|
+
async function readBrowserPageTargetOrder(browserContext) {
|
|
11383
|
+
const browser = browserContext.browser();
|
|
11384
|
+
if (!browser || !hasBrowserCdpSession(browser)) {
|
|
11385
|
+
return [];
|
|
11386
|
+
}
|
|
11387
|
+
const cdp = await browser.newBrowserCDPSession();
|
|
11388
|
+
try {
|
|
11389
|
+
const result = await cdp.send("Target.getTargets");
|
|
11390
|
+
return normalizeBrowserPageTargetOrder(result?.targetInfos ?? []);
|
|
11391
|
+
} catch {
|
|
11392
|
+
return [];
|
|
11393
|
+
} finally {
|
|
11394
|
+
await cdp.detach().catch(() => void 0);
|
|
11395
|
+
}
|
|
11396
|
+
}
|
|
11397
|
+
async function orderPagesByBrowserTargetOrder(browserContext, pages) {
|
|
11398
|
+
if (pages.length < 2) {
|
|
11399
|
+
return pages;
|
|
11400
|
+
}
|
|
11401
|
+
const orderedTargetIds = await readBrowserPageTargetOrder(browserContext);
|
|
11402
|
+
if (orderedTargetIds.length === 0) {
|
|
11403
|
+
return pages;
|
|
11404
|
+
}
|
|
11405
|
+
const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
|
|
11406
|
+
const targetIds = await Promise.all(
|
|
11407
|
+
pages.map((page) => readPageTargetId(page).catch(() => null))
|
|
11408
|
+
);
|
|
11409
|
+
return pages.map((page, index) => {
|
|
11410
|
+
const targetId = targetIds[index] ?? void 0;
|
|
11411
|
+
return {
|
|
11412
|
+
page,
|
|
11413
|
+
index,
|
|
11414
|
+
rank: targetId === void 0 ? void 0 : rankByTargetId.get(targetId)
|
|
11415
|
+
};
|
|
11416
|
+
}).sort((left, right) => {
|
|
11417
|
+
if (left.rank !== void 0 && right.rank !== void 0) {
|
|
11418
|
+
return left.rank - right.rank;
|
|
11419
|
+
}
|
|
11420
|
+
if (left.rank !== void 0) {
|
|
11421
|
+
return -1;
|
|
11422
|
+
}
|
|
11423
|
+
if (right.rank !== void 0) {
|
|
11424
|
+
return 1;
|
|
11425
|
+
}
|
|
11426
|
+
return left.index - right.index;
|
|
11427
|
+
}).map((entry) => entry.page);
|
|
11428
|
+
}
|
|
11429
|
+
function hasBrowserCdpSession(browser) {
|
|
11430
|
+
return typeof browser.newBrowserCDPSession === "function";
|
|
11431
|
+
}
|
|
11432
|
+
function normalizeBrowserPageTargetOrder(targetInfos) {
|
|
11433
|
+
const reversedPageInfos = [];
|
|
11434
|
+
for (const targetInfo of targetInfos) {
|
|
11435
|
+
if (targetInfo.type !== "page") {
|
|
11436
|
+
continue;
|
|
11437
|
+
}
|
|
11438
|
+
const targetId = typeof targetInfo.targetId === "string" && targetInfo.targetId.length > 0 ? targetInfo.targetId : void 0;
|
|
11439
|
+
if (targetId === void 0) {
|
|
11440
|
+
continue;
|
|
11441
|
+
}
|
|
11442
|
+
reversedPageInfos.push({
|
|
11443
|
+
targetId,
|
|
11444
|
+
openerId: typeof targetInfo.openerId === "string" && targetInfo.openerId.length > 0 ? targetInfo.openerId : void 0
|
|
11445
|
+
});
|
|
11446
|
+
}
|
|
11447
|
+
reversedPageInfos.reverse();
|
|
11448
|
+
const rawTargetInfoById = new Map(
|
|
11449
|
+
reversedPageInfos.map((targetInfo) => [targetInfo.targetId, targetInfo])
|
|
11450
|
+
);
|
|
11451
|
+
const targetInfoById = new Map(
|
|
11452
|
+
reversedPageInfos.map(
|
|
11453
|
+
(targetInfo) => [
|
|
11454
|
+
targetInfo.targetId,
|
|
11455
|
+
{
|
|
11456
|
+
...targetInfo,
|
|
11457
|
+
openerId: resolveAcyclicOpenerId(targetInfo.targetId, rawTargetInfoById)
|
|
11458
|
+
}
|
|
11459
|
+
]
|
|
11460
|
+
)
|
|
11461
|
+
);
|
|
11462
|
+
const orderedTargetIds = [];
|
|
11463
|
+
const placed = /* @__PURE__ */ new Set();
|
|
11464
|
+
const placeTarget = (targetId) => {
|
|
11465
|
+
if (placed.has(targetId)) {
|
|
11466
|
+
return;
|
|
11467
|
+
}
|
|
11468
|
+
const targetInfo = targetInfoById.get(targetId);
|
|
11469
|
+
if (!targetInfo) {
|
|
11470
|
+
return;
|
|
11471
|
+
}
|
|
11472
|
+
const openerId = targetInfo.openerId;
|
|
11473
|
+
if (openerId === void 0) {
|
|
11474
|
+
orderedTargetIds.push(targetId);
|
|
11475
|
+
placed.add(targetId);
|
|
11476
|
+
return;
|
|
11477
|
+
}
|
|
11478
|
+
placeTarget(openerId);
|
|
11479
|
+
const openerIndex = orderedTargetIds.indexOf(openerId);
|
|
11480
|
+
const insertionIndex = openerIndex === -1 ? orderedTargetIds.length : findPopupInsertionIndex(orderedTargetIds, openerIndex, openerId, targetInfoById);
|
|
11481
|
+
orderedTargetIds.splice(insertionIndex, 0, targetId);
|
|
11482
|
+
placed.add(targetId);
|
|
11483
|
+
};
|
|
11484
|
+
for (const targetInfo of reversedPageInfos) {
|
|
11485
|
+
placeTarget(targetInfo.targetId);
|
|
11486
|
+
}
|
|
11487
|
+
return orderedTargetIds;
|
|
11488
|
+
}
|
|
11489
|
+
function resolveAcyclicOpenerId(targetId, targetInfoById) {
|
|
11490
|
+
const openerId = targetInfoById.get(targetId)?.openerId;
|
|
11491
|
+
if (openerId === void 0 || !targetInfoById.has(openerId)) {
|
|
11492
|
+
return void 0;
|
|
11493
|
+
}
|
|
11494
|
+
const visitedTargetIds = /* @__PURE__ */ new Set([targetId]);
|
|
11495
|
+
let currentTargetId = openerId;
|
|
11496
|
+
while (currentTargetId !== void 0) {
|
|
11497
|
+
if (visitedTargetIds.has(currentTargetId)) {
|
|
11498
|
+
return void 0;
|
|
11499
|
+
}
|
|
11500
|
+
visitedTargetIds.add(currentTargetId);
|
|
11501
|
+
currentTargetId = targetInfoById.get(currentTargetId)?.openerId;
|
|
11502
|
+
}
|
|
11503
|
+
return openerId;
|
|
11504
|
+
}
|
|
11505
|
+
function findPopupInsertionIndex(orderedTargetIds, openerIndex, openerTargetId, targetInfoById) {
|
|
11506
|
+
let index = openerIndex + 1;
|
|
11507
|
+
while (index < orderedTargetIds.length) {
|
|
11508
|
+
const candidateTargetId = orderedTargetIds[index];
|
|
11509
|
+
if (!candidateTargetId || !isDescendantTarget(candidateTargetId, openerTargetId, targetInfoById)) {
|
|
11510
|
+
break;
|
|
11511
|
+
}
|
|
11512
|
+
index += 1;
|
|
11513
|
+
}
|
|
11514
|
+
return index;
|
|
11515
|
+
}
|
|
11516
|
+
function isDescendantTarget(targetId, ancestorTargetId, targetInfoById) {
|
|
11517
|
+
const visitedTargetIds = /* @__PURE__ */ new Set();
|
|
11518
|
+
let currentTargetId = targetId;
|
|
11519
|
+
while (currentTargetId !== void 0) {
|
|
11520
|
+
if (visitedTargetIds.has(currentTargetId)) {
|
|
11521
|
+
return false;
|
|
11522
|
+
}
|
|
11523
|
+
visitedTargetIds.add(currentTargetId);
|
|
11524
|
+
const currentTargetInfo = targetInfoById.get(currentTargetId);
|
|
11525
|
+
const openerId = currentTargetInfo?.openerId;
|
|
11526
|
+
if (openerId === void 0) {
|
|
11527
|
+
return false;
|
|
11528
|
+
}
|
|
11529
|
+
if (openerId === ancestorTargetId) {
|
|
11530
|
+
return true;
|
|
11531
|
+
}
|
|
11532
|
+
currentTargetId = openerId;
|
|
11533
|
+
}
|
|
11534
|
+
return false;
|
|
11535
|
+
}
|
|
11536
|
+
|
|
11164
11537
|
// src/local-view/tab-state-tracker.ts
|
|
11165
11538
|
var ACTIVATION_INTENT_DISCOVERY_GRACE_MS = 2e3;
|
|
11166
11539
|
var TabStateTracker = class {
|
|
@@ -11176,6 +11549,7 @@ var TabStateTracker = class {
|
|
|
11176
11549
|
boundContextCleanup = null;
|
|
11177
11550
|
constructor(deps) {
|
|
11178
11551
|
this.deps = deps;
|
|
11552
|
+
this.lastActivePage = deps.initialActivePage ?? null;
|
|
11179
11553
|
}
|
|
11180
11554
|
start() {
|
|
11181
11555
|
if (this.running) {
|
|
@@ -11300,7 +11674,7 @@ var TabStateTracker = class {
|
|
|
11300
11674
|
this.updatePolling(pages.length);
|
|
11301
11675
|
const preferredActivePage = this.lastActivePage ?? pages[0] ?? null;
|
|
11302
11676
|
const pageStates = await Promise.all(
|
|
11303
|
-
pages.map(async (page,
|
|
11677
|
+
pages.map(async (page, originalIndex) => {
|
|
11304
11678
|
const metadata = await this.readPageMetadata(page, {
|
|
11305
11679
|
refresh: args.refreshMetadata
|
|
11306
11680
|
});
|
|
@@ -11310,7 +11684,7 @@ var TabStateTracker = class {
|
|
|
11310
11684
|
};
|
|
11311
11685
|
return {
|
|
11312
11686
|
page,
|
|
11313
|
-
|
|
11687
|
+
originalIndex,
|
|
11314
11688
|
targetId: metadata.targetId,
|
|
11315
11689
|
url: page.url(),
|
|
11316
11690
|
title: metadata.title,
|
|
@@ -11319,17 +11693,18 @@ var TabStateTracker = class {
|
|
|
11319
11693
|
};
|
|
11320
11694
|
})
|
|
11321
11695
|
);
|
|
11696
|
+
const orderedPageStates = await this.orderPageStates(pageStates);
|
|
11322
11697
|
const activePage = this.pickActivePage(
|
|
11323
|
-
|
|
11698
|
+
orderedPageStates,
|
|
11324
11699
|
this.lastActivePage,
|
|
11325
11700
|
preferredActivePage,
|
|
11326
|
-
this.resolveIntentPage(
|
|
11701
|
+
this.resolveIntentPage(orderedPageStates)
|
|
11327
11702
|
);
|
|
11328
11703
|
if (activePage && activePage !== this.lastActivePage) {
|
|
11329
11704
|
this.lastActivePage = activePage;
|
|
11330
11705
|
this.deps.onActivePageChanged(activePage);
|
|
11331
11706
|
}
|
|
11332
|
-
const tabs =
|
|
11707
|
+
const tabs = orderedPageStates.map((state) => ({
|
|
11333
11708
|
index: state.index,
|
|
11334
11709
|
...state.targetId === void 0 ? {} : { targetId: state.targetId },
|
|
11335
11710
|
url: state.url,
|
|
@@ -11379,18 +11754,11 @@ var TabStateTracker = class {
|
|
|
11379
11754
|
if (cached) {
|
|
11380
11755
|
return cached;
|
|
11381
11756
|
}
|
|
11382
|
-
const
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
const targetId = result?.targetInfo?.targetId;
|
|
11386
|
-
if (typeof targetId === "string" && targetId.length > 0) {
|
|
11387
|
-
this.targetIdByPage.set(page, targetId);
|
|
11388
|
-
return targetId;
|
|
11389
|
-
}
|
|
11390
|
-
return null;
|
|
11391
|
-
} finally {
|
|
11392
|
-
await cdp.detach().catch(() => void 0);
|
|
11757
|
+
const targetId = await readPageTargetId(page);
|
|
11758
|
+
if (targetId) {
|
|
11759
|
+
this.targetIdByPage.set(page, targetId);
|
|
11393
11760
|
}
|
|
11761
|
+
return targetId;
|
|
11394
11762
|
}
|
|
11395
11763
|
async readFocusState(page) {
|
|
11396
11764
|
try {
|
|
@@ -11458,6 +11826,33 @@ var TabStateTracker = class {
|
|
|
11458
11826
|
this.deps.runtimeState.clearPageActivationIntent(this.deps.sessionId, intent.targetId);
|
|
11459
11827
|
return { page: matched.page };
|
|
11460
11828
|
}
|
|
11829
|
+
async orderPageStates(pageStates) {
|
|
11830
|
+
if (pageStates.length < 2) {
|
|
11831
|
+
return pageStates.map(({ originalIndex: _originalIndex, ...state }, index) => ({
|
|
11832
|
+
...state,
|
|
11833
|
+
index
|
|
11834
|
+
}));
|
|
11835
|
+
}
|
|
11836
|
+
const orderedTargetIds = await readBrowserPageTargetOrder(this.deps.browserContext);
|
|
11837
|
+
const rankByTargetId = new Map(orderedTargetIds.map((targetId, index) => [targetId, index]));
|
|
11838
|
+
return [...pageStates].sort((left, right) => {
|
|
11839
|
+
const leftRank = left.targetId === void 0 ? void 0 : rankByTargetId.get(left.targetId);
|
|
11840
|
+
const rightRank = right.targetId === void 0 ? void 0 : rankByTargetId.get(right.targetId);
|
|
11841
|
+
if (leftRank !== void 0 && rightRank !== void 0) {
|
|
11842
|
+
return leftRank - rightRank;
|
|
11843
|
+
}
|
|
11844
|
+
if (leftRank !== void 0) {
|
|
11845
|
+
return -1;
|
|
11846
|
+
}
|
|
11847
|
+
if (rightRank !== void 0) {
|
|
11848
|
+
return 1;
|
|
11849
|
+
}
|
|
11850
|
+
return left.originalIndex - right.originalIndex;
|
|
11851
|
+
}).map(({ originalIndex: _originalIndex, ...state }, index) => ({
|
|
11852
|
+
...state,
|
|
11853
|
+
index
|
|
11854
|
+
}));
|
|
11855
|
+
}
|
|
11461
11856
|
};
|
|
11462
11857
|
|
|
11463
11858
|
// src/local-view/view-stream-capture-policy.ts
|
|
@@ -11821,6 +12216,7 @@ var SessionViewStreamProducer = class {
|
|
|
11821
12216
|
sessionId: this.deps.sessionId,
|
|
11822
12217
|
pollMs: TAB_STATE_POLL_MS,
|
|
11823
12218
|
runtimeState: this.deps.runtimeState,
|
|
12219
|
+
initialActivePage: session.page,
|
|
11824
12220
|
onActivePageChanged: (page) => {
|
|
11825
12221
|
this.activePage = page;
|
|
11826
12222
|
void this.queueBindToPage(page).catch(() => void 0);
|
|
@@ -11919,7 +12315,20 @@ var SessionViewStreamProducer = class {
|
|
|
11919
12315
|
if (!context) {
|
|
11920
12316
|
throw new Error("Connected browser did not expose a Chromium browser context.");
|
|
11921
12317
|
}
|
|
11922
|
-
const
|
|
12318
|
+
const existingPages = context.pages();
|
|
12319
|
+
if (existingPages.length === 0) {
|
|
12320
|
+
const page2 = await context.newPage();
|
|
12321
|
+
return {
|
|
12322
|
+
browser,
|
|
12323
|
+
context,
|
|
12324
|
+
page: page2
|
|
12325
|
+
};
|
|
12326
|
+
}
|
|
12327
|
+
const orderedPages = await orderPagesByBrowserTargetOrder(context, existingPages);
|
|
12328
|
+
const page = await resolvePersistedActivePage(orderedPages, {
|
|
12329
|
+
...resolved.record.activePageUrl === void 0 ? {} : { activePageUrl: resolved.record.activePageUrl },
|
|
12330
|
+
...resolved.record.activePageTitle === void 0 ? {} : { activePageTitle: resolved.record.activePageTitle }
|
|
12331
|
+
}) ?? orderedPages[0];
|
|
11923
12332
|
return {
|
|
11924
12333
|
browser,
|
|
11925
12334
|
context,
|
|
@@ -12264,6 +12673,28 @@ async function disconnectPlaywrightChromiumBrowser(browser) {
|
|
|
12264
12673
|
const { disconnectPlaywrightChromiumBrowser: disconnect } = await import('@opensteer/engine-playwright');
|
|
12265
12674
|
await disconnect(browser);
|
|
12266
12675
|
}
|
|
12676
|
+
async function resolvePersistedActivePage(pages, input) {
|
|
12677
|
+
if (pages.length === 0) {
|
|
12678
|
+
return null;
|
|
12679
|
+
}
|
|
12680
|
+
if (input.activePageUrl === void 0 && input.activePageTitle === void 0) {
|
|
12681
|
+
return null;
|
|
12682
|
+
}
|
|
12683
|
+
const matchesByUrl = input.activePageUrl === void 0 ? pages : pages.filter((page) => page.url() === input.activePageUrl);
|
|
12684
|
+
if (matchesByUrl.length === 0) {
|
|
12685
|
+
return null;
|
|
12686
|
+
}
|
|
12687
|
+
if (input.activePageTitle === void 0) {
|
|
12688
|
+
return matchesByUrl[0] ?? null;
|
|
12689
|
+
}
|
|
12690
|
+
for (const page of matchesByUrl) {
|
|
12691
|
+
const title = await page.title().catch(() => "");
|
|
12692
|
+
if (title === input.activePageTitle) {
|
|
12693
|
+
return page;
|
|
12694
|
+
}
|
|
12695
|
+
}
|
|
12696
|
+
return matchesByUrl[0] ?? null;
|
|
12697
|
+
}
|
|
12267
12698
|
|
|
12268
12699
|
// src/local-view/server.ts
|
|
12269
12700
|
var DEFAULT_MAX_FPS = 12;
|