@hasna/browser 0.4.8 → 0.4.10

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
@@ -14383,7 +14383,7 @@ function buildRowRefs(rows, method, totalRows, rowCount) {
14383
14383
  row: index,
14384
14384
  text,
14385
14385
  visible: method === "dom" ? true : index >= firstVisibleRow,
14386
- selector: method === "dom" ? `#takumi-tui-dom-root [data-row="${index}"]` : undefined
14386
+ selector: method === "dom" ? `#takumi-tui-dom-root .takumi-tui-dom-row[data-row="${index}"]` : undefined
14387
14387
  };
14388
14388
  });
14389
14389
  return refs;
@@ -14591,7 +14591,7 @@ async function readDomMirrorState(page) {
14591
14591
  const runtime = window.__takumiTuiDomRenderer;
14592
14592
  if (runtime?.sync)
14593
14593
  return runtime.sync();
14594
- const rowEls = Array.from(document.querySelectorAll("#takumi-tui-dom-root [data-row]"));
14594
+ const rowEls = Array.from(document.querySelectorAll("#takumi-tui-dom-root .takumi-tui-dom-row"));
14595
14595
  const rows = rowEls.map((row) => row.getAttribute("aria-label") ?? row.textContent ?? "");
14596
14596
  const term = window.term ?? window.terminal;
14597
14597
  const active = term?.buffer?.active;
@@ -14614,17 +14614,14 @@ function isDomMethod(method) {
14614
14614
  return method === "dom";
14615
14615
  }
14616
14616
  async function withTimeout(label, operation, timeoutMs = DEFAULT_TOOL_TIMEOUT_MS) {
14617
- let timedOut = false;
14618
- const timer = setTimeout(() => {
14619
- timedOut = true;
14620
- }, timeoutMs);
14617
+ let timer;
14618
+ const timeout = new Promise((_, reject) => {
14619
+ timer = setTimeout(() => {
14620
+ reject(new BrowserError(`${label} timed out after ${timeoutMs}ms \u2014 ttyd/playwright connection may be unhealthy. Try closing and re-opening the session.`, "TUI_TIMEOUT"));
14621
+ }, timeoutMs);
14622
+ });
14621
14623
  try {
14622
- return await operation();
14623
- } catch (err) {
14624
- if (timedOut) {
14625
- throw new BrowserError(`${label} timed out after ${timeoutMs}ms \u2014 ttyd/playwright connection may be unhealthy. Try closing and re-opening the session.`, "TUI_TIMEOUT");
14626
- }
14627
- throw err;
14624
+ return await Promise.race([operation(), timeout]);
14628
14625
  } finally {
14629
14626
  clearTimeout(timer);
14630
14627
  }
@@ -14894,7 +14891,7 @@ async function waitForTerminalText(page, text, timeoutMs = 30000, method = "buff
14894
14891
  return { found: true, elapsed_ms: Date.now() - start, stuck: false };
14895
14892
  await new Promise((r) => setTimeout(r, 250));
14896
14893
  }
14897
- return { found: false, elapsed_ms: timeoutMs, stuck: false };
14894
+ return { found: false, elapsed_ms: Date.now() - start, stuck: false };
14898
14895
  }
14899
14896
  async function closeTui(session) {
14900
14897
  await destroyDomRenderer(session.page);
@@ -15007,6 +15004,23 @@ var init_selector = __esm(() => {
15007
15004
  };
15008
15005
  });
15009
15006
 
15007
+ // src/lib/tui-recording.ts
15008
+ function trackTuiRecording(sessionId, intervalId) {
15009
+ stopTuiRecording(sessionId);
15010
+ recordingIntervals.set(sessionId, intervalId);
15011
+ }
15012
+ function stopTuiRecording(sessionId) {
15013
+ const intervalId = recordingIntervals.get(sessionId);
15014
+ if (!intervalId)
15015
+ return;
15016
+ clearInterval(intervalId);
15017
+ recordingIntervals.delete(sessionId);
15018
+ }
15019
+ var recordingIntervals;
15020
+ var init_tui_recording = __esm(() => {
15021
+ recordingIntervals = new Map;
15022
+ });
15023
+
15010
15024
  // src/db/network-log.ts
15011
15025
  import { randomUUID as randomUUID2 } from "crypto";
15012
15026
  function logRequest(data) {
@@ -15226,7 +15240,9 @@ async function applyStealthPatches(page) {
15226
15240
  "User-Agent": REALISTIC_USER_AGENT
15227
15241
  });
15228
15242
  }
15229
- var REALISTIC_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", STEALTH_SCRIPT = `
15243
+ var REALISTIC_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", STEALTH_SCRIPT;
15244
+ var init_stealth = __esm(() => {
15245
+ STEALTH_SCRIPT = `
15230
15246
  // \u2500\u2500 1. Remove navigator.webdriver flag \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
15231
15247
  Object.defineProperty(navigator, 'webdriver', {
15232
15248
  get: () => false,
@@ -15274,8 +15290,14 @@ if (!window.chrome.runtime) {
15274
15290
  id: undefined,
15275
15291
  };
15276
15292
  }
15293
+
15294
+ // \u2500\u2500 5. Override navigator.userAgent to match HTTP header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
15295
+ Object.defineProperty(navigator, 'userAgent', {
15296
+ get: () => "${REALISTIC_USER_AGENT}",
15297
+ configurable: true,
15298
+ });
15277
15299
  `;
15278
- var init_stealth = () => {};
15300
+ });
15279
15301
 
15280
15302
  // src/lib/dialogs.ts
15281
15303
  var exports_dialogs = {};
