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.
Files changed (88) hide show
  1. package/dist/core/api-client.d.ts +1 -1
  2. package/dist/core/api-client.js +69 -3
  3. package/dist/core/runtime.js +649 -140
  4. package/dist/llm/worker.js +11 -2
  5. package/dist/notes/command.js +20 -4
  6. package/dist/package/preview.d.ts +1 -1
  7. package/dist/package/preview.js +881 -540
  8. package/dist/site/hot-viewer.js +4 -4
  9. package/dist/site/server-plugins.js +468 -30
  10. package/dist/site/viewer.d.ts +0 -3
  11. package/dist/site/viewer.js +11 -41
  12. package/dist/tui/launcher.d.ts +11 -0
  13. package/dist/{site/terminal.js → tui/launcher.js} +60 -15
  14. package/package.json +2 -2
  15. package/web/assets/{abnfDiagram-VRR7QNED-ooA_TyhC.js → abnfDiagram-VRR7QNED-BOm3iBSE.js} +1 -1
  16. package/web/assets/{arc-DqQkY48J.js → arc-D_zfnjJW.js} +1 -1
  17. package/web/assets/{architectureDiagram-ZJ3FMSHR-DaXzRGP0.js → architectureDiagram-ZJ3FMSHR-CTXAbDtC.js} +1 -1
  18. package/web/assets/ask-tray-CT6DQRTC-DfTc3bnC.js +1 -0
  19. package/web/assets/{blockDiagram-677ZJIJ3-Br5Y_dbP.js → blockDiagram-677ZJIJ3-CcPWOAKO.js} +1 -1
  20. package/web/assets/{c4Diagram-LMCZKHZV-CWIy0S0N.js → c4Diagram-LMCZKHZV-B_eNO2-f.js} +1 -1
  21. package/web/assets/channel-Dlb_u_Yk.js +1 -0
  22. package/web/assets/{chunk-2Q5K7J3B-8dxcuNwr.js → chunk-2Q5K7J3B-BtQX_f5i.js} +1 -1
  23. package/web/assets/{chunk-32BRIVSS-DB27hz0Y.js → chunk-32BRIVSS-BBtfsm8_.js} +1 -1
  24. package/web/assets/{chunk-5VM5RSS4-CTNxGtbA.js → chunk-5VM5RSS4-CG0bf9ML.js} +1 -1
  25. package/web/assets/{chunk-EX3LRPZG-oKNLktOC.js → chunk-EX3LRPZG-DS2H7ilP.js} +1 -1
  26. package/web/assets/{chunk-JWPE2WC7-DeQNmWoS.js → chunk-JWPE2WC7-CGLMMKkr.js} +1 -1
  27. package/web/assets/{chunk-MOJQB5TN-BrW33Mrb.js → chunk-MOJQB5TN-C76lqDkU.js} +1 -1
  28. package/web/assets/{chunk-RYQCIY6F-DEkTU6j-.js → chunk-RYQCIY6F-ZY-EzC5l.js} +1 -1
  29. package/web/assets/{chunk-V7JOEXUC-BuDLgwdB.js → chunk-V7JOEXUC-sBv90hIh.js} +1 -1
  30. package/web/assets/{chunk-VR4S4FIN-BsNgrrKe.js → chunk-VR4S4FIN-jW9aFTp3.js} +1 -1
  31. package/web/assets/{chunk-XXDRQBXY-DjT1Bydl.js → chunk-XXDRQBXY-FPtE6zce.js} +1 -1
  32. package/web/assets/classDiagram-OUVF2IWQ-CinFzRGQ.js +1 -0
  33. package/web/assets/classDiagram-v2-EOCWNBFH-CinFzRGQ.js +1 -0
  34. package/web/assets/client-5Q4VI7ZT-DYH5SwVp.js +52 -0
  35. package/web/assets/{cose-bilkent-JH36ORCC-BtAklxrd.js → cose-bilkent-JH36ORCC-NdJGLzeJ.js} +1 -1
  36. package/web/assets/{cynefin-VYW2F7L2-DabtZWZy.js → cynefin-VYW2F7L2-D-o_obcW.js} +1 -1
  37. package/web/assets/{cynefinDiagram-TSTJHNR4-DVk8uQ8L.js → cynefinDiagram-TSTJHNR4-RA4QYrtB.js} +1 -1
  38. package/web/assets/{dagre-VKFMJZFB-CiAbBCor.js → dagre-VKFMJZFB-Bq4DfO16.js} +1 -1
  39. package/web/assets/{diagram-FQU43EPY-DWp-OzJz.js → diagram-FQU43EPY-bPnACaba.js} +1 -1
  40. package/web/assets/{diagram-G47NLZAW-YDllS_1P.js → diagram-G47NLZAW-cPca5hvp.js} +1 -1
  41. package/web/assets/{diagram-NH7WQ7WH-BH3l4yRj.js → diagram-NH7WQ7WH-DHyP34H8.js} +1 -1
  42. package/web/assets/{diagram-OA4YK3LP-CszBvxdr.js → diagram-OA4YK3LP-hVk7mHu1.js} +1 -1
  43. package/web/assets/{diagram-WEI45ONY-bEhZeER6.js → diagram-WEI45ONY-Cd3AEJO3.js} +1 -1
  44. package/web/assets/document-view-VX2ZTQM5-CDdtofzz.js +24 -0
  45. package/web/assets/{ebnfDiagram-CCIWWBDH-DlpoUUO5.js → ebnfDiagram-CCIWWBDH-BxtDx2MR.js} +1 -1
  46. package/web/assets/{entity-search-page-YP4SPMBC-WryWZNPk.js → entity-search-page-RIYQYIJJ-IvZzjXvt.js} +5 -5
  47. package/web/assets/{erDiagram-Q63AITRT-DROgehIM.js → erDiagram-Q63AITRT-CtdOkrpo.js} +1 -1
  48. package/web/assets/{flowDiagram-23GEKE2U-63pYiBzl.js → flowDiagram-23GEKE2U-BDHfaJo6.js} +1 -1
  49. package/web/assets/{ganttDiagram-NO4QXBWP-tc34wKHJ.js → ganttDiagram-NO4QXBWP-BhV7JFJA.js} +1 -1
  50. package/web/assets/{gitGraphDiagram-IHSO6WYX-GOjqt583.js → gitGraphDiagram-IHSO6WYX-BVRBRDVk.js} +1 -1
  51. package/web/assets/index-BxCXNDe6.css +1 -0
  52. package/web/assets/index-Cl-kcrk3.js +502 -0
  53. package/web/assets/{infoDiagram-FWYZ7A6U-CD5BwkLQ.js → infoDiagram-FWYZ7A6U-BKuFQe6t.js} +1 -1
  54. package/web/assets/{ishikawaDiagram-FXEZZL3T-yIweY6wR.js → ishikawaDiagram-FXEZZL3T-lNbhlHEo.js} +1 -1
  55. package/web/assets/{journeyDiagram-5HDEW3XC-D3MeUGvn.js → journeyDiagram-5HDEW3XC-CoGf_pSm.js} +1 -1
  56. package/web/assets/{kanban-definition-HUTT4EX6-CnZ5N79y.js → kanban-definition-HUTT4EX6-Cgp0WGrq.js} +1 -1
  57. package/web/assets/{linear-s5G6iOMz.js → linear-B3UMk7TK.js} +1 -1
  58. package/web/assets/{linked-session-header-DOEX4FYM-CtTOXcZn.js → linked-session-header-7BUTWROP-Coqn-Dz0.js} +1 -1
  59. package/web/assets/{mermaid.core-BpUKp9r_.js → mermaid.core-6bZ5bkAq.js} +4 -4
  60. package/web/assets/{mindmap-definition-LN4V7U3C-Bp2QH5sN.js → mindmap-definition-LN4V7U3C-BowQNgIG.js} +1 -1
  61. package/web/assets/{pegDiagram-2B236MQR-lx1jDUmt.js → pegDiagram-2B236MQR-DrhWFvfI.js} +1 -1
  62. package/web/assets/{pieDiagram-ENE6RG2P--kf2YNbw.js → pieDiagram-ENE6RG2P-DNOg53mu.js} +1 -1
  63. package/web/assets/{plugin-host-modules-BKbxlu7U.js → plugin-host-modules-NvyHieUX.js} +26 -26
  64. package/web/assets/{project-search-page-TZG7AZBQ-3W40ayYn.js → project-search-page-O6YRLDGU-CFA4lf0c.js} +2 -2
  65. package/web/assets/{quadrantDiagram-ABIIQ3AL-Vr7zWQYV.js → quadrantDiagram-ABIIQ3AL-BOJ4UGbk.js} +1 -1
  66. package/web/assets/{railroadDiagram-RFXS5EU6-CqWD8KUC.js → railroadDiagram-RFXS5EU6-_UJ9_AEC.js} +1 -1
  67. package/web/assets/{requirementDiagram-TGXJPOKE-BHUBdOVh.js → requirementDiagram-TGXJPOKE-BFa61FSa.js} +1 -1
  68. package/web/assets/{sankeyDiagram-HTMAVEWB-Cw_ZddWI.js → sankeyDiagram-HTMAVEWB-V1J9w6jl.js} +1 -1
  69. package/web/assets/{selection-popup-ABYH3A27-Gc-RFSWj.js → selection-popup-ZX4VUZ3A-Duh9LSP2.js} +4 -4
  70. package/web/assets/{sequenceDiagram-DBY2YBRQ-CyCbkAfM.js → sequenceDiagram-DBY2YBRQ-xdS7JpTx.js} +1 -1
  71. package/web/assets/{sizeCapture-X5ZJPWSS-BG6RM8rb.js → sizeCapture-X5ZJPWSS-DBULLaws.js} +1 -1
  72. package/web/assets/{stateDiagram-2N3HPSRC-DKpZl5UU.js → stateDiagram-2N3HPSRC-DfBpQAQb.js} +1 -1
  73. package/web/assets/{stateDiagram-v2-6OUMAXLB-DHUy-0iS.js → stateDiagram-v2-6OUMAXLB-C-dC_PIX.js} +1 -1
  74. package/web/assets/{swimlanes-5IMT3BWC-D14uyHMH.js → swimlanes-5IMT3BWC-L-RTYuhP.js} +2 -2
  75. package/web/assets/swimlanesDiagram-G3AALYLV-BErklojq.js +8 -0
  76. package/web/assets/{timeline-definition-FHXFAJF6-f4WPhEXS.js → timeline-definition-FHXFAJF6-CAEbvdzk.js} +1 -1
  77. package/web/assets/{vennDiagram-L72KCM5P-lr4A8guw.js → vennDiagram-L72KCM5P-J10bFP8c.js} +1 -1
  78. package/web/assets/{wardleyDiagram-EHGQE667-CR-Im585.js → wardleyDiagram-EHGQE667-Bm0goK-9.js} +1 -1
  79. package/web/assets/{xychartDiagram-FW5EYKEG-DeNQfP7k.js → xychartDiagram-FW5EYKEG-BAH4TTVb.js} +1 -1
  80. package/web/index.html +2 -2
  81. package/dist/site/terminal.d.ts +0 -14
  82. package/web/assets/ask-tray-KUTWZ4MV-Ckn1y1Zw.js +0 -24
  83. package/web/assets/channel-BQxXBpeY.js +0 -1
  84. package/web/assets/classDiagram-OUVF2IWQ-BT1TSwZs.js +0 -1
  85. package/web/assets/classDiagram-v2-EOCWNBFH-BT1TSwZs.js +0 -1
  86. package/web/assets/index-Di6pguZz.css +0 -1
  87. package/web/assets/index-qmu99cjp.js +0 -493
  88. package/web/assets/swimlanesDiagram-G3AALYLV-C5riej52.js +0 -8
