kennel 0.2.43 → 0.2.45

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 (85) hide show
  1. package/dist/core/api-client.d.ts +1 -1
  2. package/dist/core/api-client.js +35 -2
  3. package/dist/core/runtime.js +548 -99
  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 +760 -495
  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-C95Ks6-o.js → abnfDiagram-VRR7QNED-D8mHNUwf.js} +1 -1
  16. package/web/assets/{arc-BZo1f2tP.js → arc-DORR-Lbn.js} +1 -1
  17. package/web/assets/{architectureDiagram-ZJ3FMSHR-DeheWY9Z.js → architectureDiagram-ZJ3FMSHR-CnmL2DAX.js} +1 -1
  18. package/web/assets/ask-tray-KUTWZ4MV-VQJJAsDn.js +1 -0
  19. package/web/assets/{blockDiagram-677ZJIJ3-C2gPgefk.js → blockDiagram-677ZJIJ3-_SWUT5nP.js} +1 -1
  20. package/web/assets/{c4Diagram-LMCZKHZV-D7eqpETg.js → c4Diagram-LMCZKHZV-r7ghc180.js} +1 -1
  21. package/web/assets/channel-r9-1szjb.js +1 -0
  22. package/web/assets/{chunk-2Q5K7J3B-CqPrOden.js → chunk-2Q5K7J3B-D_LeMwFJ.js} +1 -1
  23. package/web/assets/{chunk-32BRIVSS-CxWYyFcn.js → chunk-32BRIVSS-AeVZw1It.js} +1 -1
  24. package/web/assets/{chunk-5VM5RSS4-WxfwUe_w.js → chunk-5VM5RSS4-Bp_epQ8r.js} +1 -1
  25. package/web/assets/{chunk-EX3LRPZG-BwbKQfAB.js → chunk-EX3LRPZG-Dz4n-DeM.js} +1 -1
  26. package/web/assets/{chunk-JWPE2WC7-CY2fyOB-.js → chunk-JWPE2WC7-B0MQp8TQ.js} +1 -1
  27. package/web/assets/{chunk-MOJQB5TN-BJb4GtCJ.js → chunk-MOJQB5TN-Bkomqk1x.js} +1 -1
  28. package/web/assets/{chunk-RYQCIY6F-FZB0Ijl-.js → chunk-RYQCIY6F-B0F7WMyE.js} +1 -1
  29. package/web/assets/{chunk-V7JOEXUC-Bat_9367.js → chunk-V7JOEXUC-BDifU9oY.js} +1 -1
  30. package/web/assets/{chunk-VR4S4FIN-BV_nIsil.js → chunk-VR4S4FIN-Bgd4HCG_.js} +1 -1
  31. package/web/assets/{chunk-XXDRQBXY-nZF49NM1.js → chunk-XXDRQBXY-BK8FhRs4.js} +1 -1
  32. package/web/assets/classDiagram-OUVF2IWQ-L_jmDmNd.js +1 -0
  33. package/web/assets/classDiagram-v2-EOCWNBFH-L_jmDmNd.js +1 -0
  34. package/web/assets/client-5Q4VI7ZT-BkTtFalV.js +52 -0
  35. package/web/assets/{cose-bilkent-JH36ORCC-5jLaefF7.js → cose-bilkent-JH36ORCC-Dm3k2U47.js} +1 -1
  36. package/web/assets/{cynefin-VYW2F7L2-D_M6zxNK.js → cynefin-VYW2F7L2-DCnilF1f.js} +1 -1
  37. package/web/assets/{cynefinDiagram-TSTJHNR4-hmCcn34X.js → cynefinDiagram-TSTJHNR4-BrulEEQ9.js} +1 -1
  38. package/web/assets/{dagre-VKFMJZFB-Brka7Xhg.js → dagre-VKFMJZFB-Dda61i4h.js} +1 -1
  39. package/web/assets/{diagram-FQU43EPY-BWpeshcb.js → diagram-FQU43EPY-sTmDEDMM.js} +1 -1
  40. package/web/assets/{diagram-G47NLZAW-Cl2XbP7n.js → diagram-G47NLZAW-d-KQnPrm.js} +1 -1
  41. package/web/assets/{diagram-NH7WQ7WH-BBF0AyF6.js → diagram-NH7WQ7WH-DcRxa_ps.js} +1 -1
  42. package/web/assets/{diagram-OA4YK3LP-ggxUOgcy.js → diagram-OA4YK3LP-CYzWtWvV.js} +1 -1
  43. package/web/assets/{diagram-WEI45ONY-5gqEm0IZ.js → diagram-WEI45ONY-FI3V_N07.js} +1 -1
  44. package/web/assets/document-view-TGFRNHKS-VSJS3BGw.js +24 -0
  45. package/web/assets/{ebnfDiagram-CCIWWBDH-DvOOCI9m.js → ebnfDiagram-CCIWWBDH-DcvVmQhu.js} +1 -1
  46. package/web/assets/{entity-search-page-YP4SPMBC-CL6z_JLV.js → entity-search-page-RIYQYIJJ-Bed3y-5Q.js} +5 -5
  47. package/web/assets/{erDiagram-Q63AITRT-LMvg3eDB.js → erDiagram-Q63AITRT-DI5DBlBp.js} +1 -1
  48. package/web/assets/{flowDiagram-23GEKE2U-BkWKqObX.js → flowDiagram-23GEKE2U-Bz1AbRxB.js} +1 -1
  49. package/web/assets/{ganttDiagram-NO4QXBWP-DUGEnNqM.js → ganttDiagram-NO4QXBWP-ChKlhxeo.js} +1 -1
  50. package/web/assets/{gitGraphDiagram-IHSO6WYX-CDQG334H.js → gitGraphDiagram-IHSO6WYX-BtTqLSyY.js} +1 -1
  51. package/web/assets/{index-B4WnXxGr.js → index-CDAH4UPU.js} +157 -157
  52. package/web/assets/{infoDiagram-FWYZ7A6U-BVS6-42t.js → infoDiagram-FWYZ7A6U-COpIz9uZ.js} +1 -1
  53. package/web/assets/{ishikawaDiagram-FXEZZL3T-B63B2D4K.js → ishikawaDiagram-FXEZZL3T-CB7Bdsfr.js} +1 -1
  54. package/web/assets/{journeyDiagram-5HDEW3XC-DFZcImlX.js → journeyDiagram-5HDEW3XC-BMytnJBE.js} +1 -1
  55. package/web/assets/{kanban-definition-HUTT4EX6-Cmd4XrqE.js → kanban-definition-HUTT4EX6-BFS4G0g-.js} +1 -1
  56. package/web/assets/{linear-DAkYxlnA.js → linear-DMUO2qBp.js} +1 -1
  57. package/web/assets/{linked-session-header-DOEX4FYM-D0mAsins.js → linked-session-header-7BUTWROP-DzEcA4Fq.js} +1 -1
  58. package/web/assets/{mermaid.core-BE2AlF1A.js → mermaid.core-BAj-Ltr-.js} +4 -4
  59. package/web/assets/{mindmap-definition-LN4V7U3C-SuG3IorS.js → mindmap-definition-LN4V7U3C-CjymOEvH.js} +1 -1
  60. package/web/assets/{pegDiagram-2B236MQR-Bscdsbzn.js → pegDiagram-2B236MQR-D64jiUPu.js} +1 -1
  61. package/web/assets/{pieDiagram-ENE6RG2P-Dg_t_TWu.js → pieDiagram-ENE6RG2P-CCbI5U6o.js} +1 -1
  62. package/web/assets/{plugin-host-modules-CcpBt3st.js → plugin-host-modules-D7pHtNJn.js} +2 -2
  63. package/web/assets/{project-search-page-TZG7AZBQ-DrMBMFbS.js → project-search-page-O6YRLDGU-1bPrifgb.js} +2 -2
  64. package/web/assets/{quadrantDiagram-ABIIQ3AL-CHImGA_b.js → quadrantDiagram-ABIIQ3AL-B7dN_Kn5.js} +1 -1
  65. package/web/assets/{railroadDiagram-RFXS5EU6-BZRS_CFF.js → railroadDiagram-RFXS5EU6-BPjAzqcL.js} +1 -1
  66. package/web/assets/{requirementDiagram-TGXJPOKE-Ckw1abgN.js → requirementDiagram-TGXJPOKE-BFhnnUWa.js} +1 -1
  67. package/web/assets/{sankeyDiagram-HTMAVEWB-BZVBAe8k.js → sankeyDiagram-HTMAVEWB-W2UguUjW.js} +1 -1
  68. package/web/assets/{selection-popup-ABYH3A27-D0_B1ozz.js → selection-popup-ABYH3A27-DsiLbG7S.js} +4 -4
  69. package/web/assets/{sequenceDiagram-DBY2YBRQ-DA8YzEAG.js → sequenceDiagram-DBY2YBRQ-DcOakOD4.js} +1 -1
  70. package/web/assets/{sizeCapture-X5ZJPWSS-B_aUT6yb.js → sizeCapture-X5ZJPWSS-D3N248Z6.js} +1 -1
  71. package/web/assets/{stateDiagram-2N3HPSRC-DE0B_1vK.js → stateDiagram-2N3HPSRC-Cir3yiDe.js} +1 -1
  72. package/web/assets/{stateDiagram-v2-6OUMAXLB-LQnwbWz3.js → stateDiagram-v2-6OUMAXLB-BwL0KYLn.js} +1 -1
  73. package/web/assets/{swimlanes-5IMT3BWC-DGjZrxBh.js → swimlanes-5IMT3BWC-Dhq7FmZr.js} +2 -2
  74. package/web/assets/swimlanesDiagram-G3AALYLV-D4q-nPpB.js +8 -0
  75. package/web/assets/{timeline-definition-FHXFAJF6-BnDMvIhe.js → timeline-definition-FHXFAJF6-CMa1wjsI.js} +1 -1
  76. package/web/assets/{vennDiagram-L72KCM5P-Bolx4asy.js → vennDiagram-L72KCM5P-DvglazwQ.js} +1 -1
  77. package/web/assets/{wardleyDiagram-EHGQE667-DKGCLLoK.js → wardleyDiagram-EHGQE667-DcogrL32.js} +1 -1
  78. package/web/assets/{xychartDiagram-FW5EYKEG-BEPuzAws.js → xychartDiagram-FW5EYKEG-BlsDcr_U.js} +1 -1
  79. package/web/index.html +1 -1
  80. package/dist/site/terminal.d.ts +0 -14
  81. package/web/assets/ask-tray-KUTWZ4MV-CxvfIw9u.js +0 -24
  82. package/web/assets/channel-CrJW7F7C.js +0 -1
  83. package/web/assets/classDiagram-OUVF2IWQ-D9l66AaJ.js +0 -1
  84. package/web/assets/classDiagram-v2-EOCWNBFH-D9l66AaJ.js +0 -1
  85. package/web/assets/swimlanesDiagram-G3AALYLV-C-GBzQUn.js +0 -8
