@quanta-intellect/vessel-browser 0.1.136 → 0.1.137
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/out/main/index.js
CHANGED
|
@@ -22147,492 +22147,135 @@ function registerAgentRuntimeHandlers(runtime2, chromeView, sidebarView, sendToR
|
|
|
22147
22147
|
}
|
|
22148
22148
|
);
|
|
22149
22149
|
}
|
|
22150
|
-
|
|
22151
|
-
|
|
22152
|
-
const DEFAULT_BOOKMARK_FOLDER = "Vessel/Bookmarks";
|
|
22153
|
-
const PAGE_CONTENT_LIMIT = 6e3;
|
|
22154
|
-
const DEFAULT_LIST_LIMIT = 50;
|
|
22155
|
-
const DEFAULT_SEARCH_LIMIT = 20;
|
|
22156
|
-
function getVaultRoot() {
|
|
22157
|
-
const configured = loadSettings().obsidianVaultPath.trim();
|
|
22158
|
-
if (!configured) {
|
|
22159
|
-
throw new Error(
|
|
22160
|
-
"Obsidian not configured. Set vault path in Vessel settings to use memory capture."
|
|
22161
|
-
);
|
|
22162
|
-
}
|
|
22163
|
-
return path$1.resolve(configured);
|
|
22150
|
+
function asTextResponse$1(text) {
|
|
22151
|
+
return { content: [{ type: "text", text }] };
|
|
22164
22152
|
}
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
|
|
22171
|
-
|
|
22153
|
+
const DANGEROUS_DEVTOOLS_ACTIONS = /* @__PURE__ */ new Set([
|
|
22154
|
+
"devtools_execute_js",
|
|
22155
|
+
"devtools_modify_dom",
|
|
22156
|
+
"devtools_set_storage"
|
|
22157
|
+
]);
|
|
22158
|
+
let stateListener = null;
|
|
22159
|
+
const activityLog = [];
|
|
22160
|
+
const MAX_ACTIVITY_ENTRIES = 100;
|
|
22161
|
+
let activityCounter = 0;
|
|
22162
|
+
function setDevToolsPanelListener(listener) {
|
|
22163
|
+
stateListener = listener;
|
|
22172
22164
|
}
|
|
22173
|
-
function
|
|
22174
|
-
const
|
|
22175
|
-
|
|
22176
|
-
|
|
22177
|
-
|
|
22178
|
-
|
|
22179
|
-
|
|
22180
|
-
|
|
22181
|
-
throw new Error("Vault note folders cannot traverse outside the vault.");
|
|
22182
|
-
}
|
|
22183
|
-
return segments.join(path$1.sep);
|
|
22165
|
+
function getDevToolsPanelState(tabId) {
|
|
22166
|
+
const session = tabId ? getSession(tabId) : void 0;
|
|
22167
|
+
return {
|
|
22168
|
+
console: session?.getConsoleLogs() ?? [],
|
|
22169
|
+
network: session?.getNetworkLog() ?? [],
|
|
22170
|
+
errors: session?.getErrors() ?? [],
|
|
22171
|
+
activity: activityLog
|
|
22172
|
+
};
|
|
22184
22173
|
}
|
|
22185
|
-
function
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22174
|
+
function broadcastState(tabManager) {
|
|
22175
|
+
if (!stateListener) return;
|
|
22176
|
+
const tabId = tabManager.getActiveTabId();
|
|
22177
|
+
stateListener(getDevToolsPanelState(tabId));
|
|
22178
|
+
}
|
|
22179
|
+
async function withDevToolsAction(runtime2, tabManager, name, args, executor) {
|
|
22180
|
+
const activityEntry = {
|
|
22181
|
+
id: ++activityCounter,
|
|
22182
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22183
|
+
tool: name,
|
|
22184
|
+
args: JSON.stringify(args).slice(0, 200),
|
|
22185
|
+
result: "",
|
|
22186
|
+
durationMs: 0,
|
|
22187
|
+
status: "running"
|
|
22188
|
+
};
|
|
22189
|
+
activityLog.push(activityEntry);
|
|
22190
|
+
if (activityLog.length > MAX_ACTIVITY_ENTRIES) {
|
|
22191
|
+
activityLog.splice(0, activityLog.length - MAX_ACTIVITY_ENTRIES);
|
|
22192
22192
|
}
|
|
22193
|
-
|
|
22194
|
-
|
|
22195
|
-
|
|
22193
|
+
broadcastState(tabManager);
|
|
22194
|
+
const startTime = Date.now();
|
|
22195
|
+
try {
|
|
22196
|
+
const result = await runtime2.runControlledAction({
|
|
22197
|
+
source: "mcp",
|
|
22198
|
+
name,
|
|
22199
|
+
args,
|
|
22200
|
+
tabId: tabManager.getActiveTabId(),
|
|
22201
|
+
dangerous: DANGEROUS_DEVTOOLS_ACTIONS.has(name),
|
|
22202
|
+
executor
|
|
22203
|
+
});
|
|
22204
|
+
activityEntry.status = "completed";
|
|
22205
|
+
activityEntry.result = result.slice(0, 200);
|
|
22206
|
+
activityEntry.durationMs = Date.now() - startTime;
|
|
22207
|
+
broadcastState(tabManager);
|
|
22208
|
+
return asTextResponse$1(result);
|
|
22209
|
+
} catch (error) {
|
|
22210
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
22211
|
+
activityEntry.status = "failed";
|
|
22212
|
+
activityEntry.result = message.slice(0, 200);
|
|
22213
|
+
activityEntry.durationMs = Date.now() - startTime;
|
|
22214
|
+
broadcastState(tabManager);
|
|
22215
|
+
return asTextResponse$1(`Error: ${message}`);
|
|
22196
22216
|
}
|
|
22197
|
-
const normalized = segments.join(path$1.sep);
|
|
22198
|
-
return normalized.endsWith(".md") ? normalized : `${normalized}.md`;
|
|
22199
|
-
}
|
|
22200
|
-
function escapeYaml(value) {
|
|
22201
|
-
return JSON.stringify(value);
|
|
22202
22217
|
}
|
|
22203
|
-
function
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22218
|
+
function registerDevTools(server, tabManager, runtime2) {
|
|
22219
|
+
server.registerTool(
|
|
22220
|
+
"vessel_devtools_console_logs",
|
|
22221
|
+
{
|
|
22222
|
+
title: "DevTools: Get Console Logs",
|
|
22223
|
+
description: "Get console log entries captured from the active tab. Returns a rolling buffer of recent console.log, console.warn, console.error, etc. calls. Automatically starts capturing on first use.",
|
|
22224
|
+
inputSchema: {
|
|
22225
|
+
level: zod.z.enum(["log", "warning", "error", "info", "debug", "verbose"]).optional().describe("Filter by log level"),
|
|
22226
|
+
limit: zod.z.number().optional().describe("Maximum number of entries to return (default: all)"),
|
|
22227
|
+
search: zod.z.string().optional().describe("Filter entries containing this text (case-insensitive)")
|
|
22212
22228
|
}
|
|
22213
|
-
|
|
22229
|
+
},
|
|
22230
|
+
async ({ level, limit, search: search2 }) => {
|
|
22231
|
+
return withDevToolsAction(
|
|
22232
|
+
runtime2,
|
|
22233
|
+
tabManager,
|
|
22234
|
+
"devtools_console_logs",
|
|
22235
|
+
{ level, limit, search: search2 },
|
|
22236
|
+
async () => {
|
|
22237
|
+
const session = getOrCreateSession(tabManager);
|
|
22238
|
+
await session.ensureConsoleDomain();
|
|
22239
|
+
const entries = session.getConsoleLogs({ level, limit, search: search2 });
|
|
22240
|
+
if (entries.length === 0) {
|
|
22241
|
+
return "No console entries captured yet. Console monitoring is now active — new entries will be captured as they occur.";
|
|
22242
|
+
}
|
|
22243
|
+
return JSON.stringify(entries, null, 2);
|
|
22244
|
+
}
|
|
22245
|
+
);
|
|
22214
22246
|
}
|
|
22215
|
-
|
|
22216
|
-
|
|
22217
|
-
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
}
|
|
22234
|
-
return path$1.join(dir, candidate);
|
|
22235
|
-
}
|
|
22236
|
-
function trimContent(content, limit = PAGE_CONTENT_LIMIT) {
|
|
22237
|
-
const cleaned = content.trim();
|
|
22238
|
-
if (cleaned.length <= limit) return cleaned;
|
|
22239
|
-
return `${cleaned.slice(0, limit)}
|
|
22240
|
-
|
|
22241
|
-
[Truncated]`;
|
|
22242
|
-
}
|
|
22243
|
-
function parseFrontmatter(content) {
|
|
22244
|
-
if (!content.startsWith("---\n")) {
|
|
22245
|
-
return { body: content, tags: [] };
|
|
22246
|
-
}
|
|
22247
|
-
const closingIndex = content.indexOf("\n---\n", 4);
|
|
22248
|
-
if (closingIndex === -1) {
|
|
22249
|
-
return { body: content, tags: [] };
|
|
22250
|
-
}
|
|
22251
|
-
const raw = content.slice(4, closingIndex);
|
|
22252
|
-
const body = content.slice(closingIndex + 5);
|
|
22253
|
-
const lines = raw.split("\n");
|
|
22254
|
-
const result = { tags: [] };
|
|
22255
|
-
let activeArrayKey = "";
|
|
22256
|
-
for (const line of lines) {
|
|
22257
|
-
const trimmed = line.trim();
|
|
22258
|
-
if (!trimmed) continue;
|
|
22259
|
-
if (trimmed.startsWith("- ") && activeArrayKey === "tags") {
|
|
22260
|
-
result.tags.push(
|
|
22261
|
-
trimmed.slice(2).trim().replace(/^["']|["']$/g, "")
|
|
22247
|
+
);
|
|
22248
|
+
server.registerTool(
|
|
22249
|
+
"vessel_devtools_console_clear",
|
|
22250
|
+
{
|
|
22251
|
+
title: "DevTools: Clear Console Logs",
|
|
22252
|
+
description: "Clear the captured console log buffer for the active tab."
|
|
22253
|
+
},
|
|
22254
|
+
async () => {
|
|
22255
|
+
return withDevToolsAction(
|
|
22256
|
+
runtime2,
|
|
22257
|
+
tabManager,
|
|
22258
|
+
"devtools_console_clear",
|
|
22259
|
+
{},
|
|
22260
|
+
async () => {
|
|
22261
|
+
const session = getOrCreateSession(tabManager);
|
|
22262
|
+
const count = session.clearConsoleLogs();
|
|
22263
|
+
return `Cleared ${count} console entries.`;
|
|
22264
|
+
}
|
|
22262
22265
|
);
|
|
22263
|
-
continue;
|
|
22264
22266
|
}
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
|
|
22269
|
-
|
|
22270
|
-
|
|
22271
|
-
|
|
22272
|
-
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
|
|
22276
|
-
|
|
22277
|
-
activeArrayKey = "";
|
|
22278
|
-
}
|
|
22279
|
-
}
|
|
22280
|
-
}
|
|
22281
|
-
return { body, title: result.title, tags: result.tags };
|
|
22282
|
-
}
|
|
22283
|
-
function collectMarkdownFiles(dir) {
|
|
22284
|
-
const entries = fs$1.readdirSync(dir, { withFileTypes: true });
|
|
22285
|
-
const files = [];
|
|
22286
|
-
for (const entry of entries) {
|
|
22287
|
-
const absolutePath = path$1.join(dir, entry.name);
|
|
22288
|
-
if (entry.isDirectory()) {
|
|
22289
|
-
files.push(...collectMarkdownFiles(absolutePath));
|
|
22290
|
-
continue;
|
|
22291
|
-
}
|
|
22292
|
-
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
22293
|
-
files.push(absolutePath);
|
|
22294
|
-
}
|
|
22295
|
-
}
|
|
22296
|
-
return files;
|
|
22297
|
-
}
|
|
22298
|
-
function toSummary(absolutePath, vaultRoot) {
|
|
22299
|
-
const stats = fs$1.statSync(absolutePath);
|
|
22300
|
-
const relativePath = path$1.relative(vaultRoot, absolutePath).split(path$1.sep).join("/");
|
|
22301
|
-
const raw = fs$1.readFileSync(absolutePath, "utf-8");
|
|
22302
|
-
const parsed = parseFrontmatter(raw);
|
|
22303
|
-
const headingMatch = parsed.body.match(/^#\s+(.+)$/m);
|
|
22304
|
-
const title = parsed.title || headingMatch?.[1]?.trim() || path$1.basename(absolutePath, ".md");
|
|
22305
|
-
return {
|
|
22306
|
-
title,
|
|
22307
|
-
absolutePath,
|
|
22308
|
-
relativePath,
|
|
22309
|
-
modifiedAt: stats.mtime.toISOString(),
|
|
22310
|
-
tags: parsed.tags
|
|
22311
|
-
};
|
|
22312
|
-
}
|
|
22313
|
-
function renderBookmarkLinkBlock(bookmark, note) {
|
|
22314
|
-
const lines = [
|
|
22315
|
-
"## Linked Bookmark",
|
|
22316
|
-
"",
|
|
22317
|
-
`- Bookmark ID: \`${bookmark.id}\``,
|
|
22318
|
-
`- Title: ${bookmark.title || bookmark.url}`,
|
|
22319
|
-
`- URL: [${bookmark.url}](${bookmark.url})`,
|
|
22320
|
-
`- Saved At: ${bookmark.savedAt}`
|
|
22321
|
-
];
|
|
22322
|
-
if (note?.trim()) {
|
|
22323
|
-
lines.push("", "### Context", "", note.trim());
|
|
22324
|
-
}
|
|
22325
|
-
return `${lines.join("\n")}
|
|
22326
|
-
`;
|
|
22327
|
-
}
|
|
22328
|
-
function writeMemoryNote({
|
|
22329
|
-
title,
|
|
22330
|
-
body,
|
|
22331
|
-
folder,
|
|
22332
|
-
tags = [],
|
|
22333
|
-
frontmatter = {}
|
|
22334
|
-
}) {
|
|
22335
|
-
const vaultRoot = getVaultRoot();
|
|
22336
|
-
const relativeFolder = normalizeFolder(folder, DEFAULT_NOTE_FOLDER);
|
|
22337
|
-
const targetDir = path$1.join(vaultRoot, relativeFolder);
|
|
22338
|
-
fs$1.mkdirSync(targetDir, { recursive: true });
|
|
22339
|
-
const absolutePath = buildUniqueNotePath(targetDir, title);
|
|
22340
|
-
const relativePath = path$1.relative(vaultRoot, absolutePath);
|
|
22341
|
-
const content = [
|
|
22342
|
-
renderFrontmatter({
|
|
22343
|
-
title,
|
|
22344
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22345
|
-
tags,
|
|
22346
|
-
...frontmatter
|
|
22347
|
-
}),
|
|
22348
|
-
body.trim(),
|
|
22349
|
-
""
|
|
22350
|
-
].join("\n");
|
|
22351
|
-
fs$1.writeFileSync(absolutePath, content, "utf-8");
|
|
22352
|
-
return {
|
|
22353
|
-
title,
|
|
22354
|
-
absolutePath,
|
|
22355
|
-
relativePath: relativePath.split(path$1.sep).join("/")
|
|
22356
|
-
};
|
|
22357
|
-
}
|
|
22358
|
-
function appendToMemoryNote({
|
|
22359
|
-
notePath,
|
|
22360
|
-
content,
|
|
22361
|
-
heading
|
|
22362
|
-
}) {
|
|
22363
|
-
const vaultRoot = getVaultRoot();
|
|
22364
|
-
const relativePath = normalizeNotePath(notePath);
|
|
22365
|
-
const absolutePath = assertInsideVault(
|
|
22366
|
-
path$1.join(vaultRoot, relativePath),
|
|
22367
|
-
vaultRoot
|
|
22368
|
-
);
|
|
22369
|
-
if (!fs$1.existsSync(absolutePath)) {
|
|
22370
|
-
throw new Error(
|
|
22371
|
-
`Memory note not found: ${relativePath.split(path$1.sep).join("/")}`
|
|
22372
|
-
);
|
|
22373
|
-
}
|
|
22374
|
-
const current = fs$1.readFileSync(absolutePath, "utf-8").trimEnd();
|
|
22375
|
-
const nextParts = [current, ""];
|
|
22376
|
-
if (heading?.trim()) {
|
|
22377
|
-
nextParts.push(`## ${heading.trim()}`, "");
|
|
22378
|
-
}
|
|
22379
|
-
nextParts.push(content.trim(), "");
|
|
22380
|
-
fs$1.writeFileSync(absolutePath, nextParts.join("\n"), "utf-8");
|
|
22381
|
-
return {
|
|
22382
|
-
title: path$1.basename(absolutePath, ".md"),
|
|
22383
|
-
absolutePath,
|
|
22384
|
-
relativePath: relativePath.split(path$1.sep).join("/")
|
|
22385
|
-
};
|
|
22386
|
-
}
|
|
22387
|
-
function listMemoryNotes({
|
|
22388
|
-
folder,
|
|
22389
|
-
limit = DEFAULT_LIST_LIMIT
|
|
22390
|
-
} = {}) {
|
|
22391
|
-
const vaultRoot = getVaultRoot();
|
|
22392
|
-
const relativeFolder = normalizeFolder(folder, "");
|
|
22393
|
-
const targetDir = relativeFolder ? path$1.join(vaultRoot, relativeFolder) : vaultRoot;
|
|
22394
|
-
if (!fs$1.existsSync(targetDir)) {
|
|
22395
|
-
return [];
|
|
22396
|
-
}
|
|
22397
|
-
return collectMarkdownFiles(targetDir).map((absolutePath) => toSummary(absolutePath, vaultRoot)).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, Math.max(1, limit));
|
|
22398
|
-
}
|
|
22399
|
-
function searchMemoryNotes({
|
|
22400
|
-
query,
|
|
22401
|
-
folder,
|
|
22402
|
-
tags = [],
|
|
22403
|
-
limit = DEFAULT_SEARCH_LIMIT
|
|
22404
|
-
}) {
|
|
22405
|
-
const loweredQuery = query.trim().toLowerCase();
|
|
22406
|
-
if (!loweredQuery) {
|
|
22407
|
-
throw new Error("A non-empty memory search query is required.");
|
|
22408
|
-
}
|
|
22409
|
-
const vaultRoot = getVaultRoot();
|
|
22410
|
-
const relativeFolder = normalizeFolder(folder, "");
|
|
22411
|
-
const targetDir = relativeFolder ? path$1.join(vaultRoot, relativeFolder) : vaultRoot;
|
|
22412
|
-
if (!fs$1.existsSync(targetDir)) {
|
|
22413
|
-
return [];
|
|
22414
|
-
}
|
|
22415
|
-
const loweredTags = tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean);
|
|
22416
|
-
return collectMarkdownFiles(targetDir).map((absolutePath) => {
|
|
22417
|
-
const raw = fs$1.readFileSync(absolutePath, "utf-8");
|
|
22418
|
-
const parsed = parseFrontmatter(raw);
|
|
22419
|
-
const summary = toSummary(absolutePath, vaultRoot);
|
|
22420
|
-
const haystack = `${summary.title}
|
|
22421
|
-
${summary.relativePath}
|
|
22422
|
-
${parsed.body}`.toLowerCase();
|
|
22423
|
-
const hasQuery = haystack.includes(loweredQuery);
|
|
22424
|
-
const hasTags = loweredTags.length === 0 || loweredTags.every(
|
|
22425
|
-
(tag) => summary.tags.some((noteTag) => noteTag.toLowerCase() === tag)
|
|
22426
|
-
);
|
|
22427
|
-
return hasQuery && hasTags ? summary : null;
|
|
22428
|
-
}).filter((item) => item !== null).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, Math.max(1, limit));
|
|
22429
|
-
}
|
|
22430
|
-
function capturePageToVault({
|
|
22431
|
-
page,
|
|
22432
|
-
title,
|
|
22433
|
-
folder,
|
|
22434
|
-
summary,
|
|
22435
|
-
note,
|
|
22436
|
-
tags = []
|
|
22437
|
-
}) {
|
|
22438
|
-
const noteTitle = title?.trim() || page.title.trim() || page.url;
|
|
22439
|
-
const bodyLines = [
|
|
22440
|
-
`# ${noteTitle}`,
|
|
22441
|
-
"",
|
|
22442
|
-
`Source: [${page.title || page.url}](${page.url})`,
|
|
22443
|
-
`Captured: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
22444
|
-
];
|
|
22445
|
-
if (page.byline) {
|
|
22446
|
-
bodyLines.push(`Byline: ${page.byline}`);
|
|
22447
|
-
}
|
|
22448
|
-
bodyLines.push("");
|
|
22449
|
-
if (summary?.trim()) {
|
|
22450
|
-
bodyLines.push("## Summary", "", summary.trim(), "");
|
|
22451
|
-
}
|
|
22452
|
-
if (note?.trim()) {
|
|
22453
|
-
bodyLines.push("## Research Note", "", note.trim(), "");
|
|
22454
|
-
}
|
|
22455
|
-
if (page.excerpt.trim()) {
|
|
22456
|
-
bodyLines.push("## Excerpt", "", page.excerpt.trim(), "");
|
|
22457
|
-
}
|
|
22458
|
-
const snapshot2 = trimContent(page.content);
|
|
22459
|
-
if (snapshot2) {
|
|
22460
|
-
bodyLines.push("## Page Snapshot", "", snapshot2, "");
|
|
22461
|
-
}
|
|
22462
|
-
return writeMemoryNote({
|
|
22463
|
-
title: noteTitle,
|
|
22464
|
-
body: bodyLines.join("\n"),
|
|
22465
|
-
folder: folder || DEFAULT_PAGE_FOLDER,
|
|
22466
|
-
tags,
|
|
22467
|
-
frontmatter: {
|
|
22468
|
-
source_url: page.url,
|
|
22469
|
-
source_title: page.title || page.url
|
|
22470
|
-
}
|
|
22471
|
-
});
|
|
22472
|
-
}
|
|
22473
|
-
function linkBookmarkToMemory({
|
|
22474
|
-
bookmark,
|
|
22475
|
-
notePath,
|
|
22476
|
-
title,
|
|
22477
|
-
folder,
|
|
22478
|
-
note,
|
|
22479
|
-
tags = []
|
|
22480
|
-
}) {
|
|
22481
|
-
if (notePath?.trim()) {
|
|
22482
|
-
return appendToMemoryNote({
|
|
22483
|
-
notePath,
|
|
22484
|
-
heading: "Linked Bookmark",
|
|
22485
|
-
content: [
|
|
22486
|
-
`- Bookmark ID: \`${bookmark.id}\``,
|
|
22487
|
-
`- Title: ${bookmark.title || bookmark.url}`,
|
|
22488
|
-
`- URL: [${bookmark.url}](${bookmark.url})`,
|
|
22489
|
-
`- Saved At: ${bookmark.savedAt}`,
|
|
22490
|
-
note?.trim() ? `- Note: ${note.trim()}` : ""
|
|
22491
|
-
].filter(Boolean).join("\n")
|
|
22492
|
-
});
|
|
22493
|
-
}
|
|
22494
|
-
const noteTitle = title?.trim() || bookmark.title || bookmark.url;
|
|
22495
|
-
return writeMemoryNote({
|
|
22496
|
-
title: noteTitle,
|
|
22497
|
-
body: renderBookmarkLinkBlock(bookmark, note),
|
|
22498
|
-
folder: folder || DEFAULT_BOOKMARK_FOLDER,
|
|
22499
|
-
tags,
|
|
22500
|
-
frontmatter: {
|
|
22501
|
-
bookmark_id: bookmark.id,
|
|
22502
|
-
source_url: bookmark.url,
|
|
22503
|
-
source_title: bookmark.title || bookmark.url
|
|
22504
|
-
}
|
|
22505
|
-
});
|
|
22506
|
-
}
|
|
22507
|
-
function asTextResponse$1(text) {
|
|
22508
|
-
return { content: [{ type: "text", text }] };
|
|
22509
|
-
}
|
|
22510
|
-
const DANGEROUS_DEVTOOLS_ACTIONS = /* @__PURE__ */ new Set([
|
|
22511
|
-
"devtools_execute_js",
|
|
22512
|
-
"devtools_modify_dom",
|
|
22513
|
-
"devtools_set_storage"
|
|
22514
|
-
]);
|
|
22515
|
-
let stateListener = null;
|
|
22516
|
-
const activityLog = [];
|
|
22517
|
-
const MAX_ACTIVITY_ENTRIES = 100;
|
|
22518
|
-
let activityCounter = 0;
|
|
22519
|
-
function setDevToolsPanelListener(listener) {
|
|
22520
|
-
stateListener = listener;
|
|
22521
|
-
}
|
|
22522
|
-
function getDevToolsPanelState(tabId) {
|
|
22523
|
-
const session = tabId ? getSession(tabId) : void 0;
|
|
22524
|
-
return {
|
|
22525
|
-
console: session?.getConsoleLogs() ?? [],
|
|
22526
|
-
network: session?.getNetworkLog() ?? [],
|
|
22527
|
-
errors: session?.getErrors() ?? [],
|
|
22528
|
-
activity: activityLog
|
|
22529
|
-
};
|
|
22530
|
-
}
|
|
22531
|
-
function broadcastState(tabManager) {
|
|
22532
|
-
if (!stateListener) return;
|
|
22533
|
-
const tabId = tabManager.getActiveTabId();
|
|
22534
|
-
stateListener(getDevToolsPanelState(tabId));
|
|
22535
|
-
}
|
|
22536
|
-
async function withDevToolsAction(runtime2, tabManager, name, args, executor) {
|
|
22537
|
-
const activityEntry = {
|
|
22538
|
-
id: ++activityCounter,
|
|
22539
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22540
|
-
tool: name,
|
|
22541
|
-
args: JSON.stringify(args).slice(0, 200),
|
|
22542
|
-
result: "",
|
|
22543
|
-
durationMs: 0,
|
|
22544
|
-
status: "running"
|
|
22545
|
-
};
|
|
22546
|
-
activityLog.push(activityEntry);
|
|
22547
|
-
if (activityLog.length > MAX_ACTIVITY_ENTRIES) {
|
|
22548
|
-
activityLog.splice(0, activityLog.length - MAX_ACTIVITY_ENTRIES);
|
|
22549
|
-
}
|
|
22550
|
-
broadcastState(tabManager);
|
|
22551
|
-
const startTime = Date.now();
|
|
22552
|
-
try {
|
|
22553
|
-
const result = await runtime2.runControlledAction({
|
|
22554
|
-
source: "mcp",
|
|
22555
|
-
name,
|
|
22556
|
-
args,
|
|
22557
|
-
tabId: tabManager.getActiveTabId(),
|
|
22558
|
-
dangerous: DANGEROUS_DEVTOOLS_ACTIONS.has(name),
|
|
22559
|
-
executor
|
|
22560
|
-
});
|
|
22561
|
-
activityEntry.status = "completed";
|
|
22562
|
-
activityEntry.result = result.slice(0, 200);
|
|
22563
|
-
activityEntry.durationMs = Date.now() - startTime;
|
|
22564
|
-
broadcastState(tabManager);
|
|
22565
|
-
return asTextResponse$1(result);
|
|
22566
|
-
} catch (error) {
|
|
22567
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
22568
|
-
activityEntry.status = "failed";
|
|
22569
|
-
activityEntry.result = message.slice(0, 200);
|
|
22570
|
-
activityEntry.durationMs = Date.now() - startTime;
|
|
22571
|
-
broadcastState(tabManager);
|
|
22572
|
-
return asTextResponse$1(`Error: ${message}`);
|
|
22573
|
-
}
|
|
22574
|
-
}
|
|
22575
|
-
function registerDevTools(server, tabManager, runtime2) {
|
|
22576
|
-
server.registerTool(
|
|
22577
|
-
"vessel_devtools_console_logs",
|
|
22578
|
-
{
|
|
22579
|
-
title: "DevTools: Get Console Logs",
|
|
22580
|
-
description: "Get console log entries captured from the active tab. Returns a rolling buffer of recent console.log, console.warn, console.error, etc. calls. Automatically starts capturing on first use.",
|
|
22581
|
-
inputSchema: {
|
|
22582
|
-
level: zod.z.enum(["log", "warning", "error", "info", "debug", "verbose"]).optional().describe("Filter by log level"),
|
|
22583
|
-
limit: zod.z.number().optional().describe("Maximum number of entries to return (default: all)"),
|
|
22584
|
-
search: zod.z.string().optional().describe("Filter entries containing this text (case-insensitive)")
|
|
22585
|
-
}
|
|
22586
|
-
},
|
|
22587
|
-
async ({ level, limit, search: search2 }) => {
|
|
22588
|
-
return withDevToolsAction(
|
|
22589
|
-
runtime2,
|
|
22590
|
-
tabManager,
|
|
22591
|
-
"devtools_console_logs",
|
|
22592
|
-
{ level, limit, search: search2 },
|
|
22593
|
-
async () => {
|
|
22594
|
-
const session = getOrCreateSession(tabManager);
|
|
22595
|
-
await session.ensureConsoleDomain();
|
|
22596
|
-
const entries = session.getConsoleLogs({ level, limit, search: search2 });
|
|
22597
|
-
if (entries.length === 0) {
|
|
22598
|
-
return "No console entries captured yet. Console monitoring is now active — new entries will be captured as they occur.";
|
|
22599
|
-
}
|
|
22600
|
-
return JSON.stringify(entries, null, 2);
|
|
22601
|
-
}
|
|
22602
|
-
);
|
|
22603
|
-
}
|
|
22604
|
-
);
|
|
22605
|
-
server.registerTool(
|
|
22606
|
-
"vessel_devtools_console_clear",
|
|
22607
|
-
{
|
|
22608
|
-
title: "DevTools: Clear Console Logs",
|
|
22609
|
-
description: "Clear the captured console log buffer for the active tab."
|
|
22610
|
-
},
|
|
22611
|
-
async () => {
|
|
22612
|
-
return withDevToolsAction(
|
|
22613
|
-
runtime2,
|
|
22614
|
-
tabManager,
|
|
22615
|
-
"devtools_console_clear",
|
|
22616
|
-
{},
|
|
22617
|
-
async () => {
|
|
22618
|
-
const session = getOrCreateSession(tabManager);
|
|
22619
|
-
const count = session.clearConsoleLogs();
|
|
22620
|
-
return `Cleared ${count} console entries.`;
|
|
22621
|
-
}
|
|
22622
|
-
);
|
|
22623
|
-
}
|
|
22624
|
-
);
|
|
22625
|
-
server.registerTool(
|
|
22626
|
-
"vessel_devtools_network_log",
|
|
22627
|
-
{
|
|
22628
|
-
title: "DevTools: Get Network Log",
|
|
22629
|
-
description: "Get captured network requests/responses from the active tab. Returns method, URL, status, timing, headers, and size. Automatically starts capturing on first use.",
|
|
22630
|
-
inputSchema: {
|
|
22631
|
-
url_pattern: zod.z.string().optional().describe("Filter by URL pattern (regex or substring match)"),
|
|
22632
|
-
method: zod.z.string().optional().describe("Filter by HTTP method (GET, POST, etc.)"),
|
|
22633
|
-
status_min: zod.z.number().optional().describe("Minimum HTTP status code (e.g., 400 for errors)"),
|
|
22634
|
-
status_max: zod.z.number().optional().describe("Maximum HTTP status code"),
|
|
22635
|
-
limit: zod.z.number().optional().describe("Maximum number of entries to return (default: all)")
|
|
22267
|
+
);
|
|
22268
|
+
server.registerTool(
|
|
22269
|
+
"vessel_devtools_network_log",
|
|
22270
|
+
{
|
|
22271
|
+
title: "DevTools: Get Network Log",
|
|
22272
|
+
description: "Get captured network requests/responses from the active tab. Returns method, URL, status, timing, headers, and size. Automatically starts capturing on first use.",
|
|
22273
|
+
inputSchema: {
|
|
22274
|
+
url_pattern: zod.z.string().optional().describe("Filter by URL pattern (regex or substring match)"),
|
|
22275
|
+
method: zod.z.string().optional().describe("Filter by HTTP method (GET, POST, etc.)"),
|
|
22276
|
+
status_min: zod.z.number().optional().describe("Minimum HTTP status code (e.g., 400 for errors)"),
|
|
22277
|
+
status_max: zod.z.number().optional().describe("Maximum HTTP status code"),
|
|
22278
|
+
limit: zod.z.number().optional().describe("Maximum number of entries to return (default: all)")
|
|
22636
22279
|
}
|
|
22637
22280
|
},
|
|
22638
22281
|
async ({ url_pattern, method, status_min, status_max, limit }) => {
|
|
@@ -22788,154 +22431,529 @@ function registerDevTools(server, tabManager, runtime2) {
|
|
|
22788
22431
|
}
|
|
22789
22432
|
);
|
|
22790
22433
|
}
|
|
22791
|
-
);
|
|
22792
|
-
server.registerTool(
|
|
22793
|
-
"vessel_devtools_execute_js",
|
|
22794
|
-
{
|
|
22795
|
-
title: "DevTools: Execute JavaScript",
|
|
22796
|
-
description: "Execute a JavaScript expression in the context of the active tab's page via the Runtime.evaluate CDP method. Supports async/await. This is a dangerous action — it can modify page state.",
|
|
22797
|
-
inputSchema: {
|
|
22798
|
-
expression: zod.z.string().describe("JavaScript expression to evaluate in the page context")
|
|
22434
|
+
);
|
|
22435
|
+
server.registerTool(
|
|
22436
|
+
"vessel_devtools_execute_js",
|
|
22437
|
+
{
|
|
22438
|
+
title: "DevTools: Execute JavaScript",
|
|
22439
|
+
description: "Execute a JavaScript expression in the context of the active tab's page via the Runtime.evaluate CDP method. Supports async/await. This is a dangerous action — it can modify page state.",
|
|
22440
|
+
inputSchema: {
|
|
22441
|
+
expression: zod.z.string().describe("JavaScript expression to evaluate in the page context")
|
|
22442
|
+
}
|
|
22443
|
+
},
|
|
22444
|
+
async ({ expression }) => {
|
|
22445
|
+
return withDevToolsAction(
|
|
22446
|
+
runtime2,
|
|
22447
|
+
tabManager,
|
|
22448
|
+
"devtools_execute_js",
|
|
22449
|
+
{ expression: expression.slice(0, 200) },
|
|
22450
|
+
async () => {
|
|
22451
|
+
const session = getOrCreateSession(tabManager);
|
|
22452
|
+
const result = await session.executeJs(expression);
|
|
22453
|
+
const parts = [`[${result.type}] ${result.result}`];
|
|
22454
|
+
if (result.exceptionDetails) {
|
|
22455
|
+
parts.push(`
|
|
22456
|
+
Exception: ${result.exceptionDetails}`);
|
|
22457
|
+
}
|
|
22458
|
+
return parts.join("");
|
|
22459
|
+
}
|
|
22460
|
+
);
|
|
22461
|
+
}
|
|
22462
|
+
);
|
|
22463
|
+
server.registerTool(
|
|
22464
|
+
"vessel_devtools_get_storage",
|
|
22465
|
+
{
|
|
22466
|
+
title: "DevTools: Get Storage",
|
|
22467
|
+
description: "Read browser storage for the active tab's origin. Supports localStorage, sessionStorage, cookies, and IndexedDB database listing.",
|
|
22468
|
+
inputSchema: {
|
|
22469
|
+
type: zod.z.enum(["localStorage", "sessionStorage", "cookie", "indexedDB"]).describe("Storage type to read")
|
|
22470
|
+
}
|
|
22471
|
+
},
|
|
22472
|
+
async ({ type }) => {
|
|
22473
|
+
return withDevToolsAction(
|
|
22474
|
+
runtime2,
|
|
22475
|
+
tabManager,
|
|
22476
|
+
"devtools_get_storage",
|
|
22477
|
+
{ type },
|
|
22478
|
+
async () => {
|
|
22479
|
+
const session = getOrCreateSession(tabManager);
|
|
22480
|
+
const data = await session.getStorage(type);
|
|
22481
|
+
const count = Object.keys(data.entries).length;
|
|
22482
|
+
if (count === 0) {
|
|
22483
|
+
return `No ${type} entries found for ${data.origin}`;
|
|
22484
|
+
}
|
|
22485
|
+
return JSON.stringify(data, null, 2);
|
|
22486
|
+
}
|
|
22487
|
+
);
|
|
22488
|
+
}
|
|
22489
|
+
);
|
|
22490
|
+
server.registerTool(
|
|
22491
|
+
"vessel_devtools_set_storage",
|
|
22492
|
+
{
|
|
22493
|
+
title: "DevTools: Set Storage",
|
|
22494
|
+
description: "Set or remove a key in localStorage or sessionStorage for the active tab. This is a dangerous action that modifies page state.",
|
|
22495
|
+
inputSchema: {
|
|
22496
|
+
type: zod.z.enum(["localStorage", "sessionStorage"]).describe("Storage type to modify"),
|
|
22497
|
+
key: zod.z.string().describe("Storage key"),
|
|
22498
|
+
value: zod.z.string().nullable().describe("Value to set, or null to remove the key")
|
|
22499
|
+
}
|
|
22500
|
+
},
|
|
22501
|
+
async ({ type, key: key2, value }) => {
|
|
22502
|
+
return withDevToolsAction(
|
|
22503
|
+
runtime2,
|
|
22504
|
+
tabManager,
|
|
22505
|
+
"devtools_set_storage",
|
|
22506
|
+
{ type, key: key2, value: value ? value.slice(0, 100) : null },
|
|
22507
|
+
async () => {
|
|
22508
|
+
const session = getOrCreateSession(tabManager);
|
|
22509
|
+
return session.setStorage(type, key2, value);
|
|
22510
|
+
}
|
|
22511
|
+
);
|
|
22512
|
+
}
|
|
22513
|
+
);
|
|
22514
|
+
server.registerTool(
|
|
22515
|
+
"vessel_devtools_performance",
|
|
22516
|
+
{
|
|
22517
|
+
title: "DevTools: Performance Snapshot",
|
|
22518
|
+
description: "Get a performance snapshot for the active tab including navigation timing, paint metrics, memory usage, and resource loading statistics."
|
|
22519
|
+
},
|
|
22520
|
+
async () => {
|
|
22521
|
+
return withDevToolsAction(
|
|
22522
|
+
runtime2,
|
|
22523
|
+
tabManager,
|
|
22524
|
+
"devtools_performance",
|
|
22525
|
+
{},
|
|
22526
|
+
async () => {
|
|
22527
|
+
const session = getOrCreateSession(tabManager);
|
|
22528
|
+
const snapshot2 = await session.getPerformanceSnapshot();
|
|
22529
|
+
return JSON.stringify(snapshot2, null, 2);
|
|
22530
|
+
}
|
|
22531
|
+
);
|
|
22532
|
+
}
|
|
22533
|
+
);
|
|
22534
|
+
server.registerTool(
|
|
22535
|
+
"vessel_devtools_get_errors",
|
|
22536
|
+
{
|
|
22537
|
+
title: "DevTools: Get Errors",
|
|
22538
|
+
description: "Get captured JavaScript errors and unhandled promise rejections from the active tab. Automatically starts capturing on first use.",
|
|
22539
|
+
inputSchema: {
|
|
22540
|
+
type: zod.z.enum(["exception", "unhandled-rejection"]).optional().describe("Filter by error type"),
|
|
22541
|
+
limit: zod.z.number().optional().describe("Maximum number of entries to return (default: all)")
|
|
22542
|
+
}
|
|
22543
|
+
},
|
|
22544
|
+
async ({ type, limit }) => {
|
|
22545
|
+
return withDevToolsAction(
|
|
22546
|
+
runtime2,
|
|
22547
|
+
tabManager,
|
|
22548
|
+
"devtools_get_errors",
|
|
22549
|
+
{ type, limit },
|
|
22550
|
+
async () => {
|
|
22551
|
+
const session = getOrCreateSession(tabManager);
|
|
22552
|
+
await session.ensureErrorCapture();
|
|
22553
|
+
const entries = session.getErrors({ type, limit });
|
|
22554
|
+
if (entries.length === 0) {
|
|
22555
|
+
return "No errors captured yet. Error monitoring is now active — exceptions and unhandled rejections will be captured as they occur.";
|
|
22556
|
+
}
|
|
22557
|
+
return JSON.stringify(entries, null, 2);
|
|
22558
|
+
}
|
|
22559
|
+
);
|
|
22560
|
+
}
|
|
22561
|
+
);
|
|
22562
|
+
server.registerTool(
|
|
22563
|
+
"vessel_devtools_clear_errors",
|
|
22564
|
+
{
|
|
22565
|
+
title: "DevTools: Clear Errors",
|
|
22566
|
+
description: "Clear the captured error buffer for the active tab."
|
|
22567
|
+
},
|
|
22568
|
+
async () => {
|
|
22569
|
+
return withDevToolsAction(
|
|
22570
|
+
runtime2,
|
|
22571
|
+
tabManager,
|
|
22572
|
+
"devtools_clear_errors",
|
|
22573
|
+
{},
|
|
22574
|
+
async () => {
|
|
22575
|
+
const session = getOrCreateSession(tabManager);
|
|
22576
|
+
const count = session.clearErrors();
|
|
22577
|
+
return `Cleared ${count} error entries.`;
|
|
22578
|
+
}
|
|
22579
|
+
);
|
|
22580
|
+
}
|
|
22581
|
+
);
|
|
22582
|
+
}
|
|
22583
|
+
const DEFAULT_PAGE_FOLDER = "Vessel/Pages";
|
|
22584
|
+
const DEFAULT_NOTE_FOLDER = "Vessel/Research";
|
|
22585
|
+
const DEFAULT_BOOKMARK_FOLDER = "Vessel/Bookmarks";
|
|
22586
|
+
const PAGE_CONTENT_LIMIT = 6e3;
|
|
22587
|
+
const DEFAULT_LIST_LIMIT = 50;
|
|
22588
|
+
const DEFAULT_SEARCH_LIMIT = 20;
|
|
22589
|
+
const { access: access$1, mkdir: mkdir$1, readFile: readFile$1, readdir: readdir$1, stat, writeFile: writeFile$1 } = fs$1.promises;
|
|
22590
|
+
function getVaultRoot() {
|
|
22591
|
+
const configured = loadSettings().obsidianVaultPath.trim();
|
|
22592
|
+
if (!configured) {
|
|
22593
|
+
throw new Error(
|
|
22594
|
+
"Obsidian not configured. Set vault path in Vessel settings to use memory capture."
|
|
22595
|
+
);
|
|
22596
|
+
}
|
|
22597
|
+
return path$1.resolve(configured);
|
|
22598
|
+
}
|
|
22599
|
+
function assertInsideVault(targetPath, vaultRoot) {
|
|
22600
|
+
const resolved = path$1.resolve(targetPath);
|
|
22601
|
+
const relative = path$1.relative(vaultRoot, resolved);
|
|
22602
|
+
if (relative.startsWith("..") || path$1.isAbsolute(relative)) {
|
|
22603
|
+
throw new Error("Resolved note path is outside the configured vault.");
|
|
22604
|
+
}
|
|
22605
|
+
return resolved;
|
|
22606
|
+
}
|
|
22607
|
+
function normalizeFolder(folder, fallback) {
|
|
22608
|
+
const raw = (folder?.trim() || fallback).replace(/\\/g, "/");
|
|
22609
|
+
if (!raw) return fallback;
|
|
22610
|
+
if (path$1.isAbsolute(raw)) {
|
|
22611
|
+
throw new Error("Vault note folders must be relative to the vault root.");
|
|
22612
|
+
}
|
|
22613
|
+
const segments = raw.split("/").filter(Boolean);
|
|
22614
|
+
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
22615
|
+
throw new Error("Vault note folders cannot traverse outside the vault.");
|
|
22616
|
+
}
|
|
22617
|
+
return segments.join(path$1.sep);
|
|
22618
|
+
}
|
|
22619
|
+
function normalizeNotePath(notePath) {
|
|
22620
|
+
const raw = notePath.trim().replace(/\\/g, "/");
|
|
22621
|
+
if (!raw) {
|
|
22622
|
+
throw new Error("A note path is required.");
|
|
22623
|
+
}
|
|
22624
|
+
if (path$1.isAbsolute(raw)) {
|
|
22625
|
+
throw new Error("Note paths must be relative to the vault root.");
|
|
22626
|
+
}
|
|
22627
|
+
const segments = raw.split("/").filter(Boolean);
|
|
22628
|
+
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
22629
|
+
throw new Error("Note paths cannot traverse outside the vault.");
|
|
22630
|
+
}
|
|
22631
|
+
const normalized = segments.join(path$1.sep);
|
|
22632
|
+
return normalized.endsWith(".md") ? normalized : `${normalized}.md`;
|
|
22633
|
+
}
|
|
22634
|
+
function escapeYaml(value) {
|
|
22635
|
+
return JSON.stringify(value);
|
|
22636
|
+
}
|
|
22637
|
+
function renderFrontmatter(data) {
|
|
22638
|
+
const lines = ["---"];
|
|
22639
|
+
for (const [key2, value] of Object.entries(data)) {
|
|
22640
|
+
if (value == null) continue;
|
|
22641
|
+
if (Array.isArray(value)) {
|
|
22642
|
+
if (value.length === 0) continue;
|
|
22643
|
+
lines.push(`${key2}:`);
|
|
22644
|
+
for (const item of value) {
|
|
22645
|
+
lines.push(` - ${escapeYaml(item)}`);
|
|
22646
|
+
}
|
|
22647
|
+
continue;
|
|
22648
|
+
}
|
|
22649
|
+
lines.push(`${key2}: ${escapeYaml(value)}`);
|
|
22650
|
+
}
|
|
22651
|
+
lines.push("---", "");
|
|
22652
|
+
return lines.join("\n");
|
|
22653
|
+
}
|
|
22654
|
+
function slugify(value) {
|
|
22655
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
22656
|
+
return normalized || "note";
|
|
22657
|
+
}
|
|
22658
|
+
async function pathExists$1(filePath2) {
|
|
22659
|
+
try {
|
|
22660
|
+
await access$1(filePath2);
|
|
22661
|
+
return true;
|
|
22662
|
+
} catch {
|
|
22663
|
+
return false;
|
|
22664
|
+
}
|
|
22665
|
+
}
|
|
22666
|
+
async function buildUniqueNotePath(dir, title) {
|
|
22667
|
+
const datePrefix = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22668
|
+
const slug = slugify(title);
|
|
22669
|
+
const base = `${datePrefix}-${slug}`;
|
|
22670
|
+
let candidate = `${base}.md`;
|
|
22671
|
+
let counter = 2;
|
|
22672
|
+
while (await pathExists$1(path$1.join(dir, candidate))) {
|
|
22673
|
+
candidate = `${base}-${counter}.md`;
|
|
22674
|
+
counter += 1;
|
|
22675
|
+
}
|
|
22676
|
+
return path$1.join(dir, candidate);
|
|
22677
|
+
}
|
|
22678
|
+
function trimContent(content, limit = PAGE_CONTENT_LIMIT) {
|
|
22679
|
+
const cleaned = content.trim();
|
|
22680
|
+
if (cleaned.length <= limit) return cleaned;
|
|
22681
|
+
return `${cleaned.slice(0, limit)}
|
|
22682
|
+
|
|
22683
|
+
[Truncated]`;
|
|
22684
|
+
}
|
|
22685
|
+
function parseFrontmatter(content) {
|
|
22686
|
+
if (!content.startsWith("---\n")) {
|
|
22687
|
+
return { body: content, tags: [] };
|
|
22688
|
+
}
|
|
22689
|
+
const closingIndex = content.indexOf("\n---\n", 4);
|
|
22690
|
+
if (closingIndex === -1) {
|
|
22691
|
+
return { body: content, tags: [] };
|
|
22692
|
+
}
|
|
22693
|
+
const raw = content.slice(4, closingIndex);
|
|
22694
|
+
const body = content.slice(closingIndex + 5);
|
|
22695
|
+
const lines = raw.split("\n");
|
|
22696
|
+
const result = { tags: [] };
|
|
22697
|
+
let activeArrayKey = "";
|
|
22698
|
+
for (const line of lines) {
|
|
22699
|
+
const trimmed = line.trim();
|
|
22700
|
+
if (!trimmed) continue;
|
|
22701
|
+
if (trimmed.startsWith("- ") && activeArrayKey === "tags") {
|
|
22702
|
+
result.tags.push(
|
|
22703
|
+
trimmed.slice(2).trim().replace(/^["']|["']$/g, "")
|
|
22704
|
+
);
|
|
22705
|
+
continue;
|
|
22706
|
+
}
|
|
22707
|
+
activeArrayKey = "";
|
|
22708
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
22709
|
+
if (separatorIndex === -1) continue;
|
|
22710
|
+
const key2 = trimmed.slice(0, separatorIndex).trim();
|
|
22711
|
+
const value = trimmed.slice(separatorIndex + 1).trim();
|
|
22712
|
+
if (key2 === "title" && value) {
|
|
22713
|
+
result.title = value.replace(/^["']|["']$/g, "");
|
|
22714
|
+
} else if (key2 === "tags") {
|
|
22715
|
+
activeArrayKey = "tags";
|
|
22716
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
22717
|
+
const inline = value.slice(1, -1).split(",").map((item) => item.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
22718
|
+
result.tags.push(...inline);
|
|
22719
|
+
activeArrayKey = "";
|
|
22799
22720
|
}
|
|
22800
|
-
},
|
|
22801
|
-
async ({ expression }) => {
|
|
22802
|
-
return withDevToolsAction(
|
|
22803
|
-
runtime2,
|
|
22804
|
-
tabManager,
|
|
22805
|
-
"devtools_execute_js",
|
|
22806
|
-
{ expression: expression.slice(0, 200) },
|
|
22807
|
-
async () => {
|
|
22808
|
-
const session = getOrCreateSession(tabManager);
|
|
22809
|
-
const result = await session.executeJs(expression);
|
|
22810
|
-
const parts = [`[${result.type}] ${result.result}`];
|
|
22811
|
-
if (result.exceptionDetails) {
|
|
22812
|
-
parts.push(`
|
|
22813
|
-
Exception: ${result.exceptionDetails}`);
|
|
22814
|
-
}
|
|
22815
|
-
return parts.join("");
|
|
22816
|
-
}
|
|
22817
|
-
);
|
|
22818
22721
|
}
|
|
22819
|
-
|
|
22820
|
-
|
|
22821
|
-
|
|
22822
|
-
|
|
22823
|
-
|
|
22824
|
-
|
|
22825
|
-
|
|
22826
|
-
|
|
22722
|
+
}
|
|
22723
|
+
return { body, title: result.title, tags: result.tags };
|
|
22724
|
+
}
|
|
22725
|
+
async function collectMarkdownFiles(dir) {
|
|
22726
|
+
const entries = await readdir$1(dir, { withFileTypes: true });
|
|
22727
|
+
const nestedFiles = await Promise.all(
|
|
22728
|
+
entries.map(async (entry) => {
|
|
22729
|
+
const absolutePath = path$1.join(dir, entry.name);
|
|
22730
|
+
if (entry.isDirectory()) {
|
|
22731
|
+
return collectMarkdownFiles(absolutePath);
|
|
22827
22732
|
}
|
|
22828
|
-
|
|
22829
|
-
|
|
22830
|
-
return withDevToolsAction(
|
|
22831
|
-
runtime2,
|
|
22832
|
-
tabManager,
|
|
22833
|
-
"devtools_get_storage",
|
|
22834
|
-
{ type },
|
|
22835
|
-
async () => {
|
|
22836
|
-
const session = getOrCreateSession(tabManager);
|
|
22837
|
-
const data = await session.getStorage(type);
|
|
22838
|
-
const count = Object.keys(data.entries).length;
|
|
22839
|
-
if (count === 0) {
|
|
22840
|
-
return `No ${type} entries found for ${data.origin}`;
|
|
22841
|
-
}
|
|
22842
|
-
return JSON.stringify(data, null, 2);
|
|
22843
|
-
}
|
|
22844
|
-
);
|
|
22845
|
-
}
|
|
22846
|
-
);
|
|
22847
|
-
server.registerTool(
|
|
22848
|
-
"vessel_devtools_set_storage",
|
|
22849
|
-
{
|
|
22850
|
-
title: "DevTools: Set Storage",
|
|
22851
|
-
description: "Set or remove a key in localStorage or sessionStorage for the active tab. This is a dangerous action that modifies page state.",
|
|
22852
|
-
inputSchema: {
|
|
22853
|
-
type: zod.z.enum(["localStorage", "sessionStorage"]).describe("Storage type to modify"),
|
|
22854
|
-
key: zod.z.string().describe("Storage key"),
|
|
22855
|
-
value: zod.z.string().nullable().describe("Value to set, or null to remove the key")
|
|
22733
|
+
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
22734
|
+
return [absolutePath];
|
|
22856
22735
|
}
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
return withDevToolsAction(
|
|
22860
|
-
runtime2,
|
|
22861
|
-
tabManager,
|
|
22862
|
-
"devtools_set_storage",
|
|
22863
|
-
{ type, key: key2, value: value ? value.slice(0, 100) : null },
|
|
22864
|
-
async () => {
|
|
22865
|
-
const session = getOrCreateSession(tabManager);
|
|
22866
|
-
return session.setStorage(type, key2, value);
|
|
22867
|
-
}
|
|
22868
|
-
);
|
|
22869
|
-
}
|
|
22736
|
+
return [];
|
|
22737
|
+
})
|
|
22870
22738
|
);
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
|
|
22878
|
-
|
|
22879
|
-
|
|
22880
|
-
|
|
22881
|
-
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
|
|
22888
|
-
|
|
22889
|
-
|
|
22739
|
+
return nestedFiles.flat();
|
|
22740
|
+
}
|
|
22741
|
+
async function toSummary(absolutePath, vaultRoot, raw) {
|
|
22742
|
+
const stats = await stat(absolutePath);
|
|
22743
|
+
const relativePath = path$1.relative(vaultRoot, absolutePath).split(path$1.sep).join("/");
|
|
22744
|
+
const noteContent = raw ?? await readFile$1(absolutePath, "utf-8");
|
|
22745
|
+
const parsed = parseFrontmatter(noteContent);
|
|
22746
|
+
const headingMatch = parsed.body.match(/^#\s+(.+)$/m);
|
|
22747
|
+
const title = parsed.title || headingMatch?.[1]?.trim() || path$1.basename(absolutePath, ".md");
|
|
22748
|
+
return {
|
|
22749
|
+
title,
|
|
22750
|
+
absolutePath,
|
|
22751
|
+
relativePath,
|
|
22752
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
22753
|
+
tags: parsed.tags
|
|
22754
|
+
};
|
|
22755
|
+
}
|
|
22756
|
+
function renderBookmarkLinkBlock(bookmark, note) {
|
|
22757
|
+
const lines = [
|
|
22758
|
+
"## Linked Bookmark",
|
|
22759
|
+
"",
|
|
22760
|
+
`- Bookmark ID: \`${bookmark.id}\``,
|
|
22761
|
+
`- Title: ${bookmark.title || bookmark.url}`,
|
|
22762
|
+
`- URL: [${bookmark.url}](${bookmark.url})`,
|
|
22763
|
+
`- Saved At: ${bookmark.savedAt}`
|
|
22764
|
+
];
|
|
22765
|
+
if (note?.trim()) {
|
|
22766
|
+
lines.push("", "### Context", "", note.trim());
|
|
22767
|
+
}
|
|
22768
|
+
return `${lines.join("\n")}
|
|
22769
|
+
`;
|
|
22770
|
+
}
|
|
22771
|
+
async function writeMemoryNote({
|
|
22772
|
+
title,
|
|
22773
|
+
body,
|
|
22774
|
+
folder,
|
|
22775
|
+
tags = [],
|
|
22776
|
+
frontmatter = {}
|
|
22777
|
+
}) {
|
|
22778
|
+
const vaultRoot = getVaultRoot();
|
|
22779
|
+
const relativeFolder = normalizeFolder(folder, DEFAULT_NOTE_FOLDER);
|
|
22780
|
+
const targetDir = path$1.join(vaultRoot, relativeFolder);
|
|
22781
|
+
await mkdir$1(targetDir, { recursive: true });
|
|
22782
|
+
const absolutePath = await buildUniqueNotePath(targetDir, title);
|
|
22783
|
+
const relativePath = path$1.relative(vaultRoot, absolutePath);
|
|
22784
|
+
const content = [
|
|
22785
|
+
renderFrontmatter({
|
|
22786
|
+
title,
|
|
22787
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22788
|
+
tags,
|
|
22789
|
+
...frontmatter
|
|
22790
|
+
}),
|
|
22791
|
+
body.trim(),
|
|
22792
|
+
""
|
|
22793
|
+
].join("\n");
|
|
22794
|
+
await writeFile$1(absolutePath, content, "utf-8");
|
|
22795
|
+
return {
|
|
22796
|
+
title,
|
|
22797
|
+
absolutePath,
|
|
22798
|
+
relativePath: relativePath.split(path$1.sep).join("/")
|
|
22799
|
+
};
|
|
22800
|
+
}
|
|
22801
|
+
async function appendToMemoryNote({
|
|
22802
|
+
notePath,
|
|
22803
|
+
content,
|
|
22804
|
+
heading
|
|
22805
|
+
}) {
|
|
22806
|
+
const vaultRoot = getVaultRoot();
|
|
22807
|
+
const relativePath = normalizeNotePath(notePath);
|
|
22808
|
+
const absolutePath = assertInsideVault(
|
|
22809
|
+
path$1.join(vaultRoot, relativePath),
|
|
22810
|
+
vaultRoot
|
|
22890
22811
|
);
|
|
22891
|
-
|
|
22892
|
-
|
|
22893
|
-
|
|
22894
|
-
|
|
22895
|
-
|
|
22896
|
-
|
|
22897
|
-
|
|
22898
|
-
|
|
22899
|
-
|
|
22900
|
-
|
|
22901
|
-
|
|
22902
|
-
|
|
22903
|
-
|
|
22904
|
-
|
|
22905
|
-
|
|
22906
|
-
|
|
22907
|
-
|
|
22908
|
-
|
|
22909
|
-
|
|
22910
|
-
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22812
|
+
if (!await pathExists$1(absolutePath)) {
|
|
22813
|
+
throw new Error(
|
|
22814
|
+
`Memory note not found: ${relativePath.split(path$1.sep).join("/")}`
|
|
22815
|
+
);
|
|
22816
|
+
}
|
|
22817
|
+
const current = (await readFile$1(absolutePath, "utf-8")).trimEnd();
|
|
22818
|
+
const nextParts = [current, ""];
|
|
22819
|
+
if (heading?.trim()) {
|
|
22820
|
+
nextParts.push(`## ${heading.trim()}`, "");
|
|
22821
|
+
}
|
|
22822
|
+
nextParts.push(content.trim(), "");
|
|
22823
|
+
await writeFile$1(absolutePath, nextParts.join("\n"), "utf-8");
|
|
22824
|
+
return {
|
|
22825
|
+
title: path$1.basename(absolutePath, ".md"),
|
|
22826
|
+
absolutePath,
|
|
22827
|
+
relativePath: relativePath.split(path$1.sep).join("/")
|
|
22828
|
+
};
|
|
22829
|
+
}
|
|
22830
|
+
async function listMemoryNotes({
|
|
22831
|
+
folder,
|
|
22832
|
+
limit = DEFAULT_LIST_LIMIT
|
|
22833
|
+
} = {}) {
|
|
22834
|
+
const vaultRoot = getVaultRoot();
|
|
22835
|
+
const relativeFolder = normalizeFolder(folder, "");
|
|
22836
|
+
const targetDir = relativeFolder ? path$1.join(vaultRoot, relativeFolder) : vaultRoot;
|
|
22837
|
+
if (!await pathExists$1(targetDir)) {
|
|
22838
|
+
return [];
|
|
22839
|
+
}
|
|
22840
|
+
const notes = await Promise.all(
|
|
22841
|
+
(await collectMarkdownFiles(targetDir)).map(
|
|
22842
|
+
(absolutePath) => toSummary(absolutePath, vaultRoot)
|
|
22843
|
+
)
|
|
22844
|
+
);
|
|
22845
|
+
return notes.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, Math.max(1, limit));
|
|
22846
|
+
}
|
|
22847
|
+
async function searchMemoryNotes({
|
|
22848
|
+
query,
|
|
22849
|
+
folder,
|
|
22850
|
+
tags = [],
|
|
22851
|
+
limit = DEFAULT_SEARCH_LIMIT
|
|
22852
|
+
}) {
|
|
22853
|
+
const loweredQuery = query.trim().toLowerCase();
|
|
22854
|
+
if (!loweredQuery) {
|
|
22855
|
+
throw new Error("A non-empty memory search query is required.");
|
|
22856
|
+
}
|
|
22857
|
+
const vaultRoot = getVaultRoot();
|
|
22858
|
+
const relativeFolder = normalizeFolder(folder, "");
|
|
22859
|
+
const targetDir = relativeFolder ? path$1.join(vaultRoot, relativeFolder) : vaultRoot;
|
|
22860
|
+
if (!await pathExists$1(targetDir)) {
|
|
22861
|
+
return [];
|
|
22862
|
+
}
|
|
22863
|
+
const loweredTags = tags.map((tag) => tag.trim().toLowerCase()).filter(Boolean);
|
|
22864
|
+
const matches = await Promise.all(
|
|
22865
|
+
(await collectMarkdownFiles(targetDir)).map(async (absolutePath) => {
|
|
22866
|
+
const raw = await readFile$1(absolutePath, "utf-8");
|
|
22867
|
+
const parsed = parseFrontmatter(raw);
|
|
22868
|
+
const summary = await toSummary(absolutePath, vaultRoot, raw);
|
|
22869
|
+
const haystack = `${summary.title}
|
|
22870
|
+
${summary.relativePath}
|
|
22871
|
+
${parsed.body}`.toLowerCase();
|
|
22872
|
+
const hasQuery = haystack.includes(loweredQuery);
|
|
22873
|
+
const hasTags = loweredTags.length === 0 || loweredTags.every(
|
|
22874
|
+
(tag) => summary.tags.some((noteTag) => noteTag.toLowerCase() === tag)
|
|
22916
22875
|
);
|
|
22917
|
-
|
|
22876
|
+
return hasQuery && hasTags ? summary : null;
|
|
22877
|
+
})
|
|
22918
22878
|
);
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22879
|
+
return matches.filter((item) => item !== null).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, Math.max(1, limit));
|
|
22880
|
+
}
|
|
22881
|
+
async function capturePageToVault({
|
|
22882
|
+
page,
|
|
22883
|
+
title,
|
|
22884
|
+
folder,
|
|
22885
|
+
summary,
|
|
22886
|
+
note,
|
|
22887
|
+
tags = []
|
|
22888
|
+
}) {
|
|
22889
|
+
const noteTitle = title?.trim() || page.title.trim() || page.url;
|
|
22890
|
+
const bodyLines = [
|
|
22891
|
+
`# ${noteTitle}`,
|
|
22892
|
+
"",
|
|
22893
|
+
`Source: [${page.title || page.url}](${page.url})`,
|
|
22894
|
+
`Captured: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
22895
|
+
];
|
|
22896
|
+
if (page.byline) {
|
|
22897
|
+
bodyLines.push(`Byline: ${page.byline}`);
|
|
22898
|
+
}
|
|
22899
|
+
bodyLines.push("");
|
|
22900
|
+
if (summary?.trim()) {
|
|
22901
|
+
bodyLines.push("## Summary", "", summary.trim(), "");
|
|
22902
|
+
}
|
|
22903
|
+
if (note?.trim()) {
|
|
22904
|
+
bodyLines.push("## Research Note", "", note.trim(), "");
|
|
22905
|
+
}
|
|
22906
|
+
if (page.excerpt.trim()) {
|
|
22907
|
+
bodyLines.push("## Excerpt", "", page.excerpt.trim(), "");
|
|
22908
|
+
}
|
|
22909
|
+
const snapshot2 = trimContent(page.content);
|
|
22910
|
+
if (snapshot2) {
|
|
22911
|
+
bodyLines.push("## Page Snapshot", "", snapshot2, "");
|
|
22912
|
+
}
|
|
22913
|
+
return await writeMemoryNote({
|
|
22914
|
+
title: noteTitle,
|
|
22915
|
+
body: bodyLines.join("\n"),
|
|
22916
|
+
folder: folder || DEFAULT_PAGE_FOLDER,
|
|
22917
|
+
tags,
|
|
22918
|
+
frontmatter: {
|
|
22919
|
+
source_url: page.url,
|
|
22920
|
+
source_title: page.title || page.url
|
|
22937
22921
|
}
|
|
22938
|
-
);
|
|
22922
|
+
});
|
|
22923
|
+
}
|
|
22924
|
+
async function linkBookmarkToMemory({
|
|
22925
|
+
bookmark,
|
|
22926
|
+
notePath,
|
|
22927
|
+
title,
|
|
22928
|
+
folder,
|
|
22929
|
+
note,
|
|
22930
|
+
tags = []
|
|
22931
|
+
}) {
|
|
22932
|
+
if (notePath?.trim()) {
|
|
22933
|
+
return await appendToMemoryNote({
|
|
22934
|
+
notePath,
|
|
22935
|
+
heading: "Linked Bookmark",
|
|
22936
|
+
content: [
|
|
22937
|
+
`- Bookmark ID: \`${bookmark.id}\``,
|
|
22938
|
+
`- Title: ${bookmark.title || bookmark.url}`,
|
|
22939
|
+
`- URL: [${bookmark.url}](${bookmark.url})`,
|
|
22940
|
+
`- Saved At: ${bookmark.savedAt}`,
|
|
22941
|
+
note?.trim() ? `- Note: ${note.trim()}` : ""
|
|
22942
|
+
].filter(Boolean).join("\n")
|
|
22943
|
+
});
|
|
22944
|
+
}
|
|
22945
|
+
const noteTitle = title?.trim() || bookmark.title || bookmark.url;
|
|
22946
|
+
return await writeMemoryNote({
|
|
22947
|
+
title: noteTitle,
|
|
22948
|
+
body: renderBookmarkLinkBlock(bookmark, note),
|
|
22949
|
+
folder: folder || DEFAULT_BOOKMARK_FOLDER,
|
|
22950
|
+
tags,
|
|
22951
|
+
frontmatter: {
|
|
22952
|
+
bookmark_id: bookmark.id,
|
|
22953
|
+
source_url: bookmark.url,
|
|
22954
|
+
source_title: bookmark.title || bookmark.url
|
|
22955
|
+
}
|
|
22956
|
+
});
|
|
22939
22957
|
}
|
|
22940
22958
|
const logger$f = createLogger("MCP");
|
|
22941
22959
|
function asTextResponse(text) {
|
|
@@ -23641,106 +23659,257 @@ function registerBookmarkTools(server, tabManager, runtime2) {
|
|
|
23641
23659
|
}
|
|
23642
23660
|
);
|
|
23643
23661
|
server.registerTool(
|
|
23644
|
-
"folder_remove",
|
|
23662
|
+
"folder_remove",
|
|
23663
|
+
{
|
|
23664
|
+
title: "Remove Bookmark Folder",
|
|
23665
|
+
description: "Remove a folder. By default bookmarks in it are moved to Unsorted. Set delete_contents to true to delete them with the folder.",
|
|
23666
|
+
inputSchema: {
|
|
23667
|
+
folder_id: zod.z.string().describe("ID of the folder to remove"),
|
|
23668
|
+
delete_contents: zod.z.boolean().optional().default(false).describe(
|
|
23669
|
+
"If true, delete all bookmarks in the folder. If false (default), move them to Unsorted."
|
|
23670
|
+
)
|
|
23671
|
+
}
|
|
23672
|
+
},
|
|
23673
|
+
async ({ folder_id, delete_contents }) => {
|
|
23674
|
+
return withAction(
|
|
23675
|
+
runtime2,
|
|
23676
|
+
tabManager,
|
|
23677
|
+
"remove_bookmark_folder",
|
|
23678
|
+
{ folder_id, delete_contents },
|
|
23679
|
+
async () => {
|
|
23680
|
+
const removed = removeFolder(
|
|
23681
|
+
folder_id,
|
|
23682
|
+
delete_contents
|
|
23683
|
+
);
|
|
23684
|
+
if (!removed) return `Folder ${folder_id} not found`;
|
|
23685
|
+
return composeFolderAwareResponse$1(
|
|
23686
|
+
delete_contents ? `Removed folder ${folder_id} and deleted its bookmarks.` : `Removed folder ${folder_id}. Bookmarks moved to Unsorted.`
|
|
23687
|
+
);
|
|
23688
|
+
}
|
|
23689
|
+
);
|
|
23690
|
+
}
|
|
23691
|
+
);
|
|
23692
|
+
server.registerTool(
|
|
23693
|
+
"folder_rename",
|
|
23694
|
+
{
|
|
23695
|
+
title: "Rename Bookmark Folder",
|
|
23696
|
+
description: "Rename an existing bookmark folder.",
|
|
23697
|
+
inputSchema: {
|
|
23698
|
+
folder_id: zod.z.string().describe("ID of the folder to rename"),
|
|
23699
|
+
new_name: zod.z.string().describe("New name for the folder"),
|
|
23700
|
+
summary: zod.z.string().optional().describe("Optional one-sentence summary for the folder")
|
|
23701
|
+
}
|
|
23702
|
+
},
|
|
23703
|
+
async ({ folder_id, new_name, summary }) => {
|
|
23704
|
+
return withAction(
|
|
23705
|
+
runtime2,
|
|
23706
|
+
tabManager,
|
|
23707
|
+
"rename_bookmark_folder",
|
|
23708
|
+
{ folder_id, new_name, summary },
|
|
23709
|
+
async () => {
|
|
23710
|
+
const existing = findFolderByName(new_name);
|
|
23711
|
+
if (existing && existing.id !== folder_id) {
|
|
23712
|
+
return composeFolderAwareResponse$1(
|
|
23713
|
+
`Folder "${existing.name}" already exists (id=${existing.id})`
|
|
23714
|
+
);
|
|
23715
|
+
}
|
|
23716
|
+
const folder = renameFolder(
|
|
23717
|
+
folder_id,
|
|
23718
|
+
new_name,
|
|
23719
|
+
summary
|
|
23720
|
+
);
|
|
23721
|
+
return folder ? composeFolderAwareResponse$1(`Renamed folder to "${folder.name}"`) : `Folder ${folder_id} not found`;
|
|
23722
|
+
}
|
|
23723
|
+
);
|
|
23724
|
+
}
|
|
23725
|
+
);
|
|
23726
|
+
server.registerTool(
|
|
23727
|
+
"memory_link_bookmark",
|
|
23728
|
+
{
|
|
23729
|
+
title: "Link Bookmark To Memory",
|
|
23730
|
+
description: "Create a note for a bookmark or append bookmark details into an existing memory note.",
|
|
23731
|
+
inputSchema: {
|
|
23732
|
+
bookmark_id: zod.z.string().describe("Bookmark ID to link"),
|
|
23733
|
+
note_path: zod.z.string().optional().describe("Existing relative note path to append into"),
|
|
23734
|
+
title: zod.z.string().optional().describe("Optional title when creating a new note"),
|
|
23735
|
+
folder: zod.z.string().optional().describe("Relative folder when creating a new note"),
|
|
23736
|
+
note: zod.z.string().optional().describe(
|
|
23737
|
+
"Optional rationale or breadcrumb to store with the bookmark"
|
|
23738
|
+
),
|
|
23739
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags when creating a new note")
|
|
23740
|
+
}
|
|
23741
|
+
},
|
|
23742
|
+
async ({ bookmark_id, note_path, title, folder, note, tags }) => {
|
|
23743
|
+
return withAction(
|
|
23744
|
+
runtime2,
|
|
23745
|
+
tabManager,
|
|
23746
|
+
"memory_link_bookmark",
|
|
23747
|
+
{ bookmark_id, note_path, title, folder, tags },
|
|
23748
|
+
async () => {
|
|
23749
|
+
const bookmark = getBookmark(bookmark_id);
|
|
23750
|
+
if (!bookmark) {
|
|
23751
|
+
return `Bookmark ${bookmark_id} not found`;
|
|
23752
|
+
}
|
|
23753
|
+
const saved = await linkBookmarkToMemory({
|
|
23754
|
+
bookmark,
|
|
23755
|
+
notePath: note_path,
|
|
23756
|
+
title,
|
|
23757
|
+
folder,
|
|
23758
|
+
note,
|
|
23759
|
+
tags
|
|
23760
|
+
});
|
|
23761
|
+
return `Linked bookmark "${bookmark.title}" to memory note ${saved.relativePath}`;
|
|
23762
|
+
}
|
|
23763
|
+
);
|
|
23764
|
+
}
|
|
23765
|
+
);
|
|
23766
|
+
}
|
|
23767
|
+
function registerMemoryTools(server, tabManager, runtime2) {
|
|
23768
|
+
server.registerTool(
|
|
23769
|
+
"memory_note_create",
|
|
23770
|
+
{
|
|
23771
|
+
title: "Create Memory Note",
|
|
23772
|
+
description: "Write a markdown note into the configured Obsidian vault for research notes, breadcrumbs, or synthesis.",
|
|
23773
|
+
inputSchema: {
|
|
23774
|
+
title: zod.z.string().describe("Title of the note"),
|
|
23775
|
+
body: zod.z.string().describe("Markdown body for the note"),
|
|
23776
|
+
folder: zod.z.string().optional().describe(
|
|
23777
|
+
"Relative folder inside the vault (default: Vessel/Research)"
|
|
23778
|
+
),
|
|
23779
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags to store in frontmatter")
|
|
23780
|
+
}
|
|
23781
|
+
},
|
|
23782
|
+
async ({ title, body, folder, tags }) => {
|
|
23783
|
+
return withAction(
|
|
23784
|
+
runtime2,
|
|
23785
|
+
tabManager,
|
|
23786
|
+
"memory_note_create",
|
|
23787
|
+
{ title, folder, tags },
|
|
23788
|
+
async () => {
|
|
23789
|
+
const saved = await writeMemoryNote({ title, body, folder, tags });
|
|
23790
|
+
return `Saved memory note "${saved.title}" to ${saved.relativePath}`;
|
|
23791
|
+
}
|
|
23792
|
+
);
|
|
23793
|
+
}
|
|
23794
|
+
);
|
|
23795
|
+
server.registerTool(
|
|
23796
|
+
"memory_append",
|
|
23797
|
+
{
|
|
23798
|
+
title: "Append Memory Note",
|
|
23799
|
+
description: "Append markdown content to an existing note in the configured Obsidian vault.",
|
|
23800
|
+
inputSchema: {
|
|
23801
|
+
note_path: zod.z.string().describe("Relative path to an existing note inside the vault"),
|
|
23802
|
+
content: zod.z.string().describe("Markdown content to append"),
|
|
23803
|
+
heading: zod.z.string().optional().describe("Optional section heading to add before the content")
|
|
23804
|
+
}
|
|
23805
|
+
},
|
|
23806
|
+
async ({ note_path, content, heading }) => {
|
|
23807
|
+
return withAction(
|
|
23808
|
+
runtime2,
|
|
23809
|
+
tabManager,
|
|
23810
|
+
"memory_note_append",
|
|
23811
|
+
{ note_path, heading },
|
|
23812
|
+
async () => {
|
|
23813
|
+
const saved = await appendToMemoryNote({
|
|
23814
|
+
notePath: note_path,
|
|
23815
|
+
content,
|
|
23816
|
+
heading
|
|
23817
|
+
});
|
|
23818
|
+
return `Appended memory note at ${saved.relativePath}`;
|
|
23819
|
+
}
|
|
23820
|
+
);
|
|
23821
|
+
}
|
|
23822
|
+
);
|
|
23823
|
+
server.registerTool(
|
|
23824
|
+
"memory_list",
|
|
23645
23825
|
{
|
|
23646
|
-
title: "
|
|
23647
|
-
description: "
|
|
23826
|
+
title: "List Memory Notes",
|
|
23827
|
+
description: "List recent markdown notes in the configured Obsidian vault.",
|
|
23648
23828
|
inputSchema: {
|
|
23649
|
-
|
|
23650
|
-
|
|
23651
|
-
"If true, delete all bookmarks in the folder. If false (default), move them to Unsorted."
|
|
23652
|
-
)
|
|
23829
|
+
folder: zod.z.string().optional().describe("Optional relative folder inside the vault"),
|
|
23830
|
+
limit: zod.z.number().int().positive().max(200).optional().describe("Maximum number of notes to return")
|
|
23653
23831
|
}
|
|
23654
23832
|
},
|
|
23655
|
-
async ({
|
|
23833
|
+
async ({ folder, limit }) => {
|
|
23656
23834
|
return withAction(
|
|
23657
23835
|
runtime2,
|
|
23658
23836
|
tabManager,
|
|
23659
|
-
"
|
|
23660
|
-
{
|
|
23837
|
+
"memory_note_list",
|
|
23838
|
+
{ folder, limit },
|
|
23661
23839
|
async () => {
|
|
23662
|
-
const
|
|
23663
|
-
|
|
23664
|
-
|
|
23665
|
-
|
|
23666
|
-
|
|
23667
|
-
|
|
23668
|
-
|
|
23669
|
-
);
|
|
23840
|
+
const notes = await listMemoryNotes({ folder, limit });
|
|
23841
|
+
if (notes.length === 0) {
|
|
23842
|
+
return "No memory notes found.";
|
|
23843
|
+
}
|
|
23844
|
+
return notes.map(
|
|
23845
|
+
(note) => `- ${note.title} | path=${note.relativePath} | modified=${note.modifiedAt}${note.tags.length ? ` | tags=${note.tags.join(",")}` : ""}`
|
|
23846
|
+
).join("\n");
|
|
23670
23847
|
}
|
|
23671
23848
|
);
|
|
23672
23849
|
}
|
|
23673
23850
|
);
|
|
23674
23851
|
server.registerTool(
|
|
23675
|
-
"
|
|
23852
|
+
"memory_search",
|
|
23676
23853
|
{
|
|
23677
|
-
title: "
|
|
23678
|
-
description: "
|
|
23854
|
+
title: "Search Memory Notes",
|
|
23855
|
+
description: "Search markdown notes in the configured Obsidian vault by title, path, body, and optional tags.",
|
|
23679
23856
|
inputSchema: {
|
|
23680
|
-
|
|
23681
|
-
|
|
23682
|
-
|
|
23857
|
+
query: zod.z.string().describe("Search query"),
|
|
23858
|
+
folder: zod.z.string().optional().describe("Optional relative folder inside the vault"),
|
|
23859
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags that matching notes must contain"),
|
|
23860
|
+
limit: zod.z.number().int().positive().max(100).optional().describe("Maximum number of matching notes to return")
|
|
23683
23861
|
}
|
|
23684
23862
|
},
|
|
23685
|
-
async ({
|
|
23863
|
+
async ({ query, folder, tags, limit }) => {
|
|
23686
23864
|
return withAction(
|
|
23687
23865
|
runtime2,
|
|
23688
23866
|
tabManager,
|
|
23689
|
-
"
|
|
23690
|
-
{
|
|
23867
|
+
"memory_note_search",
|
|
23868
|
+
{ query, folder, tags, limit },
|
|
23691
23869
|
async () => {
|
|
23692
|
-
const
|
|
23693
|
-
if (
|
|
23694
|
-
return
|
|
23695
|
-
`Folder "${existing.name}" already exists (id=${existing.id})`
|
|
23696
|
-
);
|
|
23870
|
+
const notes = await searchMemoryNotes({ query, folder, tags, limit });
|
|
23871
|
+
if (notes.length === 0) {
|
|
23872
|
+
return `No memory notes matched "${query}".`;
|
|
23697
23873
|
}
|
|
23698
|
-
|
|
23699
|
-
|
|
23700
|
-
|
|
23701
|
-
summary
|
|
23702
|
-
);
|
|
23703
|
-
return folder ? composeFolderAwareResponse$1(`Renamed folder to "${folder.name}"`) : `Folder ${folder_id} not found`;
|
|
23874
|
+
return notes.map(
|
|
23875
|
+
(note) => `- ${note.title} | path=${note.relativePath} | modified=${note.modifiedAt}${note.tags.length ? ` | tags=${note.tags.join(",")}` : ""}`
|
|
23876
|
+
).join("\n");
|
|
23704
23877
|
}
|
|
23705
23878
|
);
|
|
23706
23879
|
}
|
|
23707
23880
|
);
|
|
23708
23881
|
server.registerTool(
|
|
23709
|
-
"
|
|
23882
|
+
"memory_page_capture",
|
|
23710
23883
|
{
|
|
23711
|
-
title: "
|
|
23712
|
-
description: "
|
|
23884
|
+
title: "Capture Page To Memory",
|
|
23885
|
+
description: "Capture the current page into the configured Obsidian vault as a markdown note with URL, excerpt, and content snapshot.",
|
|
23713
23886
|
inputSchema: {
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
|
|
23719
|
-
"Optional rationale or breadcrumb to store with the bookmark"
|
|
23720
|
-
),
|
|
23721
|
-
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags when creating a new note")
|
|
23887
|
+
title: zod.z.string().optional().describe("Optional note title override"),
|
|
23888
|
+
folder: zod.z.string().optional().describe("Relative folder inside the vault (default: Vessel/Pages)"),
|
|
23889
|
+
summary: zod.z.string().optional().describe("Optional summary written into the note"),
|
|
23890
|
+
note: zod.z.string().optional().describe("Optional research note or breadcrumb"),
|
|
23891
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags to store in frontmatter")
|
|
23722
23892
|
}
|
|
23723
23893
|
},
|
|
23724
|
-
async ({
|
|
23894
|
+
async ({ title, folder, summary, note, tags }) => {
|
|
23895
|
+
const tab = tabManager.getActiveTab();
|
|
23896
|
+
if (!tab) return asNoActiveTabResponse();
|
|
23725
23897
|
return withAction(
|
|
23726
23898
|
runtime2,
|
|
23727
23899
|
tabManager,
|
|
23728
|
-
"
|
|
23729
|
-
{
|
|
23900
|
+
"memory_page_capture",
|
|
23901
|
+
{ title, folder, tags },
|
|
23730
23902
|
async () => {
|
|
23731
|
-
const
|
|
23732
|
-
|
|
23733
|
-
|
|
23734
|
-
}
|
|
23735
|
-
const saved = linkBookmarkToMemory({
|
|
23736
|
-
bookmark,
|
|
23737
|
-
notePath: note_path,
|
|
23903
|
+
const page = await extractContent$1(tab.view.webContents);
|
|
23904
|
+
const saved = await capturePageToVault({
|
|
23905
|
+
page,
|
|
23738
23906
|
title,
|
|
23739
23907
|
folder,
|
|
23908
|
+
summary,
|
|
23740
23909
|
note,
|
|
23741
23910
|
tags
|
|
23742
23911
|
});
|
|
23743
|
-
return `
|
|
23912
|
+
return `Captured page "${saved.title}" to ${saved.relativePath}`;
|
|
23744
23913
|
}
|
|
23745
23914
|
);
|
|
23746
23915
|
}
|
|
@@ -25805,155 +25974,7 @@ ${JSON.stringify(otherHighlights, null, 2)}`
|
|
|
25805
25974
|
);
|
|
25806
25975
|
registerBookmarkTools(server, tabManager, runtime2);
|
|
25807
25976
|
registerSessionTools(server, tabManager, runtime2);
|
|
25808
|
-
server
|
|
25809
|
-
"memory_note_create",
|
|
25810
|
-
{
|
|
25811
|
-
title: "Create Memory Note",
|
|
25812
|
-
description: "Write a markdown note into the configured Obsidian vault for research notes, breadcrumbs, or synthesis.",
|
|
25813
|
-
inputSchema: {
|
|
25814
|
-
title: zod.z.string().describe("Title of the note"),
|
|
25815
|
-
body: zod.z.string().describe("Markdown body for the note"),
|
|
25816
|
-
folder: zod.z.string().optional().describe(
|
|
25817
|
-
"Relative folder inside the vault (default: Vessel/Research)"
|
|
25818
|
-
),
|
|
25819
|
-
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags to store in frontmatter")
|
|
25820
|
-
}
|
|
25821
|
-
},
|
|
25822
|
-
async ({ title, body, folder, tags }) => {
|
|
25823
|
-
return withAction(
|
|
25824
|
-
runtime2,
|
|
25825
|
-
tabManager,
|
|
25826
|
-
"memory_note_create",
|
|
25827
|
-
{ title, folder, tags },
|
|
25828
|
-
async () => {
|
|
25829
|
-
const saved = writeMemoryNote({ title, body, folder, tags });
|
|
25830
|
-
return `Saved memory note "${saved.title}" to ${saved.relativePath}`;
|
|
25831
|
-
}
|
|
25832
|
-
);
|
|
25833
|
-
}
|
|
25834
|
-
);
|
|
25835
|
-
server.registerTool(
|
|
25836
|
-
"memory_append",
|
|
25837
|
-
{
|
|
25838
|
-
title: "Append Memory Note",
|
|
25839
|
-
description: "Append markdown content to an existing note in the configured Obsidian vault.",
|
|
25840
|
-
inputSchema: {
|
|
25841
|
-
note_path: zod.z.string().describe("Relative path to an existing note inside the vault"),
|
|
25842
|
-
content: zod.z.string().describe("Markdown content to append"),
|
|
25843
|
-
heading: zod.z.string().optional().describe("Optional section heading to add before the content")
|
|
25844
|
-
}
|
|
25845
|
-
},
|
|
25846
|
-
async ({ note_path, content, heading }) => {
|
|
25847
|
-
return withAction(
|
|
25848
|
-
runtime2,
|
|
25849
|
-
tabManager,
|
|
25850
|
-
"memory_note_append",
|
|
25851
|
-
{ note_path, heading },
|
|
25852
|
-
async () => {
|
|
25853
|
-
const saved = appendToMemoryNote({
|
|
25854
|
-
notePath: note_path,
|
|
25855
|
-
content,
|
|
25856
|
-
heading
|
|
25857
|
-
});
|
|
25858
|
-
return `Appended memory note at ${saved.relativePath}`;
|
|
25859
|
-
}
|
|
25860
|
-
);
|
|
25861
|
-
}
|
|
25862
|
-
);
|
|
25863
|
-
server.registerTool(
|
|
25864
|
-
"memory_list",
|
|
25865
|
-
{
|
|
25866
|
-
title: "List Memory Notes",
|
|
25867
|
-
description: "List recent markdown notes in the configured Obsidian vault.",
|
|
25868
|
-
inputSchema: {
|
|
25869
|
-
folder: zod.z.string().optional().describe("Optional relative folder inside the vault"),
|
|
25870
|
-
limit: zod.z.number().int().positive().max(200).optional().describe("Maximum number of notes to return")
|
|
25871
|
-
}
|
|
25872
|
-
},
|
|
25873
|
-
async ({ folder, limit }) => {
|
|
25874
|
-
return withAction(
|
|
25875
|
-
runtime2,
|
|
25876
|
-
tabManager,
|
|
25877
|
-
"memory_note_list",
|
|
25878
|
-
{ folder, limit },
|
|
25879
|
-
async () => {
|
|
25880
|
-
const notes = listMemoryNotes({ folder, limit });
|
|
25881
|
-
if (notes.length === 0) {
|
|
25882
|
-
return "No memory notes found.";
|
|
25883
|
-
}
|
|
25884
|
-
return notes.map(
|
|
25885
|
-
(note) => `- ${note.title} | path=${note.relativePath} | modified=${note.modifiedAt}${note.tags.length ? ` | tags=${note.tags.join(",")}` : ""}`
|
|
25886
|
-
).join("\n");
|
|
25887
|
-
}
|
|
25888
|
-
);
|
|
25889
|
-
}
|
|
25890
|
-
);
|
|
25891
|
-
server.registerTool(
|
|
25892
|
-
"memory_search",
|
|
25893
|
-
{
|
|
25894
|
-
title: "Search Memory Notes",
|
|
25895
|
-
description: "Search markdown notes in the configured Obsidian vault by title, path, body, and optional tags.",
|
|
25896
|
-
inputSchema: {
|
|
25897
|
-
query: zod.z.string().describe("Search query"),
|
|
25898
|
-
folder: zod.z.string().optional().describe("Optional relative folder inside the vault"),
|
|
25899
|
-
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags that matching notes must contain"),
|
|
25900
|
-
limit: zod.z.number().int().positive().max(100).optional().describe("Maximum number of matching notes to return")
|
|
25901
|
-
}
|
|
25902
|
-
},
|
|
25903
|
-
async ({ query, folder, tags, limit }) => {
|
|
25904
|
-
return withAction(
|
|
25905
|
-
runtime2,
|
|
25906
|
-
tabManager,
|
|
25907
|
-
"memory_note_search",
|
|
25908
|
-
{ query, folder, tags, limit },
|
|
25909
|
-
async () => {
|
|
25910
|
-
const notes = searchMemoryNotes({ query, folder, tags, limit });
|
|
25911
|
-
if (notes.length === 0) {
|
|
25912
|
-
return `No memory notes matched "${query}".`;
|
|
25913
|
-
}
|
|
25914
|
-
return notes.map(
|
|
25915
|
-
(note) => `- ${note.title} | path=${note.relativePath} | modified=${note.modifiedAt}${note.tags.length ? ` | tags=${note.tags.join(",")}` : ""}`
|
|
25916
|
-
).join("\n");
|
|
25917
|
-
}
|
|
25918
|
-
);
|
|
25919
|
-
}
|
|
25920
|
-
);
|
|
25921
|
-
server.registerTool(
|
|
25922
|
-
"memory_page_capture",
|
|
25923
|
-
{
|
|
25924
|
-
title: "Capture Page To Memory",
|
|
25925
|
-
description: "Capture the current page into the configured Obsidian vault as a markdown note with URL, excerpt, and content snapshot.",
|
|
25926
|
-
inputSchema: {
|
|
25927
|
-
title: zod.z.string().optional().describe("Optional note title override"),
|
|
25928
|
-
folder: zod.z.string().optional().describe("Relative folder inside the vault (default: Vessel/Pages)"),
|
|
25929
|
-
summary: zod.z.string().optional().describe("Optional summary written into the note"),
|
|
25930
|
-
note: zod.z.string().optional().describe("Optional research note or breadcrumb"),
|
|
25931
|
-
tags: zod.z.array(zod.z.string()).optional().describe("Optional tags to store in frontmatter")
|
|
25932
|
-
}
|
|
25933
|
-
},
|
|
25934
|
-
async ({ title, folder, summary, note, tags }) => {
|
|
25935
|
-
const tab = tabManager.getActiveTab();
|
|
25936
|
-
if (!tab) return asNoActiveTabResponse();
|
|
25937
|
-
return withAction(
|
|
25938
|
-
runtime2,
|
|
25939
|
-
tabManager,
|
|
25940
|
-
"memory_page_capture",
|
|
25941
|
-
{ title, folder, tags },
|
|
25942
|
-
async () => {
|
|
25943
|
-
const page = await extractContent$1(tab.view.webContents);
|
|
25944
|
-
const saved = capturePageToVault({
|
|
25945
|
-
page,
|
|
25946
|
-
title,
|
|
25947
|
-
folder,
|
|
25948
|
-
summary,
|
|
25949
|
-
note,
|
|
25950
|
-
tags
|
|
25951
|
-
});
|
|
25952
|
-
return `Captured page "${saved.title}" to ${saved.relativePath}`;
|
|
25953
|
-
}
|
|
25954
|
-
);
|
|
25955
|
-
}
|
|
25956
|
-
);
|
|
25977
|
+
registerMemoryTools(server, tabManager, runtime2);
|
|
25957
25978
|
server.registerTool(
|
|
25958
25979
|
"flow_start",
|
|
25959
25980
|
{
|
|
@@ -27632,15 +27653,21 @@ function isSafeAutomationKitId(id) {
|
|
|
27632
27653
|
return id.length > 0 && !KIT_ID_UNSAFE_CHAR_PATTERN.test(id);
|
|
27633
27654
|
}
|
|
27634
27655
|
const logger$9 = createLogger("KitRegistry");
|
|
27656
|
+
const { access, mkdir, readFile, readdir, unlink, writeFile } = fs$1.promises;
|
|
27635
27657
|
function getUserKitsDir() {
|
|
27636
27658
|
return path$1.join(electron.app.getPath("userData"), "kits");
|
|
27637
27659
|
}
|
|
27638
|
-
function
|
|
27639
|
-
|
|
27640
|
-
|
|
27641
|
-
|
|
27660
|
+
async function pathExists(filePath2) {
|
|
27661
|
+
try {
|
|
27662
|
+
await access(filePath2);
|
|
27663
|
+
return true;
|
|
27664
|
+
} catch {
|
|
27665
|
+
return false;
|
|
27642
27666
|
}
|
|
27643
27667
|
}
|
|
27668
|
+
async function ensureKitsDir() {
|
|
27669
|
+
await mkdir(getUserKitsDir(), { recursive: true });
|
|
27670
|
+
}
|
|
27644
27671
|
function getKitFilePath(id) {
|
|
27645
27672
|
if (!isSafeAutomationKitId(id)) return null;
|
|
27646
27673
|
const kitsDir = path$1.resolve(getUserKitsDir());
|
|
@@ -27652,12 +27679,12 @@ function isValidKit(value) {
|
|
|
27652
27679
|
const k = value;
|
|
27653
27680
|
return typeof k.id === "string" && isSafeAutomationKitId(k.id) && typeof k.name === "string" && k.name.length > 0 && typeof k.description === "string" && typeof k.category === "string" && VALID_KIT_CATEGORIES.has(k.category) && typeof k.icon === "string" && typeof k.promptTemplate === "string" && k.promptTemplate.length > 0 && Array.isArray(k.inputs);
|
|
27654
27681
|
}
|
|
27655
|
-
function getInstalledKits() {
|
|
27656
|
-
ensureKitsDir();
|
|
27682
|
+
async function getInstalledKits() {
|
|
27683
|
+
await ensureKitsDir();
|
|
27657
27684
|
const dir = getUserKitsDir();
|
|
27658
27685
|
let files;
|
|
27659
27686
|
try {
|
|
27660
|
-
files =
|
|
27687
|
+
files = (await readdir(dir)).filter((f) => f.endsWith(".kit.json"));
|
|
27661
27688
|
} catch (err) {
|
|
27662
27689
|
logger$9.warn("Failed to read kit directory:", err);
|
|
27663
27690
|
return [];
|
|
@@ -27665,7 +27692,7 @@ function getInstalledKits() {
|
|
|
27665
27692
|
const kits = [];
|
|
27666
27693
|
for (const file of files) {
|
|
27667
27694
|
try {
|
|
27668
|
-
const raw =
|
|
27695
|
+
const raw = await readFile(path$1.join(dir, file), "utf-8");
|
|
27669
27696
|
const parsed = JSON.parse(raw);
|
|
27670
27697
|
if (isValidKit(parsed)) {
|
|
27671
27698
|
kits.push(parsed);
|
|
@@ -27689,7 +27716,7 @@ async function installKitFromFile() {
|
|
|
27689
27716
|
}
|
|
27690
27717
|
let raw;
|
|
27691
27718
|
try {
|
|
27692
|
-
raw =
|
|
27719
|
+
raw = await readFile(filePaths[0], "utf-8");
|
|
27693
27720
|
} catch (err) {
|
|
27694
27721
|
logger$9.warn("Failed to read selected kit file:", err);
|
|
27695
27722
|
return errorResult("Could not read the selected file.");
|
|
@@ -27711,20 +27738,20 @@ async function installKitFromFile() {
|
|
|
27711
27738
|
`Kit id "${parsed.id}" conflicts with a built-in kit and cannot be overwritten.`
|
|
27712
27739
|
);
|
|
27713
27740
|
}
|
|
27714
|
-
ensureKitsDir();
|
|
27741
|
+
await ensureKitsDir();
|
|
27715
27742
|
const dest = getKitFilePath(parsed.id);
|
|
27716
27743
|
if (!dest) {
|
|
27717
27744
|
return errorResult("Kit id contains unsupported characters.");
|
|
27718
27745
|
}
|
|
27719
27746
|
try {
|
|
27720
|
-
|
|
27747
|
+
await writeFile(dest, JSON.stringify(parsed, null, 2), "utf-8");
|
|
27721
27748
|
} catch (err) {
|
|
27722
27749
|
logger$9.warn("Failed to save kit file:", err);
|
|
27723
27750
|
return errorResult("Failed to save the kit file.");
|
|
27724
27751
|
}
|
|
27725
27752
|
return okResult({ kit: parsed });
|
|
27726
27753
|
}
|
|
27727
|
-
function uninstallKit(id, scheduledKitIds) {
|
|
27754
|
+
async function uninstallKit(id, scheduledKitIds) {
|
|
27728
27755
|
if (BUNDLED_KIT_IDS.has(id)) {
|
|
27729
27756
|
return errorResult("Built-in kits cannot be removed.");
|
|
27730
27757
|
}
|
|
@@ -27733,16 +27760,16 @@ function uninstallKit(id, scheduledKitIds) {
|
|
|
27733
27760
|
"This kit has active scheduled jobs. Delete or reassign them first."
|
|
27734
27761
|
);
|
|
27735
27762
|
}
|
|
27736
|
-
ensureKitsDir();
|
|
27763
|
+
await ensureKitsDir();
|
|
27737
27764
|
const target = getKitFilePath(id);
|
|
27738
27765
|
if (!target) {
|
|
27739
27766
|
return errorResult("Kit id contains unsupported characters.");
|
|
27740
27767
|
}
|
|
27741
|
-
if (!
|
|
27768
|
+
if (!await pathExists(target)) {
|
|
27742
27769
|
return errorResult("Kit not found.");
|
|
27743
27770
|
}
|
|
27744
27771
|
try {
|
|
27745
|
-
|
|
27772
|
+
await unlink(target);
|
|
27746
27773
|
return okResult();
|
|
27747
27774
|
} catch (err) {
|
|
27748
27775
|
logger$9.warn("Failed to remove kit file:", err);
|
|
@@ -28078,17 +28105,20 @@ function registerSystemHandlers(windowState2, sendToRendererViews) {
|
|
|
28078
28105
|
layoutViews(windowState2);
|
|
28079
28106
|
return clamped;
|
|
28080
28107
|
});
|
|
28081
|
-
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, (event) => {
|
|
28108
|
+
electron.ipcMain.handle(Channels.AUTOMATION_GET_INSTALLED, async (event) => {
|
|
28082
28109
|
assertTrustedIpcSender(event);
|
|
28083
|
-
return getInstalledKits();
|
|
28110
|
+
return await getInstalledKits();
|
|
28084
28111
|
});
|
|
28085
28112
|
electron.ipcMain.handle(Channels.AUTOMATION_INSTALL_FROM_FILE, async (event) => {
|
|
28086
28113
|
assertTrustedIpcSender(event);
|
|
28087
28114
|
return await installKitFromFile();
|
|
28088
28115
|
});
|
|
28089
|
-
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, (event, id) => {
|
|
28116
|
+
electron.ipcMain.handle(Channels.AUTOMATION_UNINSTALL, async (event, id) => {
|
|
28090
28117
|
assertTrustedIpcSender(event);
|
|
28091
|
-
return uninstallKit(
|
|
28118
|
+
return await uninstallKit(
|
|
28119
|
+
parseIpc(KitIdSchema, id, "id"),
|
|
28120
|
+
getScheduledKitIds()
|
|
28121
|
+
);
|
|
28092
28122
|
});
|
|
28093
28123
|
electron.ipcMain.handle(Channels.CLEAR_BROWSING_DATA, async (event, options) => {
|
|
28094
28124
|
assertTrustedIpcSender(event);
|