@hasna/browser 0.2.6 → 0.3.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/mcp/index.js CHANGED
@@ -4,6 +4,7 @@ var __create = Object.create;
4
4
  var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
9
  var __toESM = (mod, isNodeMode, target) => {
9
10
  target = mod != null ? __create(__getProtoOf(mod)) : {};
@@ -16,6 +17,20 @@ var __toESM = (mod, isNodeMode, target) => {
16
17
  });
17
18
  return to;
18
19
  };
20
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
21
+ var __toCommonJS = (from) => {
22
+ var entry = __moduleCache.get(from), desc;
23
+ if (entry)
24
+ return entry;
25
+ entry = __defProp({}, "__esModule", { value: true });
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
+ }));
31
+ __moduleCache.set(from, entry);
32
+ return entry;
33
+ };
19
34
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
35
  var __export = (target, all) => {
21
36
  for (var name in all)
@@ -372,6 +387,50 @@ function runMigrations(db) {
372
387
  );
373
388
  CREATE INDEX IF NOT EXISTS idx_api_endpoints_session ON api_endpoints(session_id);
374
389
  `
390
+ },
391
+ {
392
+ version: 9,
393
+ sql: `
394
+ CREATE TABLE IF NOT EXISTS scripts (
395
+ id TEXT PRIMARY KEY,
396
+ name TEXT NOT NULL UNIQUE,
397
+ domain TEXT NOT NULL DEFAULT '',
398
+ description TEXT DEFAULT '',
399
+ variables TEXT NOT NULL DEFAULT '{}',
400
+ created_at TEXT DEFAULT (datetime('now')),
401
+ updated_at TEXT DEFAULT (datetime('now')),
402
+ last_run TEXT,
403
+ run_count INTEGER DEFAULT 0
404
+ );
405
+
406
+ CREATE TABLE IF NOT EXISTS script_steps (
407
+ id TEXT PRIMARY KEY,
408
+ script_id TEXT NOT NULL REFERENCES scripts(id) ON DELETE CASCADE,
409
+ step_order INTEGER NOT NULL,
410
+ type TEXT NOT NULL,
411
+ config TEXT NOT NULL DEFAULT '{}',
412
+ description TEXT DEFAULT '',
413
+ ai_enabled INTEGER DEFAULT 0,
414
+ ai_config TEXT DEFAULT '{}'
415
+ );
416
+ CREATE INDEX IF NOT EXISTS idx_script_steps_order ON script_steps(script_id, step_order);
417
+
418
+ CREATE TABLE IF NOT EXISTS script_runs (
419
+ id TEXT PRIMARY KEY,
420
+ script_id TEXT NOT NULL REFERENCES scripts(id) ON DELETE CASCADE,
421
+ status TEXT NOT NULL DEFAULT 'running',
422
+ current_step INTEGER DEFAULT 0,
423
+ total_steps INTEGER DEFAULT 0,
424
+ current_description TEXT DEFAULT '',
425
+ variables TEXT DEFAULT '{}',
426
+ steps_log TEXT DEFAULT '[]',
427
+ errors TEXT DEFAULT '[]',
428
+ started_at TEXT DEFAULT (datetime('now')),
429
+ completed_at TEXT,
430
+ duration_ms INTEGER
431
+ );
432
+ CREATE INDEX IF NOT EXISTS idx_script_runs_script ON script_runs(script_id, status);
433
+ `
375
434
  }
376
435
  ];
377
436
  for (const m of migrations) {
@@ -2271,6 +2330,10 @@ var init_snapshot = __esm(() => {
2271
2330
  });
2272
2331
 
2273
2332
  // src/lib/self-heal.ts
2333
+ var exports_self_heal = {};
2334
+ __export(exports_self_heal, {
2335
+ healSelector: () => healSelector
2336
+ });
2274
2337
  async function healSelector(page, selector, sessionId) {
2275
2338
  const attempts = [];
2276
2339
  attempts.push(`selector: ${selector}`);
@@ -6583,7 +6646,7 @@ var require_operation = __commonJS((exports, module) => {
6583
6646
  // node_modules/@img/colour/color.cjs
6584
6647
  var require_color = __commonJS((exports, module) => {
6585
6648
  var __defProp2 = Object.defineProperty;
6586
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6649
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
6587
6650
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
6588
6651
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
6589
6652
  var __export2 = (target, all) => {
@@ -6594,16 +6657,16 @@ var require_color = __commonJS((exports, module) => {
6594
6657
  if (from && typeof from === "object" || typeof from === "function") {
6595
6658
  for (let key of __getOwnPropNames2(from))
6596
6659
  if (!__hasOwnProp2.call(to, key) && key !== except)
6597
- __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
6660
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
6598
6661
  }
6599
6662
  return to;
6600
6663
  };
6601
- var __toCommonJS = (mod) => __copyProps(__defProp2({}, "__esModule", { value: true }), mod);
6664
+ var __toCommonJS2 = (mod) => __copyProps(__defProp2({}, "__esModule", { value: true }), mod);
6602
6665
  var index_exports = {};
6603
6666
  __export2(index_exports, {
6604
6667
  default: () => index_default
6605
6668
  });
6606
- module.exports = __toCommonJS(index_exports);
6669
+ module.exports = __toCommonJS2(index_exports);
6607
6670
  var colors = {
6608
6671
  aliceblue: [240, 248, 255],
6609
6672
  antiquewhite: [250, 235, 215],
@@ -10602,395 +10665,498 @@ async function clickByVision(page, description, opts) {
10602
10665
  }
10603
10666
  var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
10604
10667
 
10605
- // src/lib/login-scripts.ts
10606
- var exports_login_scripts = {};
10607
- __export(exports_login_scripts, {
10608
- saveScript: () => saveScript,
10609
- runScriptAsync: () => runScriptAsync,
10610
- runScript: () => runScript,
10611
- loadScript: () => loadScript,
10668
+ // src/db/scripts.ts
10669
+ var exports_scripts = {};
10670
+ __export(exports_scripts, {
10671
+ upsertScript: () => upsertScript,
10672
+ updateRunProgress: () => updateRunProgress,
10673
+ startRun: () => startRun,
10674
+ migrateJsonScripts: () => migrateJsonScripts,
10612
10675
  listScripts: () => listScripts,
10613
- listJobs: () => listJobs,
10614
- getJob: () => getJob,
10676
+ listRuns: () => listRuns,
10677
+ getSteps: () => getSteps,
10678
+ getScriptByName: () => getScriptByName,
10679
+ getScript: () => getScript,
10680
+ getRun: () => getRun,
10681
+ deleteScriptByName: () => deleteScriptByName,
10615
10682
  deleteScript: () => deleteScript,
10616
- createScriptFromJSON: () => createScriptFromJSON,
10617
- createScriptFromFile: () => createScriptFromFile
10683
+ createScript: () => createScript,
10684
+ completeRun: () => completeRun
10618
10685
  });
10619
10686
  import { randomUUID as randomUUID12 } from "crypto";
10620
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync9, readdirSync as readdirSync4 } from "fs";
10621
- import { join as join9 } from "path";
10622
- function getJob(jobId) {
10623
- return activeJobs.get(jobId) ?? null;
10624
- }
10625
- function listJobs() {
10626
- return Array.from(activeJobs.values());
10687
+ function createScript(data) {
10688
+ const db = getDatabase();
10689
+ const id = randomUUID12();
10690
+ db.prepare("INSERT INTO scripts (id, name, domain, description, variables) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.domain ?? "", data.description ?? "", JSON.stringify(data.variables ?? {}));
10691
+ for (let i = 0;i < data.steps.length; i++) {
10692
+ const step = data.steps[i];
10693
+ db.prepare("INSERT INTO script_steps (id, script_id, step_order, type, config, description, ai_enabled, ai_config) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(randomUUID12(), id, i, step.type, JSON.stringify(step.config), step.description ?? "", step.ai_enabled ? 1 : 0, JSON.stringify(step.ai_config ?? {}));
10694
+ }
10695
+ return getScript(id);
10627
10696
  }
10628
- function getScriptsDir() {
10629
- const dir = join9(getDataDir(), "scripts");
10630
- mkdirSync9(dir, { recursive: true });
10631
- return dir;
10697
+ function upsertScript(data) {
10698
+ const existing = getScriptByName(data.name);
10699
+ if (existing) {
10700
+ deleteScript(existing.id);
10701
+ }
10702
+ return createScript(data);
10632
10703
  }
10633
- function saveScript(script) {
10634
- const dir = getScriptsDir();
10635
- const path = join9(dir, `${script.name}.json`);
10636
- script.updated_at = new Date().toISOString();
10637
- if (!script.created_at)
10638
- script.created_at = script.updated_at;
10639
- writeFileSync3(path, JSON.stringify(script, null, 2));
10640
- return path;
10704
+ function getScript(id) {
10705
+ const db = getDatabase();
10706
+ const row = db.query("SELECT * FROM scripts WHERE id = ?").get(id);
10707
+ if (!row)
10708
+ return null;
10709
+ return { ...row, variables: JSON.parse(row.variables), run_count: row.run_count ?? 0 };
10641
10710
  }
10642
- function loadScript(name) {
10643
- const path = join9(getScriptsDir(), `${name}.json`);
10644
- if (!existsSync5(path))
10711
+ function getScriptByName(name) {
10712
+ const db = getDatabase();
10713
+ const row = db.query("SELECT * FROM scripts WHERE name = ?").get(name);
10714
+ if (!row)
10645
10715
  return null;
10646
- return JSON.parse(readFileSync3(path, "utf8"));
10716
+ return { ...row, variables: JSON.parse(row.variables), run_count: row.run_count ?? 0 };
10647
10717
  }
10648
10718
  function listScripts() {
10649
- const dir = getScriptsDir();
10719
+ const db = getDatabase();
10720
+ return db.query("SELECT * FROM scripts ORDER BY updated_at DESC").all().map((row) => ({ ...row, variables: JSON.parse(row.variables), run_count: row.run_count ?? 0 }));
10721
+ }
10722
+ function deleteScript(id) {
10723
+ const db = getDatabase();
10724
+ return db.prepare("DELETE FROM scripts WHERE id = ?").run(id).changes > 0;
10725
+ }
10726
+ function deleteScriptByName(name) {
10727
+ const db = getDatabase();
10728
+ return db.prepare("DELETE FROM scripts WHERE name = ?").run(name).changes > 0;
10729
+ }
10730
+ function getSteps(scriptId) {
10731
+ const db = getDatabase();
10732
+ return db.query("SELECT * FROM script_steps WHERE script_id = ? ORDER BY step_order").all(scriptId).map((row) => ({
10733
+ ...row,
10734
+ config: JSON.parse(row.config),
10735
+ ai_enabled: !!row.ai_enabled,
10736
+ ai_config: JSON.parse(row.ai_config)
10737
+ }));
10738
+ }
10739
+ function startRun(scriptId, totalSteps) {
10740
+ const db = getDatabase();
10741
+ const id = randomUUID12();
10742
+ db.prepare("INSERT INTO script_runs (id, script_id, status, total_steps) VALUES (?, ?, 'running', ?)").run(id, scriptId, totalSteps);
10743
+ return getRun(id);
10744
+ }
10745
+ function updateRunProgress(runId, step, description, stepsLog, variables) {
10746
+ const db = getDatabase();
10747
+ db.prepare("UPDATE script_runs SET current_step = ?, current_description = ?, steps_log = ?, variables = ? WHERE id = ?").run(step, description, JSON.stringify(stepsLog), JSON.stringify(variables), runId);
10748
+ }
10749
+ function completeRun(runId, status, errors2, durationMs) {
10750
+ const db = getDatabase();
10751
+ db.prepare("UPDATE script_runs SET status = ?, errors = ?, duration_ms = ?, completed_at = datetime('now') WHERE id = ?").run(status, JSON.stringify(errors2), durationMs, runId);
10752
+ const run = getRun(runId);
10753
+ if (run) {
10754
+ db.prepare("UPDATE scripts SET last_run = datetime('now'), run_count = run_count + 1, updated_at = datetime('now') WHERE id = ?").run(run.script_id);
10755
+ }
10756
+ }
10757
+ function getRun(runId) {
10758
+ const db = getDatabase();
10759
+ const row = db.query("SELECT * FROM script_runs WHERE id = ?").get(runId);
10760
+ if (!row)
10761
+ return null;
10762
+ return {
10763
+ ...row,
10764
+ variables: JSON.parse(row.variables),
10765
+ steps_log: JSON.parse(row.steps_log),
10766
+ errors: JSON.parse(row.errors)
10767
+ };
10768
+ }
10769
+ function listRuns(scriptId) {
10770
+ const db = getDatabase();
10771
+ const query = scriptId ? db.query("SELECT * FROM script_runs WHERE script_id = ? ORDER BY started_at DESC LIMIT 20").all(scriptId) : db.query("SELECT * FROM script_runs ORDER BY started_at DESC LIMIT 20").all();
10772
+ return query.map((row) => ({
10773
+ ...row,
10774
+ variables: JSON.parse(row.variables),
10775
+ steps_log: JSON.parse(row.steps_log),
10776
+ errors: JSON.parse(row.errors)
10777
+ }));
10778
+ }
10779
+ function migrateJsonScripts() {
10780
+ const { existsSync: existsSync5, readdirSync: readdirSync4, readFileSync: readFileSync3 } = __require("fs");
10781
+ const { join: join9 } = __require("path");
10782
+ const { getDataDir: getDataDir4 } = (init_schema(), __toCommonJS(exports_schema));
10783
+ const dir = join9(getDataDir4(), "scripts");
10650
10784
  if (!existsSync5(dir))
10651
- return [];
10652
- return readdirSync4(dir).filter((f) => f.endsWith(".json")).map((f) => {
10785
+ return 0;
10786
+ const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
10787
+ let migrated = 0;
10788
+ for (const file of files) {
10653
10789
  try {
10654
- const script = JSON.parse(readFileSync3(join9(dir, f), "utf8"));
10655
- return { name: script.name, domain: script.domain, description: script.description, steps: script.steps.length };
10656
- } catch {
10657
- return null;
10658
- }
10659
- }).filter(Boolean);
10790
+ const raw = JSON.parse(readFileSync3(join9(dir, file), "utf8"));
10791
+ if (!raw.name || !raw.steps)
10792
+ continue;
10793
+ if (getScriptByName(raw.name))
10794
+ continue;
10795
+ const steps = raw.steps.map((s) => {
10796
+ const isAI = s.type === "ai";
10797
+ return {
10798
+ type: isAI ? "extract" : s.type,
10799
+ config: {
10800
+ action: s.action,
10801
+ selector: s.selector,
10802
+ url: s.url,
10803
+ value: s.value,
10804
+ text: s.text,
10805
+ timeout: s.timeout,
10806
+ connector: s.connector,
10807
+ args: s.args,
10808
+ format: s.format,
10809
+ pattern: s.pattern,
10810
+ json_path: s.json_path,
10811
+ check: s.check,
10812
+ source: s.check,
10813
+ seconds: s.seconds,
10814
+ equals: s.equals,
10815
+ contains: s.contains,
10816
+ skip_to: s.skip_to,
10817
+ name: s.name,
10818
+ save_as: s.save_as,
10819
+ ...isAI ? { prompt: s.prompt, source: s.check ?? "last_output" } : {}
10820
+ },
10821
+ description: s.description ?? "",
10822
+ ai_enabled: isAI || !!s.ai?.enabled,
10823
+ ai_config: isAI ? { provider: s.model === "haiku" || s.model === "sonnet" || s.model === "opus" ? "anthropic" : "cerebras", model: s.model ?? "fast" } : s.ai ?? {}
10824
+ };
10825
+ });
10826
+ createScript({
10827
+ name: raw.name,
10828
+ domain: raw.domain ?? "",
10829
+ description: raw.description ?? "",
10830
+ variables: raw.variables ?? {},
10831
+ steps
10832
+ });
10833
+ migrated++;
10834
+ } catch {}
10835
+ }
10836
+ return migrated;
10660
10837
  }
10661
- function deleteScript(name) {
10662
- const path = join9(getScriptsDir(), `${name}.json`);
10663
- if (!existsSync5(path))
10664
- return false;
10665
- const { unlinkSync: unlinkSync3 } = __require("fs");
10666
- unlinkSync3(path);
10667
- return true;
10838
+ var init_scripts = __esm(() => {
10839
+ init_schema();
10840
+ });
10841
+
10842
+ // src/lib/ai-inference.ts
10843
+ function resolve(opts) {
10844
+ if (opts?.model && ALIASES[opts.model])
10845
+ return ALIASES[opts.model];
10846
+ if (opts?.provider && opts?.model)
10847
+ return { provider: opts.provider, model: opts.model };
10848
+ if (opts?.provider === "anthropic")
10849
+ return ALIASES.haiku;
10850
+ return ALIASES.fast;
10851
+ }
10852
+ async function infer(prompt, opts) {
10853
+ const { provider, model } = resolve(opts);
10854
+ const maxTokens = opts?.maxTokens ?? 1024;
10855
+ if (provider === "anthropic") {
10856
+ const apiKey2 = process.env["ANTHROPIC_API_KEY"];
10857
+ if (!apiKey2)
10858
+ throw new Error("ANTHROPIC_API_KEY not set");
10859
+ const res2 = await fetch("https://api.anthropic.com/v1/messages", {
10860
+ method: "POST",
10861
+ headers: { "content-type": "application/json", "x-api-key": apiKey2, "anthropic-version": "2023-06-01" },
10862
+ body: JSON.stringify({ model, max_tokens: maxTokens, messages: [{ role: "user", content: prompt }] })
10863
+ });
10864
+ if (!res2.ok)
10865
+ throw new Error(`Anthropic API ${res2.status}: ${(await res2.text()).slice(0, 200)}`);
10866
+ const data2 = await res2.json();
10867
+ return data2.content?.[0]?.text ?? "";
10868
+ }
10869
+ const apiKey = process.env["CEREBRAS_API_KEY"];
10870
+ if (!apiKey)
10871
+ throw new Error("CEREBRAS_API_KEY not set");
10872
+ const res = await fetch("https://api.cerebras.ai/v1/chat/completions", {
10873
+ method: "POST",
10874
+ headers: { "content-type": "application/json", Authorization: `Bearer ${apiKey}` },
10875
+ body: JSON.stringify({ model, max_tokens: maxTokens, temperature: opts?.temperature ?? 0, messages: [{ role: "user", content: prompt }] })
10876
+ });
10877
+ if (!res.ok)
10878
+ throw new Error(`Cerebras API ${res.status}: ${(await res.text()).slice(0, 200)}`);
10879
+ const data = await res.json();
10880
+ return data.choices?.[0]?.message?.content ?? "";
10668
10881
  }
10882
+ var ALIASES;
10883
+ var init_ai_inference = __esm(() => {
10884
+ ALIASES = {
10885
+ fast: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
10886
+ scout: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
10887
+ maverick: { provider: "cerebras", model: "llama-4-maverick-17b-128e-instruct" },
10888
+ haiku: { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
10889
+ sonnet: { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
10890
+ opus: { provider: "anthropic", model: "claude-opus-4-6" }
10891
+ };
10892
+ });
10893
+
10894
+ // src/lib/script-engine.ts
10895
+ var exports_script_engine = {};
10896
+ __export(exports_script_engine, {
10897
+ executeScriptSync: () => executeScriptSync,
10898
+ executeScript: () => executeScript
10899
+ });
10669
10900
  function interpolate(template, vars) {
10670
- return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, key) => {
10671
- return vars[key] ?? match;
10901
+ return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
10902
+ }
10903
+ function interpolateConfig(config, vars) {
10904
+ const result = {};
10905
+ for (const [k, v] of Object.entries(config)) {
10906
+ if (typeof v === "string")
10907
+ result[k] = interpolate(v, vars);
10908
+ else if (Array.isArray(v))
10909
+ result[k] = v.map((item) => typeof item === "string" ? interpolate(item, vars) : item);
10910
+ else
10911
+ result[k] = v;
10912
+ }
10913
+ return result;
10914
+ }
10915
+ function executeScript(scriptId, page, overrides = {}) {
10916
+ const steps = getSteps(scriptId);
10917
+ const run = startRun(scriptId, steps.length);
10918
+ _runSteps(run.id, scriptId, steps, page, overrides).catch((err) => {
10919
+ completeRun(run.id, "failed", [err instanceof Error ? err.message : String(err)], 0);
10672
10920
  });
10921
+ return run.id;
10673
10922
  }
10674
- function stepDescription(step) {
10675
- return step.description ?? `${step.type}${step.action ? `:${step.action}` : ""}${step.connector ? `:${step.connector}` : ""}`;
10923
+ async function executeScriptSync(scriptId, page, overrides = {}) {
10924
+ const steps = getSteps(scriptId);
10925
+ const run = startRun(scriptId, steps.length);
10926
+ return _runSteps(run.id, scriptId, steps, page, overrides);
10676
10927
  }
10677
- async function runScript(script, page, overrides = {}, jobId) {
10928
+ async function _runSteps(runId, scriptId, steps, page, overrides) {
10678
10929
  const t0 = Date.now();
10679
- const vars = { ...script.variables, ...overrides };
10930
+ const { getScript: getScript2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
10931
+ const script = getScript2(scriptId);
10932
+ const vars = { ...script?.variables ?? {}, ...overrides };
10680
10933
  const errors2 = [];
10934
+ const stepsLog = [];
10681
10935
  let executed = 0;
10682
10936
  let failed = 0;
10683
- const job = jobId && activeJobs.has(jobId) ? activeJobs.get(jobId) : {
10684
- id: jobId ?? randomUUID12(),
10685
- script_name: script.name,
10686
- status: "running",
10687
- current_step: 0,
10688
- total_steps: script.steps.length,
10689
- current_step_description: "Starting...",
10690
- steps_log: [],
10691
- started_at: new Date().toISOString()
10692
- };
10693
- activeJobs.set(job.id, job);
10694
- for (let i = 0;i < script.steps.length; i++) {
10695
- const step = script.steps[i];
10696
- const desc = stepDescription(step);
10937
+ for (let i = 0;i < steps.length; i++) {
10938
+ const step = steps[i];
10939
+ const cfg = interpolateConfig(step.config, vars);
10940
+ const desc = step.description || `${step.type}`;
10697
10941
  executed++;
10698
- job.current_step = i + 1;
10699
- job.current_step_description = desc;
10700
- job.steps_log.push({ step: i + 1, type: step.type, description: desc, status: "running" });
10942
+ stepsLog.push({ step: i + 1, type: step.type, description: desc, status: "running" });
10943
+ updateRunProgress(runId, i + 1, desc, stepsLog, vars);
10701
10944
  const stepStart = Date.now();
10702
10945
  try {
10703
10946
  switch (step.type) {
10704
10947
  case "browser":
10705
- await runBrowserStep(step, page, vars);
10948
+ await execBrowser(cfg, step, page, vars);
10706
10949
  break;
10707
10950
  case "connector":
10708
- await runConnectorStep(step, vars);
10951
+ await execConnector(cfg, step, vars);
10709
10952
  break;
10710
10953
  case "extract":
10711
- runExtractStep(step, vars);
10712
- break;
10713
- case "ai":
10714
- await runAIStep(step, vars);
10954
+ await execExtract(cfg, step, vars);
10715
10955
  break;
10716
10956
  case "wait":
10717
- await new Promise((r) => setTimeout(r, (step.seconds ?? 3) * 1000));
10957
+ await new Promise((r) => setTimeout(r, (cfg.seconds ?? 3) * 1000));
10718
10958
  break;
10719
- case "condition": {
10720
- const checkVal = vars[step.check ?? ""];
10721
- let conditionMet = true;
10722
- if (step.equals !== undefined)
10723
- conditionMet = checkVal === interpolate(step.equals, vars);
10724
- if (step.contains !== undefined)
10725
- conditionMet = checkVal?.includes(interpolate(step.contains, vars)) ?? false;
10726
- if (!conditionMet && step.skip_to !== undefined) {
10727
- i = step.skip_to - 1;
10728
- }
10959
+ case "condition":
10960
+ i = execCondition(cfg, vars, i);
10729
10961
  break;
10730
- }
10731
- case "save_state": {
10732
- const stateName = interpolate(step.name ?? script.name, vars);
10733
- try {
10734
- const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
10735
- const path = await saveStateFromPage2(page, stateName);
10736
- vars["saved_state_path"] = path;
10737
- } catch {}
10962
+ case "save_state":
10963
+ await execSaveState(cfg, page, vars);
10738
10964
  break;
10739
- }
10740
10965
  }
10741
- const logEntry = job.steps_log[job.steps_log.length - 1];
10742
- logEntry.status = "ok";
10743
- logEntry.duration_ms = Date.now() - stepStart;
10966
+ stepsLog[stepsLog.length - 1].status = "ok";
10967
+ stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
10744
10968
  } catch (err) {
10745
10969
  failed++;
10746
- const msg = `Step ${i + 1} (${step.type}/${step.action ?? step.connector ?? ""}): ${err instanceof Error ? err.message : String(err)}`;
10970
+ const msg = `Step ${i + 1} (${step.type}): ${err instanceof Error ? err.message : String(err)}`;
10747
10971
  errors2.push(msg);
10748
- const logEntry = job.steps_log[job.steps_log.length - 1];
10749
- logEntry.status = "failed";
10750
- logEntry.error = err instanceof Error ? err.message : String(err);
10751
- logEntry.duration_ms = Date.now() - stepStart;
10752
- if (step.type === "browser" && step.action === "navigate")
10972
+ stepsLog[stepsLog.length - 1].status = "failed";
10973
+ stepsLog[stepsLog.length - 1].error = err instanceof Error ? err.message : String(err);
10974
+ stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
10975
+ if (step.type === "browser" && cfg.action === "navigate")
10753
10976
  break;
10754
10977
  }
10978
+ updateRunProgress(runId, i + 1, desc, stepsLog, vars);
10755
10979
  }
10756
- const result = {
10757
- success: failed === 0,
10758
- steps_executed: executed,
10759
- steps_failed: failed,
10760
- variables: vars,
10761
- errors: errors2,
10762
- duration_ms: Date.now() - t0
10763
- };
10764
- job.status = failed === 0 ? "completed" : "failed";
10765
- job.result = result;
10766
- return result;
10980
+ const durationMs = Date.now() - t0;
10981
+ const status = failed === 0 ? "completed" : "failed";
10982
+ completeRun(runId, status, errors2, durationMs);
10983
+ return { run_id: runId, success: failed === 0, steps_executed: executed, steps_failed: failed, errors: errors2, duration_ms: durationMs, variables: vars };
10767
10984
  }
10768
- function runScriptAsync(script, page, overrides = {}) {
10769
- const jobId = randomUUID12();
10770
- const job = {
10771
- id: jobId,
10772
- script_name: script.name,
10773
- status: "running",
10774
- current_step: 0,
10775
- total_steps: script.steps.length,
10776
- current_step_description: "Starting...",
10777
- steps_log: [],
10778
- started_at: new Date().toISOString()
10779
- };
10780
- activeJobs.set(jobId, job);
10781
- runScript(script, page, overrides, jobId).catch((err) => {
10782
- job.status = "failed";
10783
- job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
10784
- });
10785
- return jobId;
10786
- }
10787
- async function runBrowserStep(step, page, vars) {
10788
- const action = step.action;
10789
- if (!action)
10790
- throw new Error("Browser step missing action");
10985
+ async function execBrowser(cfg, step, page, vars) {
10986
+ const action = cfg.action;
10791
10987
  switch (action) {
10792
- case "navigate": {
10793
- const url = interpolate(step.url ?? "", vars);
10794
- await page.goto(url, { waitUntil: "domcontentloaded", timeout: step.timeout ?? 30000 });
10988
+ case "navigate":
10989
+ await page.goto(cfg.url, { waitUntil: "domcontentloaded", timeout: cfg.timeout ?? 30000 });
10795
10990
  await new Promise((r) => setTimeout(r, 1000));
10796
10991
  vars["current_url"] = page.url();
10797
10992
  vars["current_title"] = await page.title();
10798
10993
  break;
10799
- }
10800
10994
  case "type": {
10801
- const selector = interpolate(step.selector ?? "input", vars);
10802
- const value = interpolate(step.value ?? step.text ?? "", vars);
10803
- await page.fill(selector, value);
10995
+ const selector = cfg.selector ?? "input";
10996
+ const value = cfg.value ?? cfg.text ?? "";
10997
+ try {
10998
+ await page.fill(selector, value);
10999
+ } catch (origErr) {
11000
+ if (step.ai_enabled) {
11001
+ const healed = await aiSelfHeal(page, `input field for typing "${value}"`, step);
11002
+ if (healed) {
11003
+ await page.mouse.click(healed.x, healed.y);
11004
+ await page.keyboard.type(value);
11005
+ } else
11006
+ throw origErr;
11007
+ } else {
11008
+ const { healSelector: healSelector2 } = await Promise.resolve().then(() => exports_self_heal);
11009
+ const result = await healSelector2(page, selector);
11010
+ if (result.found && result.locator)
11011
+ await result.locator.fill(value);
11012
+ else
11013
+ throw origErr;
11014
+ }
11015
+ }
10804
11016
  break;
10805
11017
  }
10806
11018
  case "click": {
10807
- const selector = interpolate(step.selector ?? "", vars);
10808
- await page.click(selector, { timeout: step.timeout ?? 1e4 });
11019
+ const selector = cfg.selector;
11020
+ try {
11021
+ await page.click(selector, { timeout: cfg.timeout ?? 1e4 });
11022
+ } catch (origErr) {
11023
+ if (step.ai_enabled) {
11024
+ const healed = await aiSelfHeal(page, `clickable element matching "${selector}"`, step);
11025
+ if (healed)
11026
+ await page.mouse.click(healed.x, healed.y);
11027
+ else
11028
+ throw origErr;
11029
+ } else {
11030
+ const { healSelector: healSelector2 } = await Promise.resolve().then(() => exports_self_heal);
11031
+ const result = await healSelector2(page, selector);
11032
+ if (result.found && result.locator)
11033
+ await result.locator.click();
11034
+ else
11035
+ throw origErr;
11036
+ }
11037
+ }
10809
11038
  await new Promise((r) => setTimeout(r, 500));
10810
11039
  break;
10811
11040
  }
10812
11041
  case "click_text": {
10813
- const text = interpolate(step.text ?? "", vars);
10814
- await page.getByText(text, { exact: false }).first().click({ timeout: step.timeout ?? 1e4 });
11042
+ const text = cfg.text;
11043
+ try {
11044
+ await page.getByText(text, { exact: false }).first().click({ timeout: cfg.timeout ?? 1e4 });
11045
+ } catch (origErr) {
11046
+ if (step.ai_enabled) {
11047
+ const healed = await aiSelfHeal(page, `button or link with text "${text}"`, step);
11048
+ if (healed)
11049
+ await page.mouse.click(healed.x, healed.y);
11050
+ else
11051
+ throw origErr;
11052
+ } else
11053
+ throw origErr;
11054
+ }
10815
11055
  await new Promise((r) => setTimeout(r, 500));
10816
11056
  break;
10817
11057
  }
10818
- case "wait_for_navigation": {
11058
+ case "wait_for_navigation":
10819
11059
  try {
10820
- await page.waitForNavigation({ timeout: step.timeout ?? 15000 });
11060
+ await page.waitForNavigation({ timeout: cfg.timeout ?? 15000 });
10821
11061
  } catch {}
10822
11062
  await new Promise((r) => setTimeout(r, 1000));
10823
11063
  vars["current_url"] = page.url();
10824
11064
  break;
10825
- }
10826
11065
  case "wait_for_text": {
10827
- const text = interpolate(step.text ?? "", vars);
10828
- await page.waitForSelector(`text=${text}`, { timeout: step.timeout ?? 1e4 });
11066
+ const text = cfg.text;
11067
+ await page.waitForSelector(`text=${text}`, { timeout: cfg.timeout ?? 1e4 });
10829
11068
  break;
10830
11069
  }
10831
- case "snapshot": {
11070
+ case "snapshot":
10832
11071
  vars["page_text"] = await page.evaluate(() => document.body?.textContent?.trim() ?? "");
10833
11072
  break;
10834
- }
10835
- }
10836
- }
10837
- async function runConnectorStep(step, vars) {
10838
- const connectorName = step.connector;
10839
- if (!connectorName)
10840
- throw new Error("Connector step missing connector name");
10841
- const args = (step.args ?? []).map((a) => interpolate(a, vars));
10842
- let stdout = "";
10843
- try {
10844
- const bin = `connect-${connectorName}`;
10845
- const proc = Bun.spawn([bin, ...args], {
10846
- stdout: "pipe",
10847
- stderr: "pipe",
10848
- env: { ...process.env, HOME: process.env.HOME ?? "" }
10849
- });
10850
- stdout = await new Response(proc.stdout).text();
10851
- const stderr = await new Response(proc.stderr).text();
10852
- const exitCode = await proc.exited;
10853
- vars["last_success"] = String(exitCode === 0);
10854
- vars["last_exit_code"] = String(exitCode);
10855
- if (exitCode !== 0 && stderr)
10856
- stdout = stderr;
10857
- } catch (e) {
10858
- vars["last_success"] = "false";
10859
- throw new Error(`Connector ${connectorName} failed: ${e.message ?? String(e)}`);
10860
- }
10861
- vars["last_output"] = stdout;
10862
- try {
10863
- const parsed = JSON.parse(stdout);
10864
- if (typeof parsed === "object" && parsed !== null) {
10865
- for (const [k, v] of Object.entries(parsed)) {
10866
- if (typeof v === "string" || typeof v === "number") {
10867
- vars[`last.${k}`] = String(v);
10868
- }
10869
- }
10870
- }
10871
- } catch {}
10872
- if (step.save_as) {
10873
- vars[step.save_as] = stdout;
10874
11073
  }
10875
11074
  }
10876
- function getAIProvider(provider) {
10877
- if (provider === "anthropic") {
10878
- const apiKey2 = process.env["ANTHROPIC_API_KEY"];
10879
- if (!apiKey2)
10880
- throw new Error("ANTHROPIC_API_KEY not set");
10881
- return {
10882
- url: "https://api.anthropic.com/v1/messages",
10883
- headers: { "content-type": "application/json", "x-api-key": apiKey2, "anthropic-version": "2023-06-01" },
10884
- body: (model, prompt) => ({ model, max_tokens: 1024, messages: [{ role: "user", content: prompt }] }),
10885
- extract: (data) => data.content?.[0]?.text ?? ""
10886
- };
10887
- }
10888
- const apiKey = process.env["CEREBRAS_API_KEY"];
10889
- if (!apiKey)
10890
- throw new Error("CEREBRAS_API_KEY not set \u2014 required for AI steps (set CEREBRAS_API_KEY or use provider: 'anthropic')");
10891
- return {
10892
- url: "https://api.cerebras.ai/v1/chat/completions",
10893
- headers: { "content-type": "application/json", Authorization: `Bearer ${apiKey}` },
10894
- body: (model, prompt) => ({ model, max_tokens: 1024, messages: [{ role: "user", content: prompt }] }),
10895
- extract: (data) => data.choices?.[0]?.message?.content ?? ""
10896
- };
10897
- }
10898
- async function runAIStep(step, vars) {
10899
- const prompt = interpolate(step.prompt ?? "", vars);
10900
- if (!prompt)
10901
- throw new Error("AI step missing prompt");
10902
- const modelAlias = step.model ?? "fast";
10903
- const resolved = MODEL_MAP[modelAlias] ?? { provider: "cerebras", model: modelAlias };
10904
- const saveTo = step.save_as ?? "ai_response";
10905
- const provider = getAIProvider(resolved.provider);
10906
- const response = await fetch(provider.url, {
10907
- method: "POST",
10908
- headers: provider.headers,
10909
- body: JSON.stringify(provider.body(resolved.model, prompt))
11075
+ async function execConnector(cfg, step, vars) {
11076
+ const connector = cfg.connector;
11077
+ if (!connector)
11078
+ throw new Error("Connector step missing 'connector' in config");
11079
+ const args = cfg.args ?? [];
11080
+ const proc = Bun.spawn([`connect-${connector}`, ...args], {
11081
+ stdout: "pipe",
11082
+ stderr: "pipe",
11083
+ env: { ...process.env, HOME: process.env.HOME ?? "" }
10910
11084
  });
10911
- if (!response.ok) {
10912
- const errBody = await response.text();
10913
- throw new Error(`AI API error (${response.status}): ${errBody.slice(0, 200)}`);
10914
- }
10915
- const data = await response.json();
10916
- const text = provider.extract(data);
10917
- vars[saveTo] = text;
10918
- vars["last_output"] = text;
10919
- if (step.response_format === "json") {
10920
- try {
10921
- const jsonStr = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
10922
- const parsed = JSON.parse(jsonStr);
10923
- if (typeof parsed === "object" && parsed !== null) {
10924
- for (const [k, v] of Object.entries(parsed)) {
10925
- if (typeof v === "string" || typeof v === "number") {
10926
- vars[`${saveTo}.${k}`] = String(v);
10927
- }
10928
- }
10929
- }
10930
- } catch {}
11085
+ const stdout = await new Response(proc.stdout).text();
11086
+ const stderr = await new Response(proc.stderr).text();
11087
+ const exitCode = await proc.exited;
11088
+ if (exitCode !== 0 && !stdout)
11089
+ throw new Error(`Connector ${connector} failed: ${stderr.slice(0, 200)}`);
11090
+ const raw = stdout || stderr;
11091
+ vars["last_output"] = raw;
11092
+ if (step.ai_enabled && step.ai_config?.prompt) {
11093
+ const aiPrompt = interpolate(step.ai_config.prompt, { ...vars, last_output: raw });
11094
+ const provider = step.ai_config.provider ?? "cerebras";
11095
+ const model = step.ai_config.model ?? "fast";
11096
+ const parsed = await infer(aiPrompt, { provider, model });
11097
+ const saveTo = cfg.save_as ?? "last_output";
11098
+ vars[saveTo] = parsed.trim();
11099
+ } else if (cfg.save_as) {
11100
+ vars[cfg.save_as] = raw;
11101
+ }
11102
+ }
11103
+ async function execExtract(cfg, step, vars) {
11104
+ const saveTo = cfg.save_as ?? "extracted";
11105
+ if (step.ai_enabled || cfg.prompt) {
11106
+ const source = cfg.source ? vars[cfg.source] ?? "" : vars["last_output"] ?? "";
11107
+ const prompt = interpolate(cfg.prompt ?? `Extract the key information from this text:
11108
+
11109
+ ${source}`, { ...vars, source });
11110
+ const provider = step.ai_config?.provider ?? "cerebras";
11111
+ const model = step.ai_config?.model ?? "fast";
11112
+ const result = await infer(prompt, { provider, model });
11113
+ vars[saveTo] = result.trim();
11114
+ vars["last_output"] = result.trim();
11115
+ return;
10931
11116
  }
10932
- }
10933
- function decodeHtmlEntities(str) {
10934
- return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'");
10935
- }
10936
- function runExtractStep(step, vars) {
10937
- const saveTo = step.save_as ?? "extracted";
10938
- if (step.pattern) {
10939
- const source = step.check ? vars[step.check] ?? "" : vars["last_output"] ?? "";
10940
- const regex = new RegExp(step.pattern);
10941
- const match = regex.exec(source);
11117
+ if (cfg.pattern) {
11118
+ const source = cfg.source ? vars[cfg.source] ?? "" : vars["last_output"] ?? "";
11119
+ const match = new RegExp(cfg.pattern).exec(source);
10942
11120
  if (match) {
10943
11121
  vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
10944
11122
  }
10945
11123
  }
10946
- if (step.json_path) {
10947
- const source = vars["last_output"] ?? "{}";
10948
- try {
10949
- let obj = JSON.parse(source);
10950
- for (const key of step.json_path.split(".")) {
10951
- obj = obj?.[key];
10952
- }
10953
- if (obj !== undefined)
10954
- vars[saveTo] = String(obj);
10955
- } catch {}
10956
- }
10957
11124
  }
10958
- function createScriptFromJSON(jsonStr) {
10959
- const parsed = JSON.parse(jsonStr);
10960
- if (!parsed.name)
10961
- throw new Error("Script must have a 'name' field");
10962
- if (!parsed.domain)
10963
- throw new Error("Script must have a 'domain' field");
10964
- if (!parsed.steps || !Array.isArray(parsed.steps) || parsed.steps.length === 0) {
10965
- throw new Error("Script must have a non-empty 'steps' array");
10966
- }
10967
- return {
10968
- name: parsed.name,
10969
- domain: parsed.domain,
10970
- description: parsed.description ?? "",
10971
- variables: parsed.variables ?? {},
10972
- steps: parsed.steps,
10973
- created_at: new Date().toISOString(),
10974
- updated_at: new Date().toISOString()
10975
- };
11125
+ function execCondition(cfg, vars, i) {
11126
+ const checkVal = vars[cfg.check ?? ""];
11127
+ let met = true;
11128
+ if (cfg.equals !== undefined)
11129
+ met = checkVal === interpolate(cfg.equals, vars);
11130
+ if (cfg.contains !== undefined)
11131
+ met = checkVal?.includes(interpolate(cfg.contains, vars)) ?? false;
11132
+ if (!met && cfg.skip_to !== undefined)
11133
+ return cfg.skip_to - 1;
11134
+ return i;
10976
11135
  }
10977
- function createScriptFromFile(filePath) {
10978
- if (!existsSync5(filePath))
10979
- throw new Error(`File not found: ${filePath}`);
10980
- return createScriptFromJSON(readFileSync3(filePath, "utf8"));
11136
+ async function execSaveState(cfg, page, vars) {
11137
+ const name = interpolate(cfg.name ?? "default", vars);
11138
+ try {
11139
+ const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
11140
+ vars["saved_state_path"] = await saveStateFromPage2(page, name);
11141
+ } catch {}
10981
11142
  }
10982
- var activeJobs, MODEL_MAP;
10983
- var init_login_scripts = __esm(() => {
10984
- init_schema();
10985
- activeJobs = new Map;
10986
- MODEL_MAP = {
10987
- fast: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
10988
- scout: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
10989
- maverick: { provider: "cerebras", model: "llama-4-maverick-17b-128e-instruct" },
10990
- haiku: { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
10991
- sonnet: { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
10992
- opus: { provider: "anthropic", model: "claude-opus-4-6" }
10993
- };
11143
+ async function aiSelfHeal(page, description, step) {
11144
+ try {
11145
+ const { findElementByVision: findElementByVision2 } = await Promise.resolve().then(() => exports_vision_fallback);
11146
+ const provider = step.ai_config?.provider ?? undefined;
11147
+ const model = step.ai_config?.model ?? undefined;
11148
+ const result = await findElementByVision2(page, description, { model: model ?? provider });
11149
+ if (result.found)
11150
+ return { x: result.x, y: result.y };
11151
+ } catch {}
11152
+ return null;
11153
+ }
11154
+ function decodeHtmlEntities(str) {
11155
+ return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'");
11156
+ }
11157
+ var init_script_engine = __esm(() => {
11158
+ init_scripts();
11159
+ init_ai_inference();
10994
11160
  });
10995
11161
 
10996
11162
  // src/lib/api-detector.ts
@@ -11141,8 +11307,8 @@ __export(exports_datasets, {
11141
11307
  deleteDataset: () => deleteDataset
11142
11308
  });
11143
11309
  import { randomUUID as randomUUID14 } from "crypto";
11144
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync10 } from "fs";
11145
- import { join as join10 } from "path";
11310
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
11311
+ import { join as join9 } from "path";
11146
11312
  import { homedir as homedir9 } from "os";
11147
11313
  function saveDataset(data) {
11148
11314
  const db = getDatabase();
@@ -11181,14 +11347,14 @@ function exportDataset(name, format) {
11181
11347
  const dataset = getDatasetByName(name);
11182
11348
  if (!dataset)
11183
11349
  throw new Error(`Dataset '${name}' not found`);
11184
- const dir = join10(process.env["BROWSER_DATA_DIR"] ?? join10(homedir9(), ".browser"), "exports");
11185
- mkdirSync10(dir, { recursive: true });
11350
+ const dir = join9(process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser"), "exports");
11351
+ mkdirSync9(dir, { recursive: true });
11186
11352
  const filename = `${name}.${format}`;
11187
- const path = join10(dir, filename);
11353
+ const path = join9(dir, filename);
11188
11354
  if (format === "csv") {
11189
11355
  const rows = dataset.data;
11190
11356
  if (rows.length === 0) {
11191
- writeFileSync4(path, "");
11357
+ writeFileSync3(path, "");
11192
11358
  return { path, size: 0 };
11193
11359
  }
11194
11360
  const headers = Object.keys(rows[0]);
@@ -11201,11 +11367,11 @@ function exportDataset(name, format) {
11201
11367
  }
11202
11368
  const content = csvLines.join(`
11203
11369
  `);
11204
- writeFileSync4(path, content);
11370
+ writeFileSync3(path, content);
11205
11371
  return { path, size: content.length };
11206
11372
  } else {
11207
11373
  const content = JSON.stringify(dataset.data, null, 2);
11208
- writeFileSync4(path, content);
11374
+ writeFileSync3(path, content);
11209
11375
  return { path, size: content.length };
11210
11376
  }
11211
11377
  }
@@ -11219,8 +11385,8 @@ __export(exports_auth, {
11219
11385
  loginWithCredentials: () => loginWithCredentials,
11220
11386
  getCredentials: () => getCredentials
11221
11387
  });
11222
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
11223
- import { join as join11 } from "path";
11388
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
11389
+ import { join as join10 } from "path";
11224
11390
  import { homedir as homedir10 } from "os";
11225
11391
  async function getCredentials(service) {
11226
11392
  try {
@@ -11231,9 +11397,9 @@ async function getCredentials(service) {
11231
11397
  return { email: email.value, password: password.value };
11232
11398
  }
11233
11399
  } catch {}
11234
- const secretsPath = join11(homedir10(), ".secrets");
11235
- if (existsSync6(secretsPath)) {
11236
- const content = readFileSync4(secretsPath, "utf8");
11400
+ const secretsPath = join10(homedir10(), ".secrets");
11401
+ if (existsSync5(secretsPath)) {
11402
+ const content = readFileSync3(secretsPath, "utf8");
11237
11403
  const lines = content.split(`
11238
11404
  `);
11239
11405
  const prefix = service.toUpperCase().replace(/[^A-Z0-9]/g, "_");
@@ -11413,11 +11579,11 @@ __export(exports_dist, {
11413
11579
  DEFAULT_CONFIG: () => DEFAULT_CONFIG
11414
11580
  });
11415
11581
  import { Database as Database2 } from "bun:sqlite";
11416
- import { existsSync as existsSync7, mkdirSync as mkdirSync11 } from "fs";
11417
- import { dirname, join as join12, resolve } from "path";
11418
- import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync5, readdirSync as readdirSync5, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3 } from "fs";
11582
+ import { existsSync as existsSync6, mkdirSync as mkdirSync10 } from "fs";
11583
+ import { dirname, join as join11, resolve as resolve2 } from "path";
11584
+ import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
11419
11585
  import { homedir as homedir11 } from "os";
11420
- import { basename as basename2, dirname as dirname2, join as join22, resolve as resolve2 } from "path";
11586
+ import { basename as basename2, dirname as dirname2, join as join22, resolve as resolve22 } from "path";
11421
11587
  import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
11422
11588
  import { homedir as homedir22 } from "os";
11423
11589
  import { join as join32 } from "path";
@@ -11428,10 +11594,10 @@ function isInMemoryDb(path) {
11428
11594
  return path === ":memory:" || path.startsWith("file::memory:");
11429
11595
  }
11430
11596
  function findNearestMementosDb(startDir) {
11431
- let dir = resolve(startDir);
11597
+ let dir = resolve2(startDir);
11432
11598
  while (true) {
11433
- const candidate = join12(dir, ".mementos", "mementos.db");
11434
- if (existsSync7(candidate))
11599
+ const candidate = join11(dir, ".mementos", "mementos.db");
11600
+ if (existsSync6(candidate))
11435
11601
  return candidate;
11436
11602
  const parent = dirname(dir);
11437
11603
  if (parent === dir)
@@ -11441,9 +11607,9 @@ function findNearestMementosDb(startDir) {
11441
11607
  return null;
11442
11608
  }
11443
11609
  function findGitRoot(startDir) {
11444
- let dir = resolve(startDir);
11610
+ let dir = resolve2(startDir);
11445
11611
  while (true) {
11446
- if (existsSync7(join12(dir, ".git")))
11612
+ if (existsSync6(join11(dir, ".git")))
11447
11613
  return dir;
11448
11614
  const parent = dirname(dir);
11449
11615
  if (parent === dir)
@@ -11463,18 +11629,18 @@ function getDbPath() {
11463
11629
  if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
11464
11630
  const gitRoot = findGitRoot(cwd);
11465
11631
  if (gitRoot) {
11466
- return join12(gitRoot, ".mementos", "mementos.db");
11632
+ return join11(gitRoot, ".mementos", "mementos.db");
11467
11633
  }
11468
11634
  }
11469
11635
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
11470
- return join12(home, ".mementos", "mementos.db");
11636
+ return join11(home, ".mementos", "mementos.db");
11471
11637
  }
11472
11638
  function ensureDir2(filePath) {
11473
11639
  if (isInMemoryDb(filePath))
11474
11640
  return;
11475
- const dir = dirname(resolve(filePath));
11476
- if (!existsSync7(dir)) {
11477
- mkdirSync11(dir, { recursive: true });
11641
+ const dir = dirname(resolve2(filePath));
11642
+ if (!existsSync6(dir)) {
11643
+ mkdirSync10(dir, { recursive: true });
11478
11644
  }
11479
11645
  }
11480
11646
  function getDatabase2(dbPath) {
@@ -13328,7 +13494,7 @@ function loadConfig() {
13328
13494
  let fileConfig = {};
13329
13495
  if (existsSync22(configPath)) {
13330
13496
  try {
13331
- const raw = readFileSync5(configPath, "utf-8");
13497
+ const raw = readFileSync4(configPath, "utf-8");
13332
13498
  fileConfig = JSON.parse(raw);
13333
13499
  } catch {}
13334
13500
  }
@@ -13361,7 +13527,7 @@ function readGlobalConfig() {
13361
13527
  if (!existsSync22(p))
13362
13528
  return {};
13363
13529
  try {
13364
- return JSON.parse(readFileSync5(p, "utf-8"));
13530
+ return JSON.parse(readFileSync4(p, "utf-8"));
13365
13531
  } catch {
13366
13532
  return {};
13367
13533
  }
@@ -13369,7 +13535,7 @@ function readGlobalConfig() {
13369
13535
  function writeGlobalConfig(data) {
13370
13536
  const p = globalConfigPath();
13371
13537
  ensureDir22(dirname2(p));
13372
- writeFileSync5(p, JSON.stringify(data, null, 2), "utf-8");
13538
+ writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
13373
13539
  }
13374
13540
  function getActiveProfile() {
13375
13541
  const envProfile = process.env["MEMENTOS_PROFILE"];
@@ -13391,7 +13557,7 @@ function listProfiles2() {
13391
13557
  const dir = profilesDir();
13392
13558
  if (!existsSync22(dir))
13393
13559
  return [];
13394
- return readdirSync5(dir).filter((f) => f.endsWith(".db")).map((f) => basename2(f, ".db")).sort();
13560
+ return readdirSync4(dir).filter((f) => f.endsWith(".db")).map((f) => basename2(f, ".db")).sort();
13395
13561
  }
13396
13562
  function deleteProfile2(name) {
13397
13563
  const dbPath = join22(profilesDir(), `${name}.db`);
@@ -16034,30 +16200,30 @@ __export(exports_dist2, {
16034
16200
  acquireLock: () => acquireLock2
16035
16201
  });
16036
16202
  import { Database as Database3 } from "bun:sqlite";
16037
- import { mkdirSync as mkdirSync12 } from "fs";
16038
- import { join as join13, dirname as dirname3 } from "path";
16203
+ import { mkdirSync as mkdirSync11 } from "fs";
16204
+ import { join as join12, dirname as dirname3 } from "path";
16039
16205
  import { homedir as homedir12 } from "os";
16040
16206
  import { randomUUID as randomUUID15 } from "crypto";
16041
16207
  import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
16042
16208
  import { join as join33 } from "path";
16043
16209
  import { homedir as homedir33 } from "os";
16044
- import { readFileSync as readFileSync6 } from "fs";
16210
+ import { readFileSync as readFileSync5 } from "fs";
16045
16211
  import { join as join23 } from "path";
16046
16212
  import { homedir as homedir23 } from "os";
16047
16213
  import { randomUUID as randomUUID22 } from "crypto";
16048
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync6, mkdirSync as mkdirSync33 } from "fs";
16214
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync33 } from "fs";
16049
16215
  import { join as join43, dirname as dirname22 } from "path";
16050
16216
  import { homedir as homedir42 } from "os";
16051
16217
  function getDbPath2() {
16052
16218
  if (process.env.CONVERSATIONS_DB_PATH)
16053
16219
  return process.env.CONVERSATIONS_DB_PATH;
16054
- return join13(homedir12(), ".conversations", "messages.db");
16220
+ return join12(homedir12(), ".conversations", "messages.db");
16055
16221
  }
16056
16222
  function getDb() {
16057
16223
  if (db)
16058
16224
  return db;
16059
16225
  const dbPath = getDbPath2();
16060
- mkdirSync12(dirname3(dbPath), { recursive: true });
16226
+ mkdirSync11(dirname3(dbPath), { recursive: true });
16061
16227
  db = new Database3(dbPath, { create: true });
16062
16228
  db.exec("PRAGMA journal_mode = WAL");
16063
16229
  db.exec("PRAGMA busy_timeout = 5000");
@@ -16299,7 +16465,7 @@ function loadConfig2() {
16299
16465
  if (cachedConfig && now2 - configLoadedAt < CONFIG_CACHE_MS)
16300
16466
  return cachedConfig;
16301
16467
  try {
16302
- const raw = readFileSync6(getConfigPath(), "utf-8");
16468
+ const raw = readFileSync5(getConfigPath(), "utf-8");
16303
16469
  cachedConfig = JSON.parse(raw);
16304
16470
  configLoadedAt = now2;
16305
16471
  return cachedConfig;
@@ -17216,7 +17382,7 @@ function useSpaceMessages(spaceName) {
17216
17382
  }
17217
17383
  function isNameTaken(name) {
17218
17384
  try {
17219
- const { getDb: getDb2 } = (init_db(), __toCommonJS(exports_db));
17385
+ const { getDb: getDb2 } = (init_db(), __toCommonJS2(exports_db));
17220
17386
  const db2 = getDb2();
17221
17387
  const row = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(name);
17222
17388
  return !!row;
@@ -17245,7 +17411,7 @@ function getAutoName() {
17245
17411
  cachedAutoName = name;
17246
17412
  try {
17247
17413
  mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
17248
- writeFileSync6(AGENT_ID_FILE, name + `
17414
+ writeFileSync5(AGENT_ID_FILE, name + `
17249
17415
  `, "utf-8");
17250
17416
  } catch {}
