kennel 0.2.7 → 0.2.9
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 +22 -0
- package/dist/cli/src/plugins/external.js +48 -5
- package/dist/plugins/external.js +48 -5
- package/dist/site/kinds.js +159 -1
- package/kernel/packages/plugins/package.json +1 -1
- package/package.json +6 -5
- package/schemas/config.schema.json +114 -0
package/README.md
CHANGED
|
@@ -96,6 +96,28 @@ package metadata, not from URL text. The transcripts plugin contract is package
|
|
|
96
96
|
When this file is absent, entity search allows only the workspace opened by
|
|
97
97
|
`knl`. Invalid config fails closed.
|
|
98
98
|
|
|
99
|
+
The config format is published as a JSON Schema (draft 2020-12):
|
|
100
|
+
|
|
101
|
+
- URL: `https://raw.githubusercontent.com/raggle-ai/kennel/main/cli/schemas/config.schema.json`
|
|
102
|
+
- npm: `kennel/config.schema.json` (file `schemas/config.schema.json` in the package)
|
|
103
|
+
|
|
104
|
+
Add `$schema` to the file for editor completion and validation:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"$schema": "https://raw.githubusercontent.com/raggle-ai/kennel/main/cli/schemas/config.schema.json",
|
|
109
|
+
"allowedRoots": ["/path/to/workspace"]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The CLI validates the file against this schema each time it reads the config
|
|
114
|
+
and stops with the failing field paths when a value is malformed. Unknown keys
|
|
115
|
+
are rejected. `externalPlugins` records are resolved and written by
|
|
116
|
+
`kennel plugin add` (pinned commit, absolute entry files, export symbols); do
|
|
117
|
+
not write or edit them by hand. To change one, remove the record and add the
|
|
118
|
+
plugin again. The schema checks only the record shape: the install-time checks
|
|
119
|
+
for the cache path, package exports, and imports stay in the CLI.
|
|
120
|
+
|
|
99
121
|
Suggested convention for agents: write generated HTML components to
|
|
100
122
|
`.kennel/preview/<name>.html` and pop them with `knl pop
|
|
101
123
|
.kennel/preview/<name>.html --watch` (reuses 5180), so preview items share one
|
|
@@ -13,6 +13,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
13
13
|
import { basename, dirname, isAbsolute, join, relative, resolve, } from "node:path";
|
|
14
14
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { z } from "zod";
|
|
16
17
|
const CONFIG_FILE = join("kennel", "config.json");
|
|
17
18
|
const VALID_ID = /^[a-z][a-z0-9-]*$/u;
|
|
18
19
|
const CORE_IDS = new Set(["agents", "notes"]);
|
|
@@ -57,10 +58,51 @@ function run(command, args, options = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
return result.stdout.trim();
|
|
59
60
|
}
|
|
61
|
+
let configSchema;
|
|
62
|
+
function userConfigSchema() {
|
|
63
|
+
if (!configSchema) {
|
|
64
|
+
const json = JSON.parse(readFileSync(join(cliRoot(), "schemas", "config.schema.json"), "utf8"));
|
|
65
|
+
configSchema = { id: json.$id, schema: z.fromJSONSchema(json) };
|
|
66
|
+
}
|
|
67
|
+
return configSchema;
|
|
68
|
+
}
|
|
69
|
+
function assertUserConfig(value, path) {
|
|
70
|
+
const { id, schema } = userConfigSchema();
|
|
71
|
+
const result = schema.safeParse(value);
|
|
72
|
+
if (result.success)
|
|
73
|
+
return;
|
|
74
|
+
throw new Error(`Invalid Kennel config at ${path} (schema ${id}):\n${describeIssues(result.error.issues).join("\n")}\nexternalPlugins records are managed by \`kennel plugin add\`; remove a malformed record and add the plugin again.`);
|
|
75
|
+
}
|
|
76
|
+
function issuePath(path) {
|
|
77
|
+
return path.reduce((text, key) => typeof key === "number" ? `${text}[${key}]` : text ? `${text}.${String(key)}` : String(key), "") || "(root)";
|
|
78
|
+
}
|
|
79
|
+
function describeIssues(issues, base = [], indent = " ") {
|
|
80
|
+
return issues.flatMap((issue) => {
|
|
81
|
+
const path = [...base, ...issue.path];
|
|
82
|
+
if (issue.code !== "invalid_union" || issue.errors.length === 0)
|
|
83
|
+
return [`${indent}${issuePath(path)}: ${issue.message}`];
|
|
84
|
+
return [
|
|
85
|
+
`${indent}${issuePath(path)}: matches none of the allowed shapes`,
|
|
86
|
+
...issue.errors.flatMap((branch, index) => [
|
|
87
|
+
`${indent} shape ${index + 1}:`,
|
|
88
|
+
...describeIssues(branch, path, `${indent} `),
|
|
89
|
+
]),
|
|
90
|
+
];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
60
93
|
function readJson(path) {
|
|
61
94
|
if (!existsSync(path))
|
|
62
95
|
return {};
|
|
63
|
-
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
value = JSON.parse(readFileSync(path, "utf8"));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
+
throw new Error(`Invalid Kennel config at ${path}: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
assertUserConfig(value, path);
|
|
105
|
+
return value;
|
|
64
106
|
}
|
|
65
107
|
function writeJsonAtomic(path, value) {
|
|
66
108
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -350,19 +392,20 @@ export async function validateExternalPlugin(inputUrl, existing = [], options =
|
|
|
350
392
|
return entry;
|
|
351
393
|
}
|
|
352
394
|
export function readExternalPlugins() {
|
|
353
|
-
|
|
354
|
-
return Array.isArray(config.externalPlugins) ? config.externalPlugins : [];
|
|
395
|
+
return readJson(kennelConfigPath()).externalPlugins ?? [];
|
|
355
396
|
}
|
|
356
397
|
export async function installExternalPlugin(url, options = {}) {
|
|
357
398
|
const path = kennelConfigPath();
|
|
358
399
|
const config = readJson(path);
|
|
359
|
-
const externalPlugins =
|
|
400
|
+
const externalPlugins = config.externalPlugins ?? [];
|
|
360
401
|
let entry;
|
|
361
402
|
try {
|
|
362
403
|
entry = await validateExternalPlugin(url, externalPlugins, options);
|
|
363
404
|
const nextPlugins = [...externalPlugins, entry];
|
|
405
|
+
const nextConfig = { ...config, externalPlugins: nextPlugins };
|
|
406
|
+
assertUserConfig(nextConfig, path);
|
|
364
407
|
await ensureKernel(nextPlugins);
|
|
365
|
-
writeJsonAtomic(path,
|
|
408
|
+
writeJsonAtomic(path, nextConfig);
|
|
366
409
|
}
|
|
367
410
|
catch (error) {
|
|
368
411
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/plugins/external.js
CHANGED
|
@@ -13,6 +13,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
13
13
|
import { basename, dirname, isAbsolute, join, relative, resolve, } from "node:path";
|
|
14
14
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { z } from "zod";
|
|
16
17
|
const CONFIG_FILE = join("kennel", "config.json");
|
|
17
18
|
const VALID_ID = /^[a-z][a-z0-9-]*$/u;
|
|
18
19
|
const CORE_IDS = new Set(["agents", "notes"]);
|
|
@@ -57,10 +58,51 @@ function run(command, args, options = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
return result.stdout.trim();
|
|
59
60
|
}
|
|
61
|
+
let configSchema;
|
|
62
|
+
function userConfigSchema() {
|
|
63
|
+
if (!configSchema) {
|
|
64
|
+
const json = JSON.parse(readFileSync(join(cliRoot(), "schemas", "config.schema.json"), "utf8"));
|
|
65
|
+
configSchema = { id: json.$id, schema: z.fromJSONSchema(json) };
|
|
66
|
+
}
|
|
67
|
+
return configSchema;
|
|
68
|
+
}
|
|
69
|
+
function assertUserConfig(value, path) {
|
|
70
|
+
const { id, schema } = userConfigSchema();
|
|
71
|
+
const result = schema.safeParse(value);
|
|
72
|
+
if (result.success)
|
|
73
|
+
return;
|
|
74
|
+
throw new Error(`Invalid Kennel config at ${path} (schema ${id}):\n${describeIssues(result.error.issues).join("\n")}\nexternalPlugins records are managed by \`kennel plugin add\`; remove a malformed record and add the plugin again.`);
|
|
75
|
+
}
|
|
76
|
+
function issuePath(path) {
|
|
77
|
+
return path.reduce((text, key) => typeof key === "number" ? `${text}[${key}]` : text ? `${text}.${String(key)}` : String(key), "") || "(root)";
|
|
78
|
+
}
|
|
79
|
+
function describeIssues(issues, base = [], indent = " ") {
|
|
80
|
+
return issues.flatMap((issue) => {
|
|
81
|
+
const path = [...base, ...issue.path];
|
|
82
|
+
if (issue.code !== "invalid_union" || issue.errors.length === 0)
|
|
83
|
+
return [`${indent}${issuePath(path)}: ${issue.message}`];
|
|
84
|
+
return [
|
|
85
|
+
`${indent}${issuePath(path)}: matches none of the allowed shapes`,
|
|
86
|
+
...issue.errors.flatMap((branch, index) => [
|
|
87
|
+
`${indent} shape ${index + 1}:`,
|
|
88
|
+
...describeIssues(branch, path, `${indent} `),
|
|
89
|
+
]),
|
|
90
|
+
];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
60
93
|
function readJson(path) {
|
|
61
94
|
if (!existsSync(path))
|
|
62
95
|
return {};
|
|
63
|
-
|
|
96
|
+
let value;
|
|
97
|
+
try {
|
|
98
|
+
value = JSON.parse(readFileSync(path, "utf8"));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
+
throw new Error(`Invalid Kennel config at ${path}: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
assertUserConfig(value, path);
|
|
105
|
+
return value;
|
|
64
106
|
}
|
|
65
107
|
function writeJsonAtomic(path, value) {
|
|
66
108
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -350,19 +392,20 @@ export async function validateExternalPlugin(inputUrl, existing = [], options =
|
|
|
350
392
|
return entry;
|
|
351
393
|
}
|
|
352
394
|
export function readExternalPlugins() {
|
|
353
|
-
|
|
354
|
-
return Array.isArray(config.externalPlugins) ? config.externalPlugins : [];
|
|
395
|
+
return readJson(kennelConfigPath()).externalPlugins ?? [];
|
|
355
396
|
}
|
|
356
397
|
export async function installExternalPlugin(url, options = {}) {
|
|
357
398
|
const path = kennelConfigPath();
|
|
358
399
|
const config = readJson(path);
|
|
359
|
-
const externalPlugins =
|
|
400
|
+
const externalPlugins = config.externalPlugins ?? [];
|
|
360
401
|
let entry;
|
|
361
402
|
try {
|
|
362
403
|
entry = await validateExternalPlugin(url, externalPlugins, options);
|
|
363
404
|
const nextPlugins = [...externalPlugins, entry];
|
|
405
|
+
const nextConfig = { ...config, externalPlugins: nextPlugins };
|
|
406
|
+
assertUserConfig(nextConfig, path);
|
|
364
407
|
await ensureKernel(nextPlugins);
|
|
365
|
-
writeJsonAtomic(path,
|
|
408
|
+
writeJsonAtomic(path, nextConfig);
|
|
366
409
|
}
|
|
367
410
|
catch (error) {
|
|
368
411
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/site/kinds.js
CHANGED
|
@@ -1 +1,159 @@
|
|
|
1
|
-
|
|
1
|
+
// packages/kinds/src/kinds.ts
|
|
2
|
+
var previewTextKinds = new Set([
|
|
3
|
+
"code",
|
|
4
|
+
"eml",
|
|
5
|
+
"html",
|
|
6
|
+
"markdown",
|
|
7
|
+
"table",
|
|
8
|
+
"text"
|
|
9
|
+
]);
|
|
10
|
+
function previewTextKind(kind) {
|
|
11
|
+
return previewTextKinds.has(kind);
|
|
12
|
+
}
|
|
13
|
+
var codeExtensions = new Set([
|
|
14
|
+
".astro",
|
|
15
|
+
".bash",
|
|
16
|
+
".c",
|
|
17
|
+
".cc",
|
|
18
|
+
".cpp",
|
|
19
|
+
".cs",
|
|
20
|
+
".css",
|
|
21
|
+
".dart",
|
|
22
|
+
".ex",
|
|
23
|
+
".exs",
|
|
24
|
+
".go",
|
|
25
|
+
".graphql",
|
|
26
|
+
".h",
|
|
27
|
+
".hpp",
|
|
28
|
+
".hs",
|
|
29
|
+
".java",
|
|
30
|
+
".js",
|
|
31
|
+
".jsx",
|
|
32
|
+
".json",
|
|
33
|
+
".kt",
|
|
34
|
+
".less",
|
|
35
|
+
".lua",
|
|
36
|
+
".ml",
|
|
37
|
+
".mjs",
|
|
38
|
+
".php",
|
|
39
|
+
".proto",
|
|
40
|
+
".py",
|
|
41
|
+
".rb",
|
|
42
|
+
".rs",
|
|
43
|
+
".scss",
|
|
44
|
+
".sh",
|
|
45
|
+
".sql",
|
|
46
|
+
".svelte",
|
|
47
|
+
".swift",
|
|
48
|
+
".toml",
|
|
49
|
+
".ts",
|
|
50
|
+
".tsx",
|
|
51
|
+
".vue",
|
|
52
|
+
".xml",
|
|
53
|
+
".yaml",
|
|
54
|
+
".yml",
|
|
55
|
+
".zsh"
|
|
56
|
+
]);
|
|
57
|
+
var imageExtensions = new Set([
|
|
58
|
+
".avif",
|
|
59
|
+
".bmp",
|
|
60
|
+
".gif",
|
|
61
|
+
".ico",
|
|
62
|
+
".jpeg",
|
|
63
|
+
".jpg",
|
|
64
|
+
".png",
|
|
65
|
+
".svg",
|
|
66
|
+
".webp"
|
|
67
|
+
]);
|
|
68
|
+
var knownExtensions = new Set([
|
|
69
|
+
...codeExtensions,
|
|
70
|
+
...imageExtensions,
|
|
71
|
+
".csv",
|
|
72
|
+
".eml",
|
|
73
|
+
".htm",
|
|
74
|
+
".html",
|
|
75
|
+
".markdown",
|
|
76
|
+
".md",
|
|
77
|
+
".mdx",
|
|
78
|
+
".pdf",
|
|
79
|
+
".text",
|
|
80
|
+
".tsv",
|
|
81
|
+
".txt"
|
|
82
|
+
]);
|
|
83
|
+
function previewKind(path) {
|
|
84
|
+
const extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
85
|
+
if (extension === ".htm" || extension === ".html")
|
|
86
|
+
return "html";
|
|
87
|
+
if (extension === ".md" || extension === ".markdown" || extension === ".mdx")
|
|
88
|
+
return "markdown";
|
|
89
|
+
if (extension === ".text" || extension === ".txt")
|
|
90
|
+
return "text";
|
|
91
|
+
if (extension === ".eml")
|
|
92
|
+
return "eml";
|
|
93
|
+
if (extension === ".csv" || extension === ".tsv")
|
|
94
|
+
return "table";
|
|
95
|
+
if (imageExtensions.has(extension))
|
|
96
|
+
return "image";
|
|
97
|
+
if (codeExtensions.has(extension))
|
|
98
|
+
return "code";
|
|
99
|
+
if (knownExtensions.has(extension))
|
|
100
|
+
return "binary";
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
var rawMime = {
|
|
104
|
+
".avif": "image/avif",
|
|
105
|
+
".bmp": "image/bmp",
|
|
106
|
+
".gif": "image/gif",
|
|
107
|
+
".htm": "text/html; charset=utf-8",
|
|
108
|
+
".html": "text/html; charset=utf-8",
|
|
109
|
+
".ico": "image/x-icon",
|
|
110
|
+
".jpeg": "image/jpeg",
|
|
111
|
+
".jpg": "image/jpeg",
|
|
112
|
+
".pdf": "application/pdf",
|
|
113
|
+
".png": "image/png",
|
|
114
|
+
".svg": "image/svg+xml",
|
|
115
|
+
".webp": "image/webp"
|
|
116
|
+
};
|
|
117
|
+
function previewRawMime(path) {
|
|
118
|
+
const extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
119
|
+
const mime = rawMime[extension];
|
|
120
|
+
if (mime)
|
|
121
|
+
return mime;
|
|
122
|
+
if (previewKind(path) === "code" || previewKind(path) === "table")
|
|
123
|
+
return "text/plain; charset=utf-8";
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
var mediaMime = {
|
|
127
|
+
".aac": "audio/aac",
|
|
128
|
+
".avif": "image/avif",
|
|
129
|
+
".bmp": "image/bmp",
|
|
130
|
+
".flac": "audio/flac",
|
|
131
|
+
".gif": "image/gif",
|
|
132
|
+
".ico": "image/x-icon",
|
|
133
|
+
".jpeg": "image/jpeg",
|
|
134
|
+
".jpg": "image/jpeg",
|
|
135
|
+
".m4a": "audio/mp4",
|
|
136
|
+
".m4v": "video/mp4",
|
|
137
|
+
".mov": "video/quicktime",
|
|
138
|
+
".mp3": "audio/mpeg",
|
|
139
|
+
".mp4": "video/mp4",
|
|
140
|
+
".oga": "audio/ogg",
|
|
141
|
+
".ogg": "audio/ogg",
|
|
142
|
+
".ogv": "video/ogg",
|
|
143
|
+
".opus": "audio/opus",
|
|
144
|
+
".png": "image/png",
|
|
145
|
+
".wav": "audio/wav",
|
|
146
|
+
".webm": "video/webm",
|
|
147
|
+
".webp": "image/webp"
|
|
148
|
+
};
|
|
149
|
+
function mediaRawMime(path) {
|
|
150
|
+
const extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
151
|
+
return mediaMime[extension];
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
mediaRawMime,
|
|
155
|
+
previewKind,
|
|
156
|
+
previewRawMime,
|
|
157
|
+
previewTextKind,
|
|
158
|
+
previewTextKinds
|
|
159
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "kennel-plugins",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "Small shared contract for trusted folder view plugins in the Kennel web viewer.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kennel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Open-source pop-up previews for AI output: markdown, HTML artifacts, email, images, code, tables, notes, and calendars, from the terminal or your agent session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"./automations/notifications": {
|
|
33
33
|
"types": "./dist/automations/notifications.d.ts",
|
|
34
34
|
"import": "./dist/automations/notifications.js"
|
|
35
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"./config.schema.json": "./schemas/config.schema.json"
|
|
36
37
|
},
|
|
37
38
|
"publishConfig": {
|
|
38
39
|
"registry": "https://registry.npmjs.org",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"files": [
|
|
42
43
|
"bin",
|
|
43
44
|
"dist",
|
|
45
|
+
"schemas",
|
|
44
46
|
"templates",
|
|
45
47
|
"web",
|
|
46
48
|
"kernel"
|
|
@@ -69,9 +71,7 @@
|
|
|
69
71
|
"@raggle-ai/barkdown": "0.6.1",
|
|
70
72
|
"@raggle-ai/kennel-knowledge": "^0.1.0",
|
|
71
73
|
"@raggle-ai/kennel-mcp": "^0.1.8",
|
|
72
|
-
"@raggle-ai/kennel-preview": "^0.1.0",
|
|
73
74
|
"@raggle-ai/local": "0.14.0",
|
|
74
|
-
"@raggle-ai/markdown": "^0.1.0",
|
|
75
75
|
"@raggle-ai/scout-ui": "0.1.3",
|
|
76
76
|
"@tailwindcss/vite": "4.3.3",
|
|
77
77
|
"@tanstack/react-query": "^5.66.5",
|
|
@@ -115,7 +115,8 @@
|
|
|
115
115
|
"zod": "^4.3.6"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|
|
118
|
-
"kennel-
|
|
118
|
+
"@raggle-ai/kennel-preview": "0.1.0",
|
|
119
|
+
"kennel-plugins": "0.1.2",
|
|
119
120
|
"@types/node": "^22.13.4",
|
|
120
121
|
"typescript": "^5.9.3"
|
|
121
122
|
},
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/raggle-ai/kennel/main/cli/schemas/config.schema.json",
|
|
4
|
+
"title": "Kennel user config",
|
|
5
|
+
"description": "Kennel user config at $XDG_CONFIG_HOME/kennel/config.json (default ~/.config/kennel/config.json). externalPlugins records are written by `kennel plugin add`; do not edit them by hand.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"description": "JSON Schema reference for editor validation.",
|
|
11
|
+
"type": "string",
|
|
12
|
+
"minLength": 1
|
|
13
|
+
},
|
|
14
|
+
"allowedRoots": {
|
|
15
|
+
"description": "Absolute local folders that entity search may read.",
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": { "$ref": "#/$defs/absolutePath" }
|
|
18
|
+
},
|
|
19
|
+
"externalPlugins": {
|
|
20
|
+
"description": "Resolved external folder plugins. Managed by `kennel plugin add`.",
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": { "$ref": "#/$defs/externalPlugin" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"$defs": {
|
|
26
|
+
"absolutePath": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Absolute path: POSIX, Windows drive, or Windows UNC.",
|
|
29
|
+
"pattern": "^(/|[A-Za-z]:[\\\\/]|\\\\\\\\)"
|
|
30
|
+
},
|
|
31
|
+
"symbol": {
|
|
32
|
+
"description": "Export name in the bundled module, or \"default\".",
|
|
33
|
+
"type": "string",
|
|
34
|
+
"minLength": 1
|
|
35
|
+
},
|
|
36
|
+
"externalPlugin": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"required": ["url", "commit", "packageName", "id", "entries"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"url": {
|
|
42
|
+
"description": "Git repository URL, without the #ref fragment.",
|
|
43
|
+
"type": "string",
|
|
44
|
+
"minLength": 1
|
|
45
|
+
},
|
|
46
|
+
"requestedRef": {
|
|
47
|
+
"description": "Ref from the #ref fragment given at install time.",
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1
|
|
50
|
+
},
|
|
51
|
+
"commit": {
|
|
52
|
+
"description": "Full commit SHA the plugin is pinned to.",
|
|
53
|
+
"type": "string",
|
|
54
|
+
"pattern": "^[0-9a-f]{40}$"
|
|
55
|
+
},
|
|
56
|
+
"packageName": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1
|
|
59
|
+
},
|
|
60
|
+
"id": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"pattern": "^[a-z][a-z0-9-]*$"
|
|
63
|
+
},
|
|
64
|
+
"directory": {
|
|
65
|
+
"description": "Plugin package root relative to the repository root.",
|
|
66
|
+
"type": "string",
|
|
67
|
+
"minLength": 1
|
|
68
|
+
},
|
|
69
|
+
"trigger": {
|
|
70
|
+
"description": "File name that activates the plugin in a folder.",
|
|
71
|
+
"type": "string",
|
|
72
|
+
"pattern": "^(?!\\.{1,2}$)[^/\\\\]+$"
|
|
73
|
+
},
|
|
74
|
+
"entries": {
|
|
75
|
+
"description": "Resolved absolute entry files and export symbols. api and apiSymbol appear together or not at all.",
|
|
76
|
+
"oneOf": [
|
|
77
|
+
{ "$ref": "#/$defs/entriesWithoutApi" },
|
|
78
|
+
{ "$ref": "#/$defs/entriesWithApi" }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"entriesWithoutApi": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"required": ["client", "clientSymbol", "server", "serverSymbol", "manifest", "manifestSymbol"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"client": { "$ref": "#/$defs/absolutePath" },
|
|
89
|
+
"clientSymbol": { "$ref": "#/$defs/symbol" },
|
|
90
|
+
"server": { "$ref": "#/$defs/absolutePath" },
|
|
91
|
+
"serverSymbol": { "$ref": "#/$defs/symbol" },
|
|
92
|
+
"manifest": { "$ref": "#/$defs/absolutePath" },
|
|
93
|
+
"manifestSymbol": { "$ref": "#/$defs/symbol" },
|
|
94
|
+
"css": { "$ref": "#/$defs/absolutePath" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"entriesWithApi": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"additionalProperties": false,
|
|
100
|
+
"required": ["client", "clientSymbol", "server", "serverSymbol", "manifest", "manifestSymbol", "api", "apiSymbol"],
|
|
101
|
+
"properties": {
|
|
102
|
+
"client": { "$ref": "#/$defs/absolutePath" },
|
|
103
|
+
"clientSymbol": { "$ref": "#/$defs/symbol" },
|
|
104
|
+
"server": { "$ref": "#/$defs/absolutePath" },
|
|
105
|
+
"serverSymbol": { "$ref": "#/$defs/symbol" },
|
|
106
|
+
"manifest": { "$ref": "#/$defs/absolutePath" },
|
|
107
|
+
"manifestSymbol": { "$ref": "#/$defs/symbol" },
|
|
108
|
+
"api": { "$ref": "#/$defs/absolutePath" },
|
|
109
|
+
"apiSymbol": { "$ref": "#/$defs/symbol" },
|
|
110
|
+
"css": { "$ref": "#/$defs/absolutePath" }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|