@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/mcp/index.js
CHANGED
|
@@ -324,6 +324,54 @@ function runMigrations(db) {
|
|
|
324
324
|
CREATE INDEX IF NOT EXISTS idx_auth_flows_domain ON auth_flows(domain);
|
|
325
325
|
CREATE INDEX IF NOT EXISTS idx_auth_flows_name ON auth_flows(name);
|
|
326
326
|
`
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
version: 7,
|
|
330
|
+
sql: `
|
|
331
|
+
CREATE TABLE IF NOT EXISTS workflows (
|
|
332
|
+
id TEXT PRIMARY KEY,
|
|
333
|
+
name TEXT NOT NULL UNIQUE,
|
|
334
|
+
description TEXT,
|
|
335
|
+
steps TEXT NOT NULL DEFAULT '[]',
|
|
336
|
+
start_url TEXT,
|
|
337
|
+
last_run TEXT,
|
|
338
|
+
last_heal TEXT,
|
|
339
|
+
heal_count INTEGER DEFAULT 0,
|
|
340
|
+
run_count INTEGER DEFAULT 0,
|
|
341
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
342
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
343
|
+
);
|
|
344
|
+
`
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
version: 8,
|
|
348
|
+
sql: `
|
|
349
|
+
CREATE TABLE IF NOT EXISTS datasets (
|
|
350
|
+
id TEXT PRIMARY KEY,
|
|
351
|
+
name TEXT NOT NULL UNIQUE,
|
|
352
|
+
source_url TEXT,
|
|
353
|
+
source_type TEXT NOT NULL DEFAULT 'page',
|
|
354
|
+
data TEXT NOT NULL DEFAULT '[]',
|
|
355
|
+
schema TEXT,
|
|
356
|
+
row_count INTEGER DEFAULT 0,
|
|
357
|
+
last_refresh TEXT,
|
|
358
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
359
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
CREATE TABLE IF NOT EXISTS api_endpoints (
|
|
363
|
+
id TEXT PRIMARY KEY,
|
|
364
|
+
session_id TEXT,
|
|
365
|
+
url TEXT NOT NULL,
|
|
366
|
+
method TEXT DEFAULT 'GET',
|
|
367
|
+
response_schema TEXT,
|
|
368
|
+
sample_response TEXT,
|
|
369
|
+
status_code INTEGER,
|
|
370
|
+
content_type TEXT,
|
|
371
|
+
discovered_at TEXT DEFAULT (datetime('now'))
|
|
372
|
+
);
|
|
373
|
+
CREATE INDEX IF NOT EXISTS idx_api_endpoints_session ON api_endpoints(session_id);
|
|
374
|
+
`
|
|
327
375
|
}
|
|
328
376
|
];
|
|
329
377
|
for (const m of migrations) {
|
|
@@ -1284,14 +1332,14 @@ function enableConsoleCapture(page, sessionId) {
|
|
|
1284
1332
|
warning: "warn"
|
|
1285
1333
|
};
|
|
1286
1334
|
const level = levelMap[msg.type()] ?? "log";
|
|
1287
|
-
const
|
|
1335
|
+
const location2 = msg.location();
|
|
1288
1336
|
try {
|
|
1289
1337
|
logConsoleMessage({
|
|
1290
1338
|
session_id: sessionId,
|
|
1291
1339
|
level,
|
|
1292
1340
|
message: msg.text(),
|
|
1293
|
-
source:
|
|
1294
|
-
line_number:
|
|
1341
|
+
source: location2.url || undefined,
|
|
1342
|
+
line_number: location2.lineNumber || undefined
|
|
1295
1343
|
});
|
|
1296
1344
|
} catch {}
|
|
1297
1345
|
};
|
|
@@ -9954,6 +10002,419 @@ var init_annotate = __esm(() => {
|
|
|
9954
10002
|
import_sharp3 = __toESM(require_lib(), 1);
|
|
9955
10003
|
});
|
|
9956
10004
|
|
|
10005
|
+
// src/lib/env-detector.ts
|
|
10006
|
+
var exports_env_detector = {};
|
|
10007
|
+
__export(exports_env_detector, {
|
|
10008
|
+
detectEnvironment: () => detectEnvironment
|
|
10009
|
+
});
|
|
10010
|
+
async function detectEnvironment(page) {
|
|
10011
|
+
const url = page.url();
|
|
10012
|
+
const signals = [];
|
|
10013
|
+
let score = { local: 0, dev: 0, staging: 0, prod: 0 };
|
|
10014
|
+
try {
|
|
10015
|
+
const u = new URL(url);
|
|
10016
|
+
if (u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "0.0.0.0" || u.hostname.endsWith(".local")) {
|
|
10017
|
+
score.local += 5;
|
|
10018
|
+
signals.push(`URL hostname: ${u.hostname} \u2192 local`);
|
|
10019
|
+
} else if (u.hostname.match(/^(dev|development)\./i) || u.port !== "") {
|
|
10020
|
+
score.dev += 4;
|
|
10021
|
+
signals.push(`URL pattern: ${u.hostname}:${u.port} \u2192 dev`);
|
|
10022
|
+
} else if (u.hostname.match(/^(staging|stg|stage|preprod|uat)\./i)) {
|
|
10023
|
+
score.staging += 4;
|
|
10024
|
+
signals.push(`URL pattern: ${u.hostname} \u2192 staging`);
|
|
10025
|
+
} else {
|
|
10026
|
+
score.prod += 2;
|
|
10027
|
+
signals.push(`URL looks production: ${u.hostname}`);
|
|
10028
|
+
}
|
|
10029
|
+
if (u.port && !["80", "443", ""].includes(u.port)) {
|
|
10030
|
+
score.dev += 2;
|
|
10031
|
+
signals.push(`Non-standard port: ${u.port}`);
|
|
10032
|
+
}
|
|
10033
|
+
if (u.protocol === "https:") {
|
|
10034
|
+
score.prod += 1;
|
|
10035
|
+
signals.push("HTTPS \u2192 likely prod");
|
|
10036
|
+
} else {
|
|
10037
|
+
score.dev += 2;
|
|
10038
|
+
signals.push("HTTP \u2192 likely dev/local");
|
|
10039
|
+
}
|
|
10040
|
+
} catch {}
|
|
10041
|
+
try {
|
|
10042
|
+
const pageSignals = await page.evaluate(() => {
|
|
10043
|
+
const s = [];
|
|
10044
|
+
const w = window;
|
|
10045
|
+
const envVars = ["__ENV__", "__NEXT_DATA__", "__NUXT__", "process"];
|
|
10046
|
+
for (const v of envVars) {
|
|
10047
|
+
if (w[v]) {
|
|
10048
|
+
const env2 = w[v]?.env?.NODE_ENV ?? w[v]?.runtimeConfig?.public?.env ?? w[v]?.props?.pageProps?.env;
|
|
10049
|
+
if (env2)
|
|
10050
|
+
s.push(`window.${v}: ${env2}`);
|
|
10051
|
+
}
|
|
10052
|
+
}
|
|
10053
|
+
if (w.__REACT_DEVTOOLS_GLOBAL_HOOK__?.renderers?.size > 0) {
|
|
10054
|
+
const fiber = document.querySelector("[data-reactroot]") || document.getElementById("__next") || document.getElementById("root");
|
|
10055
|
+
if (fiber)
|
|
10056
|
+
s.push("React app detected");
|
|
10057
|
+
}
|
|
10058
|
+
const envMeta = document.querySelector('meta[name="environment"], meta[name="env"], meta[name="deploy-env"]');
|
|
10059
|
+
if (envMeta)
|
|
10060
|
+
s.push(`meta[environment]: ${envMeta.getAttribute("content")}`);
|
|
10061
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
10062
|
+
let minified = 0, unminified = 0;
|
|
10063
|
+
scripts.forEach((s2) => {
|
|
10064
|
+
const src = s2.getAttribute("src") ?? "";
|
|
10065
|
+
if (src.includes(".min.") || src.match(/\.[a-f0-9]{8,}\./))
|
|
10066
|
+
minified++;
|
|
10067
|
+
else if (src.endsWith(".js") && !src.includes("chunk"))
|
|
10068
|
+
unminified++;
|
|
10069
|
+
});
|
|
10070
|
+
if (unminified > minified && unminified > 2)
|
|
10071
|
+
s.push(`Unminified scripts (${unminified}/${minified + unminified}) \u2192 likely dev`);
|
|
10072
|
+
else if (minified > 0)
|
|
10073
|
+
s.push(`Minified/hashed scripts (${minified}/${minified + unminified}) \u2192 likely prod`);
|
|
10074
|
+
if (document.querySelector("[data-testid]"))
|
|
10075
|
+
s.push("data-testid attributes present \u2192 dev/staging");
|
|
10076
|
+
if ("serviceWorker" in navigator && navigator.serviceWorker.controller) {
|
|
10077
|
+
s.push("Service worker active \u2192 likely prod");
|
|
10078
|
+
}
|
|
10079
|
+
if (w.Sentry)
|
|
10080
|
+
s.push("Sentry SDK loaded \u2192 prod monitoring");
|
|
10081
|
+
if (w.__DATADOG_SYNTHETICS_INLINED_SCRIPT)
|
|
10082
|
+
s.push("Datadog loaded \u2192 prod monitoring");
|
|
10083
|
+
if (w.LogRocket)
|
|
10084
|
+
s.push("LogRocket loaded \u2192 prod monitoring");
|
|
10085
|
+
if (w._lr_loaded)
|
|
10086
|
+
s.push("LogRocket loaded \u2192 prod monitoring");
|
|
10087
|
+
if (w.gtag || w.ga)
|
|
10088
|
+
s.push("Google Analytics loaded \u2192 likely prod");
|
|
10089
|
+
if (w.posthog || w._ph)
|
|
10090
|
+
s.push("PostHog loaded \u2192 prod analytics");
|
|
10091
|
+
if (w.mixpanel)
|
|
10092
|
+
s.push("Mixpanel loaded \u2192 prod analytics");
|
|
10093
|
+
const robots = document.querySelector('meta[name="robots"]');
|
|
10094
|
+
if (robots) {
|
|
10095
|
+
const content = robots.getAttribute("content") ?? "";
|
|
10096
|
+
if (content.includes("noindex"))
|
|
10097
|
+
s.push(`robots: noindex \u2192 staging/dev`);
|
|
10098
|
+
}
|
|
10099
|
+
return s;
|
|
10100
|
+
});
|
|
10101
|
+
for (const signal of pageSignals) {
|
|
10102
|
+
signals.push(signal);
|
|
10103
|
+
if (signal.includes("development") || signal.includes("\u2192 dev") || signal.includes("\u2192 likely dev"))
|
|
10104
|
+
score.dev += 2;
|
|
10105
|
+
if (signal.includes("production") || signal.includes("\u2192 prod") || signal.includes("\u2192 likely prod"))
|
|
10106
|
+
score.prod += 2;
|
|
10107
|
+
if (signal.includes("staging") || signal.includes("\u2192 staging"))
|
|
10108
|
+
score.staging += 2;
|
|
10109
|
+
if (signal.includes("monitoring") || signal.includes("analytics"))
|
|
10110
|
+
score.prod += 1;
|
|
10111
|
+
if (signal.includes("noindex")) {
|
|
10112
|
+
score.staging += 2;
|
|
10113
|
+
score.dev += 1;
|
|
10114
|
+
}
|
|
10115
|
+
}
|
|
10116
|
+
} catch {}
|
|
10117
|
+
const entries = Object.entries(score);
|
|
10118
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
10119
|
+
const [env, topScore] = entries[0];
|
|
10120
|
+
const [, secondScore] = entries[1];
|
|
10121
|
+
const confidence = topScore >= 5 ? "high" : topScore > secondScore + 1 ? "medium" : "low";
|
|
10122
|
+
return { env, confidence, signals };
|
|
10123
|
+
}
|
|
10124
|
+
|
|
10125
|
+
// src/lib/deep-performance.ts
|
|
10126
|
+
var exports_deep_performance = {};
|
|
10127
|
+
__export(exports_deep_performance, {
|
|
10128
|
+
getDeepPerformance: () => getDeepPerformance
|
|
10129
|
+
});
|
|
10130
|
+
function categorizeThirdParty(domain) {
|
|
10131
|
+
for (const [pattern, category] of Object.entries(THIRD_PARTY_CATEGORIES)) {
|
|
10132
|
+
if (domain.includes(pattern))
|
|
10133
|
+
return category;
|
|
10134
|
+
}
|
|
10135
|
+
return "other";
|
|
10136
|
+
}
|
|
10137
|
+
async function getDeepPerformance(page) {
|
|
10138
|
+
return page.evaluate(() => {
|
|
10139
|
+
const perf = performance;
|
|
10140
|
+
const entries = perf.getEntriesByType("resource");
|
|
10141
|
+
const navEntry = perf.getEntriesByType("navigation")[0];
|
|
10142
|
+
const paintEntries = perf.getEntriesByType("paint");
|
|
10143
|
+
const fcp = paintEntries.find((e) => e.name === "first-contentful-paint")?.startTime;
|
|
10144
|
+
const ttfb = navEntry?.responseStart;
|
|
10145
|
+
const web_vitals = { fcp, ttfb };
|
|
10146
|
+
try {
|
|
10147
|
+
const lcpEntries = perf.getEntriesByType("largest-contentful-paint");
|
|
10148
|
+
if (lcpEntries.length > 0)
|
|
10149
|
+
web_vitals.lcp = lcpEntries[lcpEntries.length - 1].startTime;
|
|
10150
|
+
} catch {}
|
|
10151
|
+
const byType = {};
|
|
10152
|
+
let totalBytes = 0;
|
|
10153
|
+
const resourceList = [];
|
|
10154
|
+
const pageDomain = location.hostname;
|
|
10155
|
+
const thirdPartyMap = new Map;
|
|
10156
|
+
for (const entry of entries) {
|
|
10157
|
+
const size = entry.transferSize || entry.encodedBodySize || 0;
|
|
10158
|
+
totalBytes += size;
|
|
10159
|
+
let type2 = entry.initiatorType || "other";
|
|
10160
|
+
if (type2 === "xmlhttprequest" || type2 === "fetch")
|
|
10161
|
+
type2 = "xhr";
|
|
10162
|
+
if (type2 === "link" && entry.name.match(/\.css/))
|
|
10163
|
+
type2 = "css";
|
|
10164
|
+
if (type2 === "img" || entry.name.match(/\.(png|jpg|jpeg|gif|svg|webp|avif|ico)/i))
|
|
10165
|
+
type2 = "image";
|
|
10166
|
+
if (type2 === "script" || entry.name.match(/\.js/))
|
|
10167
|
+
type2 = "script";
|
|
10168
|
+
if (entry.name.match(/\.(woff2?|ttf|otf|eot)/i))
|
|
10169
|
+
type2 = "font";
|
|
10170
|
+
if (!byType[type2])
|
|
10171
|
+
byType[type2] = { count: 0, size_bytes: 0 };
|
|
10172
|
+
byType[type2].count++;
|
|
10173
|
+
byType[type2].size_bytes += size;
|
|
10174
|
+
resourceList.push({ url: entry.name, size_bytes: size, type: type2 });
|
|
10175
|
+
try {
|
|
10176
|
+
const domain = new URL(entry.name).hostname;
|
|
10177
|
+
if (domain !== pageDomain && !domain.endsWith(`.${pageDomain}`)) {
|
|
10178
|
+
if (!thirdPartyMap.has(domain))
|
|
10179
|
+
thirdPartyMap.set(domain, { scripts: 0, total_bytes: 0 });
|
|
10180
|
+
const tp = thirdPartyMap.get(domain);
|
|
10181
|
+
tp.scripts++;
|
|
10182
|
+
tp.total_bytes += size;
|
|
10183
|
+
}
|
|
10184
|
+
} catch {}
|
|
10185
|
+
}
|
|
10186
|
+
resourceList.sort((a, b) => b.size_bytes - a.size_bytes);
|
|
10187
|
+
const largest = resourceList.slice(0, 10).map((r) => ({
|
|
10188
|
+
url: r.url.length > 120 ? r.url.slice(0, 117) + "..." : r.url,
|
|
10189
|
+
size_bytes: r.size_bytes,
|
|
10190
|
+
type: r.type
|
|
10191
|
+
}));
|
|
10192
|
+
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);
|
|
10193
|
+
const allNodes = document.querySelectorAll("*");
|
|
10194
|
+
let maxDepth = 0;
|
|
10195
|
+
let textNodes = 0;
|
|
10196
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ALL);
|
|
10197
|
+
let node = walker.currentNode;
|
|
10198
|
+
while (node) {
|
|
10199
|
+
if (node.nodeType === Node.TEXT_NODE && node.textContent?.trim())
|
|
10200
|
+
textNodes++;
|
|
10201
|
+
let depth = 0;
|
|
10202
|
+
let parent = node.parentNode;
|
|
10203
|
+
while (parent) {
|
|
10204
|
+
depth++;
|
|
10205
|
+
parent = parent.parentNode;
|
|
10206
|
+
}
|
|
10207
|
+
if (depth > maxDepth)
|
|
10208
|
+
maxDepth = depth;
|
|
10209
|
+
node = walker.nextNode();
|
|
10210
|
+
}
|
|
10211
|
+
const mem = performance.memory;
|
|
10212
|
+
const memory = {
|
|
10213
|
+
js_heap_used_mb: mem ? Math.round(mem.usedJSHeapSize / 1024 / 1024 * 100) / 100 : 0,
|
|
10214
|
+
js_heap_total_mb: mem ? Math.round(mem.totalJSHeapSize / 1024 / 1024 * 100) / 100 : 0,
|
|
10215
|
+
js_heap_limit_mb: mem ? Math.round(mem.jsHeapSizeLimit / 1024 / 1024 * 100) / 100 : 0
|
|
10216
|
+
};
|
|
10217
|
+
return {
|
|
10218
|
+
web_vitals,
|
|
10219
|
+
resources: { total_transfer_bytes: totalBytes, total_resources: entries.length, by_type: byType, largest },
|
|
10220
|
+
third_party,
|
|
10221
|
+
dom: { node_count: document.all.length, max_depth: maxDepth, element_count: allNodes.length, text_node_count: textNodes },
|
|
10222
|
+
main_thread: { long_tasks: 0, total_blocking_ms: 0 },
|
|
10223
|
+
memory
|
|
10224
|
+
};
|
|
10225
|
+
}).then((result) => {
|
|
10226
|
+
for (const tp of result.third_party) {
|
|
10227
|
+
tp.category = categorizeThirdParty(tp.domain);
|
|
10228
|
+
}
|
|
10229
|
+
return result;
|
|
10230
|
+
});
|
|
10231
|
+
}
|
|
10232
|
+
var THIRD_PARTY_CATEGORIES;
|
|
10233
|
+
var init_deep_performance = __esm(() => {
|
|
10234
|
+
THIRD_PARTY_CATEGORIES = {
|
|
10235
|
+
"google-analytics.com": "analytics",
|
|
10236
|
+
"googletagmanager.com": "analytics",
|
|
10237
|
+
gtag: "analytics",
|
|
10238
|
+
"facebook.net": "social",
|
|
10239
|
+
"connect.facebook": "social",
|
|
10240
|
+
"stripe.com": "payment",
|
|
10241
|
+
"js.stripe.com": "payment",
|
|
10242
|
+
"sentry.io": "monitoring",
|
|
10243
|
+
"sentry-cdn": "monitoring",
|
|
10244
|
+
"posthog.com": "analytics",
|
|
10245
|
+
"ph.": "analytics",
|
|
10246
|
+
"intercom.io": "chat",
|
|
10247
|
+
"crisp.chat": "chat",
|
|
10248
|
+
"hotjar.com": "analytics",
|
|
10249
|
+
"clarity.ms": "analytics",
|
|
10250
|
+
"cdn.jsdelivr.net": "cdn",
|
|
10251
|
+
"cdnjs.cloudflare.com": "cdn",
|
|
10252
|
+
"unpkg.com": "cdn",
|
|
10253
|
+
"fonts.googleapis.com": "fonts",
|
|
10254
|
+
"fonts.gstatic.com": "fonts"
|
|
10255
|
+
};
|
|
10256
|
+
});
|
|
10257
|
+
|
|
10258
|
+
// src/lib/workflows.ts
|
|
10259
|
+
var exports_workflows = {};
|
|
10260
|
+
__export(exports_workflows, {
|
|
10261
|
+
saveWorkflowFromRecording: () => saveWorkflowFromRecording,
|
|
10262
|
+
saveWorkflow: () => saveWorkflow,
|
|
10263
|
+
runWorkflow: () => runWorkflow,
|
|
10264
|
+
listWorkflows: () => listWorkflows,
|
|
10265
|
+
getWorkflowByName: () => getWorkflowByName,
|
|
10266
|
+
getWorkflow: () => getWorkflow,
|
|
10267
|
+
deleteWorkflow: () => deleteWorkflow
|
|
10268
|
+
});
|
|
10269
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
10270
|
+
function saveWorkflow(data) {
|
|
10271
|
+
const db = getDatabase();
|
|
10272
|
+
const id = randomUUID10();
|
|
10273
|
+
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);
|
|
10274
|
+
return getWorkflow(id);
|
|
10275
|
+
}
|
|
10276
|
+
function saveWorkflowFromRecording(recordingId, name, description) {
|
|
10277
|
+
const db = getDatabase();
|
|
10278
|
+
const rec = db.query("SELECT steps, start_url FROM recordings WHERE id = ?").get(recordingId);
|
|
10279
|
+
if (!rec)
|
|
10280
|
+
throw new Error(`Recording not found: ${recordingId}`);
|
|
10281
|
+
const steps = JSON.parse(rec.steps);
|
|
10282
|
+
return saveWorkflow({ name, description, steps, startUrl: rec.start_url ?? undefined });
|
|
10283
|
+
}
|
|
10284
|
+
function getWorkflow(id) {
|
|
10285
|
+
const db = getDatabase();
|
|
10286
|
+
const row = db.query("SELECT * FROM workflows WHERE id = ?").get(id);
|
|
10287
|
+
if (!row)
|
|
10288
|
+
return null;
|
|
10289
|
+
return { ...row, steps: JSON.parse(row.steps) };
|
|
10290
|
+
}
|
|
10291
|
+
function getWorkflowByName(name) {
|
|
10292
|
+
const db = getDatabase();
|
|
10293
|
+
const row = db.query("SELECT * FROM workflows WHERE name = ?").get(name);
|
|
10294
|
+
if (!row)
|
|
10295
|
+
return null;
|
|
10296
|
+
return { ...row, steps: JSON.parse(row.steps) };
|
|
10297
|
+
}
|
|
10298
|
+
function listWorkflows() {
|
|
10299
|
+
const db = getDatabase();
|
|
10300
|
+
return db.query("SELECT * FROM workflows ORDER BY updated_at DESC").all().map((row) => ({ ...row, steps: JSON.parse(row.steps) }));
|
|
10301
|
+
}
|
|
10302
|
+
function deleteWorkflow(name) {
|
|
10303
|
+
const db = getDatabase();
|
|
10304
|
+
return db.prepare("DELETE FROM workflows WHERE name = ?").run(name).changes > 0;
|
|
10305
|
+
}
|
|
10306
|
+
function recordRun(id, healed) {
|
|
10307
|
+
const db = getDatabase();
|
|
10308
|
+
if (healed) {
|
|
10309
|
+
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);
|
|
10310
|
+
} else {
|
|
10311
|
+
db.prepare("UPDATE workflows SET last_run = datetime('now'), run_count = run_count + 1, updated_at = datetime('now') WHERE id = ?").run(id);
|
|
10312
|
+
}
|
|
10313
|
+
}
|
|
10314
|
+
async function runWorkflow(workflow, page) {
|
|
10315
|
+
const t0 = Date.now();
|
|
10316
|
+
let executed = 0;
|
|
10317
|
+
let failed = 0;
|
|
10318
|
+
let healed = 0;
|
|
10319
|
+
const healedDetails = [];
|
|
10320
|
+
const errors2 = [];
|
|
10321
|
+
const updatedSteps = [...workflow.steps];
|
|
10322
|
+
for (let i = 0;i < workflow.steps.length; i++) {
|
|
10323
|
+
const step = workflow.steps[i];
|
|
10324
|
+
try {
|
|
10325
|
+
switch (step.type) {
|
|
10326
|
+
case "navigate":
|
|
10327
|
+
if (step.url)
|
|
10328
|
+
await page.goto(step.url, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
10329
|
+
break;
|
|
10330
|
+
case "click":
|
|
10331
|
+
if (step.selector) {
|
|
10332
|
+
try {
|
|
10333
|
+
await page.click(step.selector, { timeout: 5000 });
|
|
10334
|
+
} catch {
|
|
10335
|
+
const result = await healSelector(page, step.selector);
|
|
10336
|
+
if (result.found && result.locator) {
|
|
10337
|
+
await result.locator.click();
|
|
10338
|
+
healed++;
|
|
10339
|
+
const healedSelector = `[healed:${result.method}]${step.selector}`;
|
|
10340
|
+
healedDetails.push({ step: i, original: step.selector, healed_to: healedSelector, method: result.method });
|
|
10341
|
+
} else {
|
|
10342
|
+
throw new Error(`Click failed: ${step.selector} (self-healing exhausted)`);
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
}
|
|
10346
|
+
break;
|
|
10347
|
+
case "type":
|
|
10348
|
+
if (step.selector && step.value) {
|
|
10349
|
+
try {
|
|
10350
|
+
await page.fill(step.selector, step.value);
|
|
10351
|
+
} catch {
|
|
10352
|
+
const result = await healSelector(page, step.selector);
|
|
10353
|
+
if (result.found && result.locator) {
|
|
10354
|
+
await result.locator.fill(step.value);
|
|
10355
|
+
healed++;
|
|
10356
|
+
healedDetails.push({ step: i, original: step.selector, healed_to: `[healed:${result.method}]`, method: result.method });
|
|
10357
|
+
} else {
|
|
10358
|
+
throw new Error(`Type failed: ${step.selector} (self-healing exhausted)`);
|
|
10359
|
+
}
|
|
10360
|
+
}
|
|
10361
|
+
}
|
|
10362
|
+
break;
|
|
10363
|
+
case "scroll":
|
|
10364
|
+
if (step.y)
|
|
10365
|
+
await page.mouse.wheel(0, step.y);
|
|
10366
|
+
break;
|
|
10367
|
+
case "hover":
|
|
10368
|
+
if (step.selector) {
|
|
10369
|
+
try {
|
|
10370
|
+
await page.hover(step.selector);
|
|
10371
|
+
} catch {}
|
|
10372
|
+
}
|
|
10373
|
+
break;
|
|
10374
|
+
case "select":
|
|
10375
|
+
if (step.selector && step.value) {
|
|
10376
|
+
try {
|
|
10377
|
+
await page.selectOption(step.selector, step.value);
|
|
10378
|
+
} catch {}
|
|
10379
|
+
}
|
|
10380
|
+
break;
|
|
10381
|
+
case "wait":
|
|
10382
|
+
if (step.selector) {
|
|
10383
|
+
try {
|
|
10384
|
+
await page.waitForSelector(step.selector, { timeout: 1e4 });
|
|
10385
|
+
} catch {}
|
|
10386
|
+
} else {
|
|
10387
|
+
await new Promise((r) => setTimeout(r, step.timestamp || 1000));
|
|
10388
|
+
}
|
|
10389
|
+
break;
|
|
10390
|
+
case "evaluate":
|
|
10391
|
+
if (step.value)
|
|
10392
|
+
await page.evaluate(step.value);
|
|
10393
|
+
break;
|
|
10394
|
+
default:
|
|
10395
|
+
break;
|
|
10396
|
+
}
|
|
10397
|
+
executed++;
|
|
10398
|
+
} catch (err) {
|
|
10399
|
+
failed++;
|
|
10400
|
+
errors2.push(`Step ${i} (${step.type}): ${err instanceof Error ? err.message : String(err)}`);
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
recordRun(workflow.id, healed > 0);
|
|
10404
|
+
return {
|
|
10405
|
+
success: failed === 0,
|
|
10406
|
+
steps_executed: executed,
|
|
10407
|
+
steps_failed: failed,
|
|
10408
|
+
steps_healed: healed,
|
|
10409
|
+
healed_details: healedDetails,
|
|
10410
|
+
errors: errors2,
|
|
10411
|
+
duration_ms: Date.now() - t0
|
|
10412
|
+
};
|
|
10413
|
+
}
|
|
10414
|
+
var init_workflows = __esm(() => {
|
|
10415
|
+
init_schema();
|
|
10416
|
+
});
|
|
10417
|
+
|
|
9957
10418
|
// src/lib/auth-flow.ts
|
|
9958
10419
|
var exports_auth_flow = {};
|
|
9959
10420
|
__export(exports_auth_flow, {
|
|
@@ -9968,10 +10429,10 @@ __export(exports_auth_flow, {
|
|
|
9968
10429
|
getAuthFlow: () => getAuthFlow,
|
|
9969
10430
|
deleteAuthFlow: () => deleteAuthFlow
|
|
9970
10431
|
});
|
|
9971
|
-
import { randomUUID as
|
|
10432
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
9972
10433
|
function saveAuthFlow(data) {
|
|
9973
10434
|
const db = getDatabase();
|
|
9974
|
-
const id =
|
|
10435
|
+
const id = randomUUID11();
|
|
9975
10436
|
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);
|
|
9976
10437
|
return getAuthFlow(id);
|
|
9977
10438
|
}
|
|
@@ -10141,6 +10602,226 @@ async function clickByVision(page, description, opts) {
|
|
|
10141
10602
|
}
|
|
10142
10603
|
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
10143
10604
|
|
|
10605
|
+
// src/lib/api-detector.ts
|
|
10606
|
+
var exports_api_detector = {};
|
|
10607
|
+
__export(exports_api_detector, {
|
|
10608
|
+
listDiscoveredAPIs: () => listDiscoveredAPIs,
|
|
10609
|
+
detectAPIs: () => detectAPIs
|
|
10610
|
+
});
|
|
10611
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
10612
|
+
function detectAPIs(sessionId) {
|
|
10613
|
+
const db = getDatabase();
|
|
10614
|
+
const requests = db.query(`SELECT method, url, status_code, response_headers, body_size
|
|
10615
|
+
FROM network_log
|
|
10616
|
+
WHERE session_id = ?
|
|
10617
|
+
AND (response_headers LIKE '%application/json%' OR response_headers LIKE '%text/json%')
|
|
10618
|
+
AND status_code >= 200 AND status_code < 400
|
|
10619
|
+
ORDER BY timestamp DESC`).all(sessionId);
|
|
10620
|
+
const seen = new Map;
|
|
10621
|
+
for (const req of requests) {
|
|
10622
|
+
try {
|
|
10623
|
+
const urlObj = new URL(req.url);
|
|
10624
|
+
if (urlObj.pathname.match(/\.(js|css|png|jpg|svg|woff|ico)$/))
|
|
10625
|
+
continue;
|
|
10626
|
+
if (urlObj.hostname.includes("googleapis.com/identitytoolkit"))
|
|
10627
|
+
continue;
|
|
10628
|
+
if (urlObj.hostname.includes("posthog"))
|
|
10629
|
+
continue;
|
|
10630
|
+
if (urlObj.hostname.includes("sentry"))
|
|
10631
|
+
continue;
|
|
10632
|
+
const key = `${req.method} ${urlObj.origin}${urlObj.pathname}`;
|
|
10633
|
+
if (!seen.has(key)) {
|
|
10634
|
+
seen.set(key, {
|
|
10635
|
+
url: `${urlObj.origin}${urlObj.pathname}`,
|
|
10636
|
+
method: req.method,
|
|
10637
|
+
status_code: req.status_code,
|
|
10638
|
+
content_type: "application/json",
|
|
10639
|
+
response_schema: {},
|
|
10640
|
+
sample_size: req.body_size ?? 0
|
|
10641
|
+
});
|
|
10642
|
+
}
|
|
10643
|
+
} catch {}
|
|
10644
|
+
}
|
|
10645
|
+
const apis = Array.from(seen.values());
|
|
10646
|
+
for (const api of apis) {
|
|
10647
|
+
const id = randomUUID12();
|
|
10648
|
+
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);
|
|
10649
|
+
}
|
|
10650
|
+
return apis;
|
|
10651
|
+
}
|
|
10652
|
+
function listDiscoveredAPIs(sessionId) {
|
|
10653
|
+
const db = getDatabase();
|
|
10654
|
+
if (sessionId) {
|
|
10655
|
+
return db.query("SELECT * FROM api_endpoints WHERE session_id = ? ORDER BY discovered_at DESC").all(sessionId);
|
|
10656
|
+
}
|
|
10657
|
+
return db.query("SELECT * FROM api_endpoints ORDER BY discovered_at DESC LIMIT 100").all();
|
|
10658
|
+
}
|
|
10659
|
+
var init_api_detector = __esm(() => {
|
|
10660
|
+
init_schema();
|
|
10661
|
+
});
|
|
10662
|
+
|
|
10663
|
+
// src/lib/structured-extract.ts
|
|
10664
|
+
var exports_structured_extract = {};
|
|
10665
|
+
__export(exports_structured_extract, {
|
|
10666
|
+
extractStructuredData: () => extractStructuredData
|
|
10667
|
+
});
|
|
10668
|
+
async function extractStructuredData(page) {
|
|
10669
|
+
return page.evaluate(() => {
|
|
10670
|
+
const result = { tables: [], lists: [], jsonLd: [], openGraph: {}, metaTags: {}, repeatedElements: [] };
|
|
10671
|
+
document.querySelectorAll("table").forEach((table, idx) => {
|
|
10672
|
+
const headers = [];
|
|
10673
|
+
table.querySelectorAll("thead th, thead td, tr:first-child th").forEach((th) => {
|
|
10674
|
+
headers.push(th.textContent?.trim() ?? "");
|
|
10675
|
+
});
|
|
10676
|
+
const rows = [];
|
|
10677
|
+
table.querySelectorAll("tbody tr, tr:not(:first-child)").forEach((tr) => {
|
|
10678
|
+
const row = [];
|
|
10679
|
+
tr.querySelectorAll("td, th").forEach((td) => {
|
|
10680
|
+
row.push(td.textContent?.trim() ?? "");
|
|
10681
|
+
});
|
|
10682
|
+
if (row.length > 0 && row.some((c) => c !== ""))
|
|
10683
|
+
rows.push(row);
|
|
10684
|
+
});
|
|
10685
|
+
if (rows.length > 0) {
|
|
10686
|
+
result.tables.push({ headers, rows, selector: `table:nth-of-type(${idx + 1})` });
|
|
10687
|
+
}
|
|
10688
|
+
});
|
|
10689
|
+
document.querySelectorAll("ul, ol").forEach((list, idx) => {
|
|
10690
|
+
const items = [];
|
|
10691
|
+
list.querySelectorAll(":scope > li").forEach((li) => {
|
|
10692
|
+
const text = li.textContent?.trim() ?? "";
|
|
10693
|
+
if (text)
|
|
10694
|
+
items.push(text);
|
|
10695
|
+
});
|
|
10696
|
+
if (items.length >= 3) {
|
|
10697
|
+
const tag = list.tagName.toLowerCase();
|
|
10698
|
+
result.lists.push({ items, selector: `${tag}:nth-of-type(${idx + 1})` });
|
|
10699
|
+
}
|
|
10700
|
+
});
|
|
10701
|
+
document.querySelectorAll('script[type="application/ld+json"]').forEach((script) => {
|
|
10702
|
+
try {
|
|
10703
|
+
result.jsonLd.push(JSON.parse(script.textContent ?? ""));
|
|
10704
|
+
} catch {}
|
|
10705
|
+
});
|
|
10706
|
+
document.querySelectorAll('meta[property^="og:"]').forEach((meta) => {
|
|
10707
|
+
const prop = meta.getAttribute("property")?.replace("og:", "") ?? "";
|
|
10708
|
+
result.openGraph[prop] = meta.getAttribute("content") ?? "";
|
|
10709
|
+
});
|
|
10710
|
+
document.querySelectorAll("meta[name]").forEach((meta) => {
|
|
10711
|
+
const name = meta.getAttribute("name") ?? "";
|
|
10712
|
+
if (name)
|
|
10713
|
+
result.metaTags[name] = meta.getAttribute("content") ?? "";
|
|
10714
|
+
});
|
|
10715
|
+
const classCounts = new Map;
|
|
10716
|
+
document.querySelectorAll("[class]").forEach((el) => {
|
|
10717
|
+
const cls = el.className.toString().trim();
|
|
10718
|
+
if (cls && cls.length > 5 && cls.length < 100) {
|
|
10719
|
+
if (!classCounts.has(cls))
|
|
10720
|
+
classCounts.set(cls, []);
|
|
10721
|
+
classCounts.get(cls).push(el);
|
|
10722
|
+
}
|
|
10723
|
+
});
|
|
10724
|
+
for (const [cls, elements] of classCounts) {
|
|
10725
|
+
if (elements.length >= 3 && elements.length <= 200) {
|
|
10726
|
+
const sample = elements.slice(0, 3).map((el) => el.textContent?.trim().slice(0, 100) ?? "");
|
|
10727
|
+
if (sample.some((s) => s.length > 10)) {
|
|
10728
|
+
result.repeatedElements.push({
|
|
10729
|
+
selector: `.${cls.split(" ")[0]}`,
|
|
10730
|
+
count: elements.length,
|
|
10731
|
+
sample
|
|
10732
|
+
});
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
result.repeatedElements.sort((a, b) => b.count - a.count);
|
|
10737
|
+
result.repeatedElements = result.repeatedElements.slice(0, 10);
|
|
10738
|
+
return result;
|
|
10739
|
+
});
|
|
10740
|
+
}
|
|
10741
|
+
|
|
10742
|
+
// src/lib/datasets.ts
|
|
10743
|
+
var exports_datasets = {};
|
|
10744
|
+
__export(exports_datasets, {
|
|
10745
|
+
saveDataset: () => saveDataset,
|
|
10746
|
+
listDatasets: () => listDatasets,
|
|
10747
|
+
getDatasetByName: () => getDatasetByName,
|
|
10748
|
+
getDataset: () => getDataset,
|
|
10749
|
+
exportDataset: () => exportDataset,
|
|
10750
|
+
deleteDataset: () => deleteDataset
|
|
10751
|
+
});
|
|
10752
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
10753
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync9 } from "fs";
|
|
10754
|
+
import { join as join9 } from "path";
|
|
10755
|
+
import { homedir as homedir9 } from "os";
|
|
10756
|
+
function saveDataset(data) {
|
|
10757
|
+
const db = getDatabase();
|
|
10758
|
+
const id = randomUUID13();
|
|
10759
|
+
const existing = db.query("SELECT id FROM datasets WHERE name = ?").get(data.name);
|
|
10760
|
+
if (existing) {
|
|
10761
|
+
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);
|
|
10762
|
+
return getDataset(existing.id);
|
|
10763
|
+
}
|
|
10764
|
+
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);
|
|
10765
|
+
return getDataset(id);
|
|
10766
|
+
}
|
|
10767
|
+
function getDataset(id) {
|
|
10768
|
+
const db = getDatabase();
|
|
10769
|
+
const row = db.query("SELECT * FROM datasets WHERE id = ?").get(id);
|
|
10770
|
+
if (!row)
|
|
10771
|
+
return null;
|
|
10772
|
+
return { ...row, data: JSON.parse(row.data), schema: row.schema ? JSON.parse(row.schema) : null };
|
|
10773
|
+
}
|
|
10774
|
+
function getDatasetByName(name) {
|
|
10775
|
+
const db = getDatabase();
|
|
10776
|
+
const row = db.query("SELECT * FROM datasets WHERE name = ?").get(name);
|
|
10777
|
+
if (!row)
|
|
10778
|
+
return null;
|
|
10779
|
+
return { ...row, data: JSON.parse(row.data), schema: row.schema ? JSON.parse(row.schema) : null };
|
|
10780
|
+
}
|
|
10781
|
+
function listDatasets() {
|
|
10782
|
+
const db = getDatabase();
|
|
10783
|
+
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 }));
|
|
10784
|
+
}
|
|
10785
|
+
function deleteDataset(name) {
|
|
10786
|
+
const db = getDatabase();
|
|
10787
|
+
return db.prepare("DELETE FROM datasets WHERE name = ?").run(name).changes > 0;
|
|
10788
|
+
}
|
|
10789
|
+
function exportDataset(name, format) {
|
|
10790
|
+
const dataset = getDatasetByName(name);
|
|
10791
|
+
if (!dataset)
|
|
10792
|
+
throw new Error(`Dataset '${name}' not found`);
|
|
10793
|
+
const dir = join9(process.env["BROWSER_DATA_DIR"] ?? join9(homedir9(), ".browser"), "exports");
|
|
10794
|
+
mkdirSync9(dir, { recursive: true });
|
|
10795
|
+
const filename = `${name}.${format}`;
|
|
10796
|
+
const path = join9(dir, filename);
|
|
10797
|
+
if (format === "csv") {
|
|
10798
|
+
const rows = dataset.data;
|
|
10799
|
+
if (rows.length === 0) {
|
|
10800
|
+
writeFileSync3(path, "");
|
|
10801
|
+
return { path, size: 0 };
|
|
10802
|
+
}
|
|
10803
|
+
const headers = Object.keys(rows[0]);
|
|
10804
|
+
const csvLines = [headers.join(",")];
|
|
10805
|
+
for (const row of rows) {
|
|
10806
|
+
csvLines.push(headers.map((h) => {
|
|
10807
|
+
const val = String(row[h] ?? "");
|
|
10808
|
+
return val.includes(",") || val.includes('"') ? `"${val.replace(/"/g, '""')}"` : val;
|
|
10809
|
+
}).join(","));
|
|
10810
|
+
}
|
|
10811
|
+
const content = csvLines.join(`
|
|
10812
|
+
`);
|
|
10813
|
+
writeFileSync3(path, content);
|
|
10814
|
+
return { path, size: content.length };
|
|
10815
|
+
} else {
|
|
10816
|
+
const content = JSON.stringify(dataset.data, null, 2);
|
|
10817
|
+
writeFileSync3(path, content);
|
|
10818
|
+
return { path, size: content.length };
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
var init_datasets = __esm(() => {
|
|
10822
|
+
init_schema();
|
|
10823
|
+
});
|
|
10824
|
+
|
|
10144
10825
|
// src/lib/auth.ts
|
|
10145
10826
|
var exports_auth = {};
|
|
10146
10827
|
__export(exports_auth, {
|
|
@@ -10148,18 +10829,18 @@ __export(exports_auth, {
|
|
|
10148
10829
|
getCredentials: () => getCredentials
|
|
10149
10830
|
});
|
|
10150
10831
|
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
10151
|
-
import { join as
|
|
10152
|
-
import { homedir as
|
|
10832
|
+
import { join as join10 } from "path";
|
|
10833
|
+
import { homedir as homedir10 } from "os";
|
|
10153
10834
|
async function getCredentials(service) {
|
|
10154
10835
|
try {
|
|
10155
|
-
const { getSecret } = await import(`${
|
|
10836
|
+
const { getSecret } = await import(`${homedir10()}/Workspace/hasna/opensource/opensourcedev/open-secrets/src/store.js`);
|
|
10156
10837
|
const email = getSecret(`${service}_email`) ?? getSecret(`${service}_username`) ?? getSecret(`${service}_login`);
|
|
10157
10838
|
const password = getSecret(`${service}_password`) ?? getSecret(`${service}_pass`);
|
|
10158
10839
|
if (email?.value && password?.value) {
|
|
10159
10840
|
return { email: email.value, password: password.value };
|
|
10160
10841
|
}
|
|
10161
10842
|
} catch {}
|
|
10162
|
-
const secretsPath =
|
|
10843
|
+
const secretsPath = join10(homedir10(), ".secrets");
|
|
10163
10844
|
if (existsSync5(secretsPath)) {
|
|
10164
10845
|
const content = readFileSync3(secretsPath, "utf8");
|
|
10165
10846
|
const lines = content.split(`
|
|
@@ -10341,10 +11022,10 @@ __export(exports_dist, {
|
|
|
10341
11022
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
10342
11023
|
});
|
|
10343
11024
|
import { Database as Database2 } from "bun:sqlite";
|
|
10344
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
10345
|
-
import { dirname, join as
|
|
10346
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync4, readdirSync as readdirSync4, writeFileSync as
|
|
10347
|
-
import { homedir as
|
|
11025
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync10 } from "fs";
|
|
11026
|
+
import { dirname, join as join11, resolve } from "path";
|
|
11027
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync4, readdirSync as readdirSync4, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
11028
|
+
import { homedir as homedir11 } from "os";
|
|
10348
11029
|
import { basename as basename2, dirname as dirname2, join as join22, resolve as resolve2 } from "path";
|
|
10349
11030
|
import { existsSync as existsSync32, mkdirSync as mkdirSync32, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
10350
11031
|
import { homedir as homedir22 } from "os";
|
|
@@ -10358,7 +11039,7 @@ function isInMemoryDb(path) {
|
|
|
10358
11039
|
function findNearestMementosDb(startDir) {
|
|
10359
11040
|
let dir = resolve(startDir);
|
|
10360
11041
|
while (true) {
|
|
10361
|
-
const candidate =
|
|
11042
|
+
const candidate = join11(dir, ".mementos", "mementos.db");
|
|
10362
11043
|
if (existsSync6(candidate))
|
|
10363
11044
|
return candidate;
|
|
10364
11045
|
const parent = dirname(dir);
|
|
@@ -10371,7 +11052,7 @@ function findNearestMementosDb(startDir) {
|
|
|
10371
11052
|
function findGitRoot(startDir) {
|
|
10372
11053
|
let dir = resolve(startDir);
|
|
10373
11054
|
while (true) {
|
|
10374
|
-
if (existsSync6(
|
|
11055
|
+
if (existsSync6(join11(dir, ".git")))
|
|
10375
11056
|
return dir;
|
|
10376
11057
|
const parent = dirname(dir);
|
|
10377
11058
|
if (parent === dir)
|
|
@@ -10391,18 +11072,18 @@ function getDbPath() {
|
|
|
10391
11072
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
10392
11073
|
const gitRoot = findGitRoot(cwd);
|
|
10393
11074
|
if (gitRoot) {
|
|
10394
|
-
return
|
|
11075
|
+
return join11(gitRoot, ".mementos", "mementos.db");
|
|
10395
11076
|
}
|
|
10396
11077
|
}
|
|
10397
11078
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
10398
|
-
return
|
|
11079
|
+
return join11(home, ".mementos", "mementos.db");
|
|
10399
11080
|
}
|
|
10400
11081
|
function ensureDir2(filePath) {
|
|
10401
11082
|
if (isInMemoryDb(filePath))
|
|
10402
11083
|
return;
|
|
10403
11084
|
const dir = dirname(resolve(filePath));
|
|
10404
11085
|
if (!existsSync6(dir)) {
|
|
10405
|
-
|
|
11086
|
+
mkdirSync10(dir, { recursive: true });
|
|
10406
11087
|
}
|
|
10407
11088
|
}
|
|
10408
11089
|
function getDatabase2(dbPath) {
|
|
@@ -12252,7 +12933,7 @@ function isValidCategory(value) {
|
|
|
12252
12933
|
return VALID_CATEGORIES.includes(value);
|
|
12253
12934
|
}
|
|
12254
12935
|
function loadConfig() {
|
|
12255
|
-
const configPath = join22(
|
|
12936
|
+
const configPath = join22(homedir11(), ".mementos", "config.json");
|
|
12256
12937
|
let fileConfig = {};
|
|
12257
12938
|
if (existsSync22(configPath)) {
|
|
12258
12939
|
try {
|
|
@@ -12279,10 +12960,10 @@ function loadConfig() {
|
|
|
12279
12960
|
return merged;
|
|
12280
12961
|
}
|
|
12281
12962
|
function profilesDir() {
|
|
12282
|
-
return join22(
|
|
12963
|
+
return join22(homedir11(), ".mementos", "profiles");
|
|
12283
12964
|
}
|
|
12284
12965
|
function globalConfigPath() {
|
|
12285
|
-
return join22(
|
|
12966
|
+
return join22(homedir11(), ".mementos", "config.json");
|
|
12286
12967
|
}
|
|
12287
12968
|
function readGlobalConfig() {
|
|
12288
12969
|
const p = globalConfigPath();
|
|
@@ -12297,7 +12978,7 @@ function readGlobalConfig() {
|
|
|
12297
12978
|
function writeGlobalConfig(data) {
|
|
12298
12979
|
const p = globalConfigPath();
|
|
12299
12980
|
ensureDir22(dirname2(p));
|
|
12300
|
-
|
|
12981
|
+
writeFileSync4(p, JSON.stringify(data, null, 2), "utf-8");
|
|
12301
12982
|
}
|
|
12302
12983
|
function getActiveProfile() {
|
|
12303
12984
|
const envProfile = process.env["MEMENTOS_PROFILE"];
|
|
@@ -14962,10 +15643,10 @@ __export(exports_dist2, {
|
|
|
14962
15643
|
acquireLock: () => acquireLock2
|
|
14963
15644
|
});
|
|
14964
15645
|
import { Database as Database3 } from "bun:sqlite";
|
|
14965
|
-
import { mkdirSync as
|
|
14966
|
-
import { join as
|
|
14967
|
-
import { homedir as
|
|
14968
|
-
import { randomUUID as
|
|
15646
|
+
import { mkdirSync as mkdirSync11 } from "fs";
|
|
15647
|
+
import { join as join12, dirname as dirname3 } from "path";
|
|
15648
|
+
import { homedir as homedir12 } from "os";
|
|
15649
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
14969
15650
|
import { mkdirSync as mkdirSync23, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
14970
15651
|
import { join as join33 } from "path";
|
|
14971
15652
|
import { homedir as homedir33 } from "os";
|
|
@@ -14973,19 +15654,19 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
14973
15654
|
import { join as join23 } from "path";
|
|
14974
15655
|
import { homedir as homedir23 } from "os";
|
|
14975
15656
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
14976
|
-
import { readFileSync as readFileSync23, writeFileSync as
|
|
15657
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync33 } from "fs";
|
|
14977
15658
|
import { join as join43, dirname as dirname22 } from "path";
|
|
14978
15659
|
import { homedir as homedir42 } from "os";
|
|
14979
15660
|
function getDbPath2() {
|
|
14980
15661
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
14981
15662
|
return process.env.CONVERSATIONS_DB_PATH;
|
|
14982
|
-
return
|
|
15663
|
+
return join12(homedir12(), ".conversations", "messages.db");
|
|
14983
15664
|
}
|
|
14984
15665
|
function getDb() {
|
|
14985
15666
|
if (db)
|
|
14986
15667
|
return db;
|
|
14987
15668
|
const dbPath = getDbPath2();
|
|
14988
|
-
|
|
15669
|
+
mkdirSync11(dirname3(dbPath), { recursive: true });
|
|
14989
15670
|
db = new Database3(dbPath, { create: true });
|
|
14990
15671
|
db.exec("PRAGMA journal_mode = WAL");
|
|
14991
15672
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -15345,7 +16026,7 @@ function guessMimeType(name) {
|
|
|
15345
16026
|
function sendMessage(opts) {
|
|
15346
16027
|
const db2 = getDb();
|
|
15347
16028
|
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
15348
|
-
const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${
|
|
16029
|
+
const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID14().slice(0, 8)}`);
|
|
15349
16030
|
const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
|
|
15350
16031
|
const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
|
|
15351
16032
|
const blocking = opts.blocking ? 1 : 0;
|
|
@@ -16173,7 +16854,7 @@ function getAutoName() {
|
|
|
16173
16854
|
cachedAutoName = name;
|
|
16174
16855
|
try {
|
|
16175
16856
|
mkdirSync33(dirname22(AGENT_ID_FILE), { recursive: true });
|
|
16176
|
-
|
|
16857
|
+
writeFileSync5(AGENT_ID_FILE, name + `
|
|
16177
16858
|
`, "utf-8");
|
|
16178
16859
|
} catch {}
|
|
16179
16860
|
return name;
|
|
@@ -18128,7 +18809,7 @@ Your code should look like:
|
|
|
18128
18809
|
}
|
|
18129
18810
|
}
|
|
18130
18811
|
}
|
|
18131
|
-
function checkPropTypes(typeSpecs, values,
|
|
18812
|
+
function checkPropTypes(typeSpecs, values, location2, componentName, element) {
|
|
18132
18813
|
{
|
|
18133
18814
|
var has = Function.call.bind(hasOwnProperty);
|
|
18134
18815
|
for (var typeSpecName in typeSpecs) {
|
|
@@ -18136,23 +18817,23 @@ Your code should look like:
|
|
|
18136
18817
|
var error$1 = undefined;
|
|
18137
18818
|
try {
|
|
18138
18819
|
if (typeof typeSpecs[typeSpecName] !== "function") {
|
|
18139
|
-
var err = Error((componentName || "React class") + ": " +
|
|
18820
|
+
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`.");
|
|
18140
18821
|
err.name = "Invariant Violation";
|
|
18141
18822
|
throw err;
|
|
18142
18823
|
}
|
|
18143
|
-
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName,
|
|
18824
|
+
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location2, null, "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED");
|
|
18144
18825
|
} catch (ex) {
|
|
18145
18826
|
error$1 = ex;
|
|
18146
18827
|
}
|
|
18147
18828
|
if (error$1 && !(error$1 instanceof Error)) {
|
|
18148
18829
|
setCurrentlyValidatingElement(element);
|
|
18149
|
-
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",
|
|
18830
|
+
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);
|
|
18150
18831
|
setCurrentlyValidatingElement(null);
|
|
18151
18832
|
}
|
|
18152
18833
|
if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {
|
|
18153
18834
|
loggedTypeFailures[error$1.message] = true;
|
|
18154
18835
|
setCurrentlyValidatingElement(element);
|
|
18155
|
-
error("Failed %s type: %s",
|
|
18836
|
+
error("Failed %s type: %s", location2, error$1.message);
|
|
18156
18837
|
setCurrentlyValidatingElement(null);
|
|
18157
18838
|
}
|
|
18158
18839
|
}
|
|
@@ -19387,11 +20068,11 @@ __export(exports_dist3, {
|
|
|
19387
20068
|
AgentNotFoundError: () => AgentNotFoundError2
|
|
19388
20069
|
});
|
|
19389
20070
|
import { Database as Database4 } from "bun:sqlite";
|
|
19390
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
19391
|
-
import { dirname as dirname5, join as
|
|
20071
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync12 } from "fs";
|
|
20072
|
+
import { dirname as dirname5, join as join13, resolve as resolve3 } from "path";
|
|
19392
20073
|
import { existsSync as existsSync33 } from "fs";
|
|
19393
20074
|
import { join as join34 } from "path";
|
|
19394
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as
|
|
20075
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync24, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
19395
20076
|
import { join as join24 } from "path";
|
|
19396
20077
|
import { existsSync as existsSync43, readFileSync as readFileSync24, readdirSync as readdirSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
19397
20078
|
import { join as join44 } from "path";
|
|
@@ -19621,7 +20302,7 @@ function isInMemoryDb2(path) {
|
|
|
19621
20302
|
function findNearestTodosDb(startDir) {
|
|
19622
20303
|
let dir = resolve3(startDir);
|
|
19623
20304
|
while (true) {
|
|
19624
|
-
const candidate =
|
|
20305
|
+
const candidate = join13(dir, ".todos", "todos.db");
|
|
19625
20306
|
if (existsSync7(candidate))
|
|
19626
20307
|
return candidate;
|
|
19627
20308
|
const parent = dirname5(dir);
|
|
@@ -19634,7 +20315,7 @@ function findNearestTodosDb(startDir) {
|
|
|
19634
20315
|
function findGitRoot2(startDir) {
|
|
19635
20316
|
let dir = resolve3(startDir);
|
|
19636
20317
|
while (true) {
|
|
19637
|
-
if (existsSync7(
|
|
20318
|
+
if (existsSync7(join13(dir, ".git")))
|
|
19638
20319
|
return dir;
|
|
19639
20320
|
const parent = dirname5(dir);
|
|
19640
20321
|
if (parent === dir)
|
|
@@ -19654,18 +20335,18 @@ function getDbPath3() {
|
|
|
19654
20335
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
19655
20336
|
const gitRoot = findGitRoot2(cwd);
|
|
19656
20337
|
if (gitRoot) {
|
|
19657
|
-
return
|
|
20338
|
+
return join13(gitRoot, ".todos", "todos.db");
|
|
19658
20339
|
}
|
|
19659
20340
|
}
|
|
19660
20341
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
19661
|
-
return
|
|
20342
|
+
return join13(home, ".todos", "todos.db");
|
|
19662
20343
|
}
|
|
19663
20344
|
function ensureDir3(filePath) {
|
|
19664
20345
|
if (isInMemoryDb2(filePath))
|
|
19665
20346
|
return;
|
|
19666
20347
|
const dir = dirname5(resolve3(filePath));
|
|
19667
20348
|
if (!existsSync7(dir)) {
|
|
19668
|
-
|
|
20349
|
+
mkdirSync12(dir, { recursive: true });
|
|
19669
20350
|
}
|
|
19670
20351
|
}
|
|
19671
20352
|
function getDatabase3(dbPath) {
|
|
@@ -20141,7 +20822,7 @@ function readJsonFile(path) {
|
|
|
20141
20822
|
}
|
|
20142
20823
|
}
|
|
20143
20824
|
function writeJsonFile(path, data) {
|
|
20144
|
-
|
|
20825
|
+
writeFileSync6(path, JSON.stringify(data, null, 2) + `
|
|
20145
20826
|
`);
|
|
20146
20827
|
}
|
|
20147
20828
|
function readHighWaterMark(dir) {
|
|
@@ -20152,7 +20833,7 @@ function readHighWaterMark(dir) {
|
|
|
20152
20833
|
return isNaN(val) ? 1 : val;
|
|
20153
20834
|
}
|
|
20154
20835
|
function writeHighWaterMark(dir, value) {
|
|
20155
|
-
|
|
20836
|
+
writeFileSync6(join24(dir, ".highwatermark"), String(value));
|
|
20156
20837
|
}
|
|
20157
20838
|
function getFileMtimeMs(path) {
|
|
20158
20839
|
try {
|
|
@@ -24742,9 +25423,9 @@ __export(exports_dist4, {
|
|
|
24742
25423
|
CATEGORIES: () => CATEGORIES,
|
|
24743
25424
|
AGENT_TARGETS: () => AGENT_TARGETS
|
|
24744
25425
|
});
|
|
24745
|
-
import { existsSync as existsSync8, cpSync, mkdirSync as
|
|
24746
|
-
import { join as
|
|
24747
|
-
import { homedir as
|
|
25426
|
+
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";
|
|
25427
|
+
import { join as join14, dirname as dirname6 } from "path";
|
|
25428
|
+
import { homedir as homedir13 } from "os";
|
|
24748
25429
|
import { fileURLToPath } from "url";
|
|
24749
25430
|
import { existsSync as existsSync24, readFileSync as readFileSync25, readdirSync as readdirSync23 } from "fs";
|
|
24750
25431
|
import { join as join25 } from "path";
|
|
@@ -24855,17 +25536,17 @@ function normalizeSkillName(name) {
|
|
|
24855
25536
|
function findSkillsDir() {
|
|
24856
25537
|
let dir = __dirname2;
|
|
24857
25538
|
for (let i = 0;i < 5; i++) {
|
|
24858
|
-
const candidate =
|
|
25539
|
+
const candidate = join14(dir, "skills");
|
|
24859
25540
|
if (existsSync8(candidate)) {
|
|
24860
25541
|
return candidate;
|
|
24861
25542
|
}
|
|
24862
25543
|
dir = dirname6(dir);
|
|
24863
25544
|
}
|
|
24864
|
-
return
|
|
25545
|
+
return join14(__dirname2, "..", "skills");
|
|
24865
25546
|
}
|
|
24866
25547
|
function getSkillPath(name) {
|
|
24867
25548
|
const skillName = normalizeSkillName(name);
|
|
24868
|
-
return
|
|
25549
|
+
return join14(SKILLS_DIR, skillName);
|
|
24869
25550
|
}
|
|
24870
25551
|
function skillExists(name) {
|
|
24871
25552
|
return existsSync8(getSkillPath(name));
|
|
@@ -24874,8 +25555,8 @@ function installSkill(name, options = {}) {
|
|
|
24874
25555
|
const { targetDir = process.cwd(), overwrite = false } = options;
|
|
24875
25556
|
const skillName = normalizeSkillName(name);
|
|
24876
25557
|
const sourcePath = getSkillPath(name);
|
|
24877
|
-
const destDir =
|
|
24878
|
-
const destPath =
|
|
25558
|
+
const destDir = join14(targetDir, ".skills");
|
|
25559
|
+
const destPath = join14(destDir, skillName);
|
|
24879
25560
|
if (!existsSync8(sourcePath)) {
|
|
24880
25561
|
return {
|
|
24881
25562
|
skill: name,
|
|
@@ -24893,7 +25574,7 @@ function installSkill(name, options = {}) {
|
|
|
24893
25574
|
}
|
|
24894
25575
|
try {
|
|
24895
25576
|
if (!existsSync8(destDir)) {
|
|
24896
|
-
|
|
25577
|
+
mkdirSync13(destDir, { recursive: true });
|
|
24897
25578
|
}
|
|
24898
25579
|
if (existsSync8(destPath) && overwrite) {
|
|
24899
25580
|
rmSync2(destPath, { recursive: true, force: true });
|
|
@@ -24934,7 +25615,7 @@ function installSkills(names, options = {}) {
|
|
|
24934
25615
|
return names.map((name) => installSkill(name, options));
|
|
24935
25616
|
}
|
|
24936
25617
|
function updateSkillsIndex(skillsDir) {
|
|
24937
|
-
const indexPath =
|
|
25618
|
+
const indexPath = join14(skillsDir, "index.ts");
|
|
24938
25619
|
const meta = loadMeta(skillsDir);
|
|
24939
25620
|
const disabledSet = new Set(meta.disabled || []);
|
|
24940
25621
|
const skills = readdirSync6(skillsDir).filter((f) => f.startsWith("skill-") && !f.includes(".") && !disabledSet.has(f.replace("skill-", "")));
|
|
@@ -24950,10 +25631,10 @@ function updateSkillsIndex(skillsDir) {
|
|
|
24950
25631
|
|
|
24951
25632
|
${exports}
|
|
24952
25633
|
`;
|
|
24953
|
-
|
|
25634
|
+
writeFileSync7(indexPath, content);
|
|
24954
25635
|
}
|
|
24955
25636
|
function getMetaPath(skillsDir) {
|
|
24956
|
-
return
|
|
25637
|
+
return join14(skillsDir, ".meta.json");
|
|
24957
25638
|
}
|
|
24958
25639
|
function loadMeta(skillsDir) {
|
|
24959
25640
|
const metaPath2 = getMetaPath(skillsDir);
|
|
@@ -24965,14 +25646,14 @@ function loadMeta(skillsDir) {
|
|
|
24965
25646
|
return { skills: {} };
|
|
24966
25647
|
}
|
|
24967
25648
|
function saveMeta(skillsDir, meta) {
|
|
24968
|
-
|
|
25649
|
+
writeFileSync7(getMetaPath(skillsDir), JSON.stringify(meta, null, 2));
|
|
24969
25650
|
}
|
|
24970
25651
|
function recordInstall(skillsDir, name) {
|
|
24971
25652
|
const meta = loadMeta(skillsDir);
|
|
24972
25653
|
const skillName = normalizeSkillName(name);
|
|
24973
25654
|
let version = "unknown";
|
|
24974
25655
|
try {
|
|
24975
|
-
const pkgPath =
|
|
25656
|
+
const pkgPath = join14(skillsDir, skillName, "package.json");
|
|
24976
25657
|
if (existsSync8(pkgPath)) {
|
|
24977
25658
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
24978
25659
|
version = pkg.version || "unknown";
|
|
@@ -24987,12 +25668,12 @@ function recordRemove(skillsDir, name) {
|
|
|
24987
25668
|
saveMeta(skillsDir, meta);
|
|
24988
25669
|
}
|
|
24989
25670
|
function getInstallMeta(targetDir = process.cwd()) {
|
|
24990
|
-
return loadMeta(
|
|
25671
|
+
return loadMeta(join14(targetDir, ".skills"));
|
|
24991
25672
|
}
|
|
24992
25673
|
function disableSkill(name, targetDir = process.cwd()) {
|
|
24993
|
-
const skillsDir =
|
|
25674
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
24994
25675
|
const skillName = normalizeSkillName(name);
|
|
24995
|
-
if (!existsSync8(
|
|
25676
|
+
if (!existsSync8(join14(skillsDir, skillName)))
|
|
24996
25677
|
return false;
|
|
24997
25678
|
const meta = loadMeta(skillsDir);
|
|
24998
25679
|
const disabled = new Set(meta.disabled || []);
|
|
@@ -25005,7 +25686,7 @@ function disableSkill(name, targetDir = process.cwd()) {
|
|
|
25005
25686
|
return true;
|
|
25006
25687
|
}
|
|
25007
25688
|
function enableSkill(name, targetDir = process.cwd()) {
|
|
25008
|
-
const skillsDir =
|
|
25689
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
25009
25690
|
const meta = loadMeta(skillsDir);
|
|
25010
25691
|
const disabled = new Set(meta.disabled || []);
|
|
25011
25692
|
if (!disabled.has(name))
|
|
@@ -25017,23 +25698,23 @@ function enableSkill(name, targetDir = process.cwd()) {
|
|
|
25017
25698
|
return true;
|
|
25018
25699
|
}
|
|
25019
25700
|
function getDisabledSkills(targetDir = process.cwd()) {
|
|
25020
|
-
const meta = loadMeta(
|
|
25701
|
+
const meta = loadMeta(join14(targetDir, ".skills"));
|
|
25021
25702
|
return meta.disabled || [];
|
|
25022
25703
|
}
|
|
25023
25704
|
function getInstalledSkills(targetDir = process.cwd()) {
|
|
25024
|
-
const skillsDir =
|
|
25705
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
25025
25706
|
if (!existsSync8(skillsDir)) {
|
|
25026
25707
|
return [];
|
|
25027
25708
|
}
|
|
25028
25709
|
return readdirSync6(skillsDir).filter((f) => {
|
|
25029
|
-
const fullPath =
|
|
25710
|
+
const fullPath = join14(skillsDir, f);
|
|
25030
25711
|
return f.startsWith("skill-") && statSync4(fullPath).isDirectory();
|
|
25031
25712
|
}).map((f) => f.replace("skill-", ""));
|
|
25032
25713
|
}
|
|
25033
25714
|
function removeSkill(name, targetDir = process.cwd()) {
|
|
25034
25715
|
const skillName = normalizeSkillName(name);
|
|
25035
|
-
const skillsDir =
|
|
25036
|
-
const skillPath =
|
|
25716
|
+
const skillsDir = join14(targetDir, ".skills");
|
|
25717
|
+
const skillPath = join14(skillsDir, skillName);
|
|
25037
25718
|
if (!existsSync8(skillPath)) {
|
|
25038
25719
|
return false;
|
|
25039
25720
|
}
|
|
@@ -25045,13 +25726,13 @@ function removeSkill(name, targetDir = process.cwd()) {
|
|
|
25045
25726
|
function getAgentSkillsDir(agent, scope = "global", projectDir) {
|
|
25046
25727
|
const agentDir = `.${agent}`;
|
|
25047
25728
|
if (scope === "project") {
|
|
25048
|
-
return
|
|
25729
|
+
return join14(projectDir || process.cwd(), agentDir, "skills");
|
|
25049
25730
|
}
|
|
25050
|
-
return
|
|
25731
|
+
return join14(homedir13(), agentDir, "skills");
|
|
25051
25732
|
}
|
|
25052
25733
|
function getAgentSkillPath(name, agent, scope = "global", projectDir) {
|
|
25053
25734
|
const skillName = normalizeSkillName(name);
|
|
25054
|
-
return
|
|
25735
|
+
return join14(getAgentSkillsDir(agent, scope, projectDir), skillName);
|
|
25055
25736
|
}
|
|
25056
25737
|
function installSkillForAgent(name, options, generateSkillMd) {
|
|
25057
25738
|
const { agent, scope = "global", projectDir } = options;
|
|
@@ -25061,7 +25742,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
25061
25742
|
return { skill: name, success: false, error: `Skill '${name}' not found` };
|
|
25062
25743
|
}
|
|
25063
25744
|
let skillMdContent = null;
|
|
25064
|
-
const skillMdPath =
|
|
25745
|
+
const skillMdPath = join14(sourcePath, "SKILL.md");
|
|
25065
25746
|
if (existsSync8(skillMdPath)) {
|
|
25066
25747
|
skillMdContent = readFileSync7(skillMdPath, "utf-8");
|
|
25067
25748
|
} else if (generateSkillMd) {
|
|
@@ -25072,7 +25753,7 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
25072
25753
|
}
|
|
25073
25754
|
const destDir = getAgentSkillPath(name, agent, scope, projectDir);
|
|
25074
25755
|
if (scope === "global") {
|
|
25075
|
-
const agentBaseDir2 =
|
|
25756
|
+
const agentBaseDir2 = join14(homedir13(), `.${agent}`);
|
|
25076
25757
|
if (!existsSync8(agentBaseDir2)) {
|
|
25077
25758
|
const agentLabels = {
|
|
25078
25759
|
claude: "Claude Code",
|
|
@@ -25096,8 +25777,8 @@ function installSkillForAgent(name, options, generateSkillMd) {
|
|
|
25096
25777
|
}
|
|
25097
25778
|
}
|
|
25098
25779
|
try {
|
|
25099
|
-
|
|
25100
|
-
|
|
25780
|
+
mkdirSync13(destDir, { recursive: true });
|
|
25781
|
+
writeFileSync7(join14(destDir, "SKILL.md"), skillMdContent);
|
|
25101
25782
|
return { skill: name, success: true, path: destDir };
|
|
25102
25783
|
} catch (error) {
|
|
25103
25784
|
return {
|
|
@@ -27034,7 +27715,7 @@ __export(exports_cron_manager, {
|
|
|
27034
27715
|
deleteCronJob: () => deleteCronJob,
|
|
27035
27716
|
createCronJob: () => createCronJob
|
|
27036
27717
|
});
|
|
27037
|
-
import { randomUUID as
|
|
27718
|
+
import { randomUUID as randomUUID15 } from "crypto";
|
|
27038
27719
|
function ensureCronTable() {
|
|
27039
27720
|
const db2 = getDatabase();
|
|
27040
27721
|
db2.exec(`
|
|
@@ -27063,7 +27744,7 @@ function ensureCronTable() {
|
|
|
27063
27744
|
function createCronJob(schedule, task, name) {
|
|
27064
27745
|
ensureCronTable();
|
|
27065
27746
|
const db2 = getDatabase();
|
|
27066
|
-
const id =
|
|
27747
|
+
const id = randomUUID15();
|
|
27067
27748
|
db2.prepare(`
|
|
27068
27749
|
INSERT INTO cron_jobs (id, name, schedule, task_json, enabled)
|
|
27069
27750
|
VALUES (?, ?, ?, ?, 1)
|
|
@@ -27121,7 +27802,7 @@ function getCronEvents(jobId, limit = 10) {
|
|
|
27121
27802
|
async function executeCronJob(job) {
|
|
27122
27803
|
ensureCronTable();
|
|
27123
27804
|
const db2 = getDatabase();
|
|
27124
|
-
const eventId =
|
|
27805
|
+
const eventId = randomUUID15();
|
|
27125
27806
|
const startedAt = new Date().toISOString();
|
|
27126
27807
|
db2.prepare("INSERT INTO cron_events (id, job_id, started_at) VALUES (?, ?, ?)").run(eventId, job.id, startedAt);
|
|
27127
27808
|
try {
|
|
@@ -27212,7 +27893,7 @@ __export(exports_url_watcher, {
|
|
|
27212
27893
|
deleteWatchJob: () => deleteWatchJob,
|
|
27213
27894
|
createWatchJob: () => createWatchJob
|
|
27214
27895
|
});
|
|
27215
|
-
import { randomUUID as
|
|
27896
|
+
import { randomUUID as randomUUID16 } from "crypto";
|
|
27216
27897
|
import { createHash } from "crypto";
|
|
27217
27898
|
function ensureWatchTables() {
|
|
27218
27899
|
const db2 = getDatabase();
|
|
@@ -27244,7 +27925,7 @@ function ensureWatchTables() {
|
|
|
27244
27925
|
function createWatchJob(url, schedule, opts) {
|
|
27245
27926
|
ensureWatchTables();
|
|
27246
27927
|
const db2 = getDatabase();
|
|
27247
|
-
const id =
|
|
27928
|
+
const id = randomUUID16();
|
|
27248
27929
|
db2.prepare(`
|
|
27249
27930
|
INSERT INTO watch_jobs (id, name, url, schedule, selector, extract_schema, enabled)
|
|
27250
27931
|
VALUES (?, ?, ?, ?, ?, ?, 1)
|
|
@@ -27280,7 +27961,7 @@ function getWatchEvents(watchId, limit = 20) {
|
|
|
27280
27961
|
async function checkWatchJob(job) {
|
|
27281
27962
|
ensureWatchTables();
|
|
27282
27963
|
const db2 = getDatabase();
|
|
27283
|
-
const eventId =
|
|
27964
|
+
const eventId = randomUUID16();
|
|
27284
27965
|
const checkedAt = new Date().toISOString();
|
|
27285
27966
|
let newContent = "";
|
|
27286
27967
|
try {
|
|
@@ -31432,7 +32113,7 @@ var NEVER = INVALID;
|
|
|
31432
32113
|
init_session();
|
|
31433
32114
|
init_actions();
|
|
31434
32115
|
import { readFileSync as readFileSync8 } from "fs";
|
|
31435
|
-
import { join as
|
|
32116
|
+
import { join as join15 } from "path";
|
|
31436
32117
|
|
|
31437
32118
|
// src/lib/screenshot.ts
|
|
31438
32119
|
init_types();
|
|
@@ -32202,7 +32883,7 @@ async function closeTab(page, index) {
|
|
|
32202
32883
|
init_dialogs();
|
|
32203
32884
|
init_profiles();
|
|
32204
32885
|
init_types();
|
|
32205
|
-
var _pkg = JSON.parse(readFileSync8(
|
|
32886
|
+
var _pkg = JSON.parse(readFileSync8(join15(import.meta.dir, "../../package.json"), "utf8"));
|
|
32206
32887
|
var networkLogCleanup = new Map;
|
|
32207
32888
|
var consoleCaptureCleanup = new Map;
|
|
32208
32889
|
var harCaptures = new Map;
|
|
@@ -32923,6 +33604,28 @@ server.tool("browser_performance", "Get performance metrics for the current page
|
|
|
32923
33604
|
return err(e);
|
|
32924
33605
|
}
|
|
32925
33606
|
});
|
|
33607
|
+
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 }) => {
|
|
33608
|
+
try {
|
|
33609
|
+
const sid = resolveSessionId(session_id);
|
|
33610
|
+
const page = getSessionPage(sid);
|
|
33611
|
+
const { detectEnvironment: detectEnvironment2 } = await Promise.resolve().then(() => exports_env_detector);
|
|
33612
|
+
const result = await detectEnvironment2(page);
|
|
33613
|
+
return json(result);
|
|
33614
|
+
} catch (e) {
|
|
33615
|
+
return err(e);
|
|
33616
|
+
}
|
|
33617
|
+
});
|
|
33618
|
+
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 }) => {
|
|
33619
|
+
try {
|
|
33620
|
+
const sid = resolveSessionId(session_id);
|
|
33621
|
+
const page = getSessionPage(sid);
|
|
33622
|
+
const { getDeepPerformance: getDeepPerformance2 } = await Promise.resolve().then(() => (init_deep_performance(), exports_deep_performance));
|
|
33623
|
+
const result = await getDeepPerformance2(page);
|
|
33624
|
+
return json(result);
|
|
33625
|
+
} catch (e) {
|
|
33626
|
+
return err(e);
|
|
33627
|
+
}
|
|
33628
|
+
});
|
|
32926
33629
|
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 }) => {
|
|
32927
33630
|
try {
|
|
32928
33631
|
const sid = resolveSessionId(session_id);
|
|
@@ -32986,6 +33689,46 @@ server.tool("browser_recordings_list", "List all recordings", { project_id: expo
|
|
|
32986
33689
|
return err(e);
|
|
32987
33690
|
}
|
|
32988
33691
|
});
|
|
33692
|
+
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 }) => {
|
|
33693
|
+
try {
|
|
33694
|
+
const { saveWorkflowFromRecording: saveWorkflowFromRecording2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
33695
|
+
return json(saveWorkflowFromRecording2(recording_id, name, description));
|
|
33696
|
+
} catch (e) {
|
|
33697
|
+
return err(e);
|
|
33698
|
+
}
|
|
33699
|
+
});
|
|
33700
|
+
server.tool("browser_workflow_list", "List all saved workflows", {}, async () => {
|
|
33701
|
+
try {
|
|
33702
|
+
const { listWorkflows: listWorkflows2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
33703
|
+
const workflows = listWorkflows2();
|
|
33704
|
+
return json({ workflows: workflows.map((w) => ({ ...w, steps: `${w.steps.length} steps` })), count: workflows.length });
|
|
33705
|
+
} catch (e) {
|
|
33706
|
+
return err(e);
|
|
33707
|
+
}
|
|
33708
|
+
});
|
|
33709
|
+
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 }) => {
|
|
33710
|
+
try {
|
|
33711
|
+
const sid = resolveSessionId(session_id);
|
|
33712
|
+
const page = getSessionPage(sid);
|
|
33713
|
+
const { getWorkflowByName: getWorkflowByName2, runWorkflow: runWorkflow2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
33714
|
+
const workflow = getWorkflowByName2(name);
|
|
33715
|
+
if (!workflow)
|
|
33716
|
+
return err(new Error(`Workflow '${name}' not found`));
|
|
33717
|
+
const result = await runWorkflow2(workflow, page);
|
|
33718
|
+
logEvent(sid, "workflow_run", { name, ...result });
|
|
33719
|
+
return json(result);
|
|
33720
|
+
} catch (e) {
|
|
33721
|
+
return err(e);
|
|
33722
|
+
}
|
|
33723
|
+
});
|
|
33724
|
+
server.tool("browser_workflow_delete", "Delete a saved workflow", { name: exports_external.string() }, async ({ name }) => {
|
|
33725
|
+
try {
|
|
33726
|
+
const { deleteWorkflow: deleteWorkflow2 } = await Promise.resolve().then(() => (init_workflows(), exports_workflows));
|
|
33727
|
+
return json({ deleted: deleteWorkflow2(name) });
|
|
33728
|
+
} catch (e) {
|
|
33729
|
+
return err(e);
|
|
33730
|
+
}
|
|
33731
|
+
});
|
|
32989
33732
|
server.tool("browser_crawl", "Crawl a URL recursively and return discovered pages", {
|
|
32990
33733
|
url: exports_external.string(),
|
|
32991
33734
|
max_depth: exports_external.number().optional().default(2),
|
|
@@ -33681,6 +34424,68 @@ server.tool("browser_profile_delete", "Delete a saved browser profile", { name:
|
|
|
33681
34424
|
return err(e);
|
|
33682
34425
|
}
|
|
33683
34426
|
});
|
|
34427
|
+
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 }) => {
|
|
34428
|
+
try {
|
|
34429
|
+
const sid = resolveSessionId(session_id);
|
|
34430
|
+
const { detectAPIs: detectAPIs2 } = await Promise.resolve().then(() => (init_api_detector(), exports_api_detector));
|
|
34431
|
+
const apis = detectAPIs2(sid);
|
|
34432
|
+
return json({ apis, count: apis.length });
|
|
34433
|
+
} catch (e) {
|
|
34434
|
+
return err(e);
|
|
34435
|
+
}
|
|
34436
|
+
});
|
|
34437
|
+
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 }) => {
|
|
34438
|
+
try {
|
|
34439
|
+
const sid = resolveSessionId(session_id);
|
|
34440
|
+
const page = getSessionPage(sid);
|
|
34441
|
+
const { extractStructuredData: extractStructuredData2 } = await Promise.resolve().then(() => exports_structured_extract);
|
|
34442
|
+
const data = await extractStructuredData2(page);
|
|
34443
|
+
return json({
|
|
34444
|
+
tables: data.tables.length,
|
|
34445
|
+
lists: data.lists.length,
|
|
34446
|
+
json_ld: data.jsonLd.length,
|
|
34447
|
+
open_graph: Object.keys(data.openGraph).length,
|
|
34448
|
+
meta_tags: Object.keys(data.metaTags).length,
|
|
34449
|
+
repeated_elements: data.repeatedElements.length,
|
|
34450
|
+
data
|
|
34451
|
+
});
|
|
34452
|
+
} catch (e) {
|
|
34453
|
+
return err(e);
|
|
34454
|
+
}
|
|
34455
|
+
});
|
|
34456
|
+
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 }) => {
|
|
34457
|
+
try {
|
|
34458
|
+
const { saveDataset: saveDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
34459
|
+
const dataset = saveDataset2({ name, rows: data, sourceUrl: source_url });
|
|
34460
|
+
return json({ id: dataset.id, name: dataset.name, row_count: dataset.row_count });
|
|
34461
|
+
} catch (e) {
|
|
34462
|
+
return err(e);
|
|
34463
|
+
}
|
|
34464
|
+
});
|
|
34465
|
+
server.tool("browser_dataset_list", "List all saved datasets", {}, async () => {
|
|
34466
|
+
try {
|
|
34467
|
+
const { listDatasets: listDatasets2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
34468
|
+
return json({ datasets: listDatasets2() });
|
|
34469
|
+
} catch (e) {
|
|
34470
|
+
return err(e);
|
|
34471
|
+
}
|
|
34472
|
+
});
|
|
34473
|
+
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 }) => {
|
|
34474
|
+
try {
|
|
34475
|
+
const { exportDataset: exportDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
34476
|
+
return json(exportDataset2(name, format));
|
|
34477
|
+
} catch (e) {
|
|
34478
|
+
return err(e);
|
|
34479
|
+
}
|
|
34480
|
+
});
|
|
34481
|
+
server.tool("browser_dataset_delete", "Delete a saved dataset", { name: exports_external.string() }, async ({ name }) => {
|
|
34482
|
+
try {
|
|
34483
|
+
const { deleteDataset: deleteDataset2 } = await Promise.resolve().then(() => (init_datasets(), exports_datasets));
|
|
34484
|
+
return json({ deleted: deleteDataset2(name) });
|
|
34485
|
+
} catch (e) {
|
|
34486
|
+
return err(e);
|
|
34487
|
+
}
|
|
34488
|
+
});
|
|
33684
34489
|
server.tool("browser_help", "Show all available browser tools grouped by category with one-line descriptions", {}, async () => {
|
|
33685
34490
|
try {
|
|
33686
34491
|
const groups = {
|
|
@@ -33767,6 +34572,20 @@ server.tool("browser_help", "Show all available browser tools grouped by categor
|
|
|
33767
34572
|
{ tool: "browser_auth_list", description: "List all saved auth flows" },
|
|
33768
34573
|
{ tool: "browser_auth_delete", description: "Delete a saved auth flow" }
|
|
33769
34574
|
],
|
|
34575
|
+
Workflows: [
|
|
34576
|
+
{ tool: "browser_workflow_save", description: "Save a recording as a reusable workflow" },
|
|
34577
|
+
{ tool: "browser_workflow_list", description: "List all saved workflows" },
|
|
34578
|
+
{ tool: "browser_workflow_run", description: "Run a workflow with self-healing replay" },
|
|
34579
|
+
{ tool: "browser_workflow_delete", description: "Delete a saved workflow" }
|
|
34580
|
+
],
|
|
34581
|
+
Data: [
|
|
34582
|
+
{ tool: "browser_extract_structured", description: "Extract tables, lists, JSON-LD, Open Graph, meta tags, repeated elements" },
|
|
34583
|
+
{ tool: "browser_detect_apis", description: "Scan network traffic for JSON API endpoints" },
|
|
34584
|
+
{ tool: "browser_dataset_save", description: "Save extracted data as a named dataset" },
|
|
34585
|
+
{ tool: "browser_dataset_list", description: "List all saved datasets" },
|
|
34586
|
+
{ tool: "browser_dataset_export", description: "Export dataset as JSON or CSV" },
|
|
34587
|
+
{ tool: "browser_dataset_delete", description: "Delete a saved dataset" }
|
|
34588
|
+
],
|
|
33770
34589
|
Crawl: [
|
|
33771
34590
|
{ tool: "browser_crawl", description: "Crawl a URL recursively" }
|
|
33772
34591
|
],
|
|
@@ -33820,6 +34639,8 @@ server.tool("browser_help", "Show all available browser tools grouped by categor
|
|
|
33820
34639
|
{ tool: "browser_check", description: "RECOMMENDED: One-call page summary with diagnostics" },
|
|
33821
34640
|
{ tool: "browser_version", description: "Show running binary version and tool count" },
|
|
33822
34641
|
{ tool: "browser_help", description: "Show this help (all tools)" },
|
|
34642
|
+
{ tool: "browser_detect_env", description: "Detect environment (prod/dev/staging/local)" },
|
|
34643
|
+
{ tool: "browser_performance_deep", description: "Deep performance: resources, third-party, DOM, memory" },
|
|
33823
34644
|
{ tool: "browser_snapshot_diff", description: "Diff current snapshot vs previous" },
|
|
33824
34645
|
{ tool: "browser_watch_start", description: "Watch page for DOM changes" },
|
|
33825
34646
|
{ tool: "browser_watch_get_changes", description: "Get captured DOM changes" },
|