@hasna/browser 0.2.5 → 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,325 +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);
10954
+ await execExtract(cfg, step, vars);
10712
10955
  break;
10713
10956
  case "wait":
10714
- await new Promise((r) => setTimeout(r, (step.seconds ?? 3) * 1000));
10957
+ await new Promise((r) => setTimeout(r, (cfg.seconds ?? 3) * 1000));
10715
10958
  break;
10716
- case "condition": {
10717
- const checkVal = vars[step.check ?? ""];
10718
- let conditionMet = true;
10719
- if (step.equals !== undefined)
10720
- conditionMet = checkVal === interpolate(step.equals, vars);
10721
- if (step.contains !== undefined)
10722
- conditionMet = checkVal?.includes(interpolate(step.contains, vars)) ?? false;
10723
- if (!conditionMet && step.skip_to !== undefined) {
10724
- i = step.skip_to - 1;
10725
- }
10959
+ case "condition":
10960
+ i = execCondition(cfg, vars, i);
10726
10961
  break;
10727
- }
10728
- case "save_state": {
10729
- const stateName = interpolate(step.name ?? script.name, vars);
10730
- try {
10731
- const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
10732
- const path = await saveStateFromPage2(page, stateName);
10733
- vars["saved_state_path"] = path;
10734
- } catch {}
10962
+ case "save_state":
10963
+ await execSaveState(cfg, page, vars);
10735
10964
  break;
10736
- }
10737
10965
  }