17251
17417
  return name;
@@ -17851,7 +18017,7 @@ function getGraphStats() {
17851
18017
  map[r.relation] = r.c;
17852
18018
  return { total_edges: total, by_relation: map };
17853
18019
  }
17854
- var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
18020
+ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc2, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
17855
18021
  target = mod != null ? __create2(__getProtoOf2(mod)) : {};
17856
18022
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", { value: mod, enumerable: true }) : target;
17857
18023
  for (let key of __getOwnPropNames2(mod))
@@ -17861,17 +18027,17 @@ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc,
17861
18027
  enumerable: true
17862
18028
  });
17863
18029
  return to;
17864
- }, __moduleCache, __toCommonJS = (from) => {
17865
- var entry = __moduleCache.get(from), desc;
18030
+ }, __moduleCache2, __toCommonJS2 = (from) => {
18031
+ var entry = __moduleCache2.get(from), desc;
17866
18032
  if (entry)
17867
18033
  return entry;
17868
18034
  entry = __defProp3({}, "__esModule", { value: true });
17869
18035
  if (from && typeof from === "object" || typeof from === "function")
17870
18036
  __getOwnPropNames2(from).map((key) => !__hasOwnProp2.call(entry, key) && __defProp3(entry, key, {
17871
18037
  get: () => from[key],
17872
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18038
+ enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
17873
18039
  }));
17874
- __moduleCache.set(from, entry);
18040
+ __moduleCache2.set(from, entry);
17875
18041
  return entry;
17876
18042
  }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __export3 = (target, all) => {
17877
18043
  for (var name in all)
@@ -17887,9 +18053,9 @@ var init_dist2 = __esm(() => {
17887
18053
  __getProtoOf2 = Object.getPrototypeOf;
17888
18054
  __defProp3 = Object.defineProperty;
17889
18055
  __getOwnPropNames2 = Object.getOwnPropertyNames;
17890
- __getOwnPropDesc = Object.getOwnPropertyDescriptor;
18056
+ __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
17891
18057
  __hasOwnProp2 = Object.prototype.hasOwnProperty;
17892
- __moduleCache = /* @__PURE__ */ new WeakMap;
18058
+ __moduleCache2 = /* @__PURE__ */ new WeakMap;
17893
18059
  exports_db = {};
17894
18060
  __export3(exports_db, {
17895
18061
  getDbPath: () => getDbPath2,
@@ -20459,18 +20625,18 @@ __export(exports_dist3, {
20459
20625
  AgentNotFoundError: () => AgentNotFoundError2
20460
20626
  });
20461
20627
  import { Database as Database4 } from "bun:sqlite";
20462
- import { existsSync as existsSync8, mkdirSync as mkdirSync13 } from "fs";
20463
- import { dirname as dirname5, join as join14, resolve as resolve3 } from "path";
20628
+ import { existsSync as existsSync7, mkdirSync as mkdirSync12 } from "fs";
20629
+ import { dirname as dirname5, join as join13, resolve as resolve3 } from "path";
20464
20630
  import { existsSync as existsSync33 } from "fs";
20465
20631
  import { join as join34 } from "path";
20466
- import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync7, readdirSync as readdirSync6, statSync as statSync3, writeFileSync as writeFileSync7 } from "fs";
20632
+ import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
20467
20633
  import { join as join24 } from "path";
20468
20634
  import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
20469
20635
  import { join as join44 } from "path";
20470
20636
  import { existsSync as existsSync52 } from "fs";
20471
20637
  import { join as join52 } from "path";
20472
20638
  import { readFileSync as readFileSync33, statSync as statSync22 } from "fs";
20473
- import { relative, resolve as resolve22, join as join62 } from "path";
20639
+ import { relative, resolve as resolve23, join as join62 } from "path";
20474
20640
  import { execSync as execSync2 } from "child_process";
20475
20641
 
20476
20642
  class TodosClient {
@@ -20693,8 +20859,8 @@ function isInMemoryDb2(path) {
20693
20859
  function findNearestTodosDb(startDir) {
20694
20860
  let dir = resolve3(startDir);
20695
20861
  while (true) {
20696
- const candidate = join14(dir, ".todos", "todos.db");
20697
- if (existsSync8(candidate))
20862
+ const candidate = join13(dir, ".todos", "todos.db");
20863
+ if (existsSync7(candidate))
20698
20864
  return candidate;
20699
20865
  const parent = dirname5(dir);
20700
20866
  if (parent === dir)
@@ -20706,7 +20872,7 @@ function findNearestTodosDb(startDir) {
20706
20872
  function findGitRoot2(startDir) {
20707
20873
  let dir = resolve3(startDir);
20708
20874
  while (true) {
20709
- if (existsSync8(join14(dir, ".git")))
20875
+ if (existsSync7(join13(dir, ".git")))
20710
20876
  return dir;
20711
20877
  const parent = dirname5(dir);
20712
20878
  if (parent === dir)
@@ -20726,18 +20892,18 @@ function getDbPath3() {
20726
20892
  if (process.env["TODOS_DB_SCOPE"] === "project") {
20727
20893
  const gitRoot = findGitRoot2(cwd);
20728
20894
  if (gitRoot) {
20729
- return join14(gitRoot, ".todos", "todos.db");
20895
+ return join13(gitRoot, ".todos", "todos.db");
20730
20896
  }
20731
20897
  }
20732
20898
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
20733
- return join14(home, ".todos", "todos.db");
20899
+ return join13(home, ".todos", "todos.db");
20734
20900
  }
20735
20901
  function ensureDir3(filePath) {
20736
20902
  if (isInMemoryDb2(filePath))
20737
20903
  return;
20738
20904
  const dir = dirname5(resolve3(filePath));
20739
- if (!existsSync8(dir)) {
20740
- mkdirSync13(dir, { recursive: true });
20905
+ if (!existsSync7(dir)) {
20906
+ mkdirSync12(dir, { recursive: true });
20741
20907
  }
20742
20908
  }
20743
20909
  function getDatabase3(dbPath) {
@@ -21203,28 +21369,28 @@ function ensureDir23(dir) {
21203
21369
  function listJsonFiles(dir) {
21204
21370
  if (!existsSync23(dir))
21205
21371
  return [];
21206
- return readdirSync6(dir).filter((f) => f.endsWith(".json"));
21372
+ return readdirSync5(dir).filter((f) => f.endsWith(".json"));
21207
21373
  }
21208
21374
  function readJsonFile(path) {
21209
21375
  try {
21210
- return JSON.parse(readFileSync7(path, "utf-8"));
21376
+ return JSON.parse(readFileSync6(path, "utf-8"));
21211
21377
  } catch {
21212
21378
  return null;
21213
21379
  }
21214
21380
  }
21215
21381
  function writeJsonFile(path, data) {
21216
- writeFileSync7(path, JSON.stringify(data, null, 2) + `
21382
+ writeFileSync6(path, JSON.stringify(data, null, 2) + `
21217
21383
  `);
21218
21384
  }
21219
21385
  function readHighWaterMark(dir) {
21220
21386
  const path = join24(dir, ".highwatermark");
21221
21387
  if (!existsSync23(path))
21222
21388
  return 1;
21223
- const val = parseInt(readFileSync7(path, "utf-8").trim(), 10);
21389
+ const val = parseInt(readFileSync6(path, "utf-8").trim(), 10);
21224
21390
  return isNaN(val) ? 1 : val;
21225
21391
  }
21226
21392
  function writeHighWaterMark(dir, value) {
21227
- writeFileSync7(join24(dir, ".highwatermark"), String(value));
21393
+ writeFileSync6(join24(dir, ".highwatermark"), String(value));
21228
21394
  }
21229
21395
  function getFileMtimeMs(path) {
21230
21396
  try {
@@ -24586,7 +24752,7 @@ function collectFiles(basePath, extensions) {
24586
24752
  return files.sort();
24587
24753
  }
24588
24754
  function extractTodos(options, db2) {
24589
- const basePath = resolve22(options.path);
24755
+ const basePath = resolve23(options.path);
24590
24756
  const tags = options.patterns || [...EXTRACT_TAGS];
24591
24757
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
24592
24758
  const files = collectFiles(basePath, extensions);
@@ -24595,7 +24761,7 @@ function extractTodos(options, db2) {
24595
24761
  const fullPath = statSync22(basePath).isFile() ? basePath : join62(basePath, file);
24596
24762
  try {
24597
24763
  const source = readFileSync33(fullPath, "utf-8");
24598
- const relPath = statSync22(basePath).isFile() ? relative(resolve22(basePath, ".."), fullPath) : file;
24764
+ const relPath = statSync22(basePath).isFile() ? relative(resolve23(basePath, ".."), fullPath) : file;
24599
24765
  const comments = extractFromSource(source, relPath, tags);
24600
24766
  allComments.push(...comments);
24601
24767
  } catch {}
@@ -25814,8 +25980,8 @@ __export(exports_dist4, {
25814
25980
  CATEGORIES: () => CATEGORIES,
25815
25981
  AGENT_TARGETS: () => AGENT_TARGETS
25816
25982
  });
25817
- import { existsSync as existsSync9, cpSync, mkdirSync as mkdirSync14, writeFileSync as writeFileSync8, rmSync as rmSync2, readdirSync as readdirSync7, statSync as statSync4, readFileSync as readFileSync8, accessSync, constants } from "fs";
25818
- import { join as join15, dirname as dirname6 } from "path";
25983
+ import { existsSync as existsSync8, cpSync, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, rmSync as rmSync2, readdirSync as readdirSync6, statSync as statSync4, readFileSync as readFileSync7, accessSync, constants } from "fs";
25984
+ import { join as join14, dirname as dirname6 } from "path";
25819
25985
  import { homedir as homedir13 } from "os";
25820
25986
  import { fileURLToPath } from "url";
25821
25987
  import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
@@ -25927,35 +26093,35 @@ function normalizeSkillName(name) {
25927
26093
  function findSkillsDir() {
25928
26094
  let dir = __dirname2;
25929
26095
  for (let i = 0;i < 5; i++) {
25930
- const candidate = join15(dir, "skills");
25931
- if (existsSync9(candidate)) {
26096
+ const candidate = join14(dir, "skills");
26097
+ if (existsSync8(candidate)) {
25932
26098
  return candidate;
25933
26099
  }
25934
26100
  dir = dirname6(dir);
25935
26101
  }
25936
- return join15(__dirname2, "..", "skills");
26102
+ return join14(__dirname2, "..", "skills");
25937
26103
  }
25938
26104
  function getSkillPath(name) {
25939
26105
  const skillName = normalizeSkillName(name);
25940
- return join15(SKILLS_DIR, skillName);
26106
+ return join14(SKILLS_DIR, skillName);
25941
26107
  }
25942
26108
  function skillExists(name) {
25943
- return existsSync9(getSkillPath(name));
26109
+ return existsSync8(getSkillPath(name));
25944
26110
  }
25945
26111
  function installSkill(name, options = {}) {
25946
26112
  const { targetDir = process.cwd(), overwrite = false } = options;
25947
26113
  const skillName = normalizeSkillName(name);
25948
26114
  const sourcePath = getSkillPath(name);
25949
- const destDir = join15(targetDir, ".skills");
25950
- const destPath = join15(destDir, skillName);
25951
- if (!existsSync9(sourcePath)) {
26115
+ const destDir = join14(targetDir, ".skills");
26116
+ const destPath = join14(destDir, skillName);
26117
+ if (!existsSync8(sourcePath)) {
25952
26118
  return {
25953
26119
  skill: name,
25954
26120
  success: false,
25955
26121
  error: `Skill '${name}' not found`
25956
26122
  };
25957
26123
  }
25958
- if (existsSync9(destPath) && !overwrite) {
26124
+ if (existsSync8(destPath) && !overwrite) {
25959
26125
  return {
25960
26126
  skill: name,
25961
26127
  success: false,
@@ -25964,10 +26130,10 @@ function installSkill(name, options = {}) {
25964
26130
  };
25965
26131
  }
25966
26132
  try {
25967
- if (!existsSync9(destDir)) {
25968
- mkdirSync14(destDir, { recursive: true });
26133
+ if (!existsSync8(destDir)) {
26134
+ mkdirSync13(destDir, { recursive: true });
25969
26135
  }
25970
- if (existsSync9(destPath) && overwrite) {
26136
+ if (existsSync8(destPath) && overwrite) {
25971
26137
  rmSync2(destPath, { recursive: true, force: true });
25972
26138
  }
25973
26139
  cpSync(sourcePath, destPath, {
@@ -26006,10 +26172,10 @@ function installSkills(names, options = {}) {
26006
26172
  return names.map((name) => installSkill(name, options));
26007
26173
  }
26008
26174
  function updateSkillsIndex(skillsDir) {
26009
- const indexPath = join15(skillsDir, "index.ts");
26175
+ const indexPath = join14(skillsDir, "index.ts");
26010
26176
  const meta = loadMeta(skillsDir);
26011
26177
  const disabledSet = new Set(meta.disabled || []);
26012
- const skills = readdirSync7(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
26178
+ const skills = readdirSync6(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
26013
26179
  const exports = skills.map((s) => {
26014
26180
  const name = s.replace("skill-", "").replace(/-/g, "_");
26015
26181
  return `export * as ${name} from './${s}/src/index.js';`;
@@ -26022,31 +26188,31 @@ function updateSkillsIndex(skillsDir) {
26022
26188
 
26023
26189
  ${exports}
26024
26190
  `;
26025
- writeFileSync8(indexPath, content);
26191
+ writeFileSync7(indexPath, content);
26026
26192
  }
26027
26193
  function getMetaPath(skillsDir) {
26028
- return join15(skillsDir, ".meta.json");
26194
+ return join14(skillsDir, ".meta.json");
26029
26195
  }
26030
26196
  function loadMeta(skillsDir) {
26031
26197
  const metaPath2 = getMetaPath(skillsDir);
26032
- if (existsSync9(metaPath2)) {
26198
+ if (existsSync8(metaPath2)) {
26033
26199
  try {
26034
- return JSON.parse(readFileSync8(metaPath2, "utf-8"));
26200
+ return JSON.parse(readFileSync7(metaPath2, "utf-8"));
26035
26201
  } catch {}
26036
26202
  }
26037
26203
  return { skills: {} };
26038
26204
  }
26039
26205
  function saveMeta(skillsDir, meta) {
26040
- writeFileSync8(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
26206
+ writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
26041
26207
  }
26042
26208
  function recordInstall(skillsDir, name) {
26043
26209
  const meta = loadMeta(skillsDir);
26044
26210
  const skillName = normalizeSkillName(name);
26045
26211
  let version = "unknown";
26046
26212
  try {
26047
- const pkgPath = join15(skillsDir, skillName, "package.json");
26048
- if (existsSync9(pkgPath)) {
26049
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
26213
+ const pkgPath = join14(skillsDir, skillName, "package.json");
26214
+ if (existsSync8(pkgPath)) {
26215
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
26050
26216
  version = pkg.version || "unknown";
26051
26217
  }
26052
26218
  } catch {}
@@ -26059,12 +26225,12 @@ function recordRemove(skillsDir, name) {
26059
26225
  saveMeta(skillsDir, meta);
26060
26226
  }
26061
26227
  function getInstallMeta(targetDir = process.cwd()) {
26062
- return loadMeta(join15(targetDir, ".skills"));
26228
+ return loadMeta(join14(targetDir, ".skills"));
26063
26229
  }
26064
26230
  function disableSkill(name, targetDir = process.cwd()) {
26065
- const skillsDir = join15(targetDir, ".skills");
26231
+ const skillsDir = join14(targetDir, ".skills");
26066
26232
  const skillName = normalizeSkillName(name);
26067
- if (!existsSync9(join15(skillsDir, skillName)))
26233
+ if (!existsSync8(join14(skillsDir, skillName)))
26068
26234
  return false;
26069
26235
  const meta = loadMeta(skillsDir);
26070
26236
  const disabled = new Set(meta.disabled || []);
@@ -26077,7 +26243,7 @@ function disableSkill(name, targetDir = process.cwd()) {
26077
26243
  return true;
26078
26244
  }
26079
26245
  function enableSkill(name, targetDir = process.cwd()) {
26080
- const skillsDir = join15(targetDir, ".skills");
26246
+ const skillsDir = join14(targetDir, ".skills");
26081
26247
  const meta = loadMeta(skillsDir);
26082
26248
  const disabled = new Set(meta.disabled || []);
26083
26249
  if (!disabled.has(name))
@@ -26089,24 +26255,24 @@ function enableSkill(name, targetDir = process.cwd()) {
26089
26255
  return true;
26090
26256
  }
26091
26257
  function getDisabledSkills(targetDir = process.cwd()) {
26092
- const meta = loadMeta(join15(targetDir, ".skills"));
26258
+ const meta = loadMeta(join14(targetDir, ".skills"));
26093
26259
  return meta.disabled || [];
26094
26260
  }
26095
26261
  function getInstalledSkills(targetDir = process.cwd()) {
26096
- const skillsDir = join15(targetDir, ".skills");
26097
- if (!existsSync9(skillsDir)) {
26262
+ const skillsDir = join14(targetDir, ".skills");
26263
+ if (!existsSync8(skillsDir)) {
26098
26264
  return [];
26099
26265
  }
26100
- return readdirSync7(skillsDir).filter((f) => {
26101
- const fullPath = join15(skillsDir, f);
26266
+ return readdirSync6(skillsDir).filter((f) => {
26267
+ const fullPath = join14(skillsDir, f);
26102
26268
  return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
26103
26269
  }).map((f) => f.replace("skill-", ""));
26104
26270
  }
26105
26271
  function removeSkill(name, targetDir = process.cwd()) {
26106
26272
  const skillName = normalizeSkillName(name);
26107
- const skillsDir = join15(targetDir, ".skills");
26108
- const skillPath = join15(skillsDir, skillName);
26109
- if (!existsSync9(skillPath)) {
26273
+ const skillsDir = join14(targetDir, ".skills");
26274
+ const skillPath = join14(skillsDir, skillName);
26275
+ if (!existsSync8(skillPath)) {
26110
26276
  return false;
26111
26277
  }
26112
26278
  rmSync2(skillPath, { recursive: true, force: true });
@@ -26117,25 +26283,25 @@ function removeSkill(name, targetDir = process.cwd()) {
26117
26283
  function getAgentSkillsDir(agent, scope = "global", projectDir) {
26118
26284
  const agentDir = `.${agent}`;
26119
26285
  if (scope === "project") {
26120
- return join15(projectDir || process.cwd(), agentDir, "skills");
26286
+ return join14(projectDir || process.cwd(), agentDir, "skills");
26121
26287
  }
26122
- return join15(homedir13(), agentDir, "skills");
26288
+ return join14(homedir13(), agentDir, "skills");
26123
26289
  }
26124
26290
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
26125
26291
  const skillName = normalizeSkillName(name);
26126
- return join15(getAgentSkillsDir(agent, scope, projectDir), skillName);
26292
+ return join14(getAgentSkillsDir(agent, scope, projectDir), skillName);
26127
26293
  }
26128
26294
  function installSkillForAgent(name, options, generateSkillMd) {
26129
26295
  const { agent, scope = "global", projectDir } = options;
26130
26296
  const skillName = normalizeSkillName(name);
26131
26297
  const sourcePath = getSkillPath(name);
26132
- if (!existsSync9(sourcePath)) {
26298
+ if (!existsSync8(sourcePath)) {
26133
26299
  return { skill: name, success: false, error: `Skill '${name}' not found` };
26134
26300
  }
26135
26301
  let skillMdContent = null;
26136
- const skillMdPath = join15(sourcePath, "SKILL.md");
26137
- if (existsSync9(skillMdPath)) {
26138
- skillMdContent = readFileSync8(skillMdPath, "utf-8");
26302
+ const skillMdPath = join14(sourcePath, "SKILL.md");
26303
+ if (existsSync8(skillMdPath)) {
26304
+ skillMdContent = readFileSync7(skillMdPath, "utf-8");
26139
26305
  } else if (generateSkillMd) {
26140
26306
  skillMdContent = generateSkillMd(name);
26141
26307
  }
@@ -26144,8 +26310,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
26144
26310
  }
26145
26311
  const destDir = getAgentSkillPath(name, agent, scope, projectDir);
26146
26312
  if (scope === "global") {
26147
- const agentBaseDir2 = join15(homedir13(), `.${agent}`);
26148
- if (!existsSync9(agentBaseDir2)) {
26313
+ const agentBaseDir2 = join14(homedir13(), `.${agent}`);
26314
+ if (!existsSync8(agentBaseDir2)) {
26149
26315
  const agentLabels = {
26150
26316
  claude: "Claude Code",
26151
26317
  codex: "Codex CLI",
@@ -26168,8 +26334,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
26168
26334
  }
26169
26335
  }
26170
26336
  try {
26171
- mkdirSync14(destDir, { recursive: true });
26172
- writeFileSync8(join15(destDir, "SKILL.md"), skillMdContent);
26337
+ mkdirSync13(destDir, { recursive: true });
26338
+ writeFileSync7(join14(destDir, "SKILL.md"), skillMdContent);
26173
26339
  return { skill: name, success: true, path: destDir };
26174
26340
  } catch (error) {
26175
26341
  return {
@@ -26182,7 +26348,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
26182
26348
  function removeSkillForAgent(name, options) {
26183
26349
  const { agent, scope = "global", projectDir } = options;
26184
26350
  const destDir = getAgentSkillPath(name, agent, scope, projectDir);
26185
- if (!existsSync9(destDir)) {
26351
+ if (!existsSync8(destDir)) {
26186
26352
  return false;
26187
26353
  }
26188
26354
  rmSync2(destDir, { recursive: true, force: true });
@@ -32503,8 +32669,8 @@ var NEVER = INVALID;
32503
32669
  // src/mcp/index.ts
32504
32670
  init_session();
32505
32671
  init_actions();
32506
- import { readFileSync as readFileSync9 } from "fs";
32507
- import { join as join16 } from "path";
32672
+ import { readFileSync as readFileSync8 } from "fs";
32673
+ import { join as join15 } from "path";
32508
32674
 
32509
32675
  // src/lib/screenshot.ts
32510
32676
  init_types();
@@ -33274,7 +33440,7 @@ async function closeTab(page, index) {
33274
33440
  init_dialogs();
33275
33441
  init_profiles();
33276
33442
  init_types();
33277
- var _pkg = JSON.parse(readFileSync9(join16(import.meta.dir, "../../package.json"), "utf8"));
33443
+ var _pkg = JSON.parse(readFileSync8(join15(import.meta.dir, "../../package.json"), "utf8"));
33278
33444
  var networkLogCleanup = new Map;
33279
33445
  var consoleCaptureCleanup = new Map;
33280
33446
  var harCaptures = new Map;
@@ -34979,14 +35145,17 @@ server.tool("browser_profile_delete", "Delete a saved browser profile", { name:
34979
35145
  return err(e);
34980
35146
  }
34981
35147
  });
34982
- server.tool("browser_script_run", "Run a saved login script asynchronously. Returns a job_id immediately \u2014 poll with browser_script_status for step-by-step progress. Combines browser actions + connector calls (e.g. magic link login via Gmail).", {
34983
- name: exports_external.string().describe("Script name (e.g. 'usestable')"),
35148
+ 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).", {
35149
+ name: exports_external.string().describe("Script name"),
34984
35150
  session_id: exports_external.string().optional(),
34985
- variables: exports_external.record(exports_external.string()).optional().describe("Override script variables (e.g. {email: 'foo@bar.com'})")
34986
- }, async ({ name, session_id, variables }) => {
35151
+ engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
35152
+ variables: exports_external.record(exports_external.string()).optional().describe("Override script variables")
35153
+ }, async ({ name, session_id, engine, variables }) => {
34987
35154
  try {
34988
- const { loadScript: loadScript2, runScriptAsync: runScriptAsync2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34989
- const script = loadScript2(name);
35155
+ const { getScriptByName: getScriptByName2, migrateJsonScripts: migrateJsonScripts2, getSteps: getSteps2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35156
+ const { executeScript: executeScript2 } = await Promise.resolve().then(() => (init_script_engine(), exports_script_engine));
35157
+ migrateJsonScripts2();
35158
+ const script = getScriptByName2(name);
34990
35159
  if (!script)
34991
35160
  return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
34992
35161
  let sid;
@@ -34995,55 +35164,72 @@ server.tool("browser_script_run", "Run a saved login script asynchronously. Retu
34995
35164
  sid = resolveSessionId(session_id);
34996
35165
  page = getSessionPage(sid);
34997
35166
  } else {
34998
- const result = await createSession2({ headless: true });
35167
+ const result = await createSession2({ engine: engine ?? "auto", headless: true });
34999
35168
  sid = result.session.id;
35000
35169
  page = result.page;
35001
35170
  }
35002
- const jobId = runScriptAsync2(script, page, variables ?? {});
35003
- return json({ job_id: jobId, session_id: sid, script: name, total_steps: script.steps.length, message: "Script running in background. Poll with browser_script_status for progress." });
35171
+ const steps = getSteps2(script.id);
35172
+ const runId = executeScript2(script.id, page, variables ?? {});
35173
+ return json({ run_id: runId, session_id: sid, script: name, total_steps: steps.length, message: "Script running. Poll with browser_script_status." });
35004
35174
  } catch (e) {
35005
35175
  return err(e);
35006
35176
  }
35007
35177
  });
35008
- server.tool("browser_script_status", "Check the progress of a running login script. Shows current step, step-by-step log with durations, and final result when complete.", { job_id: exports_external.string() }, async ({ job_id }) => {
35178
+ server.tool("browser_script_status", "Check progress of a running script. Shows current step, step-by-step log with durations, and final result when complete.", { run_id: exports_external.string() }, async ({ run_id }) => {
35009
35179
  try {
35010
- const { getJob: getJob2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
35011
- const job = getJob2(job_id);
35012
- if (!job)
35013
- return err(new Error(`Job '${job_id}' not found`));
35180
+ const { getRun: getRun3 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35181
+ const run = getRun3(run_id);
35182
+ if (!run)
35183
+ return err(new Error(`Run '${run_id}' not found`));
35014
35184
  return json({
35015
- status: job.status,
35016
- progress: `${job.current_step}/${job.total_steps}`,
35017
- current_step: job.current_step_description,
35018
- steps_log: job.steps_log,
35019
- result: job.result ?? undefined
35185
+ status: run.status,
35186
+ progress: `${run.current_step}/${run.total_steps}`,
35187
+ current_step: run.current_description,
35188
+ steps_log: run.steps_log,
35189
+ errors: run.errors.length > 0 ? run.errors : undefined,
35190
+ duration_ms: run.duration_ms,
35191
+ completed: run.completed_at
35020
35192
  });
35021
35193
  } catch (e) {
35022
35194
  return err(e);
35023
35195
  }
35024
35196
  });
35025
- server.tool("browser_script_list", "List all saved login scripts", {}, async () => {
35197
+ server.tool("browser_script_list", "List all saved scripts", {}, async () => {
35026
35198
  try {
35027
- const { listScripts: listScripts2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
35028
- return json({ scripts: listScripts2() });
35199
+ const { listScripts: listScripts2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35200
+ migrateJsonScripts2();
35201
+ const scripts = listScripts2();
35202
+ return json({ scripts: scripts.map((s) => ({ name: s.name, domain: s.domain, description: s.description, run_count: s.run_count, last_run: s.last_run })), count: scripts.length });
35029
35203
  } catch (e) {
35030
35204
  return err(e);
35031
35205
  }
35032
35206
  });
35033
- server.tool("browser_script_save", "Save a login script from a JSON definition. Use for creating custom multi-step workflows that combine browser actions + connector calls.", { script: exports_external.string().describe("JSON string of the LoginScript object") }, async ({ script: scriptJson }) => {
35207
+ server.tool("browser_script_save", "Save a script. Steps are stored in SQLite. Each step has a type (browser/connector/extract/wait/condition/save_state), config, and optional AI config for intelligent fallbacks.", {
35208
+ name: exports_external.string(),
35209
+ domain: exports_external.string().optional().default(""),
35210
+ description: exports_external.string().optional().default(""),
35211
+ variables: exports_external.record(exports_external.string()).optional().default({}),
35212
+ steps: exports_external.array(exports_external.object({
35213
+ type: exports_external.enum(["browser", "connector", "extract", "wait", "condition", "save_state"]),
35214
+ config: exports_external.record(exports_external.unknown()).default({}),
35215
+ description: exports_external.string().optional().default(""),
35216
+ ai_enabled: exports_external.boolean().optional().default(false),
35217
+ ai_config: exports_external.record(exports_external.unknown()).optional().default({})
35218
+ }))
35219
+ }, async ({ name, domain, description, variables, steps }) => {
35034
35220
  try {
35035
- const { saveScript: saveScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
35036
- const script = JSON.parse(scriptJson);
35037
- const path = saveScript2(script);
35038
- return json({ saved: true, name: script.name, path, steps: script.steps.length });
35221
+ const { upsertScript: upsertScript2, getSteps: getSteps2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35222
+ const script = upsertScript2({ name, domain, description, variables, steps });
35223
+ const savedSteps = getSteps2(script.id);
35224
+ return json({ id: script.id, name: script.name, steps: savedSteps.length });
35039
35225
  } catch (e) {
35040
35226
  return err(e);
35041
35227
  }
35042
35228
  });
35043
- server.tool("browser_script_delete", "Delete a saved login script", { name: exports_external.string() }, async ({ name }) => {
35229
+ server.tool("browser_script_delete", "Delete a saved script", { name: exports_external.string() }, async ({ name }) => {
35044
35230
  try {
35045
- const { deleteScript: deleteScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
35046
- return json({ deleted: deleteScript2(name) });
35231
+ const { deleteScriptByName: deleteScriptByName2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35232
+ return json({ deleted: deleteScriptByName2(name) });
35047
35233
  } catch (e) {
35048
35234
  return err(e);
35049
35235
  }