kennel 0.2.32 → 0.2.33

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 (80) hide show
  1. package/dist/cli/dispatch.js +5 -0
  2. package/dist/cli/usage.js +5 -0
  3. package/dist/core/runtime.js +458 -166
  4. package/dist/llm/command.js +10 -3
  5. package/dist/llm/worker.js +297 -32
  6. package/dist/notes/command.d.ts +1 -0
  7. package/dist/notes/command.js +556 -0
  8. package/dist/package/preview.js +481 -188
  9. package/dist/site/server-plugins.js +51 -10
  10. package/package.json +3 -2
  11. package/schemas/config.schema.json +11 -0
  12. package/web/assets/{abnfDiagram-VRR7QNED-DPZyna8O.js → abnfDiagram-VRR7QNED-CpP1VHKI.js} +1 -1
  13. package/web/assets/{arc-BAcU9y_V.js → arc-kL4AxfvR.js} +1 -1
  14. package/web/assets/{architectureDiagram-ZJ3FMSHR-D5yd7Cag.js → architectureDiagram-ZJ3FMSHR-BWaRktRJ.js} +1 -1
  15. package/web/assets/{ask-tray-PMT6VVXN-C4dJH21Q.js → ask-tray-PMT6VVXN-FqPDpwtw.js} +3 -3
  16. package/web/assets/{blockDiagram-677ZJIJ3-C7LSGyxV.js → blockDiagram-677ZJIJ3-B07uVoPU.js} +1 -1
  17. package/web/assets/{c4Diagram-LMCZKHZV-BW2KfoQr.js → c4Diagram-LMCZKHZV-uvoaQC09.js} +1 -1
  18. package/web/assets/channel-Djmf1Ik1.js +1 -0
  19. package/web/assets/{chunk-2Q5K7J3B-4Og6VAFG.js → chunk-2Q5K7J3B-BH3SaHqk.js} +1 -1
  20. package/web/assets/{chunk-32BRIVSS-BYOowQl5.js → chunk-32BRIVSS-SPD8Eraw.js} +1 -1
  21. package/web/assets/{chunk-5VM5RSS4-C1m-lq64.js → chunk-5VM5RSS4-DNeOgOuZ.js} +1 -1
  22. package/web/assets/{chunk-EX3LRPZG-B2hriRpJ.js → chunk-EX3LRPZG-SJQ-vKGz.js} +1 -1
  23. package/web/assets/{chunk-JWPE2WC7-C6RsnzVA.js → chunk-JWPE2WC7-Dy_9wZ2Y.js} +1 -1
  24. package/web/assets/{chunk-MOJQB5TN-yKbMmUWf.js → chunk-MOJQB5TN-CuIx80Vn.js} +1 -1
  25. package/web/assets/{chunk-RYQCIY6F-BOokm3Qc.js → chunk-RYQCIY6F-DDG61RKR.js} +1 -1
  26. package/web/assets/{chunk-V7JOEXUC-CKW-H5An.js → chunk-V7JOEXUC-7OMVhaoH.js} +1 -1
  27. package/web/assets/{chunk-VR4S4FIN-BU5pFrup.js → chunk-VR4S4FIN-Y-HNaSmV.js} +1 -1
  28. package/web/assets/{chunk-XXDRQBXY-DYtdSJHU.js → chunk-XXDRQBXY-CtG0MPgC.js} +1 -1
  29. package/web/assets/classDiagram-OUVF2IWQ-hlDS9RWf.js +1 -0
  30. package/web/assets/classDiagram-v2-EOCWNBFH-hlDS9RWf.js +1 -0
  31. package/web/assets/{cose-bilkent-JH36ORCC-BZeXtC6E.js → cose-bilkent-JH36ORCC-BZaPMYcl.js} +1 -1
  32. package/web/assets/{cynefin-VYW2F7L2-BSDVyzMI.js → cynefin-VYW2F7L2-DxAlRHA1.js} +1 -1
  33. package/web/assets/{cynefinDiagram-TSTJHNR4-CReoDBYo.js → cynefinDiagram-TSTJHNR4-gjgzmagS.js} +1 -1
  34. package/web/assets/{dagre-VKFMJZFB-CBLLzMxd.js → dagre-VKFMJZFB-DvLPeYuw.js} +1 -1
  35. package/web/assets/{diagram-FQU43EPY-BcGWiZXZ.js → diagram-FQU43EPY-443LFnzG.js} +1 -1
  36. package/web/assets/{diagram-G47NLZAW-BXUYZA9N.js → diagram-G47NLZAW-CqfUewot.js} +1 -1
  37. package/web/assets/{diagram-NH7WQ7WH-BrvrZs7N.js → diagram-NH7WQ7WH-D-FElUGW.js} +1 -1
  38. package/web/assets/{diagram-OA4YK3LP-7lnVcjXL.js → diagram-OA4YK3LP-DbidUAp5.js} +1 -1
  39. package/web/assets/{diagram-WEI45ONY-s58_bMFf.js → diagram-WEI45ONY-CPLeckCZ.js} +1 -1
  40. package/web/assets/{ebnfDiagram-CCIWWBDH-BemWecoD.js → ebnfDiagram-CCIWWBDH-DR0rqZzN.js} +1 -1
  41. package/web/assets/{entity-search-page-G6QEQNXT--sKP5dP3.js → entity-search-page-G6QEQNXT-CnG8d-at.js} +1 -1
  42. package/web/assets/{erDiagram-Q63AITRT-ClVJddch.js → erDiagram-Q63AITRT-DMXv_8Tj.js} +1 -1
  43. package/web/assets/{flowDiagram-23GEKE2U-xzUJu-i_.js → flowDiagram-23GEKE2U-DnbQjZs0.js} +1 -1
  44. package/web/assets/{ganttDiagram-NO4QXBWP-CXQmtF5g.js → ganttDiagram-NO4QXBWP-DBKIrjj8.js} +1 -1
  45. package/web/assets/{gitGraphDiagram-IHSO6WYX-DCdljcLl.js → gitGraphDiagram-IHSO6WYX-BqqxKWT_.js} +1 -1
  46. package/web/assets/{index-CtTx1o2d.js → index-Bd3ghcW-.js} +5 -5
  47. package/web/assets/{index-vjrhIAJn.css → index-Dt8OzQCe.css} +1 -1
  48. package/web/assets/{infoDiagram-FWYZ7A6U-B11SJYSK.js → infoDiagram-FWYZ7A6U-Ba6Fs17y.js} +1 -1
  49. package/web/assets/{ishikawaDiagram-FXEZZL3T-B2CCGlAm.js → ishikawaDiagram-FXEZZL3T-D3BOtFjy.js} +1 -1
  50. package/web/assets/{journeyDiagram-5HDEW3XC-CBPETzOk.js → journeyDiagram-5HDEW3XC-MbzFPQm8.js} +1 -1
  51. package/web/assets/{kanban-definition-HUTT4EX6-unhQ3RPE.js → kanban-definition-HUTT4EX6-CurBmwu1.js} +1 -1
  52. package/web/assets/{linear-DuP_pG51.js → linear-CQIr4JjI.js} +1 -1
  53. package/web/assets/{linked-session-header-M7WY6IGF-DwlNCJlj.js → linked-session-header-M7WY6IGF-BBgN8zTq.js} +1 -1
  54. package/web/assets/{mermaid.core-BlYNzkpn.js → mermaid.core-bE6Vl3bZ.js} +4 -4
  55. package/web/assets/{mindmap-definition-LN4V7U3C-CtKHN6O1.js → mindmap-definition-LN4V7U3C-C1a46OGU.js} +1 -1
  56. package/web/assets/{pegDiagram-2B236MQR-CRyz6XWY.js → pegDiagram-2B236MQR-DZxMOxek.js} +1 -1
  57. package/web/assets/{pieDiagram-ENE6RG2P-BXfkPm3Q.js → pieDiagram-ENE6RG2P-BN0XOKLi.js} +1 -1
  58. package/web/assets/{plugin-host-modules-5NQgs5Cq.js → plugin-host-modules-CaRD5_5q.js} +1 -1
  59. package/web/assets/{project-search-dialog-HJFGW4DG-DPly4OiB.js → project-search-dialog-HJFGW4DG-h_dHeh7g.js} +1 -1
  60. package/web/assets/{project-search-page-NKQEBGPF-Cc6HV7BN.js → project-search-page-NKQEBGPF-2AzjjLEC.js} +1 -1
  61. package/web/assets/{quadrantDiagram-ABIIQ3AL-Drb_qWex.js → quadrantDiagram-ABIIQ3AL-C6IegTP-.js} +1 -1
  62. package/web/assets/{railroadDiagram-RFXS5EU6-DCKk2Pix.js → railroadDiagram-RFXS5EU6-bsjyAl-q.js} +1 -1
  63. package/web/assets/{requirementDiagram-TGXJPOKE-6WwZwcpD.js → requirementDiagram-TGXJPOKE-Cl8EZWy7.js} +1 -1
  64. package/web/assets/{sankeyDiagram-HTMAVEWB-DGGk3WCE.js → sankeyDiagram-HTMAVEWB-DhK4rRux.js} +1 -1
  65. package/web/assets/{selection-popup-532EHNCZ-yPhTrk7L.js → selection-popup-532EHNCZ-E2iQ2gQQ.js} +1 -1
  66. package/web/assets/{sequenceDiagram-DBY2YBRQ-a4tGi3DH.js → sequenceDiagram-DBY2YBRQ-DQTaPSD6.js} +1 -1
  67. package/web/assets/{sizeCapture-X5ZJPWSS-CHehBmy9.js → sizeCapture-X5ZJPWSS-k77bZ1zg.js} +1 -1
  68. package/web/assets/{stateDiagram-2N3HPSRC-Bn9GSWgV.js → stateDiagram-2N3HPSRC-Cv_5WuZJ.js} +1 -1
  69. package/web/assets/{stateDiagram-v2-6OUMAXLB-DI7fdBd7.js → stateDiagram-v2-6OUMAXLB-DJ1Lrskh.js} +1 -1
  70. package/web/assets/{swimlanes-5IMT3BWC-BX0WRzfR.js → swimlanes-5IMT3BWC-gBtAjG3z.js} +2 -2
  71. package/web/assets/swimlanesDiagram-G3AALYLV-DF1K9voG.js +8 -0
  72. package/web/assets/{timeline-definition-FHXFAJF6-9Ni2kIzq.js → timeline-definition-FHXFAJF6-CFjnM1Qq.js} +1 -1
  73. package/web/assets/{vennDiagram-L72KCM5P-rfBSNh3U.js → vennDiagram-L72KCM5P-CeMWrThY.js} +1 -1
  74. package/web/assets/{wardleyDiagram-EHGQE667-CzziImCC.js → wardleyDiagram-EHGQE667-R-ZaAOAI.js} +1 -1
  75. package/web/assets/{xychartDiagram-FW5EYKEG-DTLI4h5R.js → xychartDiagram-FW5EYKEG-BFXoMXiN.js} +1 -1
  76. package/web/index.html +2 -2
  77. package/web/assets/channel-um92dqpm.js +0 -1
  78. package/web/assets/classDiagram-OUVF2IWQ-h7sFEJBg.js +0 -1
  79. package/web/assets/classDiagram-v2-EOCWNBFH-h7sFEJBg.js +0 -1
  80. package/web/assets/swimlanesDiagram-G3AALYLV-OGHC9nqi.js +0 -8
