@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/cli/index.js +815 -549
- package/dist/db/scripts.d.ts +84 -0
- package/dist/db/scripts.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/lib/ai-inference.d.ts +21 -0
- package/dist/lib/ai-inference.d.ts.map +1 -0
- package/dist/lib/login-scripts.d.ts +5 -1
- package/dist/lib/login-scripts.d.ts.map +1 -1
- package/dist/lib/script-engine.d.ts +28 -0
- package/dist/lib/script-engine.d.ts.map +1 -0
- package/dist/mcp/index.js +645 -389
- package/dist/server/index.js +44 -0
- package/package.json +1 -1
package/dist/cli/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)
|
|
@@ -2406,6 +2421,50 @@ function runMigrations(db) {
|
|
|
2406
2421
|
);
|
|
2407
2422
|
CREATE INDEX IF NOT EXISTS idx_api_endpoints_session ON api_endpoints(session_id);
|
|
2408
2423
|
`
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
version: 9,
|
|
2427
|
+
sql: `
|
|
2428
|
+
CREATE TABLE IF NOT EXISTS scripts (
|
|
2429
|
+
id TEXT PRIMARY KEY,
|
|
2430
|
+
name TEXT NOT NULL UNIQUE,
|
|
2431
|
+
domain TEXT NOT NULL DEFAULT '',
|
|
2432
|
+
description TEXT DEFAULT '',
|
|
2433
|
+
variables TEXT NOT NULL DEFAULT '{}',
|
|
2434
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
2435
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
2436
|
+
last_run TEXT,
|
|
2437
|
+
run_count INTEGER DEFAULT 0
|
|
2438
|
+
);
|
|
2439
|
+
|
|
2440
|
+
CREATE TABLE IF NOT EXISTS script_steps (
|
|
2441
|
+
id TEXT PRIMARY KEY,
|
|
2442
|
+
script_id TEXT NOT NULL REFERENCES scripts(id) ON DELETE CASCADE,
|
|
2443
|
+
step_order INTEGER NOT NULL,
|
|
2444
|
+
type TEXT NOT NULL,
|
|
2445
|
+
config TEXT NOT NULL DEFAULT '{}',
|
|
2446
|
+
description TEXT DEFAULT '',
|
|
2447
|
+
ai_enabled INTEGER DEFAULT 0,
|
|
2448
|
+
ai_config TEXT DEFAULT '{}'
|
|
2449
|
+
);
|
|
2450
|
+
CREATE INDEX IF NOT EXISTS idx_script_steps_order ON script_steps(script_id, step_order);
|
|
2451
|
+
|
|
2452
|
+
CREATE TABLE IF NOT EXISTS script_runs (
|
|
2453
|
+
id TEXT PRIMARY KEY,
|
|
2454
|
+
script_id TEXT NOT NULL REFERENCES scripts(id) ON DELETE CASCADE,
|
|
2455
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
2456
|
+
current_step INTEGER DEFAULT 0,
|
|
2457
|
+
total_steps INTEGER DEFAULT 0,
|
|
2458
|
+
current_description TEXT DEFAULT '',
|
|
2459
|
+
variables TEXT DEFAULT '{}',
|
|
2460
|
+
steps_log TEXT DEFAULT '[]',
|
|
2461
|
+
errors TEXT DEFAULT '[]',
|
|
2462
|
+
started_at TEXT DEFAULT (datetime('now')),
|
|
2463
|
+
completed_at TEXT,
|
|
2464
|
+
duration_ms INTEGER
|
|
2465
|
+
);
|
|
2466
|
+
CREATE INDEX IF NOT EXISTS idx_script_runs_script ON script_runs(script_id, status);
|
|
2467
|
+
`
|
|
2409
2468
|
}
|
|
2410
2469
|
];
|
|
2411
2470
|
for (const m of migrations) {
|
|
@@ -4309,6 +4368,10 @@ var init_snapshot = __esm(() => {
|
|
|
4309
4368
|
});
|
|
4310
4369
|
|
|
4311
4370
|
// src/lib/self-heal.ts
|
|
4371
|
+
var exports_self_heal = {};
|
|
4372
|
+
__export(exports_self_heal, {
|
|
4373
|
+
healSelector: () => healSelector
|
|
4374
|
+
});
|
|
4312
4375
|
async function healSelector(page, selector, sessionId) {
|
|
4313
4376
|
const attempts = [];
|
|
4314
4377
|
attempts.push(`selector: ${selector}`);
|
|
@@ -8621,7 +8684,7 @@ var require_operation = __commonJS((exports, module) => {
|
|
|
8621
8684
|
// node_modules/@img/colour/color.cjs
|
|
8622
8685
|
var require_color = __commonJS((exports, module) => {
|
|
8623
8686
|
var __defProp2 = Object.defineProperty;
|
|
8624
|
-
var
|
|
8687
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
8625
8688
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
8626
8689
|
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
8627
8690
|
var __export2 = (target, all) => {
|
|
@@ -8632,16 +8695,16 @@ var require_color = __commonJS((exports, module) => {
|
|
|
8632
8695
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8633
8696
|
for (let key of __getOwnPropNames2(from))
|
|
8634
8697
|
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
8635
|
-
__defProp2(to, key, { get: () => from[key], enumerable: !(desc =
|
|
8698
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
8636
8699
|
}
|
|
8637
8700
|
return to;
|
|
8638
8701
|
};
|
|
8639
|
-
var
|
|
8702
|
+
var __toCommonJS2 = (mod) => __copyProps(__defProp2({}, "__esModule", { value: true }), mod);
|
|
8640
8703
|
var index_exports = {};
|
|
8641
8704
|
__export2(index_exports, {
|
|
8642
8705
|
default: () => index_default
|
|
8643
8706
|
});
|
|
8644
|
-
module.exports =
|
|
8707
|
+
module.exports = __toCommonJS2(index_exports);
|
|
8645
8708
|
var colors = {
|
|
8646
8709
|
aliceblue: [240, 248, 255],
|
|
8647
8710
|
antiquewhite: [250, 235, 215],
|
|
@@ -12845,325 +12908,574 @@ async function loginWithCredentials(page, credentials, opts) {
|
|
|
12845
12908
|
}
|
|
12846
12909
|
var init_auth = () => {};
|
|
12847
12910
|
|
|
12848
|
-
// src/
|
|
12849
|
-
var
|
|
12850
|
-
__export(
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12911
|
+
// src/db/scripts.ts
|
|
12912
|
+
var exports_scripts = {};
|
|
12913
|
+
__export(exports_scripts, {
|
|
12914
|
+
upsertScript: () => upsertScript,
|
|
12915
|
+
updateRunProgress: () => updateRunProgress,
|
|
12916
|
+
startRun: () => startRun,
|
|
12917
|
+
migrateJsonScripts: () => migrateJsonScripts,
|
|
12855
12918
|
listScripts: () => listScripts,
|
|
12856
|
-
|
|
12857
|
-
|
|
12919
|
+
listRuns: () => listRuns,
|
|
12920
|
+
getSteps: () => getSteps,
|
|
12921
|
+
getScriptByName: () => getScriptByName,
|
|
12922
|
+
getScript: () => getScript,
|
|
12923
|
+
getRun: () => getRun,
|
|
12924
|
+
deleteScriptByName: () => deleteScriptByName,
|
|
12858
12925
|
deleteScript: () => deleteScript,
|
|
12859
|
-
|
|
12860
|
-
|
|
12926
|
+
createScript: () => createScript,
|
|
12927
|
+
completeRun: () => completeRun
|
|
12861
12928
|
});
|
|
12862
12929
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
12863
|
-
|
|
12864
|
-
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12930
|
+
function createScript(data) {
|
|
12931
|
+
const db = getDatabase();
|
|
12932
|
+
const id = randomUUID10();
|
|
12933
|
+
db.prepare("INSERT INTO scripts (id, name, domain, description, variables) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.domain ?? "", data.description ?? "", JSON.stringify(data.variables ?? {}));
|
|
12934
|
+
for (let i = 0;i < data.steps.length; i++) {
|
|
12935
|
+
const step = data.steps[i];
|
|
12936
|
+
db.prepare("INSERT INTO script_steps (id, script_id, step_order, type, config, description, ai_enabled, ai_config) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(randomUUID10(), id, i, step.type, JSON.stringify(step.config), step.description ?? "", step.ai_enabled ? 1 : 0, JSON.stringify(step.ai_config ?? {}));
|
|
12937
|
+
}
|
|
12938
|
+
return getScript(id);
|
|
12870
12939
|
}
|
|
12871
|
-
function
|
|
12872
|
-
const
|
|
12873
|
-
|
|
12874
|
-
|
|
12940
|
+
function upsertScript(data) {
|
|
12941
|
+
const existing = getScriptByName(data.name);
|
|
12942
|
+
if (existing) {
|
|
12943
|
+
deleteScript(existing.id);
|
|
12944
|
+
}
|
|
12945
|
+
return createScript(data);
|
|
12875
12946
|
}
|
|
12876
|
-
function
|
|
12877
|
-
const
|
|
12878
|
-
const
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
writeFileSync2(path, JSON.stringify(script, null, 2));
|
|
12883
|
-
return path;
|
|
12947
|
+
function getScript(id) {
|
|
12948
|
+
const db = getDatabase();
|
|
12949
|
+
const row = db.query("SELECT * FROM scripts WHERE id = ?").get(id);
|
|
12950
|
+
if (!row)
|
|
12951
|
+
return null;
|
|
12952
|
+
return { ...row, variables: JSON.parse(row.variables), run_count: row.run_count ?? 0 };
|
|
12884
12953
|
}
|
|
12885
|
-
function
|
|
12886
|
-
const
|
|
12887
|
-
|
|
12954
|
+
function getScriptByName(name) {
|
|
12955
|
+
const db = getDatabase();
|
|
12956
|
+
const row = db.query("SELECT * FROM scripts WHERE name = ?").get(name);
|
|
12957
|
+
if (!row)
|
|
12888
12958
|
return null;
|
|
12889
|
-
return JSON.parse(
|
|
12959
|
+
return { ...row, variables: JSON.parse(row.variables), run_count: row.run_count ?? 0 };
|
|
12890
12960
|
}
|
|
12891
12961
|
function listScripts() {
|
|
12892
|
-
const
|
|
12962
|
+
const db = getDatabase();
|
|
12963
|
+
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 }));
|
|
12964
|
+
}
|
|
12965
|
+
function deleteScript(id) {
|
|
12966
|
+
const db = getDatabase();
|
|
12967
|
+
return db.prepare("DELETE FROM scripts WHERE id = ?").run(id).changes > 0;
|
|
12968
|
+
}
|
|
12969
|
+
function deleteScriptByName(name) {
|
|
12970
|
+
const db = getDatabase();
|
|
12971
|
+
return db.prepare("DELETE FROM scripts WHERE name = ?").run(name).changes > 0;
|
|
12972
|
+
}
|
|
12973
|
+
function getSteps(scriptId) {
|
|
12974
|
+
const db = getDatabase();
|
|
12975
|
+
return db.query("SELECT * FROM script_steps WHERE script_id = ? ORDER BY step_order").all(scriptId).map((row) => ({
|
|
12976
|
+
...row,
|
|
12977
|
+
config: JSON.parse(row.config),
|
|
12978
|
+
ai_enabled: !!row.ai_enabled,
|
|
12979
|
+
ai_config: JSON.parse(row.ai_config)
|
|
12980
|
+
}));
|
|
12981
|
+
}
|
|
12982
|
+
function startRun(scriptId, totalSteps) {
|
|
12983
|
+
const db = getDatabase();
|
|
12984
|
+
const id = randomUUID10();
|
|
12985
|
+
db.prepare("INSERT INTO script_runs (id, script_id, status, total_steps) VALUES (?, ?, 'running', ?)").run(id, scriptId, totalSteps);
|
|
12986
|
+
return getRun(id);
|
|
12987
|
+
}
|
|
12988
|
+
function updateRunProgress(runId, step, description, stepsLog, variables) {
|
|
12989
|
+
const db = getDatabase();
|
|
12990
|
+
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);
|
|
12991
|
+
}
|
|
12992
|
+
function completeRun(runId, status, errors, durationMs) {
|
|
12993
|
+
const db = getDatabase();
|
|
12994
|
+
db.prepare("UPDATE script_runs SET status = ?, errors = ?, duration_ms = ?, completed_at = datetime('now') WHERE id = ?").run(status, JSON.stringify(errors), durationMs, runId);
|
|
12995
|
+
const run = getRun(runId);
|
|
12996
|
+
if (run) {
|
|
12997
|
+
db.prepare("UPDATE scripts SET last_run = datetime('now'), run_count = run_count + 1, updated_at = datetime('now') WHERE id = ?").run(run.script_id);
|
|
12998
|
+
}
|
|
12999
|
+
}
|
|
13000
|
+
function getRun(runId) {
|
|
13001
|
+
const db = getDatabase();
|
|
13002
|
+
const row = db.query("SELECT * FROM script_runs WHERE id = ?").get(runId);
|
|
13003
|
+
if (!row)
|
|
13004
|
+
return null;
|
|
13005
|
+
return {
|
|
13006
|
+
...row,
|
|
13007
|
+
variables: JSON.parse(row.variables),
|
|
13008
|
+
steps_log: JSON.parse(row.steps_log),
|
|
13009
|
+
errors: JSON.parse(row.errors)
|
|
13010
|
+
};
|
|
13011
|
+
}
|
|
13012
|
+
function listRuns(scriptId) {
|
|
13013
|
+
const db = getDatabase();
|
|
13014
|
+
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();
|
|
13015
|
+
return query.map((row) => ({
|
|
13016
|
+
...row,
|
|
13017
|
+
variables: JSON.parse(row.variables),
|
|
13018
|
+
steps_log: JSON.parse(row.steps_log),
|
|
13019
|
+
errors: JSON.parse(row.errors)
|
|
13020
|
+
}));
|
|
13021
|
+
}
|
|
13022
|
+
function migrateJsonScripts() {
|
|
13023
|
+
const { existsSync: existsSync5, readdirSync: readdirSync3, readFileSync: readFileSync3 } = __require("fs");
|
|
13024
|
+
const { join: join8 } = __require("path");
|
|
13025
|
+
const { getDataDir: getDataDir3 } = (init_schema(), __toCommonJS(exports_schema));
|
|
13026
|
+
const dir = join8(getDataDir3(), "scripts");
|
|
12893
13027
|
if (!existsSync5(dir))
|
|
12894
|
-
return
|
|
12895
|
-
|
|
13028
|
+
return 0;
|
|
13029
|
+
const files = readdirSync3(dir).filter((f) => f.endsWith(".json"));
|
|
13030
|
+
let migrated = 0;
|
|
13031
|
+
for (const file of files) {
|
|
12896
13032
|
try {
|
|
12897
|
-
const
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
13033
|
+
const raw = JSON.parse(readFileSync3(join8(dir, file), "utf8"));
|
|
13034
|
+
if (!raw.name || !raw.steps)
|
|
13035
|
+
continue;
|
|
13036
|
+
if (getScriptByName(raw.name))
|
|
13037
|
+
continue;
|
|
13038
|
+
const steps = raw.steps.map((s) => {
|
|
13039
|
+
const isAI = s.type === "ai";
|
|
13040
|
+
return {
|
|
13041
|
+
type: isAI ? "extract" : s.type,
|
|
13042
|
+
config: {
|
|
13043
|
+
action: s.action,
|
|
13044
|
+
selector: s.selector,
|
|
13045
|
+
url: s.url,
|
|
13046
|
+
value: s.value,
|
|
13047
|
+
text: s.text,
|
|
13048
|
+
timeout: s.timeout,
|
|
13049
|
+
connector: s.connector,
|
|
13050
|
+
args: s.args,
|
|
13051
|
+
format: s.format,
|
|
13052
|
+
pattern: s.pattern,
|
|
13053
|
+
json_path: s.json_path,
|
|
13054
|
+
check: s.check,
|
|
13055
|
+
source: s.check,
|
|
13056
|
+
seconds: s.seconds,
|
|
13057
|
+
equals: s.equals,
|
|
13058
|
+
contains: s.contains,
|
|
13059
|
+
skip_to: s.skip_to,
|
|
13060
|
+
name: s.name,
|
|
13061
|
+
save_as: s.save_as,
|
|
13062
|
+
...isAI ? { prompt: s.prompt, source: s.check ?? "last_output" } : {}
|
|
13063
|
+
},
|
|
13064
|
+
description: s.description ?? "",
|
|
13065
|
+
ai_enabled: isAI || !!s.ai?.enabled,
|
|
13066
|
+
ai_config: isAI ? { provider: s.model === "haiku" || s.model === "sonnet" || s.model === "opus" ? "anthropic" : "cerebras", model: s.model ?? "fast" } : s.ai ?? {}
|
|
13067
|
+
};
|
|
13068
|
+
});
|
|
13069
|
+
createScript({
|
|
13070
|
+
name: raw.name,
|
|
13071
|
+
domain: raw.domain ?? "",
|
|
13072
|
+
description: raw.description ?? "",
|
|
13073
|
+
variables: raw.variables ?? {},
|
|
13074
|
+
steps
|
|
13075
|
+
});
|
|
13076
|
+
migrated++;
|
|
13077
|
+
} catch {}
|
|
13078
|
+
}
|
|
13079
|
+
return migrated;
|
|
12903
13080
|
}
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
13081
|
+
var init_scripts = __esm(() => {
|
|
13082
|
+
init_schema();
|
|
13083
|
+
});
|
|
13084
|
+
|
|
13085
|
+
// src/lib/ai-inference.ts
|
|
13086
|
+
function resolve(opts) {
|
|
13087
|
+
if (opts?.model && ALIASES[opts.model])
|
|
13088
|
+
return ALIASES[opts.model];
|
|
13089
|
+
if (opts?.provider && opts?.model)
|
|
13090
|
+
return { provider: opts.provider, model: opts.model };
|
|
13091
|
+
if (opts?.provider === "anthropic")
|
|
13092
|
+
return ALIASES.haiku;
|
|
13093
|
+
return ALIASES.fast;
|
|
13094
|
+
}
|
|
13095
|
+
async function infer(prompt, opts) {
|
|
13096
|
+
const { provider, model } = resolve(opts);
|
|
13097
|
+
const maxTokens = opts?.maxTokens ?? 1024;
|
|
13098
|
+
if (provider === "anthropic") {
|
|
13099
|
+
const apiKey2 = process.env["ANTHROPIC_API_KEY"];
|
|
13100
|
+
if (!apiKey2)
|
|
13101
|
+
throw new Error("ANTHROPIC_API_KEY not set");
|
|
13102
|
+
const res2 = await fetch("https://api.anthropic.com/v1/messages", {
|
|
13103
|
+
method: "POST",
|
|
13104
|
+
headers: { "content-type": "application/json", "x-api-key": apiKey2, "anthropic-version": "2023-06-01" },
|
|
13105
|
+
body: JSON.stringify({ model, max_tokens: maxTokens, messages: [{ role: "user", content: prompt }] })
|
|
13106
|
+
});
|
|
13107
|
+
if (!res2.ok)
|
|
13108
|
+
throw new Error(`Anthropic API ${res2.status}: ${(await res2.text()).slice(0, 200)}`);
|
|
13109
|
+
const data2 = await res2.json();
|
|
13110
|
+
return data2.content?.[0]?.text ?? "";
|
|
13111
|
+
}
|
|
13112
|
+
const apiKey = process.env["CEREBRAS_API_KEY"];
|
|
13113
|
+
if (!apiKey)
|
|
13114
|
+
throw new Error("CEREBRAS_API_KEY not set");
|
|
13115
|
+
const res = await fetch("https://api.cerebras.ai/v1/chat/completions", {
|
|
13116
|
+
method: "POST",
|
|
13117
|
+
headers: { "content-type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
13118
|
+
body: JSON.stringify({ model, max_tokens: maxTokens, temperature: opts?.temperature ?? 0, messages: [{ role: "user", content: prompt }] })
|
|
13119
|
+
});
|
|
13120
|
+
if (!res.ok)
|
|
13121
|
+
throw new Error(`Cerebras API ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
13122
|
+
const data = await res.json();
|
|
13123
|
+
return data.choices?.[0]?.message?.content ?? "";
|
|
13124
|
+
}
|
|
13125
|
+
var ALIASES;
|
|
13126
|
+
var init_ai_inference = __esm(() => {
|
|
13127
|
+
ALIASES = {
|
|
13128
|
+
fast: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
|
|
13129
|
+
scout: { provider: "cerebras", model: "llama-4-scout-17b-16e-instruct" },
|
|
13130
|
+
maverick: { provider: "cerebras", model: "llama-4-maverick-17b-128e-instruct" },
|
|
13131
|
+
haiku: { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
|
|
13132
|
+
sonnet: { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
|
|
13133
|
+
opus: { provider: "anthropic", model: "claude-opus-4-6" }
|
|
13134
|
+
};
|
|
13135
|
+
});
|
|
13136
|
+
|
|
13137
|
+
// src/lib/vision-fallback.ts
|
|
13138
|
+
var exports_vision_fallback = {};
|
|
13139
|
+
__export(exports_vision_fallback, {
|
|
13140
|
+
findElementByVision: () => findElementByVision,
|
|
13141
|
+
clickByVision: () => clickByVision
|
|
13142
|
+
});
|
|
13143
|
+
async function findElementByVision(page, description, opts) {
|
|
13144
|
+
const model = opts?.model ?? process.env["BROWSER_VISION_MODEL"] ?? DEFAULT_MODEL;
|
|
13145
|
+
const screenshot = await page.screenshot({ type: "jpeg", quality: 80 });
|
|
13146
|
+
const base64 = screenshot.toString("base64");
|
|
13147
|
+
const viewport = page.viewportSize() ?? { width: 1280, height: 720 };
|
|
13148
|
+
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
13149
|
+
if (!apiKey) {
|
|
13150
|
+
return { found: false, x: 0, y: 0, confidence: "none", description, model, error: "ANTHROPIC_API_KEY not set" };
|
|
13151
|
+
}
|
|
13152
|
+
try {
|
|
13153
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
13154
|
+
method: "POST",
|
|
13155
|
+
headers: {
|
|
13156
|
+
"content-type": "application/json",
|
|
13157
|
+
"x-api-key": apiKey,
|
|
13158
|
+
"anthropic-version": "2023-06-01"
|
|
13159
|
+
},
|
|
13160
|
+
body: JSON.stringify({
|
|
13161
|
+
model,
|
|
13162
|
+
max_tokens: 256,
|
|
13163
|
+
messages: [{
|
|
13164
|
+
role: "user",
|
|
13165
|
+
content: [
|
|
13166
|
+
{
|
|
13167
|
+
type: "image",
|
|
13168
|
+
source: { type: "base64", media_type: "image/jpeg", data: base64 }
|
|
13169
|
+
},
|
|
13170
|
+
{
|
|
13171
|
+
type: "text",
|
|
13172
|
+
text: `Find the element matching this description: "${description}"
|
|
13173
|
+
|
|
13174
|
+
The screenshot is ${viewport.width}x${viewport.height} pixels.
|
|
13175
|
+
|
|
13176
|
+
Reply with ONLY a JSON object (no markdown, no explanation):
|
|
13177
|
+
{"found": true, "x": <pixel_x>, "y": <pixel_y>, "confidence": "high|medium|low", "description": "<what you found>"}
|
|
13178
|
+
|
|
13179
|
+
If you cannot find the element:
|
|
13180
|
+
{"found": false, "x": 0, "y": 0, "confidence": "none", "description": "not found"}`
|
|
13181
|
+
}
|
|
13182
|
+
]
|
|
13183
|
+
}]
|
|
13184
|
+
})
|
|
13185
|
+
});
|
|
13186
|
+
const data = await response.json();
|
|
13187
|
+
const text = data.content?.[0]?.text ?? "";
|
|
13188
|
+
const jsonStr = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
13189
|
+
const result = JSON.parse(jsonStr);
|
|
13190
|
+
result.model = model;
|
|
13191
|
+
return result;
|
|
13192
|
+
} catch (err) {
|
|
13193
|
+
return {
|
|
13194
|
+
found: false,
|
|
13195
|
+
x: 0,
|
|
13196
|
+
y: 0,
|
|
13197
|
+
confidence: "none",
|
|
13198
|
+
description,
|
|
13199
|
+
model,
|
|
13200
|
+
error: err instanceof Error ? err.message : String(err)
|
|
13201
|
+
};
|
|
13202
|
+
}
|
|
12911
13203
|
}
|
|
13204
|
+
async function clickByVision(page, description, opts) {
|
|
13205
|
+
const result = await findElementByVision(page, description, opts);
|
|
13206
|
+
if (result.found && result.x > 0 && result.y > 0) {
|
|
13207
|
+
await page.mouse.click(result.x, result.y);
|
|
13208
|
+
}
|
|
13209
|
+
return result;
|
|
13210
|
+
}
|
|
13211
|
+
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
13212
|
+
|
|
13213
|
+
// src/lib/script-engine.ts
|
|
13214
|
+
var exports_script_engine = {};
|
|
13215
|
+
__export(exports_script_engine, {
|
|
13216
|
+
executeScriptSync: () => executeScriptSync,
|
|
13217
|
+
executeScript: () => executeScript
|
|
13218
|
+
});
|
|
12912
13219
|
function interpolate(template, vars) {
|
|
12913
|
-
return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (
|
|
12914
|
-
|
|
13220
|
+
return template.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
13221
|
+
}
|
|
13222
|
+
function interpolateConfig(config, vars) {
|
|
13223
|
+
const result = {};
|
|
13224
|
+
for (const [k, v] of Object.entries(config)) {
|
|
13225
|
+
if (typeof v === "string")
|
|
13226
|
+
result[k] = interpolate(v, vars);
|
|
13227
|
+
else if (Array.isArray(v))
|
|
13228
|
+
result[k] = v.map((item) => typeof item === "string" ? interpolate(item, vars) : item);
|
|
13229
|
+
else
|
|
13230
|
+
result[k] = v;
|
|
13231
|
+
}
|
|
13232
|
+
return result;
|
|
13233
|
+
}
|
|
13234
|
+
function executeScript(scriptId, page, overrides = {}) {
|
|
13235
|
+
const steps = getSteps(scriptId);
|
|
13236
|
+
const run = startRun(scriptId, steps.length);
|
|
13237
|
+
_runSteps(run.id, scriptId, steps, page, overrides).catch((err) => {
|
|
13238
|
+
completeRun(run.id, "failed", [err instanceof Error ? err.message : String(err)], 0);
|
|
12915
13239
|
});
|
|
13240
|
+
return run.id;
|
|
12916
13241
|
}
|
|
12917
|
-
function
|
|
12918
|
-
|
|
13242
|
+
async function executeScriptSync(scriptId, page, overrides = {}) {
|
|
13243
|
+
const steps = getSteps(scriptId);
|
|
13244
|
+
const run = startRun(scriptId, steps.length);
|
|
13245
|
+
return _runSteps(run.id, scriptId, steps, page, overrides);
|
|
12919
13246
|
}
|
|
12920
|
-
async function
|
|
13247
|
+
async function _runSteps(runId, scriptId, steps, page, overrides) {
|
|
12921
13248
|
const t0 = Date.now();
|
|
12922
|
-
const
|
|
13249
|
+
const { getScript: getScript2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
13250
|
+
const script = getScript2(scriptId);
|
|
13251
|
+
const vars = { ...script?.variables ?? {}, ...overrides };
|
|
12923
13252
|
const errors = [];
|
|
13253
|
+
const stepsLog = [];
|
|
12924
13254
|
let executed = 0;
|
|
12925
13255
|
let failed = 0;
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
current_step: 0,
|
|
12931
|
-
total_steps: script.steps.length,
|
|
12932
|
-
current_step_description: "Starting...",
|
|
12933
|
-
steps_log: [],
|
|
12934
|
-
started_at: new Date().toISOString()
|
|
12935
|
-
};
|
|
12936
|
-
activeJobs.set(job.id, job);
|
|
12937
|
-
for (let i = 0;i < script.steps.length; i++) {
|
|
12938
|
-
const step = script.steps[i];
|
|
12939
|
-
const desc = stepDescription(step);
|
|
13256
|
+
for (let i = 0;i < steps.length; i++) {
|
|
13257
|
+
const step = steps[i];
|
|
13258
|
+
const cfg = interpolateConfig(step.config, vars);
|
|
13259
|
+
const desc = step.description || `${step.type}`;
|
|
12940
13260
|
executed++;
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
job.steps_log.push({ step: i + 1, type: step.type, description: desc, status: "running" });
|
|
13261
|
+
stepsLog.push({ step: i + 1, type: step.type, description: desc, status: "running" });
|
|
13262
|
+
updateRunProgress(runId, i + 1, desc, stepsLog, vars);
|
|
12944
13263
|
const stepStart = Date.now();
|
|
12945
13264
|
try {
|
|
12946
13265
|
switch (step.type) {
|
|
12947
13266
|
case "browser":
|
|
12948
|
-
await
|
|
13267
|
+
await execBrowser(cfg, step, page, vars);
|
|
12949
13268
|
break;
|
|
12950
13269
|
case "connector":
|
|
12951
|
-
await
|
|
13270
|
+
await execConnector(cfg, step, vars);
|
|
12952
13271
|
break;
|
|
12953
13272
|
case "extract":
|
|
12954
|
-
|
|
13273
|
+
await execExtract(cfg, step, vars);
|
|
12955
13274
|
break;
|
|
12956
13275
|
case "wait":
|
|
12957
|
-
await new Promise((r) => setTimeout(r, (
|
|
13276
|
+
await new Promise((r) => setTimeout(r, (cfg.seconds ?? 3) * 1000));
|
|
12958
13277
|
break;
|
|
12959
|
-
case "condition":
|
|
12960
|
-
|
|
12961
|
-
let conditionMet = true;
|
|
12962
|
-
if (step.equals !== undefined)
|
|
12963
|
-
conditionMet = checkVal === interpolate(step.equals, vars);
|
|
12964
|
-
if (step.contains !== undefined)
|
|
12965
|
-
conditionMet = checkVal?.includes(interpolate(step.contains, vars)) ?? false;
|
|
12966
|
-
if (!conditionMet && step.skip_to !== undefined) {
|
|
12967
|
-
i = step.skip_to - 1;
|
|
12968
|
-
}
|
|
13278
|
+
case "condition":
|
|
13279
|
+
i = execCondition(cfg, vars, i);
|
|
12969
13280
|
break;
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
const stateName = interpolate(step.name ?? script.name, vars);
|
|
12973
|
-
try {
|
|
12974
|
-
const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
|
|
12975
|
-
const path = await saveStateFromPage2(page, stateName);
|
|
12976
|
-
vars["saved_state_path"] = path;
|
|
12977
|
-
} catch {}
|
|
13281
|
+
case "save_state":
|
|
13282
|
+
await execSaveState(cfg, page, vars);
|
|
12978
13283
|
break;
|
|
12979
|
-
}
|
|
12980
13284
|
}
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
logEntry.duration_ms = Date.now() - stepStart;
|
|
13285
|
+
stepsLog[stepsLog.length - 1].status = "ok";
|
|
13286
|
+
stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
|
|
12984
13287
|
} catch (err) {
|
|
12985
13288
|
failed++;
|
|
12986
|
-
const msg = `Step ${i + 1} (${step.type}
|
|
13289
|
+
const msg = `Step ${i + 1} (${step.type}): ${err instanceof Error ? err.message : String(err)}`;
|
|
12987
13290
|
errors.push(msg);
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
if (step.type === "browser" && step.action === "navigate")
|
|
13291
|
+
stepsLog[stepsLog.length - 1].status = "failed";
|
|
13292
|
+
stepsLog[stepsLog.length - 1].error = err instanceof Error ? err.message : String(err);
|
|
13293
|
+
stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
|
|
13294
|
+
if (step.type === "browser" && cfg.action === "navigate")
|
|
12993
13295
|
break;
|
|
12994
13296
|
}
|
|
13297
|
+
updateRunProgress(runId, i + 1, desc, stepsLog, vars);
|
|
12995
13298
|
}
|
|
12996
|
-
const
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
variables: vars,
|
|
13001
|
-
errors,
|
|
13002
|
-
duration_ms: Date.now() - t0
|
|
13003
|
-
};
|
|
13004
|
-
job.status = failed === 0 ? "completed" : "failed";
|
|
13005
|
-
job.result = result;
|
|
13006
|
-
return result;
|
|
13299
|
+
const durationMs = Date.now() - t0;
|
|
13300
|
+
const status = failed === 0 ? "completed" : "failed";
|
|
13301
|
+
completeRun(runId, status, errors, durationMs);
|
|
13302
|
+
return { run_id: runId, success: failed === 0, steps_executed: executed, steps_failed: failed, errors, duration_ms: durationMs, variables: vars };
|
|
13007
13303
|
}
|
|
13008
|
-
function
|
|
13009
|
-
const
|
|
13010
|
-
const job = {
|
|
13011
|
-
id: jobId,
|
|
13012
|
-
script_name: script.name,
|
|
13013
|
-
status: "running",
|
|
13014
|
-
current_step: 0,
|
|
13015
|
-
total_steps: script.steps.length,
|
|
13016
|
-
current_step_description: "Starting...",
|
|
13017
|
-
steps_log: [],
|
|
13018
|
-
started_at: new Date().toISOString()
|
|
13019
|
-
};
|
|
13020
|
-
activeJobs.set(jobId, job);
|
|
13021
|
-
runScript(script, page, overrides, jobId).catch((err) => {
|
|
13022
|
-
job.status = "failed";
|
|
13023
|
-
job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
|
|
13024
|
-
});
|
|
13025
|
-
return jobId;
|
|
13026
|
-
}
|
|
13027
|
-
async function runBrowserStep(step, page, vars) {
|
|
13028
|
-
const action = step.action;
|
|
13029
|
-
if (!action)
|
|
13030
|
-
throw new Error("Browser step missing action");
|
|
13304
|
+
async function execBrowser(cfg, step, page, vars) {
|
|
13305
|
+
const action = cfg.action;
|
|
13031
13306
|
switch (action) {
|
|
13032
|
-
case "navigate":
|
|
13033
|
-
|
|
13034
|
-
await page.goto(url, { waitUntil: "domcontentloaded", timeout: step.timeout ?? 30000 });
|
|
13307
|
+
case "navigate":
|
|
13308
|
+
await page.goto(cfg.url, { waitUntil: "domcontentloaded", timeout: cfg.timeout ?? 30000 });
|
|
13035
13309
|
await new Promise((r) => setTimeout(r, 1000));
|
|
13036
13310
|
vars["current_url"] = page.url();
|
|
13037
13311
|
vars["current_title"] = await page.title();
|
|
13038
13312
|
break;
|
|
13039
|
-
}
|
|
13040
13313
|
case "type": {
|
|
13041
|
-
const selector =
|
|
13042
|
-
const value =
|
|
13043
|
-
|
|
13314
|
+
const selector = cfg.selector ?? "input";
|
|
13315
|
+
const value = cfg.value ?? cfg.text ?? "";
|
|
13316
|
+
try {
|
|
13317
|
+
await page.fill(selector, value);
|
|
13318
|
+
} catch (origErr) {
|
|
13319
|
+
if (step.ai_enabled) {
|
|
13320
|
+
const healed = await aiSelfHeal(page, `input field for typing "${value}"`, step);
|
|
13321
|
+
if (healed) {
|
|
13322
|
+
await page.mouse.click(healed.x, healed.y);
|
|
13323
|
+
await page.keyboard.type(value);
|
|
13324
|
+
} else
|
|
13325
|
+
throw origErr;
|
|
13326
|
+
} else {
|
|
13327
|
+
const { healSelector: healSelector2 } = await Promise.resolve().then(() => exports_self_heal);
|
|
13328
|
+
const result = await healSelector2(page, selector);
|
|
13329
|
+
if (result.found && result.locator)
|
|
13330
|
+
await result.locator.fill(value);
|
|
13331
|
+
else
|
|
13332
|
+
throw origErr;
|
|
13333
|
+
}
|
|
13334
|
+
}
|
|
13044
13335
|
break;
|
|
13045
13336
|
}
|
|
13046
13337
|
case "click": {
|
|
13047
|
-
const selector =
|
|
13048
|
-
|
|
13338
|
+
const selector = cfg.selector;
|
|
13339
|
+
try {
|
|
13340
|
+
await page.click(selector, { timeout: cfg.timeout ?? 1e4 });
|
|
13341
|
+
} catch (origErr) {
|
|
13342
|
+
if (step.ai_enabled) {
|
|
13343
|
+
const healed = await aiSelfHeal(page, `clickable element matching "${selector}"`, step);
|
|
13344
|
+
if (healed)
|
|
13345
|
+
await page.mouse.click(healed.x, healed.y);
|
|
13346
|
+
else
|
|
13347
|
+
throw origErr;
|
|
13348
|
+
} else {
|
|
13349
|
+
const { healSelector: healSelector2 } = await Promise.resolve().then(() => exports_self_heal);
|
|
13350
|
+
const result = await healSelector2(page, selector);
|
|
13351
|
+
if (result.found && result.locator)
|
|
13352
|
+
await result.locator.click();
|
|
13353
|
+
else
|
|
13354
|
+
throw origErr;
|
|
13355
|
+
}
|
|
13356
|
+
}
|
|
13049
13357
|
await new Promise((r) => setTimeout(r, 500));
|
|
13050
13358
|
break;
|
|
13051
13359
|
}
|
|
13052
13360
|
case "click_text": {
|
|
13053
|
-
const text =
|
|
13054
|
-
|
|
13361
|
+
const text = cfg.text;
|
|
13362
|
+
try {
|
|
13363
|
+
await page.getByText(text, { exact: false }).first().click({ timeout: cfg.timeout ?? 1e4 });
|
|
13364
|
+
} catch (origErr) {
|
|
13365
|
+
if (step.ai_enabled) {
|
|
13366
|
+
const healed = await aiSelfHeal(page, `button or link with text "${text}"`, step);
|
|
13367
|
+
if (healed)
|
|
13368
|
+
await page.mouse.click(healed.x, healed.y);
|
|
13369
|
+
else
|
|
13370
|
+
throw origErr;
|
|
13371
|
+
} else
|
|
13372
|
+
throw origErr;
|
|
13373
|
+
}
|
|
13055
13374
|
await new Promise((r) => setTimeout(r, 500));
|
|
13056
13375
|
break;
|
|
13057
13376
|
}
|
|
13058
|
-
case "wait_for_navigation":
|
|
13377
|
+
case "wait_for_navigation":
|
|
13059
13378
|
try {
|
|
13060
|
-
await page.waitForNavigation({ timeout:
|
|
13379
|
+
await page.waitForNavigation({ timeout: cfg.timeout ?? 15000 });
|
|
13061
13380
|
} catch {}
|
|
13062
13381
|
await new Promise((r) => setTimeout(r, 1000));
|
|
13063
13382
|
vars["current_url"] = page.url();
|
|
13064
13383
|
break;
|
|
13065
|
-
}
|
|
13066
13384
|
case "wait_for_text": {
|
|
13067
|
-
const text =
|
|
13068
|
-
await page.waitForSelector(`text=${text}`, { timeout:
|
|
13385
|
+
const text = cfg.text;
|
|
13386
|
+
await page.waitForSelector(`text=${text}`, { timeout: cfg.timeout ?? 1e4 });
|
|
13069
13387
|
break;
|
|
13070
13388
|
}
|
|
13071
|
-
case "snapshot":
|
|
13389
|
+
case "snapshot":
|
|
13072
13390
|
vars["page_text"] = await page.evaluate(() => document.body?.textContent?.trim() ?? "");
|
|
13073
13391
|
break;
|
|
13074
|
-
}
|
|
13075
13392
|
}
|
|
13076
13393
|
}
|
|
13077
|
-
async function
|
|
13078
|
-
const
|
|
13079
|
-
if (!
|
|
13080
|
-
throw new Error("Connector step missing connector
|
|
13081
|
-
const args =
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13394
|
+
async function execConnector(cfg, step, vars) {
|
|
13395
|
+
const connector = cfg.connector;
|
|
13396
|
+
if (!connector)
|
|
13397
|
+
throw new Error("Connector step missing 'connector' in config");
|
|
13398
|
+
const args = cfg.args ?? [];
|
|
13399
|
+
const proc = Bun.spawn([`connect-${connector}`, ...args], {
|
|
13400
|
+
stdout: "pipe",
|
|
13401
|
+
stderr: "pipe",
|
|
13402
|
+
env: { ...process.env, HOME: process.env.HOME ?? "" }
|
|
13403
|
+
});
|
|
13404
|
+
const stdout = await new Response(proc.stdout).text();
|
|
13405
|
+
const stderr = await new Response(proc.stderr).text();
|
|
13406
|
+
const exitCode = await proc.exited;
|
|
13407
|
+
if (exitCode !== 0 && !stdout)
|
|
13408
|
+
throw new Error(`Connector ${connector} failed: ${stderr.slice(0, 200)}`);
|
|
13409
|
+
const raw = stdout || stderr;
|
|
13410
|
+
vars["last_output"] = raw;
|
|
13411
|
+
if (step.ai_enabled && step.ai_config?.prompt) {
|
|
13412
|
+
const aiPrompt = interpolate(step.ai_config.prompt, { ...vars, last_output: raw });
|
|
13413
|
+
const provider = step.ai_config.provider ?? "cerebras";
|
|
13414
|
+
const model = step.ai_config.model ?? "fast";
|
|
13415
|
+
const parsed = await infer(aiPrompt, { provider, model });
|
|
13416
|
+
const saveTo = cfg.save_as ?? "last_output";
|
|
13417
|
+
vars[saveTo] = parsed.trim();
|
|
13418
|
+
} else if (cfg.save_as) {
|
|
13419
|
+
vars[cfg.save_as] = raw;
|
|
13420
|
+
}
|
|
13421
|
+
}
|
|
13422
|
+
async function execExtract(cfg, step, vars) {
|
|
13423
|
+
const saveTo = cfg.save_as ?? "extracted";
|
|
13424
|
+
if (step.ai_enabled || cfg.prompt) {
|
|
13425
|
+
const source = cfg.source ? vars[cfg.source] ?? "" : vars["last_output"] ?? "";
|
|
13426
|
+
const prompt = interpolate(cfg.prompt ?? `Extract the key information from this text:
|
|
13427
|
+
|
|
13428
|
+
${source}`, { ...vars, source });
|
|
13429
|
+
const provider = step.ai_config?.provider ?? "cerebras";
|
|
13430
|
+
const model = step.ai_config?.model ?? "fast";
|
|
13431
|
+
const result = await infer(prompt, { provider, model });
|
|
13432
|
+
vars[saveTo] = result.trim();
|
|
13433
|
+
vars["last_output"] = result.trim();
|
|
13434
|
+
return;
|
|
13112
13435
|
}
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
}
|
|
13117
|
-
function runExtractStep(step, vars) {
|
|
13118
|
-
const saveTo = step.save_as ?? "extracted";
|
|
13119
|
-
if (step.pattern) {
|
|
13120
|
-
const source = step.check ? vars[step.check] ?? "" : vars["last_output"] ?? "";
|
|
13121
|
-
const regex = new RegExp(step.pattern);
|
|
13122
|
-
const match = regex.exec(source);
|
|
13436
|
+
if (cfg.pattern) {
|
|
13437
|
+
const source = cfg.source ? vars[cfg.source] ?? "" : vars["last_output"] ?? "";
|
|
13438
|
+
const match = new RegExp(cfg.pattern).exec(source);
|
|
13123
13439
|
if (match) {
|
|
13124
13440
|
vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
|
|
13125
13441
|
}
|
|
13126
13442
|
}
|
|
13127
|
-
if (step.json_path) {
|
|
13128
|
-
const source = vars["last_output"] ?? "{}";
|
|
13129
|
-
try {
|
|
13130
|
-
let obj = JSON.parse(source);
|
|
13131
|
-
for (const key of step.json_path.split(".")) {
|
|
13132
|
-
obj = obj?.[key];
|
|
13133
|
-
}
|
|
13134
|
-
if (obj !== undefined)
|
|
13135
|
-
vars[saveTo] = String(obj);
|
|
13136
|
-
} catch {}
|
|
13137
|
-
}
|
|
13138
13443
|
}
|
|
13139
|
-
function
|
|
13140
|
-
const
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
return
|
|
13149
|
-
name: parsed.name,
|
|
13150
|
-
domain: parsed.domain,
|
|
13151
|
-
description: parsed.description ?? "",
|
|
13152
|
-
variables: parsed.variables ?? {},
|
|
13153
|
-
steps: parsed.steps,
|
|
13154
|
-
created_at: new Date().toISOString(),
|
|
13155
|
-
updated_at: new Date().toISOString()
|
|
13156
|
-
};
|
|
13444
|
+
function execCondition(cfg, vars, i) {
|
|
13445
|
+
const checkVal = vars[cfg.check ?? ""];
|
|
13446
|
+
let met = true;
|
|
13447
|
+
if (cfg.equals !== undefined)
|
|
13448
|
+
met = checkVal === interpolate(cfg.equals, vars);
|
|
13449
|
+
if (cfg.contains !== undefined)
|
|
13450
|
+
met = checkVal?.includes(interpolate(cfg.contains, vars)) ?? false;
|
|
13451
|
+
if (!met && cfg.skip_to !== undefined)
|
|
13452
|
+
return cfg.skip_to - 1;
|
|
13453
|
+
return i;
|
|
13157
13454
|
}
|
|
13158
|
-
function
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
13455
|
+
async function execSaveState(cfg, page, vars) {
|
|
13456
|
+
const name = interpolate(cfg.name ?? "default", vars);
|
|
13457
|
+
try {
|
|
13458
|
+
const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
|
|
13459
|
+
vars["saved_state_path"] = await saveStateFromPage2(page, name);
|
|
13460
|
+
} catch {}
|
|
13162
13461
|
}
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13462
|
+
async function aiSelfHeal(page, description, step) {
|
|
13463
|
+
try {
|
|
13464
|
+
const { findElementByVision: findElementByVision2 } = await Promise.resolve().then(() => exports_vision_fallback);
|
|
13465
|
+
const provider = step.ai_config?.provider ?? undefined;
|
|
13466
|
+
const model = step.ai_config?.model ?? undefined;
|
|
13467
|
+
const result = await findElementByVision2(page, description, { model: model ?? provider });
|
|
13468
|
+
if (result.found)
|
|
13469
|
+
return { x: result.x, y: result.y };
|
|
13470
|
+
} catch {}
|
|
13471
|
+
return null;
|
|
13472
|
+
}
|
|
13473
|
+
function decodeHtmlEntities(str) {
|
|
13474
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'");
|
|
13475
|
+
}
|
|
13476
|
+
var init_script_engine = __esm(() => {
|
|
13477
|
+
init_scripts();
|
|
13478
|
+
init_ai_inference();
|
|
13167
13479
|
});
|
|
13168
13480
|
|
|
13169
13481
|
// src/lib/daemon-client.ts
|
|
@@ -13175,8 +13487,8 @@ __export(exports_daemon_client, {
|
|
|
13175
13487
|
getDaemonPidFile: () => getDaemonPidFile,
|
|
13176
13488
|
getDaemonPid: () => getDaemonPid
|
|
13177
13489
|
});
|
|
13178
|
-
import { existsSync as
|
|
13179
|
-
import { join as
|
|
13490
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
13491
|
+
import { join as join8 } from "path";
|
|
13180
13492
|
import { homedir as homedir8 } from "os";
|
|
13181
13493
|
function getDaemonPidFile() {
|
|
13182
13494
|
return PID_FILE;
|
|
@@ -13185,10 +13497,10 @@ function getDaemonPort() {
|
|
|
13185
13497
|
return parseInt(process.env["BROWSER_DAEMON_PORT"] ?? String(DEFAULT_PORT), 10);
|
|
13186
13498
|
}
|
|
13187
13499
|
function isDaemonRunning() {
|
|
13188
|
-
if (!
|
|
13500
|
+
if (!existsSync5(PID_FILE))
|
|
13189
13501
|
return false;
|
|
13190
13502
|
try {
|
|
13191
|
-
const pid = parseInt(
|
|
13503
|
+
const pid = parseInt(readFileSync3(PID_FILE, "utf8").trim(), 10);
|
|
13192
13504
|
process.kill(pid, 0);
|
|
13193
13505
|
return true;
|
|
13194
13506
|
} catch {
|
|
@@ -13196,10 +13508,10 @@ function isDaemonRunning() {
|
|
|
13196
13508
|
}
|
|
13197
13509
|
}
|
|
13198
13510
|
function getDaemonPid() {
|
|
13199
|
-
if (!
|
|
13511
|
+
if (!existsSync5(PID_FILE))
|
|
13200
13512
|
return null;
|
|
13201
13513
|
try {
|
|
13202
|
-
return parseInt(
|
|
13514
|
+
return parseInt(readFileSync3(PID_FILE, "utf8").trim(), 10);
|
|
13203
13515
|
} catch {
|
|
13204
13516
|
return null;
|
|
13205
13517
|
}
|
|
@@ -13219,7 +13531,7 @@ async function getDaemonStatus() {
|
|
|
13219
13531
|
}
|
|
13220
13532
|
var PID_FILE, DEFAULT_PORT = 7030;
|
|
13221
13533
|
var init_daemon_client = __esm(() => {
|
|
13222
|
-
PID_FILE =
|
|
13534
|
+
PID_FILE = join8(process.env["BROWSER_DATA_DIR"] ?? join8(homedir8(), ".browser"), "daemon.pid");
|
|
13223
13535
|
});
|
|
13224
13536
|
|
|
13225
13537
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -17293,16 +17605,16 @@ __export(exports_downloads, {
|
|
|
17293
17605
|
cleanStaleDownloads: () => cleanStaleDownloads
|
|
17294
17606
|
});
|
|
17295
17607
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
17296
|
-
import { join as
|
|
17297
|
-
import { mkdirSync as
|
|
17608
|
+
import { join as join9, basename, extname } from "path";
|
|
17609
|
+
import { mkdirSync as mkdirSync7, existsSync as existsSync6, readdirSync as readdirSync3, statSync, unlinkSync as unlinkSync2, copyFileSync, writeFileSync as writeFileSync2, readFileSync as readFileSync4 } from "fs";
|
|
17298
17610
|
import { homedir as homedir9 } from "os";
|
|
17299
17611
|
function getDataDir3() {
|
|
17300
|
-
return process.env["BROWSER_DATA_DIR"] ??
|
|
17612
|
+
return process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser");
|
|
17301
17613
|
}
|
|
17302
17614
|
function getDownloadsDir(sessionId) {
|
|
17303
|
-
const base =
|
|
17304
|
-
const dir = sessionId ?
|
|
17305
|
-
|
|
17615
|
+
const base = join9(getDataDir3(), "downloads");
|
|
17616
|
+
const dir = sessionId ? join9(base, sessionId) : base;
|
|
17617
|
+
mkdirSync7(dir, { recursive: true });
|
|
17306
17618
|
return dir;
|
|
17307
17619
|
}
|
|
17308
17620
|
function ensureDownloadsDir() {
|
|
@@ -17317,8 +17629,8 @@ function saveToDownloads(buffer, filename, opts) {
|
|
|
17317
17629
|
const ext = extname(filename) || "";
|
|
17318
17630
|
const stem = basename(filename, ext);
|
|
17319
17631
|
const uniqueName = `${stem}-${id.slice(0, 8)}${ext}`;
|
|
17320
|
-
const filePath =
|
|
17321
|
-
|
|
17632
|
+
const filePath = join9(dir, uniqueName);
|
|
17633
|
+
writeFileSync2(filePath, buffer);
|
|
17322
17634
|
const meta = {
|
|
17323
17635
|
id,
|
|
17324
17636
|
type: opts?.type ?? detectType(filename),
|
|
@@ -17329,7 +17641,7 @@ function saveToDownloads(buffer, filename, opts) {
|
|
|
17329
17641
|
original_name: filename,
|
|
17330
17642
|
...opts?.metadata
|
|
17331
17643
|
};
|
|
17332
|
-
|
|
17644
|
+
writeFileSync2(metaPath(filePath), JSON.stringify(meta, null, 2));
|
|
17333
17645
|
return {
|
|
17334
17646
|
id,
|
|
17335
17647
|
path: filePath,
|
|
@@ -17346,23 +17658,23 @@ function listDownloads(sessionId) {
|
|
|
17346
17658
|
const dir = getDownloadsDir(sessionId);
|
|
17347
17659
|
const results = [];
|
|
17348
17660
|
function scanDir(d) {
|
|
17349
|
-
if (!
|
|
17661
|
+
if (!existsSync6(d))
|
|
17350
17662
|
return;
|
|
17351
|
-
const entries =
|
|
17663
|
+
const entries = readdirSync3(d);
|
|
17352
17664
|
for (const entry of entries) {
|
|
17353
17665
|
if (entry.endsWith(".meta.json"))
|
|
17354
17666
|
continue;
|
|
17355
|
-
const full =
|
|
17667
|
+
const full = join9(d, entry);
|
|
17356
17668
|
const stat = statSync(full);
|
|
17357
17669
|
if (stat.isDirectory()) {
|
|
17358
17670
|
scanDir(full);
|
|
17359
17671
|
continue;
|
|
17360
17672
|
}
|
|
17361
17673
|
const mpath = metaPath(full);
|
|
17362
|
-
if (!
|
|
17674
|
+
if (!existsSync6(mpath))
|
|
17363
17675
|
continue;
|
|
17364
17676
|
try {
|
|
17365
|
-
const meta = JSON.parse(
|
|
17677
|
+
const meta = JSON.parse(readFileSync4(mpath, "utf8"));
|
|
17366
17678
|
results.push({
|
|
17367
17679
|
id: meta.id,
|
|
17368
17680
|
path: full,
|
|
@@ -17390,7 +17702,7 @@ function deleteDownload(id, sessionId) {
|
|
|
17390
17702
|
return false;
|
|
17391
17703
|
try {
|
|
17392
17704
|
unlinkSync2(file.path);
|
|
17393
|
-
if (
|
|
17705
|
+
if (existsSync6(file.meta_path))
|
|
17394
17706
|
unlinkSync2(file.meta_path);
|
|
17395
17707
|
return true;
|
|
17396
17708
|
} catch {
|
|
@@ -17437,8 +17749,8 @@ function detectType(filename) {
|
|
|
17437
17749
|
var init_downloads = () => {};
|
|
17438
17750
|
|
|
17439
17751
|
// src/lib/files-integration.ts
|
|
17440
|
-
import { join as
|
|
17441
|
-
import { mkdirSync as
|
|
17752
|
+
import { join as join10 } from "path";
|
|
17753
|
+
import { mkdirSync as mkdirSync8, copyFileSync as copyFileSync2 } from "fs";
|
|
17442
17754
|
import { homedir as homedir10 } from "os";
|
|
17443
17755
|
async function persistFile(localPath, opts) {
|
|
17444
17756
|
try {
|
|
@@ -17448,12 +17760,12 @@ async function persistFile(localPath, opts) {
|
|
|
17448
17760
|
return { id: ref.id, path: ref.path ?? localPath, permanent: true, provider: "open-files" };
|
|
17449
17761
|
}
|
|
17450
17762
|
} catch {}
|
|
17451
|
-
const dataDir = process.env["BROWSER_DATA_DIR"] ??
|
|
17763
|
+
const dataDir = process.env["BROWSER_DATA_DIR"] ?? join10(homedir10(), ".browser");
|
|
17452
17764
|
const date = new Date().toISOString().split("T")[0];
|
|
17453
|
-
const dir =
|
|
17454
|
-
|
|
17765
|
+
const dir = join10(dataDir, "persistent", date);
|
|
17766
|
+
mkdirSync8(dir, { recursive: true });
|
|
17455
17767
|
const filename = localPath.split("/").pop() ?? "file";
|
|
17456
|
-
const targetPath =
|
|
17768
|
+
const targetPath = join10(dir, filename);
|
|
17457
17769
|
copyFileSync2(localPath, targetPath);
|
|
17458
17770
|
return {
|
|
17459
17771
|
id: `local-${Date.now()}`,
|
|
@@ -17930,9 +18242,9 @@ async function tryReplayAuth(page, domain) {
|
|
|
17930
18242
|
return { replayed: false };
|
|
17931
18243
|
if (flow.storage_state_path) {
|
|
17932
18244
|
try {
|
|
17933
|
-
const { existsSync:
|
|
17934
|
-
if (
|
|
17935
|
-
const state = JSON.parse(
|
|
18245
|
+
const { existsSync: existsSync7, readFileSync: readFileSync5 } = await import("fs");
|
|
18246
|
+
if (existsSync7(flow.storage_state_path)) {
|
|
18247
|
+
const state = JSON.parse(readFileSync5(flow.storage_state_path, "utf8"));
|
|
17936
18248
|
if (state.cookies?.length) {
|
|
17937
18249
|
await page.context().addCookies(state.cookies);
|
|
17938
18250
|
await page.reload();
|
|
@@ -17981,82 +18293,6 @@ var init_auth_flow = __esm(() => {
|
|
|
17981
18293
|
];
|
|
17982
18294
|
});
|
|
17983
18295
|
|
|
17984
|
-
// src/lib/vision-fallback.ts
|
|
17985
|
-
var exports_vision_fallback = {};
|
|
17986
|
-
__export(exports_vision_fallback, {
|
|
17987
|
-
findElementByVision: () => findElementByVision,
|
|
17988
|
-
clickByVision: () => clickByVision
|
|
17989
|
-
});
|
|
17990
|
-
async function findElementByVision(page, description, opts) {
|
|
17991
|
-
const model = opts?.model ?? process.env["BROWSER_VISION_MODEL"] ?? DEFAULT_MODEL;
|
|
17992
|
-
const screenshot = await page.screenshot({ type: "jpeg", quality: 80 });
|
|
17993
|
-
const base64 = screenshot.toString("base64");
|
|
17994
|
-
const viewport = page.viewportSize() ?? { width: 1280, height: 720 };
|
|
17995
|
-
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
17996
|
-
if (!apiKey) {
|
|
17997
|
-
return { found: false, x: 0, y: 0, confidence: "none", description, model, error: "ANTHROPIC_API_KEY not set" };
|
|
17998
|
-
}
|
|
17999
|
-
try {
|
|
18000
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
18001
|
-
method: "POST",
|
|
18002
|
-
headers: {
|
|
18003
|
-
"content-type": "application/json",
|
|
18004
|
-
"x-api-key": apiKey,
|
|
18005
|
-
"anthropic-version": "2023-06-01"
|
|
18006
|
-
},
|
|
18007
|
-
body: JSON.stringify({
|
|
18008
|
-
model,
|
|
18009
|
-
max_tokens: 256,
|
|
18010
|
-
messages: [{
|
|
18011
|
-
role: "user",
|
|
18012
|
-
content: [
|
|
18013
|
-
{
|
|
18014
|
-
type: "image",
|
|
18015
|
-
source: { type: "base64", media_type: "image/jpeg", data: base64 }
|
|
18016
|
-
},
|
|
18017
|
-
{
|
|
18018
|
-
type: "text",
|
|
18019
|
-
text: `Find the element matching this description: "${description}"
|
|
18020
|
-
|
|
18021
|
-
The screenshot is ${viewport.width}x${viewport.height} pixels.
|
|
18022
|
-
|
|
18023
|
-
Reply with ONLY a JSON object (no markdown, no explanation):
|
|
18024
|
-
{"found": true, "x": <pixel_x>, "y": <pixel_y>, "confidence": "high|medium|low", "description": "<what you found>"}
|
|
18025
|
-
|
|
18026
|
-
If you cannot find the element:
|
|
18027
|
-
{"found": false, "x": 0, "y": 0, "confidence": "none", "description": "not found"}`
|
|
18028
|
-
}
|
|
18029
|
-
]
|
|
18030
|
-
}]
|
|
18031
|
-
})
|
|
18032
|
-
});
|
|
18033
|
-
const data = await response.json();
|
|
18034
|
-
const text = data.content?.[0]?.text ?? "";
|
|
18035
|
-
const jsonStr = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
18036
|
-
const result = JSON.parse(jsonStr);
|
|
18037
|
-
result.model = model;
|
|
18038
|
-
return result;
|
|
18039
|
-
} catch (err) {
|
|
18040
|
-
return {
|
|
18041
|
-
found: false,
|
|
18042
|
-
x: 0,
|
|
18043
|
-
y: 0,
|
|
18044
|
-
confidence: "none",
|
|
18045
|
-
description,
|
|
18046
|
-
model,
|
|
18047
|
-
error: err instanceof Error ? err.message : String(err)
|
|
18048
|
-
};
|
|
18049
|
-
}
|
|
18050
|
-
}
|
|
18051
|
-
async function clickByVision(page, description, opts) {
|
|
18052
|
-
const result = await findElementByVision(page, description, opts);
|
|
18053
|
-
if (result.found && result.x > 0 && result.y > 0) {
|
|
18054
|
-
await page.mouse.click(result.x, result.y);
|
|
18055
|
-
}
|
|
18056
|
-
return result;
|
|
18057
|
-
}
|
|
18058
|
-
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
18059
|
-
|
|
18060
18296
|
// src/lib/datasets.ts
|
|
18061
18297
|
var exports_datasets = {};
|
|
18062
18298
|
__export(exports_datasets, {
|
|
@@ -18068,8 +18304,8 @@ __export(exports_datasets, {
|
|
|
18068
18304
|
deleteDataset: () => deleteDataset
|
|
18069
18305
|
});
|
|
18070
18306
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
18071
|
-
import { writeFileSync as
|
|
18072
|
-
import { join as
|
|
18307
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
|
|
18308
|
+
import { join as join11 } from "path";
|
|
18073
18309
|
import { homedir as homedir11 } from "os";
|
|
18074
18310
|
function saveDataset(data) {
|
|
18075
18311
|
const db = getDatabase();
|
|
@@ -18108,14 +18344,14 @@ function exportDataset(name, format) {
|
|
|
18108
18344
|
const dataset = getDatasetByName(name);
|
|
18109
18345
|
if (!dataset)
|
|
18110
18346
|
throw new Error(`Dataset '${name}' not found`);
|
|
18111
|
-
const dir =
|
|
18112
|
-
|
|
18347
|
+
const dir = join11(process.env["BROWSER_DATA_DIR"] ?? join11(homedir11(), ".browser"), "exports");
|
|
18348
|
+
mkdirSync9(dir, { recursive: true });
|
|
18113
18349
|
const filename = `${name}.${format}`;
|
|
18114
|
-
const path =
|
|
18350
|
+
const path = join11(dir, filename);
|
|
18115
18351
|
if (format === "csv") {
|
|
18116
18352
|
const rows = dataset.data;
|
|
18117
18353
|
if (rows.length === 0) {
|
|
18118
|
-
|
|
18354
|
+
writeFileSync3(path, "");
|
|
18119
18355
|
return { path, size: 0 };
|
|
18120
18356
|
}
|
|
18121
18357
|
const headers = Object.keys(rows[0]);
|
|
@@ -18128,11 +18364,11 @@ function exportDataset(name, format) {
|
|
|
18128
18364
|
}
|
|
18129
18365
|
const content = csvLines.join(`
|
|
18130
18366
|
`);
|
|
18131
|
-
|
|
18367
|
+
writeFileSync3(path, content);
|
|
18132
18368
|
return { path, size: content.length };
|
|
18133
18369
|
} else {
|
|
18134
18370
|
const content = JSON.stringify(dataset.data, null, 2);
|
|
18135
|
-
|
|
18371
|
+
writeFileSync3(path, content);
|
|
18136
18372
|
return { path, size: content.length };
|
|
18137
18373
|
}
|
|
18138
18374
|
}
|
|
@@ -18250,11 +18486,11 @@ __export(exports_dist, {
|
|
|
18250
18486
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
18251
18487
|
});
|
|
18252
18488
|
import { Database as Database2 } from "bun:sqlite";
|
|
18253
|
-
import { existsSync as
|
|
18254
|
-
import { dirname, join as
|
|
18255
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as
|
|
18489
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync10 } from "fs";
|
|
18490
|
+
import { dirname, join as join12, resolve as resolve2 } from "path";
|
|
18491
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync5, readdirSync as readdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
18256
18492
|
import { homedir as homedir12 } from "os";
|
|
18257
|
-
import { basename as basename2, dirname as dirname2, join as join22, resolve as
|
|
18493
|
+
import { basename as basename2, dirname as dirname2, join as join22, resolve as resolve22 } from "path";
|
|
18258
18494
|
import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
18259
18495
|
import { homedir as homedir22 } from "os";
|
|
18260
18496
|
import { join as join32 } from "path";
|
|
@@ -18265,10 +18501,10 @@ function isInMemoryDb(path) {
|
|
|
18265
18501
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
18266
18502
|
}
|
|
18267
18503
|
function findNearestMementosDb(startDir) {
|
|
18268
|
-
let dir =
|
|
18504
|
+
let dir = resolve2(startDir);
|
|
18269
18505
|
while (true) {
|
|
18270
|
-
const candidate =
|
|
18271
|
-
if (
|
|
18506
|
+
const candidate = join12(dir, ".mementos", "mementos.db");
|
|
18507
|
+
if (existsSync7(candidate))
|
|
18272
18508
|
return candidate;
|
|
18273
18509
|
const parent = dirname(dir);
|
|
18274
18510
|
if (parent === dir)
|
|
@@ -18278,9 +18514,9 @@ function findNearestMementosDb(startDir) {
|
|
|
18278
18514
|
return null;
|
|
18279
18515
|
}
|
|
18280
18516
|
function findGitRoot(startDir) {
|
|
18281
|
-
let dir =
|
|
18517
|
+
let dir = resolve2(startDir);
|
|
18282
18518
|
while (true) {
|
|
18283
|
-
if (
|
|
18519
|
+
if (existsSync7(join12(dir, ".git")))
|
|
18284
18520
|
return dir;
|
|
18285
18521
|
const parent = dirname(dir);
|
|
18286
18522
|
if (parent === dir)
|
|
@@ -18300,18 +18536,18 @@ function getDbPath() {
|
|
|
18300
18536
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
18301
18537
|
const gitRoot = findGitRoot(cwd);
|
|
18302
18538
|
if (gitRoot) {
|
|
18303
|
-
return
|
|
18539
|
+
return join12(gitRoot, ".mementos", "mementos.db");
|
|
18304
18540
|
}
|
|
18305
18541
|
}
|
|
18306
18542
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
18307
|
-
return
|
|
18543
|
+
return join12(home, ".mementos", "mementos.db");
|
|
18308
18544
|
}
|
|
18309
18545
|
function ensureDir2(filePath) {
|
|
18310
18546
|
if (isInMemoryDb(filePath))
|
|
18311
18547
|
return;
|
|
18312
|
-
const dir = dirname(
|
|
18313
|
-
if (!
|
|
18314
|
-
|
|
18548
|
+
const dir = dirname(resolve2(filePath));
|
|
18549
|
+
if (!existsSync7(dir)) {
|
|
18550
|
+
mkdirSync10(dir, { recursive: true });
|
|
18315
18551
|
}
|
|
18316
18552
|
}
|
|
18317
18553
|
function getDatabase2(dbPath) {
|
|
@@ -20165,7 +20401,7 @@ function loadConfig() {
|
|
|
20165
20401
|
let fileConfig = {};
|
|
20166
20402
|
if (existsSync22(configPath)) {
|
|
20167
20403
|
try {
|
|
20168
|
-
const raw =
|
|
20404
|
+
const raw = readFileSync5(configPath, "utf-8");
|
|
20169
20405
|
fileConfig = JSON.parse(raw);
|
|
20170
20406
|
} catch {}
|
|
20171
20407
|
}
|
|
@@ -20198,7 +20434,7 @@ function readGlobalConfig() {
|
|
|
20198
20434
|
if (!existsSync22(p))
|
|
20199
20435
|
return {};
|
|
20200
20436
|
try {
|
|
20201
|
-
return JSON.parse(
|
|
20437
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
20202
20438
|
} catch {
|
|
20203
20439
|
return {};
|
|
20204
20440
|
}
|
|
@@ -20206,7 +20442,7 @@ function readGlobalConfig() {
|
|
|
20206
20442
|
function writeGlobalConfig(data) {
|
|
20207
20443
|
const p = globalConfigPath();
|
|
20208
20444
|
ensureDir22(dirname2(p));
|
|
20209
|
-
|
|
20445
|
+
writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
|
|
20210
20446
|
}
|
|
20211
20447
|
function getActiveProfile() {
|
|
20212
20448
|
const envProfile = process.env["MEMENTOS_PROFILE"];
|
|
@@ -20228,7 +20464,7 @@ function listProfiles2() {
|
|
|
20228
20464
|
const dir = profilesDir();
|
|
20229
20465
|
if (!existsSync22(dir))
|
|
20230
20466
|
return [];
|
|
20231
|
-
return
|
|
20467
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".db")).map((f) => basename2(f, ".db")).sort();
|
|
20232
20468
|
}
|
|
20233
20469
|
function deleteProfile2(name) {
|
|
20234
20470
|
const dbPath = join22(profilesDir(), `${name}.db`);
|
|
@@ -22871,30 +23107,30 @@ __export(exports_dist2, {
|
|
|
22871
23107
|
acquireLock: () => acquireLock2
|
|
22872
23108
|
});
|
|
22873
23109
|
import { Database as Database3 } from "bun:sqlite";
|
|
22874
|
-
import { mkdirSync as
|
|
22875
|
-
import { join as
|
|
23110
|
+
import { mkdirSync as mkdirSync11 } from "fs";
|
|
23111
|
+
import { join as join13, dirname as dirname3 } from "path";
|
|
22876
23112
|
import { homedir as homedir13 } from "os";
|
|
22877
23113
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
22878
23114
|
import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
22879
23115
|
import { join as join33 } from "path";
|
|
22880
23116
|
import { homedir as homedir33 } from "os";
|
|
22881
|
-
import { readFileSync as
|
|
23117
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
22882
23118
|
import { join as join23 } from "path";
|
|
22883
23119
|
import { homedir as homedir23 } from "os";
|
|
22884
23120
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
22885
|
-
import { readFileSync as readFileSync23, writeFileSync as
|
|
23121
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync33 } from "fs";
|
|
22886
23122
|
import { join as join43, dirname as dirname22 } from "path";
|
|
22887
23123
|
import { homedir as homedir42 } from "os";
|
|
22888
23124
|
function getDbPath2() {
|
|
22889
23125
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
22890
23126
|
return process.env.CONVERSATIONS_DB_PATH;
|
|
22891
|
-
return
|
|
23127
|
+
return join13(homedir13(), ".conversations", "messages.db");
|
|
22892
23128
|
}
|
|
22893
23129
|
function getDb() {
|
|
22894
23130
|
if (db)
|
|
22895
23131
|
return db;
|
|
22896
23132
|
const dbPath = getDbPath2();
|
|
22897
|
-
|
|
23133
|
+
mkdirSync11(dirname3(dbPath), { recursive: true });
|
|
22898
23134
|
db = new Database3(dbPath, { create: true });
|
|
22899
23135
|
db.exec("PRAGMA journal_mode = WAL");
|
|
22900
23136
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -23136,7 +23372,7 @@ function loadConfig2() {
|
|
|
23136
23372
|
if (cachedConfig && now2 - configLoadedAt < CONFIG_CACHE_MS)
|
|
23137
23373
|
return cachedConfig;
|
|
23138
23374
|
try {
|
|
23139
|
-
const raw =
|
|
23375
|
+
const raw = readFileSync6(getConfigPath(), "utf-8");
|
|
23140
23376
|
cachedConfig = JSON.parse(raw);
|
|
23141
23377
|
configLoadedAt = now2;
|
|
23142
23378
|
return cachedConfig;
|
|
@@ -24053,7 +24289,7 @@ function useSpaceMessages(spaceName) {
|
|
|
24053
24289
|
}
|
|
24054
24290
|
function isNameTaken(name) {
|
|
24055
24291
|
try {
|
|
24056
|
-
const { getDb: getDb2 } = (init_db(),
|
|
24292
|
+
const { getDb: getDb2 } = (init_db(), __toCommonJS2(exports_db));
|
|
24057
24293
|
const db2 = getDb2();
|
|
24058
24294
|
const row = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(name);
|
|
24059
24295
|
return !!row;
|
|
@@ -24082,7 +24318,7 @@ function getAutoName() {
|
|
|
24082
24318
|
cachedAutoName = name;
|
|
24083
24319
|
try {
|
|
24084
24320
|
mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
|
|
24085
|
-
|
|
24321
|
+
writeFileSync5(AGENT_ID_FILE, name + `
|
|
24086
24322
|
`, "utf-8");
|
|
24087
24323
|
} catch {}
|
|
24088
24324
|
return name;
|
|
@@ -24688,7 +24924,7 @@ function getGraphStats() {
|
|
|
24688
24924
|
map[r.relation] = r.c;
|
|
24689
24925
|
return { total_edges: total, by_relation: map };
|
|
24690
24926
|
}
|
|
24691
|
-
var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2,
|
|
24927
|
+
var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc2, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
|
|
24692
24928
|
target = mod != null ? __create2(__getProtoOf2(mod)) : {};
|
|
24693
24929
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", { value: mod, enumerable: true }) : target;
|
|
24694
24930
|
for (let key of __getOwnPropNames2(mod))
|
|
@@ -24698,17 +24934,17 @@ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc,
|
|
|
24698
24934
|
enumerable: true
|
|
24699
24935
|
});
|
|
24700
24936
|
return to;
|
|
24701
|
-
},
|
|
24702
|
-
var entry =
|
|
24937
|
+
}, __moduleCache2, __toCommonJS2 = (from) => {
|
|
24938
|
+
var entry = __moduleCache2.get(from), desc;
|
|
24703
24939
|
if (entry)
|
|
24704
24940
|
return entry;
|
|
24705
24941
|
entry = __defProp3({}, "__esModule", { value: true });
|
|
24706
24942
|
if (from && typeof from === "object" || typeof from === "function")
|
|
24707
24943
|
__getOwnPropNames2(from).map((key) => !__hasOwnProp2.call(entry, key) && __defProp3(entry, key, {
|
|
24708
24944
|
get: () => from[key],
|
|
24709
|
-
enumerable: !(desc =
|
|
24945
|
+
enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
|
|
24710
24946
|
}));
|
|
24711
|
-
|
|
24947
|
+
__moduleCache2.set(from, entry);
|
|
24712
24948
|
return entry;
|
|
24713
24949
|
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __export3 = (target, all) => {
|
|
24714
24950
|
for (var name in all)
|
|
@@ -24724,9 +24960,9 @@ var init_dist2 = __esm(() => {
|
|
|
24724
24960
|
__getProtoOf2 = Object.getPrototypeOf;
|
|
24725
24961
|
__defProp3 = Object.defineProperty;
|
|
24726
24962
|
__getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
24727
|
-
|
|
24963
|
+
__getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
24728
24964
|
__hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
24729
|
-
|
|
24965
|
+
__moduleCache2 = /* @__PURE__ */ new WeakMap;
|
|
24730
24966
|
exports_db = {};
|
|
24731
24967
|
__export3(exports_db, {
|
|
24732
24968
|
getDbPath: () => getDbPath2,
|
|
@@ -27296,18 +27532,18 @@ __export(exports_dist3, {
|
|
|
27296
27532
|
AgentNotFoundError: () => AgentNotFoundError2
|
|
27297
27533
|
});
|
|
27298
27534
|
import { Database as Database4 } from "bun:sqlite";
|
|
27299
|
-
import { existsSync as
|
|
27300
|
-
import { dirname as dirname5, join as
|
|
27535
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync12 } from "fs";
|
|
27536
|
+
import { dirname as dirname5, join as join14, resolve as resolve3 } from "path";
|
|
27301
27537
|
import { existsSync as existsSync33 } from "fs";
|
|
27302
27538
|
import { join as join34 } from "path";
|
|
27303
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as
|
|
27539
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
27304
27540
|
import { join as join24 } from "path";
|
|
27305
27541
|
import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
27306
27542
|
import { join as join44 } from "path";
|
|
27307
27543
|
import { existsSync as existsSync52 } from "fs";
|
|
27308
27544
|
import { join as join52 } from "path";
|
|
27309
27545
|
import { readFileSync as readFileSync33, statSync as statSync22 } from "fs";
|
|
27310
|
-
import { relative, resolve as
|
|
27546
|
+
import { relative, resolve as resolve23, join as join62 } from "path";
|
|
27311
27547
|
import { execSync as execSync2 } from "child_process";
|
|
27312
27548
|
|
|
27313
27549
|
class TodosClient {
|
|
@@ -27530,8 +27766,8 @@ function isInMemoryDb2(path) {
|
|
|
27530
27766
|
function findNearestTodosDb(startDir) {
|
|
27531
27767
|
let dir = resolve3(startDir);
|
|
27532
27768
|
while (true) {
|
|
27533
|
-
const candidate =
|
|
27534
|
-
if (
|
|
27769
|
+
const candidate = join14(dir, ".todos", "todos.db");
|
|
27770
|
+
if (existsSync8(candidate))
|
|
27535
27771
|
return candidate;
|
|
27536
27772
|
const parent = dirname5(dir);
|
|
27537
27773
|
if (parent === dir)
|
|
@@ -27543,7 +27779,7 @@ function findNearestTodosDb(startDir) {
|
|
|
27543
27779
|
function findGitRoot2(startDir) {
|
|
27544
27780
|
let dir = resolve3(startDir);
|
|
27545
27781
|
while (true) {
|
|
27546
|
-
if (
|
|
27782
|
+
if (existsSync8(join14(dir, ".git")))
|
|
27547
27783
|
return dir;
|
|
27548
27784
|
const parent = dirname5(dir);
|
|
27549
27785
|
if (parent === dir)
|
|
@@ -27563,18 +27799,18 @@ function getDbPath3() {
|
|
|
27563
27799
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
27564
27800
|
const gitRoot = findGitRoot2(cwd);
|
|
27565
27801
|
if (gitRoot) {
|
|
27566
|
-
return
|
|
27802
|
+
return join14(gitRoot, ".todos", "todos.db");
|
|
27567
27803
|
}
|
|
27568
27804
|
}
|
|
27569
27805
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
27570
|
-
return
|
|
27806
|
+
return join14(home, ".todos", "todos.db");
|
|
27571
27807
|
}
|
|
27572
27808
|
function ensureDir3(filePath) {
|
|
27573
27809
|
if (isInMemoryDb2(filePath))
|
|
27574
27810
|
return;
|
|
27575
27811
|
const dir = dirname5(resolve3(filePath));
|
|
27576
|
-
if (!
|
|
27577
|
-
|
|
27812
|
+
if (!existsSync8(dir)) {
|
|
27813
|
+
mkdirSync12(dir, { recursive: true });
|
|
27578
27814
|
}
|
|
27579
27815
|
}
|
|
27580
27816
|
function getDatabase3(dbPath) {
|
|
@@ -28040,28 +28276,28 @@ function ensureDir23(dir) {
|
|
|
28040
28276
|
function listJsonFiles(dir) {
|
|
28041
28277
|
if (!existsSync23(dir))
|
|
28042
28278
|
return [];
|
|
28043
|
-
return
|
|
28279
|
+
return readdirSync5(dir).filter((f) => f.endsWith(".json"));
|
|
28044
28280
|
}
|
|
28045
28281
|
function readJsonFile(path) {
|
|
28046
28282
|
try {
|
|
28047
|
-
return JSON.parse(
|
|
28283
|
+
return JSON.parse(readFileSync7(path, "utf-8"));
|
|
28048
28284
|
} catch {
|
|
28049
28285
|
return null;
|
|
28050
28286
|
}
|
|
28051
28287
|
}
|
|
28052
28288
|
function writeJsonFile(path, data) {
|
|
28053
|
-
|
|
28289
|
+
writeFileSync6(path, JSON.stringify(data, null, 2) + `
|
|
28054
28290
|
`);
|
|
28055
28291
|
}
|
|
28056
28292
|
function readHighWaterMark(dir) {
|
|
28057
28293
|
const path = join24(dir, ".highwatermark");
|
|
28058
28294
|
if (!existsSync23(path))
|
|
28059
28295
|
return 1;
|
|
28060
|
-
const val = parseInt(
|
|
28296
|
+
const val = parseInt(readFileSync7(path, "utf-8").trim(), 10);
|
|
28061
28297
|
return isNaN(val) ? 1 : val;
|
|
28062
28298
|
}
|
|
28063
28299
|
function writeHighWaterMark(dir, value) {
|
|
28064
|
-
|
|
28300
|
+
writeFileSync6(join24(dir, ".highwatermark"), String(value));
|
|
28065
28301
|
}
|
|
28066
28302
|
function getFileMtimeMs(path) {
|
|
28067
28303
|
try {
|
|
@@ -31423,7 +31659,7 @@ function collectFiles(basePath, extensions) {
|
|
|
31423
31659
|
return files.sort();
|
|
31424
31660
|
}
|
|
31425
31661
|
function extractTodos(options, db2) {
|
|
31426
|
-
const basePath =
|
|
31662
|
+
const basePath = resolve23(options.path);
|
|
31427
31663
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
31428
31664
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
31429
31665
|
const files = collectFiles(basePath, extensions);
|
|
@@ -31432,7 +31668,7 @@ function extractTodos(options, db2) {
|
|
|
31432
31668
|
const fullPath = statSync22(basePath).isFile() ? basePath : join62(basePath, file);
|
|
31433
31669
|
try {
|
|
31434
31670
|
const source = readFileSync33(fullPath, "utf-8");
|
|
31435
|
-
const relPath = statSync22(basePath).isFile() ? relative(
|
|
31671
|
+
const relPath = statSync22(basePath).isFile() ? relative(resolve23(basePath, ".."), fullPath) : file;
|
|
31436
31672
|
const comments = extractFromSource(source, relPath, tags);
|
|
31437
31673
|
allComments.push(...comments);
|
|
31438
31674
|
} catch {}
|
|
@@ -32651,8 +32887,8 @@ __export(exports_dist4, {
|
|
|
32651
32887
|
CATEGORIES: () => CATEGORIES,
|
|
32652
32888
|
AGENT_TARGETS: () => AGENT_TARGETS
|
|
32653
32889
|
});
|
|
32654
|
-
import { existsSync as
|
|
32655
|
-
import { join as
|
|
32890
|
+
import { existsSync as existsSync9, cpSync, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, rmSync as rmSync2, readdirSync as readdirSync6, statSync as statSync4, readFileSync as readFileSync8, accessSync, constants } from "fs";
|
|
32891
|
+
import { join as join15, dirname as dirname6 } from "path";
|
|
32656
32892
|
import { homedir as homedir14 } from "os";
|
|
32657
32893
|
import { fileURLToPath } from "url";
|
|
32658
32894
|
import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
|
|
@@ -32764,35 +33000,35 @@ function normalizeSkillName(name) {
|
|
|
32764
33000
|
function findSkillsDir() {
|
|
32765
33001
|
let dir = __dirname2;
|
|
32766
33002
|
for (let i = 0;i < 5; i++) {
|
|
32767
|
-
const candidate =
|
|
32768
|
-
if (
|
|
33003
|
+
const candidate = join15(dir, "skills");
|
|
33004
|
+
if (existsSync9(candidate)) {
|
|
32769
33005
|
return candidate;
|
|
32770
33006
|
}
|
|
32771
33007
|
dir = dirname6(dir);
|
|
32772
33008
|
}
|
|
32773
|
-
return
|
|
33009
|
+
return join15(__dirname2, "..", "skills");
|
|
32774
33010
|
}
|
|
32775
33011
|
function getSkillPath(name) {
|
|
32776
33012
|
const skillName = normalizeSkillName(name);
|
|
32777
|
-
return
|
|
33013
|
+
return join15(SKILLS_DIR, skillName);
|
|
32778
33014
|
}
|
|
32779
33015
|
function skillExists(name) {
|
|
32780
|
-
return
|
|
33016
|
+
return existsSync9(getSkillPath(name));
|
|
32781
33017
|
}
|
|
32782
33018
|
function installSkill(name, options = {}) {
|
|
32783
33019
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
32784
33020
|
const skillName = normalizeSkillName(name);
|
|
32785
33021
|
const sourcePath = getSkillPath(name);
|
|
32786
|
-
const destDir =
|
|
32787
|
-
const destPath =
|
|
32788
|
-
if (!
|
|
33022
|
+
const destDir = join15(targetDir, ".skills");
|
|
33023
|
+
const destPath = join15(destDir, skillName);
|
|
33024
|
+
if (!existsSync9(sourcePath)) {
|
|
32789
33025
|
return {
|
|
32790
33026
|
skill: name,
|
|
32791
33027
|
success: false,
|
|
32792
33028
|
error: `Skill '${name}' not found`
|
|
32793
33029
|
};
|
|
32794
33030
|
}
|
|
32795
|
-
if (
|
|
33031
|
+
if (existsSync9(destPath) && !overwrite) {
|
|
32796
33032
|
return {
|
|
32797
33033
|
skill: name,
|
|
32798
33034
|
success: false,
|
|
@@ -32801,10 +33037,10 @@ function installSkill(name, options = {}) {
|
|
|
32801
33037
|
};
|
|
32802
33038
|
}
|
|
32803
33039
|
try {
|
|
32804
|
-
if (!
|
|
32805
|
-
|
|
33040
|
+
if (!existsSync9(destDir)) {
|
|
33041
|
+
mkdirSync13(destDir, { recursive: true });
|
|
32806
33042
|
}
|
|
32807
|
-
if (
|
|
33043
|
+
if (existsSync9(destPath) && overwrite) {
|
|
32808
33044
|
rmSync2(destPath, { recursive: true, force: true });
|
|
32809
33045
|
}
|
|
32810
33046
|
cpSync(sourcePath, destPath, {
|
|
@@ -32843,10 +33079,10 @@ function installSkills(names, options = {}) {
|
|
|
32843
33079
|
return names.map((name) => installSkill(name, options));
|
|
32844
33080
|
}
|
|
32845
33081
|
function updateSkillsIndex(skillsDir) {
|
|
32846
|
-
const indexPath =
|
|
33082
|
+
const indexPath = join15(skillsDir, "index.ts");
|
|
32847
33083
|
const meta = loadMeta(skillsDir);
|
|
32848
33084
|
const disabledSet = new Set(meta.disabled || []);
|
|
32849
|
-
const skills =
|
|
33085
|
+
const skills = readdirSync6(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
|
|
32850
33086
|
const exports = skills.map((s) => {
|
|
32851
33087
|
const name = s.replace("skill-", "").replace(/-/g, "_");
|
|
32852
33088
|
return `export * as ${name} from './${s}/src/index.js';`;
|
|
@@ -32859,31 +33095,31 @@ function updateSkillsIndex(skillsDir) {
|
|
|
32859
33095
|
|
|
32860
33096
|
${exports}
|
|
32861
33097
|
`;
|
|
32862
|
-
|
|
33098
|
+
writeFileSync7(indexPath, content);
|
|
32863
33099
|
}
|
|
32864
33100
|
function getMetaPath(skillsDir) {
|
|
32865
|
-
return
|
|
33101
|
+
return join15(skillsDir, ".meta.json");
|
|
32866
33102
|
}
|
|
32867
33103
|
function loadMeta(skillsDir) {
|
|
32868
33104
|
const metaPath2 = getMetaPath(skillsDir);
|
|
32869
|
-
if (
|
|
33105
|
+
if (existsSync9(metaPath2)) {
|
|
32870
33106
|
try {
|
|
32871
|
-
return JSON.parse(
|
|
33107
|
+
return JSON.parse(readFileSync8(metaPath2, "utf-8"));
|
|
32872
33108
|
} catch {}
|
|
32873
33109
|
}
|
|
32874
33110
|
return { skills: {} };
|
|
32875
33111
|
}
|
|
32876
33112
|
function saveMeta(skillsDir, meta) {
|
|
32877
|
-
|
|
33113
|
+
writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
|
|
32878
33114
|
}
|
|
32879
33115
|
function recordInstall(skillsDir, name) {
|
|
32880
33116
|
const meta = loadMeta(skillsDir);
|
|
32881
33117
|
const skillName = normalizeSkillName(name);
|
|
32882
33118
|
let version = "unknown";
|
|
32883
33119
|
try {
|
|
32884
|
-
const pkgPath =
|
|
32885
|
-
if (
|
|
32886
|
-
const pkg = JSON.parse(
|
|
33120
|
+
const pkgPath = join15(skillsDir, skillName, "package.json");
|
|
33121
|
+
if (existsSync9(pkgPath)) {
|
|
33122
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
32887
33123
|
version = pkg.version || "unknown";
|
|
32888
33124
|
}
|
|
32889
33125
|
} catch {}
|
|
@@ -32896,12 +33132,12 @@ function recordRemove(skillsDir, name) {
|
|
|
32896
33132
|
saveMeta(skillsDir, meta);
|
|
32897
33133
|
}
|
|
32898
33134
|
function getInstallMeta(targetDir = process.cwd()) {
|
|
32899
|
-
return loadMeta(
|
|
33135
|
+
return loadMeta(join15(targetDir, ".skills"));
|
|
32900
33136
|
}
|
|
32901
33137
|
function disableSkill(name, targetDir = process.cwd()) {
|
|
32902
|
-
const skillsDir =
|
|
33138
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
32903
33139
|
const skillName = normalizeSkillName(name);
|
|
32904
|
-
if (!
|
|
33140
|
+
if (!existsSync9(join15(skillsDir, skillName)))
|
|
32905
33141
|
return false;
|
|
32906
33142
|
const meta = loadMeta(skillsDir);
|
|
32907
33143
|
const disabled = new Set(meta.disabled || []);
|
|
@@ -32914,7 +33150,7 @@ function disableSkill(name, targetDir = process.cwd()) {
|
|
|
32914
33150
|
return true;
|
|
32915
33151
|
}
|
|
32916
33152
|
function enableSkill(name, targetDir = process.cwd()) {
|
|
32917
|
-
const skillsDir =
|
|
33153
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
32918
33154
|
const meta = loadMeta(skillsDir);
|
|
32919
33155
|
const disabled = new Set(meta.disabled || []);
|
|
32920
33156
|
if (!disabled.has(name))
|
|
@@ -32926,24 +33162,24 @@ function enableSkill(name, targetDir = process.cwd()) {
|
|
|
32926
33162
|
return true;
|
|
32927
33163
|
}
|
|
32928
33164
|
function getDisabledSkills(targetDir = process.cwd()) {
|
|
32929
|
-
const meta = loadMeta(
|
|
33165
|
+
const meta = loadMeta(join15(targetDir, ".skills"));
|
|
32930
33166
|
return meta.disabled || [];
|
|
32931
33167
|
}
|
|
32932
33168
|
function getInstalledSkills(targetDir = process.cwd()) {
|
|
32933
|
-
const skillsDir =
|
|
32934
|
-
if (!
|
|
33169
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
33170
|
+
if (!existsSync9(skillsDir)) {
|
|
32935
33171
|
return [];
|
|
32936
33172
|
}
|
|
32937
|
-
return
|
|
32938
|
-
const fullPath =
|
|
33173
|
+
return readdirSync6(skillsDir).filter((f) => {
|
|
33174
|
+
const fullPath = join15(skillsDir, f);
|
|
32939
33175
|
return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
|
|
32940
33176
|
}).map((f) => f.replace("skill-", ""));
|
|
32941
33177
|
}
|
|
32942
33178
|
function removeSkill(name, targetDir = process.cwd()) {
|
|
32943
33179
|
const skillName = normalizeSkillName(name);
|
|
32944
|
-
const skillsDir =
|
|
32945
|
-
const skillPath =
|
|
32946
|
-
if (!
|
|
33180
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
33181
|
+
const skillPath = join15(skillsDir, skillName);
|
|
33182
|
+
if (!existsSync9(skillPath)) {
|
|
32947
33183
|
return false;
|
|
32948
33184
|
}
|
|
32949
33185
|
rmSync2(skillPath, { recursive: true, force: true });
|
|
@@ -32954,25 +33190,25 @@ function removeSkill(name, targetDir = process.cwd()) {
|
|
|
32954
33190
|
function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
32955
33191
|
const agentDir = `.${agent}`;
|
|
32956
33192
|
if (scope === "project") {
|
|
32957
|
-
return
|
|
33193
|
+
return join15(projectDir || process.cwd(), agentDir, "skills");
|
|
32958
33194
|
}
|
|
32959
|
-
return
|
|
33195
|
+
return join15(homedir14(), agentDir, "skills");
|
|
32960
33196
|
}
|
|
32961
33197
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
32962
33198
|
const skillName = normalizeSkillName(name);
|
|
32963
|
-
return
|
|
33199
|
+
return join15(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
32964
33200
|
}
|
|
32965
33201
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
32966
33202
|
const { agent, scope = "global", projectDir } = options;
|
|
32967
33203
|
const skillName = normalizeSkillName(name);
|
|
32968
33204
|
const sourcePath = getSkillPath(name);
|
|
32969
|
-
if (!
|
|
33205
|
+
if (!existsSync9(sourcePath)) {
|
|
32970
33206
|
return { skill: name, success: false, error: `Skill '${name}' not found` };
|
|
32971
33207
|
}
|
|
32972
33208
|
let skillMdContent = null;
|
|
32973
|
-
const skillMdPath =
|
|
32974
|
-
if (
|
|
32975
|
-
skillMdContent =
|
|
33209
|
+
const skillMdPath = join15(sourcePath, "SKILL.md");
|
|
33210
|
+
if (existsSync9(skillMdPath)) {
|
|
33211
|
+
skillMdContent = readFileSync8(skillMdPath, "utf-8");
|
|
32976
33212
|
} else if (generateSkillMd) {
|
|
32977
33213
|
skillMdContent = generateSkillMd(name);
|
|
32978
33214
|
}
|
|
@@ -32981,8 +33217,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
32981
33217
|
}
|
|
32982
33218
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
32983
33219
|
if (scope === "global") {
|
|
32984
|
-
const agentBaseDir2 =
|
|
32985
|
-
if (!
|
|
33220
|
+
const agentBaseDir2 = join15(homedir14(), `.${agent}`);
|
|
33221
|
+
if (!existsSync9(agentBaseDir2)) {
|
|
32986
33222
|
const agentLabels = {
|
|
32987
33223
|
claude: "Claude Code",
|
|
32988
33224
|
codex: "Codex CLI",
|
|
@@ -33005,8 +33241,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
33005
33241
|
}
|
|
33006
33242
|
}
|
|
33007
33243
|
try {
|
|
33008
|
-
|
|
33009
|
-
|
|
33244
|
+
mkdirSync13(destDir, { recursive: true });
|
|
33245
|
+
writeFileSync7(join15(destDir, "SKILL.md"), skillMdContent);
|
|
33010
33246
|
return { skill: name, success: true, path: destDir };
|
|
33011
33247
|
} catch (error) {
|
|
33012
33248
|
return {
|
|
@@ -33019,7 +33255,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
33019
33255
|
function removeSkillForAgent(name, options) {
|
|
33020
33256
|
const { agent, scope = "global", projectDir } = options;
|
|
33021
33257
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
33022
|
-
if (!
|
|
33258
|
+
if (!existsSync9(destDir)) {
|
|
33023
33259
|
return false;
|
|
33024
33260
|
}
|
|
33025
33261
|
rmSync2(destDir, { recursive: true, force: true });
|
|
@@ -35364,8 +35600,8 @@ var init_ai_task = () => {};
|
|
|
35364
35600
|
var exports_mcp = {};
|
|
35365
35601
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35366
35602
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
35367
|
-
import { readFileSync as
|
|
35368
|
-
import { join as
|
|
35603
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
35604
|
+
import { join as join16 } from "path";
|
|
35369
35605
|
function json(data) {
|
|
35370
35606
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
35371
35607
|
}
|
|
@@ -35430,7 +35666,7 @@ var init_mcp = __esm(async () => {
|
|
|
35430
35666
|
init_dialogs();
|
|
35431
35667
|
init_profiles();
|
|
35432
35668
|
init_types();
|
|
35433
|
-
_pkg = JSON.parse(
|
|
35669
|
+
_pkg = JSON.parse(readFileSync9(join16(import.meta.dir, "../../package.json"), "utf8"));
|
|
35434
35670
|
networkLogCleanup = new Map;
|
|
35435
35671
|
consoleCaptureCleanup = new Map;
|
|
35436
35672
|
harCaptures = new Map;
|
|
@@ -37096,14 +37332,17 @@ var init_mcp = __esm(async () => {
|
|
|
37096
37332
|
return err(e);
|
|
37097
37333
|
}
|
|
37098
37334
|
});
|
|
37099
|
-
server.tool("browser_script_run", "Run a saved
|
|
37100
|
-
name: exports_external.string().describe("Script name
|
|
37335
|
+
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).", {
|
|
37336
|
+
name: exports_external.string().describe("Script name"),
|
|
37101
37337
|
session_id: exports_external.string().optional(),
|
|
37102
|
-
|
|
37103
|
-
|
|
37338
|
+
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
|
|
37339
|
+
variables: exports_external.record(exports_external.string()).optional().describe("Override script variables")
|
|
37340
|
+
}, async ({ name, session_id, engine, variables }) => {
|
|
37104
37341
|
try {
|
|
37105
|
-
const {
|
|
37106
|
-
const
|
|
37342
|
+
const { getScriptByName: getScriptByName2, migrateJsonScripts: migrateJsonScripts2, getSteps: getSteps2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37343
|
+
const { executeScript: executeScript2 } = await Promise.resolve().then(() => (init_script_engine(), exports_script_engine));
|
|
37344
|
+
migrateJsonScripts2();
|
|
37345
|
+
const script = getScriptByName2(name);
|
|
37107
37346
|
if (!script)
|
|
37108
37347
|
return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
|
|
37109
37348
|
let sid;
|
|
@@ -37112,55 +37351,72 @@ var init_mcp = __esm(async () => {
|
|
|
37112
37351
|
sid = resolveSessionId(session_id);
|
|
37113
37352
|
page = getSessionPage(sid);
|
|
37114
37353
|
} else {
|
|
37115
|
-
const result = await createSession2({ headless: true });
|
|
37354
|
+
const result = await createSession2({ engine: engine ?? "auto", headless: true });
|
|
37116
37355
|
sid = result.session.id;
|
|
37117
37356
|
page = result.page;
|
|
37118
37357
|
}
|
|
37119
|
-
const
|
|
37120
|
-
|
|
37358
|
+
const steps = getSteps2(script.id);
|
|
37359
|
+
const runId = executeScript2(script.id, page, variables ?? {});
|
|
37360
|
+
return json({ run_id: runId, session_id: sid, script: name, total_steps: steps.length, message: "Script running. Poll with browser_script_status." });
|
|
37121
37361
|
} catch (e) {
|
|
37122
37362
|
return err(e);
|
|
37123
37363
|
}
|
|
37124
37364
|
});
|
|
37125
|
-
server.tool("browser_script_status", "Check
|
|
37365
|
+
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 }) => {
|
|
37126
37366
|
try {
|
|
37127
|
-
const {
|
|
37128
|
-
const
|
|
37129
|
-
if (!
|
|
37130
|
-
return err(new Error(`
|
|
37367
|
+
const { getRun: getRun3 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37368
|
+
const run = getRun3(run_id);
|
|
37369
|
+
if (!run)
|
|
37370
|
+
return err(new Error(`Run '${run_id}' not found`));
|
|
37131
37371
|
return json({
|
|
37132
|
-
status:
|
|
37133
|
-
progress: `${
|
|
37134
|
-
current_step:
|
|
37135
|
-
steps_log:
|
|
37136
|
-
|
|
37372
|
+
status: run.status,
|
|
37373
|
+
progress: `${run.current_step}/${run.total_steps}`,
|
|
37374
|
+
current_step: run.current_description,
|
|
37375
|
+
steps_log: run.steps_log,
|
|
37376
|
+
errors: run.errors.length > 0 ? run.errors : undefined,
|
|
37377
|
+
duration_ms: run.duration_ms,
|
|
37378
|
+
completed: run.completed_at
|
|
37137
37379
|
});
|
|
37138
37380
|
} catch (e) {
|
|
37139
37381
|
return err(e);
|
|
37140
37382
|
}
|
|
37141
37383
|
});
|
|
37142
|
-
server.tool("browser_script_list", "List all saved
|
|
37384
|
+
server.tool("browser_script_list", "List all saved scripts", {}, async () => {
|
|
37143
37385
|
try {
|
|
37144
|
-
const { listScripts: listScripts2 } = await Promise.resolve().then(() => (
|
|
37145
|
-
|
|
37386
|
+
const { listScripts: listScripts2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37387
|
+
migrateJsonScripts2();
|
|
37388
|
+
const scripts = listScripts2();
|
|
37389
|
+
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 });
|
|
37146
37390
|
} catch (e) {
|
|
37147
37391
|
return err(e);
|
|
37148
37392
|
}
|
|
37149
37393
|
});
|
|
37150
|
-
server.tool("browser_script_save", "Save a
|
|
37394
|
+
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.", {
|
|
37395
|
+
name: exports_external.string(),
|
|
37396
|
+
domain: exports_external.string().optional().default(""),
|
|
37397
|
+
description: exports_external.string().optional().default(""),
|
|
37398
|
+
variables: exports_external.record(exports_external.string()).optional().default({}),
|
|
37399
|
+
steps: exports_external.array(exports_external.object({
|
|
37400
|
+
type: exports_external.enum(["browser", "connector", "extract", "wait", "condition", "save_state"]),
|
|
37401
|
+
config: exports_external.record(exports_external.unknown()).default({}),
|
|
37402
|
+
description: exports_external.string().optional().default(""),
|
|
37403
|
+
ai_enabled: exports_external.boolean().optional().default(false),
|
|
37404
|
+
ai_config: exports_external.record(exports_external.unknown()).optional().default({})
|
|
37405
|
+
}))
|
|
37406
|
+
}, async ({ name, domain, description, variables, steps }) => {
|
|
37151
37407
|
try {
|
|
37152
|
-
const {
|
|
37153
|
-
const script =
|
|
37154
|
-
const
|
|
37155
|
-
return json({
|
|
37408
|
+
const { upsertScript: upsertScript2, getSteps: getSteps2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37409
|
+
const script = upsertScript2({ name, domain, description, variables, steps });
|
|
37410
|
+
const savedSteps = getSteps2(script.id);
|
|
37411
|
+
return json({ id: script.id, name: script.name, steps: savedSteps.length });
|
|
37156
37412
|
} catch (e) {
|
|
37157
37413
|
return err(e);
|
|
37158
37414
|
}
|
|
37159
37415
|
});
|
|
37160
|
-
server.tool("browser_script_delete", "Delete a saved
|
|
37416
|
+
server.tool("browser_script_delete", "Delete a saved script", { name: exports_external.string() }, async ({ name }) => {
|
|
37161
37417
|
try {
|
|
37162
|
-
const {
|
|
37163
|
-
return json({ deleted:
|
|
37418
|
+
const { deleteScriptByName: deleteScriptByName2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37419
|
+
return json({ deleted: deleteScriptByName2(name) });
|
|
37164
37420
|
} catch (e) {
|
|
37165
37421
|
return err(e);
|
|
37166
37422
|
}
|
|
@@ -37933,8 +38189,8 @@ var init_snapshots = __esm(() => {
|
|
|
37933
38189
|
|
|
37934
38190
|
// src/server/index.ts
|
|
37935
38191
|
var exports_server = {};
|
|
37936
|
-
import { join as
|
|
37937
|
-
import { existsSync as
|
|
38192
|
+
import { join as join17 } from "path";
|
|
38193
|
+
import { existsSync as existsSync10 } from "fs";
|
|
37938
38194
|
function ok(data, status = 200) {
|
|
37939
38195
|
return new Response(JSON.stringify(data), {
|
|
37940
38196
|
status,
|
|
@@ -38193,14 +38449,14 @@ var init_server = __esm(() => {
|
|
|
38193
38449
|
if (path.match(/^\/api\/gallery\/([^/]+)\/thumbnail$/) && method === "GET") {
|
|
38194
38450
|
const id = path.split("/")[3];
|
|
38195
38451
|
const entry = getEntry(id);
|
|
38196
|
-
if (!entry?.thumbnail_path || !
|
|
38452
|
+
if (!entry?.thumbnail_path || !existsSync10(entry.thumbnail_path))
|
|
38197
38453
|
return notFound("Thumbnail not found");
|
|
38198
38454
|
return new Response(Bun.file(entry.thumbnail_path), { headers: { ...CORS_HEADERS } });
|
|
38199
38455
|
}
|
|
38200
38456
|
if (path.match(/^\/api\/gallery\/([^/]+)\/image$/) && method === "GET") {
|
|
38201
38457
|
const id = path.split("/")[3];
|
|
38202
38458
|
const entry = getEntry(id);
|
|
38203
|
-
if (!entry?.path || !
|
|
38459
|
+
if (!entry?.path || !existsSync10(entry.path))
|
|
38204
38460
|
return notFound("Image not found");
|
|
38205
38461
|
return new Response(Bun.file(entry.path), { headers: { ...CORS_HEADERS } });
|
|
38206
38462
|
}
|
|
@@ -38228,7 +38484,7 @@ var init_server = __esm(() => {
|
|
|
38228
38484
|
if (path.match(/^\/api\/downloads\/([^/]+)\/raw$/) && method === "GET") {
|
|
38229
38485
|
const id = path.split("/")[3];
|
|
38230
38486
|
const file = getDownload(id);
|
|
38231
|
-
if (!file || !
|
|
38487
|
+
if (!file || !existsSync10(file.path))
|
|
38232
38488
|
return notFound("Download not found");
|
|
38233
38489
|
return new Response(Bun.file(file.path), { headers: { ...CORS_HEADERS } });
|
|
38234
38490
|
}
|
|
@@ -38236,13 +38492,13 @@ var init_server = __esm(() => {
|
|
|
38236
38492
|
const id = path.split("/")[3];
|
|
38237
38493
|
return ok({ deleted: deleteDownload(id) });
|
|
38238
38494
|
}
|
|
38239
|
-
const dashboardDist =
|
|
38240
|
-
if (
|
|
38241
|
-
const filePath = path === "/" ?
|
|
38242
|
-
if (
|
|
38495
|
+
const dashboardDist = join17(import.meta.dir, "../../dashboard/dist");
|
|
38496
|
+
if (existsSync10(dashboardDist)) {
|
|
38497
|
+
const filePath = path === "/" ? join17(dashboardDist, "index.html") : join17(dashboardDist, path);
|
|
38498
|
+
if (existsSync10(filePath)) {
|
|
38243
38499
|
return new Response(Bun.file(filePath), { headers: CORS_HEADERS });
|
|
38244
38500
|
}
|
|
38245
|
-
return new Response(Bun.file(
|
|
38501
|
+
return new Response(Bun.file(join17(dashboardDist, "index.html")), { headers: CORS_HEADERS });
|
|
38246
38502
|
}
|
|
38247
38503
|
if (path === "/" || path === "") {
|
|
38248
38504
|
return new Response("@hasna/browser REST API running. Dashboard not built.", {
|
|
@@ -38284,10 +38540,10 @@ init_projects();
|
|
|
38284
38540
|
init_recorder();
|
|
38285
38541
|
init_recordings();
|
|
38286
38542
|
init_lightpanda();
|
|
38287
|
-
import { readFileSync as
|
|
38288
|
-
import { join as
|
|
38543
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
38544
|
+
import { join as join18 } from "path";
|
|
38289
38545
|
import chalk from "chalk";
|
|
38290
|
-
var pkg = JSON.parse(
|
|
38546
|
+
var pkg = JSON.parse(readFileSync10(join18(import.meta.dir, "../../package.json"), "utf8"));
|
|
38291
38547
|
var program2 = new Command;
|
|
38292
38548
|
program2.name("browser").description("@hasna/browser \u2014 general-purpose browser agent CLI").version(pkg.version);
|
|
38293
38549
|
program2.command("navigate <url>").description("Navigate to a URL and optionally take a screenshot").option("--engine <engine>", "Browser engine: playwright|cdp|lightpanda|auto", "auto").option("--screenshot", "Take a screenshot after navigation").option("--extract", "Extract page text after navigation").option("--headed", "Run in headed (visible) mode").option("--json", "Output as JSON").action(async (url, opts) => {
|
|
@@ -38734,18 +38990,19 @@ program2.command("login <url>").description("Login to a site: detect form, fill
|
|
|
38734
38990
|
if (!opts.headed)
|
|
38735
38991
|
await closeSession2(session.id);
|
|
38736
38992
|
});
|
|
38737
|
-
var scriptCmd = program2.command("script").description("Manage
|
|
38738
|
-
scriptCmd.command("run <name>").description("Run a saved
|
|
38739
|
-
const {
|
|
38740
|
-
const
|
|
38993
|
+
var scriptCmd = program2.command("script").description("Manage automation scripts (browser + connector + AI)");
|
|
38994
|
+
scriptCmd.command("run <name>").description("Run a saved script (sync, shows progress live)").option("--engine <engine>", "Browser engine", "auto").option("--headed", "Run in headed (visible) mode").option("--json", "Output as JSON").option("--var <pairs...>", "Set variables (key=value)").action(async (name, opts) => {
|
|
38995
|
+
const { getScriptByName: getScriptByName2, getSteps: getSteps2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
38996
|
+
const { executeScriptSync: executeScriptSync2 } = await Promise.resolve().then(() => (init_script_engine(), exports_script_engine));
|
|
38997
|
+
migrateJsonScripts2();
|
|
38998
|
+
const script = getScriptByName2(name);
|
|
38741
38999
|
if (!script) {
|
|
38742
|
-
console.log(chalk.red(`Script '${name}' not found
|
|
39000
|
+
console.log(chalk.red(`Script '${name}' not found.`));
|
|
38743
39001
|
return;
|
|
38744
39002
|
}
|
|
38745
39003
|
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
39004
|
+
const steps = getSteps2(script.id);
|
|
38746
39005
|
const overrides = {};
|
|
38747
|
-
if (opts.email)
|
|
38748
|
-
overrides.email = opts.email;
|
|
38749
39006
|
if (opts.var) {
|
|
38750
39007
|
for (const pair of opts.var) {
|
|
38751
39008
|
const [k, ...v] = pair.split("=");
|
|
@@ -38754,83 +39011,92 @@ scriptCmd.command("run <name>").description("Run a saved login script").option("
|
|
|
38754
39011
|
}
|
|
38755
39012
|
}
|
|
38756
39013
|
if (!opts.json) {
|
|
38757
|
-
console.log(chalk.gray(`Running
|
|
38758
|
-
console.log(chalk.gray(` Domain: ${script.domain}`));
|
|
39014
|
+
console.log(chalk.gray(`Running: ${script.name} (${steps.length} steps)`));
|
|
38759
39015
|
if (script.description)
|
|
38760
39016
|
console.log(chalk.gray(` ${script.description}
|
|
38761
39017
|
`));
|
|
38762
39018
|
}
|
|
38763
|
-
const result = await
|
|
39019
|
+
const result = await executeScriptSync2(script.id, page, overrides);
|
|
38764
39020
|
if (opts.json) {
|
|
38765
39021
|
console.log(JSON.stringify({ ...result, session_id: session.id }));
|
|
38766
39022
|
} else {
|
|
38767
39023
|
if (result.success) {
|
|
38768
39024
|
console.log(chalk.green(`
|
|
38769
|
-
\u2713
|
|
39025
|
+
\u2713 Completed (${result.steps_executed} steps, ${result.duration_ms}ms)`));
|
|
38770
39026
|
} else {
|
|
38771
39027
|
console.log(chalk.red(`
|
|
38772
|
-
\u2717
|
|
39028
|
+
\u2717 Failed (${result.steps_failed}/${result.steps_executed} failed)`));
|
|
38773
39029
|
result.errors.forEach((e) => console.log(chalk.red(` ${e}`)));
|
|
38774
39030
|
}
|
|
38775
|
-
console.log(chalk.gray(` Session: ${session.id}`));
|
|
38776
|
-
console.log(chalk.gray(` URL: ${result.variables.current_url ?? page.url()}`));
|
|
38777
39031
|
}
|
|
38778
39032
|
if (!opts.headed)
|
|
38779
39033
|
await closeSession2(session.id);
|
|
38780
39034
|
});
|
|
38781
|
-
scriptCmd.command("list").description("List saved
|
|
38782
|
-
const { listScripts: listScripts2 } = await Promise.resolve().then(() => (
|
|
39035
|
+
scriptCmd.command("list").description("List saved scripts").option("--json", "Output as JSON").action(async (opts) => {
|
|
39036
|
+
const { listScripts: listScripts2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
39037
|
+
migrateJsonScripts2();
|
|
38783
39038
|
const scripts = listScripts2();
|
|
38784
39039
|
if (opts.json) {
|
|
38785
39040
|
console.log(JSON.stringify(scripts, null, 2));
|
|
38786
39041
|
} else if (scripts.length === 0) {
|
|
38787
|
-
console.log(chalk.gray("No scripts
|
|
39042
|
+
console.log(chalk.gray("No scripts. Import with: browser script import <file.json>"));
|
|
38788
39043
|
} else {
|
|
38789
39044
|
scripts.forEach((s) => {
|
|
38790
|
-
console.log(`${chalk.bold(s.name)} ${chalk.gray(`(${s.domain})`)} \u2014 ${s.
|
|
39045
|
+
console.log(`${chalk.bold(s.name)} ${chalk.gray(`(${s.domain})`)} \u2014 runs: ${s.run_count}`);
|
|
38791
39046
|
if (s.description)
|
|
38792
39047
|
console.log(chalk.gray(` ${s.description}`));
|
|
38793
39048
|
});
|
|
38794
39049
|
}
|
|
38795
39050
|
});
|
|
38796
|
-
scriptCmd.command("
|
|
38797
|
-
const {
|
|
38798
|
-
|
|
38799
|
-
|
|
38800
|
-
|
|
38801
|
-
console.log(chalk.green(`\u2713 Script saved: ${script.name}`));
|
|
38802
|
-
console.log(chalk.gray(` Domain: ${script.domain}`));
|
|
38803
|
-
console.log(chalk.gray(` Steps: ${script.steps.length}`));
|
|
38804
|
-
console.log(chalk.gray(` Path: ${path}`));
|
|
38805
|
-
console.log(chalk.gray(` Run with: browser script run ${script.name}`));
|
|
38806
|
-
} catch (err2) {
|
|
38807
|
-
console.log(chalk.red(`Error: ${err2 instanceof Error ? err2.message : String(err2)}`));
|
|
39051
|
+
scriptCmd.command("import <file>").description("Import a script from a JSON file into SQLite").action(async (file) => {
|
|
39052
|
+
const { readFileSync: readFileSync11, existsSync: existsSync11 } = await import("fs");
|
|
39053
|
+
if (!existsSync11(file)) {
|
|
39054
|
+
console.log(chalk.red(`File not found: ${file}`));
|
|
39055
|
+
return;
|
|
38808
39056
|
}
|
|
39057
|
+
const raw = JSON.parse(readFileSync11(file, "utf8"));
|
|
39058
|
+
const { upsertScript: upsertScript2, getSteps: getSteps2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
39059
|
+
const steps = (raw.steps ?? []).map((s) => {
|
|
39060
|
+
const isAI = s.type === "ai";
|
|
39061
|
+
return {
|
|
39062
|
+
type: isAI ? "extract" : s.type,
|
|
39063
|
+
config: { action: s.action, selector: s.selector, url: s.url, value: s.value, text: s.text, timeout: s.timeout, connector: s.connector, args: s.args, seconds: s.seconds, check: s.check, equals: s.equals, contains: s.contains, skip_to: s.skip_to, name: s.name, save_as: s.save_as, pattern: s.pattern, ...isAI ? { prompt: s.prompt, source: s.check ?? "last_output" } : {} },
|
|
39064
|
+
description: s.description ?? "",
|
|
39065
|
+
ai_enabled: isAI || !!s.ai_enabled,
|
|
39066
|
+
ai_config: isAI ? { provider: "cerebras", model: s.model ?? "fast", prompt: s.prompt } : s.ai_config ?? {}
|
|
39067
|
+
};
|
|
39068
|
+
});
|
|
39069
|
+
const script = upsertScript2({ name: raw.name, domain: raw.domain ?? "", description: raw.description ?? "", variables: raw.variables ?? {}, steps });
|
|
39070
|
+
const saved = getSteps2(script.id);
|
|
39071
|
+
console.log(chalk.green(`\u2713 Imported: ${script.name} (${saved.length} steps)`));
|
|
38809
39072
|
});
|
|
38810
39073
|
scriptCmd.command("show <name>").description("Show script details").action(async (name) => {
|
|
38811
|
-
const {
|
|
38812
|
-
|
|
39074
|
+
const { getScriptByName: getScriptByName2, getSteps: getSteps2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
39075
|
+
migrateJsonScripts2();
|
|
39076
|
+
const script = getScriptByName2(name);
|
|
38813
39077
|
if (!script) {
|
|
38814
39078
|
console.log(chalk.red(`Script '${name}' not found`));
|
|
38815
39079
|
return;
|
|
38816
39080
|
}
|
|
39081
|
+
const steps = getSteps2(script.id);
|
|
38817
39082
|
console.log(chalk.bold(`${script.name} (${script.domain})
|
|
38818
39083
|
`));
|
|
38819
39084
|
if (script.description)
|
|
38820
39085
|
console.log(chalk.gray(` ${script.description}
|
|
38821
39086
|
`));
|
|
38822
|
-
console.log(chalk.gray(` Variables: ${Object.keys(script.variables).join(", ")}
|
|
39087
|
+
console.log(chalk.gray(` Variables: ${Object.keys(script.variables).join(", ")}`));
|
|
39088
|
+
console.log(chalk.gray(` Runs: ${script.run_count} Last: ${script.last_run ?? "never"}
|
|
38823
39089
|
`));
|
|
38824
|
-
|
|
38825
|
-
const
|
|
38826
|
-
const detail = s.url ?? s.selector ?? s.
|
|
38827
|
-
console.log(` ${chalk.cyan(`${i + 1}.`)} [${s.type}] ${
|
|
39090
|
+
steps.forEach((s, i) => {
|
|
39091
|
+
const ai = s.ai_enabled ? chalk.yellow(" [AI]") : "";
|
|
39092
|
+
const detail = s.config.url ?? s.config.selector ?? s.config.connector ?? s.config.prompt?.slice(0, 40) ?? "";
|
|
39093
|
+
console.log(` ${chalk.cyan(`${i + 1}.`)} [${s.type}]${ai} ${s.description} ${chalk.gray(String(detail).slice(0, 50))}`);
|
|
38828
39094
|
});
|
|
38829
39095
|
});
|
|
38830
39096
|
scriptCmd.command("delete <name>").description("Delete a saved script").action(async (name) => {
|
|
38831
|
-
const {
|
|
38832
|
-
if (
|
|
38833
|
-
console.log(chalk.green(`\u2713
|
|
39097
|
+
const { deleteScriptByName: deleteScriptByName2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
39098
|
+
if (deleteScriptByName2(name)) {
|
|
39099
|
+
console.log(chalk.green(`\u2713 Deleted: ${name}`));
|
|
38834
39100
|
} else {
|
|
38835
39101
|
console.log(chalk.red(`Script '${name}' not found`));
|
|
38836
39102
|
}
|
|
@@ -38860,10 +39126,10 @@ daemonCmd.command("start").description("Start the browser daemon in the backgrou
|
|
|
38860
39126
|
return;
|
|
38861
39127
|
}
|
|
38862
39128
|
const { spawn: spawn2 } = await import("child_process");
|
|
38863
|
-
const { writeFileSync:
|
|
39129
|
+
const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync14 } = await import("fs");
|
|
38864
39130
|
const { dirname: dirname4 } = await import("path");
|
|
38865
39131
|
const pidFile = getDaemonPidFile2();
|
|
38866
|
-
|
|
39132
|
+
mkdirSync14(dirname4(pidFile), { recursive: true });
|
|
38867
39133
|
const child = spawn2(process.execPath, [import.meta.dir + "/../server/index.js"], {
|
|
38868
39134
|
detached: true,
|
|
38869
39135
|
stdio: "ignore",
|
|
@@ -38871,7 +39137,7 @@ daemonCmd.command("start").description("Start the browser daemon in the backgrou
|
|
|
38871
39137
|
});
|
|
38872
39138
|
child.unref();
|
|
38873
39139
|
if (child.pid) {
|
|
38874
|
-
|
|
39140
|
+
writeFileSync8(pidFile, String(child.pid));
|
|
38875
39141
|
await new Promise((r) => setTimeout(r, 1500));
|
|
38876
39142
|
console.log(chalk.green(`\u2713 Daemon started`));
|
|
38877
39143
|
console.log(chalk.gray(` PID: ${child.pid}, Port: ${opts.port}`));
|
|
@@ -39044,11 +39310,11 @@ galleryCmd.command("stats").description("Show gallery statistics").option("--pro
|
|
|
39044
39310
|
});
|
|
39045
39311
|
galleryCmd.command("clean").description("Delete gallery entries with missing files").action(async () => {
|
|
39046
39312
|
const { listEntries: listEntries2, deleteEntry: deleteEntry2 } = await Promise.resolve().then(() => (init_gallery(), exports_gallery));
|
|
39047
|
-
const { existsSync:
|
|
39313
|
+
const { existsSync: existsSync11 } = await import("fs");
|
|
39048
39314
|
const entries = listEntries2({ limit: 9999 });
|
|
39049
39315
|
let removed = 0;
|
|
39050
39316
|
for (const e of entries) {
|
|
39051
|
-
if (!
|
|
39317
|
+
if (!existsSync11(e.path)) {
|
|
39052
39318
|
deleteEntry2(e.id);
|
|
39053
39319
|
removed++;
|
|
39054
39320
|
}
|