10738
- const logEntry = job.steps_log[job.steps_log.length - 1];
10739
- logEntry.status = "ok";
10740
- logEntry.duration_ms = Date.now() - stepStart;
10966
+ stepsLog[stepsLog.length - 1].status = "ok";
10967
+ stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
10741
10968
  } catch (err) {
10742
10969
  failed++;
10743
- 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)}`;
10744
10971
  errors2.push(msg);
10745
- const logEntry = job.steps_log[job.steps_log.length - 1];
10746
- logEntry.status = "failed";
10747
- logEntry.error = err instanceof Error ? err.message : String(err);
10748
- logEntry.duration_ms = Date.now() - stepStart;
10749
- 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")
10750
10976
  break;
10751
10977
  }
10978
+ updateRunProgress(runId, i + 1, desc, stepsLog, vars);
10752
10979
  }
10753
- const result = {
10754
- success: failed === 0,
10755
- steps_executed: executed,
10756
- steps_failed: failed,
10757
- variables: vars,
10758
- errors: errors2,
10759
- duration_ms: Date.now() - t0
10760
- };
10761
- job.status = failed === 0 ? "completed" : "failed";
10762
- job.result = result;
10763
- return result;
10764
- }
10765
- function runScriptAsync(script, page, overrides = {}) {
10766
- const jobId = randomUUID12();
10767
- const job = {
10768
- id: jobId,
10769
- script_name: script.name,
10770
- status: "running",
10771
- current_step: 0,
10772
- total_steps: script.steps.length,
10773
- current_step_description: "Starting...",
10774
- steps_log: [],
10775
- started_at: new Date().toISOString()
10776
- };
10777
- activeJobs.set(jobId, job);
10778
- runScript(script, page, overrides, jobId).catch((err) => {
10779
- job.status = "failed";
10780
- job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
10781
- });
10782
- return jobId;
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 };
10783
10984
  }
10784
- async function runBrowserStep(step, page, vars) {
10785
- const action = step.action;
10786
- if (!action)
10787
- throw new Error("Browser step missing action");
10985
+ async function execBrowser(cfg, step, page, vars) {
10986
+ const action = cfg.action;
10788
10987
  switch (action) {
10789
- case "navigate": {
10790
- const url = interpolate(step.url ?? "", vars);
10791
- 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 });
10792
10990
  await new Promise((r) => setTimeout(r, 1000));
10793
10991
  vars["current_url"] = page.url();
10794
10992
  vars["current_title"] = await page.title();
10795
10993
  break;
10796
- }
10797
10994
  case "type": {
10798
- const selector = interpolate(step.selector ?? "input", vars);
10799
- const value = interpolate(step.value ?? step.text ?? "", vars);
10800
- 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
+ }
10801
11016
  break;
10802
11017
  }
10803
11018
  case "click": {
10804
- const selector = interpolate(step.selector ?? "", vars);
10805
- 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
+ }
10806
11038
  await new Promise((r) => setTimeout(r, 500));
10807
11039
  break;
10808
11040
  }
10809
11041
  case "click_text": {
10810
- const text = interpolate(step.text ?? "", vars);
10811
- 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
+ }
10812
11055
  await new Promise((r) => setTimeout(r, 500));
10813
11056
  break;
10814
11057
  }
10815
- case "wait_for_navigation": {
11058
+ case "wait_for_navigation":
10816
11059
  try {
10817
- await page.waitForNavigation({ timeout: step.timeout ?? 15000 });
11060
+ await page.waitForNavigation({ timeout: cfg.timeout ?? 15000 });
10818
11061
  } catch {}
10819
11062
  await new Promise((r) => setTimeout(r, 1000));
10820
11063
  vars["current_url"] = page.url();
10821
11064
  break;
10822
- }
10823
11065
  case "wait_for_text": {
10824
- const text = interpolate(step.text ?? "", vars);
10825
- await page.waitForSelector(`text=${text}`, { timeout: step.timeout ?? 1e4 });
11066
+ const text = cfg.text;
11067
+ await page.waitForSelector(`text=${text}`, { timeout: cfg.timeout ?? 1e4 });
10826
11068
  break;
10827
11069
  }
10828
- case "snapshot": {
11070
+ case "snapshot":
10829
11071
  vars["page_text"] = await page.evaluate(() => document.body?.textContent?.trim() ?? "");
10830
11072
  break;
10831
- }
10832
11073
  }
10833
11074
  }
10834
- async function runConnectorStep(step, vars) {
10835
- const connectorName = step.connector;
10836
- if (!connectorName)
10837
- throw new Error("Connector step missing connector name");
10838
- const args = (step.args ?? []).map((a) => interpolate(a, vars));
10839
- let result;
10840
- try {
10841
- const bin = `connect-${connectorName}`;
10842
- const proc = Bun.spawn([bin, ...args], {
10843
- stdout: "pipe",
10844
- stderr: "pipe",
10845
- env: { ...process.env, HOME: process.env.HOME ?? "" }
10846
- });
10847
- const stdout = await new Response(proc.stdout).text();
10848
- const stderr = await new Response(proc.stderr).text();
10849
- const exitCode = await proc.exited;
10850
- result = { stdout, stderr, exitCode, success: exitCode === 0 };
10851
- } catch (e) {
10852
- result = { stdout: "", stderr: e.message ?? String(e), exitCode: 1, success: false };
10853
- }
10854
- vars["last_output"] = result.stdout;
10855
- vars["last_success"] = String(result.success);
10856
- vars["last_exit_code"] = String(result.exitCode);
10857
- try {
10858
- const parsed = JSON.parse(result.stdout);
10859
- if (typeof parsed === "object" && parsed !== null) {
10860
- for (const [k, v] of Object.entries(parsed)) {
10861
- if (typeof v === "string" || typeof v === "number") {
10862
- vars[`last.${k}`] = String(v);
10863
- }
10864
- }
10865
- }
10866
- } catch {}
10867
- if (step.save_as) {
10868
- vars[step.save_as] = result.stdout;
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 ?? "" }
11084
+ });
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;
10869
11116
  }
10870
- }
10871
- function decodeHtmlEntities(str) {
10872
- return str.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&#x27;/g, "'");
10873
- }
10874
- function runExtractStep(step, vars) {
10875
- const saveTo = step.save_as ?? "extracted";
10876
- if (step.pattern) {
10877
- const source = step.check ? vars[step.check] ?? "" : vars["last_output"] ?? "";
10878
- const regex = new RegExp(step.pattern);
10879
- 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);
10880
11120
  if (match) {
10881
11121
  vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
10882
11122
  }
10883
11123
  }
10884
- if (step.json_path) {
10885
- const source = vars["last_output"] ?? "{}";
10886
- try {
10887
- let obj = JSON.parse(source);
10888
- for (const key of step.json_path.split(".")) {
10889
- obj = obj?.[key];
10890
- }
10891
- if (obj !== undefined)
10892
- vars[saveTo] = String(obj);
10893
- } catch {}
10894
- }
10895
11124
  }
10896
- function createScriptFromJSON(jsonStr) {
10897
- const parsed = JSON.parse(jsonStr);
10898
- if (!parsed.name)
10899
- throw new Error("Script must have a 'name' field");
10900
- if (!parsed.domain)
10901
- throw new Error("Script must have a 'domain' field");
10902
- if (!parsed.steps || !Array.isArray(parsed.steps) || parsed.steps.length === 0) {
10903
- throw new Error("Script must have a non-empty 'steps' array");
10904
- }
10905
- return {
10906
- name: parsed.name,
10907
- domain: parsed.domain,
10908
- description: parsed.description ?? "",
10909
- variables: parsed.variables ?? {},
10910
- steps: parsed.steps,
10911
- created_at: new Date().toISOString(),
10912
- updated_at: new Date().toISOString()
10913
- };
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;
10914
11135
  }
10915
- function createScriptFromFile(filePath) {
10916
- if (!existsSync5(filePath))
10917
- throw new Error(`File not found: ${filePath}`);
10918
- 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 {}
10919
11142
  }
10920
- var activeJobs;
10921
- var init_login_scripts = __esm(() => {
10922
- init_schema();
10923
- activeJobs = new Map;
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();
10924
11160
  });
10925
11161
 
10926
11162
  // src/lib/api-detector.ts
@@ -11071,8 +11307,8 @@ __export(exports_datasets, {
11071
11307
  deleteDataset: () => deleteDataset
11072
11308
  });
11073
11309
  import { randomUUID as randomUUID14 } from "crypto";
11074
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync10 } from "fs";
11075
- import { join as join10 } from "path";
11310
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
11311
+ import { join as join9 } from "path";
11076
11312
  import { homedir as homedir9 } from "os";
11077
11313
  function saveDataset(data) {
11078
11314
  const db = getDatabase();
@@ -11111,14 +11347,14 @@ function exportDataset(name, format) {
11111
11347
  const dataset = getDatasetByName(name);
11112
11348
  if (!dataset)
11113
11349
  throw new Error(`Dataset '${name}' not found`);
11114
- const dir = join10(process.env["BROWSER_DATA_DIR"] ?? join10(homedir9(), ".browser"), "exports");
11115
- mkdirSync10(dir, { recursive: true });
11350
+ const dir = join9(process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser"), "exports");
11351
+ mkdirSync9(dir, { recursive: true });
11116
11352
  const filename = `${name}.${format}`;
11117
- const path = join10(dir, filename);
11353
+ const path = join9(dir, filename);
11118
11354
  if (format === "csv") {
11119
11355
  const rows = dataset.data;
11120
11356
  if (rows.length === 0) {
11121
- writeFileSync4(path, "");
11357
+ writeFileSync3(path, "");
11122
11358
  return { path, size: 0 };
11123
11359
  }
11124
11360
  const headers = Object.keys(rows[0]);
@@ -11131,11 +11367,11 @@ function exportDataset(name, format) {
11131
11367
  }
11132
11368
  const content = csvLines.join(`