@@ -38305,17 +38305,215 @@ var init_server2 = __esm(() => {
38305
38305
  init_node3();
38306
38306
  });
38307
38307
 
38308
- // packages/api/src/server/entity-folder.ts
38309
- import { readdir as readdir3, readFile as readFile5, realpath as realpath3, stat as stat3 } from "node:fs/promises";
38308
+ // packages/notes/src/store.ts
38309
+ import { execFile as execFile2 } from "node:child_process";
38310
+ import {
38311
+ mkdir as mkdir3,
38312
+ readdir as readdir3,
38313
+ readFile as readFile5,
38314
+ realpath as realpath3,
38315
+ rename as rename2,
38316
+ rm as rm2,
38317
+ stat as stat3,
38318
+ writeFile as writeFile3
38319
+ } from "node:fs/promises";
38310
38320
  import { homedir as homedir2 } from "node:os";
38321
+ import { basename as basename5, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
38322
+ import { promisify } from "node:util";
38323
+ function kennelConfigHome() {
38324
+ const xdg = process.env.XDG_CONFIG_HOME?.trim();
38325
+ return xdg ? xdg : join2(homedir2(), ".config");
38326
+ }
38327
+ function kennelConfigFile() {
38328
+ return join2(kennelConfigHome(), "kennel", "config.json");
38329
+ }
38330
+ function repositoryNotesRoot() {
38331
+ return join2(kennelConfigHome(), "kennel", "repos");
38332
+ }
38333
+ function isRepositoryId(value) {
38334
+ return typeof value === "string" && UUID.test(value);
38335
+ }
38336
+ function managedNotesDirectory(id) {
38337
+ if (!isRepositoryId(id)) {
38338
+ throw new RepositoryNotesError("invalid-id", `Invalid repository id: ${id}`);
38339
+ }
38340
+ return join2(repositoryNotesRoot(), id);
38341
+ }
38342
+ async function readRepositoryNotesSettings() {
38343
+ const file = kennelConfigFile();
38344
+ let text;
38345
+ try {
38346
+ text = await readFile5(file, "utf8");
38347
+ } catch (error) {
38348
+ if (error.code === "ENOENT") {
38349
+ return { enabled: false };
38350
+ }
38351
+ throw new RepositoryNotesError("config-invalid", `Cannot read ${file}: ${errorMessage(error)}`);
38352
+ }
38353
+ let value;
38354
+ try {
38355
+ value = JSON.parse(text);
38356
+ } catch (error) {
38357
+ throw new RepositoryNotesError("config-invalid", `Invalid JSON in ${file}: ${errorMessage(error)}`);
38358
+ }
38359
+ if (!isRecord(value)) {
38360
+ throw new RepositoryNotesError("config-invalid", `${file} must hold an object`);
38361
+ }
38362
+ const section = value.repositoryNotes;
38363
+ if (section === undefined)
38364
+ return { enabled: false };
38365
+ if (!isRecord(section) || section.enabled !== undefined && typeof section.enabled !== "boolean") {
38366
+ throw new RepositoryNotesError("config-invalid", `${file}: repositoryNotes must be an object with a boolean "enabled" field`);
38367
+ }
38368
+ return { enabled: section.enabled === true };
38369
+ }
38370
+ async function git(args, cwd) {
38371
+ const { stdout } = await run("git", args, {
38372
+ cwd,
38373
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" },
38374
+ maxBuffer: 1024 * 1024
38375
+ });
38376
+ return stdout.replace(/\r?\n$/u, "");
38377
+ }
38378
+ function gitExitCode(error) {
38379
+ const code = error.code;
38380
+ return typeof code === "number" ? code : undefined;
38381
+ }
38382
+ async function resolveCheckout(folder) {
38383
+ let start;
38384
+ try {
38385
+ start = await realpath3(resolve7(folder));
38386
+ if (!(await stat3(start)).isDirectory())
38387
+ start = dirname6(start);
38388
+ } catch {
38389
+ throw new RepositoryNotesError("not-git", `Folder does not exist: ${folder}`);
38390
+ }
38391
+ let output;
38392
+ try {
38393
+ output = await git(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
38394
+ } catch (error) {
38395
+ if (gitExitCode(error) === 128) {
38396
+ throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
38397
+ }
38398
+ throw error;
38399
+ }
38400
+ const [top, common] = output.split(/\r?\n/u);
38401
+ if (!top || !common) {
38402
+ throw new RepositoryNotesError("not-git", `Not a Git work tree: ${start}`);
38403
+ }
38404
+ const root = await realpath3(top);
38405
+ const commonDir = await realpath3(common);
38406
+ return { root, commonDir, configFile: join2(commonDir, "config") };
38407
+ }
38408
+ async function readBinding(checkout) {
38409
+ let value;
38410
+ try {
38411
+ value = await git(["config", "--file", checkout.configFile, "--get", REPOSITORY_ID_KEY], checkout.root);
38412
+ } catch (error) {
38413
+ if (gitExitCode(error) === 1)
38414
+ return;
38415
+ throw error;
38416
+ }
38417
+ const id = value.trim();
38418
+ if (!id)
38419
+ return;
38420
+ if (!isRepositoryId(id)) {
38421
+ throw new RepositoryNotesError("invalid-binding", `${checkout.configFile} has an invalid ${REPOSITORY_ID_KEY} value. Remove it or run "kennel notes new-identity".`);
38422
+ }
38423
+ return id;
38424
+ }
38425
+ function isRecord(value) {
38426
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
38427
+ }
38428
+ function errorMessage(error) {
38429
+ return error instanceof Error ? error.message : String(error);
38430
+ }
38431
+ async function lookupRepositoryNotes(folder) {
38432
+ let checkout;
38433
+ try {
38434
+ checkout = await resolveCheckout(folder);
38435
+ } catch (error) {
38436
+ if (error instanceof RepositoryNotesError && error.code === "not-git") {
38437
+ return { status: "not-git", folder };
38438
+ }
38439
+ throw error;
38440
+ }
38441
+ const id = await readBinding(checkout);
38442
+ if (!id)
38443
+ return { status: "unregistered", checkout, root: checkout.root };
38444
+ return {
38445
+ status: "registered",
38446
+ checkout,
38447
+ id,
38448
+ notesDirectory: managedNotesDirectory(id),
38449
+ root: checkout.root
38450
+ };
38451
+ }
38452
+ async function listManagedNotes(directory) {
38453
+ const notes = [];
38454
+ const visit = async (current) => {
38455
+ let entries;
38456
+ try {
38457
+ entries = await readdir3(current, { withFileTypes: true });
38458
+ } catch (error) {
38459
+ if (error.code === "ENOENT")
38460
+ return;
38461
+ throw error;
38462
+ }
38463
+ for (const entry of entries) {
38464
+ if (entry.name.startsWith(".") || entry.isSymbolicLink())
38465
+ continue;
38466
+ const path = join2(current, entry.name);
38467
+ if (entry.isDirectory()) {
38468
+ await visit(path);
38469
+ continue;
38470
+ }
38471
+ if (!entry.isFile())
38472
+ continue;
38473
+ const dot = entry.name.lastIndexOf(".");
38474
+ const extension = dot === -1 ? "" : entry.name.slice(dot).toLowerCase();
38475
+ if (!markdownExtensions.has(extension))
38476
+ continue;
38477
+ const details = await stat3(path);
38478
+ notes.push({
38479
+ activityAt: details.mtimeMs,
38480
+ name: dot === -1 ? entry.name : entry.name.slice(0, dot),
38481
+ path,
38482
+ relativePath: relative7(directory, path).split(sep4).join("/"),
38483
+ size: details.size
38484
+ });
38485
+ }
38486
+ };
38487
+ await visit(directory);
38488
+ return notes.sort((left, right) => right.activityAt - left.activityAt);
38489
+ }
38490
+ var run, REPOSITORY_ID_KEY = "kennel.repoId", UUID, markdownExtensions, caseInsensitiveHosts, RepositoryNotesError;
38491
+ var init_store = __esm(() => {
38492
+ run = promisify(execFile2);
38493
+ UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
38494
+ markdownExtensions = new Set([".md", ".markdown"]);
38495
+ caseInsensitiveHosts = new Set(["github.com", "gitlab.com", "bitbucket.org"]);
38496
+ RepositoryNotesError = class RepositoryNotesError extends Error {
38497
+ code;
38498
+ constructor(code, message) {
38499
+ super(message);
38500
+ this.name = "RepositoryNotesError";
38501
+ this.code = code;
38502
+ }
38503
+ };
38504
+ });
38505
+
38506
+ // packages/api/src/server/entity-folder.ts
38507
+ import { readdir as readdir4, readFile as readFile6, realpath as realpath4, stat as stat4 } from "node:fs/promises";
38508
+ import { homedir as homedir3 } from "node:os";
38311
38509
  import {
38312
- basename as basename5,
38313
- dirname as dirname6,
38510
+ basename as basename6,
38511
+ dirname as dirname7,
38314
38512
  extname as extname2,
38315
38513
  isAbsolute as isAbsolute4,
38316
- join as join2,
38317
- relative as relative7,
38318
- resolve as resolve7
38514
+ join as join3,
38515
+ relative as relative8,
38516
+ resolve as resolve8
38319
38517
  } from "node:path";
38320
38518
  function parseMarkdown(source) {
38321
38519
  if (!source.startsWith(`---
@@ -38350,11 +38548,11 @@ function paragraph(body) {
38350
38548
  }
38351
38549
  function configPath() {
38352
38550
  const xdg = process.env.XDG_CONFIG_HOME?.trim();
38353
- return xdg ? join2(xdg, CONFIG_FILE) : join2(homedir2(), ".config", CONFIG_FILE);
38551
+ return xdg ? join3(xdg, CONFIG_FILE) : join3(homedir3(), ".config", CONFIG_FILE);
38354
38552
  }
38355
38553
  async function readConfig(path) {
38356
38554
  try {
38357
- return parseConfig(await readFile5(path, "utf8"));
38555
+ return parseConfig(await readFile6(path, "utf8"));
38358
38556
  } catch (error) {
38359
38557
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
38360
38558
  return null;
@@ -38371,6 +38569,8 @@ function parseConfig(source) {
38371
38569
  if (!value || typeof value !== "object" || Array.isArray(value))
38372
38570
  throw new Error("Invalid Kennel entity root config");
38373
38571
  const roots = value.allowedRoots;
38572
+ if (roots === undefined)
38573
+ return {};
38374
38574
  if (!Array.isArray(roots))
38375
38575
  throw new Error("Invalid Kennel entity root config");
38376
38576
  const allowedRoots = roots.map((root) => {
@@ -38382,12 +38582,12 @@ function parseConfig(source) {
38382
38582
  }
38383
38583
  async function allowedRoots(currentWorkspaceRoot) {
38384
38584
  const config = await readConfig(configPath());
38385
- if (config)
38585
+ if (config?.allowedRoots)
38386
38586
  return config.allowedRoots;
38387
38587
  return currentWorkspaceRoot?.trim() ? [currentWorkspaceRoot] : [];
38388
38588
  }
38389
38589
  function inside4(root, path) {
38390
- const local = relative7(root, path);
38590
+ const local = relative8(root, path);
38391
38591
  return local === "" || !local.startsWith("..") && !isAbsolute4(local);
38392
38592
  }
38393
38593
  async function normalizeRoot(rootPath, currentWorkspaceRoot) {
@@ -38395,13 +38595,13 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
38395
38595
  const fallback = currentWorkspaceRoot?.trim();
38396
38596
  if (!trimmed && !fallback)
38397
38597
  return null;
38398
- const requested = resolve7(trimmed || fallback);
38399
- const resolved = await realpath3(requested);
38400
- const info = await stat3(resolved);
38598
+ const requested = resolve8(trimmed || fallback);
38599
+ const resolved = await realpath4(requested);
38600
+ const info = await stat4(resolved);
38401
38601
  if (!info.isDirectory())
38402
38602
  throw new Error(`${resolved} is not a folder`);
38403
38603
  const roots = await allowedRoots(currentWorkspaceRoot);
38404
- const realAllowedRoots = await Promise.all(roots.map((root) => realpath3(root)));
38604
+ const realAllowedRoots = await Promise.all(roots.map((root) => realpath4(root)));
38405
38605
  const allowed = realAllowedRoots.some((root) => inside4(root, resolved));
38406
38606
  if (!allowed)
38407
38607
  throw new Error("Entity root is outside the configured roots");
@@ -38410,11 +38610,11 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
38410
38610
  async function markdownFiles(folderPath) {
38411
38611
  const files = [];
38412
38612
  async function walk(currentPath) {
38413
- const entries = await readdir3(currentPath, { withFileTypes: true });
38613
+ const entries = await readdir4(currentPath, { withFileTypes: true });
38414
38614
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
38415
38615
  if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
38416
38616
  continue;
38417
- const entryPath = join2(currentPath, entry.name);
38617
+ const entryPath = join3(currentPath, entry.name);
38418
38618
  if (entry.isDirectory()) {
38419
38619
  if (entryPath !== folderPath && await hasIndex(entryPath))
38420
38620
  continue;
@@ -38429,36 +38629,36 @@ async function markdownFiles(folderPath) {
38429
38629
  return files;
38430
38630
  }
38431
38631
  async function hasIndex(folderPath) {
38432
- return stat3(join2(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
38632
+ return stat4(join3(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
38433
38633
  }
38434
38634
  async function schemaPath(folderPath, rootPath) {
38435
38635
  let current = folderPath;
38436
38636
  while (current === rootPath || current.startsWith(`${rootPath}/`)) {
38437
38637
  for (const name of ["schema.json", "_schema.json"]) {
38438
- const candidate = join2(current, name);
38439
- if (await stat3(candidate).then((value) => value.isFile()).catch(() => false))
38638
+ const candidate = join3(current, name);
38639
+ if (await stat4(candidate).then((value) => value.isFile()).catch(() => false))
38440
38640
  return candidate;
38441
38641
  }
38442
38642
  if (current === rootPath)
38443
38643
  return null;
38444
- current = dirname6(current);
38644
+ current = dirname7(current);
38445
38645
  }
38446
38646
  return null;
38447
38647
  }
38448
38648
  async function readEntity(folderPath, rootPath) {
38449
- const indexPath = join2(folderPath, "index.md");
38450
- const index = parseMarkdown(await readFile5(indexPath, "utf8"));
38649
+ const indexPath = join3(folderPath, "index.md");
38650
+ const index = parseMarkdown(await readFile6(indexPath, "utf8"));
38451
38651
  const files = await markdownFiles(folderPath);
38452
38652
  const documents = await Promise.all(files.map(async (filePath) => {
38453
- const document2 = parseMarkdown(await readFile5(filePath, "utf8"));
38653
+ const document2 = parseMarkdown(await readFile6(filePath, "utf8"));
38454
38654
  return {
38455
38655
  content: document2.body,
38456
- path: relative7(folderPath, filePath),
38457
- title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
38656
+ path: relative8(folderPath, filePath),
38657
+ title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename6(filePath, extname2(filePath))
38458
38658
  };
38459
38659
  }));
38460
- const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
38461
- const relativePath = relative7(rootPath, folderPath) || ".";
38660
+ const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename6(folderPath);
38661
+ const relativePath = relative8(rootPath, folderPath) || ".";
38462
38662
  return {
38463
38663
  aliases: stringValues(index.frontmatter.aliases),
38464
38664
  content: [
@@ -38486,11 +38686,11 @@ async function discover(rootPath) {
38486
38686
  async function walk(folderPath) {
38487
38687
  if (await hasIndex(folderPath))
38488
38688
  folders.push(folderPath);
38489
- const entries = await readdir3(folderPath, { withFileTypes: true });
38689
+ const entries = await readdir4(folderPath, { withFileTypes: true });
38490
38690
  for (const entry of entries) {
38491
38691
  if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
38492
38692
  continue;
38493
- await walk(join2(folderPath, entry.name));
38693
+ await walk(join3(folderPath, entry.name));
38494
38694
  }
38495
38695
  }
38496
38696
  await walk(rootPath);
@@ -38529,7 +38729,7 @@ async function loadEntityWorkspace(root, query, selectedPath, currentWorkspaceRo
38529
38729
  var CONFIG_FILE, IGNORED_DIRECTORIES;
38530
38730
  var init_entity_folder = __esm(() => {
38531
38731
  init_dist3();
38532
- CONFIG_FILE = join2("kennel", "config.json");
38732
+ CONFIG_FILE = join3("kennel", "config.json");
38533
38733
  IGNORED_DIRECTORIES = new Set([
38534
38734
  ".git",
38535
38735
  ".gtl",
@@ -38549,20 +38749,20 @@ function replaceSelection(content, start, end, replacement) {
38549
38749
  // packages/api/src/server/api/document-edit.ts
38550
38750
  import { randomUUID as randomUUID3 } from "node:crypto";
38551
38751
  import {
38552
- readFile as readFile6,
38553
- realpath as realpath4,
38554
- rename as rename2,
38555
- stat as stat4,
38752
+ readFile as readFile7,
38753
+ realpath as realpath5,
38754
+ rename as rename3,
38755
+ stat as stat5,
38556
38756
  unlink as unlink2,
38557
- writeFile as writeFile3
38757
+ writeFile as writeFile4
38558
38758
  } from "node:fs/promises";
38559
38759
  import {
38560
- dirname as dirname7,
38760
+ dirname as dirname8,
38561
38761
  extname as extname3,
38562
38762
  isAbsolute as isAbsolute5,
38563
- relative as relative8,
38564
- resolve as resolve8,
38565
- sep as sep4
38763
+ relative as relative9,
38764
+ resolve as resolve9,
38765
+ sep as sep5
38566
38766
  } from "node:path";
38567
38767
  async function editDocument(folder, input) {
38568
38768
  const { path, original, start, end, replacement } = input;
@@ -38570,10 +38770,10 @@ async function editDocument(folder, input) {
38570
38770
  throw apiError("bad-request", "Invalid document edit.");
38571
38771
  if (![".md", ".markdown"].includes(extname3(path).toLowerCase()))
38572
38772
  throw apiError("bad-request", "Only Markdown notes can be edited.");
38573
- const root = await realpath4(folder);
38574
- const target = await realpath4(resolve8(root, path));
38575
- const local = relative8(root, target);
38576
- if (!local || local === ".." || local.startsWith(`..${sep4}`) || isAbsolute5(local))
38773
+ const root = await realpath5(folder);
38774
+ const target = await realpath5(resolve9(root, path));
38775
+ const local = relative9(root, target);
38776
+ if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
38577
38777
  throw apiError("forbidden", "The document must be inside its folder.");
38578
38778
  let content;
38579
38779
  try {
@@ -38583,20 +38783,20 @@ async function editDocument(folder, input) {
38583
38783
  }
38584
38784
  const previous = pending.get(target) ?? Promise.resolve();
38585
38785
  const operation = previous.catch(() => {}).then(async () => {
38586
- const details = await stat4(target);
38786
+ const details = await stat5(target);
38587
38787
  if (!details.isFile() || details.nlink !== 1)
38588
38788
  throw apiError("forbidden", "Only regular, unlinked files can be edited.");
38589
- if (await readFile6(target, "utf8") !== original)
38789
+ if (await readFile7(target, "utf8") !== original)
38590
38790
  throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
38591
- const temporary = resolve8(dirname7(target), `.kennel-edit-${randomUUID3()}.tmp`);
38791
+ const temporary = resolve9(dirname8(target), `.kennel-edit-${randomUUID3()}.tmp`);
38592
38792
  try {
38593
- await writeFile3(temporary, content, {
38793
+ await writeFile4(temporary, content, {
38594
38794
  flag: "wx",
38595
38795
  mode: details.mode & 511
38596
38796
  });
38597
- if (await readFile6(target, "utf8") !== original)
38797
+ if (await readFile7(target, "utf8") !== original)
38598
38798
  throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
38599
- await rename2(temporary, target);
38799
+ await rename3(temporary, target);
38600
38800
  } finally {
38601
38801
  await unlink2(temporary).catch(() => {});
38602
38802
  }
@@ -38624,7 +38824,7 @@ var init_document_edit = __esm(() => {
38624
38824
  throw apiError("bad-request", "Invalid document edit.");
38625
38825
  const input = parsed.data;
38626
38826
  const folder = context.authorize(input.folder);
38627
- if (context.lockedFile && (typeof input.path !== "string" || await realpath4(resolve8(folder, input.path)) !== context.lockedFile))
38827
+ if (context.lockedFile && (typeof input.path !== "string" || await realpath5(resolve9(folder, input.path)) !== context.lockedFile))
38628
38828
  throw apiError("forbidden", "Only the selected document can be edited.");
38629
38829
  await editDocument(folder, input);
38630
38830
  jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
@@ -38633,8 +38833,8 @@ var init_document_edit = __esm(() => {
38633
38833
  });
38634
38834
 
38635
38835
  // packages/api/src/server/api/create-agent-file.ts
38636
- import { writeFile as writeFile4 } from "node:fs/promises";
38637
- import { resolve as resolve9 } from "node:path";
38836
+ import { writeFile as writeFile5 } from "node:fs/promises";
38837
+ import { resolve as resolve10 } from "node:path";
38638
38838
  var createAgentFileRoute;
38639
38839
  var init_create_agent_file = __esm(() => {
38640
38840
  init_endpoint();
@@ -38648,9 +38848,9 @@ var init_create_agent_file = __esm(() => {
38648
38848
  } catch (error) {
38649
38849
  throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
38650
38850
  }
38651
- const target = resolve9(folder, "AGENTS.md");
38851
+ const target = resolve10(folder, "AGENTS.md");
38652
38852
  try {
38653
- await writeFile4(target, "", { flag: "wx" });
38853
+ await writeFile5(target, "", { flag: "wx" });
38654
38854
  } catch (error) {
38655
38855
  if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
38656
38856
  throw apiError("conflict", "AGENTS.md already exists in this folder");
@@ -38663,7 +38863,7 @@ var init_create_agent_file = __esm(() => {
38663
38863
  });
38664
38864
 
38665
38865
  // packages/api/src/server/api/open-path.ts
38666
- import { execFile as execFile2 } from "node:child_process";
38866
+ import { execFile as execFile3 } from "node:child_process";
38667
38867
  var openPathRoute;
38668
38868
  var init_open_path = __esm(() => {
38669
38869
  init_endpoint();
@@ -38677,7 +38877,7 @@ var init_open_path = __esm(() => {
38677
38877
  throw apiError("not-found", error instanceof Error ? error.message : "Path not found");
38678
38878
  }
38679
38879
  await new Promise((done, fail) => {
38680
- execFile2("/usr/bin/open", [target], (error) => {
38880
+ execFile3("/usr/bin/open", [target], (error) => {
38681
38881
  if (error)
38682
38882
  fail(apiError("failed", "Could not open path"));
38683
38883
  else
@@ -38696,11 +38896,11 @@ var init_favicon2 = __esm(() => {
38696
38896
  });
38697
38897
 
38698
38898
  // packages/api/src/server/folder-favicon.ts
38699
- import { readFile as readFile7 } from "node:fs/promises";
38700
- import { join as join3 } from "node:path";
38899
+ import { readFile as readFile8 } from "node:fs/promises";
38900
+ import { join as join4 } from "node:path";
38701
38901
  async function folderWebsite(folder) {
38702
38902
  for (const name of ["AGENTS.md", "README.md"]) {
38703
- const content = await readFile7(join3(folder, name), "utf8").catch(() => "");
38903
+ const content = await readFile8(join4(folder, name), "utf8").catch(() => "");
38704
38904
  const entry = markdownMetadata(content).entries.find(([key]) => key === "website");
38705
38905
  if (entry)
38706
38906
  return websiteHost(entry[1]);
@@ -38714,7 +38914,7 @@ var init_folder_favicon = __esm(() => {
38714
38914
 
38715
38915
  // packages/api/src/server/folder-icons.ts
38716
38916
  import { createHash as createHash4 } from "node:crypto";
38717
- import { readFile as readFile8, stat as stat5 } from "node:fs/promises";
38917
+ import { readFile as readFile9, stat as stat6 } from "node:fs/promises";
38718
38918
  import { extname as extname4 } from "node:path";
38719
38919
  import { discoverFolderIcon } from "@raggle-ai/local";
38720
38920
  function assetKey(value) {
@@ -38733,13 +38933,13 @@ function iconUrl(folder, hash, mediaType) {
38733
38933
  async function folderIcon(folder, assets, assetFiles, fallback) {
38734
38934
  const icon = discoverFolderIcon(folder);
38735
38935
  const mediaType = icon ? iconTypes.get(extname4(icon).toLowerCase()) : null;
38736
- const details = icon && mediaType ? await stat5(icon).catch(() => {
38936
+ const details = icon && mediaType ? await stat6(icon).catch(() => {
38737
38937
  return;
38738
38938
  }) : undefined;
38739
38939
  if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
38740
38940
  return fallback ? await fallback(folder, assets) : undefined;
38741
38941
  }
38742
- const data = await readFile8(icon);
38942
+ const data = await readFile9(icon);
38743
38943
  const hash = assetKey(data);
38744
38944
  const key = `local:${hash}`;
38745
38945
  assetFiles.set(hash, { mediaType, path: icon });
@@ -38758,12 +38958,12 @@ async function searchIconDataUrl(icon) {
38758
38958
  const mediaType = searchIconTypes.get(extname4(icon).toLowerCase());
38759
38959
  if (!mediaType)
38760
38960
  return;
38761
- const details = await stat5(icon).catch(() => {
38961
+ const details = await stat6(icon).catch(() => {
38762
38962
  return;
38763
38963
  });
38764
38964
  if (!details?.size || details.size > maxSearchIconBytes)
38765
38965
  return;
38766
- return `data:${mediaType};base64,${(await readFile8(icon)).toString("base64")}`;
38966
+ return `data:${mediaType};base64,${(await readFile9(icon)).toString("base64")}`;
38767
38967
  }
38768
38968
  async function faviconAsset(folderName, assets) {
38769
38969
  if (process.env.KENNEL_EXTERNAL_ICONS !== "1")
@@ -38847,47 +39047,77 @@ var init_manifest = __esm(() => {
38847
39047
  });
38848
39048
 
38849
39049
  // packages/notes/src/server.ts
38850
- import { readdir as readdir4, stat as stat6 } from "node:fs/promises";
38851
- import { basename as basename6, extname as extname5, relative as relative9, resolve as resolve10, sep as sep5 } from "node:path";
39050
+ import { readdir as readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
39051
+ import {
39052
+ basename as basename7,
39053
+ extname as extname5,
39054
+ isAbsolute as isAbsolute6,
39055
+ relative as relative10,
39056
+ resolve as resolve11,
39057
+ sep as sep6
39058
+ } from "node:path";
38852
39059
  async function listMarkdownFiles(folder) {
38853
39060
  const files = [];
38854
39061
  const visit = async (current) => {
38855
- const entries = await readdir4(current, { withFileTypes: true }).catch(() => []);
39062
+ const entries = await readdir5(current, { withFileTypes: true }).catch(() => []);
38856
39063
  for (const entry of entries) {
38857
39064
  if (entry.name === "node_modules" || entry.name.startsWith("."))
38858
39065
  continue;
38859
- const path = resolve10(current, entry.name);
39066
+ const path = resolve11(current, entry.name);
38860
39067
  if (entry.isDirectory()) {
38861
39068
  await visit(path);
38862
39069
  continue;
38863
39070
  }
38864
- if (!entry.isFile() || !markdownExtensions.has(extname5(entry.name))) {
39071
+ if (!entry.isFile() || !markdownExtensions2.has(extname5(entry.name))) {
38865
39072
  continue;
38866
39073
  }
38867
- const details = await stat6(path).catch(() => {
39074
+ const details = await stat7(path).catch(() => {
38868
39075
  return;
38869
39076
  });
38870
39077
  files.push({
38871
39078
  activityAt: details?.mtimeMs ?? 0,
38872
- name: basename6(entry.name, extname5(entry.name)),
38873
- relativePath: relative9(folder, path).split(sep5).join("/")
39079
+ name: basename7(entry.name, extname5(entry.name)),
39080
+ relativePath: relative10(folder, path).split(sep6).join("/")
38874
39081
  });
38875
39082
  }
38876
39083
  };
38877
39084
  await visit(folder);
38878
39085
  return files.sort((left, right) => right.activityAt - left.activityAt);
38879
39086
  }
38880
- async function loadNotes(folder, linkedFolders = []) {
39087
+ function inside5(path, root) {
39088
+ const local = relative10(root, path);
39089
+ return local === "" || !local.startsWith("..") && !isAbsolute6(local);
39090
+ }
39091
+ async function withoutManaged(groups, managedRoots) {
39092
+ if (!managedRoots.length)
39093
+ return groups;
39094
+ const roots = await Promise.all(managedRoots.map(canonical));
39095
+ return Promise.all(groups.map(async (group) => {
39096
+ if (roots.includes(await canonical(group.folder))) {
39097
+ return { ...group, files: [] };
39098
+ }
39099
+ const files = await Promise.all(group.files.map(async (file) => {
39100
+ const real = await canonical(resolve11(group.folder, file.relativePath));
39101
+ return roots.some((root) => inside5(real, root)) ? undefined : file;
39102
+ }));
39103
+ return { ...group, files: files.filter((file) => file !== undefined) };
39104
+ }));
39105
+ }
39106
+ async function loadNotes(folder, linkedFolders = [], managedFolders = []) {
38881
39107
  const localFiles = await listMarkdownFiles(folder);
38882
- const linked = await Promise.all(linkedFolders.map(async (linkedFolder) => ({
38883
- files: await listMarkdownFiles(linkedFolder),
38884
- folder: linkedFolder
39108
+ const listGroups = (folders) => Promise.all(folders.map(async (source) => ({
39109
+ files: await listMarkdownFiles(source),
39110
+ folder: source
38885
39111
  })));
39112
+ const [managed, linked] = await Promise.all([
39113
+ listGroups(managedFolders),
39114
+ listGroups(linkedFolders).then((groups) => withoutManaged(groups, managedFolders))
39115
+ ]);
38886
39116
  return {
38887
39117
  files: {
38888
39118
  items: [
38889
39119
  ...localFiles,
38890
- ...linked.flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
39120
+ ...[...managed, ...linked].flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
38891
39121
  ]
38892
39122
  },
38893
39123
  folder,
@@ -38904,14 +39134,20 @@ function codeSpan(text) {
38904
39134
  async function notesLlmSections({
38905
39135
  folder,
38906
39136
  linkedFolders = [],
39137
+ managedFolders = [],
38907
39138
  signal
38908
39139
  }) {
38909
39140
  const groups = [];
38910
39141
  const notes = [];
38911
- for (const source of [folder, ...linkedFolders]) {
39142
+ const seen = new Set;
39143
+ for (const source of [folder, ...managedFolders, ...linkedFolders]) {
38912
39144
  signal.throwIfAborted();
39145
+ const identity = await canonical(source);
39146
+ if (seen.has(identity))
39147
+ continue;
39148
+ seen.add(identity);
38913
39149
  const files = await listMarkdownFiles(source);
38914
- const paths = files.map((file) => resolve10(source, file.relativePath));
39150
+ const paths = files.map((file) => resolve11(source, file.relativePath));
38915
39151
  groups.push({ folder: source, paths });
38916
39152
  files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
38917
39153
  }
@@ -38944,24 +39180,28 @@ function createNotesPlugin() {
38944
39180
  },
38945
39181
  async handle({
38946
39182
  folder,
38947
- linkedFolders
39183
+ linkedFolders,
39184
+ managedFolders
38948
39185
  }) {
38949
- return { data: await loadNotes(folder, linkedFolders), kind: "available" };
39186
+ return {
39187
+ data: await loadNotes(folder, linkedFolders, managedFolders),
39188
+ kind: "available"
39189
+ };
38950
39190
  },
38951
39191
  llm: notesLlmSections
38952
39192
  };
38953
39193
  }
38954
- var markdownExtensions, llmNoteLimit = 20;
39194
+ var markdownExtensions2, llmNoteLimit = 20, canonical = (path) => realpath6(path).catch(() => path);
38955
39195
  var init_server3 = __esm(() => {
38956
39196
  init_manifest();
38957
- markdownExtensions = new Set([".md", ".markdown"]);
39197
+ markdownExtensions2 = new Set([".md", ".markdown"]);
38958
39198
  });
38959
39199
 
38960
39200
  // packages/api/src/server/plugins.ts
38961
39201
  import { pathToFileURL } from "node:url";
38962
39202
  import { existsSync as existsSync4 } from "node:fs";
38963
- import { mkdir as mkdir3, readFile as readFile9, stat as stat7, writeFile as writeFile5 } from "node:fs/promises";
38964
- import { dirname as dirname8, resolve as resolve11, sep as sep6 } from "node:path";
39203
+ import { mkdir as mkdir4, readFile as readFile10, stat as stat8, writeFile as writeFile6 } from "node:fs/promises";
39204
+ import { dirname as dirname9, resolve as resolve12, sep as sep7 } from "node:path";
38965
39205
  function configuredFolderServerRegistry(registry, resolvedConfig) {
38966
39206
  const disabled = new Set(resolvedConfig.plugins.disable);
38967
39207
  const orderedIds = resolvedConfig.plugins.order.filter((id) => registry.has(id) && !disabled.has(id));
@@ -38985,14 +39225,14 @@ function parsePluginRegistryConfig(content) {
38985
39225
  };
38986
39226
  }
38987
39227
  async function nearestKennelConfig(folder) {
38988
- for (let current = folder;; current = dirname8(current)) {
38989
- const candidate = resolve11(current, "kennel.json");
38990
- const details = await stat7(candidate).catch(() => {
39228
+ for (let current = folder;; current = dirname9(current)) {
39229
+ const candidate = resolve12(current, "kennel.json");
39230
+ const details = await stat8(candidate).catch(() => {
38991
39231
  return;
38992
39232
  });
38993
39233
  if (details?.isFile())
38994
39234
  return candidate;
38995
- if (dirname8(current) === current)
39235
+ if (dirname9(current) === current)
38996
39236
  return;
38997
39237
  }
38998
39238
  }
@@ -39001,7 +39241,7 @@ async function folderPluginRegistryConfig(folder) {
39001
39241
  if (!path)
39002
39242
  return defaultPluginRegistryConfig;
39003
39243
  try {
39004
- return parsePluginRegistryConfig(await readFile9(path, "utf8"));
39244
+ return parsePluginRegistryConfig(await readFile10(path, "utf8"));
39005
39245
  } catch {
39006
39246
  return defaultPluginRegistryConfig;
39007
39247
  }
@@ -39049,7 +39289,7 @@ async function folderServerRegistryAsync() {
39049
39289
  ...server,
39050
39290
  async detect(context) {
39051
39291
  const detection = await server.detect(context);
39052
- if (detection.status === "unavailable" && existsSync4(resolve11(context.folder, entry.trigger))) {
39292
+ if (detection.status === "unavailable" && existsSync4(resolve12(context.folder, entry.trigger))) {
39053
39293
  return { status: "available" };
39054
39294
  }
39055
39295
  return detection;
@@ -39095,10 +39335,10 @@ var init_plugins = __esm(() => {
39095
39335
  });
39096
39336
 
39097
39337
  // packages/api/src/server/linked-folder-discovery.ts
39098
- import { execFile as execFile3 } from "node:child_process";
39338
+ import { execFile as execFile4 } from "node:child_process";
39099
39339
  import { existsSync as existsSync5 } from "node:fs";
39100
39340
  import { createRequire as createRequire2 } from "node:module";
39101
- import { dirname as dirname9, join as join4 } from "node:path";
39341
+ import { dirname as dirname10, join as join5 } from "node:path";
39102
39342
  import { fileURLToPath as fileURLToPath2 } from "node:url";
39103
39343
  function resolveLocalModule(from = import.meta.url) {
39104
39344
  const require2 = createRequire2(from);
@@ -39106,19 +39346,19 @@ function resolveLocalModule(from = import.meta.url) {
39106
39346
  try {
39107
39347
  candidates.push(require2.resolve("@raggle-ai/local"));
39108
39348
  } catch {}
39109
- let directory = dirname9(fileURLToPath2(from));
39110
- while (dirname9(directory) !== directory) {
39111
- if (existsSync5(join4(directory, "raggle.json"))) {
39349
+ let directory = dirname10(fileURLToPath2(from));
39350
+ while (dirname10(directory) !== directory) {
39351
+ if (existsSync5(join5(directory, "raggle.json"))) {
39112
39352
  for (const candidate of [
39113
- join4(dirname9(directory), "raggle-ai-scout", "packages", "local"),
39114
- join4(directory, "..", "..", "raggle-ai-scout", "packages", "local")
39353
+ join5(dirname10(directory), "raggle-ai-scout", "packages", "local"),
39354
+ join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
39115
39355
  ]) {
39116
39356
  if (!candidates.includes(candidate))
39117
39357
  candidates.push(candidate);
39118
39358
  }
39119
39359
  break;
39120
39360
  }
39121
- directory = dirname9(directory);
39361
+ directory = dirname10(directory);
39122
39362
  }
39123
39363
  for (const candidate of candidates) {
39124
39364
  try {
@@ -39127,11 +39367,11 @@ function resolveLocalModule(from = import.meta.url) {
39127
39367
  return candidate;
39128
39368
  } catch {}
39129
39369
  }
39130
- return candidates[0] ?? join4(dirname9(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
39370
+ return candidates[0] ?? join5(dirname10(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
39131
39371
  }
39132
39372
  function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
39133
39373
  return new Promise((resolve, reject) => {
39134
- execFile3(process.execPath, ["-e", script, modulePath, folder], {
39374
+ execFile4(process.execPath, ["-e", script, modulePath, folder], {
39135
39375
  signal,
39136
39376
  timeout: 60000,
39137
39377
  maxBuffer: 8 * 1024 * 1024,
@@ -39227,10 +39467,10 @@ const { stat } = require('node:fs/promises');
39227
39467
  var init_linked_folder_discovery = () => {};
39228
39468
 
39229
39469
  // packages/api/src/server/project-discovery.ts
39230
- import { execFile as execFile4 } from "node:child_process";
39470
+ import { execFile as execFile5 } from "node:child_process";
39231
39471
  function discoverProjects(folder, signal) {
39232
39472
  return new Promise((resolve, reject) => {
39233
- execFile4(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
39473
+ execFile5(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
39234
39474
  signal,
39235
39475
  timeout: 60000,
39236
39476
  maxBuffer: 8 * 1024 * 1024,
@@ -39317,24 +39557,24 @@ var init_project_discovery = __esm(() => {
39317
39557
  // packages/api/src/server/folder-metadata-plugin.ts
39318
39558
  import { createHash as createHash5 } from "node:crypto";
39319
39559
  import { realpathSync as realpathSync2 } from "node:fs";
39320
- import { readdir as readdir5, readFile as readFile10, stat as stat8 } from "node:fs/promises";
39321
- import { homedir as homedir3 } from "node:os";
39560
+ import { readdir as readdir6, readFile as readFile11, realpath as realpath7, stat as stat9 } from "node:fs/promises";
39561
+ import { homedir as homedir4 } from "node:os";
39322
39562
  import {
39323
- basename as basename7,
39324
- dirname as dirname10,
39563
+ basename as basename8,
39564
+ dirname as dirname11,
39325
39565
  extname as extname6,
39326
- isAbsolute as isAbsolute6,
39327
- relative as relative11,
39328
- resolve as resolve12,
39329
- sep as sep7
39566
+ isAbsolute as isAbsolute7,
39567
+ relative as relative12,
39568
+ resolve as resolve13,
39569
+ sep as sep8
39330
39570
  } from "node:path";
39331
- import { execFile as execFile5 } from "node:child_process";
39571
+ import { execFile as execFile6 } from "node:child_process";
39332
39572
  import local from "@raggle-ai/local";
39333
39573
  function metadataFile(path, root) {
39334
- const candidate = relative11(root, path);
39335
- if (!candidate || candidate.startsWith("..") || isAbsolute6(candidate))
39574
+ const candidate = relative12(root, path);
39575
+ if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
39336
39576
  return false;
39337
- const parts = candidate.split(sep7);
39577
+ const parts = candidate.split(sep8);
39338
39578
  if (parts.some((part) => part === "node_modules"))
39339
39579
  return false;
39340
39580
  return metadataExtensions.has(extname6(candidate).toLowerCase());
@@ -39345,11 +39585,11 @@ async function configuredAiInputModule(folder) {
39345
39585
  return typeof module === "string" && module.trim() ? module.trim() : null;
39346
39586
  }
39347
39587
  function folderPath(value, root, lockedRoot) {
39348
- const folder = realpathSync2(resolve12(value ?? root));
39588
+ const folder = realpathSync2(resolve13(value ?? root));
39349
39589
  if (!lockedRoot)
39350
39590
  return folder;
39351
- const path = relative11(lockedRoot, folder);
39352
- if (!path || !path.startsWith("..") && !isAbsolute6(path))
39591
+ const path = relative12(lockedRoot, folder);
39592
+ if (!path || !path.startsWith("..") && !isAbsolute7(path))
39353
39593
  return folder;
39354
39594
  throw new Error(`Folder is outside the viewer root: ${folder}`);
39355
39595
  }
@@ -39413,15 +39653,15 @@ function unique(values) {
39413
39653
  return [...new Set(values)];
39414
39654
  }
39415
39655
  function isMachineRoot(folder) {
39416
- return folder === homedir3() || dirname10(folder) === folder;
39656
+ return folder === homedir4() || dirname11(folder) === folder;
39417
39657
  }
39418
39658
  async function shallowMetadata(folder) {
39419
- const entries = await readdir5(folder, { withFileTypes: true });
39659
+ const entries = await readdir6(folder, { withFileTypes: true });
39420
39660
  const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
39421
39661
  const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname6(entry.name).toLowerCase())).map(async (entry) => {
39422
- const path = resolve12(folder, entry.name);
39423
- const details = await stat8(path);
39424
- const content = await readFile10(path, "utf8").catch(() => "");
39662
+ const path = resolve13(folder, entry.name);
39663
+ const details = await stat9(path);
39664
+ const content = await readFile11(path, "utf8").catch(() => "");
39425
39665
  const title = markdownMetadata(content).title;
39426
39666
  return {
39427
39667
  activityAt: details.mtimeMs,
@@ -39434,10 +39674,10 @@ async function shallowMetadata(folder) {
39434
39674
  };
39435
39675
  }));
39436
39676
  const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
39437
- id: createHash5("sha1").update(resolve12(folder, entry.name)).digest("hex"),
39677
+ id: createHash5("sha1").update(resolve13(folder, entry.name)).digest("hex"),
39438
39678
  name: entry.name,
39439
39679
  relativePath: entry.name,
39440
- worktree: resolve12(folder, entry.name)
39680
+ worktree: resolve13(folder, entry.name)
39441
39681
  })).sort((left, right) => left.name.localeCompare(right.name));
39442
39682
  return {
39443
39683
  changes: { items: [] },
@@ -39463,8 +39703,8 @@ async function loadFolderData(folder) {
39463
39703
  items: projects.map((project) => ({
39464
39704
  id: project.id,
39465
39705
  worktree: project.worktree,
39466
- name: project.name ?? basename7(project.worktree),
39467
- relativePath: relative11(folder, project.worktree)
39706
+ name: project.name ?? basename8(project.worktree),
39707
+ relativePath: relative12(folder, project.worktree)
39468
39708
  }))
39469
39709
  }
39470
39710
  };
@@ -39472,10 +39712,10 @@ async function loadFolderData(folder) {
39472
39712
  async function notesFiles(folder, files, origin) {
39473
39713
  return {
39474
39714
  items: await Promise.all(files.map(async (file) => {
39475
- const path = file.path ?? (file.relativePath ? resolve12(folder, file.relativePath) : undefined);
39715
+ const path = file.path ?? (file.relativePath ? resolve13(folder, file.relativePath) : undefined);
39476
39716
  if (!path)
39477
39717
  return origin ? { ...file, folder: origin } : file;
39478
- const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile10(path, "utf8").catch(() => "") : "";
39718
+ const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile11(path, "utf8").catch(() => "") : "";
39479
39719
  const title = content ? markdownMetadata(content).title : undefined;
39480
39720
  return {
39481
39721
  ...file,
@@ -39486,6 +39726,30 @@ async function notesFiles(folder, files, origin) {
39486
39726
  }))
39487
39727
  };
39488
39728
  }
39729
+ async function managedNotesFiles(directory) {
39730
+ const notes = await listManagedNotes(directory);
39731
+ return Promise.all(notes.map(async (note) => {
39732
+ const content = await readFile11(note.path, "utf8").catch(() => "");
39733
+ const title = content ? markdownMetadata(content).title : undefined;
39734
+ return {
39735
+ ...note,
39736
+ folder: directory,
39737
+ name: title ?? note.name,
39738
+ openHref: openPathHref(note.path)
39739
+ };
39740
+ }));
39741
+ }
39742
+ function insideFolder(path, root) {
39743
+ const local = relative12(root, path);
39744
+ return local === "" || !local.startsWith("..") && !isAbsolute7(local);
39745
+ }
39746
+ async function withoutManagedFiles(items, managedRoot) {
39747
+ if (!managedRoot)
39748
+ return items;
39749
+ const root = await canonicalPath(managedRoot);
39750
+ const outside = await Promise.all(items.map(async (item) => !item.path || !insideFolder(await canonicalPath(item.path), root)));
39751
+ return items.filter((_, index) => outside[index]);
39752
+ }
39489
39753
  async function localProjectSearchCache(cache) {
39490
39754
  if (cache && Date.now() - cache.loadedAt < projectSearchCacheMs)
39491
39755
  return cache;
@@ -39508,16 +39772,16 @@ async function localProjectSearchCache(cache) {
39508
39772
  function mediaTarget(folder, file, lockedTarget) {
39509
39773
  if (!file)
39510
39774
  return;
39511
- const rootReal = realpathSync2(resolve12(folder));
39512
- const path = resolve12(folder, file);
39775
+ const rootReal = realpathSync2(resolve13(folder));
39776
+ const path = resolve13(folder, file);
39513
39777
  let pathReal;
39514
39778
  try {
39515
39779
  pathReal = realpathSync2(path);
39516
39780
  } catch {
39517
39781
  return;
39518
39782
  }
39519
- const rel = relative11(rootReal, pathReal);
39520
- if (rel.startsWith("..") || isAbsolute6(rel))
39783
+ const rel = relative12(rootReal, pathReal);
39784
+ if (rel.startsWith("..") || isAbsolute7(rel))
39521
39785
  return;
39522
39786
  if (lockedTarget && pathReal !== lockedTarget)
39523
39787
  return;
@@ -39556,16 +39820,16 @@ function documentError(response) {
39556
39820
  function documentTarget(folder, file, lockedTarget) {
39557
39821
  if (!file)
39558
39822
  return;
39559
- const rootReal = realpathSync2(resolve12(folder));
39560
- const path = resolve12(folder, file);
39823
+ const rootReal = realpathSync2(resolve13(folder));
39824
+ const path = resolve13(folder, file);
39561
39825
  let pathReal;
39562
39826
  try {
39563
39827
  pathReal = realpathSync2(path);
39564
39828
  } catch {
39565
39829
  return;
39566
39830
  }
39567
- const rel = relative11(rootReal, pathReal);
39568
- if (rel.startsWith("..") || isAbsolute6(rel))
39831
+ const rel = relative12(rootReal, pathReal);
39832
+ if (rel.startsWith("..") || isAbsolute7(rel))
39569
39833
  return;
39570
39834
  if (lockedTarget && pathReal !== lockedTarget)
39571
39835
  return;
@@ -39643,6 +39907,18 @@ function createFolderApi({
39643
39907
  const linkedDiscovery = createLinkedFolderDiscovery(linkedFolderLoader, (folder) => discoveryChanged(folder));
39644
39908
  const linkedState = (folder) => lockedRoot || isMachineRoot(folder) ? { folders: [], status: "ready" } : linkedDiscovery.read(folder);
39645
39909
  const resolveLinkedFolders = (folder) => lockedRoot || isMachineRoot(folder) ? Promise.resolve([]) : linkedDiscovery.folders(folder);
39910
+ const managedNotesFolder = async (folder) => {
39911
+ if (lockedRoot || lockedTarget || isMachineRoot(folder))
39912
+ return;
39913
+ try {
39914
+ if (!(await readRepositoryNotesSettings()).enabled)
39915
+ return;
39916
+ const lookup = await lookupRepositoryNotes(folder);
39917
+ return lookup.status === "registered" ? lookup.notesDirectory : undefined;
39918
+ } catch {
39919
+ return;
39920
+ }
39921
+ };
39646
39922
  const favicon = faviconService();
39647
39923
  return {
39648
39924
  name: "kennel-folder-metadata",
@@ -39685,7 +39961,7 @@ function createFolderApi({
39685
39961
  if (!folder)
39686
39962
  return;
39687
39963
  server.emit({
39688
- data: { path: relative11(folder, path) },
39964
+ data: { path: relative12(folder, path) },
39689
39965
  event: "kennel:metadata-changed",
39690
39966
  type: "custom"
39691
39967
  });
@@ -39768,7 +40044,7 @@ function createFolderApi({
39768
40044
  "Content-Type": asset.mediaType,
39769
40045
  ETag: `"${hash}"`
39770
40046
  });
39771
- response.end(await readFile10(asset.path));
40047
+ response.end(await readFile11(asset.path));
39772
40048
  } catch (error) {
39773
40049
  response.writeHead(404, {
39774
40050
  "Content-Type": "text/plain; charset=utf-8"
@@ -39833,21 +40109,21 @@ function createFolderApi({
39833
40109
  }
39834
40110
  const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
39835
40111
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
39836
- await new Promise((done, fail) => execFile5(command, [folder], (error) => error ? fail(error) : done()));
40112
+ await new Promise((done, fail) => execFile6(command, [folder], (error) => error ? fail(error) : done()));
39837
40113
  response.writeHead(204);
39838
40114
  response.end();
39839
40115
  return;
39840
40116
  }
39841
40117
  if (url.pathname === "/api/documents") {
39842
40118
  const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
39843
- const target = lockedTarget ? documentTarget(folder, relative11(folder, lockedTarget), lockedTarget) : undefined;
40119
+ const target = lockedTarget ? documentTarget(folder, relative12(folder, lockedTarget), lockedTarget) : undefined;
39844
40120
  const dataset = lockedTarget ? {
39845
40121
  root: folder,
39846
40122
  documents: target ? [
39847
40123
  {
39848
- path: relative11(folder, target.path),
40124
+ path: relative12(folder, target.path),
39849
40125
  kind: target.kind,
39850
- content: previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : ""
40126
+ content: previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : ""
39851
40127
  }
39852
40128
  ] : []
39853
40129
  } : await readFolder(folder);
@@ -39863,7 +40139,7 @@ function createFolderApi({
39863
40139
  documentError(response);
39864
40140
  return;
39865
40141
  }
39866
- const details = await stat8(target.path);
40142
+ const details = await stat9(target.path);
39867
40143
  if (!details.isFile()) {
39868
40144
  documentError(response);
39869
40145
  return;
@@ -39875,16 +40151,16 @@ function createFolderApi({
39875
40151
  return;
39876
40152
  }
39877
40153
  watch(folder);
39878
- const content = previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : "";
40154
+ const content = previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : "";
39879
40155
  sendContract(response, url, {
39880
40156
  content,
39881
40157
  kind: target.kind,
39882
- path: relative11(target.root, target.path),
40158
+ path: relative12(target.root, target.path),
39883
40159
  root: folder,
39884
40160
  ...previewTextKind(target.kind) ? {} : {
39885
40161
  rawUrl: `/api/raw?${new URLSearchParams({
39886
40162
  path: folder,
39887
- file: relative11(target.root, target.path)
40163
+ file: relative12(target.root, target.path)
39888
40164
  })}`
39889
40165
  }
39890
40166
  }, {
@@ -39904,7 +40180,7 @@ function createFolderApi({
39904
40180
  documentError(response);
39905
40181
  return;
39906
40182
  }
39907
- const details = await stat8(target.path);
40183
+ const details = await stat9(target.path);
39908
40184
  if (!details.isFile()) {
39909
40185
  documentError(response);
39910
40186
  return;
@@ -39925,10 +40201,10 @@ function createFolderApi({
39925
40201
  ETag: etag,
39926
40202
  "X-Content-Type-Options": "nosniff",
39927
40203
  ...download ? {
39928
- "Content-Disposition": `attachment; filename="${basename7(target.path)}"`
40204
+ "Content-Disposition": `attachment; filename="${basename8(target.path)}"`
39929
40205
  } : {}
39930
40206
  });
39931
- response.end(await readFile10(target.path));
40207
+ response.end(await readFile11(target.path));
39932
40208
  } catch (error) {
39933
40209
  documentError(response);
39934
40210
  }
@@ -39942,7 +40218,7 @@ function createFolderApi({
39942
40218
  documentError(response);
39943
40219
  return;
39944
40220
  }
39945
- const details = await stat8(target.path);
40221
+ const details = await stat9(target.path);
39946
40222
  if (!details.isFile()) {
39947
40223
  documentError(response);
39948
40224
  return;
@@ -39954,7 +40230,7 @@ function createFolderApi({
39954
40230
  return;
39955
40231
  }
39956
40232
  watch(folder);
39957
- const data = await readFile10(target.path);
40233
+ const data = await readFile11(target.path);
39958
40234
  const baseHeaders = {
39959
40235
  "Accept-Ranges": "bytes",
39960
40236
  "Cache-Control": "private, max-age=0, must-revalidate",
@@ -40008,9 +40284,13 @@ function createFolderApi({
40008
40284
  if (!lockedRoot && !isMachineRoot(folder) && request.headers["x-kennel-retry-linked"] === "1")
40009
40285
  linkedDiscovery.retry(folder);
40010
40286
  const discovery = linkedState(folder);
40287
+ const managed = await managedNotesFolder(folder);
40288
+ if (managed)
40289
+ watch(managed);
40011
40290
  const result = await plugin.handle({
40012
40291
  folder,
40013
- linkedFolders: discovery.folders
40292
+ linkedFolders: discovery.folders,
40293
+ ...managed ? { managedFolders: [managed] } : {}
40014
40294
  }, subpath);
40015
40295
  if (result.kind === "unavailable") {
40016
40296
  response.writeHead(200, {
@@ -40113,7 +40393,7 @@ function createFolderApi({
40113
40393
  workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
40114
40394
  const files = [
40115
40395
  workspace.configPath,
40116
- resolve12(workspace.root, "raggle.json")
40396
+ resolve13(workspace.root, "raggle.json")
40117
40397
  ].filter((path) => Boolean(path));
40118
40398
  for (const path of files)
40119
40399
  contexts.add(path);
@@ -40137,7 +40417,14 @@ function createFolderApi({
40137
40417
  const machine = isMachineRoot(folder);
40138
40418
  if (part === "notes") {
40139
40419
  const scope = url.searchParams.get("notesScope");
40420
+ const managed = await managedNotesFolder(folder);
40140
40421
  const localFiles = scope === "linked" ? Promise.resolve({ items: [] }) : (machine ? shallowMetadata(folder).then((data) => data.files.items) : listLocalMarkdownFiles({ folder, limit: 200 })).then((files) => notesFiles(folder, files));
40422
+ const managedFiles = async () => {
40423
+ if (scope === "linked" || !managed)
40424
+ return [];
40425
+ watch(managed);
40426
+ return managedNotesFiles(managed);
40427
+ };
40141
40428
  const linkedFiles = async () => {
40142
40429
  if (scope === "local" || machine || lockedRoot)
40143
40430
  return [];
@@ -40148,20 +40435,25 @@ function createFolderApi({
40148
40435
  throw new Error("Linked notes discovery failed. Retry to try again.");
40149
40436
  return Promise.all(linkedFolders.map(async (linkedFolder) => {
40150
40437
  watch(linkedFolder);
40151
- return notesFiles(linkedFolder, await listLocalMarkdownFiles({
40438
+ const group = await notesFiles(linkedFolder, await listLocalMarkdownFiles({
40152
40439
  folder: linkedFolder,
40153
40440
  limit: 200
40154
40441
  }), linkedFolder);
40442
+ return {
40443
+ items: await withoutManagedFiles(group.items, managed)
40444
+ };
40155
40445
  }));
40156
40446
  };
40157
- const [files, linked] = await Promise.all([
40447
+ const [files, managedItems, linked] = await Promise.all([
40158
40448
  localFiles,
40449
+ managedFiles(),
40159
40450
  linkedFiles()
40160
40451
  ]);
40161
40452
  sendContract(response, url, {
40162
40453
  files: {
40163
40454
  items: [
40164
40455
  ...files.items,
40456
+ ...managedItems,
40165
40457
  ...linked.flatMap((group) => group.items)
40166
40458
  ]
40167
40459
  },
@@ -40242,7 +40534,7 @@ function createFolderApi({
40242
40534
  }
40243
40535
  };
40244
40536
  }
40245
- var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts;
40537
+ 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);
40246
40538
  var init_folder_metadata_plugin = __esm(() => {
40247
40539
  init_contracts();
40248
40540
  init_markdown();
@@ -40252,6 +40544,7 @@ var init_folder_metadata_plugin = __esm(() => {
40252
40544
  init_server();
40253
40545
  init_workspace2();
40254
40546
  init_server2();
40547
+ init_store();
40255
40548
  init_entity_folder();
40256
40549
  init_document_edit();
40257
40550
  init_create_agent_file();
@@ -40305,29 +40598,29 @@ var init_folder_metadata_plugin = __esm(() => {
40305
40598
  import {
40306
40599
  createServer
40307
40600
  } from "node:http";
40308
- import { readFile as readFile11, realpath as realpath5, stat as stat9 } from "node:fs/promises";
40309
- import { extname as extname7, isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13 } from "node:path";
40601
+ import { readFile as readFile12, realpath as realpath8, stat as stat10 } from "node:fs/promises";
40602
+ import { extname as extname7, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
40310
40603
  async function sendFile(request, response, directory, pathname, fallback) {
40311
- const root = await realpath5(directory);
40604
+ const root = await realpath8(directory);
40312
40605
  const inside = (path) => {
40313
- const local = relative12(root, path);
40314
- return local !== ".." && !local.startsWith("../") && !isAbsolute7(local);
40606
+ const local = relative13(root, path);
40607
+ return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
40315
40608
  };
40316
- let target = resolve13(root, `.${decodeURIComponent(pathname)}`);
40609
+ let target = resolve14(root, `.${decodeURIComponent(pathname)}`);
40317
40610
  if (!inside(target)) {
40318
40611
  response.writeHead(403);
40319
40612
  response.end();
40320
40613
  return;
40321
40614
  }
40322
- if (!(await stat9(target).catch(() => null))?.isFile()) {
40615
+ if (!(await stat10(target).catch(() => null))?.isFile()) {
40323
40616
  if (!fallback) {
40324
40617
  response.writeHead(404);
40325
40618
  response.end();
40326
40619
  return;
40327
40620
  }
40328
- target = resolve13(root, fallback);
40621
+ target = resolve14(root, fallback);
40329
40622
  }
40330
- target = await realpath5(target);
40623
+ target = await realpath8(target);
40331
40624
  if (!inside(target)) {
40332
40625
  response.writeHead(403);
40333
40626
  response.end();
@@ -40337,7 +40630,7 @@ async function sendFile(request, response, directory, pathname, fallback) {
40337
40630
  "Content-Type": contentTypes[extname7(target)] ?? "application/octet-stream",
40338
40631
  "X-Content-Type-Options": "nosniff"
40339
40632
  });
40340
- response.end(request.method === "HEAD" ? undefined : await readFile11(target));
40633
+ response.end(request.method === "HEAD" ? undefined : await readFile12(target));
40341
40634
  }
40342
40635
  function createKennelApi(options) {
40343
40636
  const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
@@ -40513,7 +40806,7 @@ var init_server4 = __esm(() => {
40513
40806
  import { execFileSync as execFileSync3 } from "node:child_process";
40514
40807
  import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
40515
40808
  import { createServer as createServer2 } from "node:net";
40516
- import { dirname as dirname11, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
40809
+ import { dirname as dirname12, isAbsolute as isAbsolute9, relative as relative14, resolve as resolve15 } from "node:path";
40517
40810
  import { fileURLToPath as fileURLToPath3 } from "node:url";
40518
40811
  function isLoopbackHost(host) {
40519
40812
  return loopbackHosts.has(host) || host.startsWith("127.");
@@ -40592,7 +40885,7 @@ function splitViewerArgs(input) {
40592
40885
  }
40593
40886
  if (arg.startsWith("-"))
40594
40887
  throw new Error(`Unknown viewer option: ${arg}`);
40595
- const candidate = resolve14(arg);
40888
+ const candidate = resolve15(arg);
40596
40889
  const isDirectory = existsSync6(candidate) && statSync3(candidate).isDirectory();
40597
40890
  if (isDirectory) {
40598
40891
  if (hasRoot)
@@ -40603,7 +40896,7 @@ function splitViewerArgs(input) {
40603
40896
  }
40604
40897
  if (hasRoot)
40605
40898
  throw new Error("kennel accepts one folder");
40606
- options.root = dirname11(candidate);
40899
+ options.root = dirname12(candidate);
40607
40900
  options.target = candidate;
40608
40901
  hasRoot = true;
40609
40902
  }
@@ -40644,7 +40937,7 @@ function popUrl(host, port, options) {
40644
40937
  url.pathname = `/folder/${encodeBase64Url2(options.root)}/notes`;
40645
40938
  return url.href;
40646
40939
  }
40647
- url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(relative13(options.root, options.target))}`;
40940
+ url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(relative14(options.root, options.target))}`;
40648
40941
  if (options.watch)
40649
40942
  url.searchParams.set("watch", "1");
40650
40943
  return url.href;
@@ -40655,8 +40948,8 @@ function viewerPortWasExplicit(input) {
40655
40948
  function isSameOrDescendant(parent, child) {
40656
40949
  const parentReal = realpathSync3(parent);
40657
40950
  const childReal = realpathSync3(child);
40658
- const childRelative = relative13(parentReal, childReal);
40659
- return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute8(childRelative);
40951
+ const childRelative = relative14(parentReal, childReal);
40952
+ return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute9(childRelative);
40660
40953
  }
40661
40954
  async function runningViewer(url) {
40662
40955
  try {
@@ -40717,8 +41010,8 @@ async function viewer(input) {
40717
41010
  await hotViewer(options);
40718
41011
  return;
40719
41012
  }
40720
- const web = resolve14(dirname11(fileURLToPath3(import.meta.url)), "../../web");
40721
- if (!existsSync6(resolve14(web, "index.html"))) {
41013
+ const web = resolve15(dirname12(fileURLToPath3(import.meta.url)), "../../web");
41014
+ if (!existsSync6(resolve15(web, "index.html"))) {
40722
41015
  throw new Error(`Kennel web app not found. Run the CLI build first.`);
40723
41016
  }
40724
41017
  const remote = options.tailscale || !isLoopbackHost(options.host);
@@ -40857,14 +41150,14 @@ async function startProtectedShare(handle) {
40857
41150
  var init_protected_share = () => {};
40858
41151
 
40859
41152
  // packages/preview/src/tailscale-share.ts
40860
- import { execFile as execFile6 } from "node:child_process";
40861
- import { promisify } from "node:util";
41153
+ import { execFile as execFile7 } from "node:child_process";
41154
+ import { promisify as promisify2 } from "node:util";
40862
41155
  async function serveConfig() {
40863
- const { stdout } = await run("tailscale", ["serve", "status", "--json"]);
41156
+ const { stdout } = await run2("tailscale", ["serve", "status", "--json"]);
40864
41157
  return JSON.parse(stdout);
40865
41158
  }
40866
41159
  async function tailscaleHostname() {
40867
- const { stdout } = await run("tailscale", ["status", "--json"]);
41160
+ const { stdout } = await run2("tailscale", ["status", "--json"]);
40868
41161
  const status = JSON.parse(stdout);
40869
41162
  const host = status.Self?.DNSName?.replace(/\.$/, "");
40870
41163
  if (status.BackendState !== "Running" || !host || !host.endsWith(".ts.net")) {
@@ -40883,11 +41176,11 @@ async function startTailscaleProxy(host, port) {
40883
41176
  const current = await serveConfig();
40884
41177
  const handlers = current.Web?.[authority]?.Handlers;
40885
41178
  if (handlers?.["/"]?.Proxy === target && Object.keys(handlers).length === 1) {
40886
- await run("tailscale", ["serve", `--https=${port}`, "off"]);
41179
+ await run2("tailscale", ["serve", `--https=${port}`, "off"]);
40887
41180
  }
40888
41181
  };
40889
41182
  try {
40890
- await run("tailscale", ["serve", "--bg", "--yes", `--https=${port}`, target], { timeout: 30000 });
41183
+ await run2("tailscale", ["serve", "--bg", "--yes", `--https=${port}`, target], { timeout: 30000 });
40891
41184
  const current = await serveConfig();
40892
41185
  if (current.AllowFunnel?.[authority] || current.Web?.[authority]?.Handlers?.["/"]?.Proxy !== target) {
40893
41186
  throw new Error("Tailscale did not create the expected private HTTPS proxy");
@@ -40898,13 +41191,13 @@ async function startTailscaleProxy(host, port) {
40898
41191
  throw error;
40899
41192
  }
40900
41193
  }
40901
- var run;
41194
+ var run2;
40902
41195
  var init_tailscale_share = __esm(() => {
40903
- run = promisify(execFile6);
41196
+ run2 = promisify2(execFile7);
40904
41197
  });
40905
41198
 
40906
41199
  // packages/preview/src/share.ts
40907
- import { dirname as dirname12, resolve as resolve15 } from "node:path";
41200
+ import { dirname as dirname13, resolve as resolve16 } from "node:path";
40908
41201
  import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
40909
41202
  import { fileURLToPath as fileURLToPath4 } from "node:url";
40910
41203
  function splitShareArgs(input) {
@@ -40956,7 +41249,7 @@ function splitShareArgs(input) {
40956
41249
  if (folder && !existsSync7(folder)) {
40957
41250
  throw new Error(`Folder not found: ${folder}`);
40958
41251
  }
40959
- options.folder = folder ? resolve15(folder) : undefined;
41252
+ options.folder = folder ? resolve16(folder) : undefined;
40960
41253
  return options;
40961
41254
  }
40962
41255
  async function runningViewerRoot(port = plainViewerPort) {
@@ -40977,8 +41270,8 @@ async function runningViewerRoot(port = plainViewerPort) {
40977
41270
  }
40978
41271
  async function startPreviewShare(target) {
40979
41272
  const root = realpathSync4(target.root);
40980
- const web = resolve15(dirname12(fileURLToPath4(import.meta.url)), "../../web");
40981
- if (!existsSync7(resolve15(web, "index.html"))) {
41273
+ const web = resolve16(dirname13(fileURLToPath4(import.meta.url)), "../../web");
41274
+ if (!existsSync7(resolve16(web, "index.html"))) {
40982
41275
  throw new Error("Kennel web app not found. Run the CLI build first.");
40983
41276
  }
40984
41277
  const host = await tailscaleHostname();