@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 CHANGED
@@ -12811,7 +12811,8 @@ function startHAR(page) {
12811
12811
  },
12812
12812
  timings: { send: 0, wait: duration, receive: 0 }
12813
12813
  };
12814
- entries.push(entry);
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
  });