@@ -16339,6 +16361,29 @@ __export(exports_session, {
16339
16361
  function createBunProxy(view) {
16340
16362
  return view;
16341
16363
  }
16364
+ function attachPlaywrightListeners(page, sessionId, cleanups, opts = {}) {
16365
+ if (opts.captureNetwork !== false) {
16366
+ try {
16367
+ cleanups.push(enableNetworkLogging(page, sessionId));
16368
+ } catch {}
16369
+ }
16370
+ if (opts.captureConsole !== false) {
16371
+ try {
16372
+ cleanups.push(enableConsoleCapture(page, sessionId));
16373
+ } catch {}
16374
+ }
16375
+ try {
16376
+ cleanups.push(setupDialogHandler(page, sessionId));
16377
+ } catch {}
16378
+ }
16379
+ function detachPlaywrightListeners(cleanups) {
16380
+ while (cleanups.length > 1) {
16381
+ const cleanup = cleanups.pop();
16382
+ try {
16383
+ cleanup?.();
16384
+ } catch {}
16385
+ }
16386
+ }
16342
16387
  async function createSession2(opts = {}) {
16343
16388
  if (opts.cdpUrl) {
16344
16389
  const { connectToExistingBrowser: connectToExistingBrowser2 } = await Promise.resolve().then(() => (init_cdp(), exports_cdp));
@@ -16376,9 +16421,11 @@ async function createSession2(opts = {}) {
16376
16421
  let browser = null;
16377
16422
  let bunView = null;
16378
16423
  let page;
16424
+ let actualEngine = resolvedEngine;
16379
16425
  if (resolvedEngine === "bun") {
16380
16426
  if (!isBunWebViewAvailable()) {
16381
16427
  console.warn("[browser] Bun.WebView requested but not available \u2014 falling back to playwright. Run: bun upgrade --canary");
16428
+ actualEngine = "playwright";
16382
16429
  browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
16383
16430
  page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
16384
16431
  } else {
@@ -16398,9 +16445,11 @@ async function createSession2(opts = {}) {
16398
16445
  }
16399
16446
  if (!bunWorks) {
16400
16447
  console.warn("[browser] Bun.WebView exists but Chrome not available \u2014 falling back to playwright");
16448
+ actualEngine = "playwright";
16401
16449
  browser = await launchPlaywright({ headless: opts.headless ?? true, viewport: opts.viewport, userAgent: opts.userAgent });
16402
16450
  page = await getPage(browser, { viewport: opts.viewport, userAgent: opts.userAgent });
16403
16451
  } else {
16452
+ actualEngine = "bun";
16404
16453
  bunView = testView;
16405
16454
  if (opts.stealth) {}
16406
16455
  page = createBunProxy(bunView);
@@ -16430,19 +16479,10 @@ async function createSession2(opts = {}) {
16430
16479
  });
16431
16480
  const cleanups2 = [];
16432
16481
  cleanups2.push(() => closeTui(tuiSess));
16433
- if (opts.captureNetwork !== false) {
16434
- try {
16435
- cleanups2.push(enableNetworkLogging(page, session2.id));
16436
- } catch {}
16437
- }
16438
- if (opts.captureConsole !== false) {
16439
- try {
16440
- cleanups2.push(enableConsoleCapture(page, session2.id));
16441
- } catch {}
16442
- }
16443
- try {
16444
- cleanups2.push(setupDialogHandler(page, session2.id));
16445
- } catch {}
16482
+ attachPlaywrightListeners(page, session2.id, cleanups2, {
16483
+ captureNetwork: opts.captureNetwork,
16484
+ captureConsole: opts.captureConsole
16485
+ });
16446
16486
  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, startUrl: opts.startUrl ?? "bash" });
16447
16487
  return { session: session2, page };
16448
16488
  } else {
@@ -16472,7 +16512,7 @@ async function createSession2(opts = {}) {
16472
16512
  }
16473
16513
  })() : undefined);
16474
16514
  const session = createSession({
16475
- engine: bunView ? "bun" : browser ? resolvedEngine : resolvedEngine,
16515
+ engine: actualEngine,
16476
16516
  projectId: opts.projectId,
16477
16517
  agentId: opts.agentId,
16478
16518
  startUrl: opts.startUrl,
@@ -16485,37 +16525,12 @@ async function createSession2(opts = {}) {
16485
16525
  }
16486
16526
  const cleanups = [];
16487
16527
  if (!bunView) {
16488
- if (opts.captureNetwork !== false) {
16489
- try {
16490
- cleanups.push(enableNetworkLogging(page, session.id));
16491
- } catch {}
16492
- }
16493
- if (opts.captureConsole !== false) {
16494
- try {
16495
- cleanups.push(enableConsoleCapture(page, session.id));
16496
- } catch {}
16497
- }
16498
- try {
16499
- cleanups.push(setupDialogHandler(page, session.id));
16500
- } catch {}
16501
- } else {
16502
- if (opts.captureConsole !== false) {
16503
- try {
16504
- const { logConsoleMessage: logConsoleMessage2 } = await Promise.resolve().then(() => (init_console_log(), exports_console_log));
16505
- await bunView.addInitScript(`
16506
- (() => {
16507
- const orig = { log: console.log, warn: console.warn, error: console.error, debug: console.debug, info: console.info };
16508
- ['log','warn','error','debug','info'].forEach(level => {
16509
- console[level] = (...args) => {
16510
- orig[level](...args);
16511
- };
16512
- });
16513
- })()
16514
- `);
16515
- } catch {}
16516
- }
16528
+ attachPlaywrightListeners(page, session.id, cleanups, {
16529
+ captureNetwork: opts.captureNetwork,
16530
+ captureConsole: opts.captureConsole
16531
+ });
16517
16532
  }
16518
- 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, startUrl: opts.startUrl ?? "" });
16533
+ handles.set(session.id, { browser, bunView, tuiSession: null, page, engine: actualEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false, startUrl: opts.startUrl ?? "" });
16519
16534
  if (opts.startUrl) {
16520
16535
  try {
16521
16536
  if (bunView) {
@@ -16548,7 +16563,8 @@ function getSessionBunView(sessionId) {
16548
16563
  return handles.get(sessionId)?.bunView ?? null;
16549
16564
  }
16550
16565
  function isBunSession(sessionId) {
16551
- return handles.get(sessionId)?.engine === "bun";
16566
+ const handle = handles.get(sessionId);
16567
+ return handle?.engine === "bun" && handle.bunView !== null;
16552
16568
  }
16553
16569
  function getSessionBrowser(sessionId) {
16554
16570
  const handle = handles.get(sessionId);
@@ -16574,11 +16590,16 @@ function setSessionTui(sessionId, tuiSess) {
16574
16590
  const handle = handles.get(sessionId);
16575
16591
  if (!handle)
16576
16592
  throw new SessionNotFoundError(sessionId);
16593
+ detachPlaywrightListeners(handle.cleanups);
16577
16594
  handle.tuiSession = tuiSess;
16578
16595
  handle.page = tuiSess.page;
16579
16596
  if (tuiSess.browser !== handle.browser) {
16580
16597
  handle.browser = tuiSess.browser;
16581
16598
  }
16599
+ attachPlaywrightListeners(tuiSess.page, sessionId, handle.cleanups, {
16600
+ captureNetwork: true,
16601
+ captureConsole: true
16602
+ });
16582
16603
  handle.lastActivity = Date.now();
16583
16604
  }
16584
16605
  function getSessionCommand(sessionId) {
@@ -16594,9 +16615,12 @@ async function closeSession2(sessionId) {
16594
16615
  const handle = handles.get(sessionId);
16595
16616
  try {
16596
16617
  if (handle) {
16618
+ if (handle.engine === "tui") {
16619
+ stopTuiRecording(sessionId);
16620
+ }
16597
16621
  for (const cleanup of handle.cleanups) {
16598
16622
  try {
16599
- cleanup();
16623
+ await cleanup();
16600
16624
  } catch {}
16601
16625
  }
16602
16626
  if (handle.bunView) {
@@ -16708,6 +16732,7 @@ var init_session = __esm(() => {
16708
16732
  init_bun_webview();
16709
16733
  init_selector();
16710
16734
  init_tui();
16735
+ init_tui_recording();
16711
16736
  init_network();
16712
16737
  init_console();
16713
16738
  init_stealth();
@@ -23335,10 +23360,17 @@ __export(exports_gallery, {
23335
23360
  createEntry: () => createEntry
23336
23361
  });
23337
23362
  import { randomUUID as randomUUID4 } from "crypto";
23363
+ import { join as join11, resolve, relative as relative2, isAbsolute } from "path";
23338
23364
  function validateDataPath(filePath) {
23339
23365
  if (filePath.includes("..")) {
23340
23366
  throw new Error(`File path must not contain '..': ${filePath}`);
23341
23367
  }
23368
+ const dataDir = resolve(getDataDir2());
23369
+ const resolved = resolve(isAbsolute(filePath) ? filePath : join11(dataDir, filePath));
23370
+ const rel = relative2(dataDir, resolved);
23371
+ if (rel.startsWith("..") || isAbsolute(rel)) {
23372
+ throw new Error(`File path must be within data directory: ${filePath}`);
23373
+ }
23342
23374
  return filePath;
23343
23375
  }
23344
23376
  function deserialize(row) {
@@ -23356,7 +23388,13 @@ function deserialize(row) {
23356
23388
  original_size_bytes: row.original_size_bytes ?? undefined,
23357
23389
  compressed_size_bytes: row.compressed_size_bytes ?? undefined,
23358
23390
  compression_ratio: row.compression_ratio ?? undefined,
23359
- tags: JSON.parse(row.tags),
23391
+ tags: (() => {
23392
+ try {
23393
+ return JSON.parse(row.tags);
23394
+ } catch {
23395
+ return [];
23396
+ }
23397
+ })(),
23360
23398
  notes: row.notes ?? undefined,
23361
23399
  is_favorite: row.is_favorite === 1,
23362
23400
  created_at: row.created_at
@@ -23487,12 +23525,12 @@ var init_gallery = __esm(() => {
23487
23525
  });
23488
23526
 
23489
23527
  // src/lib/screenshot.ts
23490
- import { join as join11 } from "path";
23528
+ import { join as join12 } from "path";
23491
23529
  import { mkdirSync as mkdirSync8 } from "fs";
23492
23530
  function getScreenshotDir(projectId) {
23493
- const base = join11(getDataDir2(), "screenshots");
23531
+ const base = join12(getDataDir2(), "screenshots");
23494
23532
  const date = new Date().toISOString().split("T")[0];
23495
- const dir = projectId ? join11(base, projectId, date) : join11(base, date);
23533
+ const dir = projectId ? join12(base, projectId, date) : join12(base, date);
23496
23534
  mkdirSync8(dir, { recursive: true });
23497
23535
  return dir;
23498
23536
  }
@@ -23508,7 +23546,7 @@ async function compressBuffer(raw, format, quality, maxWidth) {
23508
23546
  }
23509
23547
  }
23510
23548
  async function generateThumbnail(raw, dir, stem) {
23511
- const thumbPath = join11(dir, `${stem}.thumb.webp`);
23549
+ const thumbPath = join12(dir, `${stem}.thumb.webp`);
23512
23550
  const thumbBuffer = await import_sharp.default(raw).resize({ width: 200, withoutEnlargement: true }).webp({ quality: 70, effort: 3 }).toBuffer();
23513
23551
  await Bun.write(thumbPath, thumbBuffer);
23514
23552
  return { path: thumbPath, base64: thumbBuffer.toString("base64") };
@@ -23576,7 +23614,7 @@ async function takeScreenshot(page, opts) {
23576
23614
  const compressedSizeBytes = finalBuffer.length;
23577
23615
  const compressionRatio = originalSizeBytes > 0 ? compressedSizeBytes / originalSizeBytes : 1;
23578
23616
  const ext = format;
23579
- const screenshotPath = opts?.path ?? join11(dir, `${stem}.${ext}`);
23617
+ const screenshotPath = opts?.path ?? join12(dir, `${stem}.${ext}`);
23580
23618
  await Bun.write(screenshotPath, finalBuffer);
23581
23619
  let thumbnailPath;
23582
23620
  let thumbnailBase64;
@@ -23636,12 +23674,12 @@ async function takeScreenshot(page, opts) {
23636
23674
  }
23637
23675
  async function generatePDF(page, opts) {
23638
23676
  try {
23639
- const base = join11(getDataDir2(), "pdfs");
23677
+ const base = join12(getDataDir2(), "pdfs");
23640
23678
  const date = new Date().toISOString().split("T")[0];
23641
- const dir = opts?.projectId ? join11(base, opts.projectId, date) : join11(base, date);
23679
+ const dir = opts?.projectId ? join12(base, opts.projectId, date) : join12(base, date);
23642
23680
  mkdirSync8(dir, { recursive: true });
23643
23681
  const timestamp = Date.now();
23644
- const pdfPath = opts?.path ?? join11(dir, `${timestamp}.pdf`);
23682
+ const pdfPath = opts?.path ?? join12(dir, `${timestamp}.pdf`);
23645
23683
  const buffer = await page.pdf({
23646
23684
  path: pdfPath,
23647
23685
  format: opts?.format ?? "A4",
@@ -24159,9 +24197,9 @@ var exports_gallery_diff = {};
24159
24197
  __export(exports_gallery_diff, {
24160
24198
  diffImages: () => diffImages
24161
24199
  });
24162
- import { join as join12 } from "path";
24200
+ import { join as join13 } from "path";
24163
24201
  import { mkdirSync as mkdirSync9 } from "fs";
24164
- async function diffImages(path1, path2) {
24202
+ async function diffImages(path1, path2, threshold = 10) {
24165
24203
  const img1 = import_sharp2.default(path1);
24166
24204
  const img2 = import_sharp2.default(path2);
24167
24205
  const [meta1, meta2] = await Promise.all([img1.metadata(), img2.metadata()]);
@@ -24180,7 +24218,7 @@ async function diffImages(path1, path2) {
24180
24218
  const dg = Math.abs(raw1[i + 1] - raw2[i + 1]);
24181
24219
  const db = Math.abs(raw1[i + 2] - raw2[i + 2]);
24182
24220
  const diff = (dr + dg + db) / 3;
24183
- if (diff > 10) {
24221
+ if (diff > threshold) {
24184
24222
  changedPixels++;
24185
24223
  diffBuffer[i] = 255;
24186
24224
  diffBuffer[i + 1] = 0;
@@ -24192,9 +24230,9 @@ async function diffImages(path1, path2) {
24192
24230
  }
24193
24231
  }
24194
24232
  const dataDir = getDataDir2();
24195
- const diffDir = join12(dataDir, "diffs");
24233
+ const diffDir = join13(dataDir, "diffs");
24196
24234
  mkdirSync9(diffDir, { recursive: true });
24197
- const diffPath = join12(diffDir, `diff-${Date.now()}.webp`);
24235
+ const diffPath = join13(diffDir, `diff-${Date.now()}.webp`);
24198
24236
  const diffImageBuffer = await import_sharp2.default(diffBuffer, { raw: { width: w, height: h, channels } }).webp({ quality: 85 }).toBuffer();
24199
24237
  await Bun.write(diffPath, diffImageBuffer);
24200
24238
  return {
@@ -24386,7 +24424,7 @@ var init_scripts = __esm(() => {
24386
24424
  });
24387
24425
 
24388
24426
  // src/lib/ai-inference.ts
24389
- function resolve(opts) {
24427
+ function resolve2(opts) {
24390
24428
  if (opts?.model && ALIASES[opts.model])
24391
24429
  return ALIASES[opts.model];
24392
24430
  if (opts?.provider && opts?.model)
@@ -24396,7 +24434,7 @@ function resolve(opts) {
24396
24434
  return ALIASES.fast;
24397
24435
  }
24398
24436
  async function infer(prompt, opts) {
24399
- const { provider, model } = resolve(opts);
24437
+ const { provider, model } = resolve2(opts);
24400
24438
  const maxTokens = opts?.maxTokens ?? 1024;
24401
24439
  if (provider === "anthropic") {
24402
24440
  const apiKey2 = process.env["ANTHROPIC_API_KEY"];
@@ -25022,6 +25060,7 @@ var exports_recorder = {};
25022
25060
  __export(exports_recorder, {
25023
25061
  stopRecording: () => stopRecording,
25024
25062
  startRecording: () => startRecording,
25063
+ resetRecorderState: () => resetRecorderState,
25025
25064
  replayRecording: () => replayRecording,
25026
25065
  recordStep: () => recordStep,
25027
25066
  listRecordings: () => listRecordings,
@@ -25029,6 +25068,14 @@ __export(exports_recorder, {
25029
25068
  exportRecording: () => exportRecording,
25030
25069
  attachPageListeners: () => attachPageListeners
25031
25070
  });
25071
+ function resetRecorderState() {
25072
+ for (const active of activeRecordings.values()) {
25073
+ try {
25074
+ active.cleanup();
25075
+ } catch {}
25076
+ }
25077
+ activeRecordings.clear();
25078
+ }
25032
25079
  function startRecording(sessionId, name, startUrl) {
25033
25080
  const steps = [];
25034
25081
  const recording = createRecording({ name, start_url: startUrl, steps });
@@ -25203,21 +25250,21 @@ __export(exports_profiles, {
25203
25250
  applyProfile: () => applyProfile
25204
25251
  });
25205
25252
  import { mkdirSync as mkdirSync10, existsSync as existsSync11, readdirSync as readdirSync7, rmSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
25206
- import { join as join13 } from "path";
25253
+ import { join as join14 } from "path";
25207
25254
  function getProfilesDir() {
25208
25255
  const dataDir = getDataDir2();
25209
- const dir = join13(dataDir, "profiles");
25256
+ const dir = join14(dataDir, "profiles");
25210
25257
  mkdirSync10(dir, { recursive: true });
25211
25258
  return dir;
25212
25259
  }
25213
25260
  function getProfileDir2(name) {
25214
- return join13(getProfilesDir(), name);
25261
+ return join14(getProfilesDir(), name);
25215
25262
  }
25216
25263
  async function saveProfile(page, name) {
25217
25264
  const dir = getProfileDir2(name);
25218
25265
  mkdirSync10(dir, { recursive: true });
25219
25266
  const cookies = await page.context().cookies();
25220
- writeFileSync3(join13(dir, "cookies.json"), JSON.stringify(cookies, null, 2));
25267
+ writeFileSync3(join14(dir, "cookies.json"), JSON.stringify(cookies, null, 2));
25221
25268
  let localStorage2 = {};
25222
25269
  try {
25223
25270
  localStorage2 = await page.evaluate(() => {
@@ -25229,11 +25276,11 @@ async function saveProfile(page, name) {
25229
25276
  return result;
25230
25277
  });
25231
25278
  } catch {}
25232
- writeFileSync3(join13(dir, "storage.json"), JSON.stringify(localStorage2, null, 2));
25279
+ writeFileSync3(join14(dir, "storage.json"), JSON.stringify(localStorage2, null, 2));
25233
25280
  const savedAt = new Date().toISOString();
25234
25281
  const url = page.url();
25235
25282
  const meta = { saved_at: savedAt, url };
25236
- writeFileSync3(join13(dir, "meta.json"), JSON.stringify(meta, null, 2));
25283
+ writeFileSync3(join14(dir, "meta.json"), JSON.stringify(meta, null, 2));
25237
25284
  return {
25238
25285
  name,
25239
25286
  saved_at: savedAt,
@@ -25247,9 +25294,9 @@ function loadProfile(name) {
25247
25294
  if (!existsSync11(dir)) {
25248
25295
  throw new Error(`Profile not found: ${name}`);
25249
25296
  }
25250
- const cookiesPath = join13(dir, "cookies.json");
25251
- const storagePath = join13(dir, "storage.json");
25252
- const metaPath = join13(dir, "meta.json");
25297
+ const cookiesPath = join14(dir, "cookies.json");
25298
+ const storagePath = join14(dir, "storage.json");
25299
+ const metaPath = join14(dir, "meta.json");
25253
25300
  const cookies = existsSync11(cookiesPath) ? JSON.parse(readFileSync3(cookiesPath, "utf8")) : [];
25254
25301
  const localStorage2 = existsSync11(storagePath) ? JSON.parse(readFileSync3(storagePath, "utf8")) : {};
25255
25302
  let savedAt = new Date().toISOString();
@@ -25290,24 +25337,24 @@ function listProfiles() {
25290
25337
  if (!entry.isDirectory())
25291
25338
  continue;
25292
25339
  const name = entry.name;
25293
- const profileDir = join13(dir, name);
25340
+ const profileDir = join14(dir, name);
25294
25341
  let savedAt = "";
25295
25342
  let url;
25296
25343
  let cookieCount = 0;
25297
25344
  let storageKeyCount = 0;
25298
25345
  try {
25299
- const metaPath = join13(profileDir, "meta.json");
25346
+ const metaPath = join14(profileDir, "meta.json");
25300
25347
  if (existsSync11(metaPath)) {
25301
25348
  const meta = JSON.parse(readFileSync3(metaPath, "utf8"));
25302
25349
  savedAt = meta.saved_at ?? "";
25303
25350
  url = meta.url;
25304
25351
  }
25305
- const cookiesPath = join13(profileDir, "cookies.json");
25352
+ const cookiesPath = join14(profileDir, "cookies.json");
25306
25353
  if (existsSync11(cookiesPath)) {
25307
25354
  const cookies = JSON.parse(readFileSync3(cookiesPath, "utf8"));
25308
25355
  cookieCount = Array.isArray(cookies) ? cookies.length : 0;
25309
25356
  }
25310
- const storagePath = join13(profileDir, "storage.json");
25357
+ const storagePath = join14(profileDir, "storage.json");
25311
25358
  if (existsSync11(storagePath)) {
25312
25359
  const storage = JSON.parse(readFileSync3(storagePath, "utf8"));
25313
25360
  storageKeyCount = Object.keys(storage).length;
@@ -25341,13 +25388,14 @@ var init_profiles = __esm(() => {
25341
25388
  // src/lib/auth.ts
25342
25389
  var exports_auth = {};
25343
25390
  __export(exports_auth, {
25391
+ lookupCredentials: () => lookupCredentials,
25344
25392
  loginWithCredentials: () => loginWithCredentials,
25345
25393
  getCredentials: () => getCredentials
25346
25394
  });
25347
25395
  import { existsSync as existsSync12, readFileSync as readFileSync5 } from "fs";
25348
- import { join as join14 } from "path";
25396
+ import { join as join15 } from "path";
25349
25397
  import { homedir as homedir8 } from "os";
25350
- async function getCredentials(service) {
25398
+ async function lookupCredentials(service) {
25351
25399
  try {
25352
25400
  const secretsVaultPath = process.env["BROWSER_SECRETS_VAULT_PATH"];
25353
25401
  if (secretsVaultPath) {
@@ -25355,11 +25403,14 @@ async function getCredentials(service) {
25355
25403
  const email = getSecret(`${service}_email`) ?? getSecret(`${service}_username`) ?? getSecret(`${service}_login`);
25356
25404
  const password = getSecret(`${service}_password`) ?? getSecret(`${service}_pass`);
25357
25405
  if (email?.value && password?.value) {
25358
- return { email: email.value, password: password.value };
25406
+ return {
25407
+ credential: { email: email.value, password: password.value },
25408
+ method: "secrets_vault"
25409
+ };
25359
25410
  }
25360
25411
  }
25361
25412
  } catch {}
25362
- const secretsPath = join14(homedir8(), ".secrets");
25413
+ const secretsPath = join15(homedir8(), ".secrets");
25363
25414
  if (existsSync12(secretsPath)) {
25364
25415
  let content;
25365
25416
  try {
@@ -25378,27 +25429,33 @@ async function getCredentials(service) {
25378
25429
  }
25379
25430
  const email = vars[`${prefix}_EMAIL`] ?? vars[`${prefix}_USERNAME`];
25380
25431
  const password = vars[`${prefix}_PASSWORD`];
25381
- if (email && password)
25382
- return { email, password };
25432
+ if (email && password) {
25433
+ return { credential: { email, password }, method: "env_file" };
25434
+ }
25383
25435
  }
25384
25436
  const envPrefix = service.toUpperCase().replace(/[^A-Z0-9]/g, "_");
25385
25437
  const envEmail = process.env[`${envPrefix}_EMAIL`] ?? process.env[`${envPrefix}_USERNAME`];
25386
25438
  const envPass = process.env[`${envPrefix}_PASSWORD`];
25387
- if (envEmail && envPass)
25388
- return { email: envEmail, password: envPass };
25389
- return null;
25439
+ if (envEmail && envPass) {
25440
+ return { credential: { email: envEmail, password: envPass }, method: "process_env" };
25441
+ }
25442
+ return { credential: null, method: "not_found" };
25443
+ }
25444
+ async function getCredentials(service) {
25445
+ return (await lookupCredentials(service)).credential;
25390
25446
  }
25391
25447
  async function loginWithCredentials(page, credentials, opts) {
25392
- const { fillForm: fillForm2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
25448
+ const { fillForm: fillForm2, waitForText: waitForText2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
25393
25449
  const { saveProfile: saveProfile2 } = await Promise.resolve().then(() => (init_profiles(), exports_profiles));
25394
25450
  try {
25395
25451
  if (opts?.loginUrl) {
25396
25452
  await page.goto(opts.loginUrl, { waitUntil: "domcontentloaded" });
25397
25453
  await new Promise((r) => setTimeout(r, 500));
25398
25454
  }
25455
+ const urlBefore = page.url();
25399
25456
  const emailSel = opts?.emailSelector ?? 'input[type="email"], input[name="email"], input[id*="email"], input[placeholder*="email" i]';
25400
25457
  const passSel = opts?.passwordSelector ?? 'input[type="password"]';
25401
- const submitSel = opts?.submitSelector ?? 'button[type="submit"], input[type="submit"], button:contains("Sign in"), button:contains("Log in"), button:contains("Login")';
25458
+ const submitSel = opts?.submitSelector ?? 'button[type="submit"], input[type="submit"]';
25402
25459
  const fields = {};
25403
25460
  if (credentials.email)
25404
25461
  fields[emailSel] = credentials.email;
@@ -25407,10 +25464,38 @@ async function loginWithCredentials(page, credentials, opts) {
25407
25464
  if (credentials.password)
25408
25465
  fields[passSel] = credentials.password;
25409
25466
  const fillResult = await fillForm2(page, fields, submitSel);
25410
- const successText = opts?.waitForText ?? "dashboard|profile|account|welcome|signed in|logout";
25411
- await new Promise((r) => setTimeout(r, 1500));
25412
- const currentUrl = page.url?.() ?? "";
25413
- const logged_in = fillResult.errors.length === 0;
25467
+ if (fillResult.errors.some((e) => e.startsWith("submit("))) {
25468
+ try {
25469
+ await page.getByRole("button", { name: /sign in|log in|login|submit/i }).first().click({ timeout: 5000 });
25470
+ } catch {
25471
+ try {
25472
+ await page.locator('input[type="submit"]').first().click({ timeout: 3000 });
25473
+ } catch {}
25474
+ }
25475
+ }
25476
+ const successPattern = opts?.waitForText ?? "dashboard|profile|account|welcome|signed in|logout";
25477
+ const patterns = successPattern.split("|").map((p) => p.trim()).filter(Boolean);
25478
+ let logged_in = false;
25479
+ for (const pattern of patterns) {
25480
+ try {
25481
+ await waitForText2(page, pattern, { timeout: 5000 });
25482
+ logged_in = fillResult.errors.length === 0;
25483
+ break;
25484
+ } catch {}
25485
+ }
25486
+ if (!logged_in) {
25487
+ await page.waitForLoadState("domcontentloaded").catch(() => {});
25488
+ await new Promise((r) => setTimeout(r, 1000));
25489
+ const currentUrl2 = page.url();
25490
+ const bodyText = await page.evaluate(() => document.body?.innerText?.toLowerCase() ?? "").catch(() => "");
25491
+ const urlChanged = currentUrl2 !== urlBefore;
25492
+ const textMatch = patterns.some((p) => {
25493
+ const needle = p.toLowerCase();
25494
+ return bodyText.includes(needle) || currentUrl2.toLowerCase().includes(needle);
25495
+ });
25496
+ logged_in = fillResult.errors.length === 0 && (urlChanged || textMatch);
25497
+ }
25498
+ const currentUrl = page.url();
25414
25499
  let profile_saved = false;
25415
25500
  if (opts?.saveProfile && logged_in) {
25416
25501
  try {
@@ -25422,14 +25507,14 @@ async function loginWithCredentials(page, credentials, opts) {
25422
25507
  logged_in,
25423
25508
  redirect_url: currentUrl,
25424
25509
  profile_saved,
25425
- method: "secrets_vault"
25510
+ method: opts?.method ?? "secrets_vault"
25426
25511
  };
25427
25512
  } catch (err) {
25428
25513
  return {
25429
25514
  logged_in: false,
25430
25515
  redirect_url: "",
25431
25516
  profile_saved: false,
25432
- method: "not_found",
25517
+ method: opts?.method ?? "not_found",
25433
25518
  error: err instanceof Error ? err.message : String(err)
25434
25519
  };
25435
25520
  }
@@ -25449,11 +25534,11 @@ __export(exports_downloads, {
25449
25534
  cleanStaleDownloads: () => cleanStaleDownloads
25450
25535
  });
25451
25536
  import { randomUUID as randomUUID11 } from "crypto";
25452
- import { join as join15, basename, extname } from "path";
25537
+ import { join as join16, basename, extname } from "path";
25453
25538
  import { mkdirSync as mkdirSync11, existsSync as existsSync13, readdirSync as readdirSync8, statSync as statSync2, unlinkSync as unlinkSync3, copyFileSync as copyFileSync3, writeFileSync as writeFileSync5, readFileSync as readFileSync6 } from "fs";
25454
25539
  function getDownloadsDir(sessionId) {
25455
- const base = join15(getDataDir2(), "downloads");
25456
- const dir = sessionId ? join15(base, sessionId) : base;
25540
+ const base = join16(getDataDir2(), "downloads");
25541
+ const dir = sessionId ? join16(base, sessionId) : base;
25457
25542
  mkdirSync11(dir, { recursive: true });
25458
25543
  return dir;
25459
25544
  }
@@ -25469,7 +25554,7 @@ function saveToDownloads(buffer, filename, opts) {
25469
25554
  const ext = extname(filename) || "";
25470
25555
  const stem = basename(filename, ext);
25471
25556
  const uniqueName = `${stem}-${id.slice(0, 8)}${ext}`;
25472
- const filePath = join15(dir, uniqueName);
25557
+ const filePath = join16(dir, uniqueName);
25473
25558
  writeFileSync5(filePath, buffer);
25474
25559
  const meta = {
25475
25560
  id,
@@ -25504,7 +25589,7 @@ function listDownloads(sessionId) {
25504
25589
  for (const entry of entries) {
25505
25590
  if (entry.endsWith(".meta.json"))
25506
25591
  continue;
25507
- const full = join15(d, entry);
25592
+ const full = join16(d, entry);
25508
25593
  const stat = statSync2(full);
25509
25594
  if (stat.isDirectory()) {
25510
25595
  scanDir(full);
@@ -25600,7 +25685,7 @@ __export(exports_daemon_client, {
25600
25685
  getDaemonPid: () => getDaemonPid
25601
25686
  });
25602
25687
  import { existsSync as existsSync14, readFileSync as readFileSync7 } from "fs";
25603
- import { join as join16 } from "path";
25688
+ import { join as join17 } from "path";
25604
25689
  function getDaemonPidFile() {
25605
25690
  return PID_FILE;
25606
25691
  }
@@ -25643,7 +25728,7 @@ async function getDaemonStatus() {
25643
25728
  var PID_FILE, DEFAULT_PORT = 7030;
25644
25729
  var init_daemon_client = __esm(() => {
25645
25730
  init_schema();
25646
- PID_FILE = join16(getDataDir2(), "daemon.pid");
25731
+ PID_FILE = join17(getDataDir2(), "daemon.pid");
25647
25732
  });
25648
25733
 
25649
25734
  // node_modules/zod/v3/helpers/util.js
@@ -29705,7 +29790,7 @@ async function setSessionStorage(page, key, value) {
29705
29790
  }
29706
29791
 
29707
29792
  // src/lib/files-integration.ts
29708
- import { join as join17 } from "path";
29793
+ import { join as join18 } from "path";
29709
29794
  import { mkdirSync as mkdirSync12, copyFileSync as copyFileSync4 } from "fs";
29710
29795
  async function persistFile(localPath, opts) {
29711
29796
  try {
@@ -29717,10 +29802,10 @@ async function persistFile(localPath, opts) {
29717
29802
  } catch {}
29718
29803
  const dataDir = getDataDir2();
29719
29804
  const date = new Date().toISOString().split("T")[0];
29720
- const dir = join17(dataDir, "persistent", date);
29805
+ const dir = join18(dataDir, "persistent", date);
29721
29806
  mkdirSync12(dir, { recursive: true });
29722
29807
  const filename = localPath.split("/").pop() ?? "file";
29723
- const targetPath = join17(dir, filename);
29808
+ const targetPath = join18(dir, filename);
29724
29809
  copyFileSync4(localPath, targetPath);
29725
29810
  return {
29726
29811
  id: `local-${Date.now()}`,
@@ -30494,7 +30579,7 @@ function register5(server) {
30494
30579
  page.on("response", onResponse);
30495
30580
  page.on("requestfailed", onFailed);
30496
30581
  try {
30497
- await new Promise((resolve2, reject) => {
30582
+ await new Promise((resolve3, reject) => {
30498
30583
  const check = () => {
30499
30584
  const now = Date.now();
30500
30585
  if (now - t0 > timeout) {
@@ -30502,7 +30587,7 @@ function register5(server) {
30502
30587
  return;
30503
30588
  }
30504
30589
  if (pending === 0 && now - lastActivity >= idle_time) {
30505
- resolve2();
30590
+ resolve3();
30506
30591
  return;
30507
30592
  }
30508
30593
  setTimeout(check, 100);
@@ -31178,7 +31263,7 @@ function register6(server) {
31178
31263
  await new Promise((r) => setTimeout(r, wait_ms));
31179
31264
  const ss2 = await takeScreenshot(page, { maxWidth: 1280, compress: true, track: false });
31180
31265
  const { diffImages: diffImages2 } = await Promise.resolve().then(() => (init_gallery_diff(), exports_gallery_diff));
31181
- const diff = await diffImages2(ss1.path, ss2.path);
31266
+ const diff = await diffImages2(ss1.path, ss2.path, threshold);
31182
31267
  logEvent(sid, "diff", { url1, url2, changed_percent: diff.changed_percent });
31183
31268
  return json({
31184
31269
  url1,
@@ -31247,7 +31332,7 @@ __export(exports_dist2, {
31247
31332
  AGENT_TARGETS: () => AGENT_TARGETS
31248
31333
  });
31249
31334
  import { existsSync as existsSync15, readFileSync as readFileSync8, readdirSync as readdirSync9 } from "fs";
31250
- import { join as join18 } from "path";
31335
+ import { join as join19 } from "path";
31251
31336
  import { homedir as homedir9 } from "os";
31252
31337
  import { existsSync as existsSync22, cpSync, mkdirSync as mkdirSync13, writeFileSync as writeFileSync6, rmSync as rmSync2, readdirSync as readdirSync22, statSync as statSync3, readFileSync as readFileSync22, accessSync, constants } from "fs";
31253
31338
  import { join as join22, dirname as dirname2 } from "path";
@@ -31297,7 +31382,7 @@ function discoverSkillsInDir(dir) {
31297
31382
  for (const entry of entries) {
31298
31383
  if (!entry.isDirectory())
31299
31384
  continue;
31300
- const skillMdPath = join18(dir, entry.name, "SKILL.md");
31385
+ const skillMdPath = join19(dir, entry.name, "SKILL.md");
31301
31386
  if (!existsSync15(skillMdPath))
31302
31387
  continue;
31303
31388
  let content;
@@ -31328,11 +31413,11 @@ function loadRegistry(cwd) {
31328
31413
  return _registryCache;
31329
31414
  }
31330
31415
  const official = SKILLS.map((s) => ({ ...s, source: "official" }));
31331
- const globalCustomNew = discoverSkillsInDir(join18(homedir9(), ".hasna", "skills", "custom"));
31332
- const globalCustomOld = discoverSkillsInDir(join18(homedir9(), ".skills"));
31416
+ const globalCustomNew = discoverSkillsInDir(join19(homedir9(), ".hasna", "skills", "custom"));
31417
+ const globalCustomOld = discoverSkillsInDir(join19(homedir9(), ".skills"));
31333
31418
  const oldNames = new Set(globalCustomNew.map((s) => s.name));
31334
31419
  const globalCustom = [...globalCustomNew, ...globalCustomOld.filter((s) => !oldNames.has(s.name))];
31335
- const projectCustom = discoverSkillsInDir(join18(cwd || process.cwd(), ".skills", "custom-skills"));
31420
+ const projectCustom = discoverSkillsInDir(join19(cwd || process.cwd(), ".skills", "custom-skills"));
31336
31421
  const customNames = new Set([...globalCustom, ...projectCustom].map((s) => s.name));
31337
31422
  const filtered = official.filter((s) => !customNames.has(s.name));
31338
31423
  _registryCache = [...filtered, ...globalCustom, ...projectCustom];
@@ -33793,11 +33878,11 @@ var init_skills_runner = __esm(() => {
33793
33878
  },
33794
33879
  login: async (page, params) => {
33795
33880
  const { service, login_url } = params;
33796
- const { getCredentials: getCredentials2, loginWithCredentials: loginWithCredentials2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
33797
- const creds = await getCredentials2(service);
33881
+ const { lookupCredentials: lookupCredentials2, loginWithCredentials: loginWithCredentials2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
33882
+ const { credential: creds, method } = await lookupCredentials2(service);
33798
33883
  if (!creds)
33799
33884
  return { logged_in: false, error: `No credentials found for ${service}` };
33800
- const result = await loginWithCredentials2(page, creds, { loginUrl: login_url, saveProfile: service });
33885
+ const result = await loginWithCredentials2(page, creds, { loginUrl: login_url, saveProfile: service, method });
33801
33886
  return result;
33802
33887
  },
33803
33888
  "extract-text": async (page, params) => {
@@ -34871,7 +34956,7 @@ __export(exports_datasets, {
34871
34956
  });
34872
34957
  import { randomUUID as randomUUID15 } from "crypto";
34873
34958
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync14 } from "fs";
34874
- import { join as join19 } from "path";
34959
+ import { join as join20 } from "path";
34875
34960
  function saveDataset(data) {
34876
34961
  const db = getDatabase();
34877
34962
  const id = randomUUID15();
@@ -34909,10 +34994,10 @@ function exportDataset(name, format) {
34909
34994
  const dataset = getDatasetByName(name);
34910
34995
  if (!dataset)
34911
34996
  throw new Error(`Dataset '${name}' not found`);
34912
- const dir = join19(getDataDir2(), "exports");
34997
+ const dir = join20(getDataDir2(), "exports");
34913
34998
  mkdirSync14(dir, { recursive: true });
34914
34999
  const filename = `${name}.${format}`;
34915
- const path = join19(dir, filename);
35000
+ const path = join20(dir, filename);
34916
35001
  if (format === "csv") {
34917
35002
  const rows = dataset.data;
34918
35003
  if (rows.length === 0) {
@@ -35413,7 +35498,7 @@ import {
35413
35498
  copyFileSync as copyFileSync6
35414
35499
  } from "fs";
35415
35500
  import { homedir as homedir10 } from "os";
35416
- import { join as join20, relative as relative2 } from "path";
35501
+ import { join as join21, relative as relative3 } from "path";
35417
35502
  import { existsSync as existsSync23, mkdirSync as mkdirSync23, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
35418
35503
  import { homedir as homedir23 } from "os";
35419
35504
  import { join as join23 } from "path";
@@ -35425,7 +35510,7 @@ import { join as join43 } from "path";
35425
35510
  import { join as join62, dirname as dirname3 } from "path";
35426
35511
  import { homedir as homedir52, platform as platform2 } from "os";
35427
35512
  import { existsSync as existsSync43, mkdirSync as mkdirSync33, cpSync as cpSync2 } from "fs";
35428
- import { dirname as dirname23, join as join53, resolve as resolve2 } from "path";
35513
+ import { dirname as dirname23, join as join53, resolve as resolve3 } from "path";
35429
35514
  import { existsSync as existsSync62, mkdirSync as mkdirSync5, readFileSync as readFileSync23, readdirSync as readdirSync33, writeFileSync as writeFileSync23, unlinkSync as unlinkSync4, cpSync as cpSync22 } from "fs";
35430
35515
  import { homedir as homedir62 } from "os";
35431
35516
  import { basename as basename2, dirname as dirname32, join as join72, resolve as resolve22 } from "path";
@@ -36251,13 +36336,13 @@ function custom3(check, _params = {}, fatal) {
36251
36336
  return ZodAny3.create();
36252
36337
  }
36253
36338
  function getDataDir4(serviceName) {
36254
- const dir = join20(HASNA_DIR2, serviceName);
36339
+ const dir = join21(HASNA_DIR2, serviceName);
36255
36340
  mkdirSync15(dir, { recursive: true });
36256
36341
  return dir;
36257
36342
  }
36258
36343
  function getDbPath2(serviceName) {
36259
36344
  const dir = getDataDir4(serviceName);
36260
- return join20(dir, `${serviceName}.db`);
36345
+ return join21(dir, `${serviceName}.db`);
36261
36346
  }
36262
36347
  function getConfigDir2() {
36263
36348
  return CONFIG_DIR3;
@@ -36476,7 +36561,7 @@ function isInMemoryDb(path) {
36476
36561
  return path === ":memory:" || path.startsWith("file::memory:");
36477
36562
  }
36478
36563
  function findNearestMementosDb(startDir) {
36479
- let dir = resolve2(startDir);
36564
+ let dir = resolve3(startDir);
36480
36565
  while (true) {
36481
36566
  const candidate = join53(dir, ".mementos", "mementos.db");
36482
36567
  if (existsSync43(candidate))
@@ -36489,7 +36574,7 @@ function findNearestMementosDb(startDir) {
36489
36574
  return null;
36490
36575
  }
36491
36576
  function findGitRoot(startDir) {
36492
- let dir = resolve2(startDir);
36577
+ let dir = resolve3(startDir);
36493
36578
  while (true) {
36494
36579
  if (existsSync43(join53(dir, ".git")))
36495
36580
  return dir;
@@ -36531,7 +36616,7 @@ function getDbPath22() {
36531
36616
  function ensureDir2(filePath) {
36532
36617
  if (isInMemoryDb(filePath))
36533
36618
  return;
36534
- const dir = dirname23(resolve2(filePath));
36619
+ const dir = dirname23(resolve3(filePath));
36535
36620
  if (!existsSync43(dir)) {
36536
36621
  mkdirSync33(dir, { recursive: true });
36537
36622
  }
@@ -42769,7 +42854,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
42769
42854
  function parse(stream, callback) {
42770
42855
  const parser = new parser_1.Parser;
42771
42856
  stream.on("data", (buffer) => parser.parse(buffer, callback));
42772
- return new Promise((resolve3) => stream.on("end", () => resolve3()));
42857
+ return new Promise((resolve4) => stream.on("end", () => resolve4()));
42773
42858
  }
42774
42859
  exports.parse = parse;
42775
42860
  });
@@ -43434,12 +43519,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
43434
43519
  this._connect(callback);
43435
43520
  return;
43436
43521
  }
43437
- return new this._Promise((resolve3, reject) => {
43522
+ return new this._Promise((resolve4, reject) => {
43438
43523
  this._connect((error) => {
43439
43524
  if (error) {
43440
43525
  reject(error);
43441
43526
  } else {
43442
- resolve3(this);
43527
+ resolve4(this);
43443
43528
  }
43444
43529
  });
43445
43530
  });
@@ -43771,8 +43856,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
43771
43856
  readTimeout = config.query_timeout || this.connectionParameters.query_timeout;
43772
43857
  query = new Query3(config, values, callback);
43773
43858
  if (!query.callback) {
43774
- result = new this._Promise((resolve3, reject) => {
43775
- query.callback = (err2, res) => err2 ? reject(err2) : resolve3(res);
43859
+ result = new this._Promise((resolve4, reject) => {
43860
+ query.callback = (err2, res) => err2 ? reject(err2) : resolve4(res);
43776
43861
  }).catch((err2) => {
43777
43862
  Error.captureStackTrace(err2);
43778
43863
  throw err2;
@@ -43847,8 +43932,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
43847
43932
  if (cb) {
43848
43933
  this.connection.once("end", cb);
43849
43934
  } else {
43850
- return new this._Promise((resolve3) => {
43851
- this.connection.once("end", resolve3);
43935
+ return new this._Promise((resolve4) => {
43936
+ this.connection.once("end", resolve4);
43852
43937
  });
43853
43938
  }
43854
43939
  }
@@ -43893,8 +43978,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
43893
43978
  const cb = function(err2, client) {
43894
43979
  err2 ? rej(err2) : res(client);
43895
43980
  };
43896
- const result = new Promise2(function(resolve3, reject) {
43897
- res = resolve3;
43981
+ const result = new Promise2(function(resolve4, reject) {
43982
+ res = resolve4;
43898
43983
  rej = reject;
43899
43984
  }).catch((err2) => {
43900
43985
  Error.captureStackTrace(err2);
@@ -43955,7 +44040,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
43955
44040
  if (typeof Promise2.try === "function") {
43956
44041
  return Promise2.try(f);
43957
44042
  }
43958
- return new Promise2((resolve3) => resolve3(f()));
44043
+ return new Promise2((resolve4) => resolve4(f()));
43959
44044
  }
43960
44045
  _isFull() {
43961
44046
  return this._clients.length >= this.options.max;
@@ -44329,8 +44414,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
44329
44414
  NativeQuery.prototype._getPromise = function() {
44330
44415
  if (this._promise)
44331
44416
  return this._promise;
44332
- this._promise = new Promise(function(resolve3, reject) {
44333
- this._once("end", resolve3);
44417
+ this._promise = new Promise(function(resolve4, reject) {
44418
+ this._once("end", resolve4);
44334
44419
  this._once("error", reject);
44335
44420
  }.bind(this));
44336
44421
  return this._promise;
@@ -44504,12 +44589,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
44504
44589
  this._connect(callback);
44505
44590
  return;
44506
44591
  }
44507
- return new this._Promise((resolve3, reject) => {
44592
+ return new this._Promise((resolve4, reject) => {
44508
44593
  this._connect((error) => {
44509
44594
  if (error) {
44510
44595
  reject(error);
44511
44596
  } else {
44512
- resolve3(this);
44597
+ resolve4(this);
44513
44598
  }
44514
44599
  });
44515
44600
  });
@@ -44533,8 +44618,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
44533
44618
  query = new NativeQuery(config, values, callback);
44534
44619
  if (!query.callback) {
44535
44620
  let resolveOut, rejectOut;
44536
- result = new this._Promise((resolve3, reject) => {
44537
- resolveOut = resolve3;
44621
+ result = new this._Promise((resolve4, reject) => {
44622
+ resolveOut = resolve4;
44538
44623
  rejectOut = reject;
44539
44624
  }).catch((err2) => {
44540
44625
  Error.captureStackTrace(err2);
@@ -44592,8 +44677,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
44592
44677
  }
44593
44678
  let result;
44594
44679
  if (!cb) {
44595
- result = new this._Promise(function(resolve3, reject) {
44596
- cb = (err2) => err2 ? reject(err2) : resolve3();
44680
+ result = new this._Promise(function(resolve4, reject) {
44681
+ cb = (err2) => err2 ? reject(err2) : resolve4();
44597
44682
  });
44598
44683
  }
44599
44684
  this.native.end(function() {
@@ -47881,7 +47966,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
47881
47966
  init_external3();
47882
47967
  });
47883
47968
  init_dotfile2 = __esm22(() => {
47884
- HASNA_DIR2 = join20(homedir10(), ".hasna");
47969
+ HASNA_DIR2 = join21(homedir10(), ".hasna");
47885
47970
  });
47886
47971
  exports_config2 = {};
47887
47972
  __export22(exports_config2, {
@@ -49457,10 +49542,10 @@ import {
49457
49542
  copyFileSync as copyFileSync7
49458
49543
  } from "fs";
49459
49544
  import { homedir as homedir11 } from "os";
49460
- import { join as join21, relative as relative3 } from "path";
49545
+ import { join as join24, relative as relative4 } from "path";
49461
49546
  import { existsSync as existsSync24, mkdirSync as mkdirSync24, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
49462
49547
  import { homedir as homedir24 } from "os";
49463
- import { join as join24 } from "path";
49548
+ import { join as join25 } from "path";
49464
49549
  import { readdirSync as readdirSync24, existsSync as existsSync34 } from "fs";
49465
49550
  import { join as join34 } from "path";
49466
49551
  import { homedir as homedir34 } from "os";
@@ -50300,13 +50385,13 @@ function custom4(check, _params = {}, fatal) {
50300
50385
  return ZodAny4.create();
50301
50386
  }
50302
50387
  function getDataDir5(serviceName) {
50303
- const dir = join21(HASNA_DIR3, serviceName);
50388
+ const dir = join24(HASNA_DIR3, serviceName);
50304
50389
  mkdirSync16(dir, { recursive: true });
50305
50390
  return dir;
50306
50391
  }
50307
50392
  function getDbPath3(serviceName) {
50308
50393
  const dir = getDataDir5(serviceName);
50309
- return join21(dir, `${serviceName}.db`);
50394
+ return join24(dir, `${serviceName}.db`);
50310
50395
  }
50311
50396
  function getConfigDir3() {
50312
50397
  return CONFIG_DIR5;
@@ -55765,7 +55850,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
55765
55850
  function parse(stream, callback) {
55766
55851
  const parser = new parser_1.Parser;
55767
55852
  stream.on("data", (buffer) => parser.parse(buffer, callback));
55768
- return new Promise((resolve3) => stream.on("end", () => resolve3()));
55853
+ return new Promise((resolve4) => stream.on("end", () => resolve4()));
55769
55854
  }
55770
55855
  exports.parse = parse;
55771
55856
  });
@@ -56430,12 +56515,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
56430
56515
  this._connect(callback);
56431
56516
  return;
56432
56517
  }
56433
- return new this._Promise((resolve3, reject) => {
56518
+ return new this._Promise((resolve4, reject) => {
56434
56519
  this._connect((error) => {
56435
56520
  if (error) {
56436
56521
  reject(error);
56437
56522
  } else {
56438
- resolve3(this);
56523
+ resolve4(this);
56439
56524
  }
56440
56525
  });
56441
56526
  });
@@ -56767,8 +56852,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
56767
56852
  readTimeout = config.query_timeout || this.connectionParameters.query_timeout;
56768
56853
  query = new Query4(config, values, callback);
56769
56854
  if (!query.callback) {
56770
- result = new this._Promise((resolve3, reject) => {
56771
- query.callback = (err2, res) => err2 ? reject(err2) : resolve3(res);
56855
+ result = new this._Promise((resolve4, reject) => {
56856
+ query.callback = (err2, res) => err2 ? reject(err2) : resolve4(res);
56772
56857
  }).catch((err2) => {
56773
56858
  Error.captureStackTrace(err2);
56774
56859
  throw err2;
@@ -56843,8 +56928,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
56843
56928
  if (cb) {
56844
56929
  this.connection.once("end", cb);
56845
56930
  } else {
56846
- return new this._Promise((resolve3) => {
56847
- this.connection.once("end", resolve3);
56931
+ return new this._Promise((resolve4) => {
56932
+ this.connection.once("end", resolve4);
56848
56933
  });
56849
56934
  }
56850
56935
  }
@@ -56889,8 +56974,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
56889
56974
  const cb = function(err2, client) {
56890
56975
  err2 ? rej(err2) : res(client);
56891
56976
  };
56892
- const result = new Promise2(function(resolve3, reject) {
56893
- res = resolve3;
56977
+ const result = new Promise2(function(resolve4, reject) {
56978
+ res = resolve4;
56894
56979
  rej = reject;
56895
56980
  }).catch((err2) => {
56896
56981
  Error.captureStackTrace(err2);
@@ -56951,7 +57036,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
56951
57036
  if (typeof Promise2.try === "function") {
56952
57037
  return Promise2.try(f);
56953
57038
  }
56954
- return new Promise2((resolve3) => resolve3(f()));
57039
+ return new Promise2((resolve4) => resolve4(f()));
56955
57040
  }
56956
57041
  _isFull() {
56957
57042
  return this._clients.length >= this.options.max;
@@ -57325,8 +57410,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
57325
57410
  NativeQuery.prototype._getPromise = function() {
57326
57411
  if (this._promise)
57327
57412
  return this._promise;
57328
- this._promise = new Promise(function(resolve3, reject) {
57329
- this._once("end", resolve3);
57413
+ this._promise = new Promise(function(resolve4, reject) {
57414
+ this._once("end", resolve4);
57330
57415
  this._once("error", reject);
57331
57416
  }.bind(this));
57332
57417
  return this._promise;
@@ -57500,12 +57585,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
57500
57585
  this._connect(callback);
57501
57586
  return;
57502
57587
  }
57503
- return new this._Promise((resolve3, reject) => {
57588
+ return new this._Promise((resolve4, reject) => {
57504
57589
  this._connect((error) => {
57505
57590
  if (error) {
57506
57591
  reject(error);
57507
57592
  } else {
57508
- resolve3(this);
57593
+ resolve4(this);
57509
57594
  }
57510
57595
  });
57511
57596
  });
@@ -57529,8 +57614,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
57529
57614
  query = new NativeQuery(config, values, callback);
57530
57615
  if (!query.callback) {
57531
57616
  let resolveOut, rejectOut;
57532
- result = new this._Promise((resolve3, reject) => {
57533
- resolveOut = resolve3;
57617
+ result = new this._Promise((resolve4, reject) => {
57618
+ resolveOut = resolve4;
57534
57619
  rejectOut = reject;
57535
57620
  }).catch((err2) => {
57536
57621
  Error.captureStackTrace(err2);
@@ -57588,8 +57673,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
57588
57673
  }
57589
57674
  let result;
57590
57675
  if (!cb) {
57591
- result = new this._Promise(function(resolve3, reject) {
57592
- cb = (err2) => err2 ? reject(err2) : resolve3();
57676
+ result = new this._Promise(function(resolve4, reject) {
57677
+ cb = (err2) => err2 ? reject(err2) : resolve4();
57593
57678
  });
57594
57679
  }
57595
57680
  this.native.end(function() {
@@ -60877,7 +60962,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
60877
60962
  init_external4();
60878
60963
  });
60879
60964
  init_dotfile3 = __esm23(() => {
60880
- HASNA_DIR3 = join21(homedir11(), ".hasna");
60965
+ HASNA_DIR3 = join24(homedir11(), ".hasna");
60881
60966
  });
60882
60967
  exports_config3 = {};
60883
60968
  __export23(exports_config3, {
@@ -60908,8 +60993,8 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
60908
60993
  schedule_minutes: exports_external4.number().default(0)
60909
60994
  }).default({})
60910
60995
  });
60911
- CONFIG_DIR5 = join24(homedir24(), ".hasna", "cloud");
60912
- CONFIG_PATH3 = join24(CONFIG_DIR5, "config.json");
60996
+ CONFIG_DIR5 = join25(homedir24(), ".hasna", "cloud");
60997
+ CONFIG_PATH3 = join25(CONFIG_DIR5, "config.json");
60913
60998
  });
60914
60999
  exports_discover3 = {};
60915
61000
  __export23(exports_discover3, {
@@ -62639,14 +62724,14 @@ Check the top-level render call using <` + parentName + ">.";
62639
62724
  var thenableResult = result;
62640
62725
  var wasAwaited = false;
62641
62726
  var thenable = {
62642
- then: function(resolve3, reject) {
62727
+ then: function(resolve4, reject) {
62643
62728
  wasAwaited = true;
62644
62729
  thenableResult.then(function(returnValue2) {
62645
62730
  popActScope(prevActScopeDepth);
62646
62731
  if (actScopeDepth === 0) {
62647
- recursivelyFlushAsyncActWork(returnValue2, resolve3, reject);
62732
+ recursivelyFlushAsyncActWork(returnValue2, resolve4, reject);
62648
62733
  } else {
62649
- resolve3(returnValue2);
62734
+ resolve4(returnValue2);
62650
62735
  }
62651
62736
  }, function(error2) {
62652
62737
  popActScope(prevActScopeDepth);
@@ -62675,20 +62760,20 @@ Check the top-level render call using <` + parentName + ">.";
62675
62760
  ReactCurrentActQueue.current = null;
62676
62761
  }
62677
62762
  var _thenable = {
62678
- then: function(resolve3, reject) {
62763
+ then: function(resolve4, reject) {
62679
62764
  if (ReactCurrentActQueue.current === null) {
62680
62765
  ReactCurrentActQueue.current = [];
62681
- recursivelyFlushAsyncActWork(returnValue, resolve3, reject);
62766
+ recursivelyFlushAsyncActWork(returnValue, resolve4, reject);
62682
62767
  } else {
62683
- resolve3(returnValue);
62768
+ resolve4(returnValue);
62684
62769
  }
62685
62770
  }
62686
62771
  };
62687
62772
  return _thenable;
62688
62773
  } else {
62689
62774
  var _thenable2 = {
62690
- then: function(resolve3, reject) {
62691
- resolve3(returnValue);
62775
+ then: function(resolve4, reject) {
62776
+ resolve4(returnValue);
62692
62777
  }
62693
62778
  };
62694
62779
  return _thenable2;
@@ -62704,7 +62789,7 @@ Check the top-level render call using <` + parentName + ">.";
62704
62789
  actScopeDepth = prevActScopeDepth;
62705
62790
  }
62706
62791
  }
62707
- function recursivelyFlushAsyncActWork(returnValue, resolve3, reject) {
62792
+ function recursivelyFlushAsyncActWork(returnValue, resolve4, reject) {
62708
62793
  {
62709
62794
  var queue = ReactCurrentActQueue.current;
62710
62795
  if (queue !== null) {
@@ -62713,16 +62798,16 @@ Check the top-level render call using <` + parentName + ">.";
62713
62798
  enqueueTask(function() {
62714
62799
  if (queue.length === 0) {
62715
62800
  ReactCurrentActQueue.current = null;
62716
- resolve3(returnValue);
62801
+ resolve4(returnValue);
62717
62802
  } else {
62718
- recursivelyFlushAsyncActWork(returnValue, resolve3, reject);
62803
+ recursivelyFlushAsyncActWork(returnValue, resolve4, reject);
62719
62804
  }
62720
62805
  });
62721
62806
  } catch (error2) {
62722
62807
  reject(error2);
62723
62808
  }
62724
62809
  } else {
62725
- resolve3(returnValue);
62810
+ resolve4(returnValue);
62726
62811
  }
62727
62812
  }
62728
62813
  }
@@ -63642,11 +63727,11 @@ __export(exports_dist5, {
63642
63727
  import { hostname as osHostname, platform as osPlatform } from "os";
63643
63728
  import { Database as Database4 } from "bun:sqlite";
63644
63729
  import { existsSync as existsSync18, mkdirSync as mkdirSync17 } from "fs";
63645
- import { dirname as dirname5, join as join25, resolve as resolve3 } from "path";
63730
+ import { dirname as dirname5, join as join26, resolve as resolve4 } from "path";
63646
63731
  import { existsSync as existsSync35 } from "fs";
63647
63732
  import { join as join35 } from "path";
63648
63733
  import { existsSync as existsSync25, mkdirSync as mkdirSync25, readFileSync as readFileSync11, readdirSync as readdirSync12, statSync as statSync5, writeFileSync as writeFileSync10 } from "fs";
63649
- import { join as join26 } from "path";
63734
+ import { join as join27 } from "path";
63650
63735
  import { existsSync as existsSync45, mkdirSync as mkdirSync35, readFileSync as readFileSync25, writeFileSync as writeFileSync25 } from "fs";
63651
63736
  import { homedir as homedir12 } from "os";
63652
63737
  import { join as join45 } from "path";
@@ -63663,7 +63748,7 @@ import {
63663
63748
  copyFileSync as copyFileSync8
63664
63749
  } from "fs";
63665
63750
  import { homedir as homedir25 } from "os";
63666
- import { join as join74, relative as relative4 } from "path";
63751
+ import { join as join74, relative as relative5 } from "path";
63667
63752
  import { existsSync as existsSync222, mkdirSync as mkdirSync222, readFileSync as readFileSync44, writeFileSync as writeFileSync42 } from "fs";
63668
63753
  import { homedir as homedir222 } from "os";
63669
63754
  import { join as join222 } from "path";
@@ -64121,9 +64206,9 @@ function isInMemoryDb2(path) {
64121
64206
  return path === ":memory:" || path.startsWith("file::memory:");
64122
64207
  }
64123
64208
  function findNearestTodosDb(startDir) {
64124
- let dir = resolve3(startDir);
64209
+ let dir = resolve4(startDir);
64125
64210
  while (true) {
64126
- const candidate = join25(dir, ".todos", "todos.db");
64211
+ const candidate = join26(dir, ".todos", "todos.db");
64127
64212
  if (existsSync18(candidate))
64128
64213
  return candidate;
64129
64214
  const parent = dirname5(dir);
@@ -64134,9 +64219,9 @@ function findNearestTodosDb(startDir) {
64134
64219
  return null;
64135
64220
  }
64136
64221
  function findGitRoot2(startDir) {
64137
- let dir = resolve3(startDir);
64222
+ let dir = resolve4(startDir);
64138
64223
  while (true) {
64139
- if (existsSync18(join25(dir, ".git")))
64224
+ if (existsSync18(join26(dir, ".git")))
64140
64225
  return dir;
64141
64226
  const parent = dirname5(dir);
64142
64227
  if (parent === dir)
@@ -64159,12 +64244,12 @@ function getDbPath4() {
64159
64244
  if (process.env["TODOS_DB_SCOPE"] === "project") {
64160
64245
  const gitRoot = findGitRoot2(cwd);
64161
64246
  if (gitRoot) {
64162
- return join25(gitRoot, ".todos", "todos.db");
64247
+ return join26(gitRoot, ".todos", "todos.db");
64163
64248
  }
64164
64249
  }
64165
64250
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
64166
- const newPath = join25(home, ".hasna", "todos", "todos.db");
64167
- const legacyPath = join25(home, ".todos", "todos.db");
64251
+ const newPath = join26(home, ".hasna", "todos", "todos.db");
64252
+ const legacyPath = join26(home, ".todos", "todos.db");
64168
64253
  if (!existsSync18(newPath) && existsSync18(legacyPath)) {
64169
64254
  return legacyPath;
64170
64255
  }
@@ -64173,7 +64258,7 @@ function getDbPath4() {
64173
64258
  function ensureDir3(filePath) {
64174
64259
  if (isInMemoryDb2(filePath))
64175
64260
  return;
64176
- const dir = dirname5(resolve3(filePath));
64261
+ const dir = dirname5(resolve4(filePath));
64177
64262
  if (!existsSync18(dir)) {
64178
64263
  mkdirSync17(dir, { recursive: true });
64179
64264
  }
@@ -64792,14 +64877,14 @@ function writeJsonFile(path, data) {
64792
64877
  `);
64793
64878
  }
64794
64879
  function readHighWaterMark(dir) {
64795
- const path = join26(dir, ".highwatermark");
64880
+ const path = join27(dir, ".highwatermark");
64796
64881
  if (!existsSync25(path))
64797
64882
  return 1;
64798
64883
  const val = parseInt(readFileSync11(path, "utf-8").trim(), 10);
64799
64884
  return isNaN(val) ? 1 : val;
64800
64885
  }
64801
64886
  function writeHighWaterMark(dir, value) {
64802
- writeFileSync10(join26(dir, ".highwatermark"), String(value));
64887
+ writeFileSync10(join27(dir, ".highwatermark"), String(value));
64803
64888
  }
64804
64889
  function getFileMtimeMs(path) {
64805
64890
  try {
@@ -80829,13 +80914,14 @@ function registerIntegrationAndMeta(server) {
80829
80914
  try {
80830
80915
  const sid = resolveSessionId(session_id);
80831
80916
  const page = getSessionPage(sid);
80832
- const { getCredentials: getCredentials2, loginWithCredentials: loginWithCredentials2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
80833
- const creds = await getCredentials2(service);
80917
+ const { lookupCredentials: lookupCredentials2, loginWithCredentials: loginWithCredentials2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
80918
+ const { credential: creds, method } = await lookupCredentials2(service);
80834
80919
  if (!creds)
80835
80920
  return err(new Error(`No credentials found for '${service}'. Add them: secrets set ${service}_email yourlogin && secrets set ${service}_password yourpass`));
80836
80921
  const result = await loginWithCredentials2(page, creds, {
80837
80922
  loginUrl: login_url,
80838
- saveProfile: save_profile ? service : undefined
80923
+ saveProfile: save_profile ? service : undefined,
80924
+ method
80839
80925
  });
80840
80926
  return json(result);
80841
80927
  } catch (e) {
@@ -81218,7 +81304,11 @@ function assertTuiSession(sessionId) {
81218
81304
  }
81219
81305
  }
81220
81306
  function getTuiSession(sessionId) {
81221
- return getSessionTuiSession(sessionId);
81307
+ const session = getSessionTuiSession(sessionId);
81308
+ if (!session) {
81309
+ throw new Error(`TUI session handle missing for session ${sessionId}. Close and re-open with engine="tui".`);
81310
+ }
81311
+ return session;
81222
81312
  }
81223
81313
  function getTuiMeta(sessionId) {
81224
81314
  const session = getTuiSession(sessionId);
@@ -81264,19 +81354,19 @@ async function withTuiHealth(sessionId, operation, options = {}) {
81264
81354
  } else if (!health.healthy) {
81265
81355
  throw Object.assign(new Error(`TUI session is unhealthy: ${health.reason}. Close and reopen the session.`), { code: "TUI_UNHEALTHY" });
81266
81356
  }
81267
- let timedOut = false;
81268
- const timer = setTimeout(() => {
81269
- timedOut = true;
81270
- }, timeoutMs);
81271
- try {
81272
- return await operation(page, session);
81273
- } catch (error) {
81274
- if (timedOut) {
81357
+ let timer;
81358
+ const timeout = new Promise((_, reject) => {
81359
+ timer = setTimeout(() => {
81275
81360
  const err2 = new Error(`${operationName} timed out after ${timeoutMs}ms \u2014 ttyd/playwright connection may be unhealthy. Status: ${health.healthy ? "was healthy before op" : "was already unhealthy"}. Try closing and re-opening the session.`);
81276
81361
  Object.assign(err2, { code: "TUI_TIMEOUT" });
81277
- throw err2;
81278
- }
81279
- throw error;
81362
+ reject(err2);
81363
+ }, timeoutMs);
81364
+ });
81365
+ try {
81366
+ return await Promise.race([
81367
+ operation(page, session),
81368
+ timeout
81369
+ ]);
81280
81370
  } finally {
81281
81371
  clearTimeout(timer);
81282
81372
  }
@@ -81575,6 +81665,7 @@ CONDITION SYNTAX:
81575
81665
  }, interval_ms)
81576
81666
  };
81577
81667
  activeRecordings2.set(sid, recording);
81668
+ trackTuiRecording(sid, recording.intervalId);
81578
81669
  return json({
81579
81670
  recording: true,
81580
81671
  session_id: sid,
@@ -81594,6 +81685,7 @@ CONDITION SYNTAX:
81594
81685
  if (!recording)
81595
81686
  return err(new Error("No active recording for this session"));
81596
81687
  clearInterval(recording.intervalId);
81688
+ stopTuiRecording(sid);
81597
81689
  activeRecordings2.delete(sid);
81598
81690
  const duration = (Date.now() - recording.startTime) / 1000;
81599
81691
  const header = {
@@ -81644,6 +81736,7 @@ var init_tui2 = __esm(() => {
81644
81736
  init_helpers();
81645
81737
  init_tui();
81646
81738
  init_session();
81739
+ init_tui_recording();
81647
81740
  KEY_MAP = {
81648
81741
  "ctrl+c": "\x03",
81649
81742
  "ctrl+d": "\x04",
@@ -81692,23 +81785,21 @@ var init_tui2 = __esm(() => {
81692
81785
  activeRecordings2 = new Map;
81693
81786
  });
81694
81787
 
81788
+ // src/mcp/http.ts
81789
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
81790
+ var init_http = () => {};
81791
+
81695
81792
  // src/mcp/index.ts
81696
81793
  var exports_mcp = {};
81794
+ __export(exports_mcp, {
81795
+ buildServer: () => buildServer
81796
+ });
81697
81797
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
81698
81798
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
81699
81799
  import { readFileSync as readFileSync12 } from "fs";
81700
- import { join as join27 } from "path";
81701
- var _pkg, server, _startupToolCount, transport;
81702
- var init_mcp = __esm(async () => {
81703
- init_dist();
81704
- init_sessions2();
81705
- init_actions2();
81706
- init_capture();
81707
- init_network2();
81708
- init_data();
81709
- init_tui2();
81710
- _pkg = JSON.parse(readFileSync12(join27(import.meta.dir, "../../package.json"), "utf8"));
81711
- server = new McpServer2({
81800
+ import { join as join28 } from "path";
81801
+ function buildServer() {
81802
+ const server = new McpServer2({
81712
81803
  name: "@hasna/browser",
81713
81804
  version: _pkg.version
81714
81805
  });
@@ -81719,10 +81810,20 @@ var init_mcp = __esm(async () => {
81719
81810
  register11(server);
81720
81811
  register12(server);
81721
81812
  registerCloudTools(server, "browser");
81722
- _startupToolCount = Object.keys(server._registeredTools ?? {}).length;
81723
- console.error(`@hasna/browser v${_pkg.version} \u2014 ${_startupToolCount} tools | data: ${(await Promise.resolve().then(() => (init_schema(), exports_schema))).getDataDir()}`);
81724
- transport = new StdioServerTransport;
81725
- await server.connect(transport);
81813
+ return server;
81814
+ }
81815
+ var _pkg;
81816
+ var init_mcp = __esm(() => {
81817
+ init_dist();
81818
+ init_sessions2();
81819
+ init_actions2();
81820
+ init_capture();
81821
+ init_network2();
81822
+ init_data();
81823
+ init_tui2();
81824
+ init_http();
81825
+ _pkg = JSON.parse(readFileSync12(join28(import.meta.dir, "../../package.json"), "utf8"));
81826
+ if (false) {}
81726
81827
  });
81727
81828
 
81728
81829
  // src/db/snapshots.ts
@@ -81763,7 +81864,7 @@ var init_snapshots = __esm(() => {
81763
81864
 
81764
81865
  // src/server/index.ts
81765
81866
  var exports_server = {};
81766
- import { join as join28 } from "path";
81867
+ import { join as join30 } from "path";
81767
81868
  import { existsSync as existsSync19 } from "fs";
81768
81869
  function corsHeaders(origin) {
81769
81870
  const headers = {
@@ -81829,7 +81930,7 @@ function serverError(e, extraHeaders) {
81829
81930
  headers: { "Content-Type": "application/json", ...extraHeaders ?? {} }
81830
81931
  });
81831
81932
  }
81832
- var PORT, API_KEY, ALLOWED_ORIGIN, startTime, networkCleanup, consoleCleanup, harCaptures2, server2;
81933
+ var PORT, API_KEY, ALLOWED_ORIGIN, startTime, networkCleanup, consoleCleanup, harCaptures2, server;
81833
81934
  var init_server = __esm(() => {
81834
81935
  init_session();
81835
81936
  init_actions();
@@ -81854,7 +81955,7 @@ var init_server = __esm(() => {
81854
81955
  networkCleanup = new Map;
81855
81956
  consoleCleanup = new Map;
81856
81957
  harCaptures2 = new Map;
81857
- server2 = Bun.serve({
81958
+ server = Bun.serve({
81858
81959
  port: PORT,
81859
81960
  async fetch(req) {
81860
81961
  const url = new URL(req.url);
@@ -82150,19 +82251,19 @@ var init_server = __esm(() => {
82150
82251
  const id = path.split("/")[3];
82151
82252
  return ok({ deleted: deleteDownload(id) });
82152
82253
  }
82153
- const dashboardDist = join28(import.meta.dir, "../../dashboard/dist");
82254
+ const dashboardDist = join30(import.meta.dir, "../../dashboard/dist");
82154
82255
  if (existsSync19(dashboardDist)) {
82155
82256
  const cleanPath = path.replace(/^\//, "");
82156
82257
  if (cleanPath.includes("..") || cleanPath.startsWith("/"))
82157
82258
  return notFound("Not found", headers);
82158
- const filePath = path === "/" ? join28(dashboardDist, "index.html") : join28(dashboardDist, cleanPath);
82159
- const resolved = await Bun.file(filePath).arrayBuffer().then(() => join28(dashboardDist, cleanPath)) || "";
82259
+ const filePath = path === "/" ? join30(dashboardDist, "index.html") : join30(dashboardDist, cleanPath);
82260
+ const resolved = await Bun.file(filePath).arrayBuffer().then(() => join30(dashboardDist, cleanPath)) || "";
82160
82261
  if (!resolved.startsWith(dashboardDist))
82161
82262
  return notFound("Not found", headers);
82162
82263
  if (existsSync19(filePath)) {
82163
82264
  return new Response(Bun.file(filePath), { headers });
82164
82265
  }
82165
- return new Response(Bun.file(join28(dashboardDist, "index.html")), { headers });
82266
+ return new Response(Bun.file(join30(dashboardDist, "index.html")), { headers });
82166
82267
  }
82167
82268
  if (path === "/" || path === "") {
82168
82269
  return new Response("@hasna/browser REST API running. Dashboard not built.", {
@@ -82197,7 +82298,7 @@ var {
82197
82298
  // src/cli/index.tsx
82198
82299
  init_dist();
82199
82300
  import { readFileSync as readFileSync13 } from "fs";
82200
- import { join as join29 } from "path";
82301
+ import { join as join31 } from "path";
82201
82302
 
82202
82303
  // src/cli/commands/browse.ts
82203
82304
  init_session();
@@ -83022,7 +83123,7 @@ Stopping watch. ${checkCount} checks performed.`));
83022
83123
  });
83023
83124
  });
83024
83125
  program2.command("mcp").description("Start the MCP server (stdio)").action(async () => {
83025
- await init_mcp().then(() => exports_mcp);
83126
+ await Promise.resolve().then(() => (init_mcp(), exports_mcp));
83026
83127
  });
83027
83128
  program2.command("serve").description("Start the REST API server").option("--port <port>", "Port to listen on", "7030").action(async (opts) => {
83028
83129
  process.env["BROWSER_SERVER_PORT"] = opts.port;
@@ -83055,7 +83156,7 @@ Stopping watch. ${checkCount} checks performed.`));
83055
83156
  }
83056
83157
 
83057
83158
  // src/cli/index.tsx
83058
- var pkg = JSON.parse(readFileSync13(join29(import.meta.dir, "../../package.json"), "utf8"));
83159
+ var pkg = JSON.parse(readFileSync13(join31(import.meta.dir, "../../package.json"), "utf8"));
83059
83160
  var program2 = new Command;
83060
83161
  program2.name("browser").description("@hasna/browser \u2014 general-purpose browser agent CLI").version(pkg.version);
83061
83162
  register(program2);
@@ -83063,4 +83164,9 @@ register2(program2);
83063
83164
  register3(program2);
83064
83165
  register13(program2);
83065
83166
  registerCloudCommands(program2, "browser");
83066
- program2.parseAsync(process.argv);
83167
+ try {
83168
+ await program2.parseAsync(process.argv);
83169
+ } finally {
83170
+ const { closeAllSessions: closeAllSessions2 } = await Promise.resolve().then(() => (init_session(), exports_session));
83171
+ await closeAllSessions2();
83172
+ }