@hasna/browser 0.3.7 → 0.4.0

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
@@ -6,60 +6,39 @@ var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- function __accessProp(key) {
10
- return this[key];
11
- }
12
- var __toESMCache_node;
13
- var __toESMCache_esm;
14
9
  var __toESM = (mod, isNodeMode, target) => {
15
- var canCache = mod != null && typeof mod === "object";
16
- if (canCache) {
17
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
18
- var cached = cache.get(mod);
19
- if (cached)
20
- return cached;
21
- }
22
10
  target = mod != null ? __create(__getProtoOf(mod)) : {};
23
11
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
24
12
  for (let key of __getOwnPropNames(mod))
25
13
  if (!__hasOwnProp.call(to, key))
26
14
  __defProp(to, key, {
27
- get: __accessProp.bind(mod, key),
15
+ get: () => mod[key],
28
16
  enumerable: true
29
17
  });
30
- if (canCache)
31
- cache.set(mod, to);
32
18
  return to;
33
19
  };
20
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
34
21
  var __toCommonJS = (from) => {
35
- var entry = (__moduleCache ??= new WeakMap).get(from), desc;
22
+ var entry = __moduleCache.get(from), desc;
36
23
  if (entry)
37
24
  return entry;
38
25
  entry = __defProp({}, "__esModule", { value: true });
39
- if (from && typeof from === "object" || typeof from === "function") {
40
- for (var key of __getOwnPropNames(from))
41
- if (!__hasOwnProp.call(entry, key))
42
- __defProp(entry, key, {
43
- get: __accessProp.bind(from, key),
44
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
45
- });
46
- }
26
+ if (from && typeof from === "object" || typeof from === "function")
27
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
28
+ get: () => from[key],
29
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
30
+ }));
47
31
  __moduleCache.set(from, entry);
48
32
  return entry;
49
33
  };
50
- var __moduleCache;
51
34
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
52
- var __returnValue = (v) => v;
53
- function __exportSetter(name, newValue) {
54
- this[name] = __returnValue.bind(null, newValue);
55
- }
56
35
  var __export = (target, all) => {
57
36
  for (var name in all)
58
37
  __defProp(target, name, {
59
38
  get: all[name],
60
39
  enumerable: true,
61
40
  configurable: true,
62
- set: __exportSetter.bind(all, name)
41
+ set: (newValue) => all[name] = () => newValue
63
42
  });
64
43
  };
65
44
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -2177,11 +2156,11 @@ import { homedir as homedir4 } from "os";
2177
2156
  import { join as join4 } from "path";
2178
2157
  import { join as join6, dirname } from "path";
2179
2158
  import { homedir as homedir5, platform } from "os";
