@hasna/browser 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +814 -618
- 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/script-engine.d.ts +28 -0
- package/dist/lib/script-engine.d.ts.map +1 -0
- package/dist/mcp/index.js +644 -458
- 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,395 +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
|
+
}
|
|
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;
|
|
12911
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
|
-
|
|
12955
|
-
break;
|
|
12956
|
-
case "ai":
|
|
12957
|
-
await runAIStep(step, vars);
|
|
13273
|
+
await execExtract(cfg, step, vars);
|
|
12958
13274
|
break;
|
|
12959
13275
|
case "wait":
|
|
12960
|
-
await new Promise((r) => setTimeout(r, (
|
|
13276
|
+
await new Promise((r) => setTimeout(r, (cfg.seconds ?? 3) * 1000));
|
|
12961
13277
|
break;
|
|
12962
|
-
case "condition":
|
|
12963
|
-
|
|
12964
|
-
let conditionMet = true;
|
|
12965
|
-
if (step.equals !== undefined)
|
|
12966
|
-
conditionMet = checkVal === interpolate(step.equals, vars);
|
|
12967
|
-
if (step.contains !== undefined)
|
|
12968
|
-
conditionMet = checkVal?.includes(interpolate(step.contains, vars)) ?? false;
|
|
12969
|
-
if (!conditionMet && step.skip_to !== undefined) {
|
|
12970
|
-
i = step.skip_to - 1;
|
|
12971
|
-
}
|
|
13278
|
+
case "condition":
|
|
13279
|
+
i = execCondition(cfg, vars, i);
|
|
12972
13280
|
break;
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
const stateName = interpolate(step.name ?? script.name, vars);
|
|
12976
|
-
try {
|
|
12977
|
-
const { saveStateFromPage: saveStateFromPage2 } = await Promise.resolve().then(() => (init_storage_state(), exports_storage_state));
|
|
12978
|
-
const path = await saveStateFromPage2(page, stateName);
|
|
12979
|
-
vars["saved_state_path"] = path;
|
|
12980
|
-
} catch {}
|
|
13281
|
+
case "save_state":
|
|
13282
|
+
await execSaveState(cfg, page, vars);
|
|
12981
13283
|
break;
|
|
12982
|
-
}
|
|
12983
13284
|
}
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
logEntry.duration_ms = Date.now() - stepStart;
|
|
13285
|
+
stepsLog[stepsLog.length - 1].status = "ok";
|
|
13286
|
+
stepsLog[stepsLog.length - 1].duration_ms = Date.now() - stepStart;
|
|
12987
13287
|
} catch (err) {
|
|
12988
13288
|
failed++;
|
|
12989
|
-
const msg = `Step ${i + 1} (${step.type}
|
|
13289
|
+
const msg = `Step ${i + 1} (${step.type}): ${err instanceof Error ? err.message : String(err)}`;
|
|
12990
13290
|
errors.push(msg);
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
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")
|
|
12996
13295
|
break;
|
|
12997
13296
|
}
|
|
13297
|
+
updateRunProgress(runId, i + 1, desc, stepsLog, vars);
|
|
12998
13298
|
}
|
|
12999
|
-
const
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
variables: vars,
|
|
13004
|
-
errors,
|
|
13005
|
-
duration_ms: Date.now() - t0
|
|
13006
|
-
};
|
|
13007
|
-
job.status = failed === 0 ? "completed" : "failed";
|
|
13008
|
-
job.result = result;
|
|
13009
|
-
return result;
|
|
13010
|
-
}
|
|
13011
|
-
function runScriptAsync(script, page, overrides = {}) {
|
|
13012
|
-
const jobId = randomUUID10();
|
|
13013
|
-
const job = {
|
|
13014
|
-
id: jobId,
|
|
13015
|
-
script_name: script.name,
|
|
13016
|
-
status: "running",
|
|
13017
|
-
current_step: 0,
|
|
13018
|
-
total_steps: script.steps.length,
|
|
13019
|
-
current_step_description: "Starting...",
|
|
13020
|
-
steps_log: [],
|
|
13021
|
-
started_at: new Date().toISOString()
|
|
13022
|
-
};
|
|
13023
|
-
activeJobs.set(jobId, job);
|
|
13024
|
-
runScript(script, page, overrides, jobId).catch((err) => {
|
|
13025
|
-
job.status = "failed";
|
|
13026
|
-
job.current_step_description = `Fatal error: ${err instanceof Error ? err.message : String(err)}`;
|
|
13027
|
-
});
|
|
13028
|
-
return jobId;
|
|
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 };
|
|
13029
13303
|
}
|
|
13030
|
-
async function
|
|
13031
|
-
const action =
|
|
13032
|
-
if (!action)
|
|
13033
|
-
throw new Error("Browser step missing action");
|
|
13304
|
+
async function execBrowser(cfg, step, page, vars) {
|
|
13305
|
+
const action = cfg.action;
|
|
13034
13306
|
switch (action) {
|
|
13035
|
-
case "navigate":
|
|
13036
|
-
|
|
13037
|
-
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 });
|
|
13038
13309
|
await new Promise((r) => setTimeout(r, 1000));
|
|
13039
13310
|
vars["current_url"] = page.url();
|
|
13040
13311
|
vars["current_title"] = await page.title();
|
|
13041
13312
|
break;
|
|
13042
|
-
}
|
|
13043
13313
|
case "type": {
|
|
13044
|
-
const selector =
|
|
13045
|
-
const value =
|
|
13046
|
-
|
|
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
|
+
}
|
|
13047
13335
|
break;
|
|
13048
13336
|
}
|
|
13049
13337
|
case "click": {
|
|
13050
|
-
const selector =
|
|
13051
|
-
|
|
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
|
+
}
|
|
13052
13357
|
await new Promise((r) => setTimeout(r, 500));
|
|
13053
13358
|
break;
|
|
13054
13359
|
}
|
|
13055
13360
|
case "click_text": {
|
|
13056
|
-
const text =
|
|
13057
|
-
|
|
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
|
+
}
|
|
13058
13374
|
await new Promise((r) => setTimeout(r, 500));
|
|
13059
13375
|
break;
|
|
13060
13376
|
}
|
|
13061
|
-
case "wait_for_navigation":
|
|
13377
|
+
case "wait_for_navigation":
|
|
13062
13378
|
try {
|
|
13063
|
-
await page.waitForNavigation({ timeout:
|
|
13379
|
+
await page.waitForNavigation({ timeout: cfg.timeout ?? 15000 });
|
|
13064
13380
|
} catch {}
|
|
13065
13381
|
await new Promise((r) => setTimeout(r, 1000));
|
|
13066
13382
|
vars["current_url"] = page.url();
|
|
13067
13383
|
break;
|
|
13068
|
-
}
|
|
13069
13384
|
case "wait_for_text": {
|
|
13070
|
-
const text =
|
|
13071
|
-
await page.waitForSelector(`text=${text}`, { timeout:
|
|
13385
|
+
const text = cfg.text;
|
|
13386
|
+
await page.waitForSelector(`text=${text}`, { timeout: cfg.timeout ?? 1e4 });
|
|
13072
13387
|
break;
|
|
13073
13388
|
}
|
|
13074
|
-
case "snapshot":
|
|
13389
|
+
case "snapshot":
|
|
13075
13390
|
vars["page_text"] = await page.evaluate(() => document.body?.textContent?.trim() ?? "");
|
|
13076
13391
|
break;
|
|
13077
|
-
}
|
|
13078
13392
|
}
|
|
13079
13393
|
}
|
|
13080
|
-
async function
|
|
13081
|
-
const
|
|
13082
|
-
if (!
|
|
13083
|
-
throw new Error("Connector step missing connector
|
|
13084
|
-
const args =
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
stdout: "pipe",
|
|
13090
|
-
stderr: "pipe",
|
|
13091
|
-
env: { ...process.env, HOME: process.env.HOME ?? "" }
|
|
13092
|
-
});
|
|
13093
|
-
stdout = await new Response(proc.stdout).text();
|
|
13094
|
-
const stderr = await new Response(proc.stderr).text();
|
|
13095
|
-
const exitCode = await proc.exited;
|
|
13096
|
-
vars["last_success"] = String(exitCode === 0);
|
|
13097
|
-
vars["last_exit_code"] = String(exitCode);
|
|
13098
|
-
if (exitCode !== 0 && stderr)
|
|
13099
|
-
stdout = stderr;
|
|
13100
|
-
} catch (e) {
|
|
13101
|
-
vars["last_success"] = "false";
|
|
13102
|
-
throw new Error(`Connector ${connectorName} failed: ${e.message ?? String(e)}`);
|
|
13103
|
-
}
|
|
13104
|
-
vars["last_output"] = stdout;
|
|
13105
|
-
try {
|
|
13106
|
-
const parsed = JSON.parse(stdout);
|
|
13107
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
13108
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
13109
|
-
if (typeof v === "string" || typeof v === "number") {
|
|
13110
|
-
vars[`last.${k}`] = String(v);
|
|
13111
|
-
}
|
|
13112
|
-
}
|
|
13113
|
-
}
|
|
13114
|
-
} catch {}
|
|
13115
|
-
if (step.save_as) {
|
|
13116
|
-
vars[step.save_as] = stdout;
|
|
13117
|
-
}
|
|
13118
|
-
}
|
|
13119
|
-
function getAIProvider(provider) {
|
|
13120
|
-
if (provider === "anthropic") {
|
|
13121
|
-
const apiKey2 = process.env["ANTHROPIC_API_KEY"];
|
|
13122
|
-
if (!apiKey2)
|
|
13123
|
-
throw new Error("ANTHROPIC_API_KEY not set");
|
|
13124
|
-
return {
|
|
13125
|
-
url: "https://api.anthropic.com/v1/messages",
|
|
13126
|
-
headers: { "content-type": "application/json", "x-api-key": apiKey2, "anthropic-version": "2023-06-01" },
|
|
13127
|
-
body: (model, prompt) => ({ model, max_tokens: 1024, messages: [{ role: "user", content: prompt }] }),
|
|
13128
|
-
extract: (data) => data.content?.[0]?.text ?? ""
|
|
13129
|
-
};
|
|
13130
|
-
}
|
|
13131
|
-
const apiKey = process.env["CEREBRAS_API_KEY"];
|
|
13132
|
-
if (!apiKey)
|
|
13133
|
-
throw new Error("CEREBRAS_API_KEY not set \u2014 required for AI steps (set CEREBRAS_API_KEY or use provider: 'anthropic')");
|
|
13134
|
-
return {
|
|
13135
|
-
url: "https://api.cerebras.ai/v1/chat/completions",
|
|
13136
|
-
headers: { "content-type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
13137
|
-
body: (model, prompt) => ({ model, max_tokens: 1024, messages: [{ role: "user", content: prompt }] }),
|
|
13138
|
-
extract: (data) => data.choices?.[0]?.message?.content ?? ""
|
|
13139
|
-
};
|
|
13140
|
-
}
|
|
13141
|
-
async function runAIStep(step, vars) {
|
|
13142
|
-
const prompt = interpolate(step.prompt ?? "", vars);
|
|
13143
|
-
if (!prompt)
|
|
13144
|
-
throw new Error("AI step missing prompt");
|
|
13145
|
-
const modelAlias = step.model ?? "fast";
|
|
13146
|
-
const resolved = MODEL_MAP[modelAlias] ?? { provider: "cerebras", model: modelAlias };
|
|
13147
|
-
const saveTo = step.save_as ?? "ai_response";
|
|
13148
|
-
const provider = getAIProvider(resolved.provider);
|
|
13149
|
-
const response = await fetch(provider.url, {
|
|
13150
|
-
method: "POST",
|
|
13151
|
-
headers: provider.headers,
|
|
13152
|
-
body: JSON.stringify(provider.body(resolved.model, prompt))
|
|
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 ?? "" }
|
|
13153
13403
|
});
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
const
|
|
13160
|
-
vars[
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
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;
|
|
13174
13435
|
}
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
}
|
|
13179
|
-
function runExtractStep(step, vars) {
|
|
13180
|
-
const saveTo = step.save_as ?? "extracted";
|
|
13181
|
-
if (step.pattern) {
|
|
13182
|
-
const source = step.check ? vars[step.check] ?? "" : vars["last_output"] ?? "";
|
|
13183
|
-
const regex = new RegExp(step.pattern);
|
|
13184
|
-
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);
|
|
13185
13439
|
if (match) {
|
|
13186
13440
|
vars[saveTo] = decodeHtmlEntities(match[1] ?? match[0]);
|
|
13187
13441
|
}
|
|
13188
13442
|
}
|
|
13189
|
-
if (step.json_path) {
|
|
13190
|
-
const source = vars["last_output"] ?? "{}";
|
|
13191
|
-
try {
|
|
13192
|
-
let obj = JSON.parse(source);
|
|
13193
|
-
for (const key of step.json_path.split(".")) {
|
|
13194
|
-
obj = obj?.[key];
|
|
13195
|
-
}
|
|
13196
|
-
if (obj !== undefined)
|
|
13197
|
-
vars[saveTo] = String(obj);
|
|
13198
|
-
} catch {}
|
|
13199
|
-
}
|
|
13200
13443
|
}
|
|
13201
|
-
function
|
|
13202
|
-
const
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
return
|
|
13211
|
-
name: parsed.name,
|
|
13212
|
-
domain: parsed.domain,
|
|
13213
|
-
description: parsed.description ?? "",
|
|
13214
|
-
variables: parsed.variables ?? {},
|
|
13215
|
-
steps: parsed.steps,
|
|
13216
|
-
created_at: new Date().toISOString(),
|
|
13217
|
-
updated_at: new Date().toISOString()
|
|
13218
|
-
};
|
|
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;
|
|
13219
13454
|
}
|
|
13220
|
-
function
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
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 {}
|
|
13224
13461
|
}
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
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();
|
|
13237
13479
|
});
|
|
13238
13480
|
|
|
13239
13481
|
// src/lib/daemon-client.ts
|
|
@@ -13245,8 +13487,8 @@ __export(exports_daemon_client, {
|
|
|
13245
13487
|
getDaemonPidFile: () => getDaemonPidFile,
|
|
13246
13488
|
getDaemonPid: () => getDaemonPid
|
|
13247
13489
|
});
|
|
13248
|
-
import { existsSync as
|
|
13249
|
-
import { join as
|
|
13490
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
13491
|
+
import { join as join8 } from "path";
|
|
13250
13492
|
import { homedir as homedir8 } from "os";
|
|
13251
13493
|
function getDaemonPidFile() {
|
|
13252
13494
|
return PID_FILE;
|
|
@@ -13255,10 +13497,10 @@ function getDaemonPort() {
|
|
|
13255
13497
|
return parseInt(process.env["BROWSER_DAEMON_PORT"] ?? String(DEFAULT_PORT), 10);
|
|
13256
13498
|
}
|
|
13257
13499
|
function isDaemonRunning() {
|
|
13258
|
-
if (!
|
|
13500
|
+
if (!existsSync5(PID_FILE))
|
|
13259
13501
|
return false;
|
|
13260
13502
|
try {
|
|
13261
|
-
const pid = parseInt(
|
|
13503
|
+
const pid = parseInt(readFileSync3(PID_FILE, "utf8").trim(), 10);
|
|
13262
13504
|
process.kill(pid, 0);
|
|
13263
13505
|
return true;
|
|
13264
13506
|
} catch {
|
|
@@ -13266,10 +13508,10 @@ function isDaemonRunning() {
|
|
|
13266
13508
|
}
|
|
13267
13509
|
}
|
|
13268
13510
|
function getDaemonPid() {
|
|
13269
|
-
if (!
|
|
13511
|
+
if (!existsSync5(PID_FILE))
|
|
13270
13512
|
return null;
|
|
13271
13513
|
try {
|
|
13272
|
-
return parseInt(
|
|
13514
|
+
return parseInt(readFileSync3(PID_FILE, "utf8").trim(), 10);
|
|
13273
13515
|
} catch {
|
|
13274
13516
|
return null;
|
|
13275
13517
|
}
|
|
@@ -13289,7 +13531,7 @@ async function getDaemonStatus() {
|
|
|
13289
13531
|
}
|
|
13290
13532
|
var PID_FILE, DEFAULT_PORT = 7030;
|
|
13291
13533
|
var init_daemon_client = __esm(() => {
|
|
13292
|
-
PID_FILE =
|
|
13534
|
+
PID_FILE = join8(process.env["BROWSER_DATA_DIR"] ?? join8(homedir8(), ".browser"), "daemon.pid");
|
|
13293
13535
|
});
|
|
13294
13536
|
|
|
13295
13537
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -17363,16 +17605,16 @@ __export(exports_downloads, {
|
|
|
17363
17605
|
cleanStaleDownloads: () => cleanStaleDownloads
|
|
17364
17606
|
});
|
|
17365
17607
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
17366
|
-
import { join as
|
|
17367
|
-
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";
|
|
17368
17610
|
import { homedir as homedir9 } from "os";
|
|
17369
17611
|
function getDataDir3() {
|
|
17370
|
-
return process.env["BROWSER_DATA_DIR"] ??
|
|
17612
|
+
return process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser");
|
|
17371
17613
|
}
|
|
17372
17614
|
function getDownloadsDir(sessionId) {
|
|
17373
|
-
const base =
|
|
17374
|
-
const dir = sessionId ?
|
|
17375
|
-
|
|
17615
|
+
const base = join9(getDataDir3(), "downloads");
|
|
17616
|
+
const dir = sessionId ? join9(base, sessionId) : base;
|
|
17617
|
+
mkdirSync7(dir, { recursive: true });
|
|
17376
17618
|
return dir;
|
|
17377
17619
|
}
|
|
17378
17620
|
function ensureDownloadsDir() {
|
|
@@ -17387,8 +17629,8 @@ function saveToDownloads(buffer, filename, opts) {
|
|
|
17387
17629
|
const ext = extname(filename) || "";
|
|
17388
17630
|
const stem = basename(filename, ext);
|
|
17389
17631
|
const uniqueName = `${stem}-${id.slice(0, 8)}${ext}`;
|
|
17390
|
-
const filePath =
|
|
17391
|
-
|
|
17632
|
+
const filePath = join9(dir, uniqueName);
|
|
17633
|
+
writeFileSync2(filePath, buffer);
|
|
17392
17634
|
const meta = {
|
|
17393
17635
|
id,
|
|
17394
17636
|
type: opts?.type ?? detectType(filename),
|
|
@@ -17399,7 +17641,7 @@ function saveToDownloads(buffer, filename, opts) {
|
|
|
17399
17641
|
original_name: filename,
|
|
17400
17642
|
...opts?.metadata
|
|
17401
17643
|
};
|
|
17402
|
-
|
|
17644
|
+
writeFileSync2(metaPath(filePath), JSON.stringify(meta, null, 2));
|
|
17403
17645
|
return {
|
|
17404
17646
|
id,
|
|
17405
17647
|
path: filePath,
|
|
@@ -17416,23 +17658,23 @@ function listDownloads(sessionId) {
|
|
|
17416
17658
|
const dir = getDownloadsDir(sessionId);
|
|
17417
17659
|
const results = [];
|
|
17418
17660
|
function scanDir(d) {
|
|
17419
|
-
if (!
|
|
17661
|
+
if (!existsSync6(d))
|
|
17420
17662
|
return;
|
|
17421
|
-
const entries =
|
|
17663
|
+
const entries = readdirSync3(d);
|
|
17422
17664
|
for (const entry of entries) {
|
|
17423
17665
|
if (entry.endsWith(".meta.json"))
|
|
17424
17666
|
continue;
|
|
17425
|
-
const full =
|
|
17667
|
+
const full = join9(d, entry);
|
|
17426
17668
|
const stat = statSync(full);
|
|
17427
17669
|
if (stat.isDirectory()) {
|
|
17428
17670
|
scanDir(full);
|
|
17429
17671
|
continue;
|
|
17430
17672
|
}
|
|
17431
17673
|
const mpath = metaPath(full);
|
|
17432
|
-
if (!
|
|
17674
|
+
if (!existsSync6(mpath))
|
|
17433
17675
|
continue;
|
|
17434
17676
|
try {
|
|
17435
|
-
const meta = JSON.parse(
|
|
17677
|
+
const meta = JSON.parse(readFileSync4(mpath, "utf8"));
|
|
17436
17678
|
results.push({
|
|
17437
17679
|
id: meta.id,
|
|
17438
17680
|
path: full,
|
|
@@ -17460,7 +17702,7 @@ function deleteDownload(id, sessionId) {
|
|
|
17460
17702
|
return false;
|
|
17461
17703
|
try {
|
|
17462
17704
|
unlinkSync2(file.path);
|
|
17463
|
-
if (
|
|
17705
|
+
if (existsSync6(file.meta_path))
|
|
17464
17706
|
unlinkSync2(file.meta_path);
|
|
17465
17707
|
return true;
|
|
17466
17708
|
} catch {
|
|
@@ -17507,8 +17749,8 @@ function detectType(filename) {
|
|
|
17507
17749
|
var init_downloads = () => {};
|
|
17508
17750
|
|
|
17509
17751
|
// src/lib/files-integration.ts
|
|
17510
|
-
import { join as
|
|
17511
|
-
import { mkdirSync as
|
|
17752
|
+
import { join as join10 } from "path";
|
|
17753
|
+
import { mkdirSync as mkdirSync8, copyFileSync as copyFileSync2 } from "fs";
|
|
17512
17754
|
import { homedir as homedir10 } from "os";
|
|
17513
17755
|
async function persistFile(localPath, opts) {
|
|
17514
17756
|
try {
|
|
@@ -17518,12 +17760,12 @@ async function persistFile(localPath, opts) {
|
|
|
17518
17760
|
return { id: ref.id, path: ref.path ?? localPath, permanent: true, provider: "open-files" };
|
|
17519
17761
|
}
|
|
17520
17762
|
} catch {}
|
|
17521
|
-
const dataDir = process.env["BROWSER_DATA_DIR"] ??
|
|
17763
|
+
const dataDir = process.env["BROWSER_DATA_DIR"] ?? join10(homedir10(), ".browser");
|
|
17522
17764
|
const date = new Date().toISOString().split("T")[0];
|
|
17523
|
-
const dir =
|
|
17524
|
-
|
|
17765
|
+
const dir = join10(dataDir, "persistent", date);
|
|
17766
|
+
mkdirSync8(dir, { recursive: true });
|
|
17525
17767
|
const filename = localPath.split("/").pop() ?? "file";
|
|
17526
|
-
const targetPath =
|
|
17768
|
+
const targetPath = join10(dir, filename);
|
|
17527
17769
|
copyFileSync2(localPath, targetPath);
|
|
17528
17770
|
return {
|
|
17529
17771
|
id: `local-${Date.now()}`,
|
|
@@ -18000,9 +18242,9 @@ async function tryReplayAuth(page, domain) {
|
|
|
18000
18242
|
return { replayed: false };
|
|
18001
18243
|
if (flow.storage_state_path) {
|
|
18002
18244
|
try {
|
|
18003
|
-
const { existsSync:
|
|
18004
|
-
if (
|
|
18005
|
-
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"));
|
|
18006
18248
|
if (state.cookies?.length) {
|
|
18007
18249
|
await page.context().addCookies(state.cookies);
|
|
18008
18250
|
await page.reload();
|
|
@@ -18051,82 +18293,6 @@ var init_auth_flow = __esm(() => {
|
|
|
18051
18293
|
];
|
|
18052
18294
|
});
|
|
18053
18295
|
|
|
18054
|
-
// src/lib/vision-fallback.ts
|
|
18055
|
-
var exports_vision_fallback = {};
|
|
18056
|
-
__export(exports_vision_fallback, {
|
|
18057
|
-
findElementByVision: () => findElementByVision,
|
|
18058
|
-
clickByVision: () => clickByVision
|
|
18059
|
-
});
|
|
18060
|
-
async function findElementByVision(page, description, opts) {
|
|
18061
|
-
const model = opts?.model ?? process.env["BROWSER_VISION_MODEL"] ?? DEFAULT_MODEL;
|
|
18062
|
-
const screenshot = await page.screenshot({ type: "jpeg", quality: 80 });
|
|
18063
|
-
const base64 = screenshot.toString("base64");
|
|
18064
|
-
const viewport = page.viewportSize() ?? { width: 1280, height: 720 };
|
|
18065
|
-
const apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
18066
|
-
if (!apiKey) {
|
|
18067
|
-
return { found: false, x: 0, y: 0, confidence: "none", description, model, error: "ANTHROPIC_API_KEY not set" };
|
|
18068
|
-
}
|
|
18069
|
-
try {
|
|
18070
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
18071
|
-
method: "POST",
|
|
18072
|
-
headers: {
|
|
18073
|
-
"content-type": "application/json",
|
|
18074
|
-
"x-api-key": apiKey,
|
|
18075
|
-
"anthropic-version": "2023-06-01"
|
|
18076
|
-
},
|
|
18077
|
-
body: JSON.stringify({
|
|
18078
|
-
model,
|
|
18079
|
-
max_tokens: 256,
|
|
18080
|
-
messages: [{
|
|
18081
|
-
role: "user",
|
|
18082
|
-
content: [
|
|
18083
|
-
{
|
|
18084
|
-
type: "image",
|
|
18085
|
-
source: { type: "base64", media_type: "image/jpeg", data: base64 }
|
|
18086
|
-
},
|
|
18087
|
-
{
|
|
18088
|
-
type: "text",
|
|
18089
|
-
text: `Find the element matching this description: "${description}"
|
|
18090
|
-
|
|
18091
|
-
The screenshot is ${viewport.width}x${viewport.height} pixels.
|
|
18092
|
-
|
|
18093
|
-
Reply with ONLY a JSON object (no markdown, no explanation):
|
|
18094
|
-
{"found": true, "x": <pixel_x>, "y": <pixel_y>, "confidence": "high|medium|low", "description": "<what you found>"}
|
|
18095
|
-
|
|
18096
|
-
If you cannot find the element:
|
|
18097
|
-
{"found": false, "x": 0, "y": 0, "confidence": "none", "description": "not found"}`
|
|
18098
|
-
}
|
|
18099
|
-
]
|
|
18100
|
-
}]
|
|
18101
|
-
})
|
|
18102
|
-
});
|
|
18103
|
-
const data = await response.json();
|
|
18104
|
-
const text = data.content?.[0]?.text ?? "";
|
|
18105
|
-
const jsonStr = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
18106
|
-
const result = JSON.parse(jsonStr);
|
|
18107
|
-
result.model = model;
|
|
18108
|
-
return result;
|
|
18109
|
-
} catch (err) {
|
|
18110
|
-
return {
|
|
18111
|
-
found: false,
|
|
18112
|
-
x: 0,
|
|
18113
|
-
y: 0,
|
|
18114
|
-
confidence: "none",
|
|
18115
|
-
description,
|
|
18116
|
-
model,
|
|
18117
|
-
error: err instanceof Error ? err.message : String(err)
|
|
18118
|
-
};
|
|
18119
|
-
}
|
|
18120
|
-
}
|
|
18121
|
-
async function clickByVision(page, description, opts) {
|
|
18122
|
-
const result = await findElementByVision(page, description, opts);
|
|
18123
|
-
if (result.found && result.x > 0 && result.y > 0) {
|
|
18124
|
-
await page.mouse.click(result.x, result.y);
|
|
18125
|
-
}
|
|
18126
|
-
return result;
|
|
18127
|
-
}
|
|
18128
|
-
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
18129
|
-
|
|
18130
18296
|
// src/lib/datasets.ts
|
|
18131
18297
|
var exports_datasets = {};
|
|
18132
18298
|
__export(exports_datasets, {
|
|
@@ -18138,8 +18304,8 @@ __export(exports_datasets, {
|
|
|
18138
18304
|
deleteDataset: () => deleteDataset
|
|
18139
18305
|
});
|
|
18140
18306
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
18141
|
-
import { writeFileSync as
|
|
18142
|
-
import { join as
|
|
18307
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
|
|
18308
|
+
import { join as join11 } from "path";
|
|
18143
18309
|
import { homedir as homedir11 } from "os";
|
|
18144
18310
|
function saveDataset(data) {
|
|
18145
18311
|
const db = getDatabase();
|
|
@@ -18178,14 +18344,14 @@ function exportDataset(name, format) {
|
|
|
18178
18344
|
const dataset = getDatasetByName(name);
|
|
18179
18345
|
if (!dataset)
|
|
18180
18346
|
throw new Error(`Dataset '${name}' not found`);
|
|
18181
|
-
const dir =
|
|
18182
|
-
|
|
18347
|
+
const dir = join11(process.env["BROWSER_DATA_DIR"] ?? join11(homedir11(), ".browser"), "exports");
|
|
18348
|
+
mkdirSync9(dir, { recursive: true });
|
|
18183
18349
|
const filename = `${name}.${format}`;
|
|
18184
|
-
const path =
|
|
18350
|
+
const path = join11(dir, filename);
|
|
18185
18351
|
if (format === "csv") {
|
|
18186
18352
|
const rows = dataset.data;
|
|
18187
18353
|
if (rows.length === 0) {
|
|
18188
|
-
|
|
18354
|
+
writeFileSync3(path, "");
|
|
18189
18355
|
return { path, size: 0 };
|
|
18190
18356
|
}
|
|
18191
18357
|
const headers = Object.keys(rows[0]);
|
|
@@ -18198,11 +18364,11 @@ function exportDataset(name, format) {
|
|
|
18198
18364
|
}
|
|
18199
18365
|
const content = csvLines.join(`
|
|
18200
18366
|
`);
|
|
18201
|
-
|
|
18367
|
+
writeFileSync3(path, content);
|
|
18202
18368
|
return { path, size: content.length };
|
|
18203
18369
|
} else {
|
|
18204
18370
|
const content = JSON.stringify(dataset.data, null, 2);
|
|
18205
|
-
|
|
18371
|
+
writeFileSync3(path, content);
|
|
18206
18372
|
return { path, size: content.length };
|
|
18207
18373
|
}
|
|
18208
18374
|
}
|
|
@@ -18320,11 +18486,11 @@ __export(exports_dist, {
|
|
|
18320
18486
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
18321
18487
|
});
|
|
18322
18488
|
import { Database as Database2 } from "bun:sqlite";
|
|
18323
|
-
import { existsSync as
|
|
18324
|
-
import { dirname, join as
|
|
18325
|
-
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";
|
|
18326
18492
|
import { homedir as homedir12 } from "os";
|
|
18327
|
-
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";
|
|
18328
18494
|
import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
18329
18495
|
import { homedir as homedir22 } from "os";
|
|
18330
18496
|
import { join as join32 } from "path";
|
|
@@ -18335,10 +18501,10 @@ function isInMemoryDb(path) {
|
|
|
18335
18501
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
18336
18502
|
}
|
|
18337
18503
|
function findNearestMementosDb(startDir) {
|
|
18338
|
-
let dir =
|
|
18504
|
+
let dir = resolve2(startDir);
|
|
18339
18505
|
while (true) {
|
|
18340
|
-
const candidate =
|
|
18341
|
-
if (
|
|
18506
|
+
const candidate = join12(dir, ".mementos", "mementos.db");
|
|
18507
|
+
if (existsSync7(candidate))
|
|
18342
18508
|
return candidate;
|
|
18343
18509
|
const parent = dirname(dir);
|
|
18344
18510
|
if (parent === dir)
|
|
@@ -18348,9 +18514,9 @@ function findNearestMementosDb(startDir) {
|
|
|
18348
18514
|
return null;
|
|
18349
18515
|
}
|
|
18350
18516
|
function findGitRoot(startDir) {
|
|
18351
|
-
let dir =
|
|
18517
|
+
let dir = resolve2(startDir);
|
|
18352
18518
|
while (true) {
|
|
18353
|
-
if (
|
|
18519
|
+
if (existsSync7(join12(dir, ".git")))
|
|
18354
18520
|
return dir;
|
|
18355
18521
|
const parent = dirname(dir);
|
|
18356
18522
|
if (parent === dir)
|
|
@@ -18370,18 +18536,18 @@ function getDbPath() {
|
|
|
18370
18536
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
18371
18537
|
const gitRoot = findGitRoot(cwd);
|
|
18372
18538
|
if (gitRoot) {
|
|
18373
|
-
return
|
|
18539
|
+
return join12(gitRoot, ".mementos", "mementos.db");
|
|
18374
18540
|
}
|
|
18375
18541
|
}
|
|
18376
18542
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
18377
|
-
return
|
|
18543
|
+
return join12(home, ".mementos", "mementos.db");
|
|
18378
18544
|
}
|
|
18379
18545
|
function ensureDir2(filePath) {
|
|
18380
18546
|
if (isInMemoryDb(filePath))
|
|
18381
18547
|
return;
|
|
18382
|
-
const dir = dirname(
|
|
18383
|
-
if (!
|
|
18384
|
-
|
|
18548
|
+
const dir = dirname(resolve2(filePath));
|
|
18549
|
+
if (!existsSync7(dir)) {
|
|
18550
|
+
mkdirSync10(dir, { recursive: true });
|
|
18385
18551
|
}
|
|
18386
18552
|
}
|
|
18387
18553
|
function getDatabase2(dbPath) {
|
|
@@ -20235,7 +20401,7 @@ function loadConfig() {
|
|
|
20235
20401
|
let fileConfig = {};
|
|
20236
20402
|
if (existsSync22(configPath)) {
|
|
20237
20403
|
try {
|
|
20238
|
-
const raw =
|
|
20404
|
+
const raw = readFileSync5(configPath, "utf-8");
|
|
20239
20405
|
fileConfig = JSON.parse(raw);
|
|
20240
20406
|
} catch {}
|
|
20241
20407
|
}
|
|
@@ -20268,7 +20434,7 @@ function readGlobalConfig() {
|
|
|
20268
20434
|
if (!existsSync22(p))
|
|
20269
20435
|
return {};
|
|
20270
20436
|
try {
|
|
20271
|
-
return JSON.parse(
|
|
20437
|
+
return JSON.parse(readFileSync5(p, "utf-8"));
|
|
20272
20438
|
} catch {
|
|
20273
20439
|
return {};
|
|
20274
20440
|
}
|
|
@@ -20276,7 +20442,7 @@ function readGlobalConfig() {
|
|
|
20276
20442
|
function writeGlobalConfig(data) {
|
|
20277
20443
|
const p = globalConfigPath();
|
|
20278
20444
|
ensureDir22(dirname2(p));
|
|
20279
|
-
|
|
20445
|
+
writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
|
|
20280
20446
|
}
|
|
20281
20447
|
function getActiveProfile() {
|
|
20282
20448
|
const envProfile = process.env["MEMENTOS_PROFILE"];
|
|
@@ -20298,7 +20464,7 @@ function listProfiles2() {
|
|
|
20298
20464
|
const dir = profilesDir();
|
|
20299
20465
|
if (!existsSync22(dir))
|
|
20300
20466
|
return [];
|
|
20301
|
-
return
|
|
20467
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".db")).map((f) => basename2(f, ".db")).sort();
|
|
20302
20468
|
}
|
|
20303
20469
|
function deleteProfile2(name) {
|
|
20304
20470
|
const dbPath = join22(profilesDir(), `${name}.db`);
|
|
@@ -22941,30 +23107,30 @@ __export(exports_dist2, {
|
|
|
22941
23107
|
acquireLock: () => acquireLock2
|
|
22942
23108
|
});
|
|
22943
23109
|
import { Database as Database3 } from "bun:sqlite";
|
|
22944
|
-
import { mkdirSync as
|
|
22945
|
-
import { join as
|
|
23110
|
+
import { mkdirSync as mkdirSync11 } from "fs";
|
|
23111
|
+
import { join as join13, dirname as dirname3 } from "path";
|
|
22946
23112
|
import { homedir as homedir13 } from "os";
|
|
22947
23113
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
22948
23114
|
import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
22949
23115
|
import { join as join33 } from "path";
|
|
22950
23116
|
import { homedir as homedir33 } from "os";
|
|
22951
|
-
import { readFileSync as
|
|
23117
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
22952
23118
|
import { join as join23 } from "path";
|
|
22953
23119
|
import { homedir as homedir23 } from "os";
|
|
22954
23120
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
22955
|
-
import { readFileSync as readFileSync23, writeFileSync as
|
|
23121
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync33 } from "fs";
|
|
22956
23122
|
import { join as join43, dirname as dirname22 } from "path";
|
|
22957
23123
|
import { homedir as homedir42 } from "os";
|
|
22958
23124
|
function getDbPath2() {
|
|
22959
23125
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
22960
23126
|
return process.env.CONVERSATIONS_DB_PATH;
|
|
22961
|
-
return
|
|
23127
|
+
return join13(homedir13(), ".conversations", "messages.db");
|
|
22962
23128
|
}
|
|
22963
23129
|
function getDb() {
|
|
22964
23130
|
if (db)
|
|
22965
23131
|
return db;
|
|
22966
23132
|
const dbPath = getDbPath2();
|
|
22967
|
-
|
|
23133
|
+
mkdirSync11(dirname3(dbPath), { recursive: true });
|
|
22968
23134
|
db = new Database3(dbPath, { create: true });
|
|
22969
23135
|
db.exec("PRAGMA journal_mode = WAL");
|
|
22970
23136
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -23206,7 +23372,7 @@ function loadConfig2() {
|
|
|
23206
23372
|
if (cachedConfig && now2 - configLoadedAt < CONFIG_CACHE_MS)
|
|
23207
23373
|
return cachedConfig;
|
|
23208
23374
|
try {
|
|
23209
|
-
const raw =
|
|
23375
|
+
const raw = readFileSync6(getConfigPath(), "utf-8");
|
|
23210
23376
|
cachedConfig = JSON.parse(raw);
|
|
23211
23377
|
configLoadedAt = now2;
|
|
23212
23378
|
return cachedConfig;
|
|
@@ -24123,7 +24289,7 @@ function useSpaceMessages(spaceName) {
|
|
|
24123
24289
|
}
|
|
24124
24290
|
function isNameTaken(name) {
|
|
24125
24291
|
try {
|
|
24126
|
-
const { getDb: getDb2 } = (init_db(),
|
|
24292
|
+
const { getDb: getDb2 } = (init_db(), __toCommonJS2(exports_db));
|
|
24127
24293
|
const db2 = getDb2();
|
|
24128
24294
|
const row = db2.prepare("SELECT agent FROM agent_presence WHERE agent = ?").get(name);
|
|
24129
24295
|
return !!row;
|
|
@@ -24152,7 +24318,7 @@ function getAutoName() {
|
|
|
24152
24318
|
cachedAutoName = name;
|
|
24153
24319
|
try {
|
|
24154
24320
|
mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
|
|
24155
|
-
|
|
24321
|
+
writeFileSync5(AGENT_ID_FILE, name + `
|
|
24156
24322
|
`, "utf-8");
|
|
24157
24323
|
} catch {}
|
|
24158
24324
|
return name;
|
|
@@ -24758,7 +24924,7 @@ function getGraphStats() {
|
|
|
24758
24924
|
map[r.relation] = r.c;
|
|
24759
24925
|
return { total_edges: total, by_relation: map };
|
|
24760
24926
|
}
|
|
24761
|
-
var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2,
|
|
24927
|
+
var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc2, __hasOwnProp2, __toESM2 = (mod, isNodeMode, target) => {
|
|
24762
24928
|
target = mod != null ? __create2(__getProtoOf2(mod)) : {};
|
|
24763
24929
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp3(target, "default", { value: mod, enumerable: true }) : target;
|
|
24764
24930
|
for (let key of __getOwnPropNames2(mod))
|
|
@@ -24768,17 +24934,17 @@ var __create2, __getProtoOf2, __defProp3, __getOwnPropNames2, __getOwnPropDesc,
|
|
|
24768
24934
|
enumerable: true
|
|
24769
24935
|
});
|
|
24770
24936
|
return to;
|
|
24771
|
-
},
|
|
24772
|
-
var entry =
|
|
24937
|
+
}, __moduleCache2, __toCommonJS2 = (from) => {
|
|
24938
|
+
var entry = __moduleCache2.get(from), desc;
|
|
24773
24939
|
if (entry)
|
|
24774
24940
|
return entry;
|
|
24775
24941
|
entry = __defProp3({}, "__esModule", { value: true });
|
|
24776
24942
|
if (from && typeof from === "object" || typeof from === "function")
|
|
24777
24943
|
__getOwnPropNames2(from).map((key) => !__hasOwnProp2.call(entry, key) && __defProp3(entry, key, {
|
|
24778
24944
|
get: () => from[key],
|
|
24779
|
-
enumerable: !(desc =
|
|
24945
|
+
enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
|
|
24780
24946
|
}));
|
|
24781
|
-
|
|
24947
|
+
__moduleCache2.set(from, entry);
|
|
24782
24948
|
return entry;
|
|
24783
24949
|
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __export3 = (target, all) => {
|
|
24784
24950
|
for (var name in all)
|
|
@@ -24794,9 +24960,9 @@ var init_dist2 = __esm(() => {
|
|
|
24794
24960
|
__getProtoOf2 = Object.getPrototypeOf;
|
|
24795
24961
|
__defProp3 = Object.defineProperty;
|
|
24796
24962
|
__getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
24797
|
-
|
|
24963
|
+
__getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
24798
24964
|
__hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
24799
|
-
|
|
24965
|
+
__moduleCache2 = /* @__PURE__ */ new WeakMap;
|
|
24800
24966
|
exports_db = {};
|
|
24801
24967
|
__export3(exports_db, {
|
|
24802
24968
|
getDbPath: () => getDbPath2,
|
|
@@ -27366,18 +27532,18 @@ __export(exports_dist3, {
|
|
|
27366
27532
|
AgentNotFoundError: () => AgentNotFoundError2
|
|
27367
27533
|
});
|
|
27368
27534
|
import { Database as Database4 } from "bun:sqlite";
|
|
27369
|
-
import { existsSync as
|
|
27370
|
-
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";
|
|
27371
27537
|
import { existsSync as existsSync33 } from "fs";
|
|
27372
27538
|
import { join as join34 } from "path";
|
|
27373
|
-
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";
|
|
27374
27540
|
import { join as join24 } from "path";
|
|
27375
27541
|
import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
27376
27542
|
import { join as join44 } from "path";
|
|
27377
27543
|
import { existsSync as existsSync52 } from "fs";
|
|
27378
27544
|
import { join as join52 } from "path";
|
|
27379
27545
|
import { readFileSync as readFileSync33, statSync as statSync22 } from "fs";
|
|
27380
|
-
import { relative, resolve as
|
|
27546
|
+
import { relative, resolve as resolve23, join as join62 } from "path";
|
|
27381
27547
|
import { execSync as execSync2 } from "child_process";
|
|
27382
27548
|
|
|
27383
27549
|
class TodosClient {
|
|
@@ -27600,8 +27766,8 @@ function isInMemoryDb2(path) {
|
|
|
27600
27766
|
function findNearestTodosDb(startDir) {
|
|
27601
27767
|
let dir = resolve3(startDir);
|
|
27602
27768
|
while (true) {
|
|
27603
|
-
const candidate =
|
|
27604
|
-
if (
|
|
27769
|
+
const candidate = join14(dir, ".todos", "todos.db");
|
|
27770
|
+
if (existsSync8(candidate))
|
|
27605
27771
|
return candidate;
|
|
27606
27772
|
const parent = dirname5(dir);
|
|
27607
27773
|
if (parent === dir)
|
|
@@ -27613,7 +27779,7 @@ function findNearestTodosDb(startDir) {
|
|
|
27613
27779
|
function findGitRoot2(startDir) {
|
|
27614
27780
|
let dir = resolve3(startDir);
|
|
27615
27781
|
while (true) {
|
|
27616
|
-
if (
|
|
27782
|
+
if (existsSync8(join14(dir, ".git")))
|
|
27617
27783
|
return dir;
|
|
27618
27784
|
const parent = dirname5(dir);
|
|
27619
27785
|
if (parent === dir)
|
|
@@ -27633,18 +27799,18 @@ function getDbPath3() {
|
|
|
27633
27799
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
27634
27800
|
const gitRoot = findGitRoot2(cwd);
|
|
27635
27801
|
if (gitRoot) {
|
|
27636
|
-
return
|
|
27802
|
+
return join14(gitRoot, ".todos", "todos.db");
|
|
27637
27803
|
}
|
|
27638
27804
|
}
|
|
27639
27805
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
27640
|
-
return
|
|
27806
|
+
return join14(home, ".todos", "todos.db");
|
|
27641
27807
|
}
|
|
27642
27808
|
function ensureDir3(filePath) {
|
|
27643
27809
|
if (isInMemoryDb2(filePath))
|
|
27644
27810
|
return;
|
|
27645
27811
|
const dir = dirname5(resolve3(filePath));
|
|
27646
|
-
if (!
|
|
27647
|
-
|
|
27812
|
+
if (!existsSync8(dir)) {
|
|
27813
|
+
mkdirSync12(dir, { recursive: true });
|
|
27648
27814
|
}
|
|
27649
27815
|
}
|
|
27650
27816
|
function getDatabase3(dbPath) {
|
|
@@ -28110,28 +28276,28 @@ function ensureDir23(dir) {
|
|
|
28110
28276
|
function listJsonFiles(dir) {
|
|
28111
28277
|
if (!existsSync23(dir))
|
|
28112
28278
|
return [];
|
|
28113
|
-
return
|
|
28279
|
+
return readdirSync5(dir).filter((f) => f.endsWith(".json"));
|
|
28114
28280
|
}
|
|
28115
28281
|
function readJsonFile(path) {
|
|
28116
28282
|
try {
|
|
28117
|
-
return JSON.parse(
|
|
28283
|
+
return JSON.parse(readFileSync7(path, "utf-8"));
|
|
28118
28284
|
} catch {
|
|
28119
28285
|
return null;
|
|
28120
28286
|
}
|
|
28121
28287
|
}
|
|
28122
28288
|
function writeJsonFile(path, data) {
|
|
28123
|
-
|
|
28289
|
+
writeFileSync6(path, JSON.stringify(data, null, 2) + `
|
|
28124
28290
|
`);
|
|
28125
28291
|
}
|
|
28126
28292
|
function readHighWaterMark(dir) {
|
|
28127
28293
|
const path = join24(dir, ".highwatermark");
|
|
28128
28294
|
if (!existsSync23(path))
|
|
28129
28295
|
return 1;
|
|
28130
|
-
const val = parseInt(
|
|
28296
|
+
const val = parseInt(readFileSync7(path, "utf-8").trim(), 10);
|
|
28131
28297
|
return isNaN(val) ? 1 : val;
|
|
28132
28298
|
}
|
|
28133
28299
|
function writeHighWaterMark(dir, value) {
|
|
28134
|
-
|
|
28300
|
+
writeFileSync6(join24(dir, ".highwatermark"), String(value));
|
|
28135
28301
|
}
|
|
28136
28302
|
function getFileMtimeMs(path) {
|
|
28137
28303
|
try {
|
|
@@ -31493,7 +31659,7 @@ function collectFiles(basePath, extensions) {
|
|
|
31493
31659
|
return files.sort();
|
|
31494
31660
|
}
|
|
31495
31661
|
function extractTodos(options, db2) {
|
|
31496
|
-
const basePath =
|
|
31662
|
+
const basePath = resolve23(options.path);
|
|
31497
31663
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
31498
31664
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
31499
31665
|
const files = collectFiles(basePath, extensions);
|
|
@@ -31502,7 +31668,7 @@ function extractTodos(options, db2) {
|
|
|
31502
31668
|
const fullPath = statSync22(basePath).isFile() ? basePath : join62(basePath, file);
|
|
31503
31669
|
try {
|
|
31504
31670
|
const source = readFileSync33(fullPath, "utf-8");
|
|
31505
|
-
const relPath = statSync22(basePath).isFile() ? relative(
|
|
31671
|
+
const relPath = statSync22(basePath).isFile() ? relative(resolve23(basePath, ".."), fullPath) : file;
|
|
31506
31672
|
const comments = extractFromSource(source, relPath, tags);
|
|
31507
31673
|
allComments.push(...comments);
|
|
31508
31674
|
} catch {}
|
|
@@ -32721,8 +32887,8 @@ __export(exports_dist4, {
|
|
|
32721
32887
|
CATEGORIES: () => CATEGORIES,
|
|
32722
32888
|
AGENT_TARGETS: () => AGENT_TARGETS
|
|
32723
32889
|
});
|
|
32724
|
-
import { existsSync as
|
|
32725
|
-
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";
|
|
32726
32892
|
import { homedir as homedir14 } from "os";
|
|
32727
32893
|
import { fileURLToPath } from "url";
|
|
32728
32894
|
import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
|
|
@@ -32834,35 +33000,35 @@ function normalizeSkillName(name) {
|
|
|
32834
33000
|
function findSkillsDir() {
|
|
32835
33001
|
let dir = __dirname2;
|
|
32836
33002
|
for (let i = 0;i < 5; i++) {
|
|
32837
|
-
const candidate =
|
|
32838
|
-
if (
|
|
33003
|
+
const candidate = join15(dir, "skills");
|
|
33004
|
+
if (existsSync9(candidate)) {
|
|
32839
33005
|
return candidate;
|
|
32840
33006
|
}
|
|
32841
33007
|
dir = dirname6(dir);
|
|
32842
33008
|
}
|
|
32843
|
-
return
|
|
33009
|
+
return join15(__dirname2, "..", "skills");
|
|
32844
33010
|
}
|
|
32845
33011
|
function getSkillPath(name) {
|
|
32846
33012
|
const skillName = normalizeSkillName(name);
|
|
32847
|
-
return
|
|
33013
|
+
return join15(SKILLS_DIR, skillName);
|
|
32848
33014
|
}
|
|
32849
33015
|
function skillExists(name) {
|
|
32850
|
-
return
|
|
33016
|
+
return existsSync9(getSkillPath(name));
|
|
32851
33017
|
}
|
|
32852
33018
|
function installSkill(name, options = {}) {
|
|
32853
33019
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
32854
33020
|
const skillName = normalizeSkillName(name);
|
|
32855
33021
|
const sourcePath = getSkillPath(name);
|
|
32856
|
-
const destDir =
|
|
32857
|
-
const destPath =
|
|
32858
|
-
if (!
|
|
33022
|
+
const destDir = join15(targetDir, ".skills");
|
|
33023
|
+
const destPath = join15(destDir, skillName);
|
|
33024
|
+
if (!existsSync9(sourcePath)) {
|
|
32859
33025
|
return {
|
|
32860
33026
|
skill: name,
|
|
32861
33027
|
success: false,
|
|
32862
33028
|
error: `Skill '${name}' not found`
|
|
32863
33029
|
};
|
|
32864
33030
|
}
|
|
32865
|
-
if (
|
|
33031
|
+
if (existsSync9(destPath) && !overwrite) {
|
|
32866
33032
|
return {
|
|
32867
33033
|
skill: name,
|
|
32868
33034
|
success: false,
|
|
@@ -32871,10 +33037,10 @@ function installSkill(name, options = {}) {
|
|
|
32871
33037
|
};
|
|
32872
33038
|
}
|
|
32873
33039
|
try {
|
|
32874
|
-
if (!
|
|
32875
|
-
|
|
33040
|
+
if (!existsSync9(destDir)) {
|
|
33041
|
+
mkdirSync13(destDir, { recursive: true });
|
|
32876
33042
|
}
|
|
32877
|
-
if (
|
|
33043
|
+
if (existsSync9(destPath) && overwrite) {
|
|
32878
33044
|
rmSync2(destPath, { recursive: true, force: true });
|
|
32879
33045
|
}
|
|
32880
33046
|
cpSync(sourcePath, destPath, {
|
|
@@ -32913,10 +33079,10 @@ function installSkills(names, options = {}) {
|
|
|
32913
33079
|
return names.map((name) => installSkill(name, options));
|
|
32914
33080
|
}
|
|
32915
33081
|
function updateSkillsIndex(skillsDir) {
|
|
32916
|
-
const indexPath =
|
|
33082
|
+
const indexPath = join15(skillsDir, "index.ts");
|
|
32917
33083
|
const meta = loadMeta(skillsDir);
|
|
32918
33084
|
const disabledSet = new Set(meta.disabled || []);
|
|
32919
|
-
const skills =
|
|
33085
|
+
const skills = readdirSync6(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
|
|
32920
33086
|
const exports = skills.map((s) => {
|
|
32921
33087
|
const name = s.replace("skill-", "").replace(/-/g, "_");
|
|
32922
33088
|
return `export * as ${name} from './${s}/src/index.js';`;
|
|
@@ -32929,31 +33095,31 @@ function updateSkillsIndex(skillsDir) {
|
|
|
32929
33095
|
|
|
32930
33096
|
${exports}
|
|
32931
33097
|
`;
|
|
32932
|
-
|
|
33098
|
+
writeFileSync7(indexPath, content);
|
|
32933
33099
|
}
|
|
32934
33100
|
function getMetaPath(skillsDir) {
|
|
32935
|
-
return
|
|
33101
|
+
return join15(skillsDir, ".meta.json");
|
|
32936
33102
|
}
|
|
32937
33103
|
function loadMeta(skillsDir) {
|
|
32938
33104
|
const metaPath2 = getMetaPath(skillsDir);
|
|
32939
|
-
if (
|
|
33105
|
+
if (existsSync9(metaPath2)) {
|
|
32940
33106
|
try {
|
|
32941
|
-
return JSON.parse(
|
|
33107
|
+
return JSON.parse(readFileSync8(metaPath2, "utf-8"));
|
|
32942
33108
|
} catch {}
|
|
32943
33109
|
}
|
|
32944
33110
|
return { skills: {} };
|
|
32945
33111
|
}
|
|
32946
33112
|
function saveMeta(skillsDir, meta) {
|
|
32947
|
-
|
|
33113
|
+
writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
|
|
32948
33114
|
}
|
|
32949
33115
|
function recordInstall(skillsDir, name) {
|
|
32950
33116
|
const meta = loadMeta(skillsDir);
|
|
32951
33117
|
const skillName = normalizeSkillName(name);
|
|
32952
33118
|
let version = "unknown";
|
|
32953
33119
|
try {
|
|
32954
|
-
const pkgPath =
|
|
32955
|
-
if (
|
|
32956
|
-
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"));
|
|
32957
33123
|
version = pkg.version || "unknown";
|
|
32958
33124
|
}
|
|
32959
33125
|
} catch {}
|
|
@@ -32966,12 +33132,12 @@ function recordRemove(skillsDir, name) {
|
|
|
32966
33132
|
saveMeta(skillsDir, meta);
|
|
32967
33133
|
}
|
|
32968
33134
|
function getInstallMeta(targetDir = process.cwd()) {
|
|
32969
|
-
return loadMeta(
|
|
33135
|
+
return loadMeta(join15(targetDir, ".skills"));
|
|
32970
33136
|
}
|
|
32971
33137
|
function disableSkill(name, targetDir = process.cwd()) {
|
|
32972
|
-
const skillsDir =
|
|
33138
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
32973
33139
|
const skillName = normalizeSkillName(name);
|
|
32974
|
-
if (!
|
|
33140
|
+
if (!existsSync9(join15(skillsDir, skillName)))
|
|
32975
33141
|
return false;
|
|
32976
33142
|
const meta = loadMeta(skillsDir);
|
|
32977
33143
|
const disabled = new Set(meta.disabled || []);
|
|
@@ -32984,7 +33150,7 @@ function disableSkill(name, targetDir = process.cwd()) {
|
|
|
32984
33150
|
return true;
|
|
32985
33151
|
}
|
|
32986
33152
|
function enableSkill(name, targetDir = process.cwd()) {
|
|
32987
|
-
const skillsDir =
|
|
33153
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
32988
33154
|
const meta = loadMeta(skillsDir);
|
|
32989
33155
|
const disabled = new Set(meta.disabled || []);
|
|
32990
33156
|
if (!disabled.has(name))
|
|
@@ -32996,24 +33162,24 @@ function enableSkill(name, targetDir = process.cwd()) {
|
|
|
32996
33162
|
return true;
|
|
32997
33163
|
}
|
|
32998
33164
|
function getDisabledSkills(targetDir = process.cwd()) {
|
|
32999
|
-
const meta = loadMeta(
|
|
33165
|
+
const meta = loadMeta(join15(targetDir, ".skills"));
|
|
33000
33166
|
return meta.disabled || [];
|
|
33001
33167
|
}
|
|
33002
33168
|
function getInstalledSkills(targetDir = process.cwd()) {
|
|
33003
|
-
const skillsDir =
|
|
33004
|
-
if (!
|
|
33169
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
33170
|
+
if (!existsSync9(skillsDir)) {
|
|
33005
33171
|
return [];
|
|
33006
33172
|
}
|
|
33007
|
-
return
|
|
33008
|
-
const fullPath =
|
|
33173
|
+
return readdirSync6(skillsDir).filter((f) => {
|
|
33174
|
+
const fullPath = join15(skillsDir, f);
|
|
33009
33175
|
return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
|
|
33010
33176
|
}).map((f) => f.replace("skill-", ""));
|
|
33011
33177
|
}
|
|
33012
33178
|
function removeSkill(name, targetDir = process.cwd()) {
|
|
33013
33179
|
const skillName = normalizeSkillName(name);
|
|
33014
|
-
const skillsDir =
|
|
33015
|
-
const skillPath =
|
|
33016
|
-
if (!
|
|
33180
|
+
const skillsDir = join15(targetDir, ".skills");
|
|
33181
|
+
const skillPath = join15(skillsDir, skillName);
|
|
33182
|
+
if (!existsSync9(skillPath)) {
|
|
33017
33183
|
return false;
|
|
33018
33184
|
}
|
|
33019
33185
|
rmSync2(skillPath, { recursive: true, force: true });
|
|
@@ -33024,25 +33190,25 @@ function removeSkill(name, targetDir = process.cwd()) {
|
|
|
33024
33190
|
function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
33025
33191
|
const agentDir = `.${agent}`;
|
|
33026
33192
|
if (scope === "project") {
|
|
33027
|
-
return
|
|
33193
|
+
return join15(projectDir || process.cwd(), agentDir, "skills");
|
|
33028
33194
|
}
|
|
33029
|
-
return
|
|
33195
|
+
return join15(homedir14(), agentDir, "skills");
|
|
33030
33196
|
}
|
|
33031
33197
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
33032
33198
|
const skillName = normalizeSkillName(name);
|
|
33033
|
-
return
|
|
33199
|
+
return join15(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
33034
33200
|
}
|
|
33035
33201
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
33036
33202
|
const { agent, scope = "global", projectDir } = options;
|
|
33037
33203
|
const skillName = normalizeSkillName(name);
|
|
33038
33204
|
const sourcePath = getSkillPath(name);
|
|
33039
|
-
if (!
|
|
33205
|
+
if (!existsSync9(sourcePath)) {
|
|
33040
33206
|
return { skill: name, success: false, error: `Skill '${name}' not found` };
|
|
33041
33207
|
}
|
|
33042
33208
|
let skillMdContent = null;
|
|
33043
|
-
const skillMdPath =
|
|
33044
|
-
if (
|
|
33045
|
-
skillMdContent =
|
|
33209
|
+
const skillMdPath = join15(sourcePath, "SKILL.md");
|
|
33210
|
+
if (existsSync9(skillMdPath)) {
|
|
33211
|
+
skillMdContent = readFileSync8(skillMdPath, "utf-8");
|
|
33046
33212
|
} else if (generateSkillMd) {
|
|
33047
33213
|
skillMdContent = generateSkillMd(name);
|
|
33048
33214
|
}
|
|
@@ -33051,8 +33217,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
33051
33217
|
}
|
|
33052
33218
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
33053
33219
|
if (scope === "global") {
|
|
33054
|
-
const agentBaseDir2 =
|
|
33055
|
-
if (!
|
|
33220
|
+
const agentBaseDir2 = join15(homedir14(), `.${agent}`);
|
|
33221
|
+
if (!existsSync9(agentBaseDir2)) {
|
|
33056
33222
|
const agentLabels = {
|
|
33057
33223
|
claude: "Claude Code",
|
|
33058
33224
|
codex: "Codex CLI",
|
|
@@ -33075,8 +33241,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
33075
33241
|
}
|
|
33076
33242
|
}
|
|
33077
33243
|
try {
|
|
33078
|
-
|
|
33079
|
-
|
|
33244
|
+
mkdirSync13(destDir, { recursive: true });
|
|
33245
|
+
writeFileSync7(join15(destDir, "SKILL.md"), skillMdContent);
|
|
33080
33246
|
return { skill: name, success: true, path: destDir };
|
|
33081
33247
|
} catch (error) {
|
|
33082
33248
|
return {
|
|
@@ -33089,7 +33255,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
33089
33255
|
function removeSkillForAgent(name, options) {
|
|
33090
33256
|
const { agent, scope = "global", projectDir } = options;
|
|
33091
33257
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
33092
|
-
if (!
|
|
33258
|
+
if (!existsSync9(destDir)) {
|
|
33093
33259
|
return false;
|
|
33094
33260
|
}
|
|
33095
33261
|
rmSync2(destDir, { recursive: true, force: true });
|
|
@@ -35434,8 +35600,8 @@ var init_ai_task = () => {};
|
|
|
35434
35600
|
var exports_mcp = {};
|
|
35435
35601
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35436
35602
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
35437
|
-
import { readFileSync as
|
|
35438
|
-
import { join as
|
|
35603
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
35604
|
+
import { join as join16 } from "path";
|
|
35439
35605
|
function json(data) {
|
|
35440
35606
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
35441
35607
|
}
|
|
@@ -35500,7 +35666,7 @@ var init_mcp = __esm(async () => {
|
|
|
35500
35666
|
init_dialogs();
|
|
35501
35667
|
init_profiles();
|
|
35502
35668
|
init_types();
|
|
35503
|
-
_pkg = JSON.parse(
|
|
35669
|
+
_pkg = JSON.parse(readFileSync9(join16(import.meta.dir, "../../package.json"), "utf8"));
|
|
35504
35670
|
networkLogCleanup = new Map;
|
|
35505
35671
|
consoleCaptureCleanup = new Map;
|
|
35506
35672
|
harCaptures = new Map;
|
|
@@ -37166,14 +37332,17 @@ var init_mcp = __esm(async () => {
|
|
|
37166
37332
|
return err(e);
|
|
37167
37333
|
}
|
|
37168
37334
|
});
|
|
37169
|
-
server.tool("browser_script_run", "Run a saved
|
|
37170
|
-
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"),
|
|
37171
37337
|
session_id: exports_external.string().optional(),
|
|
37172
|
-
|
|
37173
|
-
|
|
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 }) => {
|
|
37174
37341
|
try {
|
|
37175
|
-
const {
|
|
37176
|
-
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);
|
|
37177
37346
|
if (!script)
|
|
37178
37347
|
return err(new Error(`Script '${name}' not found. Use browser_script_list to see available scripts.`));
|
|
37179
37348
|
let sid;
|
|
@@ -37182,55 +37351,72 @@ var init_mcp = __esm(async () => {
|
|
|
37182
37351
|
sid = resolveSessionId(session_id);
|
|
37183
37352
|
page = getSessionPage(sid);
|
|
37184
37353
|
} else {
|
|
37185
|
-
const result = await createSession2({ headless: true });
|
|
37354
|
+
const result = await createSession2({ engine: engine ?? "auto", headless: true });
|
|
37186
37355
|
sid = result.session.id;
|
|
37187
37356
|
page = result.page;
|
|
37188
37357
|
}
|
|
37189
|
-
const
|
|
37190
|
-
|
|
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." });
|
|
37191
37361
|
} catch (e) {
|
|
37192
37362
|
return err(e);
|
|
37193
37363
|
}
|
|
37194
37364
|
});
|
|
37195
|
-
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 }) => {
|
|
37196
37366
|
try {
|
|
37197
|
-
const {
|
|
37198
|
-
const
|
|
37199
|
-
if (!
|
|
37200
|
-
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`));
|
|
37201
37371
|
return json({
|
|
37202
|
-
status:
|
|
37203
|
-
progress: `${
|
|
37204
|
-
current_step:
|
|
37205
|
-
steps_log:
|
|
37206
|
-
|
|
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
|
|
37207
37379
|
});
|
|
37208
37380
|
} catch (e) {
|
|
37209
37381
|
return err(e);
|
|
37210
37382
|
}
|
|
37211
37383
|
});
|
|
37212
|
-
server.tool("browser_script_list", "List all saved
|
|
37384
|
+
server.tool("browser_script_list", "List all saved scripts", {}, async () => {
|
|
37213
37385
|
try {
|
|
37214
|
-
const { listScripts: listScripts2 } = await Promise.resolve().then(() => (
|
|
37215
|
-
|
|
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 });
|
|
37216
37390
|
} catch (e) {
|
|
37217
37391
|
return err(e);
|
|
37218
37392
|
}
|
|
37219
37393
|
});
|
|
37220
|
-
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 }) => {
|
|
37221
37407
|
try {
|
|
37222
|
-
const {
|
|
37223
|
-
const script =
|
|
37224
|
-
const
|
|
37225
|
-
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 });
|
|
37226
37412
|
} catch (e) {
|
|
37227
37413
|
return err(e);
|
|
37228
37414
|
}
|
|
37229
37415
|
});
|
|
37230
|
-
server.tool("browser_script_delete", "Delete a saved
|
|
37416
|
+
server.tool("browser_script_delete", "Delete a saved script", { name: exports_external.string() }, async ({ name }) => {
|
|
37231
37417
|
try {
|
|
37232
|
-
const {
|
|
37233
|
-
return json({ deleted:
|
|
37418
|
+
const { deleteScriptByName: deleteScriptByName2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
37419
|
+
return json({ deleted: deleteScriptByName2(name) });
|
|
37234
37420
|
} catch (e) {
|
|
37235
37421
|
return err(e);
|
|
37236
37422
|
}
|
|
@@ -38003,8 +38189,8 @@ var init_snapshots = __esm(() => {
|
|
|
38003
38189
|
|
|
38004
38190
|
// src/server/index.ts
|
|
38005
38191
|
var exports_server = {};
|
|
38006
|
-
import { join as
|
|
38007
|
-
import { existsSync as
|
|
38192
|
+
import { join as join17 } from "path";
|
|
38193
|
+
import { existsSync as existsSync10 } from "fs";
|
|
38008
38194
|
function ok(data, status = 200) {
|
|
38009
38195
|
return new Response(JSON.stringify(data), {
|
|
38010
38196
|
status,
|
|
@@ -38263,14 +38449,14 @@ var init_server = __esm(() => {
|
|
|
38263
38449
|
if (path.match(/^\/api\/gallery\/([^/]+)\/thumbnail$/) && method === "GET") {
|
|
38264
38450
|
const id = path.split("/")[3];
|
|
38265
38451
|
const entry = getEntry(id);
|
|
38266
|
-
if (!entry?.thumbnail_path || !
|
|
38452
|
+
if (!entry?.thumbnail_path || !existsSync10(entry.thumbnail_path))
|
|
38267
38453
|
return notFound("Thumbnail not found");
|
|
38268
38454
|
return new Response(Bun.file(entry.thumbnail_path), { headers: { ...CORS_HEADERS } });
|
|
38269
38455
|
}
|
|
38270
38456
|
if (path.match(/^\/api\/gallery\/([^/]+)\/image$/) && method === "GET") {
|
|
38271
38457
|
const id = path.split("/")[3];
|
|
38272
38458
|
const entry = getEntry(id);
|
|
38273
|
-
if (!entry?.path || !
|
|
38459
|
+
if (!entry?.path || !existsSync10(entry.path))
|
|
38274
38460
|
return notFound("Image not found");
|
|
38275
38461
|
return new Response(Bun.file(entry.path), { headers: { ...CORS_HEADERS } });
|
|
38276
38462
|
}
|
|
@@ -38298,7 +38484,7 @@ var init_server = __esm(() => {
|
|
|
38298
38484
|
if (path.match(/^\/api\/downloads\/([^/]+)\/raw$/) && method === "GET") {
|
|
38299
38485
|
const id = path.split("/")[3];
|
|
38300
38486
|
const file = getDownload(id);
|
|
38301
|
-
if (!file || !
|
|
38487
|
+
if (!file || !existsSync10(file.path))
|
|
38302
38488
|
return notFound("Download not found");
|
|
38303
38489
|
return new Response(Bun.file(file.path), { headers: { ...CORS_HEADERS } });
|
|
38304
38490
|
}
|
|
@@ -38306,13 +38492,13 @@ var init_server = __esm(() => {
|
|
|
38306
38492
|
const id = path.split("/")[3];
|
|
38307
38493
|
return ok({ deleted: deleteDownload(id) });
|
|
38308
38494
|
}
|
|
38309
|
-
const dashboardDist =
|
|
38310
|
-
if (
|
|
38311
|
-
const filePath = path === "/" ?
|
|
38312
|
-
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)) {
|
|
38313
38499
|
return new Response(Bun.file(filePath), { headers: CORS_HEADERS });
|
|
38314
38500
|
}
|
|
38315
|
-
return new Response(Bun.file(
|
|
38501
|
+
return new Response(Bun.file(join17(dashboardDist, "index.html")), { headers: CORS_HEADERS });
|
|
38316
38502
|
}
|
|
38317
38503
|
if (path === "/" || path === "") {
|
|
38318
38504
|
return new Response("@hasna/browser REST API running. Dashboard not built.", {
|
|
@@ -38354,10 +38540,10 @@ init_projects();
|
|
|
38354
38540
|
init_recorder();
|
|
38355
38541
|
init_recordings();
|
|
38356
38542
|
init_lightpanda();
|
|
38357
|
-
import { readFileSync as
|
|
38358
|
-
import { join as
|
|
38543
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
38544
|
+
import { join as join18 } from "path";
|
|
38359
38545
|
import chalk from "chalk";
|
|
38360
|
-
var pkg = JSON.parse(
|
|
38546
|
+
var pkg = JSON.parse(readFileSync10(join18(import.meta.dir, "../../package.json"), "utf8"));
|
|
38361
38547
|
var program2 = new Command;
|
|
38362
38548
|
program2.name("browser").description("@hasna/browser \u2014 general-purpose browser agent CLI").version(pkg.version);
|
|
38363
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) => {
|
|
@@ -38804,18 +38990,19 @@ program2.command("login <url>").description("Login to a site: detect form, fill
|
|
|
38804
38990
|
if (!opts.headed)
|
|
38805
38991
|
await closeSession2(session.id);
|
|
38806
38992
|
});
|
|
38807
|
-
var scriptCmd = program2.command("script").description("Manage
|
|
38808
|
-
scriptCmd.command("run <name>").description("Run a saved
|
|
38809
|
-
const {
|
|
38810
|
-
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);
|
|
38811
38999
|
if (!script) {
|
|
38812
|
-
console.log(chalk.red(`Script '${name}' not found
|
|
39000
|
+
console.log(chalk.red(`Script '${name}' not found.`));
|
|
38813
39001
|
return;
|
|
38814
39002
|
}
|
|
38815
39003
|
const { session, page } = await createSession2({ engine: opts.engine, headless: !opts.headed });
|
|
39004
|
+
const steps = getSteps2(script.id);
|
|
38816
39005
|
const overrides = {};
|
|
38817
|
-
if (opts.email)
|
|
38818
|
-
overrides.email = opts.email;
|
|
38819
39006
|
if (opts.var) {
|
|
38820
39007
|
for (const pair of opts.var) {
|
|
38821
39008
|
const [k, ...v] = pair.split("=");
|
|
@@ -38824,83 +39011,92 @@ scriptCmd.command("run <name>").description("Run a saved login script").option("
|
|
|
38824
39011
|
}
|
|
38825
39012
|
}
|
|
38826
39013
|
if (!opts.json) {
|
|
38827
|
-
console.log(chalk.gray(`Running
|
|
38828
|
-
console.log(chalk.gray(` Domain: ${script.domain}`));
|
|
39014
|
+
console.log(chalk.gray(`Running: ${script.name} (${steps.length} steps)`));
|
|
38829
39015
|
if (script.description)
|
|
38830
39016
|
console.log(chalk.gray(` ${script.description}
|
|
38831
39017
|
`));
|
|
38832
39018
|
}
|
|
38833
|
-
const result = await
|
|
39019
|
+
const result = await executeScriptSync2(script.id, page, overrides);
|
|
38834
39020
|
if (opts.json) {
|
|
38835
39021
|
console.log(JSON.stringify({ ...result, session_id: session.id }));
|
|
38836
39022
|
} else {
|
|
38837
39023
|
if (result.success) {
|
|
38838
39024
|
console.log(chalk.green(`
|
|
38839
|
-
\u2713
|
|
39025
|
+
\u2713 Completed (${result.steps_executed} steps, ${result.duration_ms}ms)`));
|
|
38840
39026
|
} else {
|
|
38841
39027
|
console.log(chalk.red(`
|
|
38842
|
-
\u2717
|
|
39028
|
+
\u2717 Failed (${result.steps_failed}/${result.steps_executed} failed)`));
|
|
38843
39029
|
result.errors.forEach((e) => console.log(chalk.red(` ${e}`)));
|
|
38844
39030
|
}
|
|
38845
|
-
console.log(chalk.gray(` Session: ${session.id}`));
|
|
38846
|
-
console.log(chalk.gray(` URL: ${result.variables.current_url ?? page.url()}`));
|
|
38847
39031
|
}
|
|
38848
39032
|
if (!opts.headed)
|
|
38849
39033
|
await closeSession2(session.id);
|
|
38850
39034
|
});
|
|
38851
|
-
scriptCmd.command("list").description("List saved
|
|
38852
|
-
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();
|
|
38853
39038
|
const scripts = listScripts2();
|
|
38854
39039
|
if (opts.json) {
|
|
38855
39040
|
console.log(JSON.stringify(scripts, null, 2));
|
|
38856
39041
|
} else if (scripts.length === 0) {
|
|
38857
|
-
console.log(chalk.gray("No scripts
|
|
39042
|
+
console.log(chalk.gray("No scripts. Import with: browser script import <file.json>"));
|
|
38858
39043
|
} else {
|
|
38859
39044
|
scripts.forEach((s) => {
|
|
38860
|
-
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}`);
|
|
38861
39046
|
if (s.description)
|
|
38862
39047
|
console.log(chalk.gray(` ${s.description}`));
|
|
38863
39048
|
});
|
|
38864
39049
|
}
|
|
38865
39050
|
});
|
|
38866
|
-
scriptCmd.command("
|
|
38867
|
-
const {
|
|
38868
|
-
|
|
38869
|
-
|
|
38870
|
-
|
|
38871
|
-
console.log(chalk.green(`\u2713 Script saved: ${script.name}`));
|
|
38872
|
-
console.log(chalk.gray(` Domain: ${script.domain}`));
|
|
38873
|
-
console.log(chalk.gray(` Steps: ${script.steps.length}`));
|
|
38874
|
-
console.log(chalk.gray(` Path: ${path}`));
|
|
38875
|
-
console.log(chalk.gray(` Run with: browser script run ${script.name}`));
|
|
38876
|
-
} catch (err2) {
|
|
38877
|
-
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;
|
|
38878
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)`));
|
|
38879
39072
|
});
|
|
38880
39073
|
scriptCmd.command("show <name>").description("Show script details").action(async (name) => {
|
|
38881
|
-
const {
|
|
38882
|
-
|
|
39074
|
+
const { getScriptByName: getScriptByName2, getSteps: getSteps2, migrateJsonScripts: migrateJsonScripts2 } = await Promise.resolve().then(() => (init_scripts(), exports_scripts));
|
|
39075
|
+
migrateJsonScripts2();
|
|
39076
|
+
const script = getScriptByName2(name);
|
|
38883
39077
|
if (!script) {
|
|
38884
39078
|
console.log(chalk.red(`Script '${name}' not found`));
|
|
38885
39079
|
return;
|
|
38886
39080
|
}
|
|
39081
|
+
const steps = getSteps2(script.id);
|
|
38887
39082
|
console.log(chalk.bold(`${script.name} (${script.domain})
|
|
38888
39083
|
`));
|
|
38889
39084
|
if (script.description)
|
|
38890
39085
|
console.log(chalk.gray(` ${script.description}
|
|
38891
39086
|
`));
|
|
38892
|
-
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"}
|
|
38893
39089
|
`));
|
|
38894
|
-
|
|
38895
|
-
const
|
|
38896
|
-
const detail = s.url ?? s.selector ?? s.
|
|
38897
|
-
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))}`);
|
|
38898
39094
|
});
|
|
38899
39095
|
});
|
|
38900
39096
|
scriptCmd.command("delete <name>").description("Delete a saved script").action(async (name) => {
|
|
38901
|
-
const {
|
|
38902
|
-
if (
|
|
38903
|
-
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}`));
|
|
38904
39100
|
} else {
|
|
38905
39101
|
console.log(chalk.red(`Script '${name}' not found`));
|
|
38906
39102
|
}
|
|
@@ -38930,10 +39126,10 @@ daemonCmd.command("start").description("Start the browser daemon in the backgrou
|
|
|
38930
39126
|
return;
|
|
38931
39127
|
}
|
|
38932
39128
|
const { spawn: spawn2 } = await import("child_process");
|
|
38933
|
-
const { writeFileSync:
|
|
39129
|
+
const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync14 } = await import("fs");
|
|
38934
39130
|
const { dirname: dirname4 } = await import("path");
|
|
38935
39131
|
const pidFile = getDaemonPidFile2();
|
|
38936
|
-
|
|
39132
|
+
mkdirSync14(dirname4(pidFile), { recursive: true });
|
|
38937
39133
|
const child = spawn2(process.execPath, [import.meta.dir + "/../server/index.js"], {
|
|
38938
39134
|
detached: true,
|
|
38939
39135
|
stdio: "ignore",
|
|
@@ -38941,7 +39137,7 @@ daemonCmd.command("start").description("Start the browser daemon in the backgrou
|
|
|
38941
39137
|
});
|
|
38942
39138
|
child.unref();
|
|
38943
39139
|
if (child.pid) {
|
|
38944
|
-
|
|
39140
|
+
writeFileSync8(pidFile, String(child.pid));
|
|
38945
39141
|
await new Promise((r) => setTimeout(r, 1500));
|
|
38946
39142
|
console.log(chalk.green(`\u2713 Daemon started`));
|
|
38947
39143
|
console.log(chalk.gray(` PID: ${child.pid}, Port: ${opts.port}`));
|
|
@@ -39114,11 +39310,11 @@ galleryCmd.command("stats").description("Show gallery statistics").option("--pro
|
|
|
39114
39310
|
});
|
|
39115
39311
|
galleryCmd.command("clean").description("Delete gallery entries with missing files").action(async () => {
|
|
39116
39312
|
const { listEntries: listEntries2, deleteEntry: deleteEntry2 } = await Promise.resolve().then(() => (init_gallery(), exports_gallery));
|
|
39117
|
-
const { existsSync:
|
|
39313
|
+
const { existsSync: existsSync11 } = await import("fs");
|
|
39118
39314
|
const entries = listEntries2({ limit: 9999 });
|
|
39119
39315
|
let removed = 0;
|
|
39120
39316
|
for (const e of entries) {
|
|
39121
|
-
if (!
|
|
39317
|
+
if (!existsSync11(e.path)) {
|
|
39122
39318
|
deleteEntry2(e.id);
|
|
39123
39319
|
removed++;
|
|
39124
39320
|
}
|