kennel 0.2.44 → 0.2.46
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/core/api-client.d.ts +1 -1
- package/dist/core/api-client.js +69 -3
- package/dist/core/runtime.js +649 -140
- package/dist/llm/worker.js +11 -2
- package/dist/notes/command.js +20 -4
- package/dist/package/preview.d.ts +1 -1
- package/dist/package/preview.js +881 -540
- package/dist/site/hot-viewer.js +4 -4
- package/dist/site/server-plugins.js +468 -30
- package/dist/site/viewer.d.ts +0 -3
- package/dist/site/viewer.js +11 -41
- package/dist/tui/launcher.d.ts +11 -0
- package/dist/{site/terminal.js → tui/launcher.js} +60 -15
- package/package.json +2 -2
- package/web/assets/{abnfDiagram-VRR7QNED-ooA_TyhC.js → abnfDiagram-VRR7QNED-BOm3iBSE.js} +1 -1
- package/web/assets/{arc-DqQkY48J.js → arc-D_zfnjJW.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-DaXzRGP0.js → architectureDiagram-ZJ3FMSHR-CTXAbDtC.js} +1 -1
- package/web/assets/ask-tray-CT6DQRTC-DfTc3bnC.js +1 -0
- package/web/assets/{blockDiagram-677ZJIJ3-Br5Y_dbP.js → blockDiagram-677ZJIJ3-CcPWOAKO.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CWIy0S0N.js → c4Diagram-LMCZKHZV-B_eNO2-f.js} +1 -1
- package/web/assets/channel-Dlb_u_Yk.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-8dxcuNwr.js → chunk-2Q5K7J3B-BtQX_f5i.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-DB27hz0Y.js → chunk-32BRIVSS-BBtfsm8_.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-CTNxGtbA.js → chunk-5VM5RSS4-CG0bf9ML.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-oKNLktOC.js → chunk-EX3LRPZG-DS2H7ilP.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-DeQNmWoS.js → chunk-JWPE2WC7-CGLMMKkr.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-BrW33Mrb.js → chunk-MOJQB5TN-C76lqDkU.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-DEkTU6j-.js → chunk-RYQCIY6F-ZY-EzC5l.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-BuDLgwdB.js → chunk-V7JOEXUC-sBv90hIh.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BsNgrrKe.js → chunk-VR4S4FIN-jW9aFTp3.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DjT1Bydl.js → chunk-XXDRQBXY-FPtE6zce.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-CinFzRGQ.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-CinFzRGQ.js +1 -0
- package/web/assets/client-5Q4VI7ZT-DYH5SwVp.js +52 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BtAklxrd.js → cose-bilkent-JH36ORCC-NdJGLzeJ.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DabtZWZy.js → cynefin-VYW2F7L2-D-o_obcW.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DVk8uQ8L.js → cynefinDiagram-TSTJHNR4-RA4QYrtB.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-CiAbBCor.js → dagre-VKFMJZFB-Bq4DfO16.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-DWp-OzJz.js → diagram-FQU43EPY-bPnACaba.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-YDllS_1P.js → diagram-G47NLZAW-cPca5hvp.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BH3l4yRj.js → diagram-NH7WQ7WH-DHyP34H8.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-CszBvxdr.js → diagram-OA4YK3LP-hVk7mHu1.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-bEhZeER6.js → diagram-WEI45ONY-Cd3AEJO3.js} +1 -1
- package/web/assets/document-view-VX2ZTQM5-CDdtofzz.js +24 -0
- package/web/assets/{ebnfDiagram-CCIWWBDH-DlpoUUO5.js → ebnfDiagram-CCIWWBDH-BxtDx2MR.js} +1 -1
- package/web/assets/{entity-search-page-YP4SPMBC-WryWZNPk.js → entity-search-page-RIYQYIJJ-IvZzjXvt.js} +5 -5
- package/web/assets/{erDiagram-Q63AITRT-DROgehIM.js → erDiagram-Q63AITRT-CtdOkrpo.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-63pYiBzl.js → flowDiagram-23GEKE2U-BDHfaJo6.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-tc34wKHJ.js → ganttDiagram-NO4QXBWP-BhV7JFJA.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-GOjqt583.js → gitGraphDiagram-IHSO6WYX-BVRBRDVk.js} +1 -1
- package/web/assets/index-BxCXNDe6.css +1 -0
- package/web/assets/index-Cl-kcrk3.js +502 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-CD5BwkLQ.js → infoDiagram-FWYZ7A6U-BKuFQe6t.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-yIweY6wR.js → ishikawaDiagram-FXEZZL3T-lNbhlHEo.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-D3MeUGvn.js → journeyDiagram-5HDEW3XC-CoGf_pSm.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-CnZ5N79y.js → kanban-definition-HUTT4EX6-Cgp0WGrq.js} +1 -1
- package/web/assets/{linear-s5G6iOMz.js → linear-B3UMk7TK.js} +1 -1
- package/web/assets/{linked-session-header-DOEX4FYM-CtTOXcZn.js → linked-session-header-7BUTWROP-Coqn-Dz0.js} +1 -1
- package/web/assets/{mermaid.core-BpUKp9r_.js → mermaid.core-6bZ5bkAq.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-Bp2QH5sN.js → mindmap-definition-LN4V7U3C-BowQNgIG.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-lx1jDUmt.js → pegDiagram-2B236MQR-DrhWFvfI.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P--kf2YNbw.js → pieDiagram-ENE6RG2P-DNOg53mu.js} +1 -1
- package/web/assets/{plugin-host-modules-BKbxlu7U.js → plugin-host-modules-NvyHieUX.js} +26 -26
- package/web/assets/{project-search-page-TZG7AZBQ-3W40ayYn.js → project-search-page-O6YRLDGU-CFA4lf0c.js} +2 -2
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Vr7zWQYV.js → quadrantDiagram-ABIIQ3AL-BOJ4UGbk.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-CqWD8KUC.js → railroadDiagram-RFXS5EU6-_UJ9_AEC.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-BHUBdOVh.js → requirementDiagram-TGXJPOKE-BFa61FSa.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-Cw_ZddWI.js → sankeyDiagram-HTMAVEWB-V1J9w6jl.js} +1 -1
- package/web/assets/{selection-popup-ABYH3A27-Gc-RFSWj.js → selection-popup-ZX4VUZ3A-Duh9LSP2.js} +4 -4
- package/web/assets/{sequenceDiagram-DBY2YBRQ-CyCbkAfM.js → sequenceDiagram-DBY2YBRQ-xdS7JpTx.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-BG6RM8rb.js → sizeCapture-X5ZJPWSS-DBULLaws.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-DKpZl5UU.js → stateDiagram-2N3HPSRC-DfBpQAQb.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-DHUy-0iS.js → stateDiagram-v2-6OUMAXLB-C-dC_PIX.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-D14uyHMH.js → swimlanes-5IMT3BWC-L-RTYuhP.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-BErklojq.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-f4WPhEXS.js → timeline-definition-FHXFAJF6-CAEbvdzk.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-lr4A8guw.js → vennDiagram-L72KCM5P-J10bFP8c.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-CR-Im585.js → wardleyDiagram-EHGQE667-Bm0goK-9.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DeNQfP7k.js → xychartDiagram-FW5EYKEG-BAH4TTVb.js} +1 -1
- package/web/index.html +2 -2
- package/dist/site/terminal.d.ts +0 -14
- package/web/assets/ask-tray-KUTWZ4MV-Ckn1y1Zw.js +0 -24
- package/web/assets/channel-BQxXBpeY.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-BT1TSwZs.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-BT1TSwZs.js +0 -1
- package/web/assets/index-Di6pguZz.css +0 -1
- package/web/assets/index-qmu99cjp.js +0 -493
- package/web/assets/swimlanesDiagram-G3AALYLV-C5riej52.js +0 -8
package/dist/package/preview.js
CHANGED
|
@@ -210,10 +210,119 @@ var init_document_kinds = __esm(() => {
|
|
|
210
210
|
};
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
-
// packages/
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
// packages/plugins/src/client.ts
|
|
214
|
+
function folderPathId(path) {
|
|
215
|
+
const bytes = new TextEncoder().encode(path);
|
|
216
|
+
let binary = "";
|
|
217
|
+
for (const byte of bytes)
|
|
218
|
+
binary += String.fromCharCode(byte);
|
|
219
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
220
|
+
}
|
|
221
|
+
var init_client = () => {};
|
|
222
|
+
|
|
223
|
+
// packages/api/src/shared/viewer-route.ts
|
|
224
|
+
function decodeBase64Url(value) {
|
|
225
|
+
const padded = `${value}${"=".repeat((4 - value.length % 4) % 4)}`;
|
|
226
|
+
const binary = atob(padded.replaceAll("-", "+").replaceAll("_", "/"));
|
|
227
|
+
return new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
|
|
228
|
+
}
|
|
229
|
+
function url(value) {
|
|
230
|
+
if (/^https?:\/\//.test(value))
|
|
231
|
+
return new URL(value);
|
|
232
|
+
if (value.startsWith("?"))
|
|
233
|
+
return new URL(`/${value}`, "http://127.0.0.1");
|
|
234
|
+
return new URL(value || "/", "http://127.0.0.1");
|
|
235
|
+
}
|
|
236
|
+
function tabSection(tab) {
|
|
237
|
+
if (tab === "files")
|
|
238
|
+
return "notes";
|
|
239
|
+
if (tab === "projects" || tab === "sessions")
|
|
240
|
+
return tab;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
function folderPathId2(path) {
|
|
244
|
+
return folderPathId(path);
|
|
245
|
+
}
|
|
246
|
+
function decodeFolderPathId(id) {
|
|
247
|
+
return decodeBase64Url(id);
|
|
248
|
+
}
|
|
249
|
+
function folderHomeHref(folder) {
|
|
250
|
+
return `/folder/${folderPathId2(folder)}`;
|
|
251
|
+
}
|
|
252
|
+
function folderPluginHref(folder, pluginId, subpath = "") {
|
|
253
|
+
const base = `/folder/${folderPathId2(folder)}/views/${encodeURIComponent(pluginId)}`;
|
|
254
|
+
return subpath ? `${base}/${encodeURIComponent(subpath)}` : base;
|
|
255
|
+
}
|
|
256
|
+
function folderUrl(base, folder, options = {}) {
|
|
257
|
+
const current = new URL(base);
|
|
258
|
+
current.pathname = options.view ? folderPluginHref(folder, options.view.pluginId, options.view.subpath) : options.document ? folderDocumentHref(folder, options.document) : folderHomeHref(folder);
|
|
259
|
+
current.searchParams.delete("folder");
|
|
260
|
+
if (options.document && options.watch && !options.view) {
|
|
261
|
+
current.searchParams.set("watch", "1");
|
|
262
|
+
}
|
|
263
|
+
return current;
|
|
264
|
+
}
|
|
265
|
+
function folderDocumentHref(folder, document2) {
|
|
266
|
+
return `/folder/${folderPathId2(folder)}/document/${folderPathId2(document2)}`;
|
|
267
|
+
}
|
|
268
|
+
function parseViewerRoute(value) {
|
|
269
|
+
const current = url(value);
|
|
270
|
+
const session = current.searchParams.get("session") ?? undefined;
|
|
271
|
+
const folder = current.searchParams.get("folder");
|
|
272
|
+
if (folder) {
|
|
273
|
+
return {
|
|
274
|
+
document: current.searchParams.get("document") ?? current.searchParams.get("file") ?? undefined,
|
|
275
|
+
embedded: current.searchParams.get("embedded") === "1",
|
|
276
|
+
folder,
|
|
277
|
+
panel: current.searchParams.get("panel") === "1",
|
|
278
|
+
section: tabSection(current.searchParams.get("tab")) ?? "notes",
|
|
279
|
+
...session ? { session } : {}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const parts = current.pathname.split("/").filter(Boolean);
|
|
283
|
+
if (parts[0] !== "folder" || !parts[1]) {
|
|
284
|
+
return {
|
|
285
|
+
document: current.searchParams.get("file") ?? current.searchParams.get("document") ?? undefined,
|
|
286
|
+
embedded: current.searchParams.get("embedded") === "1",
|
|
287
|
+
folder: undefined,
|
|
288
|
+
panel: current.searchParams.get("panel") === "1",
|
|
289
|
+
section: tabSection(current.searchParams.get("tab")),
|
|
290
|
+
...session ? { session } : {}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const section = parts[2];
|
|
294
|
+
const route = {
|
|
295
|
+
embedded: current.searchParams.get("embedded") === "1",
|
|
296
|
+
folder: decodeFolderPathId(parts[1]),
|
|
297
|
+
panel: current.searchParams.get("panel") === "1",
|
|
298
|
+
...session ? { session } : {}
|
|
299
|
+
};
|
|
300
|
+
if (section === "collections" && parts[3]) {
|
|
301
|
+
return { ...route, collectionId: decodeFolderPathId(parts[3]) };
|
|
302
|
+
}
|
|
303
|
+
if (section === "views" && parts[3]) {
|
|
304
|
+
return {
|
|
305
|
+
...route,
|
|
306
|
+
view: {
|
|
307
|
+
pluginId: decodeURIComponent(parts[3]),
|
|
308
|
+
...parts[4] ? { subpath: parts[4] } : {}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (section === "notes" || section === "projects" || section === "sessions") {
|
|
313
|
+
return { ...route, section };
|
|
314
|
+
}
|
|
315
|
+
if (section === "document" && parts[3]) {
|
|
316
|
+
return { ...route, document: decodeFolderPathId(parts[3]) };
|
|
317
|
+
}
|
|
318
|
+
return route;
|
|
319
|
+
}
|
|
320
|
+
var init_viewer_route = __esm(() => {
|
|
321
|
+
init_client();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// packages/preview/src/runtime.ts
|
|
325
|
+
import { execFile } from "node:child_process";
|
|
217
326
|
function openPreview(url) {
|
|
218
327
|
const command = process.platform === "darwin" ? "/usr/bin/open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
219
328
|
execFile(command, [url], (error) => {
|
|
@@ -221,212 +330,21 @@ function openPreview(url) {
|
|
|
221
330
|
console.error(`Could not open browser: ${error.message}`);
|
|
222
331
|
});
|
|
223
332
|
}
|
|
224
|
-
function
|
|
225
|
-
const command = process.platform === "darwin" ? "/usr/bin/pbcopy" : process.platform === "win32" ? "clip" : process.env.WAYLAND_DISPLAY ? "wl-copy" : "xclip";
|
|
226
|
-
const args = command === "xclip" ? ["-selection", "clipboard"] : [];
|
|
227
|
-
return new Promise((done, fail) => {
|
|
228
|
-
const child = execFile(command, args, (error) => error ? fail(error) : done());
|
|
229
|
-
child.stdin?.on("error", fail);
|
|
230
|
-
child.stdin?.end(url);
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
function repositoryName(folder) {
|
|
234
|
-
try {
|
|
235
|
-
const root = execFileSync("git", ["-C", folder, "rev-parse", "--show-toplevel"], {
|
|
236
|
-
encoding: "utf8",
|
|
237
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
238
|
-
timeout: 1000
|
|
239
|
-
});
|
|
240
|
-
return terminalText(basename(root.trim()));
|
|
241
|
-
} catch {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
function previewTerminal(url, target, startShare, views) {
|
|
246
|
-
url = new URL(url).href;
|
|
247
|
-
const input = process.stdin;
|
|
248
|
-
const output = process.stdout;
|
|
249
|
-
const terminal = Boolean(output.isTTY && process.env.TERM !== "dumb");
|
|
250
|
-
const interactive = terminal && Boolean(input.isTTY);
|
|
251
|
-
const name = terminalText(basename(target.target ?? target.root) || target.root);
|
|
252
|
-
const title = terminalText(basename(target.root) || target.root);
|
|
253
|
-
const repository = interactive ? repositoryName(target.root) : undefined;
|
|
254
|
-
const color = terminal && process.env.NO_COLOR === undefined && output.hasColors();
|
|
255
|
-
const accent = (text) => color ? `\x1B[32m${text}\x1B[39m` : text;
|
|
256
|
-
const linkTo = (label, href, plain = `${label}
|
|
257
|
-
${href}`) => terminal ? `\x1B]8;;${href}\x07${accent(label)}\x1B]8;;\x07` : plain;
|
|
258
|
-
const link = (label) => linkTo(label, url);
|
|
259
|
-
const wasRaw = Boolean(input.isRaw);
|
|
260
|
-
const wasFlowing = input.readableFlowing === true;
|
|
333
|
+
function viewerLifetime() {
|
|
261
334
|
let resolveClosed;
|
|
262
335
|
const closed = new Promise((resolve) => {
|
|
263
336
|
resolveClosed = resolve;
|
|
264
337
|
});
|
|
265
|
-
let finished = false;
|
|
266
|
-
let busy = false;
|
|
267
|
-
let selected = 0;
|
|
268
|
-
let status = "";
|
|
269
|
-
let share;
|
|
270
|
-
const render = () => {
|
|
271
|
-
if (!interactive || finished)
|
|
272
|
-
return;
|
|
273
|
-
cursorTo(output, 0, 0);
|
|
274
|
-
clearScreenDown(output);
|
|
275
|
-
const width = Math.max(1, (output.columns || 80) - 1);
|
|
276
|
-
const fit = (text) => {
|
|
277
|
-
const characters = Array.from(terminalText(text));
|
|
278
|
-
return characters.length > width ? characters.slice(0, width - 1).join("") + "…" : characters.join("");
|
|
279
|
-
};
|
|
280
|
-
const choices = [];
|
|
281
|
-
actions.forEach((item, index) => {
|
|
282
|
-
const label = fit(item.shortcut ? `[${item.shortcut}] ${item.label}` : item.label);
|
|
283
|
-
let text = index === selected ? accent(`› ${label}`) : ` ${label}`;
|
|
284
|
-
if (item.view !== undefined)
|
|
285
|
-
text = linkTo(text, item.view, accent(text));
|
|
286
|
-
choices.push(text);
|
|
287
|
-
});
|
|
288
|
-
output.write([
|
|
289
|
-
`\x1B[1m${link(fit(title))}\x1B[22m`,
|
|
290
|
-
...repository ? [`${color ? "\x1B[2m" : ""}${fit(repository)}${color ? "\x1B[22m" : ""}`] : [],
|
|
291
|
-
...target.target ? [`File: ${fit(name)}`] : [],
|
|
292
|
-
"",
|
|
293
|
-
...choices,
|
|
294
|
-
"",
|
|
295
|
-
"↑ ↓ to move · Enter to choose · Esc or Ctrl+C to exit",
|
|
296
|
-
...status ? ["", status] : [],
|
|
297
|
-
""
|
|
298
|
-
].join(`
|
|
299
|
-
`));
|
|
300
|
-
};
|
|
301
|
-
const shareFolder = async (password = false) => {
|
|
302
|
-
status = "Preparing a private Tailscale link…";
|
|
303
|
-
render();
|
|
304
|
-
try {
|
|
305
|
-
share ??= startShare(password);
|
|
306
|
-
let active;
|
|
307
|
-
try {
|
|
308
|
-
active = await share;
|
|
309
|
-
} catch (error) {
|
|
310
|
-
share = undefined;
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
if (finished)
|
|
314
|
-
return;
|
|
315
|
-
status = active.password ? `Tailscale link: ${active.url}
|
|
316
|
-
Username: ${active.username} · Password: ${active.password}
|
|
317
|
-
Password required. Send the password separately.
|
|
318
|
-
Connect Tailscale on your phone to the same tailnet.
|
|
319
|
-
Trusted HTTPS; no certificate installation required.
|
|
320
|
-
Keep this command running. Ctrl+C stops sharing.` : `Tailscale link: ${active.url}
|
|
321
|
-
Connect Tailscale on your phone to the same tailnet.
|
|
322
|
-
Trusted HTTPS; no password required on the tailnet.
|
|
323
|
-
Keep this command running. Ctrl+C stops sharing.`;
|
|
324
|
-
render();
|
|
325
|
-
try {
|
|
326
|
-
await copyPreviewLink(active.url);
|
|
327
|
-
status += `
|
|
328
|
-
Tailscale link copied to clipboard (without the password).`;
|
|
329
|
-
} catch (error) {
|
|
330
|
-
status += `
|
|
331
|
-
Could not copy link: ${terminalText(error instanceof Error ? error.message : String(error))}`;
|
|
332
|
-
}
|
|
333
|
-
} catch (error) {
|
|
334
|
-
status = `Could not share folder: ${terminalText(error instanceof Error ? error.message : String(error))}`;
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
338
|
const close = () => {
|
|
338
|
-
if (finished)
|
|
339
|
-
return;
|
|
340
|
-
finished = true;
|
|
341
339
|
process.off("SIGINT", close);
|
|
342
340
|
process.off("SIGTERM", close);
|
|
343
|
-
|
|
344
|
-
if (interactive) {
|
|
345
|
-
input.off("keypress", keypress);
|
|
346
|
-
input.off("end", close);
|
|
347
|
-
output.off("resize", render);
|
|
348
|
-
output.write("\x1B[?25h\x1B[?1049l");
|
|
349
|
-
input.setRawMode(wasRaw);
|
|
350
|
-
if (!wasFlowing)
|
|
351
|
-
input.pause();
|
|
352
|
-
}
|
|
353
|
-
if (share) {
|
|
354
|
-
share.then((active) => active.close()).catch((error) => {
|
|
355
|
-
console.error(` Could not stop the Tailscale share: ${error instanceof Error ? error.message : error}`);
|
|
356
|
-
}).finally(() => resolveClosed());
|
|
357
|
-
} else
|
|
358
|
-
resolveClosed();
|
|
359
|
-
};
|
|
360
|
-
const actions = [
|
|
361
|
-
...(views ?? []).map((view) => ({ label: view.id, run: () => {
|
|
362
|
-
openPreview(view.url);
|
|
363
|
-
status = "Browser open requested.";
|
|
364
|
-
}, view: view.url })),
|
|
365
|
-
{ label: "Open in browser", shortcut: "o", run: () => {
|
|
366
|
-
openPreview(url);
|
|
367
|
-
status = "Browser open requested.";
|
|
368
|
-
} },
|
|
369
|
-
{ label: "Share to another device", shortcut: "s", run: () => shareFolder(false) },
|
|
370
|
-
{ label: "Share link with password", shortcut: "p", run: () => shareFolder(true) },
|
|
371
|
-
{ label: "Exit", shortcut: "q", run: close }
|
|
372
|
-
];
|
|
373
|
-
const activate = async () => {
|
|
374
|
-
busy = true;
|
|
375
|
-
try {
|
|
376
|
-
await actions[selected].run();
|
|
377
|
-
} catch (error) {
|
|
378
|
-
status = `Could not complete action: ${terminalText(error instanceof Error ? error.message : String(error))}`;
|
|
379
|
-
} finally {
|
|
380
|
-
busy = false;
|
|
381
|
-
render();
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
const keypress = (_text, key) => {
|
|
385
|
-
if (key.name === "escape" || key.ctrl && (key.name === "c" || key.name === "d")) {
|
|
386
|
-
close();
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (busy || finished)
|
|
390
|
-
return;
|
|
391
|
-
const shortcut = !key.ctrl && !key.meta ? actions.findIndex((item) => item.shortcut === key.name) : -1;
|
|
392
|
-
if (shortcut !== -1) {
|
|
393
|
-
selected = shortcut;
|
|
394
|
-
activate();
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
if (key.name === "up")
|
|
398
|
-
selected = (selected - 1 + actions.length) % actions.length;
|
|
399
|
-
else if (key.name === "down")
|
|
400
|
-
selected = (selected + 1) % actions.length;
|
|
401
|
-
else if (key.name === "return") {
|
|
402
|
-
activate();
|
|
403
|
-
return;
|
|
404
|
-
} else
|
|
405
|
-
return;
|
|
406
|
-
render();
|
|
341
|
+
resolveClosed();
|
|
407
342
|
};
|
|
408
343
|
process.once("SIGINT", close);
|
|
409
344
|
process.once("SIGTERM", close);
|
|
410
|
-
|
|
411
|
-
if (interactive) {
|
|
412
|
-
output.write("\x1B[?1049h\x1B[?25l");
|
|
413
|
-
output.on("resize", render);
|
|
414
|
-
emitKeypressEvents(input);
|
|
415
|
-
input.setRawMode(true);
|
|
416
|
-
input.on("keypress", keypress);
|
|
417
|
-
input.once("end", close);
|
|
418
|
-
input.resume();
|
|
419
|
-
}
|
|
420
|
-
if (interactive)
|
|
421
|
-
render();
|
|
422
|
-
else
|
|
423
|
-
console.log(`
|
|
424
|
-
${link(name)}
|
|
425
|
-
`);
|
|
426
|
-
return { close, closed, interactive };
|
|
345
|
+
return { close, closed };
|
|
427
346
|
}
|
|
428
|
-
var
|
|
429
|
-
var init_terminal = () => {};
|
|
347
|
+
var init_runtime = () => {};
|
|
430
348
|
|
|
431
349
|
// node_modules/.bun/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
|
|
432
350
|
function randomID() {
|
|
@@ -5737,7 +5655,7 @@ var init_contract2 = __esm(() => {
|
|
|
5737
5655
|
});
|
|
5738
5656
|
|
|
5739
5657
|
// packages/api/src/contracts/schemas.ts
|
|
5740
|
-
var items = (item) => object({ items: array(item) }), icon, file, project, session, collectionSummary2, workspace, plugin, plugins, folder, assets, folderMetadataSchema, collection, metadataSchemas, metadataQuerySchema, documentEditSchema, document2, documentSchema, entity, projectSearchItemSchema, pathQuery, emptyQuery, routes;
|
|
5658
|
+
var items = (item) => object({ items: array(item) }), icon, file, project, session, collectionSummary2, workspace, plugin, plugins, folder, assets, folderMetadataSchema, collection, metadataSchemas, metadataQuerySchema, deleteNoteSchema, documentEditSchema, document2, documentSchema, entity, projectSearchItemSchema, pathQuery, emptyQuery, routes;
|
|
5741
5659
|
var init_schemas3 = __esm(() => {
|
|
5742
5660
|
init_contract2();
|
|
5743
5661
|
init_zod();
|
|
@@ -5792,6 +5710,7 @@ var init_schemas3 = __esm(() => {
|
|
|
5792
5710
|
icon: object({ paths: array(string2().min(1)).min(1) }).optional(),
|
|
5793
5711
|
id: string2(),
|
|
5794
5712
|
label: string2(),
|
|
5713
|
+
pages: array(object({ id: string2(), label: string2() })).optional(),
|
|
5795
5714
|
subpath: string2().optional()
|
|
5796
5715
|
});
|
|
5797
5716
|
plugins = object({
|
|
@@ -5848,6 +5767,10 @@ var init_schemas3 = __esm(() => {
|
|
|
5848
5767
|
sort: _enum(["alpha", "recent"]).optional(),
|
|
5849
5768
|
schema: string2().optional()
|
|
5850
5769
|
});
|
|
5770
|
+
deleteNoteSchema = object({
|
|
5771
|
+
folder: string2(),
|
|
5772
|
+
path: string2()
|
|
5773
|
+
});
|
|
5851
5774
|
documentEditSchema = object({
|
|
5852
5775
|
folder: string2(),
|
|
5853
5776
|
path: string2(),
|
|
@@ -5907,7 +5830,11 @@ var init_schemas3 = __esm(() => {
|
|
|
5907
5830
|
path: "/api/health",
|
|
5908
5831
|
method: "get",
|
|
5909
5832
|
query: emptyQuery,
|
|
5910
|
-
response: object({
|
|
5833
|
+
response: object({
|
|
5834
|
+
viewer: string2(),
|
|
5835
|
+
root: string2(),
|
|
5836
|
+
kernelKey: string2()
|
|
5837
|
+
})
|
|
5911
5838
|
},
|
|
5912
5839
|
capabilities: {
|
|
5913
5840
|
path: "/api/capabilities",
|
|
@@ -5995,87 +5922,17 @@ var init_schemas3 = __esm(() => {
|
|
|
5995
5922
|
path: "/api/open-path",
|
|
5996
5923
|
method: "get",
|
|
5997
5924
|
query: object({ path: string2() })
|
|
5925
|
+
},
|
|
5926
|
+
deleteNote: {
|
|
5927
|
+
path: "/api/delete-note",
|
|
5928
|
+
method: "post",
|
|
5929
|
+
query: emptyQuery,
|
|
5930
|
+
body: deleteNoteSchema,
|
|
5931
|
+
response: object({ deleted: literal(true) })
|
|
5998
5932
|
}
|
|
5999
5933
|
};
|
|
6000
5934
|
});
|
|
6001
5935
|
|
|
6002
|
-
// packages/api/src/shared/viewer-route.ts
|
|
6003
|
-
function decodeBase64Url(value) {
|
|
6004
|
-
const padded = `${value}${"=".repeat((4 - value.length % 4) % 4)}`;
|
|
6005
|
-
const binary = atob(padded.replaceAll("-", "+").replaceAll("_", "/"));
|
|
6006
|
-
return new TextDecoder().decode(Uint8Array.from(binary, (character) => character.charCodeAt(0)));
|
|
6007
|
-
}
|
|
6008
|
-
function url(value) {
|
|
6009
|
-
if (/^https?:\/\//.test(value))
|
|
6010
|
-
return new URL(value);
|
|
6011
|
-
if (value.startsWith("?"))
|
|
6012
|
-
return new URL(`/${value}`, "http://127.0.0.1");
|
|
6013
|
-
return new URL(value || "/", "http://127.0.0.1");
|
|
6014
|
-
}
|
|
6015
|
-
function tabSection(tab) {
|
|
6016
|
-
if (tab === "files")
|
|
6017
|
-
return "notes";
|
|
6018
|
-
if (tab === "projects" || tab === "sessions")
|
|
6019
|
-
return tab;
|
|
6020
|
-
return;
|
|
6021
|
-
}
|
|
6022
|
-
function decodeFolderPathId(id) {
|
|
6023
|
-
return decodeBase64Url(id);
|
|
6024
|
-
}
|
|
6025
|
-
function parseViewerRoute(value) {
|
|
6026
|
-
const current = url(value);
|
|
6027
|
-
const session = current.searchParams.get("session") ?? undefined;
|
|
6028
|
-
const folder = current.searchParams.get("folder");
|
|
6029
|
-
if (folder) {
|
|
6030
|
-
return {
|
|
6031
|
-
document: current.searchParams.get("document") ?? current.searchParams.get("file") ?? undefined,
|
|
6032
|
-
embedded: current.searchParams.get("embedded") === "1",
|
|
6033
|
-
folder,
|
|
6034
|
-
panel: current.searchParams.get("panel") === "1",
|
|
6035
|
-
section: tabSection(current.searchParams.get("tab")) ?? "notes",
|
|
6036
|
-
...session ? { session } : {}
|
|
6037
|
-
};
|
|
6038
|
-
}
|
|
6039
|
-
const parts = current.pathname.split("/").filter(Boolean);
|
|
6040
|
-
if (parts[0] !== "folder" || !parts[1]) {
|
|
6041
|
-
return {
|
|
6042
|
-
document: current.searchParams.get("file") ?? current.searchParams.get("document") ?? undefined,
|
|
6043
|
-
embedded: current.searchParams.get("embedded") === "1",
|
|
6044
|
-
folder: undefined,
|
|
6045
|
-
panel: current.searchParams.get("panel") === "1",
|
|
6046
|
-
section: tabSection(current.searchParams.get("tab")),
|
|
6047
|
-
...session ? { session } : {}
|
|
6048
|
-
};
|
|
6049
|
-
}
|
|
6050
|
-
const section = parts[2];
|
|
6051
|
-
const route = {
|
|
6052
|
-
embedded: current.searchParams.get("embedded") === "1",
|
|
6053
|
-
folder: decodeFolderPathId(parts[1]),
|
|
6054
|
-
panel: current.searchParams.get("panel") === "1",
|
|
6055
|
-
...session ? { session } : {}
|
|
6056
|
-
};
|
|
6057
|
-
if (section === "collections" && parts[3]) {
|
|
6058
|
-
return { ...route, collectionId: decodeFolderPathId(parts[3]) };
|
|
6059
|
-
}
|
|
6060
|
-
if (section === "views" && parts[3]) {
|
|
6061
|
-
return {
|
|
6062
|
-
...route,
|
|
6063
|
-
view: {
|
|
6064
|
-
pluginId: parts[3],
|
|
6065
|
-
...parts[4] ? { subpath: parts[4] } : {}
|
|
6066
|
-
}
|
|
6067
|
-
};
|
|
6068
|
-
}
|
|
6069
|
-
if (section === "notes" || section === "projects" || section === "sessions") {
|
|
6070
|
-
return { ...route, section };
|
|
6071
|
-
}
|
|
6072
|
-
if (section === "document" && parts[3]) {
|
|
6073
|
-
return { ...route, document: decodeFolderPathId(parts[3]) };
|
|
6074
|
-
}
|
|
6075
|
-
return { ...route, section: "notes" };
|
|
6076
|
-
}
|
|
6077
|
-
var init_viewer_route = () => {};
|
|
6078
|
-
|
|
6079
5936
|
// packages/api-client/src/errors.ts
|
|
6080
5937
|
async function expectedError(response) {
|
|
6081
5938
|
const body = await response.text().catch(() => "");
|
|
@@ -6188,6 +6045,16 @@ function createKennelClient(options = {}) {
|
|
|
6188
6045
|
parseAs: "text"
|
|
6189
6046
|
});
|
|
6190
6047
|
},
|
|
6048
|
+
async deleteNote(body, input = {}) {
|
|
6049
|
+
const response = await (options.fetch ?? globalThis.fetch)(new URL(routes.deleteNote.path, baseUrl), {
|
|
6050
|
+
method: "POST",
|
|
6051
|
+
headers: { "Content-Type": "application/json" },
|
|
6052
|
+
body: JSON.stringify(deleteNoteSchema.parse(body)),
|
|
6053
|
+
signal: input.signal
|
|
6054
|
+
});
|
|
6055
|
+
if (!response.ok)
|
|
6056
|
+
throw await expectedError(response);
|
|
6057
|
+
},
|
|
6191
6058
|
async openFolder(path, input = {}) {
|
|
6192
6059
|
await transport.POST(routes.openFolder.path, {
|
|
6193
6060
|
params: { query: { path } },
|
|
@@ -6228,13 +6095,14 @@ var init_src = __esm(() => {
|
|
|
6228
6095
|
init_schemas3();
|
|
6229
6096
|
init_viewer_route();
|
|
6230
6097
|
init_errors3();
|
|
6098
|
+
init_viewer_route();
|
|
6231
6099
|
init_errors3();
|
|
6232
6100
|
init_responses();
|
|
6233
6101
|
api2 = createKennelClient();
|
|
6234
6102
|
});
|
|
6235
6103
|
|
|
6236
6104
|
// packages/preview/src/hot-viewer.ts
|
|
6237
|
-
import { execFileSync
|
|
6105
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
6238
6106
|
import { randomUUID } from "node:crypto";
|
|
6239
6107
|
import { existsSync } from "node:fs";
|
|
6240
6108
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -6283,13 +6151,13 @@ function startupLock(host, port) {
|
|
|
6283
6151
|
return resolve(tmpdir(), `kennel-hot-viewer-${name}.lock`);
|
|
6284
6152
|
}
|
|
6285
6153
|
function lanHost() {
|
|
6286
|
-
const route =
|
|
6154
|
+
const route = execFileSync("/sbin/route", ["-n", "get", "default"], {
|
|
6287
6155
|
encoding: "utf8"
|
|
6288
6156
|
});
|
|
6289
6157
|
const device = route.match(/interface:\s+(\S+)/)?.[1];
|
|
6290
6158
|
if (!device)
|
|
6291
6159
|
throw new Error("kennel could not find the local network interface");
|
|
6292
|
-
const host =
|
|
6160
|
+
const host = execFileSync("/usr/sbin/ipconfig", ["getifaddr", device], {
|
|
6293
6161
|
encoding: "utf8"
|
|
6294
6162
|
}).trim();
|
|
6295
6163
|
if (!host)
|
|
@@ -6350,18 +6218,9 @@ async function hotViewer(options) {
|
|
|
6350
6218
|
return;
|
|
6351
6219
|
}
|
|
6352
6220
|
const reuse = async () => {
|
|
6353
|
-
|
|
6354
|
-
await Promise.resolve().then(() => init_share());
|
|
6355
|
-
return startPreviewShare(options);
|
|
6356
|
-
});
|
|
6221
|
+
console.log(viewerUrl);
|
|
6357
6222
|
if (options.open)
|
|
6358
6223
|
openPreview(viewerUrl);
|
|
6359
|
-
try {
|
|
6360
|
-
if (terminal.interactive)
|
|
6361
|
-
await terminal.closed;
|
|
6362
|
-
} finally {
|
|
6363
|
-
terminal.close();
|
|
6364
|
-
}
|
|
6365
6224
|
};
|
|
6366
6225
|
const status = await viewerStatus(url);
|
|
6367
6226
|
if (status === "running") {
|
|
@@ -6409,11 +6268,9 @@ async function hotViewer(options) {
|
|
|
6409
6268
|
},
|
|
6410
6269
|
stdio: ["ignore", "inherit", "inherit"]
|
|
6411
6270
|
});
|
|
6412
|
-
const
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
});
|
|
6416
|
-
terminal.closed.then(() => {
|
|
6271
|
+
const lifetime = viewerLifetime();
|
|
6272
|
+
console.log(viewerUrl);
|
|
6273
|
+
lifetime.closed.then(() => {
|
|
6417
6274
|
if (child.exitCode === null && child.signalCode === null)
|
|
6418
6275
|
child.kill("SIGTERM");
|
|
6419
6276
|
});
|
|
@@ -6424,7 +6281,7 @@ async function hotViewer(options) {
|
|
|
6424
6281
|
done();
|
|
6425
6282
|
return;
|
|
6426
6283
|
}
|
|
6427
|
-
|
|
6284
|
+
lifetime.close();
|
|
6428
6285
|
if (await viewerRunning(url, 20, true)) {
|
|
6429
6286
|
await reuse();
|
|
6430
6287
|
done();
|
|
@@ -6433,12 +6290,12 @@ async function hotViewer(options) {
|
|
|
6433
6290
|
fail(new Error(`Kennel hot viewer stopped with exit code ${code ?? "unknown"}`));
|
|
6434
6291
|
});
|
|
6435
6292
|
}).finally(async () => {
|
|
6436
|
-
|
|
6293
|
+
lifetime.close();
|
|
6437
6294
|
await releaseLock(lock, token);
|
|
6438
6295
|
});
|
|
6439
6296
|
}
|
|
6440
6297
|
var init_hot_viewer = __esm(() => {
|
|
6441
|
-
|
|
6298
|
+
init_runtime();
|
|
6442
6299
|
init_src();
|
|
6443
6300
|
});
|
|
6444
6301
|
|
|
@@ -28324,7 +28181,7 @@ var init_server = __esm(() => {
|
|
|
28324
28181
|
// packages/schemas/src/data/workspace.ts
|
|
28325
28182
|
import { existsSync as existsSync3, readFileSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
28326
28183
|
import {
|
|
28327
|
-
basename
|
|
28184
|
+
basename,
|
|
28328
28185
|
dirname as dirname3,
|
|
28329
28186
|
isAbsolute,
|
|
28330
28187
|
relative as relative3,
|
|
@@ -28385,7 +28242,7 @@ function folderWorkspace(input, { allowMissingSchemas = false } = {}) {
|
|
|
28385
28242
|
for (let current = folder;inside(root, current); current = dirname3(current)) {
|
|
28386
28243
|
breadcrumbs.unshift({
|
|
28387
28244
|
path: current,
|
|
28388
|
-
title:
|
|
28245
|
+
title: basename(current),
|
|
28389
28246
|
schema: schemaRoots.some((owner) => inside(owner, current))
|
|
28390
28247
|
});
|
|
28391
28248
|
if (current === root)
|
|
@@ -37934,7 +37791,7 @@ var init_load = __esm(() => {
|
|
|
37934
37791
|
// packages/schemas/src/node.ts
|
|
37935
37792
|
import { readdir as readdir2, readFile as readFile4, realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
37936
37793
|
import {
|
|
37937
|
-
basename as
|
|
37794
|
+
basename as basename2,
|
|
37938
37795
|
dirname as dirname5,
|
|
37939
37796
|
isAbsolute as isAbsolute3,
|
|
37940
37797
|
relative as relative5,
|
|
@@ -37967,7 +37824,7 @@ function createCoreFolderIndex() {
|
|
|
37967
37824
|
affected.add(scope);
|
|
37968
37825
|
}
|
|
37969
37826
|
}
|
|
37970
|
-
if (inside3(path, scope) ||
|
|
37827
|
+
if (inside3(path, scope) || basename2(path) === "raggle.json" && inside3(dirname5(path), scope) || ["schema.json", "_schema.json"].includes(basename2(path)) && dirname5(path) === scope)
|
|
37971
37828
|
affected.add(scope);
|
|
37972
37829
|
}
|
|
37973
37830
|
for (const file of records.keys())
|
|
@@ -38076,7 +37933,7 @@ function createCoreFolderIndex() {
|
|
|
38076
37933
|
path,
|
|
38077
37934
|
relativePath: localPath,
|
|
38078
37935
|
modifiedAt: info.mtimeMs,
|
|
38079
|
-
title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ??
|
|
37936
|
+
title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ?? basename2(path, ".md"),
|
|
38080
37937
|
fields: parsed.fields,
|
|
38081
37938
|
validationErrors,
|
|
38082
37939
|
icon: await recordIcon(parsed.fields, path, entry.folder, parsed.body)
|
|
@@ -38102,7 +37959,7 @@ function createCoreFolderIndex() {
|
|
|
38102
37959
|
const data = {
|
|
38103
37960
|
id,
|
|
38104
37961
|
...entry,
|
|
38105
|
-
title: text(schema?.title) ?? config.title ??
|
|
37962
|
+
title: text(schema?.title) ?? config.title ?? basename2(entry.folder),
|
|
38106
37963
|
navLabel: config.ui?.navLabel,
|
|
38107
37964
|
icon: config.icon,
|
|
38108
37965
|
count: indexedRecords.length,
|
|
@@ -38173,7 +38030,7 @@ var init_node3 = __esm(() => {
|
|
|
38173
38030
|
|
|
38174
38031
|
// packages/schemas/src/server.ts
|
|
38175
38032
|
import { createHash as createHash3 } from "node:crypto";
|
|
38176
|
-
import { basename as
|
|
38033
|
+
import { basename as basename3, relative as relative6 } from "node:path";
|
|
38177
38034
|
function createSchemaCollectionServer(host, options = {}) {
|
|
38178
38035
|
const collectionIndex = createCoreFolderIndex();
|
|
38179
38036
|
const collectionScopes = new Set;
|
|
@@ -38223,7 +38080,7 @@ function createSchemaCollectionServer(host, options = {}) {
|
|
|
38223
38080
|
};
|
|
38224
38081
|
const invalidate = (path) => {
|
|
38225
38082
|
const affected = collectionIndex.invalidate(path);
|
|
38226
|
-
if (affected.length ||
|
|
38083
|
+
if (affected.length || basename3(path) === "raggle.json" && collectionScopes.size) {
|
|
38227
38084
|
clearTimeout(collectionRefresh);
|
|
38228
38085
|
collectionRefresh = setTimeout(() => {
|
|
38229
38086
|
host.emit({
|
|
@@ -38329,7 +38186,7 @@ import {
|
|
|
38329
38186
|
writeFile as writeFile3
|
|
38330
38187
|
} from "node:fs/promises";
|
|
38331
38188
|
import { homedir as homedir2 } from "node:os";
|
|
38332
|
-
import { basename as
|
|
38189
|
+
import { basename as basename4, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
|
|
38333
38190
|
import { promisify } from "node:util";
|
|
38334
38191
|
function kennelConfigHome() {
|
|
38335
38192
|
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
@@ -38403,7 +38260,12 @@ async function resolveCheckout(folder) {
|
|
|
38403
38260
|
}
|
|
38404
38261
|
let output;
|
|
38405
38262
|
try {
|
|
38406
|
-
output = await git([
|
|
38263
|
+
output = await git([
|
|
38264
|
+
"rev-parse",
|
|
38265
|
+
"--path-format=absolute",
|
|
38266
|
+
"--show-toplevel",
|
|
38267
|
+
"--git-common-dir"
|
|
38268
|
+
], start);
|
|
38407
38269
|
} catch (error) {
|
|
38408
38270
|
if (gitExitCode(error) === 128) {
|
|
38409
38271
|
throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
|
|
@@ -38505,7 +38367,11 @@ var init_store = __esm(() => {
|
|
|
38505
38367
|
run = promisify(execFile2);
|
|
38506
38368
|
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
38507
38369
|
markdownExtensions = new Set([".md", ".markdown"]);
|
|
38508
|
-
caseInsensitiveHosts = new Set([
|
|
38370
|
+
caseInsensitiveHosts = new Set([
|
|
38371
|
+
"github.com",
|
|
38372
|
+
"gitlab.com",
|
|
38373
|
+
"bitbucket.org"
|
|
38374
|
+
]);
|
|
38509
38375
|
RepositoryNotesError = class RepositoryNotesError extends Error {
|
|
38510
38376
|
code;
|
|
38511
38377
|
constructor(code, message) {
|
|
@@ -38520,7 +38386,7 @@ var init_store = __esm(() => {
|
|
|
38520
38386
|
import { readdir as readdir4, readFile as readFile6, realpath as realpath4, stat as stat4 } from "node:fs/promises";
|
|
38521
38387
|
import { homedir as homedir3 } from "node:os";
|
|
38522
38388
|
import {
|
|
38523
|
-
basename as
|
|
38389
|
+
basename as basename5,
|
|
38524
38390
|
dirname as dirname7,
|
|
38525
38391
|
extname as extname2,
|
|
38526
38392
|
isAbsolute as isAbsolute4,
|
|
@@ -38667,10 +38533,10 @@ async function readEntity(folderPath, rootPath) {
|
|
|
38667
38533
|
return {
|
|
38668
38534
|
content: document2.body,
|
|
38669
38535
|
path: relative8(folderPath, filePath),
|
|
38670
|
-
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ??
|
|
38536
|
+
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
|
|
38671
38537
|
};
|
|
38672
38538
|
}));
|
|
38673
|
-
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ??
|
|
38539
|
+
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
|
|
38674
38540
|
const relativePath = relative8(rootPath, folderPath) || ".";
|
|
38675
38541
|
return {
|
|
38676
38542
|
aliases: stringValues(index.frontmatter.aliases),
|
|
@@ -38752,6 +38618,43 @@ var init_entity_folder = __esm(() => {
|
|
|
38752
38618
|
]);
|
|
38753
38619
|
});
|
|
38754
38620
|
|
|
38621
|
+
// packages/api/src/server/api/delete-note.ts
|
|
38622
|
+
import { realpath as realpath5, stat as stat5, unlink as unlink2 } from "node:fs/promises";
|
|
38623
|
+
import { extname as extname3, isAbsolute as isAbsolute5, relative as relative9, resolve as resolve9, sep as sep5 } from "node:path";
|
|
38624
|
+
async function containedTarget(path, folder) {
|
|
38625
|
+
const target = await realpath5(resolve9(folder, path));
|
|
38626
|
+
const local = relative9(folder, target);
|
|
38627
|
+
if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
|
|
38628
|
+
throw apiError("forbidden", "The note must be inside its folder.");
|
|
38629
|
+
return target;
|
|
38630
|
+
}
|
|
38631
|
+
var markdownExtensions2, deleteNoteRoute;
|
|
38632
|
+
var init_delete_note = __esm(() => {
|
|
38633
|
+
init_schemas3();
|
|
38634
|
+
init_endpoint();
|
|
38635
|
+
markdownExtensions2 = new Set([".md", ".markdown"]);
|
|
38636
|
+
deleteNoteRoute = {
|
|
38637
|
+
method: "POST",
|
|
38638
|
+
maxBodyBytes: 4 * 1024,
|
|
38639
|
+
async handle({ body, context, response }) {
|
|
38640
|
+
const parsed = deleteNoteSchema.safeParse(parseJsonBody(body));
|
|
38641
|
+
if (!parsed.success)
|
|
38642
|
+
throw apiError("bad-request", "Invalid delete request.");
|
|
38643
|
+
const folder = context.authorize(parsed.data.folder);
|
|
38644
|
+
if (!markdownExtensions2.has(extname3(parsed.data.path).toLowerCase()))
|
|
38645
|
+
throw apiError("bad-request", "Only Markdown notes can be deleted.");
|
|
38646
|
+
const target = await containedTarget(parsed.data.path, folder);
|
|
38647
|
+
const details = await stat5(target).catch(() => {
|
|
38648
|
+
return;
|
|
38649
|
+
});
|
|
38650
|
+
if (!details?.isFile() || details.nlink !== 1)
|
|
38651
|
+
throw apiError("forbidden", "Only regular, unlinked files can be deleted.");
|
|
38652
|
+
await unlink2(target);
|
|
38653
|
+
jsonResponse(response, 200, routes.deleteNote.response.parse({ deleted: true }));
|
|
38654
|
+
}
|
|
38655
|
+
};
|
|
38656
|
+
});
|
|
38657
|
+
|
|
38755
38658
|
// packages/api/src/shared/selection.ts
|
|
38756
38659
|
function replaceSelection(content, start, end, replacement) {
|
|
38757
38660
|
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end <= start || end > content.length)
|
|
@@ -38763,30 +38666,30 @@ function replaceSelection(content, start, end, replacement) {
|
|
|
38763
38666
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
38764
38667
|
import {
|
|
38765
38668
|
readFile as readFile7,
|
|
38766
|
-
realpath as
|
|
38669
|
+
realpath as realpath6,
|
|
38767
38670
|
rename as rename3,
|
|
38768
|
-
stat as
|
|
38769
|
-
unlink as
|
|
38671
|
+
stat as stat6,
|
|
38672
|
+
unlink as unlink3,
|
|
38770
38673
|
writeFile as writeFile4
|
|
38771
38674
|
} from "node:fs/promises";
|
|
38772
38675
|
import {
|
|
38773
38676
|
dirname as dirname8,
|
|
38774
|
-
extname as
|
|
38775
|
-
isAbsolute as
|
|
38776
|
-
relative as
|
|
38777
|
-
resolve as
|
|
38778
|
-
sep as
|
|
38677
|
+
extname as extname4,
|
|
38678
|
+
isAbsolute as isAbsolute6,
|
|
38679
|
+
relative as relative10,
|
|
38680
|
+
resolve as resolve10,
|
|
38681
|
+
sep as sep6
|
|
38779
38682
|
} from "node:path";
|
|
38780
38683
|
async function editDocument(folder, input) {
|
|
38781
38684
|
const { path, original, start, end, replacement } = input;
|
|
38782
38685
|
if (typeof path !== "string" || typeof original !== "string" || typeof replacement !== "string" || typeof start !== "number" || typeof end !== "number")
|
|
38783
38686
|
throw apiError("bad-request", "Invalid document edit.");
|
|
38784
|
-
if (![".md", ".markdown"].includes(
|
|
38687
|
+
if (![".md", ".markdown"].includes(extname4(path).toLowerCase()))
|
|
38785
38688
|
throw apiError("bad-request", "Only Markdown notes can be edited.");
|
|
38786
|
-
const root = await
|
|
38787
|
-
const target = await
|
|
38788
|
-
const local =
|
|
38789
|
-
if (!local || local === ".." || local.startsWith(`..${
|
|
38689
|
+
const root = await realpath6(folder);
|
|
38690
|
+
const target = await realpath6(resolve10(root, path));
|
|
38691
|
+
const local = relative10(root, target);
|
|
38692
|
+
if (!local || local === ".." || local.startsWith(`..${sep6}`) || isAbsolute6(local))
|
|
38790
38693
|
throw apiError("forbidden", "The document must be inside its folder.");
|
|
38791
38694
|
let content;
|
|
38792
38695
|
try {
|
|
@@ -38796,12 +38699,12 @@ async function editDocument(folder, input) {
|
|
|
38796
38699
|
}
|
|
38797
38700
|
const previous = pending.get(target) ?? Promise.resolve();
|
|
38798
38701
|
const operation = previous.catch(() => {}).then(async () => {
|
|
38799
|
-
const details = await
|
|
38702
|
+
const details = await stat6(target);
|
|
38800
38703
|
if (!details.isFile() || details.nlink !== 1)
|
|
38801
38704
|
throw apiError("forbidden", "Only regular, unlinked files can be edited.");
|
|
38802
38705
|
if (await readFile7(target, "utf8") !== original)
|
|
38803
38706
|
throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
|
|
38804
|
-
const temporary =
|
|
38707
|
+
const temporary = resolve10(dirname8(target), `.kennel-edit-${randomUUID3()}.tmp`);
|
|
38805
38708
|
try {
|
|
38806
38709
|
await writeFile4(temporary, content, {
|
|
38807
38710
|
flag: "wx",
|
|
@@ -38811,7 +38714,7 @@ async function editDocument(folder, input) {
|
|
|
38811
38714
|
throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
|
|
38812
38715
|
await rename3(temporary, target);
|
|
38813
38716
|
} finally {
|
|
38814
|
-
await
|
|
38717
|
+
await unlink3(temporary).catch(() => {});
|
|
38815
38718
|
}
|
|
38816
38719
|
return content;
|
|
38817
38720
|
});
|
|
@@ -38837,7 +38740,7 @@ var init_document_edit = __esm(() => {
|
|
|
38837
38740
|
throw apiError("bad-request", "Invalid document edit.");
|
|
38838
38741
|
const input = parsed.data;
|
|
38839
38742
|
const folder = context.authorize(input.folder);
|
|
38840
|
-
if (context.lockedFile && (typeof input.path !== "string" || await
|
|
38743
|
+
if (context.lockedFile && (typeof input.path !== "string" || await realpath6(resolve10(folder, input.path)) !== context.lockedFile))
|
|
38841
38744
|
throw apiError("forbidden", "Only the selected document can be edited.");
|
|
38842
38745
|
await editDocument(folder, input);
|
|
38843
38746
|
jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
|
|
@@ -38847,7 +38750,7 @@ var init_document_edit = __esm(() => {
|
|
|
38847
38750
|
|
|
38848
38751
|
// packages/api/src/server/api/create-agent-file.ts
|
|
38849
38752
|
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
38850
|
-
import { resolve as
|
|
38753
|
+
import { resolve as resolve11 } from "node:path";
|
|
38851
38754
|
var createAgentFileRoute;
|
|
38852
38755
|
var init_create_agent_file = __esm(() => {
|
|
38853
38756
|
init_endpoint();
|
|
@@ -38861,7 +38764,7 @@ var init_create_agent_file = __esm(() => {
|
|
|
38861
38764
|
} catch (error) {
|
|
38862
38765
|
throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
|
|
38863
38766
|
}
|
|
38864
|
-
const target =
|
|
38767
|
+
const target = resolve11(folder, "AGENTS.md");
|
|
38865
38768
|
try {
|
|
38866
38769
|
await writeFile5(target, "", { flag: "wx" });
|
|
38867
38770
|
} catch (error) {
|
|
@@ -38927,8 +38830,8 @@ var init_folder_favicon = __esm(() => {
|
|
|
38927
38830
|
|
|
38928
38831
|
// packages/api/src/server/folder-icons.ts
|
|
38929
38832
|
import { createHash as createHash4 } from "node:crypto";
|
|
38930
|
-
import { readFile as readFile9, stat as
|
|
38931
|
-
import { extname as
|
|
38833
|
+
import { readFile as readFile9, stat as stat7 } from "node:fs/promises";
|
|
38834
|
+
import { extname as extname5 } from "node:path";
|
|
38932
38835
|
import { discoverFolderIcon } from "@raggle-ai/local";
|
|
38933
38836
|
function assetKey(value) {
|
|
38934
38837
|
return createHash4("sha256").update(value).digest("hex");
|
|
@@ -38945,8 +38848,8 @@ function iconUrl(folder, hash, mediaType) {
|
|
|
38945
38848
|
}
|
|
38946
38849
|
async function folderIcon(folder, assets, assetFiles, fallback) {
|
|
38947
38850
|
const icon = discoverFolderIcon(folder);
|
|
38948
|
-
const mediaType = icon ? iconTypes.get(
|
|
38949
|
-
const details = icon && mediaType ? await
|
|
38851
|
+
const mediaType = icon ? iconTypes.get(extname5(icon).toLowerCase()) : null;
|
|
38852
|
+
const details = icon && mediaType ? await stat7(icon).catch(() => {
|
|
38950
38853
|
return;
|
|
38951
38854
|
}) : undefined;
|
|
38952
38855
|
if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
|
|
@@ -38968,10 +38871,10 @@ async function searchIconDataUrl(icon) {
|
|
|
38968
38871
|
return;
|
|
38969
38872
|
if (icon.startsWith("data:"))
|
|
38970
38873
|
return icon;
|
|
38971
|
-
const mediaType = searchIconTypes.get(
|
|
38874
|
+
const mediaType = searchIconTypes.get(extname5(icon).toLowerCase());
|
|
38972
38875
|
if (!mediaType)
|
|
38973
38876
|
return;
|
|
38974
|
-
const details = await
|
|
38877
|
+
const details = await stat7(icon).catch(() => {
|
|
38975
38878
|
return;
|
|
38976
38879
|
});
|
|
38977
38880
|
if (!details?.size || details.size > maxSearchIconBytes)
|
|
@@ -39043,9 +38946,461 @@ var init_folder_icons = __esm(() => {
|
|
|
39043
38946
|
]);
|
|
39044
38947
|
});
|
|
39045
38948
|
|
|
38949
|
+
// packages/knl/src/folder-guard.ts
|
|
38950
|
+
import { existsSync as existsSync4, realpathSync as realpathSync2 } from "node:fs";
|
|
38951
|
+
import { realpath as realpath7 } from "node:fs/promises";
|
|
38952
|
+
import { dirname as dirname9, resolve as resolve12, sep as sep7 } from "node:path";
|
|
38953
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
38954
|
+
async function knlRealRoot(owner) {
|
|
38955
|
+
const expected = resolve12(await realpath7(owner), "knl");
|
|
38956
|
+
const real = await realpath7(resolve12(owner, "knl"));
|
|
38957
|
+
if (real !== expected)
|
|
38958
|
+
throw new Error("knl/ must be a folder, not a link to another place.");
|
|
38959
|
+
return real;
|
|
38960
|
+
}
|
|
38961
|
+
async function knlReadRoots(owner) {
|
|
38962
|
+
const packages = await Promise.all(knlHostPackageFolders.map((folder) => realpath7(folder).catch(() => {
|
|
38963
|
+
return;
|
|
38964
|
+
})));
|
|
38965
|
+
return [
|
|
38966
|
+
await knlRealRoot(owner),
|
|
38967
|
+
...packages.filter((folder) => Boolean(folder))
|
|
38968
|
+
];
|
|
38969
|
+
}
|
|
38970
|
+
function isKnlReadable(file, roots) {
|
|
38971
|
+
let real;
|
|
38972
|
+
try {
|
|
38973
|
+
real = realpathSync2(file);
|
|
38974
|
+
} catch {
|
|
38975
|
+
real = resolve12(file);
|
|
38976
|
+
}
|
|
38977
|
+
return roots.some((root) => real === root || real.startsWith(`${root}${sep7}`));
|
|
38978
|
+
}
|
|
38979
|
+
function knlReadError(file) {
|
|
38980
|
+
return `KNL pages can only use files in knl/ and installed packages: ${file}`;
|
|
38981
|
+
}
|
|
38982
|
+
function assertKnlReadable(file, roots) {
|
|
38983
|
+
if (!isKnlReadable(file, roots))
|
|
38984
|
+
throw new Error(knlReadError(file));
|
|
38985
|
+
}
|
|
38986
|
+
var knlHostResolveDir, knlHostPackageFolders;
|
|
38987
|
+
var init_folder_guard = __esm(() => {
|
|
38988
|
+
knlHostResolveDir = dirname9(fileURLToPath2(import.meta.url));
|
|
38989
|
+
knlHostPackageFolders = (() => {
|
|
38990
|
+
const folders = [];
|
|
38991
|
+
let current = knlHostResolveDir;
|
|
38992
|
+
while (true) {
|
|
38993
|
+
const nodeModules = resolve12(current, "node_modules");
|
|
38994
|
+
if (existsSync4(nodeModules))
|
|
38995
|
+
folders.push(nodeModules);
|
|
38996
|
+
const parent = dirname9(current);
|
|
38997
|
+
if (parent === current)
|
|
38998
|
+
return folders;
|
|
38999
|
+
current = parent;
|
|
39000
|
+
}
|
|
39001
|
+
})();
|
|
39002
|
+
});
|
|
39003
|
+
|
|
39004
|
+
// packages/knl/src/tailwind.ts
|
|
39005
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
39006
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
39007
|
+
import { dirname as dirname10, resolve as resolve13 } from "node:path";
|
|
39008
|
+
import { compile } from "@tailwindcss/node";
|
|
39009
|
+
import { Scanner } from "@tailwindcss/oxide";
|
|
39010
|
+
function starterKnlStyles() {
|
|
39011
|
+
return `@layer theme, utilities;
|
|
39012
|
+
@import "tailwindcss/theme.css" layer(theme) prefix(knl);
|
|
39013
|
+
@import "tailwindcss/utilities.css" layer(utilities) source(none);
|
|
39014
|
+
|
|
39015
|
+
@theme {
|
|
39016
|
+
--font-sans: system-ui, sans-serif;
|
|
39017
|
+
}
|
|
39018
|
+
`;
|
|
39019
|
+
}
|
|
39020
|
+
async function compileKnlCss(js, stylesheet, roots) {
|
|
39021
|
+
const base = dirname10(stylesheet);
|
|
39022
|
+
assertKnlReadable(stylesheet, roots);
|
|
39023
|
+
const css = await readFile10(stylesheet, "utf8").catch((error) => {
|
|
39024
|
+
if (error.code === "ENOENT")
|
|
39025
|
+
return starterKnlStyles();
|
|
39026
|
+
throw error;
|
|
39027
|
+
});
|
|
39028
|
+
const compiler = await compile(css, {
|
|
39029
|
+
base,
|
|
39030
|
+
from: stylesheet,
|
|
39031
|
+
onDependency: (path) => assertKnlReadable(path, roots),
|
|
39032
|
+
customJsResolver: async (id) => {
|
|
39033
|
+
throw new Error(`KNL does not load Tailwind plugins or configs (${id}).`);
|
|
39034
|
+
},
|
|
39035
|
+
customCssResolver: async (id) => id === "tailwindcss" || id.startsWith("tailwindcss/") ? require2.resolve(id) : undefined
|
|
39036
|
+
});
|
|
39037
|
+
const sources = [...compiler.sources];
|
|
39038
|
+
if (compiler.root !== "none") {
|
|
39039
|
+
sources.push({
|
|
39040
|
+
...compiler.root ?? { base, pattern: "**/*" },
|
|
39041
|
+
negated: false
|
|
39042
|
+
});
|
|
39043
|
+
}
|
|
39044
|
+
for (const source of sources) {
|
|
39045
|
+
if (source.negated)
|
|
39046
|
+
continue;
|
|
39047
|
+
const fixed = source.pattern.split("/");
|
|
39048
|
+
const glob = fixed.findIndex((part) => /[*?[{]/.test(part));
|
|
39049
|
+
assertKnlReadable(resolve13(source.base, fixed.slice(0, glob < 0 ? undefined : glob).join("/")), roots);
|
|
39050
|
+
}
|
|
39051
|
+
const scanner = new Scanner({ sources });
|
|
39052
|
+
const candidates = [
|
|
39053
|
+
...scanner.scan(),
|
|
39054
|
+
...scanner.scanFiles([{ content: js, extension: "js" }])
|
|
39055
|
+
];
|
|
39056
|
+
return compiler.build(candidates);
|
|
39057
|
+
}
|
|
39058
|
+
var require2;
|
|
39059
|
+
var init_tailwind = __esm(() => {
|
|
39060
|
+
init_folder_guard();
|
|
39061
|
+
require2 = createRequire2(import.meta.url);
|
|
39062
|
+
});
|
|
39063
|
+
|
|
39064
|
+
// packages/knl/src/knl.ts
|
|
39065
|
+
import { readdir as readdir5, stat as stat8 } from "node:fs/promises";
|
|
39066
|
+
import { basename as basename6, dirname as dirname11, relative as relative11, resolve as resolve14, sep as sep8 } from "node:path";
|
|
39067
|
+
import * as esbuild from "esbuild";
|
|
39068
|
+
function sourceFor(file, knlRoot, scanRoot, owner) {
|
|
39069
|
+
const id = toModuleId(file, knlRoot);
|
|
39070
|
+
return {
|
|
39071
|
+
id,
|
|
39072
|
+
file,
|
|
39073
|
+
relative: relative11(scanRoot, file).split(sep8).join("/"),
|
|
39074
|
+
folder: dirname11(file),
|
|
39075
|
+
owner
|
|
39076
|
+
};
|
|
39077
|
+
}
|
|
39078
|
+
function toModuleId(file, knlRoot) {
|
|
39079
|
+
const relativeKnl = relative11(knlRoot, file).split(sep8).join("/");
|
|
39080
|
+
return relativeKnl.replace(/\/?index\.(tsx|ts)$/, "") || "index";
|
|
39081
|
+
}
|
|
39082
|
+
async function collectKnlSources(scanRoot, owner) {
|
|
39083
|
+
const knlRoot = resolve14(scanRoot, KNK_FOLDER);
|
|
39084
|
+
const knlEntries = await readdir5(knlRoot, { withFileTypes: true }).catch(() => []);
|
|
39085
|
+
const sources = [];
|
|
39086
|
+
const addModule = async (folder) => {
|
|
39087
|
+
for (const candidate of MODULE_FILES) {
|
|
39088
|
+
const file = resolve14(folder, candidate);
|
|
39089
|
+
if (await stat8(file).then((d) => d.isFile()).catch(() => false)) {
|
|
39090
|
+
sources.push(sourceFor(file, knlRoot, scanRoot, owner));
|
|
39091
|
+
return;
|
|
39092
|
+
}
|
|
39093
|
+
}
|
|
39094
|
+
};
|
|
39095
|
+
await addModule(knlRoot);
|
|
39096
|
+
for (const entry of knlEntries) {
|
|
39097
|
+
if (!entry.isDirectory())
|
|
39098
|
+
continue;
|
|
39099
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
39100
|
+
continue;
|
|
39101
|
+
await addModule(resolve14(knlRoot, entry.name));
|
|
39102
|
+
}
|
|
39103
|
+
return sources.sort((left, right) => {
|
|
39104
|
+
const rank = (left.id === "index" ? 0 : 1) - (right.id === "index" ? 0 : 1);
|
|
39105
|
+
return rank !== 0 ? rank : left.id.localeCompare(right.id);
|
|
39106
|
+
});
|
|
39107
|
+
}
|
|
39108
|
+
async function loadKnlSources(viewRoot, linkedFolders = []) {
|
|
39109
|
+
const sources = [
|
|
39110
|
+
...await collectKnlSources(viewRoot, viewRoot),
|
|
39111
|
+
...(await Promise.all(linkedFolders.map(async (folder) => (await collectKnlSources(folder, folder)).map((source) => ({
|
|
39112
|
+
...source,
|
|
39113
|
+
id: `${basename6(folder)}:${source.id}`,
|
|
39114
|
+
relative: `${basename6(folder)}/${source.relative}`
|
|
39115
|
+
}))))).flat()
|
|
39116
|
+
];
|
|
39117
|
+
return { sources };
|
|
39118
|
+
}
|
|
39119
|
+
function knlNodePaths(knlRoot) {
|
|
39120
|
+
return [resolve14(knlRoot, "node_modules"), ...knlHostPackageFolders];
|
|
39121
|
+
}
|
|
39122
|
+
async function compileKnlSource(source) {
|
|
39123
|
+
const sourceBytes = (await stat8(source.file)).size;
|
|
39124
|
+
const tooLarge = (bytes, what) => ({
|
|
39125
|
+
id: source.id,
|
|
39126
|
+
relative: source.relative,
|
|
39127
|
+
js: "",
|
|
39128
|
+
css: "",
|
|
39129
|
+
issues: [
|
|
39130
|
+
{
|
|
39131
|
+
path: source.relative,
|
|
39132
|
+
message: `Module ${what} is too large (${bytes} bytes; limit ${MAX_SOURCE_BYTES}).`
|
|
39133
|
+
}
|
|
39134
|
+
]
|
|
39135
|
+
});
|
|
39136
|
+
if (sourceBytes > MAX_SOURCE_BYTES)
|
|
39137
|
+
return tooLarge(sourceBytes, "source");
|
|
39138
|
+
const cached = compileCache.get(source.file);
|
|
39139
|
+
const unchanged = cached && (await Promise.all(cached.dependencies.map(async ({ file, mtimeMs }) => (await stat8(file).catch(() => {
|
|
39140
|
+
return;
|
|
39141
|
+
}))?.mtimeMs === mtimeMs))).every(Boolean);
|
|
39142
|
+
const knlRoot = resolve14(source.owner, KNK_FOLDER);
|
|
39143
|
+
let issuePath = source.relative;
|
|
39144
|
+
try {
|
|
39145
|
+
const nodePaths = knlNodePaths(knlRoot);
|
|
39146
|
+
const roots = await knlReadRoots(source.owner);
|
|
39147
|
+
assertKnlReadable(source.file, roots);
|
|
39148
|
+
let js;
|
|
39149
|
+
if (cached && unchanged) {
|
|
39150
|
+
js = cached.js;
|
|
39151
|
+
} else {
|
|
39152
|
+
const result = await esbuild.build({
|
|
39153
|
+
absWorkingDir: source.folder,
|
|
39154
|
+
stdin: {
|
|
39155
|
+
contents: `import { createElement } from "react"
|
|
39156
|
+
import { createRoot } from "react-dom/client"
|
|
39157
|
+
import Component from ${JSON.stringify(source.file)}
|
|
39158
|
+
|
|
39159
|
+
globalThis.__knlMount = (container, context, onError) => {
|
|
39160
|
+
const root = createRoot(container, { onUncaughtError: onError })
|
|
39161
|
+
root.render(createElement(Component, context))
|
|
39162
|
+
}
|
|
39163
|
+
`,
|
|
39164
|
+
sourcefile: `${source.file}.mount.ts`,
|
|
39165
|
+
loader: "ts",
|
|
39166
|
+
resolveDir: source.folder
|
|
39167
|
+
},
|
|
39168
|
+
bundle: true,
|
|
39169
|
+
format: "iife",
|
|
39170
|
+
target: "es2022",
|
|
39171
|
+
jsx: "automatic",
|
|
39172
|
+
platform: "browser",
|
|
39173
|
+
write: false,
|
|
39174
|
+
minify: false,
|
|
39175
|
+
sourcemap: false,
|
|
39176
|
+
logLevel: "silent",
|
|
39177
|
+
metafile: true,
|
|
39178
|
+
nodePaths,
|
|
39179
|
+
plugins: [readGuard(roots)]
|
|
39180
|
+
});
|
|
39181
|
+
js = result.outputFiles?.[0]?.text ?? "";
|
|
39182
|
+
const files = new Set([
|
|
39183
|
+
resolve14(knlRoot, "package.json"),
|
|
39184
|
+
...Object.keys(result.metafile.inputs).map((file) => resolve14(source.folder, file)).filter((file) => file !== `${source.file}.mount.ts`)
|
|
39185
|
+
]);
|
|
39186
|
+
const dependencies = await Promise.all([...files].map(async (file) => ({
|
|
39187
|
+
file,
|
|
39188
|
+
mtimeMs: (await stat8(file).catch(() => {
|
|
39189
|
+
return;
|
|
39190
|
+
}))?.mtimeMs
|
|
39191
|
+
})));
|
|
39192
|
+
compileCache.delete(source.file);
|
|
39193
|
+
compileCache.set(source.file, { js, dependencies });
|
|
39194
|
+
if (compileCache.size > MAX_CACHED_MODULES)
|
|
39195
|
+
compileCache.delete(compileCache.keys().next().value);
|
|
39196
|
+
}
|
|
39197
|
+
const stylesheet = resolve14(knlRoot, "style.css");
|
|
39198
|
+
issuePath = relative11(source.owner, stylesheet).split(sep8).join("/");
|
|
39199
|
+
const css = await compileKnlCss(js, stylesheet, roots);
|
|
39200
|
+
issuePath = source.relative;
|
|
39201
|
+
if (js.length + css.length > MAX_JS_BYTES) {
|
|
39202
|
+
return {
|
|
39203
|
+
id: source.id,
|
|
39204
|
+
relative: source.relative,
|
|
39205
|
+
js: "",
|
|
39206
|
+
css: "",
|
|
39207
|
+
issues: [
|
|
39208
|
+
{
|
|
39209
|
+
path: source.relative,
|
|
39210
|
+
message: `Compiled module is too large (${js.length + css.length} bytes; limit ${MAX_JS_BYTES}).`
|
|
39211
|
+
}
|
|
39212
|
+
]
|
|
39213
|
+
};
|
|
39214
|
+
}
|
|
39215
|
+
return { id: source.id, relative: source.relative, js, css, issues: [] };
|
|
39216
|
+
} catch (error) {
|
|
39217
|
+
return {
|
|
39218
|
+
id: source.id,
|
|
39219
|
+
relative: source.relative,
|
|
39220
|
+
js: "",
|
|
39221
|
+
css: "",
|
|
39222
|
+
issues: knlBuildIssues(error, issuePath, source)
|
|
39223
|
+
};
|
|
39224
|
+
}
|
|
39225
|
+
}
|
|
39226
|
+
function readGuard(roots) {
|
|
39227
|
+
return {
|
|
39228
|
+
name: "knl-read-guard",
|
|
39229
|
+
setup(build) {
|
|
39230
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
39231
|
+
if (args.pluginData === guardResolve)
|
|
39232
|
+
return;
|
|
39233
|
+
const resolveFrom = (resolveDir) => build.resolve(args.path, {
|
|
39234
|
+
importer: args.importer,
|
|
39235
|
+
kind: args.kind,
|
|
39236
|
+
pluginData: guardResolve,
|
|
39237
|
+
resolveDir
|
|
39238
|
+
});
|
|
39239
|
+
const readable = (result) => result.namespace !== "file" || !result.path || isKnlReadable(result.path, roots);
|
|
39240
|
+
let result = await resolveFrom(args.resolveDir);
|
|
39241
|
+
const bare = !/^(?:\.{1,2}(?:\/|$)|\/)/.test(args.path);
|
|
39242
|
+
if (bare && result.errors.length === 0 && !readable(result))
|
|
39243
|
+
result = await resolveFrom(knlHostResolveDir);
|
|
39244
|
+
if (result.errors.length > 0)
|
|
39245
|
+
return { errors: result.errors };
|
|
39246
|
+
if (!readable(result))
|
|
39247
|
+
return { errors: [{ text: knlReadError(args.path) }] };
|
|
39248
|
+
return result;
|
|
39249
|
+
});
|
|
39250
|
+
}
|
|
39251
|
+
};
|
|
39252
|
+
}
|
|
39253
|
+
function knlBuildIssues(error, relativePath, source) {
|
|
39254
|
+
const failures = error?.errors;
|
|
39255
|
+
if (Array.isArray(failures) && failures.length > 0) {
|
|
39256
|
+
const first = failures[0];
|
|
39257
|
+
const file = first?.location?.file ? relative11(source.owner, resolve14(source.folder, first.location.file)).split(sep8).join("/") : relativePath;
|
|
39258
|
+
const location = first?.location ? `${file}:${first.location.line ?? "?"}` : relativePath;
|
|
39259
|
+
return [
|
|
39260
|
+
{ path: location, message: first?.text ?? "Module failed to compile." }
|
|
39261
|
+
];
|
|
39262
|
+
}
|
|
39263
|
+
return [
|
|
39264
|
+
{
|
|
39265
|
+
path: relativePath,
|
|
39266
|
+
message: error instanceof Error && error.message ? error.message : "Module failed to compile."
|
|
39267
|
+
}
|
|
39268
|
+
];
|
|
39269
|
+
}
|
|
39270
|
+
function starterKnlSource() {
|
|
39271
|
+
return `// knl/index.tsx — this folder's homepage (KNL folder web framework).
|
|
39272
|
+
// Every other page lives in its own folder: knl/<page>/index.tsx.
|
|
39273
|
+
import * as React from "react"
|
|
39274
|
+
|
|
39275
|
+
export default function Home() {
|
|
39276
|
+
return (
|
|
39277
|
+
<div className="knl:p-4 knl:font-sans">
|
|
39278
|
+
<h1 className="knl:m-0 knl:text-2xl">Home</h1>
|
|
39279
|
+
<p>Edit this file to build the homepage.</p>
|
|
39280
|
+
</div>
|
|
39281
|
+
)
|
|
39282
|
+
}
|
|
39283
|
+
`;
|
|
39284
|
+
}
|
|
39285
|
+
var KNK_FOLDER = "knl", MAX_SOURCE_BYTES, MAX_JS_BYTES, MODULE_FILES, MAX_CACHED_MODULES = 200, compileCache, guardResolve;
|
|
39286
|
+
var init_knl = __esm(() => {
|
|
39287
|
+
init_folder_guard();
|
|
39288
|
+
init_tailwind();
|
|
39289
|
+
MAX_SOURCE_BYTES = 512 * 1024;
|
|
39290
|
+
MAX_JS_BYTES = 5 * 1024 * 1024;
|
|
39291
|
+
MODULE_FILES = ["index.tsx", "index.ts"];
|
|
39292
|
+
compileCache = new Map;
|
|
39293
|
+
guardResolve = Symbol("knl-guard");
|
|
39294
|
+
});
|
|
39295
|
+
|
|
39296
|
+
// packages/knl/src/manifest.ts
|
|
39297
|
+
var knlManifest;
|
|
39298
|
+
var init_manifest = __esm(() => {
|
|
39299
|
+
knlManifest = {
|
|
39300
|
+
id: "knl",
|
|
39301
|
+
label: "KNL"
|
|
39302
|
+
};
|
|
39303
|
+
});
|
|
39304
|
+
|
|
39305
|
+
// packages/knl/src/server.ts
|
|
39306
|
+
import { stat as stat9 } from "node:fs/promises";
|
|
39307
|
+
import { join as join5 } from "node:path";
|
|
39308
|
+
function createKnlPlugin() {
|
|
39309
|
+
return {
|
|
39310
|
+
...knlManifest,
|
|
39311
|
+
async detect(context) {
|
|
39312
|
+
const hasKnlFolder = await stat9(join5(context.folder, KNK_FOLDER)).then((entry) => entry.isDirectory()).catch(() => false);
|
|
39313
|
+
if (!hasKnlFolder)
|
|
39314
|
+
return { status: "unavailable" };
|
|
39315
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
39316
|
+
if (!sources.some((source) => source.id === "index"))
|
|
39317
|
+
return { status: "unavailable" };
|
|
39318
|
+
return {
|
|
39319
|
+
count: sources.length,
|
|
39320
|
+
pages: sources.filter((source) => source.id !== "index" && !source.id.endsWith(":index")).map((source) => ({
|
|
39321
|
+
id: source.id,
|
|
39322
|
+
label: source.id.replaceAll(/[-_]/g, " ").replace(/^./, (letter) => letter.toUpperCase())
|
|
39323
|
+
})),
|
|
39324
|
+
status: "available"
|
|
39325
|
+
};
|
|
39326
|
+
},
|
|
39327
|
+
async handle(context, subpath) {
|
|
39328
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
39329
|
+
const folder = context.folder;
|
|
39330
|
+
if (sources.length === 0) {
|
|
39331
|
+
return { kind: "available", data: { folder, status: "unavailable" } };
|
|
39332
|
+
}
|
|
39333
|
+
const requested = subpath.trim() || "index";
|
|
39334
|
+
const selected = sources.filter((source) => source.id === requested);
|
|
39335
|
+
if (selected.length === 0) {
|
|
39336
|
+
return {
|
|
39337
|
+
kind: "available",
|
|
39338
|
+
data: {
|
|
39339
|
+
folder,
|
|
39340
|
+
status: "error",
|
|
39341
|
+
message: `KNL page not found: ${requested}`
|
|
39342
|
+
}
|
|
39343
|
+
};
|
|
39344
|
+
}
|
|
39345
|
+
const compiled = await Promise.all(selected.map(compileKnlSource));
|
|
39346
|
+
const entries = compiled.map((result) => {
|
|
39347
|
+
const source = sources.find((item) => item.id === result.id);
|
|
39348
|
+
return {
|
|
39349
|
+
id: result.id,
|
|
39350
|
+
relative: result.relative,
|
|
39351
|
+
...result.js ? { js: result.js } : {},
|
|
39352
|
+
css: result.css,
|
|
39353
|
+
...result.issues.length > 0 ? { issues: result.issues } : {},
|
|
39354
|
+
owner: source?.owner ?? folder
|
|
39355
|
+
};
|
|
39356
|
+
});
|
|
39357
|
+
const failing = entries.filter((entry) => entry.issues?.length);
|
|
39358
|
+
if (failing.length > 0) {
|
|
39359
|
+
return {
|
|
39360
|
+
kind: "available",
|
|
39361
|
+
data: {
|
|
39362
|
+
folder,
|
|
39363
|
+
status: "error",
|
|
39364
|
+
entries,
|
|
39365
|
+
issues: failing[0]?.issues
|
|
39366
|
+
}
|
|
39367
|
+
};
|
|
39368
|
+
}
|
|
39369
|
+
return {
|
|
39370
|
+
kind: "available",
|
|
39371
|
+
data: {
|
|
39372
|
+
folder,
|
|
39373
|
+
status: "available",
|
|
39374
|
+
entries
|
|
39375
|
+
}
|
|
39376
|
+
};
|
|
39377
|
+
},
|
|
39378
|
+
async init() {
|
|
39379
|
+
return {
|
|
39380
|
+
files: [
|
|
39381
|
+
{ path: "knl/index.tsx", content: starterKnlSource() },
|
|
39382
|
+
{ path: "knl/style.css", content: starterKnlStyles() },
|
|
39383
|
+
{
|
|
39384
|
+
path: "knl/package.json",
|
|
39385
|
+
content: `${JSON.stringify({ private: true, type: "module" }, null, 2)}
|
|
39386
|
+
`
|
|
39387
|
+
},
|
|
39388
|
+
{ path: "knl/.gitignore", content: `node_modules/
|
|
39389
|
+
` }
|
|
39390
|
+
]
|
|
39391
|
+
};
|
|
39392
|
+
}
|
|
39393
|
+
};
|
|
39394
|
+
}
|
|
39395
|
+
var init_server3 = __esm(() => {
|
|
39396
|
+
init_knl();
|
|
39397
|
+
init_manifest();
|
|
39398
|
+
init_tailwind();
|
|
39399
|
+
});
|
|
39400
|
+
|
|
39046
39401
|
// packages/notes/src/manifest.ts
|
|
39047
39402
|
var notesManifest;
|
|
39048
|
-
var
|
|
39403
|
+
var init_manifest2 = __esm(() => {
|
|
39049
39404
|
notesManifest = {
|
|
39050
39405
|
id: "notes",
|
|
39051
39406
|
icon: {
|
|
@@ -39060,37 +39415,37 @@ var init_manifest = __esm(() => {
|
|
|
39060
39415
|
});
|
|
39061
39416
|
|
|
39062
39417
|
// packages/notes/src/server.ts
|
|
39063
|
-
import { readdir as
|
|
39418
|
+
import { readdir as readdir6, realpath as realpath8, stat as stat10 } from "node:fs/promises";
|
|
39064
39419
|
import {
|
|
39065
39420
|
basename as basename7,
|
|
39066
|
-
extname as
|
|
39067
|
-
isAbsolute as
|
|
39068
|
-
relative as
|
|
39069
|
-
resolve as
|
|
39070
|
-
sep as
|
|
39421
|
+
extname as extname6,
|
|
39422
|
+
isAbsolute as isAbsolute7,
|
|
39423
|
+
relative as relative12,
|
|
39424
|
+
resolve as resolve15,
|
|
39425
|
+
sep as sep9
|
|
39071
39426
|
} from "node:path";
|
|
39072
39427
|
async function listMarkdownFiles(folder) {
|
|
39073
39428
|
const files = [];
|
|
39074
39429
|
const visit = async (current) => {
|
|
39075
|
-
const entries = await
|
|
39430
|
+
const entries = await readdir6(current, { withFileTypes: true }).catch(() => []);
|
|
39076
39431
|
for (const entry of entries) {
|
|
39077
39432
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
39078
39433
|
continue;
|
|
39079
|
-
const path =
|
|
39434
|
+
const path = resolve15(current, entry.name);
|
|
39080
39435
|
if (entry.isDirectory()) {
|
|
39081
39436
|
await visit(path);
|
|
39082
39437
|
continue;
|
|
39083
39438
|
}
|
|
39084
|
-
if (!entry.isFile() || !
|
|
39439
|
+
if (!entry.isFile() || !markdownExtensions3.has(extname6(entry.name))) {
|
|
39085
39440
|
continue;
|
|
39086
39441
|
}
|
|
39087
|
-
const details = await
|
|
39442
|
+
const details = await stat10(path).catch(() => {
|
|
39088
39443
|
return;
|
|
39089
39444
|
});
|
|
39090
39445
|
files.push({
|
|
39091
39446
|
activityAt: details?.mtimeMs ?? 0,
|
|
39092
|
-
name: basename7(entry.name,
|
|
39093
|
-
relativePath:
|
|
39447
|
+
name: basename7(entry.name, extname6(entry.name)),
|
|
39448
|
+
relativePath: relative12(folder, path).split(sep9).join("/")
|
|
39094
39449
|
});
|
|
39095
39450
|
}
|
|
39096
39451
|
};
|
|
@@ -39098,8 +39453,8 @@ async function listMarkdownFiles(folder) {
|
|
|
39098
39453
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
39099
39454
|
}
|
|
39100
39455
|
function inside5(path, root) {
|
|
39101
|
-
const local =
|
|
39102
|
-
return local === "" || !local.startsWith("..") && !
|
|
39456
|
+
const local = relative12(root, path);
|
|
39457
|
+
return local === "" || !local.startsWith("..") && !isAbsolute7(local);
|
|
39103
39458
|
}
|
|
39104
39459
|
async function withoutManaged(groups, managedRoots) {
|
|
39105
39460
|
if (!managedRoots.length)
|
|
@@ -39110,7 +39465,7 @@ async function withoutManaged(groups, managedRoots) {
|
|
|
39110
39465
|
return { ...group, files: [] };
|
|
39111
39466
|
}
|
|
39112
39467
|
const files = await Promise.all(group.files.map(async (file) => {
|
|
39113
|
-
const real = await canonical(
|
|
39468
|
+
const real = await canonical(resolve15(group.folder, file.relativePath));
|
|
39114
39469
|
return roots.some((root) => inside5(real, root)) ? undefined : file;
|
|
39115
39470
|
}));
|
|
39116
39471
|
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
@@ -39160,7 +39515,7 @@ async function notesLlmSections({
|
|
|
39160
39515
|
continue;
|
|
39161
39516
|
seen.add(identity);
|
|
39162
39517
|
const files = await listMarkdownFiles(source);
|
|
39163
|
-
const paths = files.map((file) =>
|
|
39518
|
+
const paths = files.map((file) => resolve15(source, file.relativePath));
|
|
39164
39519
|
groups.push({ folder: source, paths });
|
|
39165
39520
|
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
39166
39521
|
}
|
|
@@ -39204,17 +39559,17 @@ function createNotesPlugin() {
|
|
|
39204
39559
|
llm: notesLlmSections
|
|
39205
39560
|
};
|
|
39206
39561
|
}
|
|
39207
|
-
var
|
|
39208
|
-
var
|
|
39209
|
-
|
|
39210
|
-
|
|
39562
|
+
var markdownExtensions3, llmNoteLimit = 20, canonical = (path) => realpath8(path).catch(() => path);
|
|
39563
|
+
var init_server4 = __esm(() => {
|
|
39564
|
+
init_manifest2();
|
|
39565
|
+
markdownExtensions3 = new Set([".md", ".markdown"]);
|
|
39211
39566
|
});
|
|
39212
39567
|
|
|
39213
39568
|
// packages/api/src/server/plugins.ts
|
|
39214
39569
|
import { pathToFileURL } from "node:url";
|
|
39215
|
-
import { existsSync as
|
|
39216
|
-
import { mkdir as mkdir4, readFile as
|
|
39217
|
-
import { dirname as
|
|
39570
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
39571
|
+
import { mkdir as mkdir4, readFile as readFile11, stat as stat11, writeFile as writeFile6 } from "node:fs/promises";
|
|
39572
|
+
import { dirname as dirname12, resolve as resolve16, sep as sep10 } from "node:path";
|
|
39218
39573
|
function configuredFolderServerRegistry(registry, resolvedConfig) {
|
|
39219
39574
|
const disabled = new Set(resolvedConfig.plugins.disable);
|
|
39220
39575
|
const orderedIds = resolvedConfig.plugins.order.filter((id) => registry.has(id) && !disabled.has(id));
|
|
@@ -39238,14 +39593,14 @@ function parsePluginRegistryConfig(content) {
|
|
|
39238
39593
|
};
|
|
39239
39594
|
}
|
|
39240
39595
|
async function nearestKennelConfig(folder) {
|
|
39241
|
-
for (let current = folder;; current =
|
|
39242
|
-
const candidate =
|
|
39243
|
-
const details = await
|
|
39596
|
+
for (let current = folder;; current = dirname12(current)) {
|
|
39597
|
+
const candidate = resolve16(current, "kennel.json");
|
|
39598
|
+
const details = await stat11(candidate).catch(() => {
|
|
39244
39599
|
return;
|
|
39245
39600
|
});
|
|
39246
39601
|
if (details?.isFile())
|
|
39247
39602
|
return candidate;
|
|
39248
|
-
if (
|
|
39603
|
+
if (dirname12(current) === current)
|
|
39249
39604
|
return;
|
|
39250
39605
|
}
|
|
39251
39606
|
}
|
|
@@ -39254,13 +39609,13 @@ async function folderPluginRegistryConfig(folder) {
|
|
|
39254
39609
|
if (!path)
|
|
39255
39610
|
return defaultPluginRegistryConfig;
|
|
39256
39611
|
try {
|
|
39257
|
-
return parsePluginRegistryConfig(await
|
|
39612
|
+
return parsePluginRegistryConfig(await readFile11(path, "utf8"));
|
|
39258
39613
|
} catch {
|
|
39259
39614
|
return defaultPluginRegistryConfig;
|
|
39260
39615
|
}
|
|
39261
39616
|
}
|
|
39262
39617
|
function createFolderServerPlugins() {
|
|
39263
|
-
return [createNotesPlugin()];
|
|
39618
|
+
return [createNotesPlugin(), createKnlPlugin()];
|
|
39264
39619
|
}
|
|
39265
39620
|
function folderServerRegistry() {
|
|
39266
39621
|
return new Map(createFolderServerPlugins().map((plugin) => [plugin.id, plugin]));
|
|
@@ -39302,7 +39657,7 @@ async function folderServerRegistryAsync() {
|
|
|
39302
39657
|
...server,
|
|
39303
39658
|
async detect(context) {
|
|
39304
39659
|
const detection = await server.detect(context);
|
|
39305
|
-
if (detection.status === "unavailable" &&
|
|
39660
|
+
if (detection.status === "unavailable" && existsSync5(resolve16(context.folder, entry.trigger))) {
|
|
39306
39661
|
return { status: "available" };
|
|
39307
39662
|
}
|
|
39308
39663
|
return detection;
|
|
@@ -39342,6 +39697,7 @@ async function externalApiHandlers() {
|
|
|
39342
39697
|
var defaultPluginRegistryConfig;
|
|
39343
39698
|
var init_plugins = __esm(() => {
|
|
39344
39699
|
init_server3();
|
|
39700
|
+
init_server4();
|
|
39345
39701
|
defaultPluginRegistryConfig = {
|
|
39346
39702
|
plugins: { disable: [], order: [] }
|
|
39347
39703
|
};
|
|
@@ -39349,29 +39705,29 @@ var init_plugins = __esm(() => {
|
|
|
39349
39705
|
|
|
39350
39706
|
// packages/api/src/server/linked-folder-discovery.ts
|
|
39351
39707
|
import { execFile as execFile4 } from "node:child_process";
|
|
39352
|
-
import { existsSync as
|
|
39353
|
-
import { createRequire as
|
|
39354
|
-
import { dirname as
|
|
39355
|
-
import { fileURLToPath as
|
|
39708
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
39709
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
39710
|
+
import { dirname as dirname13, join as join6 } from "node:path";
|
|
39711
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
39356
39712
|
function resolveLocalModule(from = import.meta.url) {
|
|
39357
|
-
const require2 =
|
|
39713
|
+
const require2 = createRequire3(from);
|
|
39358
39714
|
const candidates = [];
|
|
39359
39715
|
try {
|
|
39360
39716
|
candidates.push(require2.resolve("@raggle-ai/local"));
|
|
39361
39717
|
} catch {}
|
|
39362
|
-
let directory =
|
|
39363
|
-
while (
|
|
39364
|
-
if (
|
|
39718
|
+
let directory = dirname13(fileURLToPath3(from));
|
|
39719
|
+
while (dirname13(directory) !== directory) {
|
|
39720
|
+
if (existsSync6(join6(directory, "raggle.json"))) {
|
|
39365
39721
|
for (const candidate of [
|
|
39366
|
-
|
|
39367
|
-
|
|
39722
|
+
join6(dirname13(directory), "raggle-ai-scout", "packages", "local"),
|
|
39723
|
+
join6(directory, "..", "..", "raggle-ai-scout", "packages", "local")
|
|
39368
39724
|
]) {
|
|
39369
39725
|
if (!candidates.includes(candidate))
|
|
39370
39726
|
candidates.push(candidate);
|
|
39371
39727
|
}
|
|
39372
39728
|
break;
|
|
39373
39729
|
}
|
|
39374
|
-
directory =
|
|
39730
|
+
directory = dirname13(directory);
|
|
39375
39731
|
}
|
|
39376
39732
|
for (const candidate of candidates) {
|
|
39377
39733
|
try {
|
|
@@ -39380,7 +39736,7 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
39380
39736
|
return candidate;
|
|
39381
39737
|
} catch {}
|
|
39382
39738
|
}
|
|
39383
|
-
return candidates[0] ??
|
|
39739
|
+
return candidates[0] ?? join6(dirname13(fileURLToPath3(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
|
|
39384
39740
|
}
|
|
39385
39741
|
function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
|
|
39386
39742
|
return new Promise((resolve, reject) => {
|
|
@@ -39569,28 +39925,28 @@ var init_project_discovery = __esm(() => {
|
|
|
39569
39925
|
|
|
39570
39926
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
39571
39927
|
import { createHash as createHash5 } from "node:crypto";
|
|
39572
|
-
import { realpathSync as
|
|
39573
|
-
import { readdir as
|
|
39928
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
39929
|
+
import { readdir as readdir7, readFile as readFile12, realpath as realpath9, stat as stat12 } from "node:fs/promises";
|
|
39574
39930
|
import { homedir as homedir4 } from "node:os";
|
|
39575
39931
|
import {
|
|
39576
39932
|
basename as basename8,
|
|
39577
|
-
dirname as
|
|
39578
|
-
extname as
|
|
39579
|
-
isAbsolute as
|
|
39580
|
-
relative as
|
|
39581
|
-
resolve as
|
|
39582
|
-
sep as
|
|
39933
|
+
dirname as dirname14,
|
|
39934
|
+
extname as extname7,
|
|
39935
|
+
isAbsolute as isAbsolute8,
|
|
39936
|
+
relative as relative14,
|
|
39937
|
+
resolve as resolve17,
|
|
39938
|
+
sep as sep11
|
|
39583
39939
|
} from "node:path";
|
|
39584
39940
|
import { execFile as execFile6 } from "node:child_process";
|
|
39585
39941
|
import local from "@raggle-ai/local";
|
|
39586
39942
|
function metadataFile(path, root) {
|
|
39587
|
-
const candidate =
|
|
39588
|
-
if (!candidate || candidate.startsWith("..") ||
|
|
39943
|
+
const candidate = relative14(root, path);
|
|
39944
|
+
if (!candidate || candidate.startsWith("..") || isAbsolute8(candidate))
|
|
39589
39945
|
return false;
|
|
39590
|
-
const parts = candidate.split(
|
|
39946
|
+
const parts = candidate.split(sep11);
|
|
39591
39947
|
if (parts.some((part) => part === "node_modules"))
|
|
39592
39948
|
return false;
|
|
39593
|
-
return metadataExtensions.has(
|
|
39949
|
+
return metadataExtensions.has(extname7(candidate).toLowerCase());
|
|
39594
39950
|
}
|
|
39595
39951
|
async function configuredAiInputModule(folder) {
|
|
39596
39952
|
const config = folderWorkspace(folder).config;
|
|
@@ -39598,11 +39954,11 @@ async function configuredAiInputModule(folder) {
|
|
|
39598
39954
|
return typeof module === "string" && module.trim() ? module.trim() : null;
|
|
39599
39955
|
}
|
|
39600
39956
|
function folderPath(value, root, lockedRoot) {
|
|
39601
|
-
const folder =
|
|
39957
|
+
const folder = realpathSync3(resolve17(value ?? root));
|
|
39602
39958
|
if (!lockedRoot)
|
|
39603
39959
|
return folder;
|
|
39604
|
-
const path =
|
|
39605
|
-
if (!path || !path.startsWith("..") && !
|
|
39960
|
+
const path = relative14(lockedRoot, folder);
|
|
39961
|
+
if (!path || !path.startsWith("..") && !isAbsolute8(path))
|
|
39606
39962
|
return folder;
|
|
39607
39963
|
throw new Error(`Folder is outside the viewer root: ${folder}`);
|
|
39608
39964
|
}
|
|
@@ -39666,15 +40022,15 @@ function unique(values) {
|
|
|
39666
40022
|
return [...new Set(values)];
|
|
39667
40023
|
}
|
|
39668
40024
|
function isMachineRoot(folder) {
|
|
39669
|
-
return folder === homedir4() ||
|
|
40025
|
+
return folder === homedir4() || dirname14(folder) === folder;
|
|
39670
40026
|
}
|
|
39671
40027
|
async function shallowMetadata(folder) {
|
|
39672
|
-
const entries = await
|
|
40028
|
+
const entries = await readdir7(folder, { withFileTypes: true });
|
|
39673
40029
|
const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
|
|
39674
|
-
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(
|
|
39675
|
-
const path =
|
|
39676
|
-
const details = await
|
|
39677
|
-
const content = await
|
|
40030
|
+
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname7(entry.name).toLowerCase())).map(async (entry) => {
|
|
40031
|
+
const path = resolve17(folder, entry.name);
|
|
40032
|
+
const details = await stat12(path);
|
|
40033
|
+
const content = await readFile12(path, "utf8").catch(() => "");
|
|
39678
40034
|
const title = markdownMetadata(content).title;
|
|
39679
40035
|
return {
|
|
39680
40036
|
activityAt: details.mtimeMs,
|
|
@@ -39687,10 +40043,10 @@ async function shallowMetadata(folder) {
|
|
|
39687
40043
|
};
|
|
39688
40044
|
}));
|
|
39689
40045
|
const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
39690
|
-
id: createHash5("sha1").update(
|
|
40046
|
+
id: createHash5("sha1").update(resolve17(folder, entry.name)).digest("hex"),
|
|
39691
40047
|
name: entry.name,
|
|
39692
40048
|
relativePath: entry.name,
|
|
39693
|
-
worktree:
|
|
40049
|
+
worktree: resolve17(folder, entry.name)
|
|
39694
40050
|
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
39695
40051
|
return {
|
|
39696
40052
|
changes: { items: [] },
|
|
@@ -39717,7 +40073,7 @@ async function loadFolderData(folder) {
|
|
|
39717
40073
|
id: project.id,
|
|
39718
40074
|
worktree: project.worktree,
|
|
39719
40075
|
name: project.name ?? basename8(project.worktree),
|
|
39720
|
-
relativePath:
|
|
40076
|
+
relativePath: relative14(folder, project.worktree)
|
|
39721
40077
|
}))
|
|
39722
40078
|
}
|
|
39723
40079
|
};
|
|
@@ -39725,10 +40081,10 @@ async function loadFolderData(folder) {
|
|
|
39725
40081
|
async function notesFiles(folder, files, origin) {
|
|
39726
40082
|
return {
|
|
39727
40083
|
items: await Promise.all(files.map(async (file) => {
|
|
39728
|
-
const path = file.path ?? (file.relativePath ?
|
|
40084
|
+
const path = file.path ?? (file.relativePath ? resolve17(folder, file.relativePath) : undefined);
|
|
39729
40085
|
if (!path)
|
|
39730
40086
|
return origin ? { ...file, folder: origin } : file;
|
|
39731
|
-
const content = [".markdown", ".md"].includes(
|
|
40087
|
+
const content = [".markdown", ".md"].includes(extname7(path).toLowerCase()) ? await readFile12(path, "utf8").catch(() => "") : "";
|
|
39732
40088
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
39733
40089
|
return {
|
|
39734
40090
|
...file,
|
|
@@ -39742,7 +40098,7 @@ async function notesFiles(folder, files, origin) {
|
|
|
39742
40098
|
async function managedNotesFiles(directory) {
|
|
39743
40099
|
const notes = await listManagedNotes(directory);
|
|
39744
40100
|
return Promise.all(notes.map(async (note) => {
|
|
39745
|
-
const content = await
|
|
40101
|
+
const content = await readFile12(note.path, "utf8").catch(() => "");
|
|
39746
40102
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
39747
40103
|
return {
|
|
39748
40104
|
...note,
|
|
@@ -39753,8 +40109,8 @@ async function managedNotesFiles(directory) {
|
|
|
39753
40109
|
}));
|
|
39754
40110
|
}
|
|
39755
40111
|
function insideFolder(path, root) {
|
|
39756
|
-
const local =
|
|
39757
|
-
return local === "" || !local.startsWith("..") && !
|
|
40112
|
+
const local = relative14(root, path);
|
|
40113
|
+
return local === "" || !local.startsWith("..") && !isAbsolute8(local);
|
|
39758
40114
|
}
|
|
39759
40115
|
async function withoutManagedFiles(items, managedRoot) {
|
|
39760
40116
|
if (!managedRoot)
|
|
@@ -39785,16 +40141,16 @@ async function localProjectSearchCache(cache) {
|
|
|
39785
40141
|
function mediaTarget(folder, file, lockedTarget) {
|
|
39786
40142
|
if (!file)
|
|
39787
40143
|
return;
|
|
39788
|
-
const rootReal =
|
|
39789
|
-
const path =
|
|
40144
|
+
const rootReal = realpathSync3(resolve17(folder));
|
|
40145
|
+
const path = resolve17(folder, file);
|
|
39790
40146
|
let pathReal;
|
|
39791
40147
|
try {
|
|
39792
|
-
pathReal =
|
|
40148
|
+
pathReal = realpathSync3(path);
|
|
39793
40149
|
} catch {
|
|
39794
40150
|
return;
|
|
39795
40151
|
}
|
|
39796
|
-
const rel =
|
|
39797
|
-
if (rel.startsWith("..") ||
|
|
40152
|
+
const rel = relative14(rootReal, pathReal);
|
|
40153
|
+
if (rel.startsWith("..") || isAbsolute8(rel))
|
|
39798
40154
|
return;
|
|
39799
40155
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
39800
40156
|
return;
|
|
@@ -39833,16 +40189,16 @@ function documentError(response) {
|
|
|
39833
40189
|
function documentTarget(folder, file, lockedTarget) {
|
|
39834
40190
|
if (!file)
|
|
39835
40191
|
return;
|
|
39836
|
-
const rootReal =
|
|
39837
|
-
const path =
|
|
40192
|
+
const rootReal = realpathSync3(resolve17(folder));
|
|
40193
|
+
const path = resolve17(folder, file);
|
|
39838
40194
|
let pathReal;
|
|
39839
40195
|
try {
|
|
39840
|
-
pathReal =
|
|
40196
|
+
pathReal = realpathSync3(path);
|
|
39841
40197
|
} catch {
|
|
39842
40198
|
return;
|
|
39843
40199
|
}
|
|
39844
|
-
const rel =
|
|
39845
|
-
if (rel.startsWith("..") ||
|
|
40200
|
+
const rel = relative14(rootReal, pathReal);
|
|
40201
|
+
if (rel.startsWith("..") || isAbsolute8(rel))
|
|
39846
40202
|
return;
|
|
39847
40203
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
39848
40204
|
return;
|
|
@@ -39899,7 +40255,8 @@ async function pluginAvailability(registry, folder, linkedFolders, configWarning
|
|
|
39899
40255
|
error,
|
|
39900
40256
|
icon: plugin.icon,
|
|
39901
40257
|
id: plugin.id,
|
|
39902
|
-
label: plugin.label
|
|
40258
|
+
label: plugin.label,
|
|
40259
|
+
...detection.status === "available" && detection.pages ? { pages: detection.pages } : {}
|
|
39903
40260
|
});
|
|
39904
40261
|
}
|
|
39905
40262
|
} catch {}
|
|
@@ -39914,8 +40271,8 @@ function createFolderApi({
|
|
|
39914
40271
|
remoteAccess = false,
|
|
39915
40272
|
lockedFile
|
|
39916
40273
|
}) {
|
|
39917
|
-
let lockedRoot = restrictToRoot ?
|
|
39918
|
-
const lockedTarget = lockedFile ?
|
|
40274
|
+
let lockedRoot = restrictToRoot ? realpathSync3(root) : undefined;
|
|
40275
|
+
const lockedTarget = lockedFile ? realpathSync3(lockedFile) : undefined;
|
|
39919
40276
|
let basePlugins;
|
|
39920
40277
|
let baseExternalApiHandlers;
|
|
39921
40278
|
const assetFiles = new Map;
|
|
@@ -39953,7 +40310,7 @@ function createFolderApi({
|
|
|
39953
40310
|
const serverHost = server.host;
|
|
39954
40311
|
const remote = remoteAccess || serverHost === true || typeof serverHost === "string" && !isLoopbackServerHost(serverHost);
|
|
39955
40312
|
if (remote)
|
|
39956
|
-
lockedRoot = lockedRoot ??
|
|
40313
|
+
lockedRoot = lockedRoot ?? realpathSync3(root);
|
|
39957
40314
|
const collectionServer = createSchemaCollectionServer(server, {
|
|
39958
40315
|
boundary: lockedRoot,
|
|
39959
40316
|
lockedTarget
|
|
@@ -39979,7 +40336,7 @@ function createFolderApi({
|
|
|
39979
40336
|
if (!folder)
|
|
39980
40337
|
return;
|
|
39981
40338
|
server.emit({
|
|
39982
|
-
data: { path:
|
|
40339
|
+
data: { path: relative14(folder, path) },
|
|
39983
40340
|
event: "kennel:metadata-changed",
|
|
39984
40341
|
type: "custom"
|
|
39985
40342
|
});
|
|
@@ -40062,7 +40419,7 @@ function createFolderApi({
|
|
|
40062
40419
|
"Content-Type": asset.mediaType,
|
|
40063
40420
|
ETag: `"${hash}"`
|
|
40064
40421
|
});
|
|
40065
|
-
response.end(await
|
|
40422
|
+
response.end(await readFile12(asset.path));
|
|
40066
40423
|
} catch (error) {
|
|
40067
40424
|
response.writeHead(404, {
|
|
40068
40425
|
"Content-Type": "text/plain; charset=utf-8"
|
|
@@ -40134,14 +40491,14 @@ function createFolderApi({
|
|
|
40134
40491
|
}
|
|
40135
40492
|
if (url.pathname === "/api/documents") {
|
|
40136
40493
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
40137
|
-
const target = lockedTarget ? documentTarget(folder,
|
|
40494
|
+
const target = lockedTarget ? documentTarget(folder, relative14(folder, lockedTarget), lockedTarget) : undefined;
|
|
40138
40495
|
const dataset = lockedTarget ? {
|
|
40139
40496
|
root: folder,
|
|
40140
40497
|
documents: target ? [
|
|
40141
40498
|
{
|
|
40142
|
-
path:
|
|
40499
|
+
path: relative14(folder, target.path),
|
|
40143
40500
|
kind: target.kind,
|
|
40144
|
-
content: previewTextKind(target.kind) ? await
|
|
40501
|
+
content: previewTextKind(target.kind) ? await readFile12(target.path, "utf8") : ""
|
|
40145
40502
|
}
|
|
40146
40503
|
] : []
|
|
40147
40504
|
} : await readFolder(folder);
|
|
@@ -40157,7 +40514,7 @@ function createFolderApi({
|
|
|
40157
40514
|
documentError(response);
|
|
40158
40515
|
return;
|
|
40159
40516
|
}
|
|
40160
|
-
const details = await
|
|
40517
|
+
const details = await stat12(target.path);
|
|
40161
40518
|
if (!details.isFile()) {
|
|
40162
40519
|
documentError(response);
|
|
40163
40520
|
return;
|
|
@@ -40169,16 +40526,16 @@ function createFolderApi({
|
|
|
40169
40526
|
return;
|
|
40170
40527
|
}
|
|
40171
40528
|
watch(folder);
|
|
40172
|
-
const content = previewTextKind(target.kind) ? await
|
|
40529
|
+
const content = previewTextKind(target.kind) ? await readFile12(target.path, "utf8") : "";
|
|
40173
40530
|
sendContract(response, url, {
|
|
40174
40531
|
content,
|
|
40175
40532
|
kind: target.kind,
|
|
40176
|
-
path:
|
|
40533
|
+
path: relative14(target.root, target.path),
|
|
40177
40534
|
root: folder,
|
|
40178
40535
|
...previewTextKind(target.kind) ? {} : {
|
|
40179
40536
|
rawUrl: `/api/raw?${new URLSearchParams({
|
|
40180
40537
|
path: folder,
|
|
40181
|
-
file:
|
|
40538
|
+
file: relative14(target.root, target.path)
|
|
40182
40539
|
})}`
|
|
40183
40540
|
}
|
|
40184
40541
|
}, {
|
|
@@ -40198,7 +40555,7 @@ function createFolderApi({
|
|
|
40198
40555
|
documentError(response);
|
|
40199
40556
|
return;
|
|
40200
40557
|
}
|
|
40201
|
-
const details = await
|
|
40558
|
+
const details = await stat12(target.path);
|
|
40202
40559
|
if (!details.isFile()) {
|
|
40203
40560
|
documentError(response);
|
|
40204
40561
|
return;
|
|
@@ -40222,7 +40579,7 @@ function createFolderApi({
|
|
|
40222
40579
|
"Content-Disposition": `attachment; filename="${basename8(target.path)}"`
|
|
40223
40580
|
} : {}
|
|
40224
40581
|
});
|
|
40225
|
-
response.end(await
|
|
40582
|
+
response.end(await readFile12(target.path));
|
|
40226
40583
|
} catch (error) {
|
|
40227
40584
|
documentError(response);
|
|
40228
40585
|
}
|
|
@@ -40236,7 +40593,7 @@ function createFolderApi({
|
|
|
40236
40593
|
documentError(response);
|
|
40237
40594
|
return;
|
|
40238
40595
|
}
|
|
40239
|
-
const details = await
|
|
40596
|
+
const details = await stat12(target.path);
|
|
40240
40597
|
if (!details.isFile()) {
|
|
40241
40598
|
documentError(response);
|
|
40242
40599
|
return;
|
|
@@ -40248,7 +40605,7 @@ function createFolderApi({
|
|
|
40248
40605
|
return;
|
|
40249
40606
|
}
|
|
40250
40607
|
watch(folder);
|
|
40251
|
-
const data = await
|
|
40608
|
+
const data = await readFile12(target.path);
|
|
40252
40609
|
const baseHeaders = {
|
|
40253
40610
|
"Accept-Ranges": "bytes",
|
|
40254
40611
|
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
@@ -40357,6 +40714,19 @@ function createFolderApi({
|
|
|
40357
40714
|
}))
|
|
40358
40715
|
return;
|
|
40359
40716
|
}
|
|
40717
|
+
if (url.pathname === "/api/delete-note") {
|
|
40718
|
+
if (remote) {
|
|
40719
|
+
response.writeHead(403, {
|
|
40720
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
40721
|
+
});
|
|
40722
|
+
response.end("Host actions are disabled for remote access");
|
|
40723
|
+
return;
|
|
40724
|
+
}
|
|
40725
|
+
await runApiRoute(request, response, url, deleteNoteRoute, {
|
|
40726
|
+
authorize: (folder) => folderPath(folder, root, lockedRoot)
|
|
40727
|
+
});
|
|
40728
|
+
return;
|
|
40729
|
+
}
|
|
40360
40730
|
if (url.pathname === "/api/create-agent-file") {
|
|
40361
40731
|
if (remote) {
|
|
40362
40732
|
response.writeHead(403, {
|
|
@@ -40411,7 +40781,7 @@ function createFolderApi({
|
|
|
40411
40781
|
workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
|
|
40412
40782
|
const files = [
|
|
40413
40783
|
workspace.configPath,
|
|
40414
|
-
|
|
40784
|
+
resolve17(workspace.root, "raggle.json")
|
|
40415
40785
|
].filter((path) => Boolean(path));
|
|
40416
40786
|
for (const path of files)
|
|
40417
40787
|
contexts.add(path);
|
|
@@ -40552,7 +40922,7 @@ function createFolderApi({
|
|
|
40552
40922
|
}
|
|
40553
40923
|
};
|
|
40554
40924
|
}
|
|
40555
|
-
var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts, canonicalPath = (path) =>
|
|
40925
|
+
var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts, canonicalPath = (path) => realpath9(path).catch(() => path);
|
|
40556
40926
|
var init_folder_metadata_plugin = __esm(() => {
|
|
40557
40927
|
init_contracts();
|
|
40558
40928
|
init_markdown();
|
|
@@ -40564,6 +40934,7 @@ var init_folder_metadata_plugin = __esm(() => {
|
|
|
40564
40934
|
init_server2();
|
|
40565
40935
|
init_store();
|
|
40566
40936
|
init_entity_folder();
|
|
40937
|
+
init_delete_note();
|
|
40567
40938
|
init_document_edit();
|
|
40568
40939
|
init_create_agent_file();
|
|
40569
40940
|
init_open_path();
|
|
@@ -40616,39 +40987,39 @@ var init_folder_metadata_plugin = __esm(() => {
|
|
|
40616
40987
|
import {
|
|
40617
40988
|
createServer
|
|
40618
40989
|
} from "node:http";
|
|
40619
|
-
import { readFile as
|
|
40620
|
-
import { extname as
|
|
40990
|
+
import { readFile as readFile13, realpath as realpath10, stat as stat13 } from "node:fs/promises";
|
|
40991
|
+
import { extname as extname8, isAbsolute as isAbsolute9, relative as relative15, resolve as resolve18 } from "node:path";
|
|
40621
40992
|
async function sendFile(request, response, directory, pathname, fallback) {
|
|
40622
|
-
const root = await
|
|
40993
|
+
const root = await realpath10(directory);
|
|
40623
40994
|
const inside = (path) => {
|
|
40624
|
-
const local =
|
|
40625
|
-
return local !== ".." && !local.startsWith("../") && !
|
|
40995
|
+
const local = relative15(root, path);
|
|
40996
|
+
return local !== ".." && !local.startsWith("../") && !isAbsolute9(local);
|
|
40626
40997
|
};
|
|
40627
|
-
let target =
|
|
40998
|
+
let target = resolve18(root, `.${decodeURIComponent(pathname)}`);
|
|
40628
40999
|
if (!inside(target)) {
|
|
40629
41000
|
response.writeHead(403);
|
|
40630
41001
|
response.end();
|
|
40631
41002
|
return;
|
|
40632
41003
|
}
|
|
40633
|
-
if (!(await
|
|
41004
|
+
if (!(await stat13(target).catch(() => null))?.isFile()) {
|
|
40634
41005
|
if (!fallback) {
|
|
40635
41006
|
response.writeHead(404);
|
|
40636
41007
|
response.end();
|
|
40637
41008
|
return;
|
|
40638
41009
|
}
|
|
40639
|
-
target =
|
|
41010
|
+
target = resolve18(root, fallback);
|
|
40640
41011
|
}
|
|
40641
|
-
target = await
|
|
41012
|
+
target = await realpath10(target);
|
|
40642
41013
|
if (!inside(target)) {
|
|
40643
41014
|
response.writeHead(403);
|
|
40644
41015
|
response.end();
|
|
40645
41016
|
return;
|
|
40646
41017
|
}
|
|
40647
41018
|
response.writeHead(200, {
|
|
40648
|
-
"Content-Type": contentTypes[
|
|
41019
|
+
"Content-Type": contentTypes[extname8(target)] ?? "application/octet-stream",
|
|
40649
41020
|
"X-Content-Type-Options": "nosniff"
|
|
40650
41021
|
});
|
|
40651
|
-
response.end(request.method === "HEAD" ? undefined : await
|
|
41022
|
+
response.end(request.method === "HEAD" ? undefined : await readFile13(target));
|
|
40652
41023
|
}
|
|
40653
41024
|
function createKennelApi(options) {
|
|
40654
41025
|
const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
|
|
@@ -40800,7 +41171,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
40800
41171
|
};
|
|
40801
41172
|
}
|
|
40802
41173
|
var contentTypes;
|
|
40803
|
-
var
|
|
41174
|
+
var init_server5 = __esm(() => {
|
|
40804
41175
|
init_watcher();
|
|
40805
41176
|
init_folder_metadata_plugin();
|
|
40806
41177
|
contentTypes = {
|
|
@@ -40820,18 +41191,31 @@ var init_server4 = __esm(() => {
|
|
|
40820
41191
|
};
|
|
40821
41192
|
});
|
|
40822
41193
|
|
|
41194
|
+
// packages/preview/src/share.ts
|
|
41195
|
+
import { dirname as dirname16, resolve as resolve20, join as join7 } from "node:path";
|
|
41196
|
+
import { homedir as homedir5 } from "node:os";
|
|
41197
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
41198
|
+
import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
|
|
41199
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
41200
|
+
|
|
40823
41201
|
// packages/preview/src/viewer.ts
|
|
40824
|
-
|
|
40825
|
-
|
|
41202
|
+
init_document_kinds();
|
|
41203
|
+
init_viewer_route();
|
|
41204
|
+
init_runtime();
|
|
41205
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
41206
|
+
import { existsSync as existsSync7, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
|
|
40826
41207
|
import { createServer as createServer2 } from "node:net";
|
|
40827
|
-
import { dirname as
|
|
40828
|
-
import { fileURLToPath as
|
|
41208
|
+
import { dirname as dirname15, isAbsolute as isAbsolute10, relative as relative16, resolve as resolve19 } from "node:path";
|
|
41209
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
41210
|
+
var loopbackHosts = new Set([
|
|
41211
|
+
"127.0.0.1",
|
|
41212
|
+
"::1",
|
|
41213
|
+
"::ffff:127.0.0.1",
|
|
41214
|
+
"localhost"
|
|
41215
|
+
]);
|
|
40829
41216
|
function isLoopbackHost(host) {
|
|
40830
41217
|
return loopbackHosts.has(host) || host.startsWith("127.");
|
|
40831
41218
|
}
|
|
40832
|
-
function encodeBase64Url2(value) {
|
|
40833
|
-
return Buffer.from(value, "utf8").toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
40834
|
-
}
|
|
40835
41219
|
function splitViewerArgs(input) {
|
|
40836
41220
|
let hasHost = false;
|
|
40837
41221
|
let hasRoot = false;
|
|
@@ -40907,8 +41291,8 @@ function splitViewerArgs(input) {
|
|
|
40907
41291
|
}
|
|
40908
41292
|
if (arg.startsWith("-"))
|
|
40909
41293
|
throw new Error(`Unknown viewer option: ${arg}`);
|
|
40910
|
-
const candidate =
|
|
40911
|
-
const isDirectory =
|
|
41294
|
+
const candidate = resolve19(arg);
|
|
41295
|
+
const isDirectory = existsSync7(candidate) && statSync3(candidate).isDirectory();
|
|
40912
41296
|
if (isDirectory) {
|
|
40913
41297
|
if (hasRoot)
|
|
40914
41298
|
throw new Error("kennel accepts one folder");
|
|
@@ -40918,7 +41302,7 @@ function splitViewerArgs(input) {
|
|
|
40918
41302
|
}
|
|
40919
41303
|
if (hasRoot)
|
|
40920
41304
|
throw new Error("kennel accepts one folder");
|
|
40921
|
-
options.root =
|
|
41305
|
+
options.root = dirname15(candidate);
|
|
40922
41306
|
options.target = candidate;
|
|
40923
41307
|
hasRoot = true;
|
|
40924
41308
|
}
|
|
@@ -40937,7 +41321,7 @@ function splitViewerArgs(input) {
|
|
|
40937
41321
|
}
|
|
40938
41322
|
if (options.lan)
|
|
40939
41323
|
options.host = "0.0.0.0";
|
|
40940
|
-
if (!
|
|
41324
|
+
if (!existsSync7(options.root) || !statSync3(options.root).isDirectory()) {
|
|
40941
41325
|
throw new Error(`Folder not found: ${options.root}`);
|
|
40942
41326
|
}
|
|
40943
41327
|
if (options.target) {
|
|
@@ -40954,24 +41338,19 @@ function splitViewerArgs(input) {
|
|
|
40954
41338
|
function popUrl(host, port, options) {
|
|
40955
41339
|
const scheme = options.tailscale ? "https" : "http";
|
|
40956
41340
|
const authority = options.tailscale ? host : `${host}:${port}`;
|
|
40957
|
-
|
|
40958
|
-
|
|
40959
|
-
|
|
40960
|
-
|
|
40961
|
-
}
|
|
40962
|
-
url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(relative14(options.root, options.target))}`;
|
|
40963
|
-
if (options.watch)
|
|
40964
|
-
url.searchParams.set("watch", "1");
|
|
40965
|
-
return url.href;
|
|
41341
|
+
return folderUrl(`${scheme}://${authority}/`, options.root, {
|
|
41342
|
+
document: options.target ? relative16(options.root, options.target) : undefined,
|
|
41343
|
+
watch: options.watch
|
|
41344
|
+
}).href;
|
|
40966
41345
|
}
|
|
40967
41346
|
function viewerPortWasExplicit(input) {
|
|
40968
41347
|
return input.some((arg) => arg === "--port" || arg.startsWith("--port="));
|
|
40969
41348
|
}
|
|
40970
41349
|
function isSameOrDescendant(parent, child) {
|
|
40971
|
-
const parentReal =
|
|
40972
|
-
const childReal =
|
|
40973
|
-
const childRelative =
|
|
40974
|
-
return childRelative === "" || !childRelative.startsWith("..") && !
|
|
41350
|
+
const parentReal = realpathSync4(parent);
|
|
41351
|
+
const childReal = realpathSync4(child);
|
|
41352
|
+
const childRelative = relative16(parentReal, childReal);
|
|
41353
|
+
return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute10(childRelative);
|
|
40975
41354
|
}
|
|
40976
41355
|
async function runningViewer(url) {
|
|
40977
41356
|
try {
|
|
@@ -41004,13 +41383,13 @@ async function availableViewerPort(start, host) {
|
|
|
41004
41383
|
throw new Error(`Kennel could not find a free port at or above ${start}`);
|
|
41005
41384
|
}
|
|
41006
41385
|
function lanHost2() {
|
|
41007
|
-
const route =
|
|
41386
|
+
const route = execFileSync2("/sbin/route", ["-n", "get", "default"], {
|
|
41008
41387
|
encoding: "utf8"
|
|
41009
41388
|
});
|
|
41010
41389
|
const device = route.match(/interface:\s+(\S+)/)?.[1];
|
|
41011
41390
|
if (!device)
|
|
41012
41391
|
throw new Error("kennel could not find the local network interface");
|
|
41013
|
-
const host =
|
|
41392
|
+
const host = execFileSync2("/usr/sbin/ipconfig", ["getifaddr", device], {
|
|
41014
41393
|
encoding: "utf8"
|
|
41015
41394
|
}).trim();
|
|
41016
41395
|
if (!host)
|
|
@@ -41018,7 +41397,7 @@ function lanHost2() {
|
|
|
41018
41397
|
return host;
|
|
41019
41398
|
}
|
|
41020
41399
|
function tailscaleHost() {
|
|
41021
|
-
const status = JSON.parse(
|
|
41400
|
+
const status = JSON.parse(execFileSync2("tailscale", ["status", "--json"], { encoding: "utf8" }));
|
|
41022
41401
|
const host = typeof status.Self?.DNSName === "string" ? status.Self.DNSName.replace(/\.$/, "") : "";
|
|
41023
41402
|
if (host)
|
|
41024
41403
|
return host;
|
|
@@ -41032,13 +41411,13 @@ async function viewer(input) {
|
|
|
41032
41411
|
await hotViewer(options);
|
|
41033
41412
|
return;
|
|
41034
41413
|
}
|
|
41035
|
-
const web =
|
|
41036
|
-
if (!
|
|
41414
|
+
const web = resolve19(dirname15(fileURLToPath4(import.meta.url)), "../../web");
|
|
41415
|
+
if (!existsSync7(resolve19(web, "index.html"))) {
|
|
41037
41416
|
throw new Error(`Kennel web app not found. Run the CLI build first.`);
|
|
41038
41417
|
}
|
|
41039
41418
|
const remote = options.tailscale || !isLoopbackHost(options.host);
|
|
41040
|
-
const lockedTarget = options.target ?
|
|
41041
|
-
await Promise.resolve().then(() =>
|
|
41419
|
+
const lockedTarget = options.target ? realpathSync4(options.target) : undefined;
|
|
41420
|
+
await Promise.resolve().then(() => init_server5());
|
|
41042
41421
|
const displayHost = options.tailscale ? tailscaleHost() : options.lan ? lanHost2() : options.host;
|
|
41043
41422
|
if (remote)
|
|
41044
41423
|
process.env.KENNEL_VIEWER_HOSTS = [
|
|
@@ -41049,18 +41428,9 @@ async function viewer(input) {
|
|
|
41049
41428
|
const existing = await runningViewer(initialViewerUrl);
|
|
41050
41429
|
if (existing && isSameOrDescendant(existing.root, options.root)) {
|
|
41051
41430
|
const viewerUrl = popUrl(displayHost, options.port, options);
|
|
41052
|
-
|
|
41053
|
-
await Promise.resolve().then(() => init_share());
|
|
41054
|
-
return startPreviewShare(options);
|
|
41055
|
-
});
|
|
41431
|
+
console.log(viewerUrl);
|
|
41056
41432
|
if (options.open)
|
|
41057
41433
|
openPreview(viewerUrl);
|
|
41058
|
-
try {
|
|
41059
|
-
if (terminal.interactive)
|
|
41060
|
-
await terminal.closed;
|
|
41061
|
-
} finally {
|
|
41062
|
-
terminal.close();
|
|
41063
|
-
}
|
|
41064
41434
|
return;
|
|
41065
41435
|
}
|
|
41066
41436
|
if (!portExplicit) {
|
|
@@ -41077,38 +41447,25 @@ async function viewer(input) {
|
|
|
41077
41447
|
try {
|
|
41078
41448
|
await api.listen(Number(options.port));
|
|
41079
41449
|
if (options.tailscale) {
|
|
41080
|
-
|
|
41450
|
+
execFileSync2("tailscale", ["serve", "--bg", "--yes", options.port]);
|
|
41081
41451
|
}
|
|
41082
41452
|
const viewerUrl = popUrl(displayHost, options.port, options);
|
|
41083
|
-
const
|
|
41084
|
-
await Promise.resolve().then(() => init_share());
|
|
41085
|
-
return startPreviewShare(options);
|
|
41086
|
-
});
|
|
41087
|
-
if (options.open)
|
|
41088
|
-
openPreview(viewerUrl);
|
|
41453
|
+
const lifetime = viewerLifetime();
|
|
41089
41454
|
try {
|
|
41090
|
-
|
|
41455
|
+
console.log(viewerUrl);
|
|
41456
|
+
if (options.open)
|
|
41457
|
+
openPreview(viewerUrl);
|
|
41458
|
+
await lifetime.closed;
|
|
41091
41459
|
} finally {
|
|
41092
|
-
|
|
41460
|
+
lifetime.close();
|
|
41093
41461
|
}
|
|
41094
41462
|
} finally {
|
|
41095
41463
|
await api.close();
|
|
41096
41464
|
}
|
|
41097
41465
|
if (options.tailscale) {
|
|
41098
|
-
|
|
41466
|
+
execFileSync2("tailscale", ["serve", "--https=443", "off"]);
|
|
41099
41467
|
}
|
|
41100
41468
|
}
|
|
41101
|
-
var loopbackHosts;
|
|
41102
|
-
var init_viewer = __esm(() => {
|
|
41103
|
-
init_document_kinds();
|
|
41104
|
-
init_terminal();
|
|
41105
|
-
loopbackHosts = new Set([
|
|
41106
|
-
"127.0.0.1",
|
|
41107
|
-
"::1",
|
|
41108
|
-
"::ffff:127.0.0.1",
|
|
41109
|
-
"localhost"
|
|
41110
|
-
]);
|
|
41111
|
-
});
|
|
41112
41469
|
|
|
41113
41470
|
// packages/preview/src/protected-share.ts
|
|
41114
41471
|
import { createHash as createHash6, randomBytes, timingSafeEqual } from "node:crypto";
|
|
@@ -41169,11 +41526,11 @@ async function startProtectedShare(handle) {
|
|
|
41169
41526
|
throw error;
|
|
41170
41527
|
}
|
|
41171
41528
|
}
|
|
41172
|
-
var init_protected_share = () => {};
|
|
41173
41529
|
|
|
41174
41530
|
// packages/preview/src/tailscale-share.ts
|
|
41175
41531
|
import { execFile as execFile7 } from "node:child_process";
|
|
41176
41532
|
import { promisify as promisify2 } from "node:util";
|
|
41533
|
+
var run2 = promisify2(execFile7);
|
|
41177
41534
|
async function serveConfig() {
|
|
41178
41535
|
const { stdout } = await run2("tailscale", ["serve", "status", "--json"]);
|
|
41179
41536
|
return JSON.parse(stdout);
|
|
@@ -41213,17 +41570,9 @@ async function startTailscaleProxy(host, port) {
|
|
|
41213
41570
|
throw error;
|
|
41214
41571
|
}
|
|
41215
41572
|
}
|
|
41216
|
-
var run2;
|
|
41217
|
-
var init_tailscale_share = __esm(() => {
|
|
41218
|
-
run2 = promisify2(execFile7);
|
|
41219
|
-
});
|
|
41220
41573
|
|
|
41221
41574
|
// packages/preview/src/share.ts
|
|
41222
|
-
|
|
41223
|
-
import { homedir as homedir5 } from "node:os";
|
|
41224
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
41225
|
-
import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
|
|
41226
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
41575
|
+
var plainViewerPort = "5180";
|
|
41227
41576
|
function splitShareArgs(input) {
|
|
41228
41577
|
const options = {
|
|
41229
41578
|
folder: undefined,
|
|
@@ -41275,10 +41624,10 @@ function splitShareArgs(input) {
|
|
|
41275
41624
|
if (options.port && !/^\d+$/.test(options.port)) {
|
|
41276
41625
|
throw new Error("kennel share requires a numeric port");
|
|
41277
41626
|
}
|
|
41278
|
-
if (folder && !
|
|
41627
|
+
if (folder && !existsSync8(folder)) {
|
|
41279
41628
|
throw new Error(`Folder not found: ${folder}`);
|
|
41280
41629
|
}
|
|
41281
|
-
options.folder = folder ?
|
|
41630
|
+
options.folder = folder ? resolve20(folder) : undefined;
|
|
41282
41631
|
return options;
|
|
41283
41632
|
}
|
|
41284
41633
|
async function runningViewerRoot(port = plainViewerPort) {
|
|
@@ -41298,27 +41647,27 @@ async function runningViewerRoot(port = plainViewerPort) {
|
|
|
41298
41647
|
return;
|
|
41299
41648
|
}
|
|
41300
41649
|
async function startPreviewShare(target) {
|
|
41301
|
-
const root =
|
|
41302
|
-
const configPath =
|
|
41650
|
+
const root = realpathSync5(target.root);
|
|
41651
|
+
const configPath = join7(process.env.XDG_CONFIG_HOME?.trim() || join7(homedir5(), ".config"), "kennel", "config.json");
|
|
41303
41652
|
let configuredPassword = false;
|
|
41304
41653
|
try {
|
|
41305
41654
|
const config = JSON.parse(readFileSync2(configPath, "utf8"));
|
|
41306
41655
|
configuredPassword = config.linkSharing?.password === true;
|
|
41307
41656
|
} catch {}
|
|
41308
41657
|
const password = target.password ?? configuredPassword;
|
|
41309
|
-
const web =
|
|
41310
|
-
if (!
|
|
41658
|
+
const web = resolve20(dirname16(fileURLToPath5(import.meta.url)), "../../web");
|
|
41659
|
+
if (!existsSync8(resolve20(web, "index.html"))) {
|
|
41311
41660
|
throw new Error("Kennel web app not found. Run the CLI build first.");
|
|
41312
41661
|
}
|
|
41313
41662
|
const host = await tailscaleHostname();
|
|
41314
|
-
await Promise.resolve().then(() =>
|
|
41663
|
+
await Promise.resolve().then(() => init_server5());
|
|
41315
41664
|
process.env.KENNEL_VIEWER_HOSTS = [process.env.KENNEL_VIEWER_HOSTS, host].filter(Boolean).join(",");
|
|
41316
41665
|
const api = createKennelApi({
|
|
41317
41666
|
root,
|
|
41318
41667
|
host: "127.0.0.1",
|
|
41319
41668
|
remoteAccess: true,
|
|
41320
41669
|
restrictToRoot: true,
|
|
41321
|
-
lockedFile: target.target ?
|
|
41670
|
+
lockedFile: target.target ? realpathSync5(target.target) : undefined,
|
|
41322
41671
|
webRoot: web
|
|
41323
41672
|
});
|
|
41324
41673
|
let shared;
|
|
@@ -41398,7 +41747,7 @@ async function share(input) {
|
|
|
41398
41747
|
console.log(`Sharing ${folder}`);
|
|
41399
41748
|
if (options.dryRun) {
|
|
41400
41749
|
const port = options.port ?? "5181";
|
|
41401
|
-
const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}
|
|
41750
|
+
const url = options.tailscale ? `https://<tailnet-name>.ts.net/folder/${Buffer.from(folder).toString("base64url")}` : popUrl(lanHost2(), port, { dryRun: true, host: "0.0.0.0", hot: false, lan: true, open: false, root: folder, port, tailscale: false, watch: false, password: false });
|
|
41402
41751
|
console.log(url);
|
|
41403
41752
|
console.log(`kennel serve --static ${options.tailscale ? "--tailscale" : "--lan"}${options.password ? " --password" : ""}${options.port ? ` --port ${options.port}` : ""} ${folder}`);
|
|
41404
41753
|
return;
|
|
@@ -41411,14 +41760,6 @@ async function share(input) {
|
|
|
41411
41760
|
folder
|
|
41412
41761
|
]);
|
|
41413
41762
|
}
|
|
41414
|
-
var plainViewerPort = "5180";
|
|
41415
|
-
var init_share = __esm(() => {
|
|
41416
|
-
init_viewer();
|
|
41417
|
-
init_protected_share();
|
|
41418
|
-
init_tailscale_share();
|
|
41419
|
-
});
|
|
41420
|
-
init_share();
|
|
41421
|
-
|
|
41422
41763
|
export {
|
|
41423
41764
|
plainViewerPort,
|
|
41424
41765
|
runningViewerRoot,
|