2180
- function __accessProp2(key) {
2159
+ function __accessProp(key) {
2181
2160
  return this[key];
2182
2161
  }
2183
- function __exportSetter2(name, newValue) {
2184
- this[name] = __returnValue2.bind(null, newValue);
2162
+ function __exportSetter(name, newValue) {
2163
+ this[name] = __returnValue.bind(null, newValue);
2185
2164
  }
2186
2165
  function translateSql(sql, dialect) {
2187
2166
  if (dialect === "sqlite")
@@ -3211,10 +3190,10 @@ class SyncProgressTracker {
3211
3190
  }
3212
3191
  }
3213
3192
  }
3214
- var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node2, __toESMCache_esm2, __toESM2 = (mod, isNodeMode, target) => {
3193
+ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node, __toESMCache_esm, __toESM2 = (mod, isNodeMode, target) => {
3215
3194
  var canCache = mod != null && typeof mod === "object";
3216
3195
  if (canCache) {
3217
- var cache = isNodeMode ? __toESMCache_node2 ??= new WeakMap : __toESMCache_esm2 ??= new WeakMap;
3196
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
3218
3197
  var cached = cache.get(mod);
3219
3198
  if (cached)
3220
3199
  return cached;
@@ -3224,19 +3203,19 @@ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __t
3224
3203
  for (let key of __getOwnPropNames2(mod))
3225
3204
  if (!__hasOwnProp2.call(to, key))
3226
3205
  __defProp2(to, key, {
3227
- get: __accessProp2.bind(mod, key),
3206
+ get: __accessProp.bind(mod, key),
3228
3207
  enumerable: true
3229
3208
  });
3230
3209
  if (canCache)
3231
3210
  cache.set(mod, to);
3232
3211
  return to;
3233
- }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue2 = (v) => v, __export2 = (target, all) => {
3212
+ }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue = (v) => v, __export2 = (target, all) => {
3234
3213
  for (var name in all)
3235
3214
  __defProp2(target, name, {
3236
3215
  get: all[name],
3237
3216
  enumerable: true,
3238
3217
  configurable: true,
3239
- set: __exportSetter2.bind(all, name)
3218
+ set: __exportSetter.bind(all, name)
3240
3219
  });
3241
3220
  }, __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), __require2, require_postgres_array, require_arrayParser, require_postgres_date, require_mutable, require_postgres_interval, require_postgres_bytea, require_textParsers, require_pg_int8, require_binaryParsers, require_builtins, require_pg_types, require_defaults, require_utils, require_utils_legacy, require_utils_webcrypto, require_utils2, require_cert_signatures, require_sasl, require_type_overrides, require_pg_connection_string, require_connection_parameters, require_result, require_query, require_messages, require_buffer_writer, require_serializer, require_buffer_reader, require_parser, require_dist, require_empty, require_stream, require_connection, require_split2, require_helper, require_lib, require_client, require_pg_pool, require_query2, require_client2, require_lib2, import_lib, Client, Pool, Connection, types, Query, DatabaseError, escapeIdentifier, escapeLiteral, Result, TypeOverrides, defaults, esm_default, init_esm, init_adapter, util, objectUtil, ZodParsedType, getParsedType = (data) => {
3242
3221
  const t = typeof data;
@@ -12653,6 +12632,89 @@ var init_bun_webview = __esm(() => {
12653
12632
  };
12654
12633
  });
12655
12634
 
12635
+ // src/engines/tui.ts
12636
+ import { execSync as execSync2, spawn as spawn2 } from "child_process";
12637
+ function isTuiAvailable() {
12638
+ try {
12639
+ execSync2("which ttyd", { stdio: "ignore" });
12640
+ return true;
12641
+ } catch {
12642
+ return false;
12643
+ }
12644
+ }
12645
+ async function findAvailablePort(startPort) {
12646
+ let port = startPort;
12647
+ for (let i = 0;i < 100; i++) {
12648
+ try {
12649
+ const resp = await fetch(`http://localhost:${port}`);
12650
+ port++;
12651
+ } catch {
12652
+ return port;
12653
+ }
12654
+ }
12655
+ throw new BrowserError("No available port found for ttyd", "TUI_PORT_EXHAUSTED");
12656
+ }
12657
+ async function waitForTtyd(port, timeoutMs = 1e4) {
12658
+ const start = Date.now();
12659
+ while (Date.now() - start < timeoutMs) {
12660
+ try {
12661
+ const resp = await fetch(`http://localhost:${port}`);
12662
+ if (resp.ok || resp.status === 200)
12663
+ return;
12664
+ } catch {}
12665
+ await new Promise((r) => setTimeout(r, 150));
12666
+ }
12667
+ throw new BrowserError(`ttyd did not start within ${timeoutMs}ms`, "TUI_TIMEOUT");
12668
+ }
12669
+ async function launchTui(command, options = {}) {
12670
+ if (!isTuiAvailable()) {
12671
+ throw new BrowserError("ttyd not found \u2014 install with: brew install ttyd", "TUI_NOT_AVAILABLE");
12672
+ }
12673
+ const port = await findAvailablePort(nextPort);
12674
+ nextPort = port + 1;
12675
+ const ttydProcess = spawn2("ttyd", ["--writable", "--port", String(port), "/bin/sh", "-c", command], {
12676
+ stdio: "ignore",
12677
+ detached: false
12678
+ });
12679
+ ttydProcess.on("error", (err) => {
12680
+ console.error(`[tui] ttyd process error: ${err.message}`);
12681
+ });
12682
+ try {
12683
+ await waitForTtyd(port);
12684
+ const viewport = options.viewport ?? { width: 1280, height: 720 };
12685
+ const browser = await launchPlaywright({
12686
+ headless: options.headless ?? true,
12687
+ viewport
12688
+ });
12689
+ const page = await getPage(browser, { viewport });
12690
+ await page.goto(`http://localhost:${port}`, {
12691
+ waitUntil: "domcontentloaded"
12692
+ });
12693
+ await page.waitForSelector(".xterm-screen", { timeout: 1e4 });
12694
+ return { ttydProcess, port, browser, page };
12695
+ } catch (err) {
12696
+ ttydProcess.kill();
12697
+ throw err;
12698
+ }
12699
+ }
12700
+ async function closeTui(session) {
12701
+ try {
12702
+ await session.page.close();
12703
+ } catch {}
12704
+ try {
12705
+ await session.browser.close();
12706
+ } catch {}
12707
+ try {
12708
+ session.ttydProcess.kill("SIGTERM");
12709
+ } catch {}
12710
+ }
12711
+ var DEFAULT_TTYD_PORT_START = 7780, nextPort;
12712
+ var init_tui = __esm(() => {
12713
+ init_types();
12714
+ init_playwright();
12715
+ nextPort = DEFAULT_TTYD_PORT_START;
12716
+ });
12717
+
12656
12718
  // src/engines/selector.ts
12657
12719
  function selectEngine(useCase, explicit) {
12658
12720
  if (explicit && explicit !== "auto")
@@ -12676,6 +12738,7 @@ var init_selector = __esm(() => {
12676
12738
  init_types();
12677
12739
  init_lightpanda();
12678
12740
  init_bun_webview();
12741
+ init_tui();
12679
12742
  ENGINE_MAP = {
12680
12743
  ["scrape" /* SCRAPE */]: "bun",
12681
12744
  ["extract_links" /* EXTRACT_LINKS */]: "bun",
@@ -12686,6 +12749,7 @@ var init_selector = __esm(() => {
12686
12749
  ["auth_flow" /* AUTH_FLOW */]: "playwright",
12687
12750
  ["multi_tab" /* MULTI_TAB */]: "playwright",
12688
12751
  ["record_replay" /* RECORD_REPLAY */]: "playwright",
12752
+ ["terminal_test" /* TERMINAL_TEST */]: "tui",
12689
12753
  ["network_monitor" /* NETWORK_MONITOR */]: "cdp",
12690
12754
  ["har_capture" /* HAR_CAPTURE */]: "cdp",
12691
12755
  ["perf_profile" /* PERF_PROFILE */]: "cdp",
@@ -12811,7 +12875,8 @@ function startHAR(page) {
12811
12875
  },
12812
12876
  timings: { send: 0, wait: duration, receive: 0 }
12813
12877
  };
12814
- entries.push(entry);
12878
+ if (entries.length < HAR_MAX_ENTRIES)
12879
+ entries.push(entry);
12815
12880
  };
12816
12881
  const onRequestFailed = (req) => {
12817
12882
  requestStart.delete(req.url() + req.method());
@@ -12836,6 +12901,7 @@ function startHAR(page) {
12836
12901
  }
12837
12902
  };
12838
12903
  }
12904
+ var HAR_MAX_ENTRIES = 5000;
12839
12905
  var init_network = __esm(() => {
12840
12906
  init_network_log();
12841
12907
  });
@@ -13238,337 +13304,6 @@ var init_storage_state = __esm(() => {
13238
13304
  STATES_DIR = join8(getDataDir2(), "states");
13239
13305
  });
13240
13306
 
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
13307
  // src/lib/snapshot.ts
13573
13308
  var exports_snapshot = {};
13574
13309
  __export(exports_snapshot, {
@@ -13919,6 +13654,7 @@ __export(exports_actions, {
13919
13654
  typeRef: () => typeRef,
13920
13655
  type: () => type,
13921
13656
  stopWatch: () => stopWatch,
13657
+ stopAllWatchesForSession: () => stopAllWatchesForSession,
13922
13658
  selectRef: () => selectRef,
13923
13659
  selectOption: () => selectOption,
13924
13660
  scrollTo: () => scrollTo,
@@ -14245,6 +13981,12 @@ function stopWatch(watchId) {
14245
13981
  activeWatches.delete(watchId);
14246
13982
  }
14247
13983
  }
13984
+ function stopAllWatchesForSession(_sessionId) {
13985
+ for (const [id, w] of activeWatches) {
13986
+ clearInterval(w.interval);
13987
+ activeWatches.delete(id);
13988
+ }
13989
+ }
14248
13990
  async function clickRef(page, sessionId, ref, opts) {
14249
13991
  try {
14250
13992
  const locator = getRefLocator(page, sessionId, ref);
@@ -14320,6 +14062,396 @@ var init_actions = __esm(() => {
14320
14062
  activeWatches = new Map;
14321
14063
  });
14322
14064
 
14065
+ // src/lib/session.ts
14066
+ var exports_session = {};
14067
+ __export(exports_session, {
14068
+ setSessionPage: () => setSessionPage,
14069
+ renameSession: () => renameSession2,
14070
+ listSessions: () => listSessions2,
14071
+ isBunSession: () => isBunSession,
14072
+ isAutoGallery: () => isAutoGallery,
14073
+ hasActiveHandle: () => hasActiveHandle,
14074
+ getTokenBudget: () => getTokenBudget,
14075
+ getSessionPage: () => getSessionPage,
14076
+ getSessionEngine: () => getSessionEngine,
14077
+ getSessionByName: () => getSessionByName2,
14078
+ getSessionBunView: () => getSessionBunView,
14079
+ getSessionBrowser: () => getSessionBrowser,
14080
+ getSession: () => getSession2,
14081
+ getDefaultSession: () => getDefaultSession,
14082
+ getActiveSessions: () => getActiveSessions,
14083
+ getActiveSessionForAgent: () => getActiveSessionForAgent2,
14084
+ createSession: () => createSession2,
14085
+ countActiveSessions: () => countActiveSessions2,
14086
+ closeSession: () => closeSession2,
14087
+ closeAllSessions: () => closeAllSessions,
14088
+ browserPool: () => pool
14089
+ });
14090
+ function createBunProxy(view) {
14091
+ return view;
14092
+ }
14093
+ async function createSession2(opts = {}) {
14094
+ if (opts.cdpUrl) {
14095
+ const { connectToExistingBrowser: connectToExistingBrowser2 } = await Promise.resolve().then(() => (init_cdp(), exports_cdp));
14096
+ const cdpBrowser = await connectToExistingBrowser2(opts.cdpUrl);
14097
+ const contexts = cdpBrowser.contexts();
14098
+ const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();
14099
+ const pages = context.pages();
14100
+ const page2 = pages.length > 0 ? pages[0] : await context.newPage();
14101
+ const session2 = createSession({
14102
+ engine: "cdp",
14103
+ projectId: opts.projectId,
14104
+ agentId: opts.agentId,
14105
+ startUrl: page2.url(),
14106
+ name: opts.name ?? "attached"
14107
+ });
14108
+ const cleanups2 = [];
14109
+ if (opts.captureNetwork !== false) {
14110
+ try {
14111
+ cleanups2.push(enableNetworkLogging(page2, session2.id));
14112
+ } catch {}
14113
+ }
14114
+ if (opts.captureConsole !== false) {
14115
+ try {
14116
+ cleanups2.push(enableConsoleCapture(page2, session2.id));
14117
+ } catch {}
14118
+ }
14119
+ try {
14120
+ cleanups2.push(setupDialogHandler(page2, session2.id));
14121
+ } catch {}
14122
+ handles.set(session2.id, { browser: cdpBrowser, bunView: null, tuiSession: null, page: page2, engine: "cdp", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
14123
+ return { session: session2, page: page2 };
14124
+ }
14125
+ const engine = opts.engine === "auto" || !opts.engine ? selectEngine(opts.useCase ?? "spa_navigate" /* SPA_NAVIGATE */, opts.engine) : opts.engine;
14126
+ const resolvedEngine = engine === "auto" ? "playwright" : engine;
14127
+ let browser = null;
14128
+ let bunView = null;
14129
+ let page;
14130
+ if (resolvedEngine === "bun") {
14131
+ if (!isBunWebViewAvailable()) {
14132
+ console.warn("[browser] Bun.WebView requested but not available \u2014 falling back to playwright. Run: bun upgrade --canary");
14133
+ browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
14134
+ page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
14135
+ } else {
14136
+ bunView = new BunWebViewSession({
14137
+ width: opts.viewport?.width ?? 1280,
14138
+ height: opts.viewport?.height ?? 720,
14139
+ profile: opts.name ?? undefined
14140
+ });
14141
+ if (opts.stealth) {}
14142
+ page = createBunProxy(bunView);
14143
+ }
14144
+ } else if (resolvedEngine === "lightpanda") {
14145
+ browser = await connectLightpanda();
14146
+ const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
14147
+ page = await context.newPage();
14148
+ } else if (resolvedEngine === "tui") {
14149
+ const command = opts.startUrl ?? "bash";
14150
+ const tuiSess = await launchTui(command, {
14151
+ headless: opts.headless ?? true,
14152
+ viewport: opts.viewport
14153
+ });
14154
+ browser = tuiSess.browser;
14155
+ page = tuiSess.page;
14156
+ const session2 = createSession({
14157
+ engine: "tui",
14158
+ projectId: opts.projectId,
14159
+ agentId: opts.agentId,
14160
+ startUrl: opts.startUrl,
14161
+ name: opts.name ?? "tui"
14162
+ });
14163
+ const cleanups2 = [];
14164
+ cleanups2.push(() => closeTui(tuiSess));
14165
+ if (opts.captureNetwork !== false) {
14166
+ try {
14167
+ cleanups2.push(enableNetworkLogging(page, session2.id));
14168
+ } catch {}
14169
+ }
14170
+ if (opts.captureConsole !== false) {
14171
+ try {
14172
+ cleanups2.push(enableConsoleCapture(page, session2.id));
14173
+ } catch {}
14174
+ }
14175
+ try {
14176
+ cleanups2.push(setupDialogHandler(page, session2.id));
14177
+ } catch {}
14178
+ handles.set(session2.id, { browser, bunView: null, tuiSession: tuiSess, page, engine: "tui", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
14179
+ return { session: session2, page };
14180
+ } else {
14181
+ browser = await pool.acquire(opts.headless ?? true);
14182
+ if (opts.storageState) {
14183
+ const { loadStatePath: loadStatePath2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
14184
+ const statePath2 = loadStatePath2(opts.storageState);
14185
+ if (statePath2) {
14186
+ const context = await browser.newContext({
14187
+ viewport: opts.viewport ?? { width: 1280, height: 720 },
14188
+ userAgent: opts.userAgent,
14189
+ storageState: statePath2
14190
+ });
14191
+ page = await context.newPage();
14192
+ } else {
14193
+ page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
14194
+ }
14195
+ } else {
14196
+ page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
14197
+ }
14198
+ }
14199
+ const sessionName = opts.name ?? (opts.startUrl ? (() => {
14200
+ try {
14201
+ return new URL(opts.startUrl).hostname;
14202
+ } catch {
14203
+ return;
14204
+ }
14205
+ })() : undefined);
14206
+ const session = createSession({
14207
+ engine: bunView ? "bun" : browser ? resolvedEngine : resolvedEngine,
14208
+ projectId: opts.projectId,
14209
+ agentId: opts.agentId,
14210
+ startUrl: opts.startUrl,
14211
+ name: sessionName
14212
+ });
14213
+ if (opts.stealth && !bunView) {
14214
+ try {
14215
+ await applyStealthPatches(page);
14216
+ } catch {}
14217
+ }
14218
+ const cleanups = [];
14219
+ if (!bunView) {
14220
+ if (opts.captureNetwork !== false) {
14221
+ try {
14222
+ cleanups.push(enableNetworkLogging(page, session.id));
14223
+ } catch {}
14224
+ }
14225
+ if (opts.captureConsole !== false) {
14226
+ try {
14227
+ cleanups.push(enableConsoleCapture(page, session.id));
14228
+ } catch {}
14229
+ }
14230
+ try {
14231
+ cleanups.push(setupDialogHandler(page, session.id));
14232
+ } catch {}
14233
+ } else {
14234
+ if (opts.captureConsole !== false) {
14235
+ try {
14236
+ const { logConsoleMessage: logConsoleMessage2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
14237
+ await bunView.addInitScript(`
14238
+ (() => {
14239
+ const orig = { log: console.log, warn: console.warn, error: console.error, debug: console.debug, info: console.info };
14240
+ ['log','warn','error','debug','info'].forEach(level => {
14241
+ console[level] = (...args) => {
14242
+ orig[level](...args);
14243
+ };
14244
+ });
14245
+ })()
14246
+ `);
14247
+ } catch {}
14248
+ }
14249
+ }
14250
+ handles.set(session.id, { browser, bunView, tuiSession: null, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
14251
+ if (opts.startUrl) {
14252
+ try {
14253
+ if (bunView) {
14254
+ await bunView.goto(opts.startUrl);
14255
+ } else {
14256
+ await page.goto(opts.startUrl, { waitUntil: "domcontentloaded" });
14257
+ }
14258
+ } catch {}
14259
+ }
14260
+ return { session, page };
14261
+ }
14262
+ function getSessionPage(sessionId) {
14263
+ const handle = handles.get(sessionId);
14264
+ if (!handle)
14265
+ throw new SessionNotFoundError(sessionId);
14266
+ try {
14267
+ if (handle.bunView) {
14268
+ handle.bunView.url();
14269
+ } else {
14270
+ handle.page.url();
14271
+ }
14272
+ } catch {
14273
+ handles.delete(sessionId);
14274
+ throw new SessionNotFoundError(sessionId);
14275
+ }
14276
+ handle.lastActivity = Date.now();
14277
+ return handle.page;
14278
+ }
14279
+ function getSessionBunView(sessionId) {
14280
+ return handles.get(sessionId)?.bunView ?? null;
14281
+ }
14282
+ function isBunSession(sessionId) {
14283
+ return handles.get(sessionId)?.engine === "bun";
14284
+ }
14285
+ function getSessionBrowser(sessionId) {
14286
+ const handle = handles.get(sessionId);
14287
+ if (!handle)
14288
+ throw new SessionNotFoundError(sessionId);
14289
+ if (!handle.browser)
14290
+ throw new BrowserError("This session uses Bun.WebView (no Playwright browser)", "NO_PLAYWRIGHT_BROWSER");
14291
+ return handle.browser;
14292
+ }
14293
+ function getSessionEngine(sessionId) {
14294
+ const handle = handles.get(sessionId);
14295
+ if (!handle)
14296
+ throw new SessionNotFoundError(sessionId);
14297
+ return handle.engine;
14298
+ }
14299
+ function hasActiveHandle(sessionId) {
14300
+ return handles.has(sessionId);
14301
+ }
14302
+ function setSessionPage(sessionId, page) {
14303
+ const handle = handles.get(sessionId);
14304
+ if (!handle)
14305
+ throw new SessionNotFoundError(sessionId);
14306
+ handle.page = page;
14307
+ }
14308
+ async function closeSession2(sessionId) {
14309
+ const handle = handles.get(sessionId);
14310
+ if (handle) {
14311
+ for (const cleanup of handle.cleanups) {
14312
+ try {
14313
+ cleanup();
14314
+ } catch {}
14315
+ }
14316
+ if (handle.bunView) {
14317
+ try {
14318
+ await handle.bunView.close();
14319
+ } catch {}
14320
+ } else if (handle.tuiSession) {} else {
14321
+ try {
14322
+ await handle.page.context().close();
14323
+ } catch {}
14324
+ if (handle.browser)
14325
+ pool.release(handle.browser);
14326
+ }
14327
+ handles.delete(sessionId);
14328
+ }
14329
+ try {
14330
+ const { clearLastSnapshot: clearLastSnapshot2, clearSessionRefs: clearSessionRefs2 } = await Promise.resolve().then(() => (init_snapshot(), exports_snapshot));
14331
+ clearLastSnapshot2(sessionId);
14332
+ clearSessionRefs2(sessionId);
14333
+ } catch {}
14334
+ try {
14335
+ const { stopAllWatchesForSession: stopAllWatchesForSession2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
14336
+ stopAllWatchesForSession2(sessionId);
14337
+ } catch {}
14338
+ try {
14339
+ const { clearDialogs: clearDialogs2 } = await Promise.resolve().then(() => (init_dialogs(), exports_dialogs));
14340
+ clearDialogs2(sessionId);
14341
+ } catch {}
14342
+ return closeSession(sessionId);
14343
+ }
14344
+ function getSession2(sessionId) {
14345
+ return getSession(sessionId);
14346
+ }
14347
+ function listSessions2(filter) {
14348
+ return listSessions(filter);
14349
+ }
14350
+ function getActiveSessions() {
14351
+ return listSessions({ status: "active" });
14352
+ }
14353
+ async function closeAllSessions() {
14354
+ for (const [id] of handles) {
14355
+ await closeSession2(id).catch(() => {});
14356
+ }
14357
+ await pool.destroyAll();
14358
+ }
14359
+ function getSessionByName2(name) {
14360
+ return getSessionByName(name);
14361
+ }
14362
+ function renameSession2(id, name) {
14363
+ return renameSession(id, name);
14364
+ }
14365
+ function getTokenBudget(sessionId) {
14366
+ const handle = handles.get(sessionId);
14367
+ return handle ? handle.tokenBudget : null;
14368
+ }
14369
+ function getActiveSessionForAgent2(agentId) {
14370
+ const session = getActiveSessionForAgent(agentId);
14371
+ if (!session)
14372
+ return null;
14373
+ const handle = handles.get(session.id);
14374
+ if (!handle)
14375
+ return null;
14376
+ try {
14377
+ if (handle.bunView)
14378
+ handle.bunView.url();
14379
+ else
14380
+ handle.page.url();
14381
+ } catch {
14382
+ handles.delete(session.id);
14383
+ return null;
14384
+ }
14385
+ return { session, page: handle.page };
14386
+ }
14387
+ function getDefaultSession() {
14388
+ const session = getDefaultActiveSession();
14389
+ if (!session)
14390
+ return null;
14391
+ const handle = handles.get(session.id);
14392
+ if (!handle)
14393
+ return null;
14394
+ try {
14395
+ if (handle.bunView)
14396
+ handle.bunView.url();
14397
+ else
14398
+ handle.page.url();
14399
+ } catch {
14400
+ handles.delete(session.id);
14401
+ return null;
14402
+ }
14403
+ return { session, page: handle.page };
14404
+ }
14405
+ function isAutoGallery(sessionId) {
14406
+ return handles.get(sessionId)?.autoGallery ?? false;
14407
+ }
14408
+ function countActiveSessions2() {
14409
+ return countActiveSessions();
14410
+ }
14411
+ var handles, pool, SESSION_TTL_MS, ttlInterval, DB_PRUNE_INTERVAL_MS, DB_RETENTION_HOURS = 24, dbPruneInterval;
14412
+ var init_session = __esm(() => {
14413
+ init_types();
14414
+ init_types();
14415
+ init_sessions();
14416
+ init_playwright();
14417
+ init_lightpanda();
14418
+ init_bun_webview();
14419
+ init_selector();
14420
+ init_tui();
14421
+ init_network();
14422
+ init_console();
14423
+ init_stealth();
14424
+ init_dialogs();
14425
+ handles = new Map;
14426
+ pool = new BrowserPool(5);
14427
+ SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
14428
+ ttlInterval = setInterval(async () => {
14429
+ const now = Date.now();
14430
+ for (const [id, handle] of handles) {
14431
+ if (now - handle.lastActivity > SESSION_TTL_MS) {
14432
+ try {
14433
+ await closeSession2(id);
14434
+ } catch {}
14435
+ }
14436
+ }
14437
+ }, 60000);
14438
+ if (ttlInterval.unref)
14439
+ ttlInterval.unref();
14440
+ DB_PRUNE_INTERVAL_MS = 30 * 60000;
14441
+ dbPruneInterval = setInterval(() => {
14442
+ try {
14443
+ const { getDatabase: getDatabase2 } = (init_schema(), __toCommonJS(exports_schema));
14444
+ const db = getDatabase2();
14445
+ const cutoff = new Date(Date.now() - DB_RETENTION_HOURS * 3600000).toISOString();
14446
+ db.prepare("DELETE FROM network_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
14447
+ db.prepare("DELETE FROM console_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
14448
+ db.prepare("DELETE FROM snapshots WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
14449
+ } catch {}
14450
+ }, DB_PRUNE_INTERVAL_MS);
14451
+ if (dbPruneInterval.unref)
14452
+ dbPruneInterval.unref();
14453
+ });
14454
+
14323
14455
  // src/lib/extractor.ts
14324
14456
  var exports_extractor = {};
14325
14457
  __export(exports_extractor, {
@@ -27446,7 +27578,7 @@ var init_helpers = __esm(() => {
27446
27578
  // src/mcp/sessions.ts
27447
27579
  function register4(server) {
27448
27580
  server.tool("browser_session_create", "Create a new browser session. If agent_id is set and already has an active session, returns the existing one (use force_new to override). If session_id is omitted on other tools, the single active session is auto-selected. Use cdp_url to attach to an already-running Chrome instance.", {
27449
- engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
27581
+ engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto"),
27450
27582
  use_case: exports_external2.string().optional(),
27451
27583
  project_id: exports_external2.string().optional(),
27452
27584
  agent_id: exports_external2.string().optional(),
@@ -32183,7 +32315,7 @@ function register8(server) {
32183
32315
  max_pages: exports_external2.number().optional().default(50),
32184
32316
  same_domain: exports_external2.boolean().optional().default(true),
32185
32317
  project_id: exports_external2.string().optional(),
32186
- engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto")
32318
+ engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto")
32187
32319
  }, async ({ url, max_depth, max_pages, same_domain, project_id, engine }) => {
32188
32320
  try {
32189
32321
  const result = await crawl(url, {
@@ -32351,7 +32483,7 @@ function register9(server) {
32351
32483
  server.tool("browser_script_run", "Run a saved script asynchronously. Returns run_id immediately \u2014 poll with browser_script_status for step-by-step progress. Scripts combine browser actions + connector calls + AI reasoning. Works with any engine (Bun.WebView, Playwright, CDP).", {
32352
32484
  name: exports_external2.string().describe("Script name"),
32353
32485
  session_id: exports_external2.string().optional(),
32354
- engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
32486
+ engine: exports_external2.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto"),
32355
32487
  variables: exports_external2.record(exports_external2.string()).optional().describe("Override script variables")
32356
32488
  }, async ({ name, session_id, engine, variables }) => {
32357
32489
  try {
@@ -32606,8 +32738,8 @@ import { join as join33 } from "path";
32606
32738
  import { existsSync as existsSync43, mkdirSync as mkdirSync4, readFileSync as readFileSync33, writeFileSync as writeFileSync33 } from "fs";
32607
32739
  import { homedir as homedir33 } from "os";
32608
32740
  import { join as join43 } from "path";
32609
- function __exportSetter3(name, newValue) {
32610
- this[name] = __returnValue3.bind(null, newValue);
32741
+ function __exportSetter2(name, newValue) {
32742
+ this[name] = __returnValue2.bind(null, newValue);
32611
32743
  }
32612
32744
  function isInMemoryDb(path) {
32613
32745
  return path === ":memory:" || path.startsWith("file::memory:");
@@ -35721,13 +35853,13 @@ function clearActiveModel() {
35721
35853
  delete config.activeModel;
35722
35854
  writeConfig(config);
35723
35855
  }
35724
- var __defProp3, __returnValue3 = (v) => v, __export3 = (target, all) => {
35856
+ var __defProp3, __returnValue2 = (v) => v, __export3 = (target, all) => {
35725
35857
  for (var name in all)
35726
35858
  __defProp3(target, name, {
35727
35859
  get: all[name],
35728
35860
  enumerable: true,
35729
35861
  configurable: true,
35730
- set: __exportSetter3.bind(all, name)
35862
+ set: __exportSetter2.bind(all, name)
35731
35863
  });
35732
35864
  }, __esm3 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), exports_database, MIGRATIONS, _db2 = null, init_database, AgentConflictError, EntityNotFoundError, MemoryNotFoundError, DuplicateMemoryError, MemoryExpiredError, InvalidScopeError, VersionConflictError, MemoryConflictError, OPENAI_EMBED_URL = "https://api.openai.com/v1/embeddings", EMBED_MODEL = "text-embedding-3-small", EMBED_DIMENSIONS = 1536, REDACTED = "[REDACTED]", SECRET_PATTERNS, _idCounter = 0, hookRegistry, INSTRUCTION_PATTERNS, PROMOTIONAL_PATTERNS, RECALL_PROMOTE_THRESHOLD = 3, CONFLICT_WINDOW_MS, MEMORY_WRITE_TTL = 30, MemoryLockConflictError, sessionFocus, STOP_WORDS, DEFAULT_CONFIG, VALID_SCOPES, VALID_CATEGORIES, defaultSyncAgents, DEFAULT_AUTO_MEMORY_CONFIG, MEMORY_EXTRACTION_SYSTEM_PROMPT = `You are a precise memory extraction engine for an AI agent.
35733
35865
  Given text, extract facts worth remembering as structured JSON.
@@ -37108,6 +37240,11 @@ async function rememberPage(url, facts, tags) {
37108
37240
  return;
37109
37241
  } catch {}
37110
37242
  }
37243
+ if (inMemoryCache.size >= MEMORY_MAX_SIZE && !inMemoryCache.has(key)) {
37244
+ const firstKey = inMemoryCache.keys().next().value;
37245
+ if (firstKey)
37246
+ inMemoryCache.delete(firstKey);
37247
+ }
37111
37248
  inMemoryCache.set(key, {
37112
37249
  data: memory,
37113
37250
  expires: Date.now() + DEFAULT_TTL_HOURS * 60 * 60 * 1000
@@ -37137,9 +37274,18 @@ async function forgetPage(url) {
37137
37274
  const key = cacheKey(url);
37138
37275
  inMemoryCache.delete(key);
37139
37276
  }
37140
- var MEMORY_KEY_PREFIX = "browser-page:", DEFAULT_TTL_HOURS = 24, inMemoryCache;
37277
+ var MEMORY_KEY_PREFIX = "browser-page:", DEFAULT_TTL_HOURS = 24, inMemoryCache, MEMORY_MAX_SIZE = 200, _memorySweeper;
37141
37278
  var init_page_memory = __esm(() => {
37142
37279
  inMemoryCache = new Map;
37280
+ _memorySweeper = setInterval(() => {
37281
+ const now2 = Date.now();
37282
+ for (const [key, entry] of inMemoryCache) {
37283
+ if (entry.expires <= now2)
37284
+ inMemoryCache.delete(key);
37285
+ }
37286
+ }, 300000);
37287
+ if (_memorySweeper.unref)
37288
+ _memorySweeper.unref();
37143
37289
  });
37144
37290
 
37145
37291
  // node_modules/@hasna/conversations/dist/index.js
@@ -37236,11 +37382,11 @@ import { randomUUID as randomUUID22 } from "crypto";
37236
37382
  import { readFileSync as readFileSync24, writeFileSync as writeFileSync7, mkdirSync as mkdirSync34 } from "fs";
37237
37383
  import { join as join44, dirname as dirname24 } from "path";
37238
37384
  import { homedir as homedir42 } from "os";
37239
- function __accessProp3(key) {
37385
+ function __accessProp2(key) {
37240
37386
  return this[key];
37241
37387
  }
37242
- function __exportSetter4(name, newValue) {
37243
- this[name] = __returnValue4.bind(null, newValue);
37388
+ function __exportSetter3(name, newValue) {
37389
+ this[name] = __returnValue3.bind(null, newValue);
37244
37390
  }
37245
37391
  function getDbPath3() {
37246
37392
  if (process.env.CONVERSATIONS_DB_PATH)
@@ -39225,10 +39371,10 @@ function getGraphStats() {
39225
39371
  map[r.relation] = r.c;
39226
39372
  return { total_edges: total, by_relation: map };
39227
39373
  }
39228
- var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2, __hasOwnProp3, __toESMCache_node3, __toESMCache_esm3, __toESM3 = (mod, isNodeMode, target) => {
39374
+ var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2, __hasOwnProp3, __toESMCache_node2, __toESMCache_esm2, __toESM3 = (mod, isNodeMode, target) => {
39229
39375
  var canCache = mod != null && typeof mod === "object";
39230
39376
  if (canCache) {
39231
- var cache = isNodeMode ? __toESMCache_node3 ??= new WeakMap : __toESMCache_esm3 ??= new WeakMap;
39377
+ var cache = isNodeMode ? __toESMCache_node2 ??= new WeakMap : __toESMCache_esm2 ??= new WeakMap;
39232
39378
  var cached = cache.get(mod);
39233
39379
  if (cached)
39234
39380
  return cached;
@@ -39238,7 +39384,7 @@ var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2,
39238
39384
  for (let key of __getOwnPropNames3(mod))
39239
39385
  if (!__hasOwnProp3.call(to, key))
39240
39386
  __defProp4(to, key, {
39241
- get: __accessProp3.bind(mod, key),
39387
+ get: __accessProp2.bind(mod, key),
39242
39388
  enumerable: true
39243
39389
  });
39244
39390
  if (canCache)
@@ -39253,19 +39399,19 @@ var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2,
39253
39399
  for (var key of __getOwnPropNames3(from))
39254
39400
  if (!__hasOwnProp3.call(entry, key))
39255
39401
  __defProp4(entry, key, {
39256
- get: __accessProp3.bind(from, key),
39402
+ get: __accessProp2.bind(from, key),
39257
39403
  enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
39258
39404
  });
39259
39405
  }
39260
39406
  __moduleCache2.set(from, entry);
39261
39407
  return entry;
39262
- }, __moduleCache2, __commonJS3 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue4 = (v) => v, __export4 = (target, all) => {
39408
+ }, __moduleCache2, __commonJS3 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue3 = (v) => v, __export4 = (target, all) => {
39263
39409
  for (var name in all)
39264
39410
  __defProp4(target, name, {
39265
39411
  get: all[name],
39266
39412
  enumerable: true,
39267
39413
  configurable: true,
39268
- set: __exportSetter4.bind(all, name)
39414
+ set: __exportSetter3.bind(all, name)
39269
39415
  });
39270
39416
  }, exports_db, db = null, init_db = () => {}, require_react_development, require_react, cachedConfig = null, configLoadedAt = 0, CONFIG_CACHE_MS = 1e4, import_react, AGENT_NAMES, AGENT_ID_FILE, cachedAutoName = null, ONLINE_THRESHOLD_SECONDS = 60, CONFLICT_THRESHOLD_SECONDS, DEFAULT_LOCK_EXPIRY_MS, STALE_HEARTBEAT_SECONDS, STOPWORDS;
39271
39417
  var init_dist4 = __esm(() => {
@@ -41603,6 +41749,11 @@ async function announceNavigation(url, sessionId, agentName = "browser-agent") {
41603
41749
  await sdk.sendMessage(SPACE_NAME, `\uD83C\uDF10 Navigating to ${hostname} (session: ${sessionId.slice(0, 8)})`);
41604
41750
  } catch {}
41605
41751
  }
41752
+ if (activeNavigations.size >= NAV_MAX_SIZE && !activeNavigations.has(hostname)) {
41753
+ const firstKey = activeNavigations.keys().next().value;
41754
+ if (firstKey)
41755
+ activeNavigations.delete(firstKey);
41756
+ }
41606
41757
  activeNavigations.set(hostname, { agentName, timestamp: Date.now() });
41607
41758
  }
41608
41759
  async function checkDuplicate2(url) {
@@ -41638,10 +41789,19 @@ async function checkDuplicate2(url) {
41638
41789
  }
41639
41790
  return { is_duplicate: false };
41640
41791
  }
41641
- var SPACE_NAME = "browser", DUPLICATE_WINDOW_MS, activeNavigations;
41792
+ var SPACE_NAME = "browser", DUPLICATE_WINDOW_MS, activeNavigations, NAV_MAX_SIZE = 200, _navSweeper;
41642
41793
  var init_coordination = __esm(() => {
41643
41794
  DUPLICATE_WINDOW_MS = 5 * 60 * 1000;
41644
41795
  activeNavigations = new Map;
41796
+ _navSweeper = setInterval(() => {
41797
+ const cutoff = Date.now() - DUPLICATE_WINDOW_MS;
41798
+ for (const [key, entry] of activeNavigations) {
41799
+ if (entry.timestamp < cutoff)
41800
+ activeNavigations.delete(key);
41801
+ }
41802
+ }, 120000);
41803
+ if (_navSweeper.unref)
41804
+ _navSweeper.unref();
41645
41805
  });
41646
41806
 
41647
41807
  // node_modules/@hasna/todos/dist/index.js
@@ -41865,7 +42025,7 @@ import { existsSync as existsSync62 } from "fs";
41865
42025
  import { join as join62 } from "path";
41866
42026
  import { readFileSync as readFileSync43, statSync as statSync22 } from "fs";
41867
42027
  import { relative as relative2, resolve as resolve23, join as join72 } from "path";
41868
- import { execSync as execSync2 } from "child_process";
42028
+ import { execSync as execSync3 } from "child_process";
41869
42029
 
41870
42030
  class TodosClient {
41871
42031
  baseUrl;
@@ -46213,7 +46373,7 @@ function parseGitHubUrl(url) {
46213
46373
  return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
46214
46374
  }
46215
46375
  function fetchGitHubIssue(owner, repo, number) {
46216
- const json2 = execSync2(`gh api repos/${owner}/${repo}/issues/${number}`, { encoding: "utf-8", timeout: 15000 });
46376
+ const json2 = execSync3(`gh api repos/${owner}/${repo}/issues/${number}`, { encoding: "utf-8", timeout: 15000 });
46217
46377
  const data = JSON.parse(json2);
46218
46378
  return {
46219
46379
  number: data.number,
@@ -47244,6 +47404,8 @@ Skill: ${task.skill}` : ""}`,
47244
47404
  };
47245
47405
  } catch {}
47246
47406
  }
47407
+ if (inMemoryQueue.length >= QUEUE_MAX_SIZE)
47408
+ inMemoryQueue.shift();
47247
47409
  const id = `btask-${Date.now()}`;
47248
47410
  const entry = { ...task, id, status: "pending", created_at: new Date().toISOString() };
47249
47411
  inMemoryQueue.push(entry);
@@ -47277,7 +47439,7 @@ async function completeBrowserTask(taskId, result) {
47277
47439
  if (idx >= 0)
47278
47440
  inMemoryQueue.splice(idx, 1);
47279
47441
  }
47280
- var inMemoryQueue;
47442
+ var QUEUE_MAX_SIZE = 100, inMemoryQueue;
47281
47443
  var init_task_queue = __esm(() => {
47282
47444
  inMemoryQueue = [];
47283
47445
  });
@@ -49441,9 +49603,9 @@ ${entries.length} entries`));
49441
49603
  });
49442
49604
  program2.command("install-browser").description("Install a browser engine").option("--engine <engine>", "Engine to install: lightpanda|chromium", "chromium").action(async (opts) => {
49443
49605
  if (opts.engine === "chromium") {
49444
- const { execSync: execSync3 } = await import("child_process");
49606
+ const { execSync: execSync4 } = await import("child_process");
49445
49607
  console.log(chalk4.gray("Installing Chromium via Playwright..."));
49446
- execSync3("bunx playwright install chromium", { stdio: "inherit" });
49608
+ execSync4("bunx playwright install chromium", { stdio: "inherit" });
49447
49609
  console.log(chalk4.green("\u2713 Chromium installed"));
49448
49610
  } else if (opts.engine === "lightpanda") {
49449
49611
  console.log(chalk4.yellow("Lightpanda must be installed manually."));
@@ -49463,12 +49625,12 @@ ${entries.length} entries`));
49463
49625
  console.log(chalk4.gray(` PID: ${status.pid}, Port: ${status.port}, Sessions: ${status.sessions ?? "?"}`));
49464
49626
  return;
49465
49627
  }
49466
- const { spawn: spawn2 } = await import("child_process");
49628
+ const { spawn: spawn3 } = await import("child_process");
49467
49629
  const { writeFileSync: writeFileSync9, mkdirSync: mkdirSync17 } = await import("fs");
49468
49630
  const { dirname: dirname6 } = await import("path");
49469
49631
  const pidFile = getDaemonPidFile2();
49470
49632
  mkdirSync17(dirname6(pidFile), { recursive: true });
49471
- const child = spawn2(process.execPath, [import.meta.dir + "/../../server/index.js"], {
49633
+ const child = spawn3(process.execPath, [import.meta.dir + "/../../server/index.js"], {
49472
49634
  detached: true,
49473
49635
  stdio: "ignore",
49474
49636
  env: { ...process.env, BROWSER_SERVER_PORT: opts.port }