kennel 0.2.21 → 0.2.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -8
- package/dist/cli/dispatch.js +7 -5
- package/dist/cli/usage.js +5 -1
- package/dist/package/preview.js +138 -67
- package/dist/plugins/external.d.ts +1 -0
- package/dist/plugins/external.js +17 -0
- package/dist/site/hot-viewer.d.ts +3 -1
- package/dist/site/hot-viewer.js +43 -33
- package/dist/site/plugin.d.ts +2 -0
- package/dist/site/plugin.js +32 -0
- package/dist/site/terminal.d.ts +1 -0
- package/dist/site/terminal.js +77 -0
- package/dist/site/viewer.d.ts +6 -6
- package/dist/site/viewer.js +39 -32
- package/package.json +2 -2
- package/schemas/config.schema.json +9 -0
- package/web/assets/{abnfDiagram-VRR7QNED-D8JkjVKl.js → abnfDiagram-VRR7QNED-aRemVX7f.js} +1 -1
- package/web/assets/{arc-BU-iDKmm.js → arc-BpDsnNTf.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-CqrAC8xg.js → architectureDiagram-ZJ3FMSHR-DNXHQddP.js} +1 -1
- package/web/assets/ask-tray-PMT6VVXN-HMPARq3A.js +23 -0
- package/web/assets/{blockDiagram-677ZJIJ3-CwKkdWVm.js → blockDiagram-677ZJIJ3-CF4H4upo.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-C0j-XG7a.js → c4Diagram-LMCZKHZV-BPWUnjtx.js} +1 -1
- package/web/assets/channel-CEZ4sDYU.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-D91WnA8d.js → chunk-2Q5K7J3B-DGsa7uv7.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-CMUWV3Yb.js → chunk-32BRIVSS-BRfic3W2.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-CBonp6Qu.js → chunk-5VM5RSS4-L2h7hliu.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-B5p4u10U.js → chunk-EX3LRPZG-BPgD_fgx.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-BvcBor40.js → chunk-JWPE2WC7-CckCaqu_.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-eoj4Adi1.js → chunk-MOJQB5TN-zyGgx-VW.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-DlFyEcEU.js → chunk-RYQCIY6F-Drzpg5IG.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-Drt-X5bO.js → chunk-V7JOEXUC-Di4ESKz-.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BHmZP44S.js → chunk-VR4S4FIN-C5lQtDI1.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-BpRFqfxG.js → chunk-XXDRQBXY-Cell0ft2.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-CjxqMQn2.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-CjxqMQn2.js +1 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BWofo0Io.js → cose-bilkent-JH36ORCC-CiKtOFxl.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-vZngODMB.js → cynefin-VYW2F7L2-B75MWf5b.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-Cexlv1-W.js → cynefinDiagram-TSTJHNR4-BmNVkSWW.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-Z9zxnPrH.js → dagre-VKFMJZFB-BcM1Wqu5.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-DePIt5Jb.js → diagram-FQU43EPY-BiBWrxa8.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-BhO62yXV.js → diagram-G47NLZAW-Cfxukr7T.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BPB7x0TD.js → diagram-NH7WQ7WH-C83VkBYW.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-BTu87fOH.js → diagram-OA4YK3LP-Oj0uj6CX.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-NbsxbHXS.js → diagram-WEI45ONY-DhbL0nvt.js} +1 -1
- package/web/assets/{ebnfDiagram-CCIWWBDH-DcabTYw5.js → ebnfDiagram-CCIWWBDH-D4JYp1UC.js} +1 -1
- package/web/assets/{entity-search-page-G6QEQNXT-_igDx9q_.js → entity-search-page-G6QEQNXT-UKzUU5id.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-qZDSW4hk.js → erDiagram-Q63AITRT-C-oyRt9s.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-BKWFG8oh.js → flowDiagram-23GEKE2U-0Tplj9FK.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-RVyAZqbP.js → ganttDiagram-NO4QXBWP-wgh2e0B_.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-DyJiGxOQ.js → gitGraphDiagram-IHSO6WYX-D6z9qDXa.js} +1 -1
- package/web/assets/index-D_Fcgs3V.js +451 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-11u7EDGL.js → infoDiagram-FWYZ7A6U-D3o7el5X.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-BExEsB80.js → ishikawaDiagram-FXEZZL3T-CM0QyWYH.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-CBxrerT9.js → journeyDiagram-5HDEW3XC-fmIoh0h6.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-zA-xYwTx.js → kanban-definition-HUTT4EX6-aEtqsiWf.js} +1 -1
- package/web/assets/{linear-DfGuGay4.js → linear-aJ-2UZO5.js} +1 -1
- package/web/assets/{linked-session-header-M7WY6IGF-BZNiFQoC.js → linked-session-header-M7WY6IGF-CltMJZiX.js} +1 -1
- package/web/assets/{markdown-D8rwfvNM.js → markdown-BP-54rPU.js} +1 -1
- package/web/assets/{mermaid.core-B11SxqQq.js → mermaid.core-CL9g6xVu.js} +5 -5
- package/web/assets/{mindmap-definition-LN4V7U3C-Cxv0my2M.js → mindmap-definition-LN4V7U3C-CtQo0XrW.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-BneyaGqf.js → pegDiagram-2B236MQR-ecu1L_3A.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-CBUh5NNN.js → pieDiagram-ENE6RG2P-CdCKg3Cn.js} +1 -1
- package/web/assets/{plugin-host-modules-_3hYj4i2.js → plugin-host-modules-Dz_BPJRS.js} +1 -1
- package/web/assets/{project-search-dialog-HJFGW4DG-t9HH44Ls.js → project-search-dialog-HJFGW4DG-CKtXc-UP.js} +1 -1
- package/web/assets/project-search-page-NKQEBGPF-CMzuYEeM.js +2 -0
- package/web/assets/{quadrantDiagram-ABIIQ3AL-_70f5MLq.js → quadrantDiagram-ABIIQ3AL-Bt8r5TlW.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-BjRVGtzp.js → railroadDiagram-RFXS5EU6-DrWDVEVi.js} +1 -1
- package/web/assets/react-DELVxLrX.js +9 -0
- package/web/assets/{requirementDiagram-TGXJPOKE-b4jG1W5K.js → requirementDiagram-TGXJPOKE-Ewjwpo6V.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-Cid_FHOs.js → sankeyDiagram-HTMAVEWB-CJCMHuzX.js} +1 -1
- package/web/assets/{selection-popup-532EHNCZ-Czn9J64H.js → selection-popup-532EHNCZ-py5aCy1E.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-XmWVFjsx.js → sequenceDiagram-DBY2YBRQ-Chj-4L6Q.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-DcCu2WOn.js → sizeCapture-X5ZJPWSS-BguWhDIF.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-DJJtthm-.js → stateDiagram-2N3HPSRC-BWisWMpK.js} +1 -1
- package/web/assets/stateDiagram-v2-6OUMAXLB-AiBXToz-.js +1 -0
- package/web/assets/{swimlanes-5IMT3BWC-DafiRSZ_.js → swimlanes-5IMT3BWC-CxVQ97_C.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-DliPmGmf.js +8 -0
- package/web/assets/{tanstack-DzYfxaKh.js → tanstack-CRaSxWtK.js} +1 -1
- package/web/assets/{timeline-definition-FHXFAJF6-BSuB4MkD.js → timeline-definition-FHXFAJF6-CCbI0InF.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-DzQg-Ww0.js → vennDiagram-L72KCM5P-mA5yKpJj.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-DQuK20Nj.js → wardleyDiagram-EHGQE667-1Epw2EsU.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DVpYoZuG.js → xychartDiagram-FW5EYKEG-DM1CMD-p.js} +1 -1
- package/web/index.html +4 -4
- package/dist/site/mail.d.ts +0 -1
- package/dist/site/mail.js +0 -10
- package/web/assets/ask-tray-PMT6VVXN-wk47U8hG.js +0 -23
- package/web/assets/channel-DPvp96Av.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-UEDL7zk8.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-UEDL7zk8.js +0 -1
- package/web/assets/index-DDNcaGdr.js +0 -451
- package/web/assets/project-search-page-NKQEBGPF-C-SLaeRU.js +0 -2
- package/web/assets/react-Df0zP-bB.js +0 -9
- package/web/assets/stateDiagram-v2-6OUMAXLB-N45X3hYp.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-CAU4pVsD.js +0 -8
package/README.md
CHANGED
|
@@ -31,10 +31,12 @@ kennel pop plan.md --watch
|
|
|
31
31
|
kennel pop artifacts/pricing-card.html --lan
|
|
32
32
|
kennel view /path/to/workspace
|
|
33
33
|
kennel serve /path/to/workspace
|
|
34
|
+
knl tasks /path/to/workspace
|
|
35
|
+
knl mail /path/to/workspace --item draft-id
|
|
34
36
|
kennel dev /path/to/docs-site --config index.json
|
|
35
37
|
kennel init .
|
|
36
|
-
kennel plugin add https://github.com
|
|
37
|
-
kennel plugin add https://github.com/raggle-ai/kennel-plugins.git --path plugins/calendar
|
|
38
|
+
kennel plugin add https://github.com/<owner>/<plugin-repo>.git#main
|
|
39
|
+
kennel plugin add https://github.com/raggle-ai/kennel-starter-plugins.git --path plugins/calendar
|
|
38
40
|
kennel core-folders .
|
|
39
41
|
kennel mcp generate . --config index.json
|
|
40
42
|
kennel publish init cloudflare .
|
|
@@ -44,7 +46,7 @@ kennel dp .
|
|
|
44
46
|
|
|
45
47
|
`kennel pop <file>` is the pop-up preview: it starts the static viewer rooted at
|
|
46
48
|
the file's folder, prints a clickable `/folder/<id>/document/<id>` URL, and
|
|
47
|
-
opens it in the default
|
|
49
|
+
opens it in the default browser only when `--open` is supplied. Markdown, HTML artifacts, text, images,
|
|
48
50
|
code, CSV/TSV tables, and `.eml` files are recognized by extension; images and
|
|
49
51
|
binary files are served through `/api/raw`. `--watch` makes the pop-up refresh
|
|
50
52
|
on save (the URL carries `?watch=1`). Folder mode (`--lan`/`--tailscale`)
|
|
@@ -63,6 +65,30 @@ endpoints (`/api/open-path`, `/api/open-folder`) return 403, ACP sessions are
|
|
|
63
65
|
disabled, HTML is never streamed inline from `/api/raw`, and mutation POSTs
|
|
64
66
|
require a same-origin `Origin`/`Referer`. The default host stays `127.0.0.1`.
|
|
65
67
|
|
|
68
|
+
### Terminal output
|
|
69
|
+
|
|
70
|
+
`knl`, file previews, and plugin commands show a named terminal link:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
(open my-folder)
|
|
74
|
+
|
|
75
|
+
shortcuts:
|
|
76
|
+
o - open in browser
|
|
77
|
+
ctrl+c - exit
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The `open my-folder` label is clickable in terminals that support hyperlinks. File previews use the file name instead. Press `o` without Enter to open the printed link in the default browser. Ctrl+C restores the terminal and exits the command; it does not stop a viewer that the command reused.
|
|
81
|
+
|
|
82
|
+
When output is redirected, Kennel prints the label and full URL without colors, terminal escape codes, or keyboard controls. `--open` still opens the browser explicitly; `--dry-run` remains non-interactive.
|
|
83
|
+
|
|
84
|
+
### Installed plugins as commands
|
|
85
|
+
|
|
86
|
+
Every installed plugin ID is a top-level command: `knl tasks`, `knl calendar`, or `knl flights`. The current folder is used unless you pass another folder. Add `--item <id>` to select a plugin-owned detail view.
|
|
87
|
+
|
|
88
|
+
Plugins can declare optional `aliases` in `package.json > kennel.plugin` or `kennel.plugin.json`. The `kennel-email` package uses ID `email` with alias `mail`, so `knl email` and `knl mail` select the same view. Other plugins need no alias declaration when their ID is already the desired command. Reinstall a plugin after changing aliases.
|
|
89
|
+
|
|
90
|
+
Built-in commands take priority over plugin IDs; plugin IDs take priority over aliases. Ambiguous aliases are refused. Use `./tasks` rather than `tasks` to open a folder whose name is also a plugin command. Plugin commands use the same viewer startup as other previews, not a plugin-specific server.
|
|
91
|
+
|
|
66
92
|
### Localhost convention (one viewer on port 5180)
|
|
67
93
|
|
|
68
94
|
Kennel can keep one always-running localhost viewer at
|
|
@@ -88,8 +114,11 @@ The same user config stores trusted external folder plugins added with
|
|
|
88
114
|
command pins the requested ref, or `HEAD` with no fragment, to a full commit,
|
|
89
115
|
selects the repository root or the requested subdirectory as the plugin package
|
|
90
116
|
root, validates package metadata and explicit client/server/manifest exports
|
|
91
|
-
plus optional API/CSS exports,
|
|
92
|
-
|
|
117
|
+
plus optional API/CSS exports, installs the package's declared production
|
|
118
|
+
`dependencies` with npm (no dev or peer dependencies, no install scripts),
|
|
119
|
+
compiles the plugin in the XDG cache, and only then atomically updates config.
|
|
120
|
+
React, `kennel-plugins` and the other shared viewer packages always come from
|
|
121
|
+
Kennel, so plugins declare them as peers. Installed plugin imports are derived from
|
|
93
122
|
package metadata, not from URL text. The transcripts plugin contract is package
|
|
94
123
|
`kennel-transcripts-plugin`, id `memos`, trigger `memos.json`.
|
|
95
124
|
|
|
@@ -139,7 +168,7 @@ well-known scratch folder across any AI client.
|
|
|
139
168
|
`kennel init` scaffolds starter content files for installed folder plugins into
|
|
140
169
|
the current folder (or the given path). The external Tasks and Calendar plugins
|
|
141
170
|
provide `todo.json` and `calendar.json` templates after they are installed from
|
|
142
|
-
`raggle-ai/kennel-plugins` with `kennel plugin add ... --path plugins/<id>`.
|
|
171
|
+
`raggle-ai/kennel-starter-plugins` with `kennel plugin add ... --path plugins/<id>`.
|
|
143
172
|
Existing files are left untouched; `--plugin <id>` scaffolds one plugin,
|
|
144
173
|
`--force` overwrites, and `--list` prints what would be created without
|
|
145
174
|
writing.
|
|
@@ -151,8 +180,10 @@ diagnostics come from the declared schema and Markdown frontmatter. Pass
|
|
|
151
180
|
list. See [Core folders and metadata indexing](../docs/core-folders.md) for
|
|
152
181
|
patterns, parser support, navigation labels and refresh semantics.
|
|
153
182
|
|
|
154
|
-
`kennel view` starts the source Vite viewer
|
|
155
|
-
external plugins are installed.
|
|
183
|
+
In a Kennel checkout, `kennel view` starts the source Vite viewer, also when
|
|
184
|
+
external plugins are installed. An npm install has no source checkout, so
|
|
185
|
+
`kennel view` and `kennel open` start the packaged static viewer, the same as
|
|
186
|
+
`kennel serve`. React and CSS changes use HMR. Changes to
|
|
156
187
|
schema and content files cause a metadata refetch without a full page reload.
|
|
157
188
|
The Vite viewer and the packaged static viewer load the same compiled plugin
|
|
158
189
|
build and report its plugin-set key from `/api/health`. The hot
|
package/dist/cli/dispatch.js
CHANGED
|
@@ -97,11 +97,6 @@ export async function dispatch(argv) {
|
|
|
97
97
|
await viewer(['--hot', ...args]);
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
if (command === 'mail') {
|
|
101
|
-
const { mail } = await import('../site/mail.js');
|
|
102
|
-
await mail(args);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
100
|
if (command === 'serve') {
|
|
106
101
|
const { viewer } = await import('../site/viewer.js');
|
|
107
102
|
await viewer(['--static', ...args]);
|
|
@@ -155,6 +150,13 @@ export async function dispatch(argv) {
|
|
|
155
150
|
deploy(args);
|
|
156
151
|
return;
|
|
157
152
|
}
|
|
153
|
+
// Built-in commands win; use an explicit path (./tasks) to open a folder
|
|
154
|
+
// whose name is also an installed plugin command.
|
|
155
|
+
if (/^[a-z][a-z0-9-]*$/u.test(command)) {
|
|
156
|
+
const { pluginCommand } = await import('../site/plugin.js');
|
|
157
|
+
if (await pluginCommand(command, args))
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
158
160
|
// Positionals that are flags or existing paths still go straight to the
|
|
159
161
|
// viewer (`kennel [folder]`, `kennel note.md`, `kennel --lan .`). Unknown
|
|
160
162
|
// command words fail loudly instead of silently opening a viewer.
|
package/dist/cli/usage.js
CHANGED
|
@@ -9,7 +9,7 @@ export function usage() {
|
|
|
9
9
|
kennel pop|p <file|folder> [--open] [--watch] [--host <host>] [--port <port>] [--lan|--tailscale]
|
|
10
10
|
kennel view [folder] [--host <host>] [--port <port>] [--dry-run]
|
|
11
11
|
kennel open|o [folder] [--open] [--host <host>] [--port <port>] [--dry-run]
|
|
12
|
-
|
|
12
|
+
knl <plugin-id|alias> [folder] [--item <id>] [--open] [--dry-run]
|
|
13
13
|
kennel serve [folder] [--host <host>] [--port <port>] [--lan|--tailscale]
|
|
14
14
|
kennel share [folder] [--lan|--tailscale] [--port <port>] [--dry-run]
|
|
15
15
|
kennel dev [path] [--config <json-file>] [--port <port>] [--script <script>] [--dry-run] [-- <args...>]
|
|
@@ -22,5 +22,9 @@ export function usage() {
|
|
|
22
22
|
kennel publish vercel [path] [--config <json-file>] [--dry-run] [--force] [--hostname <hostname>] [--skip-test]
|
|
23
23
|
kennel deploy [path] [--config <json-file>] [legacy alias for publish]
|
|
24
24
|
kennel dp [path] [--config <json-file>] [legacy alias for publish]
|
|
25
|
+
|
|
26
|
+
Installed plugin ids are commands, for example knl tasks or knl calendar.
|
|
27
|
+
Plugins can declare extra aliases, for example knl mail for plugin id email.
|
|
28
|
+
Use --item <id> for a plugin detail view. Built-in commands take priority.
|
|
25
29
|
`);
|
|
26
30
|
}
|
package/dist/package/preview.js
CHANGED
|
@@ -215,6 +215,81 @@ var init_src = __esm(() => {
|
|
|
215
215
|
init_kinds();
|
|
216
216
|
});
|
|
217
217
|
|
|
218
|
+
// packages/preview/src/terminal.ts
|
|
219
|
+
import { execFile } from "node:child_process";
|
|
220
|
+
import { basename } from "node:path";
|
|
221
|
+
import { emitKeypressEvents } from "node:readline";
|
|
222
|
+
function openPreview(url) {
|
|
223
|
+
const command = process.platform === "darwin" ? "/usr/bin/open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
224
|
+
execFile(command, [url], (error) => {
|
|
225
|
+
if (error)
|
|
226
|
+
console.error(`Could not open browser: ${error.message}`);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function previewTerminal(url, target) {
|
|
230
|
+
url = new URL(url).href;
|
|
231
|
+
const input = process.stdin;
|
|
232
|
+
const output = process.stdout;
|
|
233
|
+
const terminal = Boolean(output.isTTY && process.env.TERM !== "dumb");
|
|
234
|
+
const interactive = terminal && Boolean(input.isTTY);
|
|
235
|
+
const name = (basename(target.target ?? target.root) || target.root).replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
|
|
236
|
+
const label = `(open ${name})`;
|
|
237
|
+
const color = terminal && process.env.NO_COLOR === undefined && output.hasColors();
|
|
238
|
+
const accent = (text) => color ? `\x1B[36m${text}\x1B[39m` : text;
|
|
239
|
+
const link = terminal ? `\x1B]8;;${url}\x1B\\${accent(label)}\x1B]8;;\x1B\\` : `${label}
|
|
240
|
+
${url}`;
|
|
241
|
+
const wasRaw = Boolean(input.isRaw);
|
|
242
|
+
const wasFlowing = input.readableFlowing === true;
|
|
243
|
+
let resolveClosed;
|
|
244
|
+
const closed = new Promise((resolve) => {
|
|
245
|
+
resolveClosed = resolve;
|
|
246
|
+
});
|
|
247
|
+
let finished = false;
|
|
248
|
+
const close = () => {
|
|
249
|
+
if (finished)
|
|
250
|
+
return;
|
|
251
|
+
finished = true;
|
|
252
|
+
process.off("SIGINT", close);
|
|
253
|
+
process.off("SIGTERM", close);
|
|
254
|
+
process.off("exit", close);
|
|
255
|
+
if (interactive) {
|
|
256
|
+
input.off("keypress", keypress);
|
|
257
|
+
input.off("end", close);
|
|
258
|
+
input.setRawMode(wasRaw);
|
|
259
|
+
if (!wasFlowing)
|
|
260
|
+
input.pause();
|
|
261
|
+
}
|
|
262
|
+
resolveClosed();
|
|
263
|
+
};
|
|
264
|
+
const keypress = (_text, key) => {
|
|
265
|
+
if (key.ctrl && (key.name === "c" || key.name === "d"))
|
|
266
|
+
close();
|
|
267
|
+
else if (key.name === "o" && !key.ctrl && !key.meta)
|
|
268
|
+
openPreview(url);
|
|
269
|
+
};
|
|
270
|
+
process.once("SIGINT", close);
|
|
271
|
+
process.once("SIGTERM", close);
|
|
272
|
+
process.once("exit", close);
|
|
273
|
+
if (interactive) {
|
|
274
|
+
emitKeypressEvents(input);
|
|
275
|
+
input.setRawMode(true);
|
|
276
|
+
input.on("keypress", keypress);
|
|
277
|
+
input.once("end", close);
|
|
278
|
+
input.resume();
|
|
279
|
+
}
|
|
280
|
+
console.log(`
|
|
281
|
+
${link}
|
|
282
|
+
`);
|
|
283
|
+
if (interactive) {
|
|
284
|
+
console.log(` shortcuts:
|
|
285
|
+
${accent("o")} - open in browser
|
|
286
|
+
ctrl+c - exit
|
|
287
|
+
`);
|
|
288
|
+
}
|
|
289
|
+
return { close, closed, interactive };
|
|
290
|
+
}
|
|
291
|
+
var init_terminal = () => {};
|
|
292
|
+
|
|
218
293
|
// node_modules/.bun/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
|
|
219
294
|
function randomID() {
|
|
220
295
|
return Math.random().toString(36).slice(2, 11);
|
|
@@ -6028,7 +6103,7 @@ var init_src2 = __esm(() => {
|
|
|
6028
6103
|
});
|
|
6029
6104
|
|
|
6030
6105
|
// packages/preview/src/hot-viewer.ts
|
|
6031
|
-
import {
|
|
6106
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
6032
6107
|
import { randomUUID } from "node:crypto";
|
|
6033
6108
|
import { existsSync } from "node:fs";
|
|
6034
6109
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -6138,18 +6213,25 @@ async function hotViewer(options) {
|
|
|
6138
6213
|
const displayHost = options.lan ? lanHost() : options.host;
|
|
6139
6214
|
const viewerHosts = [displayHost, "localhost", "127.0.0.1"].join(",");
|
|
6140
6215
|
const viewerUrl = `http://${displayHost}:${options.port}${openPath}`;
|
|
6141
|
-
console.log(`Kennel hot viewer: ${viewerUrl}`);
|
|
6142
6216
|
if (options.dryRun) {
|
|
6217
|
+
console.log(`Kennel hot viewer: ${viewerUrl}`);
|
|
6143
6218
|
console.log(`bun ${[...args, ...options.open ? ["--open", openPath] : []].join(" ")}`);
|
|
6144
6219
|
return;
|
|
6145
6220
|
}
|
|
6221
|
+
const reuse = async () => {
|
|
6222
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
6223
|
+
if (options.open)
|
|
6224
|
+
openPreview(viewerUrl);
|
|
6225
|
+
try {
|
|
6226
|
+
if (terminal.interactive)
|
|
6227
|
+
await terminal.closed;
|
|
6228
|
+
} finally {
|
|
6229
|
+
terminal.close();
|
|
6230
|
+
}
|
|
6231
|
+
};
|
|
6146
6232
|
const status = await viewerStatus(url);
|
|
6147
6233
|
if (status === "running") {
|
|
6148
|
-
|
|
6149
|
-
console.log(`Kennel hot viewer is already running at ${viewerUrl}`);
|
|
6150
|
-
} else {
|
|
6151
|
-
console.log("Kennel hot viewer is already running. Reusing it.");
|
|
6152
|
-
}
|
|
6234
|
+
await reuse();
|
|
6153
6235
|
return;
|
|
6154
6236
|
}
|
|
6155
6237
|
if (status === "other") {
|
|
@@ -6157,11 +6239,7 @@ async function hotViewer(options) {
|
|
|
6157
6239
|
}
|
|
6158
6240
|
if (await portOpen(options.host, options.port)) {
|
|
6159
6241
|
if (await viewerRunning(url, 20, true)) {
|
|
6160
|
-
|
|
6161
|
-
console.log(`Kennel hot viewer is already running at ${viewerUrl}`);
|
|
6162
|
-
} else {
|
|
6163
|
-
console.log("Kennel hot viewer is already running. Reusing it.");
|
|
6164
|
-
}
|
|
6242
|
+
await reuse();
|
|
6165
6243
|
return;
|
|
6166
6244
|
}
|
|
6167
6245
|
throw new Error(`Port ${options.port} is used by another service. Stop it or select another port.`);
|
|
@@ -6170,11 +6248,7 @@ async function hotViewer(options) {
|
|
|
6170
6248
|
let token = await acquireLock(lock);
|
|
6171
6249
|
if (!token) {
|
|
6172
6250
|
if (await viewerRunning(url, 40, true)) {
|
|
6173
|
-
|
|
6174
|
-
console.log(`Kennel hot viewer is already running at ${viewerUrl}`);
|
|
6175
|
-
} else {
|
|
6176
|
-
console.log("Kennel hot viewer is already running. Reusing it.");
|
|
6177
|
-
}
|
|
6251
|
+
await reuse();
|
|
6178
6252
|
return;
|
|
6179
6253
|
}
|
|
6180
6254
|
await rm(lock, { force: true, recursive: true });
|
|
@@ -6184,14 +6258,9 @@ async function hotViewer(options) {
|
|
|
6184
6258
|
throw new Error(`Could not acquire the hot viewer startup lock for port ${options.port}.`);
|
|
6185
6259
|
if (await viewerRunning(url)) {
|
|
6186
6260
|
await releaseLock(lock, token);
|
|
6187
|
-
|
|
6188
|
-
console.log(`Kennel hot viewer is already running at ${viewerUrl}`);
|
|
6189
|
-
} else {
|
|
6190
|
-
console.log("Kennel hot viewer is already running. Reusing it.");
|
|
6191
|
-
}
|
|
6261
|
+
await reuse();
|
|
6192
6262
|
return;
|
|
6193
6263
|
}
|
|
6194
|
-
console.log(`bun ${args.join(" ")}`);
|
|
6195
6264
|
if (options.open)
|
|
6196
6265
|
args.push("--open", openPath);
|
|
6197
6266
|
const child = spawn("bun", args, {
|
|
@@ -6204,7 +6273,12 @@ async function hotViewer(options) {
|
|
|
6204
6273
|
...options.target ? { KENNEL_VIEWER_TARGET: options.target } : {},
|
|
6205
6274
|
LC_ALL: "en_GB.UTF-8"
|
|
6206
6275
|
},
|
|
6207
|
-
stdio: "inherit"
|
|
6276
|
+
stdio: ["ignore", "inherit", "inherit"]
|
|
6277
|
+
});
|
|
6278
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
6279
|
+
terminal.closed.then(() => {
|
|
6280
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
6281
|
+
child.kill("SIGTERM");
|
|
6208
6282
|
});
|
|
6209
6283
|
await new Promise((done, fail) => {
|
|
6210
6284
|
child.once("error", fail);
|
|
@@ -6213,16 +6287,21 @@ async function hotViewer(options) {
|
|
|
6213
6287
|
done();
|
|
6214
6288
|
return;
|
|
6215
6289
|
}
|
|
6290
|
+
terminal.close();
|
|
6216
6291
|
if (await viewerRunning(url, 20, true)) {
|
|
6217
|
-
|
|
6292
|
+
await reuse();
|
|
6218
6293
|
done();
|
|
6219
6294
|
return;
|
|
6220
6295
|
}
|
|
6221
6296
|
fail(new Error(`Kennel hot viewer stopped with exit code ${code ?? "unknown"}`));
|
|
6222
6297
|
});
|
|
6223
|
-
}).finally(() =>
|
|
6298
|
+
}).finally(async () => {
|
|
6299
|
+
terminal.close();
|
|
6300
|
+
await releaseLock(lock, token);
|
|
6301
|
+
});
|
|
6224
6302
|
}
|
|
6225
6303
|
var init_hot_viewer = __esm(() => {
|
|
6304
|
+
init_terminal();
|
|
6226
6305
|
init_src2();
|
|
6227
6306
|
});
|
|
6228
6307
|
|
|
@@ -28108,7 +28187,7 @@ var init_server = __esm(() => {
|
|
|
28108
28187
|
// packages/schemas/src/data/workspace.ts
|
|
28109
28188
|
import { existsSync as existsSync3, readFileSync, realpathSync, statSync as statSync2 } from "node:fs";
|
|
28110
28189
|
import {
|
|
28111
|
-
basename,
|
|
28190
|
+
basename as basename2,
|
|
28112
28191
|
dirname as dirname3,
|
|
28113
28192
|
isAbsolute,
|
|
28114
28193
|
relative as relative3,
|
|
@@ -28169,7 +28248,7 @@ function folderWorkspace(input, { allowMissingSchemas = false } = {}) {
|
|
|
28169
28248
|
for (let current = folder;inside(root, current); current = dirname3(current)) {
|
|
28170
28249
|
breadcrumbs.unshift({
|
|
28171
28250
|
path: current,
|
|
28172
|
-
title:
|
|
28251
|
+
title: basename2(current),
|
|
28173
28252
|
schema: schemaRoots.some((owner) => inside(owner, current))
|
|
28174
28253
|
});
|
|
28175
28254
|
if (current === root)
|
|
@@ -37714,7 +37793,7 @@ var init_load = __esm(() => {
|
|
|
37714
37793
|
// packages/schemas/src/node.ts
|
|
37715
37794
|
import { readdir as readdir2, readFile as readFile4, realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
37716
37795
|
import {
|
|
37717
|
-
basename as
|
|
37796
|
+
basename as basename3,
|
|
37718
37797
|
dirname as dirname5,
|
|
37719
37798
|
isAbsolute as isAbsolute3,
|
|
37720
37799
|
relative as relative5,
|
|
@@ -37747,7 +37826,7 @@ function createCoreFolderIndex() {
|
|
|
37747
37826
|
affected.add(scope);
|
|
37748
37827
|
}
|
|
37749
37828
|
}
|
|
37750
|
-
if (inside3(path, scope) ||
|
|
37829
|
+
if (inside3(path, scope) || basename3(path) === "raggle.json" && inside3(dirname5(path), scope) || ["schema.json", "_schema.json"].includes(basename3(path)) && dirname5(path) === scope)
|
|
37751
37830
|
affected.add(scope);
|
|
37752
37831
|
}
|
|
37753
37832
|
for (const file of records.keys())
|
|
@@ -37856,7 +37935,7 @@ function createCoreFolderIndex() {
|
|
|
37856
37935
|
path,
|
|
37857
37936
|
relativePath: localPath,
|
|
37858
37937
|
modifiedAt: info.mtimeMs,
|
|
37859
|
-
title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ??
|
|
37938
|
+
title: text(fieldValue(parsed.fields, primary)) ?? text(parsed.fields.title) ?? parsed.heading ?? basename3(path, ".md"),
|
|
37860
37939
|
fields: parsed.fields,
|
|
37861
37940
|
validationErrors,
|
|
37862
37941
|
icon: await recordIcon(parsed.fields, path, entry.folder, parsed.body)
|
|
@@ -37882,7 +37961,7 @@ function createCoreFolderIndex() {
|
|
|
37882
37961
|
const data = {
|
|
37883
37962
|
id,
|
|
37884
37963
|
...entry,
|
|
37885
|
-
title: text(schema?.title) ?? config.title ??
|
|
37964
|
+
title: text(schema?.title) ?? config.title ?? basename3(entry.folder),
|
|
37886
37965
|
navLabel: config.ui?.navLabel,
|
|
37887
37966
|
icon: config.icon,
|
|
37888
37967
|
count: indexedRecords.length,
|
|
@@ -37953,7 +38032,7 @@ var init_node3 = __esm(() => {
|
|
|
37953
38032
|
|
|
37954
38033
|
// packages/schemas/src/server.ts
|
|
37955
38034
|
import { createHash as createHash3 } from "node:crypto";
|
|
37956
|
-
import { basename as
|
|
38035
|
+
import { basename as basename4, relative as relative6 } from "node:path";
|
|
37957
38036
|
function createSchemaCollectionServer(host, options = {}) {
|
|
37958
38037
|
const collectionIndex = createCoreFolderIndex();
|
|
37959
38038
|
const collectionScopes = new Set;
|
|
@@ -38003,7 +38082,7 @@ function createSchemaCollectionServer(host, options = {}) {
|
|
|
38003
38082
|
};
|
|
38004
38083
|
const invalidate = (path) => {
|
|
38005
38084
|
const affected = collectionIndex.invalidate(path);
|
|
38006
|
-
if (affected.length ||
|
|
38085
|
+
if (affected.length || basename4(path) === "raggle.json" && collectionScopes.size) {
|
|
38007
38086
|
clearTimeout(collectionRefresh);
|
|
38008
38087
|
collectionRefresh = setTimeout(() => {
|
|
38009
38088
|
host.emit({
|
|
@@ -38100,7 +38179,7 @@ var init_server2 = __esm(() => {
|
|
|
38100
38179
|
import { readdir as readdir3, readFile as readFile5, realpath as realpath3, stat as stat3 } from "node:fs/promises";
|
|
38101
38180
|
import { homedir as homedir2 } from "node:os";
|
|
38102
38181
|
import {
|
|
38103
|
-
basename as
|
|
38182
|
+
basename as basename5,
|
|
38104
38183
|
dirname as dirname6,
|
|
38105
38184
|
extname as extname2,
|
|
38106
38185
|
isAbsolute as isAbsolute4,
|
|
@@ -38245,10 +38324,10 @@ async function readEntity(folderPath, rootPath) {
|
|
|
38245
38324
|
return {
|
|
38246
38325
|
content: document2.body,
|
|
38247
38326
|
path: relative7(folderPath, filePath),
|
|
38248
|
-
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ??
|
|
38327
|
+
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
|
|
38249
38328
|
};
|
|
38250
38329
|
}));
|
|
38251
|
-
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ??
|
|
38330
|
+
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
|
|
38252
38331
|
const relativePath = relative7(rootPath, folderPath) || ".";
|
|
38253
38332
|
return {
|
|
38254
38333
|
aliases: stringValues(index.frontmatter.aliases),
|
|
@@ -38576,7 +38655,7 @@ var init_manifest = __esm(() => {
|
|
|
38576
38655
|
|
|
38577
38656
|
// packages/notes/src/server.ts
|
|
38578
38657
|
import { readdir as readdir4, stat as stat6 } from "node:fs/promises";
|
|
38579
|
-
import { basename as
|
|
38658
|
+
import { basename as basename6, extname as extname5, relative as relative9, resolve as resolve10, sep as sep5 } from "node:path";
|
|
38580
38659
|
async function listMarkdownFiles(folder) {
|
|
38581
38660
|
const files = [];
|
|
38582
38661
|
const visit = async (current) => {
|
|
@@ -38597,7 +38676,7 @@ async function listMarkdownFiles(folder) {
|
|
|
38597
38676
|
});
|
|
38598
38677
|
files.push({
|
|
38599
38678
|
activityAt: details?.mtimeMs ?? 0,
|
|
38600
|
-
name:
|
|
38679
|
+
name: basename6(entry.name, extname5(entry.name)),
|
|
38601
38680
|
relativePath: relative9(folder, path).split(sep5).join("/")
|
|
38602
38681
|
});
|
|
38603
38682
|
}
|
|
@@ -38968,7 +39047,7 @@ import { realpathSync as realpathSync2 } from "node:fs";
|
|
|
38968
39047
|
import { readdir as readdir5, readFile as readFile9, stat as stat7 } from "node:fs/promises";
|
|
38969
39048
|
import { homedir as homedir3 } from "node:os";
|
|
38970
39049
|
import {
|
|
38971
|
-
basename as
|
|
39050
|
+
basename as basename7,
|
|
38972
39051
|
dirname as dirname10,
|
|
38973
39052
|
extname as extname6,
|
|
38974
39053
|
isAbsolute as isAbsolute6,
|
|
@@ -39163,7 +39242,7 @@ async function loadFolderData(folder) {
|
|
|
39163
39242
|
items: projects.map((project) => ({
|
|
39164
39243
|
id: project.id,
|
|
39165
39244
|
worktree: project.worktree,
|
|
39166
|
-
name: project.name ??
|
|
39245
|
+
name: project.name ?? basename7(project.worktree),
|
|
39167
39246
|
relativePath: relative11(folder, project.worktree)
|
|
39168
39247
|
}))
|
|
39169
39248
|
}
|
|
@@ -39661,7 +39740,7 @@ function createFolderApi({
|
|
|
39661
39740
|
ETag: etag,
|
|
39662
39741
|
"X-Content-Type-Options": "nosniff",
|
|
39663
39742
|
...download ? {
|
|
39664
|
-
"Content-Disposition": `attachment; filename="${
|
|
39743
|
+
"Content-Disposition": `attachment; filename="${basename7(target.path)}"`
|
|
39665
39744
|
} : {}
|
|
39666
39745
|
});
|
|
39667
39746
|
response.end(await readFile9(target.path));
|
|
@@ -44803,7 +44882,7 @@ import { resolve as resolve15 } from "node:path";
|
|
|
44803
44882
|
import { existsSync as existsSync7 } from "node:fs";
|
|
44804
44883
|
|
|
44805
44884
|
// packages/preview/src/viewer.ts
|
|
44806
|
-
import {
|
|
44885
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
44807
44886
|
import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
44808
44887
|
import { createServer as createServer2 } from "node:net";
|
|
44809
44888
|
import { dirname as dirname11, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
|
|
@@ -44813,6 +44892,7 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
|
44813
44892
|
init_src();
|
|
44814
44893
|
|
|
44815
44894
|
// packages/preview/src/viewer.ts
|
|
44895
|
+
init_terminal();
|
|
44816
44896
|
var loopbackHosts = new Set([
|
|
44817
44897
|
"127.0.0.1",
|
|
44818
44898
|
"::1",
|
|
@@ -44992,13 +45072,6 @@ async function availableViewerPort(start, host) {
|
|
|
44992
45072
|
}
|
|
44993
45073
|
throw new Error(`Kennel could not find a free port at or above ${start}`);
|
|
44994
45074
|
}
|
|
44995
|
-
function openUrl(url) {
|
|
44996
|
-
const command = process.platform === "darwin" ? "/usr/bin/open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
44997
|
-
execFile6(command, [url], (error) => {
|
|
44998
|
-
if (error)
|
|
44999
|
-
console.error(`Could not open browser: ${error.message}`);
|
|
45000
|
-
});
|
|
45001
|
-
}
|
|
45002
45075
|
function lanHost2() {
|
|
45003
45076
|
const route = execFileSync2("/sbin/route", ["-n", "get", "default"], {
|
|
45004
45077
|
encoding: "utf8"
|
|
@@ -45045,10 +45118,15 @@ async function viewer(input) {
|
|
|
45045
45118
|
const existing = await runningViewer(initialViewerUrl);
|
|
45046
45119
|
if (existing && isSameOrDescendant(existing.root, options.root)) {
|
|
45047
45120
|
const viewerUrl = popUrl(displayHost, options.port, options);
|
|
45048
|
-
|
|
45049
|
-
console.log(`Kennel is already running at ${viewerUrl}`);
|
|
45121
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
45050
45122
|
if (options.open)
|
|
45051
|
-
|
|
45123
|
+
openPreview(viewerUrl);
|
|
45124
|
+
try {
|
|
45125
|
+
if (terminal.interactive)
|
|
45126
|
+
await terminal.closed;
|
|
45127
|
+
} finally {
|
|
45128
|
+
terminal.close();
|
|
45129
|
+
}
|
|
45052
45130
|
return;
|
|
45053
45131
|
}
|
|
45054
45132
|
if (!portExplicit) {
|
|
@@ -45064,25 +45142,18 @@ async function viewer(input) {
|
|
|
45064
45142
|
});
|
|
45065
45143
|
try {
|
|
45066
45144
|
await api.listen(Number(options.port));
|
|
45067
|
-
console.log(`Reading documents from ${options.root}`);
|
|
45068
45145
|
if (options.tailscale) {
|
|
45069
45146
|
execFileSync2("tailscale", ["serve", "--bg", "--yes", options.port]);
|
|
45070
45147
|
}
|
|
45071
45148
|
const viewerUrl = popUrl(displayHost, options.port, options);
|
|
45072
|
-
|
|
45073
|
-
console.log(`Kennel is running at ${viewerUrl}`);
|
|
45074
|
-
console.log("Press Ctrl+C to stop.");
|
|
45149
|
+
const terminal = previewTerminal(viewerUrl, options);
|
|
45075
45150
|
if (options.open)
|
|
45076
|
-
|
|
45077
|
-
|
|
45078
|
-
|
|
45079
|
-
|
|
45080
|
-
|
|
45081
|
-
|
|
45082
|
-
};
|
|
45083
|
-
process.once("SIGINT", close);
|
|
45084
|
-
process.once("SIGTERM", close);
|
|
45085
|
-
});
|
|
45151
|
+
openPreview(viewerUrl);
|
|
45152
|
+
try {
|
|
45153
|
+
await terminal.closed;
|
|
45154
|
+
} finally {
|
|
45155
|
+
terminal.close();
|
|
45156
|
+
}
|
|
45086
45157
|
} finally {
|
|
45087
45158
|
await api.close();
|
|
45088
45159
|
}
|
package/dist/plugins/external.js
CHANGED
|
@@ -165,6 +165,12 @@ function pluginRoot(repoRoot, directory) {
|
|
|
165
165
|
throw new Error(`Missing plugin directory: ${directory}`);
|
|
166
166
|
return target;
|
|
167
167
|
}
|
|
168
|
+
// Peers (react, kennel-plugins) come from the host; scripts never run.
|
|
169
|
+
function installPluginDependencies(root, dependencies) {
|
|
170
|
+
if (!dependencies || typeof dependencies !== "object" || Object.keys(dependencies).length === 0)
|
|
171
|
+
return;
|
|
172
|
+
run("npm", ["install", "--omit=dev", "--omit=peer", "--ignore-scripts", "--no-audit", "--no-fund"], { cwd: root });
|
|
173
|
+
}
|
|
168
174
|
function safeRelativeFile(root, value, label) {
|
|
169
175
|
if (typeof value !== "string" || !value.trim())
|
|
170
176
|
throw new Error(`Missing ${label}`);
|
|
@@ -376,6 +382,15 @@ export async function validateExternalPlugin(inputUrl, options = {}) {
|
|
|
376
382
|
if (CORE_IDS.has(id))
|
|
377
383
|
throw new Error(`External plugin id collides with core plugin: ${id}`);
|
|
378
384
|
const packageName = typeof pkg.name === "string" && pkg.name.trim() ? pkg.name.trim() : id;
|
|
385
|
+
let aliases;
|
|
386
|
+
if (metadata?.aliases !== undefined) {
|
|
387
|
+
if (!Array.isArray(metadata.aliases))
|
|
388
|
+
throw new Error("Plugin aliases must be an array of command names");
|
|
389
|
+
aliases = metadata.aliases.map((alias) => stringField(alias, "plugin alias"));
|
|
390
|
+
if (aliases.some((alias) => !VALID_ID.test(alias)) || new Set(aliases).size !== aliases.length) {
|
|
391
|
+
throw new Error("Plugin aliases must be unique lowercase command names");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
379
394
|
const trigger = metadata?.trigger === undefined ? undefined : stringField(metadata.trigger, "trigger file");
|
|
380
395
|
if (trigger && (trigger.includes("/") || trigger.includes("\\") || trigger === "." || trigger === ".."))
|
|
381
396
|
throw new Error(`Invalid trigger file: ${trigger}`);
|
|
@@ -391,6 +406,7 @@ export async function validateExternalPlugin(inputUrl, options = {}) {
|
|
|
391
406
|
commit,
|
|
392
407
|
packageName,
|
|
393
408
|
id,
|
|
409
|
+
...(aliases?.length ? { aliases } : {}),
|
|
394
410
|
...(directory ? { directory } : {}),
|
|
395
411
|
...(source.path ? { path: source.path } : {}),
|
|
396
412
|
...(trigger ? { trigger } : {}),
|
|
@@ -402,6 +418,7 @@ export async function validateExternalPlugin(inputUrl, options = {}) {
|
|
|
402
418
|
...(css ? { css } : {}),
|
|
403
419
|
},
|
|
404
420
|
};
|
|
421
|
+
installPluginDependencies(root, pkg.dependencies);
|
|
405
422
|
await validateImports(entry);
|
|
406
423
|
return entry;
|
|
407
424
|
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ViewerOptions } from './viewer.js';
|
|
2
|
+
/** The hot viewer runs Vite from a Kennel source checkout; npm installs have none. */
|
|
3
|
+
export declare function hotViewerAvailable(): boolean;
|
|
2
4
|
export declare function hotViewer(options: ViewerOptions): Promise<void>;
|