11133
11369
  `);
11134
- writeFileSync4(path, content);
11370
+ writeFileSync3(path, content);
11135
11371
  return { path, size: content.length };
11136
11372
  } else {
11137
11373
  const content = JSON.stringify(dataset.data, null, 2);
11138
- writeFileSync4(path, content);
11374
+ writeFileSync3(path, content);
11139
11375
  return { path, size: content.length };
11140
11376
  }
11141
11377
  }
@@ -11149,8 +11385,8 @@ __export(exports_auth, {
11149
11385
  loginWithCredentials: () => loginWithCredentials,
11150
11386
  getCredentials: () => getCredentials
11151
11387
  });
11152
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
11153
- import { join as join11 } from "path";
11388
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
11389
+ import { join as join10 } from "path";
11154
11390
  import { homedir as homedir10 } from "os";
11155
11391
  async function getCredentials(service) {
11156
11392
  try {
@@ -11161,9 +11397,9 @@ async function getCredentials(service) {
11161
11397
  return { email: email.value, password: password.value };
11162
11398
  }
11163
11399
  } catch {}
11164
- const secretsPath = join11(homedir10(), ".secrets");
11165
- if (existsSync6(secretsPath)) {
11166
- const content = readFileSync4(secretsPath, "utf8");
11400
+ const secretsPath = join10(homedir10(), ".secrets");
11401
+ if (existsSync5(secretsPath)) {
11402
+ const content = readFileSync3(secretsPath, "utf8");
11167
11403
  const lines = content.split(`
11168
11404
  `);
11169
11405
  const prefix = service.toUpperCase().replace(/[^A-Z0-9]/g, "_");
@@ -11343,11 +11579,11 @@ __export(exports_dist, {
11343
11579
  DEFAULT_CONFIG: () => DEFAULT_CONFIG
11344
11580
  });
11345
11581
  import { Database as Database2 } from "bun:sqlite";
11346
- import { existsSync as existsSync7, mkdirSync as mkdirSync11 } from "fs";
11347
- import { dirname, join as join12, resolve } from "path";
11348
- 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";
11349
11585
  import { homedir as homedir11 } from "os";
11350
- 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";
11351
11587
  import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
11352
11588
  import { homedir as homedir22 } from "os";
11353
11589
  import { join as join32 } from "path";
@@ -11358,10 +11594,10 @@ function isInMemoryDb(path) {
11358
11594
  return path === ":memory:" || path.startsWith("file::memory:");
11359
11595
  }
11360
11596
  function findNearestMementosDb(startDir) {
11361
- let dir = resolve(startDir);
11597
+ let dir = resolve2(startDir);
11362
11598
  while (true) {
11363
- const candidate = join12(dir, ".mementos", "mementos.db");
11364
- if (existsSync7(candidate))
11599
+ const candidate = join11(dir, ".mementos", "mementos.db");
11600
+ if (existsSync6(candidate))
11365
11601
  return candidate;
11366
11602
  const parent = dirname(dir);
11367
11603
  if (parent === dir)
@@ -11371,9 +11607,9 @@ function findNearestMementosDb(startDir) {
11371
11607
  return null;
11372
11608
  }
11373
11609
  function findGitRoot(startDir) {
11374
- let dir = resolve(startDir);
11610
+ let dir = resolve2(startDir);
11375
11611
  while (true) {
11376
- if (existsSync7(join12(dir, ".git")))
11612
+ if (existsSync6(join11(dir, ".git")))
11377
11613
  return dir;
11378
11614
  const parent = dirname(dir);
11379
11615
  if (parent === dir)
@@ -11393,18 +11629,18 @@ function getDbPath() {
11393
11629
  if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
11394
11630
  const gitRoot = findGitRoot(cwd);
11395
11631
  if (gitRoot) {
11396
- return join12(gitRoot, ".mementos", "mementos.db");
11632
+ return join11(gitRoot, ".mementos", "mementos.db");
11397
11633
  }
11398
11634
  }
11399
11635
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
11400
- return join12(home, ".mementos", "mementos.db");
11636
+ return join11(home, ".mementos", "mementos.db");
11401
11637
  }
11402
11638
  function ensureDir2(filePath) {
11403
11639
  if (isInMemoryDb(filePath))
11404
11640
  return;
11405
- const dir = dirname(resolve(filePath));
11406
- if (!existsSync7(dir)) {
11407
- mkdirSync11(dir, { recursive: true });
11641
+ const dir = dirname(resolve2(filePath));
11642
+ if (!existsSync6(dir)) {
11643
+ mkdirSync10(dir, { recursive: true });
11408
11644
  }
11409
11645
  }
11410
11646
  function getDatabase2(dbPath) {
@@ -13258,7 +13494,7 @@ function loadConfig() {
13258
13494
  let fileConfig = {};
13259
13495
  if (existsSync22(configPath)) {
13260
13496
  try {
13261
- const raw = readFileSync5(configPath, "utf-8");
13497
+ const raw = readFileSync4(configPath, "utf-8");
13262
13498
  fileConfig = JSON.parse(raw);
13263
13499
  } catch {}
13264
13500
  }
@@ -13291,7 +13527,7 @@ function readGlobalConfig() {
13291
13527
  if (!existsSync22(p))
13292
13528
  return {};
13293
13529
  try {
13294
- return JSON.parse(readFileSync5(p, "utf-8"));
13530
+ return JSON.parse(readFileSync4(p, "utf-8"));
13295
13531
  } catch {
13296
13532
  return {};
13297
13533
  }
@@ -13299,7 +13535,7 @@ function readGlobalConfig() {
13299
13535
  function writeGlobalConfig(data) {
13300
13536
  const p = globalConfigPath();
13301
13537
  ensureDir22(dirname2(p));
13302
- writeFileSync5(p, JSON.stringify(data, null, 2), "utf-8");
13538
+ writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
13303
13539
  }
13304
13540
  function getActiveProfile() {
13305
13541
  const envProfile = process.env["MEMENTOS_PROFILE"];
@@ -13321,7 +13557,7 @@ function listProfiles2() {
13321
13557
  const dir = profilesDir();
13322
13558
  if (!existsSync22(dir))
13323
13559
  return [];
13324
- 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();
13325
13561
  }
13326
13562
  function deleteProfile2(name) {
13327
13563
  const dbPath = join22(profilesDir(), `${name}.db`);
@@ -15964,30 +16200,30 @@ __export(exports_dist2, {
15964
16200
  acquireLock: () => acquireLock2
15965
16201
  });
15966
16202
  import { Database as Database3 } from "bun:sqlite";
15967
- import { mkdirSync as mkdirSync12 } from "fs";
15968
- 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";
15969
16205
  import { homedir as homedir12 } from "os";
15970
16206
  import { randomUUID as randomUUID15 } from "crypto";
15971
16207
  import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
15972
16208
  import { join as join33 } from "path";
15973
16209
  import { homedir as homedir33 } from "os";
15974
- import { readFileSync as readFileSync6 } from "fs";
16210
+ import { readFileSync as readFileSync5 } from "fs";
15975
16211
  import { join as join23 } from "path";
15976
16212
  import { homedir as homedir23 } from "os";
15977
16213
  import { randomUUID as randomUUID22 } from "crypto";
15978
- 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";
15979
16215
  import { join as join43, dirname as dirname22 } from "path";
15980
16216
  import { homedir as homedir42 } from "os";
15981
16217
  function getDbPath2() {
15982
16218
  if (process.env.CONVERSATIONS_DB_PATH)
15983
16219
  return process.env.CONVERSATIONS_DB_PATH;
15984
- return join13(homedir12(), ".conversations", "messages.db");
16220
+ return join12(homedir12(), ".conversations", "messages.db");
15985
16221
  }
15986
16222
  function getDb() {
15987
16223
  if (db)
15988
16224
  return db;
15989
16225
  const dbPath = getDbPath2();
15990
- mkdirSync12(dirname3(dbPath), { recursive: true });
16226
+ mkdirSync11(dirname3(dbPath), { recursive: true });
15991
16227
  db = new Database3(dbPath, { create: true });
15992
16228
  db.exec("PRAGMA journal_mode = WAL");
15993
16229
  db.exec("PRAGMA busy_timeout = 5000");
@@ -16229,7 +16465,7 @@ function loadConfig2() {
16229
16465
  if (cachedConfig && now2 - configLoadedAt < CONFIG_CACHE_MS)
16230
16466
  return cachedConfig;
16231
16467
  try {
16232
- const raw = readFileSync6(getConfigPath(), "utf-8");
16468
+ const raw = readFileSync5(getConfigPath(), "utf-8");
16233
16469
  cachedConfig = JSON.parse(raw);
16234
16470
  configLoadedAt = now2;
16235
16471
  return cachedConfig;
@@ -17146,7 +17382,7 @@ function useSpaceMessages(spaceName) {
17146
17382
  }
17147
17383
  function isNameTaken(name) {
17148
17384
  try {
17149
- const { getDb: getDb2 } = (init_db(), __toCommonJS(exports_db));
17385
+ const { getDb: getDb2 } = (init_db(), __toCommonJS2(exports_db));
17150
17386
  const db2 = getDb2();
17151
17387
  const row = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(name);
17152
17388
  return !!row;
@@ -17175,7 +17411,7 @@ function getAutoName() {
17175
17411
  cachedAutoName = name;
17176
17412
  try {
17177
17413
  mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
17178
- writeFileSync6(AGENT_ID_FILE, name + `
17414
+ writeFileSync5(AGENT_ID_FILE, name + `
17179
17415
  `, "utf-8");
