@hasna/browser 0.3.7 → 0.3.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/dist/cli/index.js +400 -335
- package/dist/index.js +8253 -7844
- package/dist/lib/actions.d.ts +1 -0
- package/dist/lib/actions.d.ts.map +1 -1
- package/dist/lib/coordination.d.ts.map +1 -1
- package/dist/lib/network.d.ts.map +1 -1
- package/dist/lib/page-memory.d.ts.map +1 -1
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/task-queue.d.ts.map +1 -1
- package/dist/mcp/index.js +594 -529
- package/dist/server/index.js +1048 -312
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -12811,7 +12811,8 @@ function startHAR(page) {
|
|
|
12811
12811
|
},
|
|
12812
12812
|
timings: { send: 0, wait: duration, receive: 0 }
|
|
12813
12813
|
};
|
|
12814
|
-
entries.
|
|
12814
|
+
if (entries.length < HAR_MAX_ENTRIES)
|
|
12815
|
+
entries.push(entry);
|
|
12815
12816
|
};
|
|
12816
12817
|
const onRequestFailed = (req) => {
|
|
12817
12818
|
requestStart.delete(req.url() + req.method());
|
|
@@ -12836,6 +12837,7 @@ function startHAR(page) {
|
|
|
12836
12837
|
}
|
|
12837
12838
|
};
|
|
12838
12839
|
}
|
|
12840
|
+
var HAR_MAX_ENTRIES = 5000;
|
|
12839
12841
|
var init_network = __esm(() => {
|
|
12840
12842
|
init_network_log();
|
|
12841
12843
|
});
|
|
@@ -13238,337 +13240,6 @@ var init_storage_state = __esm(() => {
|
|
|
13238
13240
|
STATES_DIR = join8(getDataDir2(), "states");
|
|
13239
13241
|
});
|
|
13240
13242
|
|
|
13241
|
-
// src/lib/session.ts
|
|
13242
|
-
var exports_session = {};
|
|
13243
|
-
__export(exports_session, {
|
|
13244
|
-
setSessionPage: () => setSessionPage,
|
|
13245
|
-
renameSession: () => renameSession2,
|
|
13246
|
-
listSessions: () => listSessions2,
|
|
13247
|
-
isBunSession: () => isBunSession,
|
|
13248
|
-
isAutoGallery: () => isAutoGallery,
|
|
13249
|
-
hasActiveHandle: () => hasActiveHandle,
|
|
13250
|
-
getTokenBudget: () => getTokenBudget,
|
|
13251
|
-
getSessionPage: () => getSessionPage,
|
|
13252
|
-
getSessionEngine: () => getSessionEngine,
|
|
13253
|
-
getSessionByName: () => getSessionByName2,
|
|
13254
|
-
getSessionBunView: () => getSessionBunView,
|
|
13255
|
-
getSessionBrowser: () => getSessionBrowser,
|
|
13256
|
-
getSession: () => getSession2,
|
|
13257
|
-
getDefaultSession: () => getDefaultSession,
|
|
13258
|
-
getActiveSessions: () => getActiveSessions,
|
|
13259
|
-
getActiveSessionForAgent: () => getActiveSessionForAgent2,
|
|
13260
|
-
createSession: () => createSession2,
|
|
13261
|
-
countActiveSessions: () => countActiveSessions2,
|
|
13262
|
-
closeSession: () => closeSession2,
|
|
13263
|
-
closeAllSessions: () => closeAllSessions,
|
|
13264
|
-
browserPool: () => pool
|
|
13265
|
-
});
|
|
13266
|
-
function createBunProxy(view) {
|
|
13267
|
-
return view;
|
|
13268
|
-
}
|
|
13269
|
-
async function createSession2(opts = {}) {
|
|
13270
|
-
if (opts.cdpUrl) {
|
|
13271
|
-
const { connectToExistingBrowser: connectToExistingBrowser2 } = await Promise.resolve().then(() => (init_cdp(), exports_cdp));
|
|
13272
|
-
const cdpBrowser = await connectToExistingBrowser2(opts.cdpUrl);
|
|
13273
|
-
const contexts = cdpBrowser.contexts();
|
|
13274
|
-
const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();
|
|
13275
|
-
const pages = context.pages();
|
|
13276
|
-
const page2 = pages.length > 0 ? pages[0] : await context.newPage();
|
|
13277
|
-
const session2 = createSession({
|
|
13278
|
-
engine: "cdp",
|
|
13279
|
-
projectId: opts.projectId,
|
|
13280
|
-
agentId: opts.agentId,
|
|
13281
|
-
startUrl: page2.url(),
|
|
13282
|
-
name: opts.name ?? "attached"
|
|
13283
|
-
});
|
|
13284
|
-
const cleanups2 = [];
|
|
13285
|
-
if (opts.captureNetwork !== false) {
|
|
13286
|
-
try {
|
|
13287
|
-
cleanups2.push(enableNetworkLogging(page2, session2.id));
|
|
13288
|
-
} catch {}
|
|
13289
|
-
}
|
|
13290
|
-
if (opts.captureConsole !== false) {
|
|
13291
|
-
try {
|
|
13292
|
-
cleanups2.push(enableConsoleCapture(page2, session2.id));
|
|
13293
|
-
} catch {}
|
|
13294
|
-
}
|
|
13295
|
-
try {
|
|
13296
|
-
cleanups2.push(setupDialogHandler(page2, session2.id));
|
|
13297
|
-
} catch {}
|
|
13298
|
-
handles.set(session2.id, { browser: cdpBrowser, bunView: null, page: page2, engine: "cdp", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
13299
|
-
return { session: session2, page: page2 };
|
|
13300
|
-
}
|
|
13301
|
-
const engine = opts.engine === "auto" || !opts.engine ? selectEngine(opts.useCase ?? "spa_navigate" /* SPA_NAVIGATE */, opts.engine) : opts.engine;
|
|
13302
|
-
const resolvedEngine = engine === "auto" ? "playwright" : engine;
|
|
13303
|
-
let browser = null;
|
|
13304
|
-
let bunView = null;
|
|
13305
|
-
let page;
|
|
13306
|
-
if (resolvedEngine === "bun") {
|
|
13307
|
-
if (!isBunWebViewAvailable()) {
|
|
13308
|
-
console.warn("[browser] Bun.WebView requested but not available \u2014 falling back to playwright. Run: bun upgrade --canary");
|
|
13309
|
-
browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
|
|
13310
|
-
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
13311
|
-
} else {
|
|
13312
|
-
bunView = new BunWebViewSession({
|
|
13313
|
-
width: opts.viewport?.width ?? 1280,
|
|
13314
|
-
height: opts.viewport?.height ?? 720,
|
|
13315
|
-
profile: opts.name ?? undefined
|
|
13316
|
-
});
|
|
13317
|
-
if (opts.stealth) {}
|
|
13318
|
-
page = createBunProxy(bunView);
|
|
13319
|
-
}
|
|
13320
|
-
} else if (resolvedEngine === "lightpanda") {
|
|
13321
|
-
browser = await connectLightpanda();
|
|
13322
|
-
const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
|
|
13323
|
-
page = await context.newPage();
|
|
13324
|
-
} else {
|
|
13325
|
-
browser = await pool.acquire(opts.headless ?? true);
|
|
13326
|
-
if (opts.storageState) {
|
|
13327
|
-
const { loadStatePath: loadStatePath2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
|
|
13328
|
-
const statePath2 = loadStatePath2(opts.storageState);
|
|
13329
|
-
if (statePath2) {
|
|
13330
|
-
const context = await browser.newContext({
|
|
13331
|
-
viewport: opts.viewport ?? { width: 1280, height: 720 },
|
|
13332
|
-
userAgent: opts.userAgent,
|
|
13333
|
-
storageState: statePath2
|
|
13334
|
-
});
|
|
13335
|
-
page = await context.newPage();
|
|
13336
|
-
} else {
|
|
13337
|
-
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
13338
|
-
}
|
|
13339
|
-
} else {
|
|
13340
|
-
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
13341
|
-
}
|
|
13342
|
-
}
|
|
13343
|
-
const sessionName = opts.name ?? (opts.startUrl ? (() => {
|
|
13344
|
-
try {
|
|
13345
|
-
return new URL(opts.startUrl).hostname;
|
|
13346
|
-
} catch {
|
|
13347
|
-
return;
|
|
13348
|
-
}
|
|
13349
|
-
})() : undefined);
|
|
13350
|
-
const session = createSession({
|
|
13351
|
-
engine: bunView ? "bun" : browser ? resolvedEngine : resolvedEngine,
|
|
13352
|
-
projectId: opts.projectId,
|
|
13353
|
-
agentId: opts.agentId,
|
|
13354
|
-
startUrl: opts.startUrl,
|
|
13355
|
-
name: sessionName
|
|
13356
|
-
});
|
|
13357
|
-
if (opts.stealth && !bunView) {
|
|
13358
|
-
try {
|
|
13359
|
-
await applyStealthPatches(page);
|
|
13360
|
-
} catch {}
|
|
13361
|
-
}
|
|
13362
|
-
const cleanups = [];
|
|
13363
|
-
if (!bunView) {
|
|
13364
|
-
if (opts.captureNetwork !== false) {
|
|
13365
|
-
try {
|
|
13366
|
-
cleanups.push(enableNetworkLogging(page, session.id));
|
|
13367
|
-
} catch {}
|
|
13368
|
-
}
|
|
13369
|
-
if (opts.captureConsole !== false) {
|
|
13370
|
-
try {
|
|
13371
|
-
cleanups.push(enableConsoleCapture(page, session.id));
|
|
13372
|
-
} catch {}
|
|
13373
|
-
}
|
|
13374
|
-
try {
|
|
13375
|
-
cleanups.push(setupDialogHandler(page, session.id));
|
|
13376
|
-
} catch {}
|
|
13377
|
-
} else {
|
|
13378
|
-
if (opts.captureConsole !== false) {
|
|
13379
|
-
try {
|
|
13380
|
-
const { logConsoleMessage: logConsoleMessage2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
|
|
13381
|
-
await bunView.addInitScript(`
|
|
13382
|
-
(() => {
|
|
13383
|
-
const orig = { log: console.log, warn: console.warn, error: console.error, debug: console.debug, info: console.info };
|
|
13384
|
-
['log','warn','error','debug','info'].forEach(level => {
|
|
13385
|
-
console[level] = (...args) => {
|
|
13386
|
-
orig[level](...args);
|
|
13387
|
-
};
|
|
13388
|
-
});
|
|
13389
|
-
})()
|
|
13390
|
-
`);
|
|
13391
|
-
} catch {}
|
|
13392
|
-
}
|
|
13393
|
-
}
|
|
13394
|
-
handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
13395
|
-
if (opts.startUrl) {
|
|
13396
|
-
try {
|
|
13397
|
-
if (bunView) {
|
|
13398
|
-
await bunView.goto(opts.startUrl);
|
|
13399
|
-
} else {
|
|
13400
|
-
await page.goto(opts.startUrl, { waitUntil: "domcontentloaded" });
|
|
13401
|
-
}
|
|
13402
|
-
} catch {}
|
|
13403
|
-
}
|
|
13404
|
-
return { session, page };
|
|
13405
|
-
}
|
|
13406
|
-
function getSessionPage(sessionId) {
|
|
13407
|
-
const handle = handles.get(sessionId);
|
|
13408
|
-
if (!handle)
|
|
13409
|
-
throw new SessionNotFoundError(sessionId);
|
|
13410
|
-
try {
|
|
13411
|
-
if (handle.bunView) {
|
|
13412
|
-
handle.bunView.url();
|
|
13413
|
-
} else {
|
|
13414
|
-
handle.page.url();
|
|
13415
|
-
}
|
|
13416
|
-
} catch {
|
|
13417
|
-
handles.delete(sessionId);
|
|
13418
|
-
throw new SessionNotFoundError(sessionId);
|
|
13419
|
-
}
|
|
13420
|
-
handle.lastActivity = Date.now();
|
|
13421
|
-
return handle.page;
|
|
13422
|
-
}
|
|
13423
|
-
function getSessionBunView(sessionId) {
|
|
13424
|
-
return handles.get(sessionId)?.bunView ?? null;
|
|
13425
|
-
}
|
|
13426
|
-
function isBunSession(sessionId) {
|
|
13427
|
-
return handles.get(sessionId)?.engine === "bun";
|
|
13428
|
-
}
|
|
13429
|
-
function getSessionBrowser(sessionId) {
|
|
13430
|
-
const handle = handles.get(sessionId);
|
|
13431
|
-
if (!handle)
|
|
13432
|
-
throw new SessionNotFoundError(sessionId);
|
|
13433
|
-
if (!handle.browser)
|
|
13434
|
-
throw new BrowserError("This session uses Bun.WebView (no Playwright browser)", "NO_PLAYWRIGHT_BROWSER");
|
|
13435
|
-
return handle.browser;
|
|
13436
|
-
}
|
|
13437
|
-
function getSessionEngine(sessionId) {
|
|
13438
|
-
const handle = handles.get(sessionId);
|
|
13439
|
-
if (!handle)
|
|
13440
|
-
throw new SessionNotFoundError(sessionId);
|
|
13441
|
-
return handle.engine;
|
|
13442
|
-
}
|
|
13443
|
-
function hasActiveHandle(sessionId) {
|
|
13444
|
-
return handles.has(sessionId);
|
|
13445
|
-
}
|
|
13446
|
-
function setSessionPage(sessionId, page) {
|
|
13447
|
-
const handle = handles.get(sessionId);
|
|
13448
|
-
if (!handle)
|
|
13449
|
-
throw new SessionNotFoundError(sessionId);
|
|
13450
|
-
handle.page = page;
|
|
13451
|
-
}
|
|
13452
|
-
async function closeSession2(sessionId) {
|
|
13453
|
-
const handle = handles.get(sessionId);
|
|
13454
|
-
if (handle) {
|
|
13455
|
-
for (const cleanup of handle.cleanups) {
|
|
13456
|
-
try {
|
|
13457
|
-
cleanup();
|
|
13458
|
-
} catch {}
|
|
13459
|
-
}
|
|
13460
|
-
if (handle.bunView) {
|
|
13461
|
-
try {
|
|
13462
|
-
await handle.bunView.close();
|
|
13463
|
-
} catch {}
|
|
13464
|
-
} else {
|
|
13465
|
-
try {
|
|
13466
|
-
await handle.page.context().close();
|
|
13467
|
-
} catch {}
|
|
13468
|
-
if (handle.browser)
|
|
13469
|
-
pool.release(handle.browser);
|
|
13470
|
-
}
|
|
13471
|
-
handles.delete(sessionId);
|
|
13472
|
-
}
|
|
13473
|
-
return closeSession(sessionId);
|
|
13474
|
-
}
|
|
13475
|
-
function getSession2(sessionId) {
|
|
13476
|
-
return getSession(sessionId);
|
|
13477
|
-
}
|
|
13478
|
-
function listSessions2(filter) {
|
|
13479
|
-
return listSessions(filter);
|
|
13480
|
-
}
|
|
13481
|
-
function getActiveSessions() {
|
|
13482
|
-
return listSessions({ status: "active" });
|
|
13483
|
-
}
|
|
13484
|
-
async function closeAllSessions() {
|
|
13485
|
-
for (const [id] of handles) {
|
|
13486
|
-
await closeSession2(id).catch(() => {});
|
|
13487
|
-
}
|
|
13488
|
-
await pool.destroyAll();
|
|
13489
|
-
}
|
|
13490
|
-
function getSessionByName2(name) {
|
|
13491
|
-
return getSessionByName(name);
|
|
13492
|
-
}
|
|
13493
|
-
function renameSession2(id, name) {
|
|
13494
|
-
return renameSession(id, name);
|
|
13495
|
-
}
|
|
13496
|
-
function getTokenBudget(sessionId) {
|
|
13497
|
-
const handle = handles.get(sessionId);
|
|
13498
|
-
return handle ? handle.tokenBudget : null;
|
|
13499
|
-
}
|
|
13500
|
-
function getActiveSessionForAgent2(agentId) {
|
|
13501
|
-
const session = getActiveSessionForAgent(agentId);
|
|
13502
|
-
if (!session)
|
|
13503
|
-
return null;
|
|
13504
|
-
const handle = handles.get(session.id);
|
|
13505
|
-
if (!handle)
|
|
13506
|
-
return null;
|
|
13507
|
-
try {
|
|
13508
|
-
if (handle.bunView)
|
|
13509
|
-
handle.bunView.url();
|
|
13510
|
-
else
|
|
13511
|
-
handle.page.url();
|
|
13512
|
-
} catch {
|
|
13513
|
-
handles.delete(session.id);
|
|
13514
|
-
return null;
|
|
13515
|
-
}
|
|
13516
|
-
return { session, page: handle.page };
|
|
13517
|
-
}
|
|
13518
|
-
function getDefaultSession() {
|
|
13519
|
-
const session = getDefaultActiveSession();
|
|
13520
|
-
if (!session)
|
|
13521
|
-
return null;
|
|
13522
|
-
const handle = handles.get(session.id);
|
|
13523
|
-
if (!handle)
|
|
13524
|
-
return null;
|
|
13525
|
-
try {
|
|
13526
|
-
if (handle.bunView)
|
|
13527
|
-
handle.bunView.url();
|
|
13528
|
-
else
|
|
13529
|
-
handle.page.url();
|
|
13530
|
-
} catch {
|
|
13531
|
-
handles.delete(session.id);
|
|
13532
|
-
return null;
|
|
13533
|
-
}
|
|
13534
|
-
return { session, page: handle.page };
|
|
13535
|
-
}
|
|
13536
|
-
function isAutoGallery(sessionId) {
|
|
13537
|
-
return handles.get(sessionId)?.autoGallery ?? false;
|
|
13538
|
-
}
|
|
13539
|
-
function countActiveSessions2() {
|
|
13540
|
-
return countActiveSessions();
|
|
13541
|
-
}
|
|
13542
|
-
var handles, pool, SESSION_TTL_MS, ttlInterval;
|
|
13543
|
-
var init_session = __esm(() => {
|
|
13544
|
-
init_types();
|
|
13545
|
-
init_types();
|
|
13546
|
-
init_sessions();
|
|
13547
|
-
init_playwright();
|
|
13548
|
-
init_lightpanda();
|
|
13549
|
-
init_bun_webview();
|
|
13550
|
-
init_selector();
|
|
13551
|
-
init_network();
|
|
13552
|
-
init_console();
|
|
13553
|
-
init_stealth();
|
|
13554
|
-
init_dialogs();
|
|
13555
|
-
handles = new Map;
|
|
13556
|
-
pool = new BrowserPool(5);
|
|
13557
|
-
SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
|
|
13558
|
-
ttlInterval = setInterval(async () => {
|
|
13559
|
-
const now = Date.now();
|
|
13560
|
-
for (const [id, handle] of handles) {
|
|
13561
|
-
if (now - handle.lastActivity > SESSION_TTL_MS) {
|
|
13562
|
-
try {
|
|
13563
|
-
await closeSession2(id);
|
|
13564
|
-
} catch {}
|
|
13565
|
-
}
|
|
13566
|
-
}
|
|
13567
|
-
}, 60000);
|
|
13568
|
-
if (ttlInterval.unref)
|
|
13569
|
-
ttlInterval.unref();
|
|
13570
|
-
});
|
|
13571
|
-
|
|
13572
13243
|
// src/lib/snapshot.ts
|
|
13573
13244
|
var exports_snapshot = {};
|
|
13574
13245
|
__export(exports_snapshot, {
|
|
@@ -13919,6 +13590,7 @@ __export(exports_actions, {
|
|
|
13919
13590
|
typeRef: () => typeRef,
|
|
13920
13591
|
type: () => type,
|
|
13921
13592
|
stopWatch: () => stopWatch,
|
|
13593
|
+
stopAllWatchesForSession: () => stopAllWatchesForSession,
|
|
13922
13594
|
selectRef: () => selectRef,
|
|
13923
13595
|
selectOption: () => selectOption,
|
|
13924
13596
|
scrollTo: () => scrollTo,
|
|
@@ -14245,6 +13917,12 @@ function stopWatch(watchId) {
|
|
|
14245
13917
|
activeWatches.delete(watchId);
|
|
14246
13918
|
}
|
|
14247
13919
|
}
|
|
13920
|
+
function stopAllWatchesForSession(_sessionId) {
|
|
13921
|
+
for (const [id, w] of activeWatches) {
|
|
13922
|
+
clearInterval(w.interval);
|
|
13923
|
+
activeWatches.delete(id);
|
|
13924
|
+
}
|
|
13925
|
+
}
|
|
14248
13926
|
async function clickRef(page, sessionId, ref, opts) {
|
|
14249
13927
|
try {
|
|
14250
13928
|
const locator = getRefLocator(page, sessionId, ref);
|
|
@@ -14320,6 +13998,363 @@ var init_actions = __esm(() => {
|
|
|
14320
13998
|
activeWatches = new Map;
|
|
14321
13999
|
});
|
|
14322
14000
|
|
|
14001
|
+
// src/lib/session.ts
|
|
14002
|
+
var exports_session = {};
|
|
14003
|
+
__export(exports_session, {
|
|
14004
|
+
setSessionPage: () => setSessionPage,
|
|
14005
|
+
renameSession: () => renameSession2,
|
|
14006
|
+
listSessions: () => listSessions2,
|
|
14007
|
+
isBunSession: () => isBunSession,
|
|
14008
|
+
isAutoGallery: () => isAutoGallery,
|
|
14009
|
+
hasActiveHandle: () => hasActiveHandle,
|
|
14010
|
+
getTokenBudget: () => getTokenBudget,
|
|
14011
|
+
getSessionPage: () => getSessionPage,
|
|
14012
|
+
getSessionEngine: () => getSessionEngine,
|
|
14013
|
+
getSessionByName: () => getSessionByName2,
|
|
14014
|
+
getSessionBunView: () => getSessionBunView,
|
|
14015
|
+
getSessionBrowser: () => getSessionBrowser,
|
|
14016
|
+
getSession: () => getSession2,
|
|
14017
|
+
getDefaultSession: () => getDefaultSession,
|
|
14018
|
+
getActiveSessions: () => getActiveSessions,
|
|
14019
|
+
getActiveSessionForAgent: () => getActiveSessionForAgent2,
|
|
14020
|
+
createSession: () => createSession2,
|
|
14021
|
+
countActiveSessions: () => countActiveSessions2,
|
|
14022
|
+
closeSession: () => closeSession2,
|
|
14023
|
+
closeAllSessions: () => closeAllSessions,
|
|
14024
|
+
browserPool: () => pool
|
|
14025
|
+
});
|
|
14026
|
+
function createBunProxy(view) {
|
|
14027
|
+
return view;
|
|
14028
|
+
}
|
|
14029
|
+
async function createSession2(opts = {}) {
|
|
14030
|
+
if (opts.cdpUrl) {
|
|
14031
|
+
const { connectToExistingBrowser: connectToExistingBrowser2 } = await Promise.resolve().then(() => (init_cdp(), exports_cdp));
|
|
14032
|
+
const cdpBrowser = await connectToExistingBrowser2(opts.cdpUrl);
|
|
14033
|
+
const contexts = cdpBrowser.contexts();
|
|
14034
|
+
const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();
|
|
14035
|
+
const pages = context.pages();
|
|
14036
|
+
const page2 = pages.length > 0 ? pages[0] : await context.newPage();
|
|
14037
|
+
const session2 = createSession({
|
|
14038
|
+
engine: "cdp",
|
|
14039
|
+
projectId: opts.projectId,
|
|
14040
|
+
agentId: opts.agentId,
|
|
14041
|
+
startUrl: page2.url(),
|
|
14042
|
+
name: opts.name ?? "attached"
|
|
14043
|
+
});
|
|
14044
|
+
const cleanups2 = [];
|
|
14045
|
+
if (opts.captureNetwork !== false) {
|
|
14046
|
+
try {
|
|
14047
|
+
cleanups2.push(enableNetworkLogging(page2, session2.id));
|
|
14048
|
+
} catch {}
|
|
14049
|
+
}
|
|
14050
|
+
if (opts.captureConsole !== false) {
|
|
14051
|
+
try {
|
|
14052
|
+
cleanups2.push(enableConsoleCapture(page2, session2.id));
|
|
14053
|
+
} catch {}
|
|
14054
|
+
}
|
|
14055
|
+
try {
|
|
14056
|
+
cleanups2.push(setupDialogHandler(page2, session2.id));
|
|
14057
|
+
} catch {}
|
|
14058
|
+
handles.set(session2.id, { browser: cdpBrowser, bunView: null, page: page2, engine: "cdp", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
14059
|
+
return { session: session2, page: page2 };
|
|
14060
|
+
}
|
|
14061
|
+
const engine = opts.engine === "auto" || !opts.engine ? selectEngine(opts.useCase ?? "spa_navigate" /* SPA_NAVIGATE */, opts.engine) : opts.engine;
|
|
14062
|
+
const resolvedEngine = engine === "auto" ? "playwright" : engine;
|
|
14063
|
+
let browser = null;
|
|
14064
|
+
let bunView = null;
|
|
14065
|
+
let page;
|
|
14066
|
+
if (resolvedEngine === "bun") {
|
|
14067
|
+
if (!isBunWebViewAvailable()) {
|
|
14068
|
+
console.warn("[browser] Bun.WebView requested but not available \u2014 falling back to playwright. Run: bun upgrade --canary");
|
|
14069
|
+
browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
|
|
14070
|
+
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
14071
|
+
} else {
|
|
14072
|
+
bunView = new BunWebViewSession({
|
|
14073
|
+
width: opts.viewport?.width ?? 1280,
|
|
14074
|
+
height: opts.viewport?.height ?? 720,
|
|
14075
|
+
profile: opts.name ?? undefined
|
|
14076
|
+
});
|
|
14077
|
+
if (opts.stealth) {}
|
|
14078
|
+
page = createBunProxy(bunView);
|
|
14079
|
+
}
|
|
14080
|
+
} else if (resolvedEngine === "lightpanda") {
|
|
14081
|
+
browser = await connectLightpanda();
|
|
14082
|
+
const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
|
|
14083
|
+
page = await context.newPage();
|
|
14084
|
+
} else {
|
|
14085
|
+
browser = await pool.acquire(opts.headless ?? true);
|
|
14086
|
+
if (opts.storageState) {
|
|
14087
|
+
const { loadStatePath: loadStatePath2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
|
|
14088
|
+
const statePath2 = loadStatePath2(opts.storageState);
|
|
14089
|
+
if (statePath2) {
|
|
14090
|
+
const context = await browser.newContext({
|
|
14091
|
+
viewport: opts.viewport ?? { width: 1280, height: 720 },
|
|
14092
|
+
userAgent: opts.userAgent,
|
|
14093
|
+
storageState: statePath2
|
|
14094
|
+
});
|
|
14095
|
+
page = await context.newPage();
|
|
14096
|
+
} else {
|
|
14097
|
+
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
14098
|
+
}
|
|
14099
|
+
} else {
|
|
14100
|
+
page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
|
|
14101
|
+
}
|
|
14102
|
+
}
|
|
14103
|
+
const sessionName = opts.name ?? (opts.startUrl ? (() => {
|
|
14104
|
+
try {
|
|
14105
|
+
return new URL(opts.startUrl).hostname;
|
|
14106
|
+
} catch {
|
|
14107
|
+
return;
|
|
14108
|
+
}
|
|
14109
|
+
})() : undefined);
|
|
14110
|
+
const session = createSession({
|
|
14111
|
+
engine: bunView ? "bun" : browser ? resolvedEngine : resolvedEngine,
|
|
14112
|
+
projectId: opts.projectId,
|
|
14113
|
+
agentId: opts.agentId,
|
|
14114
|
+
startUrl: opts.startUrl,
|
|
14115
|
+
name: sessionName
|
|
14116
|
+
});
|
|
14117
|
+
if (opts.stealth && !bunView) {
|
|
14118
|
+
try {
|
|
14119
|
+
await applyStealthPatches(page);
|
|
14120
|
+
} catch {}
|
|
14121
|
+
}
|
|
14122
|
+
const cleanups = [];
|
|
14123
|
+
if (!bunView) {
|
|
14124
|
+
if (opts.captureNetwork !== false) {
|
|
14125
|
+
try {
|
|
14126
|
+
cleanups.push(enableNetworkLogging(page, session.id));
|
|
14127
|
+
} catch {}
|
|
14128
|
+
}
|
|
14129
|
+
if (opts.captureConsole !== false) {
|
|
14130
|
+
try {
|
|
14131
|
+
cleanups.push(enableConsoleCapture(page, session.id));
|
|
14132
|
+
} catch {}
|
|
14133
|
+
}
|
|
14134
|
+
try {
|
|
14135
|
+
cleanups.push(setupDialogHandler(page, session.id));
|
|
14136
|
+
} catch {}
|
|
14137
|
+
} else {
|
|
14138
|
+
if (opts.captureConsole !== false) {
|
|
14139
|
+
try {
|
|
14140
|
+
const { logConsoleMessage: logConsoleMessage2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
|
|
14141
|
+
await bunView.addInitScript(`
|
|
14142
|
+
(() => {
|
|
14143
|
+
const orig = { log: console.log, warn: console.warn, error: console.error, debug: console.debug, info: console.info };
|
|
14144
|
+
['log','warn','error','debug','info'].forEach(level => {
|
|
14145
|
+
console[level] = (...args) => {
|
|
14146
|
+
orig[level](...args);
|
|
14147
|
+
};
|
|
14148
|
+
});
|
|
14149
|
+
})()
|
|
14150
|
+
`);
|
|
14151
|
+
} catch {}
|
|
14152
|
+
}
|
|
14153
|
+
}
|
|
14154
|
+
handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
14155
|
+
if (opts.startUrl) {
|
|
14156
|
+
try {
|
|
14157
|
+
if (bunView) {
|
|
14158
|
+
await bunView.goto(opts.startUrl);
|
|
14159
|
+
} else {
|
|
14160
|
+
await page.goto(opts.startUrl, { waitUntil: "domcontentloaded" });
|
|
14161
|
+
}
|
|
14162
|
+
} catch {}
|
|
14163
|
+
}
|
|
14164
|
+
return { session, page };
|
|
14165
|
+
}
|
|
14166
|
+
function getSessionPage(sessionId) {
|
|
14167
|
+
const handle = handles.get(sessionId);
|
|
14168
|
+
if (!handle)
|
|
14169
|
+
throw new SessionNotFoundError(sessionId);
|
|
14170
|
+
try {
|
|
14171
|
+
if (handle.bunView) {
|
|
14172
|
+
handle.bunView.url();
|
|
14173
|
+
} else {
|
|
14174
|
+
handle.page.url();
|
|
14175
|
+
}
|
|
14176
|
+
} catch {
|
|
14177
|
+
handles.delete(sessionId);
|
|
14178
|
+
throw new SessionNotFoundError(sessionId);
|
|
14179
|
+
}
|
|
14180
|
+
handle.lastActivity = Date.now();
|
|
14181
|
+
return handle.page;
|
|
14182
|
+
}
|
|
14183
|
+
function getSessionBunView(sessionId) {
|
|
14184
|
+
return handles.get(sessionId)?.bunView ?? null;
|
|
14185
|
+
}
|
|
14186
|
+
function isBunSession(sessionId) {
|
|
14187
|
+
return handles.get(sessionId)?.engine === "bun";
|
|
14188
|
+
}
|
|
14189
|
+
function getSessionBrowser(sessionId) {
|
|
14190
|
+
const handle = handles.get(sessionId);
|
|
14191
|
+
if (!handle)
|
|
14192
|
+
throw new SessionNotFoundError(sessionId);
|
|
14193
|
+
if (!handle.browser)
|
|
14194
|
+
throw new BrowserError("This session uses Bun.WebView (no Playwright browser)", "NO_PLAYWRIGHT_BROWSER");
|
|
14195
|
+
return handle.browser;
|
|
14196
|
+
}
|
|
14197
|
+
function getSessionEngine(sessionId) {
|
|
14198
|
+
const handle = handles.get(sessionId);
|
|
14199
|
+
if (!handle)
|
|
14200
|
+
throw new SessionNotFoundError(sessionId);
|
|
14201
|
+
return handle.engine;
|
|
14202
|
+
}
|
|
14203
|
+
function hasActiveHandle(sessionId) {
|
|
14204
|
+
return handles.has(sessionId);
|
|
14205
|
+
}
|
|
14206
|
+
function setSessionPage(sessionId, page) {
|
|
14207
|
+
const handle = handles.get(sessionId);
|
|
14208
|
+
if (!handle)
|
|
14209
|
+
throw new SessionNotFoundError(sessionId);
|
|
14210
|
+
handle.page = page;
|
|
14211
|
+
}
|
|
14212
|
+
async function closeSession2(sessionId) {
|
|
14213
|
+
const handle = handles.get(sessionId);
|
|
14214
|
+
if (handle) {
|
|
14215
|
+
for (const cleanup of handle.cleanups) {
|
|
14216
|
+
try {
|
|
14217
|
+
cleanup();
|
|
14218
|
+
} catch {}
|
|
14219
|
+
}
|
|
14220
|
+
if (handle.bunView) {
|
|
14221
|
+
try {
|
|
14222
|
+
await handle.bunView.close();
|
|
14223
|
+
} catch {}
|
|
14224
|
+
} else {
|
|
14225
|
+
try {
|
|
14226
|
+
await handle.page.context().close();
|
|
14227
|
+
} catch {}
|
|
14228
|
+
if (handle.browser)
|
|
14229
|
+
pool.release(handle.browser);
|
|
14230
|
+
}
|
|
14231
|
+
handles.delete(sessionId);
|
|
14232
|
+
}
|
|
14233
|
+
try {
|
|
14234
|
+
const { clearLastSnapshot: clearLastSnapshot2, clearSessionRefs: clearSessionRefs2 } = await Promise.resolve().then(() => (init_snapshot(), exports_snapshot));
|
|
14235
|
+
clearLastSnapshot2(sessionId);
|
|
14236
|
+
clearSessionRefs2(sessionId);
|
|
14237
|
+
} catch {}
|
|
14238
|
+
try {
|
|
14239
|
+
const { stopAllWatchesForSession: stopAllWatchesForSession2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
|
|
14240
|
+
stopAllWatchesForSession2(sessionId);
|
|
14241
|
+
} catch {}
|
|
14242
|
+
try {
|
|
14243
|
+
const { clearDialogs: clearDialogs2 } = await Promise.resolve().then(() => (init_dialogs(), exports_dialogs));
|
|
14244
|
+
clearDialogs2(sessionId);
|
|
14245
|
+
} catch {}
|
|
14246
|
+
return closeSession(sessionId);
|
|
14247
|
+
}
|
|
14248
|
+
function getSession2(sessionId) {
|
|
14249
|
+
return getSession(sessionId);
|
|
14250
|
+
}
|
|
14251
|
+
function listSessions2(filter) {
|
|
14252
|
+
return listSessions(filter);
|
|
14253
|
+
}
|
|
14254
|
+
function getActiveSessions() {
|
|
14255
|
+
return listSessions({ status: "active" });
|
|
14256
|
+
}
|
|
14257
|
+
async function closeAllSessions() {
|
|
14258
|
+
for (const [id] of handles) {
|
|
14259
|
+
await closeSession2(id).catch(() => {});
|
|
14260
|
+
}
|
|
14261
|
+
await pool.destroyAll();
|
|
14262
|
+
}
|
|
14263
|
+
function getSessionByName2(name) {
|
|
14264
|
+
return getSessionByName(name);
|
|
14265
|
+
}
|
|
14266
|
+
function renameSession2(id, name) {
|
|
14267
|
+
return renameSession(id, name);
|
|
14268
|
+
}
|
|
14269
|
+
function getTokenBudget(sessionId) {
|
|
14270
|
+
const handle = handles.get(sessionId);
|
|
14271
|
+
return handle ? handle.tokenBudget : null;
|
|
14272
|
+
}
|
|
14273
|
+
function getActiveSessionForAgent2(agentId) {
|
|
14274
|
+
const session = getActiveSessionForAgent(agentId);
|
|
14275
|
+
if (!session)
|
|
14276
|
+
return null;
|
|
14277
|
+
const handle = handles.get(session.id);
|
|
14278
|
+
if (!handle)
|
|
14279
|
+
return null;
|
|
14280
|
+
try {
|
|
14281
|
+
if (handle.bunView)
|
|
14282
|
+
handle.bunView.url();
|
|
14283
|
+
else
|
|
14284
|
+
handle.page.url();
|
|
14285
|
+
} catch {
|
|
14286
|
+
handles.delete(session.id);
|
|
14287
|
+
return null;
|
|
14288
|
+
}
|
|
14289
|
+
return { session, page: handle.page };
|
|
14290
|
+
}
|
|
14291
|
+
function getDefaultSession() {
|
|
14292
|
+
const session = getDefaultActiveSession();
|
|
14293
|
+
if (!session)
|
|
14294
|
+
return null;
|
|
14295
|
+
const handle = handles.get(session.id);
|
|
14296
|
+
if (!handle)
|
|
14297
|
+
return null;
|
|
14298
|
+
try {
|
|
14299
|
+
if (handle.bunView)
|
|
14300
|
+
handle.bunView.url();
|
|
14301
|
+
else
|
|
14302
|
+
handle.page.url();
|
|
14303
|
+
} catch {
|
|
14304
|
+
handles.delete(session.id);
|
|
14305
|
+
return null;
|
|
14306
|
+
}
|
|
14307
|
+
return { session, page: handle.page };
|
|
14308
|
+
}
|
|
14309
|
+
function isAutoGallery(sessionId) {
|
|
14310
|
+
return handles.get(sessionId)?.autoGallery ?? false;
|
|
14311
|
+
}
|
|
14312
|
+
function countActiveSessions2() {
|
|
14313
|
+
return countActiveSessions();
|
|
14314
|
+
}
|
|
14315
|
+
var handles, pool, SESSION_TTL_MS, ttlInterval, DB_PRUNE_INTERVAL_MS, DB_RETENTION_HOURS = 24, dbPruneInterval;
|
|
14316
|
+
var init_session = __esm(() => {
|
|
14317
|
+
init_types();
|
|
14318
|
+
init_types();
|
|
14319
|
+
init_sessions();
|
|
14320
|
+
init_playwright();
|
|
14321
|
+
init_lightpanda();
|
|
14322
|
+
init_bun_webview();
|
|
14323
|
+
init_selector();
|
|
14324
|
+
init_network();
|
|
14325
|
+
init_console();
|
|
14326
|
+
init_stealth();
|
|
14327
|
+
init_dialogs();
|
|
14328
|
+
handles = new Map;
|
|
14329
|
+
pool = new BrowserPool(5);
|
|
14330
|
+
SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
|
|
14331
|
+
ttlInterval = setInterval(async () => {
|
|
14332
|
+
const now = Date.now();
|
|
14333
|
+
for (const [id, handle] of handles) {
|
|
14334
|
+
if (now - handle.lastActivity > SESSION_TTL_MS) {
|
|
14335
|
+
try {
|
|
14336
|
+
await closeSession2(id);
|
|
14337
|
+
} catch {}
|
|
14338
|
+
}
|
|
14339
|
+
}
|
|
14340
|
+
}, 60000);
|
|
14341
|
+
if (ttlInterval.unref)
|
|
14342
|
+
ttlInterval.unref();
|
|
14343
|
+
DB_PRUNE_INTERVAL_MS = 30 * 60000;
|
|
14344
|
+
dbPruneInterval = setInterval(() => {
|
|
14345
|
+
try {
|
|
14346
|
+
const { getDatabase: getDatabase2 } = (init_schema(), __toCommonJS(exports_schema));
|
|
14347
|
+
const db = getDatabase2();
|
|
14348
|
+
const cutoff = new Date(Date.now() - DB_RETENTION_HOURS * 3600000).toISOString();
|
|
14349
|
+
db.prepare("DELETE FROM network_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
14350
|
+
db.prepare("DELETE FROM console_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
14351
|
+
db.prepare("DELETE FROM snapshots WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
|
|
14352
|
+
} catch {}
|
|
14353
|
+
}, DB_PRUNE_INTERVAL_MS);
|
|
14354
|
+
if (dbPruneInterval.unref)
|
|
14355
|
+
dbPruneInterval.unref();
|
|
14356
|
+
});
|
|
14357
|
+
|
|
14323
14358
|
// src/lib/extractor.ts
|
|
14324
14359
|
var exports_extractor = {};
|
|
14325
14360
|
__export(exports_extractor, {
|
|
@@ -37108,6 +37143,11 @@ async function rememberPage(url, facts, tags) {
|
|
|
37108
37143
|
return;
|
|
37109
37144
|
} catch {}
|
|
37110
37145
|
}
|
|
37146
|
+
if (inMemoryCache.size >= MEMORY_MAX_SIZE && !inMemoryCache.has(key)) {
|
|
37147
|
+
const firstKey = inMemoryCache.keys().next().value;
|
|
37148
|
+
if (firstKey)
|
|
37149
|
+
inMemoryCache.delete(firstKey);
|
|
37150
|
+
}
|
|
37111
37151
|
inMemoryCache.set(key, {
|
|
37112
37152
|
data: memory,
|
|
37113
37153
|
expires: Date.now() + DEFAULT_TTL_HOURS * 60 * 60 * 1000
|
|
@@ -37137,9 +37177,18 @@ async function forgetPage(url) {
|
|
|
37137
37177
|
const key = cacheKey(url);
|
|
37138
37178
|
inMemoryCache.delete(key);
|
|
37139
37179
|
}
|
|
37140
|
-
var MEMORY_KEY_PREFIX = "browser-page:", DEFAULT_TTL_HOURS = 24, inMemoryCache;
|
|
37180
|
+
var MEMORY_KEY_PREFIX = "browser-page:", DEFAULT_TTL_HOURS = 24, inMemoryCache, MEMORY_MAX_SIZE = 200, _memorySweeper;
|
|
37141
37181
|
var init_page_memory = __esm(() => {
|
|
37142
37182
|
inMemoryCache = new Map;
|
|
37183
|
+
_memorySweeper = setInterval(() => {
|
|
37184
|
+
const now2 = Date.now();
|
|
37185
|
+
for (const [key, entry] of inMemoryCache) {
|
|
37186
|
+
if (entry.expires <= now2)
|
|
37187
|
+
inMemoryCache.delete(key);
|
|
37188
|
+
}
|
|
37189
|
+
}, 300000);
|
|
37190
|
+
if (_memorySweeper.unref)
|
|
37191
|
+
_memorySweeper.unref();
|
|
37143
37192
|
});
|
|
37144
37193
|
|
|
37145
37194
|
// node_modules/@hasna/conversations/dist/index.js
|
|
@@ -41603,6 +41652,11 @@ async function announceNavigation(url, sessionId, agentName = "browser-agent") {
|
|
|
41603
41652
|
await sdk.sendMessage(SPACE_NAME, `\uD83C\uDF10 Navigating to ${hostname} (session: ${sessionId.slice(0, 8)})`);
|
|
41604
41653
|
} catch {}
|
|
41605
41654
|
}
|
|
41655
|
+
if (activeNavigations.size >= NAV_MAX_SIZE && !activeNavigations.has(hostname)) {
|
|
41656
|
+
const firstKey = activeNavigations.keys().next().value;
|
|
41657
|
+
if (firstKey)
|
|
41658
|
+
activeNavigations.delete(firstKey);
|
|
41659
|
+
}
|
|
41606
41660
|
activeNavigations.set(hostname, { agentName, timestamp: Date.now() });
|
|
41607
41661
|
}
|
|
41608
41662
|
async function checkDuplicate2(url) {
|
|
@@ -41638,10 +41692,19 @@ async function checkDuplicate2(url) {
|
|
|
41638
41692
|
}
|
|
41639
41693
|
return { is_duplicate: false };
|
|
41640
41694
|
}
|
|
41641
|
-
var SPACE_NAME = "browser", DUPLICATE_WINDOW_MS, activeNavigations;
|
|
41695
|
+
var SPACE_NAME = "browser", DUPLICATE_WINDOW_MS, activeNavigations, NAV_MAX_SIZE = 200, _navSweeper;
|
|
41642
41696
|
var init_coordination = __esm(() => {
|
|
41643
41697
|
DUPLICATE_WINDOW_MS = 5 * 60 * 1000;
|
|
41644
41698
|
activeNavigations = new Map;
|
|
41699
|
+
_navSweeper = setInterval(() => {
|
|
41700
|
+
const cutoff = Date.now() - DUPLICATE_WINDOW_MS;
|
|
41701
|
+
for (const [key, entry] of activeNavigations) {
|
|
41702
|
+
if (entry.timestamp < cutoff)
|
|
41703
|
+
activeNavigations.delete(key);
|
|
41704
|
+
}
|
|
41705
|
+
}, 120000);
|
|
41706
|
+
if (_navSweeper.unref)
|
|
41707
|
+
_navSweeper.unref();
|
|
41645
41708
|
});
|
|
41646
41709
|
|
|
41647
41710
|
// node_modules/@hasna/todos/dist/index.js
|
|
@@ -47244,6 +47307,8 @@ Skill: ${task.skill}` : ""}`,
|
|
|
47244
47307
|
};
|
|
47245
47308
|
} catch {}
|
|
47246
47309
|
}
|
|
47310
|
+
if (inMemoryQueue.length >= QUEUE_MAX_SIZE)
|
|
47311
|
+
inMemoryQueue.shift();
|
|
47247
47312
|
const id = `btask-${Date.now()}`;
|
|
47248
47313
|
const entry = { ...task, id, status: "pending", created_at: new Date().toISOString() };
|
|
47249
47314
|
inMemoryQueue.push(entry);
|
|
@@ -47277,7 +47342,7 @@ async function completeBrowserTask(taskId, result) {
|
|
|
47277
47342
|
if (idx >= 0)
|
|
47278
47343
|
inMemoryQueue.splice(idx, 1);
|
|
47279
47344
|
}
|
|
47280
|
-
var inMemoryQueue;
|
|
47345
|
+
var QUEUE_MAX_SIZE = 100, inMemoryQueue;
|
|
47281
47346
|
var init_task_queue = __esm(() => {
|
|
47282
47347
|
inMemoryQueue = [];
|
|
47283
47348
|
});
|