@hasna/browser 0.1.0 → 0.1.1
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 +913 -92
- package/dist/index.js +48 -0
- package/dist/lib/api-detector.d.ts +17 -0
- package/dist/lib/api-detector.d.ts.map +1 -0
- package/dist/lib/datasets.d.ts +33 -0
- package/dist/lib/datasets.d.ts.map +1 -0
- package/dist/lib/deep-performance.d.ts +49 -0
- package/dist/lib/deep-performance.d.ts.map +1 -0
- package/dist/lib/env-detector.d.ts +12 -0
- package/dist/lib/env-detector.d.ts.map +1 -0
- package/dist/lib/structured-extract.d.ts +26 -0
- package/dist/lib/structured-extract.d.ts.map +1 -0
- package/dist/lib/workflows.d.ts +46 -0
- package/dist/lib/workflows.d.ts.map +1 -0
- package/dist/mcp/index.js +905 -84
- package/dist/server/index.js +48 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2358,6 +2358,54 @@ function runMigrations(db) {
|
|
|
2358
2358
|
CREATE INDEX IF NOT EXISTS idx_auth_flows_domain ON auth_flows(domain);
|
|
2359
2359
|
CREATE INDEX IF NOT EXISTS idx_auth_flows_name ON auth_flows(name);
|
|
2360
2360
|
`
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
version: 7,
|
|
2364
|
+
sql: `
|
|
2365
|
+
CREATE TABLE IF NOT EXISTS workflows (
|
|
2366
|
+
id TEXT PRIMARY KEY,
|
|
2367
|
+
name TEXT NOT NULL UNIQUE,
|
|
2368
|
+
description TEXT,
|
|
2369
|
+
steps TEXT NOT NULL DEFAULT '[]',
|
|
2370
|
+
start_url TEXT,
|
|
2371
|
+
last_run TEXT,
|
|
2372
|
+
last_heal TEXT,
|
|
2373
|
+
heal_count INTEGER DEFAULT 0,
|
|
2374
|
+
run_count INTEGER DEFAULT 0,
|
|
2375
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
2376
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
2377
|
+
);
|
|
2378
|
+
`
|
|
2379
|
+
},
|
|
2380
|
+
{
|
|
2381
|
+
version: 8,
|
|
2382
|
+
sql: `
|
|
2383
|
+
CREATE TABLE IF NOT EXISTS datasets (
|
|
2384
|
+
id TEXT PRIMARY KEY,
|
|
2385
|
+
name TEXT NOT NULL UNIQUE,
|
|
2386
|
+
source_url TEXT,
|
|
2387
|
+
source_type TEXT NOT NULL DEFAULT 'page',
|
|
2388
|
+
data TEXT NOT NULL DEFAULT '[]',
|
|
2389
|
+
schema TEXT,
|
|
2390
|
+
row_count INTEGER DEFAULT 0,
|
|
2391
|
+
last_refresh TEXT,
|
|
2392
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
2393
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
2394
|
+
);
|
|
2395
|
+
|
|
2396
|
+
CREATE TABLE IF NOT EXISTS api_endpoints (
|
|
2397
|
+
id TEXT PRIMARY KEY,
|
|
2398
|
+
session_id TEXT,
|
|
2399
|
+
url TEXT NOT NULL,
|
|
2400
|
+
method TEXT DEFAULT 'GET',
|
|
2401
|
+
response_schema TEXT,
|
|
2402
|
+
sample_response TEXT,
|
|
2403
|
+
status_code INTEGER,
|
|
2404
|
+
content_type TEXT,
|
|
2405
|
+
discovered_at TEXT DEFAULT (datetime('now'))
|
|
2406
|
+
);
|
|
2407
|
+
CREATE INDEX IF NOT EXISTS idx_api_endpoints_session ON api_endpoints(session_id);
|
|
2408
|
+
`
|
|
2361
2409
|
}
|
|
2362
2410
|
];
|
|
2363
2411
|
for (const m of migrations) {
|
|
@@ -3322,14 +3370,14 @@ function enableConsoleCapture(page, sessionId) {
|
|
|
3322
3370
|
warning: "warn"
|
|
3323
3371
|
};
|
|
3324
3372
|
const level = levelMap[msg.type()] ?? "log";
|
|
3325
|
-
const
|
|
3373
|
+
const location2 = msg.location();
|
|
3326
3374
|
try {
|
|
3327
3375
|
logConsoleMessage({
|
|
3328
3376
|
session_id: sessionId,
|
|
3329
3377
|
level,
|
|
3330
3378
|
message: msg.text(),
|
|
3331
|
-
source:
|
|
3332
|
-
line_number:
|
|
3379
|
+
source: location2.url || undefined,
|
|
3380
|
+
line_number: location2.lineNumber || undefined
|
|
3333
3381
|
});
|
|
3334
3382
|
} catch {}
|
|
3335
3383
|
};
|
|
@@ -16805,6 +16853,419 @@ var init_annotate = __esm(() => {
|
|
|
16805
16853
|
import_sharp3 = __toESM(require_lib(), 1);
|
|
16806
16854
|
});
|
|
16807
16855
|
|
|
16856
|
+
// src/lib/env-detector.ts
|
|
16857
|
+
var exports_env_detector = {};
|
|
16858
|
+
__export(exports_env_detector, {
|
|
16859
|
+
detectEnvironment: () => detectEnvironment
|
|
16860
|
+
});
|
|
16861
|
+
async function detectEnvironment(page) {
|
|
16862
|
+
const url = page.url();
|
|
16863
|
+
const signals = [];
|
|
16864
|
+
let score = { local: 0, dev: 0, staging: 0, prod: 0 };
|
|
16865
|
+
try {
|
|
16866
|
+
const u = new URL(url);
|
|
16867
|
+
if (u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "0.0.0.0" || u.hostname.endsWith(".local")) {
|
|
16868
|
+
score.local += 5;
|
|
16869
|
+
signals.push(`URL hostname: ${u.hostname} \u2192 local`);
|
|
16870
|
+
} else if (u.hostname.match(/^(dev|development)\./i) || u.port !== "") {
|
|
16871
|
+
score.dev += 4;
|
|
16872
|
+
signals.push(`URL pattern: ${u.hostname}:${u.port} \u2192 dev`);
|
|
16873
|
+
} else if (u.hostname.match(/^(staging|stg|stage|preprod|uat)\./i)) {
|
|
16874
|
+
score.staging += 4;
|
|
16875
|
+
signals.push(`URL pattern: ${u.hostname} \u2192 staging`);
|
|
16876
|
+
} else {
|
|
16877
|
+
score.prod += 2;
|
|
16878
|
+
signals.push(`URL looks production: ${u.hostname}`);
|
|
16879
|
+
}
|
|
16880
|
+
if (u.port && !["80", "443", ""].includes(u.port)) {
|
|
16881
|
+
score.dev += 2;
|
|
16882
|
+
signals.push(`Non-standard port: ${u.port}`);
|
|
16883
|
+
}
|
|
16884
|
+
if (u.protocol === "https:") {
|
|
16885
|
+
score.prod += 1;
|
|
16886
|
+
signals.push("HTTPS \u2192 likely prod");
|
|
16887
|
+
} else {
|
|
16888
|
+
score.dev += 2;
|
|
16889
|
+
signals.push("HTTP \u2192 likely dev/local");
|
|
16890
|
+
}
|
|
16891
|
+
} catch {}
|
|
16892
|
+
try {
|
|
16893
|
+
const pageSignals = await page.evaluate(() => {
|
|
16894
|
+
const s = [];
|
|
16895
|
+
const w = window;
|
|
16896
|
+
const envVars = ["__ENV__", "__NEXT_DATA__", "__NUXT__", "process"];
|
|
16897
|
+
for (const v of envVars) {
|
|
16898
|
+
if (w[v]) {
|
|
16899
|
+
const env2 = w[v]?.env?.NODE_ENV ?? w[v]?.runtimeConfig?.public?.env ?? w[v]?.props?.pageProps?.env;
|
|
16900
|
+
if (env2)
|
|
16901
|
+
s.push(`window.${v}: ${env2}`);
|
|
16902
|
+
}
|
|
16903
|
+
}
|
|
16904
|
+
if (w.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 0) {
|
|
16905
|
+
const fiber = document.querySelector("[data-reactroot]") || document.getElementById("__next") || document.getElementById("root");
|
|
16906
|
+
if (fiber)
|
|
16907
|
+
s.push("React app detected");
|
|
16908
|
+
}
|
|
16909
|
+
const envMeta = document.querySelector('meta[name="environment"], meta[name="env"], meta[name="deploy-env"]');
|
|
16910
|
+
if (envMeta)
|
|
16911
|
+
s.push(`meta[environment]: ${envMeta.getAttribute("content")}`);
|
|
16912
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
16913
|
+
let minified = 0, unminified = 0;
|
|
16914
|
+
scripts.forEach((s2) => {
|
|
16915
|
+
const src = s2.getAttribute("src") ?? "";
|
|
16916
|
+
if (src.includes(".min.") || src.match(/\.[a-f0-9]{8,}\./))
|
|
16917
|
+
minified++;
|
|
16918
|
+
else if (src.endsWith(".js") && !src.includes("chunk"))
|
|
16919
|
+
unminified++;
|
|
16920
|
+
});
|
|
16921
|
+
if (unminified > minified && unminified > 2)
|
|
16922
|
+
s.push(`Unminified scripts (${unminified}/${minified + unminified}) \u2192 likely dev`);
|
|
16923
|
+
else if (minified > 0)
|
|
16924
|
+
s.push(`Minified/hashed scripts (${minified}/${minified + unminified}) \u2192 likely prod`);
|
|
16925
|
+
if (document.querySelector("[data-testid]"))
|
|
16926
|
+
s.push("data-testid attributes present \u2192 dev/staging");
|
|
16927
|
+
if ("serviceWorker" in navigator && navigator.serviceWorker.controller) {
|
|
16928
|
+
s.push("Service worker active \u2192 likely prod");
|
|
16929
|
+
}
|
|
16930
|
+
if (w.Sentry)
|
|
16931
|
+
s.push("Sentry SDK loaded \u2192 prod monitoring");
|
|
16932
|
+
if (w.__DATADOG_SYNTHETICS_INLINED_SCRIPT)
|
|
16933
|
+
s.push("Datadog loaded \u2192 prod monitoring");
|
|
16934
|
+
if (w.LogRocket)
|
|
16935
|
+
s.push("LogRocket loaded \u2192 prod monitoring");
|
|
16936
|
+
if (w._lr_loaded)
|
|
16937
|
+
s.push("LogRocket loaded \u2192 prod monitoring");
|
|
16938
|
+
if (w.gtag || w.ga)
|
|
16939
|
+
s.push("Google Analytics loaded \u2192 likely prod");
|
|
16940
|
+
if (w.posthog || w._ph)
|
|
16941
|
+
s.push("PostHog loaded \u2192 prod analytics");
|
|
16942
|
+
if (w.mixpanel)
|
|
16943
|
+
s.push("Mixpanel loaded \u2192 prod analytics");
|
|
16944
|
+
const robots = document.querySelector('meta[name="robots"]');
|
|
16945
|
+
if (robots) {
|
|
16946
|
+
const content = robots.getAttribute("content") ?? "";
|
|
16947
|
+
if (content.includes("noindex"))
|
|
16948
|
+
s.push(`robots: noindex \u2192 staging/dev`);
|
|
16949
|
+
}
|
|
16950
|
+
return s;
|
|
16951
|
+
});
|
|
16952
|
+
for (const signal of pageSignals) {
|
|
16953
|
+
signals.push(signal);
|
|
16954
|
+
if (signal.includes("development") || signal.includes("\u2192 dev") || signal.includes("\u2192 likely dev"))
|
|
16955
|
+
score.dev += 2;
|
|
16956
|
+
if (signal.includes("production") || signal.includes("\u2192 prod") || signal.includes("\u2192 likely prod"))
|
|
16957
|
+
score.prod += 2;
|
|
16958
|
+
if (signal.includes("staging") || signal.includes("\u2192 staging"))
|
|
16959
|
+
score.staging += 2;
|
|
16960
|
+
if (signal.includes("monitoring") || signal.includes("analytics"))
|
|
16961
|
+
score.prod += 1;
|
|
16962
|
+
if (signal.includes("noindex")) {
|
|
16963
|
+
score.staging += 2;
|
|
16964
|
+
score.dev += 1;
|
|
16965
|
+
}
|
|
16966
|
+
}
|
|
16967
|
+
} catch {}
|
|
16968
|
+
const entries = Object.entries(score);
|
|
16969
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
16970
|
+
const [env, topScore] = entries[0];
|
|
16971
|
+
const [, secondScore] = entries[1];
|
|
16972
|
+
const confidence = topScore >= 5 ? "high" : topScore > secondScore + 1 ? "medium" : "low";
|
|
16973
|
+
return { env, confidence, signals };
|
|
16974
|
+
}
|
|
16975
|
+
|
|
16976
|
+
// src/lib/deep-performance.ts
|
|
16977
|
+
var exports_deep_performance = {};
|
|
16978
|
+
__export(exports_deep_performance, {
|
|
16979
|
+
getDeepPerformance: () => getDeepPerformance
|
|
16980
|
+
});
|
|
16981
|
+
function categorizeThirdParty(domain) {
|
|
16982
|
+
for (const [pattern, category] of Object.entries(THIRD_PARTY_CATEGORIES)) {
|
|
16983
|
+
if (domain.includes(pattern))
|
|
16984
|
+
return category;
|
|
16985
|
+
}
|
|
16986
|
+
return "other";
|
|
16987
|
+
}
|
|
16988
|
+
async function getDeepPerformance(page) {
|
|
16989
|
+
return page.evaluate(() => {
|
|
16990
|
+
const perf = performance;
|
|
16991
|
+
const entries = perf.getEntriesByType("resource");
|
|
16992
|
+
const navEntry = perf.getEntriesByType("navigation")[0];
|
|
16993
|
+
const paintEntries = perf.getEntriesByType("paint");
|
|
16994
|
+
const fcp = paintEntries.find((e) => e.name === "first-contentful-paint")?.startTime;
|
|
16995
|
+
const ttfb = navEntry?.responseStart;
|
|
16996
|
+
const web_vitals = { fcp, ttfb };
|
|
16997
|
+
try {
|
|
16998
|
+
const lcpEntries = perf.getEntriesByType("largest-contentful-paint");
|
|
16999
|
+
if (lcpEntries.length > 0)
|
|
17000
|
+
web_vitals.lcp = lcpEntries[lcpEntries.length - 1].startTime;
|
|
17001
|
+
} catch {}
|
|
17002
|
+
const byType = {};
|
|
17003
|
+
let totalBytes = 0;
|
|
17004
|
+
const resourceList = [];
|
|
17005
|
+
const pageDomain = location.hostname;
|
|
17006
|
+
const thirdPartyMap = new Map;
|
|
17007
|
+
for (const entry of entries) {
|
|
17008
|
+
const size = entry.transferSize || entry.encodedBodySize || 0;
|
|
17009
|
+
totalBytes += size;
|
|
17010
|
+
let type2 = entry.initiatorType || "other";
|
|
17011
|
+
if (type2 === "xmlhttprequest" || type2 === "fetch")
|
|
17012
|
+
type2 = "xhr";
|
|
17013
|
+
if (type2 === "link" && entry.name.match(/\.css/))
|
|
17014
|
+
type2 = "css";
|
|
17015
|
+
if (type2 === "img" || entry.name.match(/\.(png|jpg|jpeg|gif|svg|webp|avif|ico)/i))
|
|
17016
|
+
type2 = "image";
|
|
17017
|
+
if (type2 === "script" || entry.name.match(/\.js/))
|
|
17018
|
+
type2 = "script";
|
|
17019
|
+
if (entry.name.match(/\.(woff2?|ttf|otf|eot)/i))
|
|
17020
|
+
type2 = "font";
|
|
17021
|
+
if (!byType[type2])
|
|
17022
|
+
byType[type2] = { count: 0, size_bytes: 0 };
|
|
17023
|
+
byType[type2].count++;
|
|
17024
|
+
byType[type2].size_bytes += size;
|
|
17025
|
+
resourceList.push({ url: entry.name, size_bytes: size, type: type2 });
|
|
17026
|
+
try {
|
|
17027
|
+
const domain = new URL(entry.name).hostname;
|
|
17028
|
+
if (domain !== pageDomain && !domain.endsWith(`.${pageDomain}`)) {
|
|
17029
|
+
if (!thirdPartyMap.has(domain))
|
|
17030
|
+
thirdPartyMap.set(domain, { scripts: 0, total_bytes: 0 });
|
|
17031
|
+
const tp = thirdPartyMap.get(domain);
|
|
17032
|
+
tp.scripts++;
|
|
17033
|
+
tp.total_bytes += size;
|
|
17034
|
+
}
|
|
17035
|
+
} catch {}
|
|
17036
|
+
}
|
|
17037
|
+
resourceList.sort((a, b) => b.size_bytes - a.size_bytes);
|
|
17038
|
+
const largest = resourceList.slice(0, 10).map((r) => ({
|
|
17039
|
+
url: r.url.length > 120 ? r.url.slice(0, 117) + "..." : r.url,
|
|
17040
|
+
size_bytes: r.size_bytes,
|
|
17041
|
+
type: r.type
|
|
17042
|
+
}));
|
|
17043
|
+
const third_party = Array.from(thirdPartyMap.entries()).map(([domain, data]) => ({ domain, ...data, category: "" })).sort((a, b) => b.total_bytes - a.total_bytes).slice(0, 15);
|
|
17044
|
+
const allNodes = document.querySelectorAll("*");
|
|
17045
|
+
let maxDepth = 0;
|
|
17046
|
+
let textNodes = 0;
|
|
17047
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL);
|
|
17048
|
+
let node = walker.currentNode;
|
|
17049
|
+
while (node) {
|
|
17050
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim())
|
|
17051
|
+
textNodes++;
|
|
17052
|
+
let depth = 0;
|
|
17053
|
+
let parent = node.parentNode;
|
|
17054
|
+
while (parent) {
|
|
17055
|
+
depth++;
|
|
17056
|
+
parent = parent.parentNode;
|
|
17057
|
+
}
|
|
17058
|
+
if (depth > maxDepth)
|
|
17059
|
+
maxDepth = depth;
|
|
17060
|
+
node = walker.nextNode();
|
|
17061
|
+
}
|
|
17062
|
+
const mem = performance.memory;
|
|
17063
|
+
const memory = {
|
|
17064
|
+
js_heap_used_mb: mem ? Math.round(mem.usedJSHeapSize / 1024 / 1024 * 100) / 100 : 0,
|
|
17065
|
+
js_heap_total_mb: mem ? Math.round(mem.totalJSHeapSize / 1024 / 1024 * 100) / 100 : 0,
|
|
17066
|
+
js_heap_limit_mb: mem ? Math.round(mem.jsHeapSizeLimit / 1024 / 1024 * 100) / 100 : 0
|
|
17067
|
+
};
|
|
17068
|
+
return {
|
|
17069
|
+
web_vitals,
|
|
17070
|
+
resources: { total_transfer_bytes: totalBytes, total_resources: entries.length, by_type: byType, largest },
|
|
17071
|
+
third_party,
|
|
17072
|
+
dom: { node_count: document.all.length, max_depth: maxDepth, element_count: allNodes.length, text_node_count: textNodes },
|
|
17073
|
+
main_thread: { long_tasks: 0, total_blocking_ms: 0 },
|
|
17074
|
+
memory
|
|
17075
|
+
};
|
|
17076
|
+
}).then((result) => {
|
|
17077
|
+
for (const tp of result.third_party) {
|
|
17078
|
+
tp.category = categorizeThirdParty(tp.domain);
|
|
17079
|
+
}
|
|
17080
|
+
return result;
|
|
17081
|
+
});
|
|
17082
|
+
}
|
|
17083
|
+
var THIRD_PARTY_CATEGORIES;
|
|
17084
|
+
var init_deep_performance = __esm(() => {
|
|
17085
|
+
THIRD_PARTY_CATEGORIES = {
|
|
17086
|
+
"google-analytics.com": "analytics",
|
|
17087
|
+
"googletagmanager.com": "analytics",
|
|
17088
|
+
gtag: "analytics",
|
|
17089
|
+
"facebook.net": "social",
|
|
17090
|
+
"connect.facebook": "social",
|
|
17091
|
+
"stripe.com": "payment",
|
|
17092
|
+
"js.stripe.com": "payment",
|
|
17093
|
+
"sentry.io": "monitoring",
|
|
17094
|
+
"sentry-cdn": "monitoring",
|
|
17095
|
+
"posthog.com": "analytics",
|
|
17096
|
+
"ph.": "analytics",
|
|
17097
|
+
"intercom.io": "chat",
|
|
17098
|
+
"crisp.chat": "chat",
|
|
17099
|
+
"hotjar.com": "analytics",
|
|
17100
|
+
"clarity.ms": "analytics",
|
|
17101
|
+
"cdn.jsdelivr.net": "cdn",
|
|
17102
|
+
"cdnjs.cloudflare.com": "cdn",
|
|
17103
|
+
"unpkg.com": "cdn",
|
|
17104
|
+
"fonts.googleapis.com": "fonts",
|
|
17105
|
+
"fonts.gstatic.com": "fonts"
|
|
17106
|
+
};
|
|
17107
|
+
});
|
|
17108
|
+
|
|
17109
|
+
// src/lib/workflows.ts
|
|
17110
|
+
var exports_workflows = {};
|
|
17111
|
+
__export(exports_workflows, {
|
|
17112
|
+
saveWorkflowFromRecording: () => saveWorkflowFromRecording,
|
|
17113
|
+
saveWorkflow: () => saveWorkflow,
|
|
17114
|
+
runWorkflow: () => runWorkflow,
|
|
17115
|
+
listWorkflows: () => listWorkflows,
|
|
17116
|
+
getWorkflowByName: () => getWorkflowByName,
|
|
17117
|
+
getWorkflow: () => getWorkflow,
|
|
17118
|
+
deleteWorkflow: () => deleteWorkflow
|
|
17119
|
+
});
|
|
17120
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
17121
|
+
function saveWorkflow(data) {
|
|
17122
|
+
const db = getDatabase();
|
|
17123
|
+
const id = randomUUID10();
|
|
17124
|
+
db.prepare("INSERT OR REPLACE INTO workflows (id, name, description, steps, start_url) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.description ?? null, JSON.stringify(data.steps), data.startUrl ?? null);
|
|
17125
|
+
return getWorkflow(id);
|
|
17126
|
+
}
|
|
17127
|
+
function saveWorkflowFromRecording(recordingId, name, description) {
|
|
17128
|
+
const db = getDatabase();
|
|
17129
|
+
const rec = db.query("SELECT steps, start_url FROM recordings WHERE id = ?").get(recordingId);
|
|
17130
|
+
if (!rec)
|
|
17131
|
+
throw new Error(`Recording not found: ${recordingId}`);
|
|
17132
|
+
const steps = JSON.parse(rec.steps);
|
|
17133
|
+
return saveWorkflow({ name, description, steps, startUrl: rec.start_url ?? undefined });
|
|
17134
|
+
}
|
|
17135
|
+
function getWorkflow(id) {
|
|
17136
|
+
const db = getDatabase();
|
|
17137
|
+
const row = db.query("SELECT * FROM workflows WHERE id = ?").get(id);
|
|
17138
|
+
if (!row)
|
|
17139
|
+
return null;
|
|
17140
|
+
return { ...row, steps: JSON.parse(row.steps) };
|
|
17141
|
+
}
|
|
17142
|
+
function getWorkflowByName(name) {
|
|
17143
|
+
const db = getDatabase();
|
|
17144
|
+
const row = db.query("SELECT * FROM workflows WHERE name = ?").get(name);
|
|
17145
|
+
if (!row)
|
|
17146
|
+
return null;
|
|
17147
|
+
return { ...row, steps: JSON.parse(row.steps) };
|
|
17148
|
+
}
|
|
17149
|
+
function listWorkflows() {
|
|
17150
|
+
const db = getDatabase();
|
|
17151
|
+
return db.query("SELECT * FROM workflows ORDER BY updated_at DESC").all().map((row) => ({ ...row, steps: JSON.parse(row.steps) }));
|
|
17152
|
+
}
|
|
17153
|
+
function deleteWorkflow(name) {
|
|
17154
|
+
const db = getDatabase();
|
|
17155
|
+
return db.prepare("DELETE FROM workflows WHERE name = ?").run(name).changes > 0;
|
|
17156
|
+
}
|
|
17157
|
+
function recordRun(id, healed) {
|
|
17158
|
+
const db = getDatabase();
|
|
17159
|
+
if (healed) {
|
|
17160
|
+
db.prepare("UPDATE workflows SET last_run = datetime('now'), last_heal = datetime('now'), heal_count = heal_count + 1, run_count = run_count + 1, updated_at = datetime('now') WHERE id = ?").run(id);
|
|
17161
|
+
} else {
|
|
17162
|
+
db.prepare("UPDATE workflows SET last_run = datetime('now'), run_count = run_count + 1, updated_at = datetime('now') WHERE id = ?").run(id);
|
|
17163
|
+
}
|
|
17164
|
+
}
|
|
17165
|
+
async function runWorkflow(workflow, page) {
|
|
17166
|
+
const t0 = Date.now();
|
|
17167
|
+
let executed = 0;
|
|
17168
|
+
let failed = 0;
|
|
17169
|
+
let healed = 0;
|
|
17170
|
+
const healedDetails = [];
|
|
17171
|
+
const errors2 = [];
|
|
17172
|
+
const updatedSteps = [...workflow.steps];
|
|
17173
|
+
for (let i = 0;i < workflow.steps.length; i++) {
|
|
17174
|
+
const step = workflow.steps[i];
|
|
17175
|
+
try {
|
|
17176
|
+
switch (step.type) {
|
|
17177
|
+
case "navigate":
|
|
17178
|
+
if (step.url)
|
|
17179
|
+
await page.goto(step.url, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
17180
|
+
break;
|
|
17181
|
+
case "click":
|
|
17182
|
+
if (step.selector) {
|
|
17183
|
+
try {
|
|
17184
|
+
await page.click(step.selector, { timeout: 5000 });
|
|
17185
|
+
} catch {
|
|
17186
|
+
const result = await healSelector(page, step.selector);
|
|
17187
|
+
if (result.found && result.locator) {
|
|
17188
|
+
await result.locator.click();
|
|
17189
|
+
healed++;
|
|
17190
|
+
const healedSelector = `[healed:${result.method}]${step.selector}`;
|
|
17191
|
+
healedDetails.push({ step: i, original: step.selector, healed_to: healedSelector, method: result.method });
|
|
17192
|
+
} else {
|
|
17193
|
+
throw new Error(`Click failed: ${step.selector} (self-healing exhausted)`);
|
|
17194
|
+
}
|
|
17195
|
+
}
|
|
17196
|
+
}
|
|
17197
|
+
break;
|
|
17198
|
+
case "type":
|
|
17199
|
+
if (step.selector && step.value) {
|
|
17200
|
+
try {
|
|
17201
|
+
await page.fill(step.selector, step.value);
|
|
17202
|
+
} catch {
|
|
17203
|
+
const result = await healSelector(page, step.selector);
|
|
17204
|
+
if (result.found && result.locator) {
|
|
17205
|
+
await result.locator.fill(step.value);
|
|
17206
|
+
healed++;
|
|
17207
|
+
healedDetails.push({ step: i, original: step.selector, healed_to: `[healed:${result.method}]`, method: result.method });
|
|
17208
|
+
} else {
|
|
17209
|
+
throw new Error(`Type failed: ${step.selector} (self-healing exhausted)`);
|
|
17210
|
+
}
|
|
17211
|
+
}
|
|
17212
|
+
}
|
|
17213
|
+
break;
|
|
17214
|
+
case "scroll":
|
|
17215
|
+
if (step.y)
|
|
17216
|
+
await page.mouse.wheel(0, step.y);
|
|
17217
|
+
break;
|
|
17218
|
+
case "hover":
|
|
17219
|
+
if (step.selector) {
|
|
17220
|
+
try {
|
|
17221
|
+
await page.hover(step.selector);
|
|
17222
|
+
} catch {}
|
|
17223
|
+
}
|
|
17224
|
+
break;
|
|
17225
|
+
case "select":
|
|
17226
|
+
if (step.selector && step.value) {
|
|
17227
|
+
try {
|
|
17228
|
+
await page.selectOption(step.selector, step.value);
|
|
17229
|
+
} catch {}
|
|
17230
|
+
}
|
|
17231
|
+
break;
|
|
17232
|
+
case "wait":
|
|
17233
|
+
if (step.selector) {
|
|
17234
|
+
try {
|
|
17235
|
+
await page.waitForSelector(step.selector, { timeout: 1e4 });
|
|
17236
|
+
} catch {}
|
|
17237
|
+
} else {
|
|
17238
|
+
await new Promise((r) => setTimeout(r, step.timestamp || 1000));
|
|
17239
|
+
}
|
|
17240
|
+
break;
|
|
17241
|
+
case "evaluate":
|
|
17242
|
+
if (step.value)
|
|
17243
|
+
await page.evaluate(step.value);
|
|
17244
|
+
break;
|
|
17245
|
+
default:
|
|
17246
|
+
break;
|
|
17247
|
+
}
|
|
17248
|
+
executed++;
|
|
17249
|
+
} catch (err) {
|
|
17250
|
+
failed++;
|
|
17251
|
+
errors2.push(`Step ${i} (${step.type}): ${err instanceof Error ? err.message : String(err)}`);
|
|
17252
|
+
}
|
|
17253
|
+
}
|
|
17254
|
+
recordRun(workflow.id, healed > 0);
|
|
17255
|
+
return {
|
|
17256
|
+
success: failed === 0,
|
|
17257
|
+
steps_executed: executed,
|
|
17258
|
+
steps_failed: failed,
|
|
17259
|
+
steps_healed: healed,
|
|
17260
|
+
healed_details: healedDetails,
|
|
17261
|
+
errors: errors2,
|
|
17262
|
+
duration_ms: Date.now() - t0
|
|
17263
|
+
};
|
|
17264
|
+
}
|
|
17265
|
+
var init_workflows = __esm(() => {
|
|
17266
|
+
init_schema();
|
|
17267
|
+
});
|
|
17268
|
+
|
|
16808
17269
|
// src/lib/auth-flow.ts
|
|
16809
17270
|
var exports_auth_flow = {};
|
|
16810
17271
|
__export(exports_auth_flow, {
|
|
@@ -16819,10 +17280,10 @@ __export(exports_auth_flow, {
|
|
|
16819
17280
|
getAuthFlow: () => getAuthFlow,
|
|
16820
17281
|
deleteAuthFlow: () => deleteAuthFlow
|
|
16821
17282
|
});
|
|
16822
|
-
import { randomUUID as
|
|
17283
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
16823
17284
|
function saveAuthFlow(data) {
|
|
16824
17285
|
const db = getDatabase();
|
|
16825
|
-
const id =
|
|
17286
|
+
const id = randomUUID11();
|
|
16826
17287
|
db.prepare("INSERT OR REPLACE INTO auth_flows (id, name, domain, recording_id, storage_state_path) VALUES (?, ?, ?, ?, ?)").run(id, data.name, data.domain, data.recordingId ?? null, data.storageStatePath ?? null);
|
|
16827
17288
|
return getAuthFlow(id);
|
|
16828
17289
|
}
|
|
@@ -16992,6 +17453,226 @@ async function clickByVision(page, description, opts) {
|
|
|
16992
17453
|
}
|
|
16993
17454
|
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
16994
17455
|
|
|
17456
|
+
// src/lib/api-detector.ts
|
|
17457
|
+
var exports_api_detector = {};
|
|
17458
|
+
__export(exports_api_detector, {
|
|
17459
|
+
listDiscoveredAPIs: () => listDiscoveredAPIs,
|
|
17460
|
+
detectAPIs: () => detectAPIs
|
|
17461
|
+
});
|
|
17462
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
17463
|
+
function detectAPIs(sessionId) {
|
|
17464
|
+
const db = getDatabase();
|
|
17465
|
+
const requests = db.query(`SELECT method, url, status_code, response_headers, body_size
|
|
17466
|
+
FROM network_log
|
|
17467
|
+
WHERE session_id = ?
|
|
17468
|
+
AND (response_headers LIKE '%application/json%' OR response_headers LIKE '%text/json%')
|
|
17469
|
+
AND status_code >= 200 AND status_code < 400
|
|
17470
|
+
ORDER BY timestamp DESC`).all(sessionId);
|
|
17471
|
+
const seen = new Map;
|
|
17472
|
+
for (const req of requests) {
|
|
17473
|
+
try {
|
|
17474
|
+
const urlObj = new URL(req.url);
|
|
17475
|
+
if (urlObj.pathname.match(/\.(js|css|png|jpg|svg|woff|ico)$/))
|
|
17476
|
+
continue;
|
|
17477
|
+
if (urlObj.hostname.includes("googleapis.com/identitytoolkit"))
|
|
17478
|
+
continue;
|
|
17479
|
+
if (urlObj.hostname.includes("posthog"))
|
|
17480
|
+
continue;
|
|
17481
|
+
if (urlObj.hostname.includes("sentry"))
|
|
17482
|
+
continue;
|
|
17483
|
+
const key = `${req.method} ${urlObj.origin}${urlObj.pathname}`;
|
|
17484
|
+
if (!seen.has(key)) {
|
|
17485
|
+
seen.set(key, {
|
|
17486
|
+
url: `${urlObj.origin}${urlObj.pathname}`,
|
|
17487
|
+
method: req.method,
|
|
17488
|
+
status_code: req.status_code,
|
|
17489
|
+
content_type: "application/json",
|
|
17490
|
+
response_schema: {},
|
|
17491
|
+
sample_size: req.body_size ?? 0
|
|
17492
|
+
});
|
|
17493
|
+
}
|
|
17494
|
+
} catch {}
|
|
17495
|
+
}
|
|
17496
|
+
const apis = Array.from(seen.values());
|
|
17497
|
+
for (const api of apis) {
|
|
17498
|
+
const id = randomUUID12();
|
|
17499
|
+
db.prepare("INSERT OR IGNORE INTO api_endpoints (id, session_id, url, method, status_code, content_type) VALUES (?, ?, ?, ?, ?, ?)").run(id, sessionId, api.url, api.method, api.status_code, api.content_type);
|
|
17500
|
+
}
|
|
17501
|
+
return apis;
|
|
17502
|
+
}
|
|
17503
|
+
function listDiscoveredAPIs(sessionId) {
|
|
17504
|
+
const db = getDatabase();
|
|
17505
|
+
if (sessionId) {
|
|
17506
|
+
return db.query("SELECT * FROM api_endpoints WHERE session_id = ? ORDER BY discovered_at DESC").all(sessionId);
|
|
17507
|
+
}
|
|
17508
|
+
return db.query("SELECT * FROM api_endpoints ORDER BY discovered_at DESC LIMIT 100").all();
|
|
17509
|
+
}
|
|
17510
|
+
var init_api_detector = __esm(() => {
|
|
17511
|
+
init_schema();
|
|
17512
|
+
});
|
|
17513
|
+
|
|
17514
|
+
// src/lib/structured-extract.ts
|
|
17515
|
+
var exports_structured_extract = {};
|
|
17516
|
+
__export(exports_structured_extract, {
|
|
17517
|
+
extractStructuredData: () => extractStructuredData
|
|
17518
|
+
});
|
|
17519
|
+
async function extractStructuredData(page) {
|
|
17520
|
+
return page.evaluate(() => {
|
|
17521
|
+
const result = { tables: [], lists: [], jsonLd: [], openGraph: {}, metaTags: {}, repeatedElements: [] };
|
|
17522
|
+
document.querySelectorAll("table").forEach((table, idx) => {
|
|
17523
|
+
const headers = [];
|
|
17524
|
+
table.querySelectorAll("thead th, thead td, tr:first-child th").forEach((th) => {
|
|
17525
|
+
headers.push(th.textContent?.trim() ?? "");
|
|
17526
|
+
});
|
|
17527
|
+
const rows = [];
|
|
17528
|
+
table.querySelectorAll("tbody tr, tr:not(:first-child)").forEach((tr) => {
|
|
17529
|
+
const row = [];
|
|
17530
|
+
tr.querySelectorAll("td, th").forEach((td) => {
|
|
17531
|
+
row.push(td.textContent?.trim() ?? "");
|
|
17532
|
+
});
|
|
17533
|
+
if (row.length > 0 && row.some((c) => c !== ""))
|
|
17534
|
+
rows.push(row);
|
|
17535
|
+
});
|
|
17536
|
+
if (rows.length > 0) {
|
|
17537
|
+
result.tables.push({ headers, rows, selector: `table:nth-of-type(${idx + 1})` });
|
|
17538
|
+
}
|
|
17539
|
+
});
|
|
17540
|
+
document.querySelectorAll("ul, ol").forEach((list, idx) => {
|
|
17541
|
+
const items = [];
|
|
17542
|
+
list.querySelectorAll(":scope > li").forEach((li) => {
|
|
17543
|
+
const text = li.textContent?.trim() ?? "";
|
|
17544
|
+
if (text)
|
|
17545
|
+
items.push(text);
|
|
17546
|
+
});
|
|
17547
|
+
if (items.length >= 3) {
|
|
17548
|
+
const tag = list.tagName.toLowerCase();
|
|
17549
|
+
result.lists.push({ items, selector: `${tag}:nth-of-type(${idx + 1})` });
|
|
17550
|
+
}
|
|
17551
|
+
});
|
|
17552
|
+
document.querySelectorAll('script[type="application/ld+json"]').forEach((script) => {
|
|
17553
|
+
try {
|
|
17554
|
+
result.jsonLd.push(JSON.parse(script.textContent ?? ""));
|
|
17555
|
+
} catch {}
|
|
17556
|
+
});
|
|
17557
|
+
document.querySelectorAll('meta[property^="og:"]').forEach((meta) => {
|
|
17558
|
+
const prop = meta.getAttribute("property")?.replace("og:", "") ?? "";
|
|
17559
|
+
result.openGraph[prop] = meta.getAttribute("content") ?? "";
|
|
17560
|
+
});
|
|
17561
|
+
document.querySelectorAll("meta[name]").forEach((meta) => {
|
|
17562
|
+
const name = meta.getAttribute("name") ?? "";
|
|
17563
|
+
if (name)
|
|
17564
|
+
result.metaTags[name] = meta.getAttribute("content") ?? "";
|
|
17565
|
+
});
|
|
17566
|
+
const classCounts = new Map;
|
|
17567
|
+
document.querySelectorAll("[class]").forEach((el) => {
|
|
17568
|
+
const cls = el.className.toString().trim();
|
|
17569
|
+
if (cls && cls.length > 5 && cls.length < 100) {
|
|
17570
|
+
if (!classCounts.has(cls))
|
|
17571
|
+
classCounts.set(cls, []);
|
|
17572
|
+
classCounts.get(cls).push(el);
|
|
17573
|
+
}
|
|
17574
|
+
});
|
|
17575
|
+
for (const [cls, elements] of classCounts) {
|
|
17576
|
+
if (elements.length >= 3 && elements.length <= 200) {
|
|
17577
|
+
const sample = elements.slice(0, 3).map((el) => el.textContent?.trim().slice(0, 100) ?? "");
|
|
17578
|
+
if (sample.some((s) => s.length > 10)) {
|
|
17579
|
+
result.repeatedElements.push({
|
|
17580
|
+
selector: `.${cls.split(" ")[0]}`,
|
|
17581
|
+
count: elements.length,
|
|
17582
|
+
sample
|
|
17583
|
+
});
|
|
17584
|
+
}
|
|
17585
|
+
}
|
|
17586
|
+
}
|
|
17587
|
+
result.repeatedElements.sort((a, b) => b.count - a.count);
|
|
17588
|
+
result.repeatedElements = result.repeatedElements.slice(0, 10);
|
|
17589
|
+
return result;
|
|
17590
|
+
});
|
|
17591
|
+
}
|
|
17592
|
+
|
|
17593
|
+
// src/lib/datasets.ts
|
|
17594
|
+
var exports_datasets = {};
|
|
17595
|
+
__export(exports_datasets, {
|
|
17596
|
+
saveDataset: () => saveDataset,
|
|
17597
|
+
listDatasets: () => listDatasets,
|
|
17598
|
+
getDatasetByName: () => getDatasetByName,
|
|
17599
|
+
getDataset: () => getDataset,
|
|
17600
|
+
exportDataset: () => exportDataset,
|
|
17601
|
+
deleteDataset: () => deleteDataset
|
|
17602
|
+
});
|
|
17603
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
17604
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
|
|
17605
|
+
import { join as join9 } from "path";
|
|
17606
|
+
import { homedir as homedir9 } from "os";
|
|
17607
|
+
function saveDataset(data) {
|
|
17608
|
+
const db = getDatabase();
|
|
17609
|
+
const id = randomUUID13();
|
|
17610
|
+
const existing = db.query("SELECT id FROM datasets WHERE name = ?").get(data.name);
|
|
17611
|
+
if (existing) {
|
|
17612
|
+
db.prepare("UPDATE datasets SET data = ?, row_count = ?, source_url = ?, schema = ?, last_refresh = datetime('now'), updated_at = datetime('now') WHERE name = ?").run(JSON.stringify(data.rows), data.rows.length, data.sourceUrl ?? null, data.schema ? JSON.stringify(data.schema) : null, data.name);
|
|
17613
|
+
return getDataset(existing.id);
|
|
17614
|
+
}
|
|
17615
|
+
db.prepare("INSERT INTO datasets (id, name, source_url, source_type, data, row_count, schema) VALUES (?, ?, ?, ?, ?, ?, ?)").run(id, data.name, data.sourceUrl ?? null, data.sourceType ?? "page", JSON.stringify(data.rows), data.rows.length, data.schema ? JSON.stringify(data.schema) : null);
|
|
17616
|
+
return getDataset(id);
|
|
17617
|
+
}
|
|
17618
|
+
function getDataset(id) {
|
|
17619
|
+
const db = getDatabase();
|
|
17620
|
+
const row = db.query("SELECT * FROM datasets WHERE id = ?").get(id);
|
|
17621
|
+
if (!row)
|
|
17622
|
+
return null;
|
|
17623
|
+
return { ...row, data: JSON.parse(row.data), schema: row.schema ? JSON.parse(row.schema) : null };
|
|
17624
|
+
}
|
|
17625
|
+
function getDatasetByName(name) {
|
|
17626
|
+
const db = getDatabase();
|
|
17627
|
+
const row = db.query("SELECT * FROM datasets WHERE name = ?").get(name);
|
|
17628
|
+
if (!row)
|
|
17629
|
+
return null;
|
|
17630
|
+
return { ...row, data: JSON.parse(row.data), schema: row.schema ? JSON.parse(row.schema) : null };
|
|
17631
|
+
}
|
|
17632
|
+
function listDatasets() {
|
|
17633
|
+
const db = getDatabase();
|
|
17634
|
+
return db.query("SELECT id, name, source_url, source_type, row_count, last_refresh, created_at, updated_at FROM datasets ORDER BY updated_at DESC").all().map((row) => ({ ...row, data: `${row.row_count} rows`, schema: null }));
|
|
17635
|
+
}
|
|
17636
|
+
function deleteDataset(name) {
|
|
17637
|
+
const db = getDatabase();
|
|
17638
|
+
return db.prepare("DELETE FROM datasets WHERE name = ?").run(name).changes > 0;
|
|
17639
|
+
}
|
|
17640
|
+
function exportDataset(name, format) {
|
|
17641
|
+
const dataset = getDatasetByName(name);
|
|
17642
|
+
if (!dataset)
|
|
17643
|
+
throw new Error(`Dataset '${name}' not found`);
|
|
17644
|
+
const dir = join9(process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser"), "exports");
|
|
17645
|
+
mkdirSync9(dir, { recursive: true });
|
|
17646
|
+
const filename = `${name}.${format}`;
|
|
17647
|
+
const path = join9(dir, filename);
|
|
17648
|
+
if (format === "csv") {
|
|
17649
|
+
const rows = dataset.data;
|
|
17650
|
+
if (rows.length === 0) {
|
|
17651
|
+
writeFileSync3(path, "");
|
|
17652
|
+
return { path, size: 0 };
|
|
17653
|
+
}
|
|
17654
|
+
const headers = Object.keys(rows[0]);
|
|
17655
|
+
const csvLines = [headers.join(",")];
|
|
17656
|
+
for (const row of rows) {
|
|
17657
|
+
csvLines.push(headers.map((h) => {
|
|
17658
|
+
const val = String(row[h] ?? "");
|
|
17659
|
+
return val.includes(",") || val.includes('"') ? `"${val.replace(/"/g, '""')}"` : val;
|
|
17660
|
+
}).join(","));
|
|
17661
|
+
}
|
|
17662
|
+
const content = csvLines.join(`
|
|
17663
|
+
`);
|
|
17664
|
+
writeFileSync3(path, content);
|
|
17665
|
+
return { path, size: content.length };
|
|
17666
|
+
} else {
|
|
17667
|
+
const content = JSON.stringify(dataset.data, null, 2);
|
|
17668
|
+
writeFileSync3(path, content);
|
|
17669
|
+
return { path, size: content.length };
|
|
17670
|
+
}
|
|
17671
|
+
}
|
|
17672
|
+
var init_datasets = __esm(() => {
|
|
17673
|
+
init_schema();
|
|
17674
|
+
});
|
|
17675
|
+
|
|
16995
17676
|
// src/lib/auth.ts
|
|
16996
17677
|
var exports_auth = {};
|
|
16997
17678
|
__export(exports_auth, {
|
|
@@ -16999,18 +17680,18 @@ __export(exports_auth, {
|
|
|
16999
17680
|
getCredentials: () => getCredentials
|
|
17000
17681
|
});
|
|
17001
17682
|
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
17002
|
-
import { join as
|
|
17003
|
-
import { homedir as
|
|
17683
|
+
import { join as join10 } from "path";
|
|
17684
|
+
import { homedir as homedir10 } from "os";
|
|
17004
17685
|
async function getCredentials(service) {
|
|
17005
17686
|
try {
|
|
17006
|
-
const { getSecret } = await import(`${
|
|
17687
|
+
const { getSecret } = await import(`${homedir10()}/Workspace/hasna/opensource/opensourcedev/open-secrets/src/store.js`);
|
|
17007
17688
|
const email = getSecret(`${service}_email`) ?? getSecret(`${service}_username`) ?? getSecret(`${service}_login`);
|
|
17008
17689
|
const password = getSecret(`${service}_password`) ?? getSecret(`${service}_pass`);
|
|
17009
17690
|
if (email?.value && password?.value) {
|
|
17010
17691
|
return { email: email.value, password: password.value };
|
|
17011
17692
|
}
|
|
17012
17693
|
} catch {}
|
|
17013
|
-
const secretsPath =
|
|
17694
|
+
const secretsPath = join10(homedir10(), ".secrets");
|
|
17014
17695
|
if (existsSync5(secretsPath)) {
|
|
17015
17696
|
const content = readFileSync3(secretsPath, "utf8");
|
|
17016
17697
|
const lines = content.split(`
|
|
@@ -17192,10 +17873,10 @@ __export(exports_dist, {
|
|
|
17192
17873
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
17193
17874
|
});
|
|
17194
17875
|
import { Database as Database2 } from "bun:sqlite";
|
|
17195
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
17196
|
-
import { dirname, join as
|
|
17197
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync4, readdirSync as readdirSync4, writeFileSync as
|
|
17198
|
-
import { homedir as
|
|
17876
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync10 } from "fs";
|
|
17877
|
+
import { dirname, join as join11, resolve } from "path";
|
|
17878
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
17879
|
+
import { homedir as homedir11 } from "os";
|
|
17199
17880
|
import { basename as basename2, dirname as dirname2, join as join22, resolve as resolve2 } from "path";
|
|
17200
17881
|
import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
17201
17882
|
import { homedir as homedir22 } from "os";
|
|
@@ -17209,7 +17890,7 @@ function isInMemoryDb(path) {
|
|
|
17209
17890
|
function findNearestMementosDb(startDir) {
|
|
17210
17891
|
let dir = resolve(startDir);
|
|
17211
17892
|
while (true) {
|
|
17212
|
-
const candidate =
|
|
17893
|
+
const candidate = join11(dir, ".mementos", "mementos.db");
|
|
17213
17894
|
if (existsSync6(candidate))
|
|
17214
17895
|
return candidate;
|
|
17215
17896
|
const parent = dirname(dir);
|
|
@@ -17222,7 +17903,7 @@ function findNearestMementosDb(startDir) {
|
|
|
17222
17903
|
function findGitRoot(startDir) {
|
|
17223
17904
|
let dir = resolve(startDir);
|
|
17224
17905
|
while (true) {
|
|
17225
|
-
if (existsSync6(
|
|
17906
|
+
if (existsSync6(join11(dir, ".git")))
|
|
17226
17907
|
return dir;
|
|
17227
17908
|
const parent = dirname(dir);
|
|
17228
17909
|
if (parent === dir)
|
|
@@ -17242,18 +17923,18 @@ function getDbPath() {
|
|
|
17242
17923
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
17243
17924
|
const gitRoot = findGitRoot(cwd);
|
|
17244
17925
|
if (gitRoot) {
|
|
17245
|
-
return
|
|
17926
|
+
return join11(gitRoot, ".mementos", "mementos.db");
|
|
17246
17927
|
}
|
|
17247
17928
|
}
|
|
17248
17929
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
17249
|
-
return
|
|
17930
|
+
return join11(home, ".mementos", "mementos.db");
|
|
17250
17931
|
}
|
|
17251
17932
|
function ensureDir2(filePath) {
|
|
17252
17933
|
if (isInMemoryDb(filePath))
|
|
17253
17934
|
return;
|
|
17254
17935
|
const dir = dirname(resolve(filePath));
|
|
17255
17936
|
if (!existsSync6(dir)) {
|
|
17256
|
-
|
|
17937
|
+
mkdirSync10(dir, { recursive: true });
|
|
17257
17938
|
}
|
|
17258
17939
|
}
|
|
17259
17940
|
function getDatabase2(dbPath) {
|
|
@@ -19103,7 +19784,7 @@ function isValidCategory(value) {
|
|
|
19103
19784
|
return VALID_CATEGORIES.includes(value);
|
|
19104
19785
|
}
|
|
19105
19786
|
function loadConfig() {
|
|
19106
|
-
const configPath = join22(
|
|
19787
|
+
const configPath = join22(homedir11(), ".mementos", "config.json");
|
|
19107
19788
|
let fileConfig = {};
|
|
19108
19789
|
if (existsSync22(configPath)) {
|
|
19109
19790
|
try {
|
|
@@ -19130,10 +19811,10 @@ function loadConfig() {
|
|
|
19130
19811
|
return merged;
|
|
19131
19812
|
}
|
|
19132
19813
|
function profilesDir() {
|
|
19133
|
-
return join22(
|
|
19814
|
+
return join22(homedir11(), ".mementos", "profiles");
|
|
19134
19815
|
}
|
|
19135
19816
|
function globalConfigPath() {
|
|
19136
|
-
return join22(
|
|
19817
|
+
return join22(homedir11(), ".mementos", "config.json");
|
|
19137
19818
|
}
|
|
19138
19819
|
function readGlobalConfig() {
|
|
19139
19820
|
const p = globalConfigPath();
|
|
@@ -19148,7 +19829,7 @@ function readGlobalConfig() {
|
|
|
19148
19829
|
function writeGlobalConfig(data) {
|
|
19149
19830
|
const p = globalConfigPath();
|
|
19150
19831
|
ensureDir22(dirname2(p));
|
|
19151
|
-
|
|
19832
|
+
writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
|
|
19152
19833
|
}
|
|
19153
19834
|
function getActiveProfile() {
|
|
19154
19835
|
const envProfile = process.env["MEMENTOS_PROFILE"];
|
|
@@ -21813,10 +22494,10 @@ __export(exports_dist2, {
|
|
|
21813
22494
|
acquireLock: () => acquireLock2
|
|
21814
22495
|
});
|
|
21815
22496
|
import { Database as Database3 } from "bun:sqlite";
|
|
21816
|
-
import { mkdirSync as
|
|
21817
|
-
import { join as
|
|
21818
|
-
import { homedir as
|
|
21819
|
-
import { randomUUID as
|
|
22497
|
+
import { mkdirSync as mkdirSync11 } from "fs";
|
|
22498
|
+
import { join as join12, dirname as dirname3 } from "path";
|
|
22499
|
+
import { homedir as homedir12 } from "os";
|
|
22500
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
21820
22501
|
import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
21821
22502
|
import { join as join33 } from "path";
|
|
21822
22503
|
import { homedir as homedir33 } from "os";
|
|
@@ -21824,19 +22505,19 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
21824
22505
|
import { join as join23 } from "path";
|
|
21825
22506
|
import { homedir as homedir23 } from "os";
|
|
21826
22507
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
21827
|
-
import { readFileSync as readFileSync23, writeFileSync as
|
|
22508
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync33 } from "fs";
|
|
21828
22509
|
import { join as join43, dirname as dirname22 } from "path";
|
|
21829
22510
|
import { homedir as homedir42 } from "os";
|
|
21830
22511
|
function getDbPath2() {
|
|
21831
22512
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
21832
22513
|
return process.env.CONVERSATIONS_DB_PATH;
|
|
21833
|
-
return
|
|
22514
|
+
return join12(homedir12(), ".conversations", "messages.db");
|
|
21834
22515
|
}
|
|
21835
22516
|
function getDb() {
|
|
21836
22517
|
if (db)
|
|
21837
22518
|
return db;
|
|
21838
22519
|
const dbPath = getDbPath2();
|
|
21839
|
-
|
|
22520
|
+
mkdirSync11(dirname3(dbPath), { recursive: true });
|
|
21840
22521
|
db = new Database3(dbPath, { create: true });
|
|
21841
22522
|
db.exec("PRAGMA journal_mode = WAL");
|
|
21842
22523
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -22196,7 +22877,7 @@ function guessMimeType(name) {
|
|
|
22196
22877
|
function sendMessage(opts) {
|
|
22197
22878
|
const db2 = getDb();
|
|
22198
22879
|
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
22199
|
-
const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${
|
|
22880
|
+
const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID14().slice(0, 8)}`);
|
|
22200
22881
|
const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
|
|
22201
22882
|
const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
|
|
22202
22883
|
const blocking = opts.blocking ? 1 : 0;
|
|
@@ -23024,7 +23705,7 @@ function getAutoName() {
|
|
|
23024
23705
|
cachedAutoName = name;
|
|
23025
23706
|
try {
|
|
23026
23707
|
mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
|
|
23027
|
-
|
|
23708
|
+
writeFileSync5(AGENT_ID_FILE, name + `
|
|
23028
23709
|
`, "utf-8");
|
|
23029
23710
|
} catch {}
|
|
23030
23711
|
return name;
|
|
@@ -24979,7 +25660,7 @@ Your code should look like:
|
|
|
24979
25660
|
}
|
|
24980
25661
|
}
|
|
24981
25662
|
}
|
|
24982
|
-
function checkPropTypes(typeSpecs, values,
|
|
25663
|
+
function checkPropTypes(typeSpecs, values, location2, componentName, element) {
|
|
24983
25664
|
{
|
|
24984
25665
|
var has = Function.call.bind(hasOwnProperty);
|
|
24985
25666
|
for (var typeSpecName in typeSpecs) {
|
|
@@ -24987,23 +25668,23 @@ Your code should look like:
|
|
|
24987
25668
|
var error$1 = undefined;
|
|
24988
25669
|
try {
|
|
24989
25670
|
if (typeof typeSpecs[typeSpecName] !== "function") {
|
|
24990
|
-
var err = Error((componentName || "React class") + ": " +
|
|
25671
|
+
var err = Error((componentName || "React class") + ": " + location2 + " type `" + typeSpecName + "` is invalid; " + "it must be a function, usually from the `prop-types` package, but received `" + typeof typeSpecs[typeSpecName] + "`." + "This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.");
|
|
24991
25672
|
err.name = "Invariant Violation";
|
|
24992
25673
|
throw err;
|
|
24993
25674
|
}
|
|
24994
|
-
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName,
|
|
25675
|
+
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location2, null, "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED");
|
|
24995
25676
|
} catch (ex) {
|
|
24996
25677
|
error$1 = ex;
|
|
24997
25678
|
}
|
|
24998
25679
|
if (error$1 && !(error$1 instanceof Error)) {
|
|
24999
25680
|
setCurrentlyValidatingElement(element);
|
|
25000
|
-
error("%s: type specification of %s" + " `%s` is invalid; the type checker " + "function must return `null` or an `Error` but returned a %s. " + "You may have forgotten to pass an argument to the type checker " + "creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and " + "shape all require an argument).", componentName || "React class",
|
|
25681
|
+
error("%s: type specification of %s" + " `%s` is invalid; the type checker " + "function must return `null` or an `Error` but returned a %s. " + "You may have forgotten to pass an argument to the type checker " + "creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and " + "shape all require an argument).", componentName || "React class", location2, typeSpecName, typeof error$1);
|
|
25001
25682
|
setCurrentlyValidatingElement(null);
|
|
25002
25683
|
}
|
|
25003
25684
|
if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {
|
|
25004
25685
|
loggedTypeFailures[error$1.message] = true;
|
|
25005
25686
|
setCurrentlyValidatingElement(element);
|
|
25006
|
-
error("Failed %s type: %s",
|
|
25687
|
+
error("Failed %s type: %s", location2, error$1.message);
|
|
25007
25688
|
setCurrentlyValidatingElement(null);
|
|
25008
25689
|
}
|
|
25009
25690
|
}
|
|
@@ -26238,11 +26919,11 @@ __export(exports_dist3, {
|
|
|
26238
26919
|
AgentNotFoundError: () => AgentNotFoundError2
|
|
26239
26920
|
});
|
|
26240
26921
|
import { Database as Database4 } from "bun:sqlite";
|
|
26241
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
26242
|
-
import { dirname as dirname5, join as
|
|
26922
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync12 } from "fs";
|
|
26923
|
+
import { dirname as dirname5, join as join13, resolve as resolve3 } from "path";
|
|
26243
26924
|
import { existsSync as existsSync33 } from "fs";
|
|
26244
26925
|
import { join as join34 } from "path";
|
|
26245
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as
|
|
26926
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
26246
26927
|
import { join as join24 } from "path";
|
|
26247
26928
|
import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
26248
26929
|
import { join as join44 } from "path";
|
|
@@ -26472,7 +27153,7 @@ function isInMemoryDb2(path) {
|
|
|
26472
27153
|
function findNearestTodosDb(startDir) {
|
|
26473
27154
|
let dir = resolve3(startDir);
|
|
26474
27155
|
while (true) {
|
|
26475
|
-
const candidate =
|
|
27156
|
+
const candidate = join13(dir, ".todos", "todos.db");
|
|
26476
27157
|
if (existsSync7(candidate))
|
|
26477
27158
|
return candidate;
|
|
26478
27159
|
const parent = dirname5(dir);
|
|
@@ -26485,7 +27166,7 @@ function findNearestTodosDb(startDir) {
|
|
|
26485
27166
|
function findGitRoot2(startDir) {
|
|
26486
27167
|
let dir = resolve3(startDir);
|
|
26487
27168
|
while (true) {
|
|
26488
|
-
if (existsSync7(
|
|
27169
|
+
if (existsSync7(join13(dir, ".git")))
|
|
26489
27170
|
return dir;
|
|
26490
27171
|
const parent = dirname5(dir);
|
|
26491
27172
|
if (parent === dir)
|
|
@@ -26505,18 +27186,18 @@ function getDbPath3() {
|
|
|
26505
27186
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
26506
27187
|
const gitRoot = findGitRoot2(cwd);
|
|
26507
27188
|
if (gitRoot) {
|
|
26508
|
-
return
|
|
27189
|
+
return join13(gitRoot, ".todos", "todos.db");
|
|
26509
27190
|
}
|
|
26510
27191
|
}
|
|
26511
27192
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
26512
|
-
return
|
|
27193
|
+
return join13(home, ".todos", "todos.db");
|
|
26513
27194
|
}
|
|
26514
27195
|
function ensureDir3(filePath) {
|
|
26515
27196
|
if (isInMemoryDb2(filePath))
|
|
26516
27197
|
return;
|
|
26517
27198
|
const dir = dirname5(resolve3(filePath));
|
|
26518
27199
|
if (!existsSync7(dir)) {
|
|
26519
|
-
|
|
27200
|
+
mkdirSync12(dir, { recursive: true });
|
|
26520
27201
|
}
|
|
26521
27202
|
}
|
|
26522
27203
|
function getDatabase3(dbPath) {
|
|
@@ -26992,7 +27673,7 @@ function readJsonFile(path) {
|
|
|
26992
27673
|
}
|
|
26993
27674
|
}
|
|
26994
27675
|
function writeJsonFile(path, data) {
|
|
26995
|
-
|
|
27676
|
+
writeFileSync6(path, JSON.stringify(data, null, 2) + `
|
|
26996
27677
|
`);
|
|
26997
27678
|
}
|
|
26998
27679
|
function readHighWaterMark(dir) {
|
|
@@ -27003,7 +27684,7 @@ function readHighWaterMark(dir) {
|
|
|
27003
27684
|
return isNaN(val) ? 1 : val;
|
|
27004
27685
|
}
|
|
27005
27686
|
function writeHighWaterMark(dir, value) {
|
|
27006
|
-
|
|
27687
|
+
writeFileSync6(join24(dir, ".highwatermark"), String(value));
|
|
27007
27688
|
}
|
|
27008
27689
|
function getFileMtimeMs(path) {
|
|
27009
27690
|
try {
|
|
@@ -31593,9 +32274,9 @@ __export(exports_dist4, {
|
|
|
31593
32274
|
CATEGORIES: () => CATEGORIES,
|
|
31594
32275
|
AGENT_TARGETS: () => AGENT_TARGETS
|
|
31595
32276
|
});
|
|
31596
|
-
import { existsSync as existsSync8, cpSync, mkdirSync as
|
|
31597
|
-
import { join as
|
|
31598
|
-
import { homedir as
|
|
32277
|
+
import { existsSync as existsSync8, cpSync, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, rmSync as rmSync2, readdirSync as readdirSync6, statSync as statSync4, readFileSync as readFileSync7, accessSync, constants } from "fs";
|
|
32278
|
+
import { join as join14, dirname as dirname6 } from "path";
|
|
32279
|
+
import { homedir as homedir13 } from "os";
|
|
31599
32280
|
import { fileURLToPath } from "url";
|
|
31600
32281
|
import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
|
|
31601
32282
|
import { join as join25 } from "path";
|
|
@@ -31706,17 +32387,17 @@ function normalizeSkillName(name) {
|
|
|
31706
32387
|
function findSkillsDir() {
|
|
31707
32388
|
let dir = __dirname2;
|
|
31708
32389
|
for (let i = 0;i < 5; i++) {
|
|
31709
|
-
const candidate =
|
|
32390
|
+
const candidate = join14(dir, "skills");
|
|
31710
32391
|
if (existsSync8(candidate)) {
|
|
31711
32392
|
return candidate;
|
|
31712
32393
|
}
|
|
31713
32394
|
dir = dirname6(dir);
|
|
31714
32395
|
}
|
|
31715
|
-
return
|
|
32396
|
+
return join14(__dirname2, "..", "skills");
|
|
31716
32397
|
}
|
|
31717
32398
|
function getSkillPath(name) {
|
|
31718
32399
|
const skillName = normalizeSkillName(name);
|
|
31719
|
-
return
|
|
32400
|
+
return join14(SKILLS_DIR, skillName);
|
|
31720
32401
|
}
|
|
31721
32402
|
function skillExists(name) {
|
|
31722
32403
|
return existsSync8(getSkillPath(name));
|
|
@@ -31725,8 +32406,8 @@ function installSkill(name, options = {}) {
|
|
|
31725
32406
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
31726
32407
|
const skillName = normalizeSkillName(name);
|
|
31727
32408
|
const sourcePath = getSkillPath(name);
|
|
31728
|
-
const destDir =
|
|
31729
|
-
const destPath =
|
|
32409
|
+
const destDir = join14(targetDir, ".skills");
|
|
32410
|
+
const destPath = join14(destDir, skillName);
|
|
31730
32411
|
if (!existsSync8(sourcePath)) {
|
|
31731
32412
|
return {
|
|
31732
32413
|
skill: name,
|
|
@@ -31744,7 +32425,7 @@ function installSkill(name, options = {}) {
|
|
|
31744
32425
|
}
|
|
31745
32426
|
try {
|
|
31746
32427
|
if (!existsSync8(destDir)) {
|
|
31747
|
-
|
|
32428
|
+
mkdirSync13(destDir, { recursive: true });
|
|
31748
32429
|
}
|
|
31749
32430
|
if (existsSync8(destPath) && overwrite) {
|
|
31750
32431
|
rmSync2(destPath, { recursive: true, force: true });
|
|
@@ -31785,7 +32466,7 @@ function installSkills(names, options = {}) {
|
|
|
31785
32466
|
return names.map((name) => installSkill(name, options));
|
|
31786
32467
|
}
|
|
31787
32468
|
function updateSkillsIndex(skillsDir) {
|
|
31788
|
-
const indexPath =
|
|
32469
|
+
const indexPath = join14(skillsDir, "index.ts");
|
|
31789
32470
|
const meta = loadMeta(skillsDir);
|
|
31790
32471
|
const disabledSet = new Set(meta.disabled || []);
|
|
31791
32472
|
const skills = readdirSync6(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
|
|
@@ -31801,10 +32482,10 @@ function updateSkillsIndex(skillsDir) {
|
|
|
31801
32482
|
|
|
31802
32483
|
${exports}
|
|
31803
32484
|
`;
|
|
31804
|
-
|
|
32485
|
+
writeFileSync7(indexPath, content);
|
|
31805
32486
|
}
|
|
31806
32487
|
function getMetaPath(skillsDir) {
|
|
31807
|
-
return
|
|
32488
|
+
return join14(skillsDir, ".meta.json");
|
|
31808
32489
|
}
|
|
31809
32490
|
function loadMeta(skillsDir) {
|
|
31810
32491
|
const metaPath2 = getMetaPath(skillsDir);
|
|
@@ -31816,14 +32497,14 @@ function loadMeta(skillsDir) {
|
|
|
31816
32497
|
return { skills: {} };
|
|
31817
32498
|
}
|
|
31818
32499
|
function saveMeta(skillsDir, meta) {
|
|
31819
|
-
|
|
32500
|
+
writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
|
|
31820
32501
|
}
|
|
31821
32502
|
function recordInstall(skillsDir, name) {
|
|
31822
32503
|
const meta = loadMeta(skillsDir);
|
|
31823
32504
|
const skillName = normalizeSkillName(name);
|
|
31824
32505
|
let version = "unknown";
|
|
31825
32506
|
try {
|
|
31826
|
-
const pkgPath =
|
|
32507
|
+
const pkgPath = join14(skillsDir, skillName, "package.json");
|
|
31827
32508
|
if (existsSync8(pkgPath)) {
|
|
31828
32509
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
31829
32510
|
version = pkg.version || "unknown";
|
|
@@ -31838,12 +32519,12 @@ function recordRemove(skillsDir, name) {
|
|
|
31838
32519
|
saveMeta(skillsDir, meta);
|
|
31839
32520
|
}
|
|
31840
32521
|
function getInstallMeta(targetDir = process.cwd()) {
|
|
31841
|
-
return loadMeta(
|
|
32522
|
+
return loadMeta(join14(targetDir, ".skills"));
|
|
31842
32523
|
}
|
|
31843
32524
|
function disableSkill(name, targetDir = process.cwd()) {
|
|
31844
|
-
const skillsDir =
|
|
32525
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
31845
32526
|
const skillName = normalizeSkillName(name);
|
|
31846
|
-
if (!existsSync8(
|
|
32527
|
+
if (!existsSync8(join14(skillsDir, skillName)))
|
|
31847
32528
|
return false;
|
|
31848
32529
|
const meta = loadMeta(skillsDir);
|
|
31849
32530
|
const disabled = new Set(meta.disabled || []);
|
|
@@ -31856,7 +32537,7 @@ function disableSkill(name, targetDir = process.cwd()) {
|
|
|
31856
32537
|
return true;
|
|
31857
32538
|
}
|
|
31858
32539
|
function enableSkill(name, targetDir = process.cwd()) {
|
|
31859
|
-
const skillsDir =
|
|
32540
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
31860
32541
|
const meta = loadMeta(skillsDir);
|
|
31861
32542
|
const disabled = new Set(meta.disabled || []);
|
|
31862
32543
|
if (!disabled.has(name))
|
|
@@ -31868,23 +32549,23 @@ function enableSkill(name, targetDir = process.cwd()) {
|
|
|
31868
32549
|
return true;
|
|
31869
32550
|
}
|
|
31870
32551
|
function getDisabledSkills(targetDir = process.cwd()) {
|
|
31871
|
-
const meta = loadMeta(
|
|
32552
|
+
const meta = loadMeta(join14(targetDir, ".skills"));
|
|
31872
32553
|
return meta.disabled || [];
|
|
31873
32554
|
}
|
|
31874
32555
|
function getInstalledSkills(targetDir = process.cwd()) {
|
|
31875
|
-
const skillsDir =
|
|
32556
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
31876
32557
|
if (!existsSync8(skillsDir)) {
|
|
31877
32558
|
return [];
|
|
31878
32559
|
}
|
|
31879
32560
|
return readdirSync6(skillsDir).filter((f) => {
|
|
31880
|
-
const fullPath =
|
|
32561
|
+
const fullPath = join14(skillsDir, f);
|
|
31881
32562
|
return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
|
|
31882
32563
|
}).map((f) => f.replace("skill-", ""));
|
|
31883
32564
|
}
|
|
31884
32565
|
function removeSkill(name, targetDir = process.cwd()) {
|
|
31885
32566
|
const skillName = normalizeSkillName(name);
|
|
31886
|
-
const skillsDir =
|
|
31887
|
-
const skillPath =
|
|
32567
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
32568
|
+
const skillPath = join14(skillsDir, skillName);
|
|
31888
32569
|
if (!existsSync8(skillPath)) {
|
|
31889
32570
|
return false;
|
|
31890
32571
|
}
|
|
@@ -31896,13 +32577,13 @@ function removeSkill(name, targetDir = process.cwd()) {
|
|
|
31896
32577
|
function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
31897
32578
|
const agentDir = `.${agent}`;
|
|
31898
32579
|
if (scope === "project") {
|
|
31899
|
-
return
|
|
32580
|
+
return join14(projectDir || process.cwd(), agentDir, "skills");
|
|
31900
32581
|
}
|
|
31901
|
-
return
|
|
32582
|
+
return join14(homedir13(), agentDir, "skills");
|
|
31902
32583
|
}
|
|
31903
32584
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
31904
32585
|
const skillName = normalizeSkillName(name);
|
|
31905
|
-
return
|
|
32586
|
+
return join14(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
31906
32587
|
}
|
|
31907
32588
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
31908
32589
|
const { agent, scope = "global", projectDir } = options;
|
|
@@ -31912,7 +32593,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
31912
32593
|
return { skill: name, success: false, error: `Skill '${name}' not found` };
|
|
31913
32594
|
}
|
|
31914
32595
|
let skillMdContent = null;
|
|
31915
|
-
const skillMdPath =
|
|
32596
|
+
const skillMdPath = join14(sourcePath, "SKILL.md");
|
|
31916
32597
|
if (existsSync8(skillMdPath)) {
|
|
31917
32598
|
skillMdContent = readFileSync7(skillMdPath, "utf-8");
|
|
31918
32599
|
} else if (generateSkillMd) {
|
|
@@ -31923,7 +32604,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
31923
32604
|
}
|
|
31924
32605
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
31925
32606
|
if (scope === "global") {
|
|
31926
|
-
const agentBaseDir2 =
|
|
32607
|
+
const agentBaseDir2 = join14(homedir13(), `.${agent}`);
|
|
31927
32608
|
if (!existsSync8(agentBaseDir2)) {
|
|
31928
32609
|
const agentLabels = {
|
|
31929
32610
|
claude: "Claude Code",
|
|
@@ -31947,8 +32628,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
31947
32628
|
}
|
|
31948
32629
|
}
|
|
31949
32630
|
try {
|
|
31950
|
-
|
|
31951
|
-
|
|
32631
|
+
mkdirSync13(destDir, { recursive: true });
|
|
32632
|
+
writeFileSync7(join14(destDir, "SKILL.md"), skillMdContent);
|
|
31952
32633
|
return { skill: name, success: true, path: destDir };
|
|
31953
32634
|
} catch (error) {
|
|
31954
32635
|
return {
|
|
@@ -33885,7 +34566,7 @@ __export(exports_cron_manager, {
|
|
|
33885
34566
|
deleteCronJob: () => deleteCronJob,
|
|
33886
34567
|
createCronJob: () => createCronJob
|
|
33887
34568
|
});
|
|
33888
|
-
import { randomUUID as
|
|
34569
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
33889
34570
|
function ensureCronTable() {
|
|
33890
34571
|
const db2 = getDatabase();
|
|
33891
34572
|
db2.exec(`
|
|
@@ -33914,7 +34595,7 @@ function ensureCronTable() {
|
|
|
33914
34595
|
function createCronJob(schedule, task, name) {
|
|
33915
34596
|
ensureCronTable();
|
|
33916
34597
|
const db2 = getDatabase();
|
|
33917
|
-
const id =
|
|
34598
|
+
const id = randomUUID15();
|
|
33918
34599
|
db2.prepare(`
|
|
33919
34600
|
INSERT INTO cron_jobs (id, name, schedule, task_json, enabled)
|
|
33920
34601
|
VALUES (?, ?, ?, ?, 1)
|
|
@@ -33972,7 +34653,7 @@ function getCronEvents(jobId, limit = 10) {
|
|
|
33972
34653
|
async function executeCronJob(job) {
|
|
33973
34654
|
ensureCronTable();
|
|
33974
34655
|
const db2 = getDatabase();
|
|
33975
|
-
const eventId =
|
|
34656
|
+
const eventId = randomUUID15();
|
|
33976
34657
|
const startedAt = new Date().toISOString();
|
|
33977
34658
|
db2.prepare("INSERT INTO cron_events (id, job_id, started_at) VALUES (?, ?, ?)").run(eventId, job.id, startedAt);
|
|
33978
34659
|
try {
|
|
@@ -34063,7 +34744,7 @@ __export(exports_url_watcher, {
|
|
|
34063
34744
|
deleteWatchJob: () => deleteWatchJob,
|
|
34064
34745
|
createWatchJob: () => createWatchJob
|
|
34065
34746
|
});
|
|
34066
|
-
import { randomUUID as
|
|
34747
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
34067
34748
|
import { createHash } from "crypto";
|
|
34068
34749
|
function ensureWatchTables() {
|
|
34069
34750
|
const db2 = getDatabase();
|
|
@@ -34095,7 +34776,7 @@ function ensureWatchTables() {
|
|
|
34095
34776
|
function createWatchJob(url, schedule, opts) {
|
|
34096
34777
|
ensureWatchTables();
|
|
34097
34778
|
const db2 = getDatabase();
|
|
34098
|
-
const id =
|
|
34779
|
+
const id = randomUUID16();
|
|
34099
34780
|
db2.prepare(`
|
|
34100
34781
|
INSERT INTO watch_jobs (id, name, url, schedule, selector, extract_schema, enabled)
|
|
34101
34782
|
VALUES (?, ?, ?, ?, ?, ?, 1)
|
|
@@ -34131,7 +34812,7 @@ function getWatchEvents(watchId, limit = 20) {
|
|
|
34131
34812
|
async function checkWatchJob(job) {
|
|
34132
34813
|
ensureWatchTables();
|
|
34133
34814
|
const db2 = getDatabase();
|
|
34134
|
-
const eventId =
|
|
34815
|
+
const eventId = randomUUID16();
|
|
34135
34816
|
const checkedAt = new Date().toISOString();
|
|
34136
34817
|
let newContent = "";
|
|
34137
34818
|
try {
|
|
@@ -34307,7 +34988,7 @@ var exports_mcp = {};
|
|
|
34307
34988
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
34308
34989
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
34309
34990
|
import { readFileSync as readFileSync8 } from "fs";
|
|
34310
|
-
import { join as
|
|
34991
|
+
import { join as join15 } from "path";
|
|
34311
34992
|
function json(data) {
|
|
34312
34993
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
34313
34994
|
}
|
|
@@ -34372,7 +35053,7 @@ var init_mcp = __esm(async () => {
|
|
|
34372
35053
|
init_dialogs();
|
|
34373
35054
|
init_profiles();
|
|
34374
35055
|
init_types();
|
|
34375
|
-
_pkg = JSON.parse(readFileSync8(
|
|
35056
|
+
_pkg = JSON.parse(readFileSync8(join15(import.meta.dir, "../../package.json"), "utf8"));
|
|
34376
35057
|
networkLogCleanup = new Map;
|
|
34377
35058
|
consoleCaptureCleanup = new Map;
|
|
34378
35059
|
harCaptures = new Map;
|
|
@@ -35054,6 +35735,28 @@ var init_mcp = __esm(async () => {
|
|
|
35054
35735
|
return err(e);
|
|
35055
35736
|
}
|
|
35056
35737
|
});
|
|
35738
|
+
server.tool("browser_detect_env", "Detect if the current page is running in production, development, staging, or local environment. Analyzes URL, meta tags, source maps, analytics SDKs, and more.", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
35739
|
+
try {
|
|
35740
|
+
const sid = resolveSessionId(session_id);
|
|
35741
|
+
const page = getSessionPage(sid);
|
|
35742
|
+
const { detectEnvironment: detectEnvironment2 } = await Promise.resolve().then(() => exports_env_detector);
|
|
35743
|
+
const result = await detectEnvironment2(page);
|
|
35744
|
+
return json(result);
|
|
35745
|
+
} catch (e) {
|
|
35746
|
+
return err(e);
|
|
35747
|
+
}
|
|
35748
|
+
});
|
|
35749
|
+
server.tool("browser_performance_deep", "Deep performance analysis: Web Vitals, resource breakdown by type, largest resources, third-party scripts with categories, DOM complexity, memory usage.", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
35750
|
+
try {
|
|
35751
|
+
const sid = resolveSessionId(session_id);
|
|
35752
|
+
const page = getSessionPage(sid);
|
|
35753
|
+
const { getDeepPerformance: getDeepPerformance2 } = await Promise.resolve().then(() => (init_deep_performance(), exports_deep_performance));
|
|
35754
|
+
const result = await getDeepPerformance2(page);
|
|
35755
|
+
return json(result);
|
|
35756
|
+
} catch (e) {
|
|
35757
|
+
return err(e);
|
|
35758
|
+
}
|
|
35759
|
+
});
|
|
35057
35760
|
server.tool("browser_console_log", "Get captured console messages for a session", { session_id: exports_external.string().optional(), level: exports_external.enum(["log", "warn", "error", "debug", "info"]).optional() }, async ({ session_id, level }) => {
|
|
35058
35761
|
try {
|
|
35059
35762
|
const sid = resolveSessionId(session_id);
|
|
@@ -35117,6 +35820,46 @@ var init_mcp = __esm(async () => {
|
|
|
35117
35820
|
return err(e);
|
|
35118
35821
|
}
|
|
35119
35822
|
});
|
|
35823
|
+
server.tool("browser_workflow_save", "Save a recording as a reusable workflow with self-healing replay", { recording_id: exports_external.string(), name: exports_external.string(), description: exports_external.string().optional() }, async ({ recording_id, name, description }) => {
|
|
35824
|
+
try {
|
|
35825
|
+
const { saveWorkflowFromRecording: saveWorkflowFromRecording2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
35826
|
+
return json(saveWorkflowFromRecording2(recording_id, name, description));
|
|
35827
|
+
} catch (e) {
|
|
35828
|
+
return err(e);
|
|
35829
|
+
}
|
|
35830
|
+
});
|
|
35831
|
+
server.tool("browser_workflow_list", "List all saved workflows", {}, async () => {
|
|
35832
|
+
try {
|
|
35833
|
+
const { listWorkflows: listWorkflows2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
35834
|
+
const workflows = listWorkflows2();
|
|
35835
|
+
return json({ workflows: workflows.map((w) => ({ ...w, steps: `${w.steps.length} steps` })), count: workflows.length });
|
|
35836
|
+
} catch (e) {
|
|
35837
|
+
return err(e);
|
|
35838
|
+
}
|
|
35839
|
+
});
|
|
35840
|
+
server.tool("browser_workflow_run", "Run a saved workflow with self-healing. If selectors changed, auto-adapts and reports what was healed.", { session_id: exports_external.string().optional(), name: exports_external.string() }, async ({ session_id, name }) => {
|
|
35841
|
+
try {
|
|
35842
|
+
const sid = resolveSessionId(session_id);
|
|
35843
|
+
const page = getSessionPage(sid);
|
|
35844
|
+
const { getWorkflowByName: getWorkflowByName2, runWorkflow: runWorkflow2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
35845
|
+
const workflow = getWorkflowByName2(name);
|
|
35846
|
+
if (!workflow)
|
|
35847
|
+
return err(new Error(`Workflow '${name}' not found`));
|
|
35848
|
+
const result = await runWorkflow2(workflow, page);
|
|
35849
|
+
logEvent(sid, "workflow_run", { name, ...result });
|
|
35850
|
+
return json(result);
|
|
35851
|
+
} catch (e) {
|
|
35852
|
+
return err(e);
|
|
35853
|
+
}
|
|
35854
|
+
});
|
|
35855
|
+
server.tool("browser_workflow_delete", "Delete a saved workflow", { name: exports_external.string() }, async ({ name }) => {
|
|
35856
|
+
try {
|
|
35857
|
+
const { deleteWorkflow: deleteWorkflow2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
35858
|
+
return json({ deleted: deleteWorkflow2(name) });
|
|
35859
|
+
} catch (e) {
|
|
35860
|
+
return err(e);
|
|
35861
|
+
}
|
|
35862
|
+
});
|
|
35120
35863
|
server.tool("browser_crawl", "Crawl a URL recursively and return discovered pages", {
|
|
35121
35864
|
url: exports_external.string(),
|
|
35122
35865
|
max_depth: exports_external.number().optional().default(2),
|
|
@@ -35812,6 +36555,68 @@ var init_mcp = __esm(async () => {
|
|
|
35812
36555
|
return err(e);
|
|
35813
36556
|
}
|
|
35814
36557
|
});
|
|
36558
|
+
server.tool("browser_detect_apis", "Scan network traffic for JSON API endpoints. Returns discovered endpoints with methods, status codes, and URLs.", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
36559
|
+
try {
|
|
36560
|
+
const sid = resolveSessionId(session_id);
|
|
36561
|
+
const { detectAPIs: detectAPIs2 } = await Promise.resolve().then(() => (init_api_detector(), exports_api_detector));
|
|
36562
|
+
const apis = detectAPIs2(sid);
|
|
36563
|
+
return json({ apis, count: apis.length });
|
|
36564
|
+
} catch (e) {
|
|
36565
|
+
return err(e);
|
|
36566
|
+
}
|
|
36567
|
+
});
|
|
36568
|
+
server.tool("browser_extract_structured", "Extract structured data from page: tables, lists, JSON-LD, Open Graph, meta tags, and repeated elements (cards/items).", { session_id: exports_external.string().optional() }, async ({ session_id }) => {
|
|
36569
|
+
try {
|
|
36570
|
+
const sid = resolveSessionId(session_id);
|
|
36571
|
+
const page = getSessionPage(sid);
|
|
36572
|
+
const { extractStructuredData: extractStructuredData2 } = await Promise.resolve().then(() => exports_structured_extract);
|
|
36573
|
+
const data = await extractStructuredData2(page);
|
|
36574
|
+
return json({
|
|
36575
|
+
tables: data.tables.length,
|
|
36576
|
+
lists: data.lists.length,
|
|
36577
|
+
json_ld: data.jsonLd.length,
|
|
36578
|
+
open_graph: Object.keys(data.openGraph).length,
|
|
36579
|
+
meta_tags: Object.keys(data.metaTags).length,
|
|
36580
|
+
repeated_elements: data.repeatedElements.length,
|
|
36581
|
+
data
|
|
36582
|
+
});
|
|
36583
|
+
} catch (e) {
|
|
36584
|
+
return err(e);
|
|
36585
|
+
}
|
|
36586
|
+
});
|
|
36587
|
+
server.tool("browser_dataset_save", "Save extracted data as a named dataset for later use", { name: exports_external.string(), data: exports_external.array(exports_external.record(exports_external.unknown())), source_url: exports_external.string().optional() }, async ({ name, data, source_url }) => {
|
|
36588
|
+
try {
|
|
36589
|
+
const { saveDataset: saveDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
36590
|
+
const dataset = saveDataset2({ name, rows: data, sourceUrl: source_url });
|
|
36591
|
+
return json({ id: dataset.id, name: dataset.name, row_count: dataset.row_count });
|
|
36592
|
+
} catch (e) {
|
|
36593
|
+
return err(e);
|
|
36594
|
+
}
|
|
36595
|
+
});
|
|
36596
|
+
server.tool("browser_dataset_list", "List all saved datasets", {}, async () => {
|
|
36597
|
+
try {
|
|
36598
|
+
const { listDatasets: listDatasets2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
36599
|
+
return json({ datasets: listDatasets2() });
|
|
36600
|
+
} catch (e) {
|
|
36601
|
+
return err(e);
|
|
36602
|
+
}
|
|
36603
|
+
});
|
|
36604
|
+
server.tool("browser_dataset_export", "Export a dataset as JSON or CSV file", { name: exports_external.string(), format: exports_external.enum(["json", "csv"]).optional().default("json") }, async ({ name, format }) => {
|
|
36605
|
+
try {
|
|
36606
|
+
const { exportDataset: exportDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
36607
|
+
return json(exportDataset2(name, format));
|
|
36608
|
+
} catch (e) {
|
|
36609
|
+
return err(e);
|
|
36610
|
+
}
|
|
36611
|
+
});
|
|
36612
|
+
server.tool("browser_dataset_delete", "Delete a saved dataset", { name: exports_external.string() }, async ({ name }) => {
|
|
36613
|
+
try {
|
|
36614
|
+
const { deleteDataset: deleteDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
36615
|
+
return json({ deleted: deleteDataset2(name) });
|
|
36616
|
+
} catch (e) {
|
|
36617
|
+
return err(e);
|
|
36618
|
+
}
|
|
36619
|
+
});
|
|
35815
36620
|
server.tool("browser_help", "Show all available browser tools grouped by category with one-line descriptions", {}, async () => {
|
|
35816
36621
|
try {
|
|
35817
36622
|
const groups = {
|
|
@@ -35898,6 +36703,20 @@ var init_mcp = __esm(async () => {
|
|
|
35898
36703
|
{ tool: "browser_auth_list", description: "List all saved auth flows" },
|
|
35899
36704
|
{ tool: "browser_auth_delete", description: "Delete a saved auth flow" }
|
|
35900
36705
|
],
|
|
36706
|
+
Workflows: [
|
|
36707
|
+
{ tool: "browser_workflow_save", description: "Save a recording as a reusable workflow" },
|
|
36708
|
+
{ tool: "browser_workflow_list", description: "List all saved workflows" },
|
|
36709
|
+
{ tool: "browser_workflow_run", description: "Run a workflow with self-healing replay" },
|
|
36710
|
+
{ tool: "browser_workflow_delete", description: "Delete a saved workflow" }
|
|
36711
|
+
],
|
|
36712
|
+
Data: [
|
|
36713
|
+
{ tool: "browser_extract_structured", description: "Extract tables, lists, JSON-LD, Open Graph, meta tags, repeated elements" },
|
|
36714
|
+
{ tool: "browser_detect_apis", description: "Scan network traffic for JSON API endpoints" },
|
|
36715
|
+
{ tool: "browser_dataset_save", description: "Save extracted data as a named dataset" },
|
|
36716
|
+
{ tool: "browser_dataset_list", description: "List all saved datasets" },
|
|
36717
|
+
{ tool: "browser_dataset_export", description: "Export dataset as JSON or CSV" },
|
|
36718
|
+
{ tool: "browser_dataset_delete", description: "Delete a saved dataset" }
|
|
36719
|
+
],
|
|
35901
36720
|
Crawl: [
|
|
35902
36721
|
{ tool: "browser_crawl", description: "Crawl a URL recursively" }
|
|
35903
36722
|
],
|
|
@@ -35951,6 +36770,8 @@ var init_mcp = __esm(async () => {
|
|
|
35951
36770
|
{ tool: "browser_check", description: "RECOMMENDED: One-call page summary with diagnostics" },
|
|
35952
36771
|
{ tool: "browser_version", description: "Show running binary version and tool count" },
|
|
35953
36772
|
{ tool: "browser_help", description: "Show this help (all tools)" },
|
|
36773
|
+
{ tool: "browser_detect_env", description: "Detect environment (prod/dev/staging/local)" },
|
|
36774
|
+
{ tool: "browser_performance_deep", description: "Deep performance: resources, third-party, DOM, memory" },
|
|
35954
36775
|
{ tool: "browser_snapshot_diff", description: "Diff current snapshot vs previous" },
|
|
35955
36776
|
{ tool: "browser_watch_start", description: "Watch page for DOM changes" },
|
|
35956
36777
|
{ tool: "browser_watch_get_changes", description: "Get captured DOM changes" },
|
|
@@ -36468,10 +37289,10 @@ __export(exports_snapshots, {
|
|
|
36468
37289
|
deleteSnapshot: () => deleteSnapshot,
|
|
36469
37290
|
createSnapshot: () => createSnapshot
|
|
36470
37291
|
});
|
|
36471
|
-
import { randomUUID as
|
|
37292
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
36472
37293
|
function createSnapshot(data) {
|
|
36473
37294
|
const db2 = getDatabase();
|
|
36474
|
-
const id =
|
|
37295
|
+
const id = randomUUID17();
|
|
36475
37296
|
db2.prepare("INSERT INTO snapshots (id, session_id, url, title, html, screenshot_path) VALUES (?, ?, ?, ?, ?, ?)").run(id, data.session_id, data.url, data.title ?? null, data.html ?? null, data.screenshot_path ?? null);
|
|
36476
37297
|
return getSnapshot(id);
|
|
36477
37298
|
}
|
|
@@ -36497,7 +37318,7 @@ var init_snapshots = __esm(() => {
|
|
|
36497
37318
|
|
|
36498
37319
|
// src/server/index.ts
|
|
36499
37320
|
var exports_server = {};
|
|
36500
|
-
import { join as
|
|
37321
|
+
import { join as join16 } from "path";
|
|
36501
37322
|
import { existsSync as existsSync9 } from "fs";
|
|
36502
37323
|
function ok(data, status = 200) {
|
|
36503
37324
|
return new Response(JSON.stringify(data), {
|
|
@@ -36791,13 +37612,13 @@ var init_server = __esm(() => {
|
|
|
36791
37612
|
const id = path.split("/")[3];
|
|
36792
37613
|
return ok({ deleted: deleteDownload(id) });
|
|
36793
37614
|
}
|
|
36794
|
-
const dashboardDist =
|
|
37615
|
+
const dashboardDist = join16(import.meta.dir, "../../dashboard/dist");
|
|
36795
37616
|
if (existsSync9(dashboardDist)) {
|
|
36796
|
-
const filePath = path === "/" ?
|
|
37617
|
+
const filePath = path === "/" ? join16(dashboardDist, "index.html") : join16(dashboardDist, path);
|
|
36797
37618
|
if (existsSync9(filePath)) {
|
|
36798
37619
|
return new Response(Bun.file(filePath), { headers: CORS_HEADERS });
|
|
36799
37620
|
}
|
|
36800
|
-
return new Response(Bun.file(
|
|
37621
|
+
return new Response(Bun.file(join16(dashboardDist, "index.html")), { headers: CORS_HEADERS });
|
|
36801
37622
|
}
|
|
36802
37623
|
if (path === "/" || path === "") {
|
|
36803
37624
|
return new Response("@hasna/browser REST API running. Dashboard not built.", {
|
|
@@ -36840,9 +37661,9 @@ init_recorder();
|
|
|
36840
37661
|
init_recordings();
|
|
36841
37662
|
init_lightpanda();
|
|
36842
37663
|
import { readFileSync as readFileSync9 } from "fs";
|
|
36843
|
-
import { join as
|
|
37664
|
+
import { join as join17 } from "path";
|
|
36844
37665
|
import chalk from "chalk";
|
|
36845
|
-
var pkg = JSON.parse(readFileSync9(
|
|
37666
|
+
var pkg = JSON.parse(readFileSync9(join17(import.meta.dir, "../../package.json"), "utf8"));
|
|
36846
37667
|
var program2 = new Command;
|
|
36847
37668
|
program2.name("browser").description("@hasna/browser \u2014 general-purpose browser agent CLI").version(pkg.version);
|
|
36848
37669
|
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) => {
|