@@ -210,10 +210,119 @@ var init_document_kinds = __esm(() => {
210
210
  };
211
211
  });
212
212
 
213
- // packages/preview/src/terminal.ts
214
- import { execFile, execFileSync } from "node:child_process";
215
- import { basename } from "node:path";
216
- import { clearScreenDown, cursorTo, emitKeypressEvents } from "node:readline";
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 copyPreviewLink(url) {
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
- process.off("exit", close);
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
- process.once("exit", close);
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 terminalText = (text) => text.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
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({ viewer: string2(), root: string2(), kernelKey: string2() })
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 as execFileSync2, spawn } from "node:child_process";
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 = execFileSync2("/sbin/route", ["-n", "get", "default"], {
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 = execFileSync2("/usr/sbin/ipconfig", ["getifaddr", device], {
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
- const terminal = previewTerminal(viewerUrl, options, async () => {
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 terminal = previewTerminal(viewerUrl, options, async () => {
6413
- await Promise.resolve().then(() => init_share());
6414
- return startPreviewShare(options);
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
- terminal.close();
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
- terminal.close();
6293
+ lifetime.close();
6437
6294
  await releaseLock(lock, token);
6438
6295
  });
6439
6296
  }
6440
6297
  var init_hot_viewer = __esm(() => {
6441
- init_terminal();
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 as basename2,
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: basename2(current),
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 basename3,
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) || basename3(path) === "raggle.json" && inside3(dirname5(path), scope) || ["schema.json", "_schema.json"].includes(basename3(path)) && dirname5(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 ?? basename3(path, ".md"),
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 ?? basename3(entry.folder),
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 basename4, relative as relative6 } from "node:path";
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 || basename4(path) === "raggle.json" && collectionScopes.size) {
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 basename5, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
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(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
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(["github.com", "gitlab.com", "bitbucket.org"]);
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 basename6,
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) ?? basename6(filePath, extname2(filePath))
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) ?? basename6(folderPath);
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 realpath5,
38669
+ realpath as realpath6,
38767
38670
  rename as rename3,
38768
- stat as stat5,
38769
- unlink as unlink2,
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 extname3,
38775
- isAbsolute as isAbsolute5,
38776
- relative as relative9,
38777
- resolve as resolve9,
38778
- sep as sep5
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(extname3(path).toLowerCase()))
38687
+ if (![".md", ".markdown"].includes(extname4(path).toLowerCase()))
38785
38688
  throw apiError("bad-request", "Only Markdown notes can be edited.");
38786
- const root = await realpath5(folder);
38787
- const target = await realpath5(resolve9(root, path));
38788
- const local = relative9(root, target);
38789
- if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
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 stat5(target);
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 = resolve9(dirname8(target), `.kennel-edit-${randomUUID3()}.tmp`);
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 unlink2(temporary).catch(() => {});
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 realpath5(resolve9(folder, input.path)) !== context.lockedFile))
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 resolve10 } from "node:path";
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 = resolve10(folder, "AGENTS.md");
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 stat6 } from "node:fs/promises";
38931
- import { extname as extname4 } from "node:path";
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(extname4(icon).toLowerCase()) : null;
38949
- const details = icon && mediaType ? await stat6(icon).catch(() => {
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(extname4(icon).toLowerCase());
38874
+ const mediaType = searchIconTypes.get(extname5(icon).toLowerCase());
38972
38875
  if (!mediaType)
38973
38876
  return;
38974
- const details = await stat6(icon).catch(() => {
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 init_manifest = __esm(() => {
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 readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
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 extname5,
39067
- isAbsolute as isAbsolute6,
39068
- relative as relative10,
39069
- resolve as resolve11,
39070
- sep as sep6
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 readdir5(current, { withFileTypes: true }).catch(() => []);
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 = resolve11(current, entry.name);
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() || !markdownExtensions2.has(extname5(entry.name))) {
39439
+ if (!entry.isFile() || !markdownExtensions3.has(extname6(entry.name))) {
39085
39440
  continue;
39086
39441
  }
39087
- const details = await stat7(path).catch(() => {
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, extname5(entry.name)),
39093
- relativePath: relative10(folder, path).split(sep6).join("/")
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 = relative10(root, path);
39102
- return local === "" || !local.startsWith("..") && !isAbsolute6(local);
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(resolve11(group.folder, file.relativePath));
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) => resolve11(source, file.relativePath));
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 markdownExtensions2, llmNoteLimit = 20, canonical = (path) => realpath6(path).catch(() => path);
39208
- var init_server3 = __esm(() => {
39209
- init_manifest();
39210
- markdownExtensions2 = new Set([".md", ".markdown"]);
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 existsSync4 } from "node:fs";
39216
- import { mkdir as mkdir4, readFile as readFile10, stat as stat8, writeFile as writeFile6 } from "node:fs/promises";
39217
- import { dirname as dirname9, resolve as resolve12, sep as sep7 } from "node:path";
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 = dirname9(current)) {
39242
- const candidate = resolve12(current, "kennel.json");
39243
- const details = await stat8(candidate).catch(() => {
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 (dirname9(current) === current)
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 readFile10(path, "utf8"));
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" && existsSync4(resolve12(context.folder, entry.trigger))) {
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 existsSync5 } from "node:fs";
39353
- import { createRequire as createRequire2 } from "node:module";
39354
- import { dirname as dirname10, join as join5 } from "node:path";
39355
- import { fileURLToPath as fileURLToPath2 } from "node:url";
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 = createRequire2(from);
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 = dirname10(fileURLToPath2(from));
39363
- while (dirname10(directory) !== directory) {
39364
- if (existsSync5(join5(directory, "raggle.json"))) {
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
- join5(dirname10(directory), "raggle-ai-scout", "packages", "local"),
39367
- join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
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 = dirname10(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] ?? join5(dirname10(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
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 realpathSync2 } from "node:fs";
39573
- import { readdir as readdir6, readFile as readFile11, realpath as realpath7, stat as stat9 } from "node:fs/promises";
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 dirname11,
39578
- extname as extname6,
39579
- isAbsolute as isAbsolute7,
39580
- relative as relative12,
39581
- resolve as resolve13,
39582
- sep as sep8
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 = relative12(root, path);
39588
- if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
39943
+ const candidate = relative14(root, path);
39944
+ if (!candidate || candidate.startsWith("..") || isAbsolute8(candidate))
39589
39945
  return false;
39590
- const parts = candidate.split(sep8);
39946
+ const parts = candidate.split(sep11);
39591
39947
  if (parts.some((part) => part === "node_modules"))
39592
39948
  return false;
39593
- return metadataExtensions.has(extname6(candidate).toLowerCase());
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 = realpathSync2(resolve13(value ?? root));
39957
+ const folder = realpathSync3(resolve17(value ?? root));
39602
39958
  if (!lockedRoot)
39603
39959
  return folder;
39604
- const path = relative12(lockedRoot, folder);
39605
- if (!path || !path.startsWith("..") && !isAbsolute7(path))
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() || dirname11(folder) === folder;
40025
+ return folder === homedir4() || dirname14(folder) === folder;
39670
40026
  }
39671
40027
  async function shallowMetadata(folder) {
39672
- const entries = await readdir6(folder, { withFileTypes: true });
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(extname6(entry.name).toLowerCase())).map(async (entry) => {
39675
- const path = resolve13(folder, entry.name);
39676
- const details = await stat9(path);
39677
- const content = await readFile11(path, "utf8").catch(() => "");
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(resolve13(folder, entry.name)).digest("hex"),
40046
+ id: createHash5("sha1").update(resolve17(folder, entry.name)).digest("hex"),
39691
40047
  name: entry.name,
39692
40048
  relativePath: entry.name,
39693
- worktree: resolve13(folder, entry.name)
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: relative12(folder, project.worktree)
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 ? resolve13(folder, file.relativePath) : undefined);
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(extname6(path).toLowerCase()) ? await readFile11(path, "utf8").catch(() => "") : "";
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 readFile11(note.path, "utf8").catch(() => "");
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 = relative12(root, path);
39757
- return local === "" || !local.startsWith("..") && !isAbsolute7(local);
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 = realpathSync2(resolve13(folder));
39789
- const path = resolve13(folder, file);
40144
+ const rootReal = realpathSync3(resolve17(folder));
40145
+ const path = resolve17(folder, file);
39790
40146
  let pathReal;
39791
40147
  try {
39792
- pathReal = realpathSync2(path);
40148
+ pathReal = realpathSync3(path);
39793
40149
  } catch {
39794
40150
  return;
39795
40151
  }
39796
- const rel = relative12(rootReal, pathReal);
39797
- if (rel.startsWith("..") || isAbsolute7(rel))
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 = realpathSync2(resolve13(folder));
39837
- const path = resolve13(folder, file);
40192
+ const rootReal = realpathSync3(resolve17(folder));
40193
+ const path = resolve17(folder, file);
39838
40194
  let pathReal;
39839
40195
  try {
39840
- pathReal = realpathSync2(path);
40196
+ pathReal = realpathSync3(path);
39841
40197
  } catch {
39842
40198
  return;
39843
40199
  }
39844
- const rel = relative12(rootReal, pathReal);
39845
- if (rel.startsWith("..") || isAbsolute7(rel))
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 ? realpathSync2(root) : undefined;
39918
- const lockedTarget = lockedFile ? realpathSync2(lockedFile) : undefined;
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 ?? realpathSync2(root);
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: relative12(folder, 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 readFile11(asset.path));
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, relative12(folder, lockedTarget), lockedTarget) : undefined;
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: relative12(folder, target.path),
40499
+ path: relative14(folder, target.path),
40143
40500
  kind: target.kind,
40144
- content: previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : ""
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 stat9(target.path);
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 readFile11(target.path, "utf8") : "";
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: relative12(target.root, target.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: relative12(target.root, target.path)
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 stat9(target.path);
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 readFile11(target.path));
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 stat9(target.path);
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 readFile11(target.path);
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
- resolve13(workspace.root, "raggle.json")
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) => realpath7(path).catch(() => 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 readFile12, realpath as realpath8, stat as stat10 } from "node:fs/promises";
40620
- import { extname as extname7, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
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 realpath8(directory);
40993
+ const root = await realpath10(directory);
40623
40994
  const inside = (path) => {
40624
- const local = relative13(root, path);
40625
- return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
40995
+ const local = relative15(root, path);
40996
+ return local !== ".." && !local.startsWith("../") && !isAbsolute9(local);
40626
40997
  };
40627
- let target = resolve14(root, `.${decodeURIComponent(pathname)}`);
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 stat10(target).catch(() => null))?.isFile()) {
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 = resolve14(root, fallback);
41010
+ target = resolve18(root, fallback);
40640
41011
  }
40641
- target = await realpath8(target);
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[extname7(target)] ?? "application/octet-stream",
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 readFile12(target));
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 init_server4 = __esm(() => {
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
- import { execFileSync as execFileSync3 } from "node:child_process";
40825
- import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
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 dirname12, isAbsolute as isAbsolute9, relative as relative14, resolve as resolve15 } from "node:path";
40828
- import { fileURLToPath as fileURLToPath3 } from "node:url";
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 = resolve15(arg);
40911
- const isDirectory = existsSync6(candidate) && statSync3(candidate).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 = dirname12(candidate);
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 (!existsSync6(options.root) || !statSync3(options.root).isDirectory()) {
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
- const url = new URL(`${scheme}://${authority}/`);
40958
- if (!options.target) {
40959
- url.pathname = `/folder/${encodeBase64Url2(options.root)}/notes`;
40960
- return url.href;
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 = realpathSync3(parent);
40972
- const childReal = realpathSync3(child);
40973
- const childRelative = relative14(parentReal, childReal);
40974
- return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute9(childRelative);
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 = execFileSync3("/sbin/route", ["-n", "get", "default"], {
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 = execFileSync3("/usr/sbin/ipconfig", ["getifaddr", device], {
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(execFileSync3("tailscale", ["status", "--json"], { encoding: "utf8" }));
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 = resolve15(dirname12(fileURLToPath3(import.meta.url)), "../../web");
41036
- if (!existsSync6(resolve15(web, "index.html"))) {
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 ? realpathSync3(options.target) : undefined;
41041
- await Promise.resolve().then(() => init_server4());
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
- const terminal = previewTerminal(viewerUrl, options, async () => {
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
- execFileSync3("tailscale", ["serve", "--bg", "--yes", options.port]);
41450
+ execFileSync2("tailscale", ["serve", "--bg", "--yes", options.port]);
41081
41451
  }
41082
41452
  const viewerUrl = popUrl(displayHost, options.port, options);
41083
- const terminal = previewTerminal(viewerUrl, options, async () => {
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
- await terminal.closed;
41455
+ console.log(viewerUrl);
41456
+ if (options.open)
41457
+ openPreview(viewerUrl);
41458
+ await lifetime.closed;
41091
41459
  } finally {
41092
- terminal.close();
41460
+ lifetime.close();
41093
41461
  }
41094
41462
  } finally {
41095
41463
  await api.close();
41096
41464
  }
41097
41465
  if (options.tailscale) {
41098
- execFileSync3("tailscale", ["serve", "--https=443", "off"]);
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
- import { dirname as dirname13, resolve as resolve16, join as join6 } from "node:path";
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 && !existsSync7(folder)) {
41627
+ if (folder && !existsSync8(folder)) {
41279
41628
  throw new Error(`Folder not found: ${folder}`);
41280
41629
  }
41281
- options.folder = folder ? resolve16(folder) : undefined;
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 = realpathSync4(target.root);
41302
- const configPath = join6(process.env.XDG_CONFIG_HOME?.trim() || join6(homedir5(), ".config"), "kennel", "config.json");
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 = resolve16(dirname13(fileURLToPath4(import.meta.url)), "../../web");
41310
- if (!existsSync7(resolve16(web, "index.html"))) {
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(() => init_server4());
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 ? realpathSync4(target.target) : undefined,
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")}/notes` : 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 });
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,