@@ -6,10 +6,10 @@ import { createConnection } from 'node:net';
6
6
  import { tmpdir } from 'node:os';
7
7
  import { dirname, relative, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
- import { popUrl, pluginViewLinks } from './viewer.js';
9
+ import { popUrl } from './viewer.js';
10
10
  import { createKennelClient } from '../core/api-client.js';
11
11
  import { pluginSetKey } from '../plugins/external.js';
12
- import { openPreview, previewTerminal } from './terminal.js';
12
+ import { openPreview, previewTerminal } from '../tui/launcher.js';
13
13
  function sourceRoot() {
14
14
  return resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
15
15
  }
@@ -179,7 +179,7 @@ export async function hotViewer(options, restarting = false) {
179
179
  return hotViewer(options, true);
180
180
  throw new Error(stale);
181
181
  }
182
- const terminal = previewTerminal(viewerUrl, options, pluginViewLinks(viewerUrl, options.root));
182
+ const terminal = previewTerminal(viewerUrl, options);
183
183
  if (options.open)
184
184
  openPreview(viewerUrl);
185
185
  try {
@@ -245,7 +245,7 @@ export async function hotViewer(options, restarting = false) {
245
245
  },
246
246
  stdio: ['ignore', 'inherit', 'inherit'],
247
247
  });