17180
17416
  } catch {}
17181
17417
  return name;
@@ -17781,7 +18017,7 @@ function getGraphStats() {
17781
18017
  map[r.relation] = r.c;
17782
18018
  return { total_edges: total, by_relation: map };
17783
18019
  }
17784
- var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
18020
+ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc2, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
17785
18021
  target = mod != null ? __create2(__getProtoOf2(mod)) : {};
17786
18022
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", { value: mod, enumerable: true }) : target;
17787
18023
  for (let key of __getOwnPropNames2(mod))
@@ -17791,17 +18027,17 @@ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc,
17791
18027
  enumerable: true
17792
18028
  });
17793
18029
  return to;
17794
- }, __moduleCache, __toCommonJS = (from) => {
17795
- var entry = __moduleCache.get(from), desc;
18030
+ }, __moduleCache2, __toCommonJS2 = (from) => {
18031
+ var entry = __moduleCache2.get(from), desc;
17796
18032
  if (entry)
17797
18033
  return entry;
17798
18034
  entry = __defProp3({}, "__esModule", { value: true });
17799
18035
  if (from && typeof from === "object" || typeof from === "function")
17800
18036
  __getOwnPropNames2(from).map((key) => !__hasOwnProp2.call(entry, key) && __defProp3(entry, key, {
17801
18037
  get: () => from[key],
17802
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18038
+ enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
17803
18039
  }));
17804
- __moduleCache.set(from, entry);
18040
+ __moduleCache2.set(from, entry);
17805
18041
  return entry;
17806
18042
  }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __export3 = (target, all) => {
17807
18043
  for (var name in all)
@@ -17817,9 +18053,9 @@ var init_dist2 = __esm(() => {
17817
18053
  __getProtoOf2 = Object.getPrototypeOf;
17818
18054
  __defProp3 = Object.defineProperty;
17819
18055
  __getOwnPropNames2 = Object.getOwnPropertyNames;
17820
- __getOwnPropDesc = Object.getOwnPropertyDescriptor;
18056
+ __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
17821
18057
  __hasOwnProp2 = Object.prototype.hasOwnProperty;
17822
- __moduleCache = /* @__PURE__ */ new WeakMap;
18058
+ __moduleCache2 = /* @__PURE__ */ new WeakMap;
17823
18059
  exports_db = {};
17824
18060
  __export3(exports_db, {
17825
18061
  getDbPath: () => getDbPath2,
@@ -20389,18 +20625,18 @@ __export(exports_dist3, {
20389
20625
  AgentNotFoundError: () => AgentNotFoundError2
20390
20626
  });
20391
20627
  import { Database as Database4 } from "bun:sqlite";
20392
- import { existsSync as existsSync8, mkdirSync as mkdirSync13 } from "fs";
20393
- 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";
20394
20630
  import { existsSync as existsSync33 } from "fs";
20395
20631
  import { join as join34 } from "path";
20396
- 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";
20397
20633
  import { join as join24 } from "path";
20398
20634
  import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
20399
20635
  import { join as join44 } from "path";
20400
20636
  import { existsSync as existsSync52 } from "fs";
20401
20637
  import { join as join52 } from "path";
20402
20638
  import { readFileSync as readFileSync33, statSync as statSync22 } from "fs";
20403
- import { relative, resolve as resolve22, join as join62 } from "path";
20639
+ import { relative, resolve as resolve23, join as join62 } from "path";
20404
20640
  import { execSync as execSync2 } from "child_process";
20405
20641
 
20406
20642
  class TodosClient {
@@ -20623,8 +20859,8 @@ function isInMemoryDb2(path) {
20623
20859
  function findNearestTodosDb(startDir) {
20624
20860
  let dir = resolve3(startDir);
20625
20861
  while (true) {
20626
- const candidate = join14(dir, ".todos", "todos.db");
20627
- if (existsSync8(candidate))
20862
+ const candidate = join13(dir, ".todos", "todos.db");
20863
+ if (existsSync7(candidate))
20628
20864
  return candidate;
20629
20865
  const parent = dirname5(dir);
20630
20866
  if (parent === dir)
@@ -20636,7 +20872,7 @@ function findNearestTodosDb(startDir) {
20636
20872
  function findGitRoot2(startDir) {
20637
20873
  let dir = resolve3(startDir);
20638
20874
  while (true) {
20639
- if (existsSync8(join14(dir, ".git")))
20875
+ if (existsSync7(join13(dir, ".git")))
20640
20876
  return dir;
20641
20877
  const parent = dirname5(dir);
20642
20878
  if (parent === dir)
@@ -20656,18 +20892,18 @@ function getDbPath3() {
20656
20892
  if (process.env["TODOS_DB_SCOPE"] === "project") {
20657
20893
  const gitRoot = findGitRoot2(cwd);
20658
20894
  if (gitRoot) {
20659
- return join14(gitRoot, ".todos", "todos.db");
20895
+ return join13(gitRoot, ".todos", "todos.db");
20660
20896
  }
20661
20897
  }
20662
20898
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
20663
- return join14(home, ".todos", "todos.db");
20899
+ return join13(home, ".todos", "todos.db");
20664
20900
  }
20665
20901
  function ensureDir3(filePath) {
20666
20902
  if (isInMemoryDb2(filePath))
20667
20903
  return;
20668
20904
  const dir = dirname5(resolve3(filePath));
20669
- if (!existsSync8(dir)) {
20670
- mkdirSync13(dir, { recursive: true });
20905
+ if (!existsSync7(dir)) {
20906
+ mkdirSync12(dir, { recursive: true });
20671
20907
  }
20672
20908
  }
20673
20909
  function getDatabase3(dbPath) {
@@ -21133,28 +21369,28 @@ function ensureDir23(dir) {
21133
21369
  function listJsonFiles(dir) {
21134
21370
  if (!existsSync23(dir))
21135
21371
  return [];
21136
- return readdirSync6(dir).filter((f) => f.endsWith(".json"));
21372
+ return readdirSync5(dir).filter((f) => f.endsWith(".json"));
21137
21373
  }
21138
21374
  function readJsonFile(path) {
21139
21375
  try {
21140
- return JSON.parse(readFileSync7(path, "utf-8"));
21376
+ return JSON.parse(readFileSync6(path, "utf-8"));
21141
21377
  } catch {
21142
21378
  return null;
21143
21379
  }
21144
21380
  }
21145
21381
  function writeJsonFile(path, data) {
21146
- writeFileSync7(path, JSON.stringify(data, null, 2) + `
21382
+ writeFileSync6(path, JSON.stringify(data, null, 2) + `
21147
21383
  `);
21148
21384
  }
21149
21385
  function readHighWaterMark(dir) {
21150
21386
  const path = join24(dir, ".highwatermark");
21151
21387
  if (!existsSync23(path))
21152
21388
  return 1;
21153
- const val = parseInt(readFileSync7(path, "utf-8").trim(), 10);
21389
+ const val = parseInt(readFileSync6(path, "utf-8").trim(), 10);
21154
21390
  return isNaN(val) ? 1 : val;
21155
21391
  }
21156
21392
  function writeHighWaterMark(dir, value) {
21157
- writeFileSync7(join24(dir, ".highwatermark"), String(value));
21393
+ writeFileSync6(join24(dir, ".highwatermark"), String(value));
21158
21394
  }
21159
21395
  function getFileMtimeMs(path) {
21160
21396
  try {
@@ -24516,7 +24752,7 @@ function collectFiles(basePath, extensions) {
24516
24752
  return files.sort();
24517
24753
  }
24518
24754
  function extractTodos(options, db2) {
24519
- const basePath = resolve22(options.path);
24755
+ const basePath = resolve23(options.path);
24520
24756
  const tags = options.patterns || [...EXTRACT_TAGS];
24521
24757
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
24522
24758
  const files = collectFiles(basePath, extensions);
@@ -24525,7 +24761,7 @@ function extractTodos(options, db2) {
24525
24761
  const fullPath = statSync22(basePath).isFile() ? basePath : join62(basePath, file);
24526
24762
  try {
24527
24763
  const source = readFileSync33(fullPath, "utf-8");
24528
- const relPath = statSync22(basePath).isFile() ? relative(resolve22(basePath, ".."), fullPath) : file;
24764
+ const relPath = statSync22(basePath).isFile() ? relative(resolve23(basePath, ".."), fullPath) : file;
24529
24765
  const comments = extractFromSource(source, relPath, tags);
24530
24766
  allComments.push(...comments);
24531
24767
  } catch {}
@@ -25744,8 +25980,8 @@ __export(exports_dist4, {
25744
25980
  CATEGORIES: () => CATEGORIES,
25745
25981
  AGENT_TARGETS: () => AGENT_TARGETS
25746
25982
  });
25747
- 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";
25748
- 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";
25749
25985
  import { homedir as homedir13 } from "os";
25750
25986
  import { fileURLToPath } from "url";
25751
25987
  import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
@@ -25857,35 +26093,35 @@ function normalizeSkillName(name) {
25857
26093
  function findSkillsDir() {
25858
26094
  let dir = __dirname2;
25859
26095
  for (let i = 0;i < 5; i++) {
25860
- const candidate = join15(dir, "skills");
25861
- if (existsSync9(candidate)) {
26096
+ const candidate = join14(dir, "skills");
26097
+ if (existsSync8(candidate)) {
25862
26098
  return candidate;
25863
26099
  }
25864
26100
  dir = dirname6(dir);
25865
26101
  }
25866
- return join15(__dirname2, "..", "skills");
26102
+ return join14(__dirname2, "..", "skills");
25867
26103
  }
25868
26104
  function getSkillPath(name) {
25869
26105
  const skillName = normalizeSkillName(name);
25870
- return join15(SKILLS_DIR, skillName);
26106
+ return join14(SKILLS_DIR, skillName);
25871
26107
  }
25872
26108
  function skillExists(name) {
25873
- return existsSync9(getSkillPath(name));
26109
+ return existsSync8(getSkillPath(name));
25874
26110
  }
25875
26111
  function installSkill(name, options = {}) {
25876
26112
  const { targetDir = process.cwd(), overwrite = false } = options;
25877
26113
  const skillName = normalizeSkillName(name);
25878
26114
  const sourcePath = getSkillPath(name);
25879
- const destDir = join15(targetDir, ".skills");
25880
- const destPath = join15(destDir, skillName);
25881
- if (!existsSync9(sourcePath)) {
26115
+ const destDir = join14(targetDir, ".skills");
26116
+ const destPath = join14(destDir, skillName);
26117
+ if (!existsSync8(sourcePath)) {
25882
26118
  return {
25883
26119
  skill: name,
25884
26120
  success: false,
25885
26121
  error: `Skill '${name}' not found`
25886
26122
  };
25887
26123
  }
25888
- if (existsSync9(destPath) && !overwrite) {
26124
+ if (existsSync8(destPath) && !overwrite) {
25889
26125
  return {
25890
26126
  skill: name,
25891
26127
  success: false,
@@ -25894,10 +26130,10 @@ function installSkill(name, options = {}) {
25894
26130
  };
25895
26131
  }
25896
26132
  try {
25897
- if (!existsSync9(destDir)) {
25898
- mkdirSync14(destDir, { recursive: true });
26133
+ if (!existsSync8(destDir)) {
26134
+ mkdirSync13(destDir, { recursive: true });
25899
26135
  }
25900
- if (existsSync9(destPath) && overwrite) {
26136
+ if (existsSync8(destPath) && overwrite) {
25901
26137
  rmSync2(destPath, { recursive: true, force: true });
25902
26138
  }
25903
26139
  cpSync(sourcePath, destPath, {
@@ -25936,10 +26172,10 @@ function installSkills(names, options = {}) {
25936
26172
  return names.map((name) => installSkill(name, options));
25937
26173
  }
25938
26174
  function updateSkillsIndex(skillsDir) {
25939
- const indexPath = join15(skillsDir, "index.ts");
26175
+ const indexPath = join14(skillsDir, "index.ts");
25940
26176
  const meta = loadMeta(skillsDir);
25941
26177
  const disabledSet = new Set(meta.disabled || []);
25942
- 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-", "")));
25943
26179
  const exports = skills.map((s) => {
25944
26180
  const name = s.replace("skill-", "").replace(/-/g, "_");
25945
26181
  return `export * as ${name} from './${s}/src/index.js';`;
@@ -25952,31 +26188,31 @@ function updateSkillsIndex(skillsDir) {
25952
26188
 
25953
26189
  ${exports}
25954
26190
  `;
25955
- writeFileSync8(indexPath, content);
26191
+ writeFileSync7(indexPath, content);
25956
26192
  }
25957
26193
  function getMetaPath(skillsDir) {
25958
- return join15(skillsDir, ".meta.json");
26194
+ return join14(skillsDir, ".meta.json");
25959
26195
  }
25960
26196
  function loadMeta(skillsDir) {
25961
26197
  const metaPath2 = getMetaPath(skillsDir);
25962
- if (existsSync9(metaPath2)) {
26198
+ if (existsSync8(metaPath2)) {
25963
26199
  try {
25964
- return JSON.parse(readFileSync8(metaPath2, "utf-8"));
26200
+ return JSON.parse(readFileSync7(metaPath2, "utf-8"));
25965
26201
  } catch {}
25966
26202
  }
25967
26203
  return { skills: {} };
25968
26204
  }
25969
26205
  function saveMeta(skillsDir, meta) {
25970
- writeFileSync8(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
26206
+ writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
25971
26207
  }
25972
26208
  function recordInstall(skillsDir, name) {
25973
26209
  const meta = loadMeta(skillsDir);
25974
26210
  const skillName = normalizeSkillName(name);
25975
26211
  let version = "unknown";
25976
26212
  try {
25977
- const pkgPath = join15(skillsDir, skillName, "package.json");
25978
- if (existsSync9(pkgPath)) {
25979
- 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"));
25980
26216
  version = pkg.version || "unknown";
25981
26217
  }
25982
26218
  } catch {}
@@ -25989,12 +26225,12 @@ function recordRemove(skillsDir, name) {
25989
26225
  saveMeta(skillsDir, meta);
25990
26226
  }
25991
26227
  function getInstallMeta(targetDir = process.cwd()) {
25992
- return loadMeta(join15(targetDir, ".skills"));
26228
+ return loadMeta(join14(targetDir, ".skills"));
25993
26229
  }
25994
26230
  function disableSkill(name, targetDir = process.cwd()) {
25995
- const skillsDir = join15(targetDir, ".skills");
26231
+ const skillsDir = join14(targetDir, ".skills");
25996
26232
  const skillName = normalizeSkillName(name);
25997
- if (!existsSync9(join15(skillsDir, skillName)))
26233
+ if (!existsSync8(join14(skillsDir, skillName)))
25998
26234
  return false;
25999
26235
  const meta = loadMeta(skillsDir);
26000
26236
  const disabled = new Set(meta.disabled || []);
@@ -26007,7 +26243,7 @@ function disableSkill(name, targetDir = process.cwd()) {
26007
26243
  return true;
26008
26244
  }
26009
26245
  function enableSkill(name, targetDir = process.cwd()) {
26010
- const skillsDir = join15(targetDir, ".skills");
26246
+ const skillsDir = join14(targetDir, ".skills");
26011
26247
  const meta = loadMeta(skillsDir);
26012
26248
  const disabled = new Set(meta.disabled || []);
26013
26249
  if (!disabled.has(name))
@@ -26019,24 +26255,24 @@ function enableSkill(name, targetDir = process.cwd()) {
26019
26255
  return true;
26020
26256
  }
26021
26257
  function getDisabledSkills(targetDir = process.cwd()) {
26022
- const meta = loadMeta(join15(targetDir, ".skills"));
26258
+ const meta = loadMeta(join14(targetDir, ".skills"));
26023
26259
  return meta.disabled || [];
26024
26260
  }
26025
26261
  function getInstalledSkills(targetDir = process.cwd()) {
26026
- const skillsDir = join15(targetDir, ".skills");
26027
- if (!existsSync9(skillsDir)) {
26262
+ const skillsDir = join14(targetDir, ".skills");
26263
+ if (!existsSync8(skillsDir)) {
26028
26264
  return [];
26029
26265
  }
26030
- return readdirSync7(skillsDir).filter((f) => {
26031
- const fullPath = join15(skillsDir, f);
26266
+ return readdirSync6(skillsDir).filter((f) => {
26267
+ const fullPath = join14(skillsDir, f);
26032
26268
  return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
26033
26269
  }).map((f) => f.replace("skill-", ""));
26034
26270
  }
26035
26271
  function removeSkill(name, targetDir = process.cwd()) {
26036
26272
  const skillName = normalizeSkillName(name);
26037
- const skillsDir = join15(targetDir, ".skills");
26038
- const skillPath = join15(skillsDir, skillName);
26039
- if (!existsSync9(skillPath)) {
26273
+ const skillsDir = join14(targetDir, ".skills");
26274
+ const skillPath = join14(skillsDir, skillName);
26275
+ if (!existsSync8(skillPath)) {
26040
26276
  return false;
26041
26277
  }
26042
26278
  rmSync2(skillPath, { recursive: true, force: true });
@@ -26047,25 +26283,25 @@ function removeSkill(name, targetDir = process.cwd()) {
26047
26283
  function getAgentSkillsDir(agent, scope = "global", projectDir) {
26048
26284
  const agentDir = `.${agent}`;
26049
26285
  if (scope === "project") {
26050
- return join15(projectDir || process.cwd(), agentDir, "skills");
26286
+ return join14(projectDir || process.cwd(), agentDir, "skills");
26051
26287
  }
26052
- return join15(homedir13(), agentDir, "skills");
26288
+ return join14(homedir13(), agentDir, "skills");
26053
26289
  }
26054
26290
  function getAgentSkillPath(name, agent, scope = "global", projectDir) {
26055
26291
  const skillName = normalizeSkillName(name);
26056
- return join15(getAgentSkillsDir(agent, scope, projectDir), skillName);
26292
+ return join14(getAgentSkillsDir(agent, scope, projectDir), skillName);
26057
26293
  }
26058
26294
  function installSkillForAgent(name, options, generateSkillMd) {
26059
26295
  const { agent, scope = "global", projectDir } = options;
26060
26296
  const skillName = normalizeSkillName(name);
26061
26297
  const sourcePath = getSkillPath(name);
26062
- if (!existsSync9(sourcePath)) {
26298
+ if (!existsSync8(sourcePath)) {
26063
26299
  return { skill: name, success: false, error: `Skill '${name}' not found` };
26064
26300
  }
26065
26301
  let skillMdContent = null;
26066
- const skillMdPath = join15(sourcePath, "SKILL.md");
26067
- if (existsSync9(skillMdPath)) {
26068
- skillMdContent = readFileSync8(skillMdPath, "utf-8");
26302
+ const skillMdPath = join14(sourcePath, "SKILL.md");
26303
+ if (existsSync8(skillMdPath)) {
26304
+ skillMdContent = readFileSync7(skillMdPath, "utf-8");
26069
26305
  } else if (generateSkillMd) {
26070
26306
  skillMdContent = generateSkillMd(name);
26071
26307
  }
@@ -26074,8 +26310,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
26074
26310
  }
26075
26311
  const destDir = getAgentSkillPath(name, agent, scope, projectDir);
26076
26312
  if (scope === "global") {
26077
- const agentBaseDir2 = join15(homedir13(), `.${agent}`);
26078
- if (!existsSync9(agentBaseDir2)) {
26313
+ const agentBaseDir2 = join14(homedir13(), `.${agent}`);
26314
+ if (!existsSync8(agentBaseDir2)) {
26079
26315
  const agentLabels = {
26080
26316
  claude: "Claude Code",
26081
26317
  codex: "Codex CLI",
@@ -26098,8 +26334,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
26098
26334
  }
26099
26335
  }
26100
26336
  try {
26101
- mkdirSync14(destDir, { recursive: true });
26102
- writeFileSync8(join15(destDir, "SKILL.md"), skillMdContent);
26337
+ mkdirSync13(destDir, { recursive: true });
26338
+ writeFileSync7(join14(destDir, "SKILL.md"), skillMdContent);
26103
26339
  return { skill: name, success: true, path: destDir };
26104
26340
  } catch (error) {
26105
26341
  return {
@@ -26112,7 +26348,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
26112
26348
  function removeSkillForAgent(name, options) {
26113
26349
  const { agent, scope = "global", projectDir } = options;
26114
26350
  const destDir = getAgentSkillPath(name, agent, scope, projectDir);
26115
- if (!existsSync9(destDir)) {
26351
+ if (!existsSync8(destDir)) {
26116
26352
  return false;
26117
26353
  }
26118
26354
  rmSync2(destDir, { recursive: true, force: true });
@@ -32433,8 +32669,8 @@ var NEVER = INVALID;
32433
32669
  // src/mcp/index.ts
32434
32670
  init_session();
32435
32671
  init_actions();
32436
- import { readFileSync as readFileSync9 } from "fs";
32437
- import { join as join16 } from "path";
32672
+ import { readFileSync as readFileSync8 } from "fs";
32673
+ import { join as join15 } from "path";
32438
32674
 
32439
32675
  // src/lib/screenshot.ts
32440
32676
  init_types();
@@ -33204,7 +33440,7 @@ async function closeTab(page, index) {
33204
33440
  init_dialogs();
33205
33441
  init_profiles();
33206
33442
  init_types();
33207
- 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"));
33208
33444
  var networkLogCleanup = new Map;
33209
33445
  var consoleCaptureCleanup = new Map;
33210
33446
  var harCaptures = new Map;
@@ -34909,14 +35145,17 @@ server.tool("browser_profile_delete", "Delete a saved browser profile", { name:
34909
35145
  return err(e);
34910
35146
  }
34911
35147
  });
34912
- 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).", {
34913
- 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"),
34914
35150
  session_id: exports_external.string().optional(),
34915
- variables: exports_external.record(exports_external.string()).optional().describe("Override script variables (e.g. {email: 'foo@bar.com'})")
34916
- }, 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 }) => {
34917
35154
  try {
34918
- const { loadScript: loadScript2, runScriptAsync: runScriptAsync2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34919
- 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);
34920
35159
  if (!script)
34921
35160
  return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
34922
35161
  let sid;
@@ -34925,55 +35164,72 @@ server.tool("browser_script_run", "Run a saved login script asynchronously. Retu
34925
35164
  sid = resolveSessionId(session_id);
34926
35165
  page = getSessionPage(sid);
34927
35166
  } else {
34928
- const result = await createSession2({ headless: true });
35167
+ const result = await createSession2({ engine: engine ?? "auto", headless: true });
34929
35168
  sid = result.session.id;
34930
35169
  page = result.page;
34931
35170
  }
34932
- const jobId = runScriptAsync2(script, page, variables ?? {});
34933
- 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." });
34934
35174
  } catch (e) {
34935
35175
  return err(e);
34936
35176
  }
34937
35177
  });
34938
- 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 }) => {
34939
35179
  try {
34940
- const { getJob: getJob2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34941
- const job = getJob2(job_id);
34942
- if (!job)
34943
- 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`));
34944
35184
  return json({
34945
- status: job.status,
34946
- progress: `${job.current_step}/${job.total_steps}`,
34947
- current_step: job.current_step_description,
34948
- steps_log: job.steps_log,
34949
- 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
34950
35192
  });
34951
35193
  } catch (e) {
34952
35194
  return err(e);
34953
35195
  }
34954
35196
  });
34955
- server.tool("browser_script_list", "List all saved login scripts", {}, async () => {
35197
+ server.tool("browser_script_list", "List all saved scripts", {}, async () => {
34956
35198
  try {
34957
- const { listScripts: listScripts2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34958
- 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 });
34959
35203
  } catch (e) {
34960
35204
  return err(e);
34961
35205
  }
34962
35206
  });
34963
- 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 }) => {
34964
35220
  try {
34965
- const { saveScript: saveScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34966
- const script = JSON.parse(scriptJson);
34967
- const path = saveScript2(script);
34968
- 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 });
34969
35225
  } catch (e) {
34970
35226
  return err(e);
34971
35227
  }
34972
35228
  });
34973
- 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 }) => {
34974
35230
  try {
34975
- const { deleteScript: deleteScript2 } = await Promise.resolve().then(() => (init_login_scripts(), exports_login_scripts));
34976
- return json({ deleted: deleteScript2(name) });
35231
+ const { deleteScriptByName: deleteScriptByName2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
35232
+ return json({ deleted: deleteScriptByName2(name) });
34977
35233
  } catch (e) {
34978
35234
  return err(e);
34979
35235
  }