248
- const terminal = previewTerminal(viewerUrl, options, pluginViewLinks(viewerUrl, options.root));
248
+ const terminal = previewTerminal(viewerUrl, options);
249
249
  void terminal.closed.then(() => {
250
250
  if (child.exitCode === null && child.signalCode === null)
251
251
  child.kill('SIGTERM');
@@ -1,8 +1,8 @@
1
1
  // packages/api/src/server/plugins.ts
2
2
  import { pathToFileURL } from "node:url";
3
- import { existsSync } from "node:fs";
4
- import { mkdir, readFile, stat as stat2, writeFile } from "node:fs/promises";
5
- import { dirname, resolve as resolve2, sep as sep2 } from "node:path";
3
+ import { existsSync as existsSync2 } from "node:fs";
4
+ import { mkdir, readFile as readFile2, stat as stat4, writeFile } from "node:fs/promises";
5
+ import { dirname as dirname4, resolve as resolve5, sep as sep4 } from "node:path";
6
6
 
7
7
  // packages/plugins/src/server.ts
8
8
  class FolderPluginFailure extends Error {
@@ -12,15 +12,453 @@ class FolderPluginFailure extends Error {
12
12
  }
13
13
  }
14
14
 
15
+ // packages/knl/src/server.ts
16
+ import { stat as stat2 } from "node:fs/promises";
17
+ import { join } from "node:path";
18
+
19
+ // packages/knl/src/knl.ts
20
+ import { readdir, stat } from "node:fs/promises";
21
+ import { basename, dirname as dirname3, relative, resolve as resolve3, sep as sep2 } from "node:path";
22
+ import * as esbuild from "esbuild";
23
+
24
+ // packages/knl/src/folder-guard.ts
25
+ import { existsSync, realpathSync } from "node:fs";
26
+ import { realpath } from "node:fs/promises";
27
+ import { dirname, resolve, sep } from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+ var knlHostResolveDir = dirname(fileURLToPath(import.meta.url));
30
+ var knlHostPackageFolders = (() => {
31
+ const folders = [];
32
+ let current = knlHostResolveDir;
33
+ while (true) {
34
+ const nodeModules = resolve(current, "node_modules");
35
+ if (existsSync(nodeModules))
36
+ folders.push(nodeModules);
37
+ const parent = dirname(current);
38
+ if (parent === current)
39
+ return folders;
40
+ current = parent;
41
+ }
42
+ })();
43
+ async function knlRealRoot(owner) {
44
+ const expected = resolve(await realpath(owner), "knl");
45
+ const real = await realpath(resolve(owner, "knl"));
46
+ if (real !== expected)
47
+ throw new Error("knl/ must be a folder, not a link to another place.");
48
+ return real;
49
+ }
50
+ async function knlReadRoots(owner) {
51
+ const packages = await Promise.all(knlHostPackageFolders.map((folder) => realpath(folder).catch(() => {
52
+ return;
53
+ })));
54
+ return [
55
+ await knlRealRoot(owner),
56
+ ...packages.filter((folder) => Boolean(folder))
57
+ ];
58
+ }
59
+ function isKnlReadable(file, roots) {
60
+ let real;
61
+ try {
62
+ real = realpathSync(file);
63
+ } catch {
64
+ real = resolve(file);
65
+ }
66
+ return roots.some((root) => real === root || real.startsWith(`${root}${sep}`));
67
+ }
68
+ function knlReadError(file) {
69
+ return `KNL pages can only use files in knl/ and installed packages: ${file}`;
70
+ }
71
+ function assertKnlReadable(file, roots) {
72
+ if (!isKnlReadable(file, roots))
73
+ throw new Error(knlReadError(file));
74
+ }
75
+
76
+ // packages/knl/src/tailwind.ts
77
+ import { readFile } from "node:fs/promises";
78
+ import { createRequire } from "node:module";
79
+ import { dirname as dirname2, resolve as resolve2 } from "node:path";
80
+ import { compile } from "@tailwindcss/node";
81
+ import { Scanner } from "@tailwindcss/oxide";
82
+ var require2 = createRequire(import.meta.url);
83
+ function starterKnlStyles() {
84
+ return `@layer theme, utilities;
85
+ @import "tailwindcss/theme.css" layer(theme) prefix(knl);
86
+ @import "tailwindcss/utilities.css" layer(utilities) source(none);
87
+
88
+ @theme {
89
+ --font-sans: system-ui, sans-serif;
90
+ }
91
+ `;
92
+ }
93
+ async function compileKnlCss(js, stylesheet, roots) {
94
+ const base = dirname2(stylesheet);
95
+ assertKnlReadable(stylesheet, roots);
96
+ const css = await readFile(stylesheet, "utf8").catch((error) => {
97
+ if (error.code === "ENOENT")
98
+ return starterKnlStyles();
99
+ throw error;
100
+ });
101
+ const compiler = await compile(css, {
102
+ base,
103
+ from: stylesheet,
104
+ onDependency: (path) => assertKnlReadable(path, roots),
105
+ customJsResolver: async (id) => {
106
+ throw new Error(`KNL does not load Tailwind plugins or configs (${id}).`);
107
+ },
108
+ customCssResolver: async (id) => id === "tailwindcss" || id.startsWith("tailwindcss/") ? require2.resolve(id) : undefined
109
+ });
110
+ const sources = [...compiler.sources];
111
+ if (compiler.root !== "none") {
112
+ sources.push({
113
+ ...compiler.root ?? { base, pattern: "**/*" },
114
+ negated: false
115
+ });
116
+ }
117
+ for (const source of sources) {
118
+ if (source.negated)
119
+ continue;
120
+ const fixed = source.pattern.split("/");
121
+ const glob = fixed.findIndex((part) => /[*?[{]/.test(part));
122
+ assertKnlReadable(resolve2(source.base, fixed.slice(0, glob < 0 ? undefined : glob).join("/")), roots);
123
+ }
124
+ const scanner = new Scanner({ sources });
125
+ const candidates = [
126
+ ...scanner.scan(),
127
+ ...scanner.scanFiles([{ content: js, extension: "js" }])
128
+ ];
129
+ return compiler.build(candidates);
130
+ }
131
+
132
+ // packages/knl/src/knl.ts
133
+ var KNK_FOLDER = "knl";
134
+ var MAX_SOURCE_BYTES = 512 * 1024;
135
+ var MAX_JS_BYTES = 5 * 1024 * 1024;
136
+ var MODULE_FILES = ["index.tsx", "index.ts"];
137
+ function sourceFor(file, knlRoot, scanRoot, owner) {
138
+ const id = toModuleId(file, knlRoot);
139
+ return {
140
+ id,
141
+ file,
142
+ relative: relative(scanRoot, file).split(sep2).join("/"),
143
+ folder: dirname3(file),
144
+ owner
145
+ };
146
+ }
147
+ function toModuleId(file, knlRoot) {
148
+ const relativeKnl = relative(knlRoot, file).split(sep2).join("/");
149
+ return relativeKnl.replace(/\/?index\.(tsx|ts)$/, "") || "index";
150
+ }
151
+ async function collectKnlSources(scanRoot, owner) {
152
+ const knlRoot = resolve3(scanRoot, KNK_FOLDER);
153
+ const knlEntries = await readdir(knlRoot, { withFileTypes: true }).catch(() => []);
154
+ const sources = [];
155
+ const addModule = async (folder) => {
156
+ for (const candidate of MODULE_FILES) {
157
+ const file = resolve3(folder, candidate);
158
+ if (await stat(file).then((d) => d.isFile()).catch(() => false)) {
159
+ sources.push(sourceFor(file, knlRoot, scanRoot, owner));
160
+ return;
161
+ }
162
+ }
163
+ };
164
+ await addModule(knlRoot);
165
+ for (const entry of knlEntries) {
166
+ if (!entry.isDirectory())
167
+ continue;
168
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
169
+ continue;
170
+ await addModule(resolve3(knlRoot, entry.name));
171
+ }
172
+ return sources.sort((left, right) => {
173
+ const rank = (left.id === "index" ? 0 : 1) - (right.id === "index" ? 0 : 1);
174
+ return rank !== 0 ? rank : left.id.localeCompare(right.id);
175
+ });
176
+ }
177
+ async function loadKnlSources(viewRoot, linkedFolders = []) {
178
+ const sources = [
179
+ ...await collectKnlSources(viewRoot, viewRoot),
180
+ ...(await Promise.all(linkedFolders.map(async (folder) => (await collectKnlSources(folder, folder)).map((source) => ({
181
+ ...source,
182
+ id: `${basename(folder)}:${source.id}`,
183
+ relative: `${basename(folder)}/${source.relative}`
184
+ }))))).flat()
185
+ ];
186
+ return { sources };
187
+ }
188
+ var MAX_CACHED_MODULES = 200;
189
+ var compileCache = new Map;
190
+ function knlNodePaths(knlRoot) {
191
+ return [resolve3(knlRoot, "node_modules"), ...knlHostPackageFolders];
192
+ }
193
+ async function compileKnlSource(source) {
194
+ const sourceBytes = (await stat(source.file)).size;
195
+ const tooLarge = (bytes, what) => ({
196
+ id: source.id,
197
+ relative: source.relative,
198
+ js: "",
199
+ css: "",
200
+ issues: [
201
+ {
202
+ path: source.relative,
203
+ message: `Module ${what} is too large (${bytes} bytes; limit ${MAX_SOURCE_BYTES}).`
204
+ }
205
+ ]
206
+ });
207
+ if (sourceBytes > MAX_SOURCE_BYTES)
208
+ return tooLarge(sourceBytes, "source");
209
+ const cached = compileCache.get(source.file);
210
+ const unchanged = cached && (await Promise.all(cached.dependencies.map(async ({ file, mtimeMs }) => (await stat(file).catch(() => {
211
+ return;
212
+ }))?.mtimeMs === mtimeMs))).every(Boolean);
213
+ const knlRoot = resolve3(source.owner, KNK_FOLDER);
214
+ let issuePath = source.relative;
215
+ try {
216
+ const nodePaths = knlNodePaths(knlRoot);
217
+ const roots = await knlReadRoots(source.owner);
218
+ assertKnlReadable(source.file, roots);
219
+ let js;
220
+ if (cached && unchanged) {
221
+ js = cached.js;
222
+ } else {
223
+ const result = await esbuild.build({
224
+ absWorkingDir: source.folder,
225
+ stdin: {
226
+ contents: `import { createElement } from "react"
227
+ import { createRoot } from "react-dom/client"
228
+ import Component from ${JSON.stringify(source.file)}
229
+
230
+ globalThis.__knlMount = (container, context, onError) => {
231
+ const root = createRoot(container, { onUncaughtError: onError })
232
+ root.render(createElement(Component, context))
233
+ }
234
+ `,
235
+ sourcefile: `${source.file}.mount.ts`,
236
+ loader: "ts",
237
+ resolveDir: source.folder
238
+ },
239
+ bundle: true,
240
+ format: "iife",
241
+ target: "es2022",
242
+ jsx: "automatic",
243
+ platform: "browser",
244
+ write: false,
245
+ minify: false,
246
+ sourcemap: false,
247
+ logLevel: "silent",
248
+ metafile: true,
249
+ nodePaths,
250
+ plugins: [readGuard(roots)]
251
+ });
252
+ js = result.outputFiles?.[0]?.text ?? "";
253
+ const files = new Set([
254
+ resolve3(knlRoot, "package.json"),
255
+ ...Object.keys(result.metafile.inputs).map((file) => resolve3(source.folder, file)).filter((file) => file !== `${source.file}.mount.ts`)
256
+ ]);
257
+ const dependencies = await Promise.all([...files].map(async (file) => ({
258
+ file,
259
+ mtimeMs: (await stat(file).catch(() => {
260
+ return;
261
+ }))?.mtimeMs
262
+ })));
263
+ compileCache.delete(source.file);
264
+ compileCache.set(source.file, { js, dependencies });
265
+ if (compileCache.size > MAX_CACHED_MODULES)
266
+ compileCache.delete(compileCache.keys().next().value);
267
+ }
268
+ const stylesheet = resolve3(knlRoot, "style.css");
269
+ issuePath = relative(source.owner, stylesheet).split(sep2).join("/");
270
+ const css = await compileKnlCss(js, stylesheet, roots);
271
+ issuePath = source.relative;
272
+ if (js.length + css.length > MAX_JS_BYTES) {
273
+ return {
274
+ id: source.id,
275
+ relative: source.relative,
276
+ js: "",
277
+ css: "",
278
+ issues: [
279
+ {
280
+ path: source.relative,
281
+ message: `Compiled module is too large (${js.length + css.length} bytes; limit ${MAX_JS_BYTES}).`
282
+ }
283
+ ]
284
+ };
285
+ }
286
+ return { id: source.id, relative: source.relative, js, css, issues: [] };
287
+ } catch (error) {
288
+ return {
289
+ id: source.id,
290
+ relative: source.relative,
291
+ js: "",
292
+ css: "",
293
+ issues: knlBuildIssues(error, issuePath, source)
294
+ };
295
+ }
296
+ }
297
+ var guardResolve = Symbol("knl-guard");
298
+ function readGuard(roots) {
299
+ return {
300
+ name: "knl-read-guard",
301
+ setup(build) {
302
+ build.onResolve({ filter: /.*/ }, async (args) => {
303
+ if (args.pluginData === guardResolve)
304
+ return;
305
+ const resolveFrom = (resolveDir) => build.resolve(args.path, {
306
+ importer: args.importer,
307
+ kind: args.kind,
308
+ pluginData: guardResolve,
309
+ resolveDir
310
+ });
311
+ const readable = (result) => result.namespace !== "file" || !result.path || isKnlReadable(result.path, roots);
312
+ let result = await resolveFrom(args.resolveDir);
313
+ const bare = !/^(?:\.{1,2}(?:\/|$)|\/)/.test(args.path);
314
+ if (bare && result.errors.length === 0 && !readable(result))
315
+ result = await resolveFrom(knlHostResolveDir);
316
+ if (result.errors.length > 0)
317
+ return { errors: result.errors };
318
+ if (!readable(result))
319
+ return { errors: [{ text: knlReadError(args.path) }] };
320
+ return result;
321
+ });
322
+ }
323
+ };
324
+ }
325
+ function knlBuildIssues(error, relativePath, source) {
326
+ const failures = error?.errors;
327
+ if (Array.isArray(failures) && failures.length > 0) {
328
+ const first = failures[0];
329
+ const file = first?.location?.file ? relative(source.owner, resolve3(source.folder, first.location.file)).split(sep2).join("/") : relativePath;
330
+ const location = first?.location ? `${file}:${first.location.line ?? "?"}` : relativePath;
331
+ return [
332
+ { path: location, message: first?.text ?? "Module failed to compile." }
333
+ ];
334
+ }
335
+ return [
336
+ {
337
+ path: relativePath,
338
+ message: error instanceof Error && error.message ? error.message : "Module failed to compile."
339
+ }
340
+ ];
341
+ }
342
+ function starterKnlSource() {
343
+ return `// knl/index.tsx — this folder's homepage (KNL folder web framework).
344
+ // Every other page lives in its own folder: knl/<page>/index.tsx.
345
+ import * as React from "react"
346
+
347
+ export default function Home() {
348
+ return (
349
+ <div className="knl:p-4 knl:font-sans">
350
+ <h1 className="knl:m-0 knl:text-2xl">Home</h1>
351
+ <p>Edit this file to build the homepage.</p>
352
+ </div>
353
+ )
354
+ }
355
+ `;
356
+ }
357
+
358
+ // packages/knl/src/manifest.ts
359
+ var knlManifest = {
360
+ id: "knl",
361
+ label: "KNL"
362
+ };
363
+
364
+ // packages/knl/src/server.ts
365
+ function createKnlPlugin() {
366
+ return {
367
+ ...knlManifest,
368
+ async detect(context) {
369
+ const hasKnlFolder = await stat2(join(context.folder, KNK_FOLDER)).then((entry) => entry.isDirectory()).catch(() => false);
370
+ if (!hasKnlFolder)
371
+ return { status: "unavailable" };
372
+ const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
373
+ if (!sources.some((source) => source.id === "index"))
374
+ return { status: "unavailable" };
375
+ return {
376
+ count: sources.length,
377
+ pages: sources.filter((source) => source.id !== "index" && !source.id.endsWith(":index")).map((source) => ({
378
+ id: source.id,
379
+ label: source.id.replaceAll(/[-_]/g, " ").replace(/^./, (letter) => letter.toUpperCase())
380
+ })),
381
+ status: "available"
382
+ };
383
+ },
384
+ async handle(context, subpath) {
385
+ const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
386
+ const folder = context.folder;
387
+ if (sources.length === 0) {
388
+ return { kind: "available", data: { folder, status: "unavailable" } };
389
+ }
390
+ const requested = subpath.trim() || "index";
391
+ const selected = sources.filter((source) => source.id === requested);
392
+ if (selected.length === 0) {
393
+ return {
394
+ kind: "available",
395
+ data: {
396
+ folder,
397
+ status: "error",
398
+ message: `KNL page not found: ${requested}`
399
+ }
400
+ };
401
+ }
402
+ const compiled = await Promise.all(selected.map(compileKnlSource));
403
+ const entries = compiled.map((result) => {
404
+ const source = sources.find((item) => item.id === result.id);
405
+ return {
406
+ id: result.id,
407
+ relative: result.relative,
408
+ ...result.js ? { js: result.js } : {},
409
+ css: result.css,
410
+ ...result.issues.length > 0 ? { issues: result.issues } : {},
411
+ owner: source?.owner ?? folder
412
+ };
413
+ });
414
+ const failing = entries.filter((entry) => entry.issues?.length);
415
+ if (failing.length > 0) {
416
+ return {
417
+ kind: "available",
418
+ data: {
419
+ folder,
420
+ status: "error",
421
+ entries,
422
+ issues: failing[0]?.issues
423
+ }
424
+ };
425
+ }
426
+ return {
427
+ kind: "available",
428
+ data: {
429
+ folder,
430
+ status: "available",
431
+ entries
432
+ }
433
+ };
434
+ },
435
+ async init() {
436
+ return {
437
+ files: [
438
+ { path: "knl/index.tsx", content: starterKnlSource() },
439
+ { path: "knl/style.css", content: starterKnlStyles() },
440
+ {
441
+ path: "knl/package.json",
442
+ content: `${JSON.stringify({ private: true, type: "module" }, null, 2)}
443
+ `
444
+ },
445
+ { path: "knl/.gitignore", content: `node_modules/
446
+ ` }
447
+ ]
448
+ };
449
+ }
450
+ };
451
+ }
452
+
15
453
  // packages/notes/src/server.ts
16
- import { readdir, realpath, stat } from "node:fs/promises";
454
+ import { readdir as readdir2, realpath as realpath2, stat as stat3 } from "node:fs/promises";
17
455
  import {
18
- basename,
456
+ basename as basename2,
19
457
  extname,
20
458
  isAbsolute,
21
- relative,
22
- resolve,
23
- sep
459
+ relative as relative2,
460
+ resolve as resolve4,
461
+ sep as sep3
24
462
  } from "node:path";
25
463
 
26
464
  // packages/notes/src/manifest.ts
@@ -42,11 +480,11 @@ var llmNoteLimit = 20;
42
480
  async function listMarkdownFiles(folder) {
43
481
  const files = [];
44
482
  const visit = async (current) => {
45
- const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
483
+ const entries = await readdir2(current, { withFileTypes: true }).catch(() => []);
46
484
  for (const entry of entries) {
47
485
  if (entry.name === "node_modules" || entry.name.startsWith("."))
48
486
  continue;
49
- const path = resolve(current, entry.name);
487
+ const path = resolve4(current, entry.name);
50
488
  if (entry.isDirectory()) {
51
489
  await visit(path);
52
490
  continue;
@@ -54,22 +492,22 @@ async function listMarkdownFiles(folder) {
54
492
  if (!entry.isFile() || !markdownExtensions.has(extname(entry.name))) {
55
493
  continue;
56
494
  }
57
- const details = await stat(path).catch(() => {
495
+ const details = await stat3(path).catch(() => {
58
496
  return;
59
497
  });
60
498
  files.push({
61
499
  activityAt: details?.mtimeMs ?? 0,
62
- name: basename(entry.name, extname(entry.name)),
63
- relativePath: relative(folder, path).split(sep).join("/")
500
+ name: basename2(entry.name, extname(entry.name)),
501
+ relativePath: relative2(folder, path).split(sep3).join("/")
64
502
  });
65
503
  }
66
504
  };
67
505
  await visit(folder);
68
506
  return files.sort((left, right) => right.activityAt - left.activityAt);
69
507
  }
70
- var canonical = (path) => realpath(path).catch(() => path);
508
+ var canonical = (path) => realpath2(path).catch(() => path);
71
509
  function inside(path, root) {
72
- const local = relative(root, path);
510
+ const local = relative2(root, path);
73
511
  return local === "" || !local.startsWith("..") && !isAbsolute(local);
74
512
  }
75
513
  async function withoutManaged(groups, managedRoots) {
@@ -81,7 +519,7 @@ async function withoutManaged(groups, managedRoots) {
81
519
  return { ...group, files: [] };
82
520
  }
83
521
  const files = await Promise.all(group.files.map(async (file) => {
84
- const real = await canonical(resolve(group.folder, file.relativePath));
522
+ const real = await canonical(resolve4(group.folder, file.relativePath));
85
523
  return roots.some((root) => inside(real, root)) ? undefined : file;
86
524
  }));
87
525
  return { ...group, files: files.filter((file) => file !== undefined) };
@@ -131,7 +569,7 @@ async function notesLlmSections({
131
569
  continue;
132
570
  seen.add(identity);
133
571
  const files = await listMarkdownFiles(source);
134
- const paths = files.map((file) => resolve(source, file.relativePath));
572
+ const paths = files.map((file) => resolve4(source, file.relativePath));
135
573
  groups.push({ folder: source, paths });
136
574
  files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
137
575
  }
@@ -203,14 +641,14 @@ function parsePluginRegistryConfig(content) {
203
641
  };
204
642
  }
205
643
  async function nearestKennelConfig(folder) {
206
- for (let current = folder;; current = dirname(current)) {
207
- const candidate = resolve2(current, "kennel.json");
208
- const details = await stat2(candidate).catch(() => {
644
+ for (let current = folder;; current = dirname4(current)) {
645
+ const candidate = resolve5(current, "kennel.json");
646
+ const details = await stat4(candidate).catch(() => {
209
647
  return;
210
648
  });
211
649
  if (details?.isFile())
212
650
  return candidate;
213
- if (dirname(current) === current)
651
+ if (dirname4(current) === current)
214
652
  return;
215
653
  }
216
654
  }
@@ -219,13 +657,13 @@ async function folderPluginRegistryConfig(folder) {
219
657
  if (!path)
220
658
  return defaultPluginRegistryConfig;
221
659
  try {
222
- return parsePluginRegistryConfig(await readFile(path, "utf8"));
660
+ return parsePluginRegistryConfig(await readFile2(path, "utf8"));
223
661
  } catch {
224
662
  return defaultPluginRegistryConfig;
225
663
  }
226
664
  }
227
665
  function createFolderServerPlugins() {
228
- return [createNotesPlugin()];
666
+ return [createNotesPlugin(), createKnlPlugin()];
229
667
  }
230
668
  function folderServerRegistry() {
231
669
  return new Map(createFolderServerPlugins().map((plugin) => [plugin.id, plugin]));
@@ -267,7 +705,7 @@ async function folderServerRegistryAsync() {
267
705
  ...server,
268
706
  async detect(context) {
269
707
  const detection = await server.detect(context);
270
- if (detection.status === "unavailable" && existsSync(resolve2(context.folder, entry.trigger))) {
708
+ if (detection.status === "unavailable" && existsSync2(resolve5(context.folder, entry.trigger))) {
271
709
  return { status: "available" };
272
710
  }
273
711
  return detection;
@@ -305,7 +743,7 @@ async function externalApiHandlers() {
305
743
  return handlers;
306
744
  }
307
745
  async function scaffoldFolderPlugins(folder, options = {}) {
308
- const root = resolve2(folder);
746
+ const root = resolve5(folder);
309
747
  const registry = folderServerRegistry();
310
748
  const created = [];
311
749
  const skipped = [];
@@ -316,8 +754,8 @@ async function scaffoldFolderPlugins(folder, options = {}) {
316
754
  const context = { folder: root };
317
755
  const result = await plugin.init(context);
318
756
  const files = result.files.filter((file) => {
319
- const target = resolve2(root, file.path);
320
- const contained = target === root || target.startsWith(root + sep2);
757
+ const target = resolve5(root, file.path);
758
+ const contained = target === root || target.startsWith(root + sep4);
321
759
  return contained && file.path !== ".";
322
760
  });
323
761
  supported.push({
@@ -325,15 +763,15 @@ async function scaffoldFolderPlugins(folder, options = {}) {
325
763
  files: files.map((file) => file.path)
326
764
  });
327
765
  for (const file of files) {
328
- const target = resolve2(root, file.path);
329
- const exists = await stat2(target).then(() => true).catch(() => false);
766
+ const target = resolve5(root, file.path);
767
+ const exists = await stat4(target).then(() => true).catch(() => false);
330
768
  if (exists && !options.force) {
331
769
  skipped.push({ path: file.path, plugin: plugin.id });
332
770
  continue;
333
771
  }
334
772
  if (options.write === false)
335
773
  continue;
336
- await mkdir(dirname(target), { recursive: true });
774
+ await mkdir(dirname4(target), { recursive: true });
337
775
  await writeFile(target, file.content);
338
776
  created.push({ path: file.path, plugin: plugin.id });
339
777
  }
@@ -1,6 +1,3 @@
1
- import { type PluginView } from "./terminal.js";
2
- /** Installed-plugin view links for the terminal menu; each is hyperlinked. */
3
- export declare function pluginViewLinks(viewerUrl: string, root: string): PluginView[];
4
1
  export type ViewerOptions = {
5
2
  dryRun: boolean;
6
3
  host: string;