kennel 0.2.23 → 0.2.25
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/dist/cli/dispatch.js +4 -52
- package/dist/cli/plugin.d.ts +1 -0
- package/dist/cli/plugin.js +103 -0
- package/dist/cli/usage.js +3 -1
- package/dist/core/api-client.js +1 -2
- package/dist/core/command.js +1 -1
- package/dist/core/core-folders.js +6 -2
- package/dist/core/runtime.js +30 -4657
- package/dist/mcp/install-page.d.ts +1 -1
- package/dist/mcp/install-page.js +419 -4
- package/dist/package/preview.js +229 -4774
- package/dist/plugins/external.d.ts +32 -0
- package/dist/plugins/external.js +60 -1
- package/dist/plugins/skills.d.ts +30 -0
- package/dist/plugins/skills.js +175 -0
- package/dist/site/folder-icons.d.ts +0 -7
- package/dist/site/folder-icons.js +3 -21
- package/dist/site/hot-viewer.js +1 -1
- package/dist/site/kinds.d.ts +1 -1
- package/dist/site/kinds.js +2 -59
- package/package.json +5 -5
- package/schemas/config.schema.json +30 -0
- package/web/assets/{abnfDiagram-VRR7QNED-aRemVX7f.js → abnfDiagram-VRR7QNED-CIyEzNaJ.js} +1 -1
- package/web/assets/{arc-BpDsnNTf.js → arc-Dba3y3eW.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-DNXHQddP.js → architectureDiagram-ZJ3FMSHR-DjFjO3tR.js} +1 -1
- package/web/assets/ask-tray-PMT6VVXN-ButKBzsY.js +23 -0
- package/web/assets/{blockDiagram-677ZJIJ3-CF4H4upo.js → blockDiagram-677ZJIJ3-C8ZQgtnK.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-BPWUnjtx.js → c4Diagram-LMCZKHZV-DhWn2tZm.js} +1 -1
- package/web/assets/channel-BuqSObVl.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-DGsa7uv7.js → chunk-2Q5K7J3B-BEkPxYAB.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-BRfic3W2.js → chunk-32BRIVSS-BZ0ofM9m.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-L2h7hliu.js → chunk-5VM5RSS4-CTgBiom0.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-BPgD_fgx.js → chunk-EX3LRPZG-B7lDJEfz.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-CckCaqu_.js → chunk-JWPE2WC7-DotlQGla.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-zyGgx-VW.js → chunk-MOJQB5TN-BSlB5ICN.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-Drzpg5IG.js → chunk-RYQCIY6F-BbDvgRvj.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-Di4ESKz-.js → chunk-V7JOEXUC-CqC2yHBO.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-C5lQtDI1.js → chunk-VR4S4FIN-18aLOCVw.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-Cell0ft2.js → chunk-XXDRQBXY-uulB67ep.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-xfk1LZB_.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-xfk1LZB_.js +1 -0
- package/web/assets/{cose-bilkent-JH36ORCC-CiKtOFxl.js → cose-bilkent-JH36ORCC-C8yqL7ml.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-B75MWf5b.js → cynefin-VYW2F7L2-D-wd85t1.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-BmNVkSWW.js → cynefinDiagram-TSTJHNR4-rrV6rLov.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-BcM1Wqu5.js → dagre-VKFMJZFB-BxwiLhMr.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-BiBWrxa8.js → diagram-FQU43EPY-8sr7lcj4.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-Cfxukr7T.js → diagram-G47NLZAW-BQIU_Mie.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-C83VkBYW.js → diagram-NH7WQ7WH-Bt-xVbVq.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-Oj0uj6CX.js → diagram-OA4YK3LP-p-i-3cps.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-DhbL0nvt.js → diagram-WEI45ONY-E6t6-_mU.js} +1 -1
- package/web/assets/{ebnfDiagram-CCIWWBDH-D4JYp1UC.js → ebnfDiagram-CCIWWBDH-DCszfyU3.js} +1 -1
- package/web/assets/{entity-search-page-G6QEQNXT-UKzUU5id.js → entity-search-page-G6QEQNXT-D6cvoqrF.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-C-oyRt9s.js → erDiagram-Q63AITRT-C4gBewIM.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-0Tplj9FK.js → flowDiagram-23GEKE2U-BEyFAV-g.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-wgh2e0B_.js → ganttDiagram-NO4QXBWP-Bat5BF2x.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-D6z9qDXa.js → gitGraphDiagram-IHSO6WYX-CGZs8b5E.js} +1 -1
- package/web/assets/index-DmJQJIC1.js +451 -0
- package/web/assets/{index-BGwTWQMJ.css → index-tpV2etC4.css} +1 -1
- package/web/assets/{infoDiagram-FWYZ7A6U-D3o7el5X.js → infoDiagram-FWYZ7A6U-Cglwuazb.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-CM0QyWYH.js → ishikawaDiagram-FXEZZL3T-C95-Gz9z.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-fmIoh0h6.js → journeyDiagram-5HDEW3XC-C055Trr6.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-aEtqsiWf.js → kanban-definition-HUTT4EX6-CznSv3_t.js} +1 -1
- package/web/assets/{linear-aJ-2UZO5.js → linear-CnWGyi6E.js} +1 -1
- package/web/assets/{linked-session-header-M7WY6IGF-CltMJZiX.js → linked-session-header-M7WY6IGF-BFioq_q7.js} +1 -1
- package/web/assets/{mermaid.core-CL9g6xVu.js → mermaid.core-BIJiZKob.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-CtQo0XrW.js → mindmap-definition-LN4V7U3C-k97sTi1e.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-ecu1L_3A.js → pegDiagram-2B236MQR-C-4ETzyd.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-CdCKg3Cn.js → pieDiagram-ENE6RG2P-CH8gYIx3.js} +1 -1
- package/web/assets/{plugin-host-modules-Dz_BPJRS.js → plugin-host-modules-BI0aGGBk.js} +2 -2
- package/web/assets/{project-search-dialog-HJFGW4DG-CKtXc-UP.js → project-search-dialog-HJFGW4DG-DC6mBFkm.js} +1 -1
- package/web/assets/{project-search-page-NKQEBGPF-CMzuYEeM.js → project-search-page-NKQEBGPF-B0OxN-l5.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Bt8r5TlW.js → quadrantDiagram-ABIIQ3AL-BFV2fyQY.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-DrWDVEVi.js → railroadDiagram-RFXS5EU6-Dxj8XtW1.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-Ewjwpo6V.js → requirementDiagram-TGXJPOKE-Drb3A2JL.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-CJCMHuzX.js → sankeyDiagram-HTMAVEWB-DP7Lyg91.js} +1 -1
- package/web/assets/{selection-popup-532EHNCZ-py5aCy1E.js → selection-popup-532EHNCZ-C8EBNgOw.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-Chj-4L6Q.js → sequenceDiagram-DBY2YBRQ-6qzKd9e_.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-BguWhDIF.js → sizeCapture-X5ZJPWSS-DhCU_-w6.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-BWisWMpK.js → stateDiagram-2N3HPSRC-C_BN15Uj.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-AiBXToz-.js → stateDiagram-v2-6OUMAXLB-DtNCoZjH.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-CxVQ97_C.js → swimlanes-5IMT3BWC-CW-FMmDB.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-Cak69Ty7.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-CCbI0InF.js → timeline-definition-FHXFAJF6-VX8Kq-Hg.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-mA5yKpJj.js → vennDiagram-L72KCM5P-DU3qCuRJ.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-1Epw2EsU.js → wardleyDiagram-EHGQE667-CAA7wYyD.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DM1CMD-p.js → xychartDiagram-FW5EYKEG-B-GbIjdQ.js} +1 -1
- package/web/index.html +2 -2
- package/README.md +0 -317
- package/web/assets/ask-tray-PMT6VVXN-HMPARq3A.js +0 -23
- package/web/assets/channel-CEZ4sDYU.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-CjxqMQn2.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-CjxqMQn2.js +0 -1
- package/web/assets/index-D_Fcgs3V.js +0 -451
- package/web/assets/swimlanesDiagram-G3AALYLV-DliPmGmf.js +0 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SkillState } from "./skills.js";
|
|
1
2
|
export type ExternalPluginEntry = {
|
|
2
3
|
url: string;
|
|
3
4
|
requestedRef?: string;
|
|
@@ -15,6 +16,15 @@ export type ExternalPluginEntry = {
|
|
|
15
16
|
api?: string;
|
|
16
17
|
css?: string;
|
|
17
18
|
};
|
|
19
|
+
skill?: {
|
|
20
|
+
entry: string;
|
|
21
|
+
name: string;
|
|
22
|
+
installed?: {
|
|
23
|
+
path: string;
|
|
24
|
+
commit: string;
|
|
25
|
+
digest: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
18
28
|
};
|
|
19
29
|
export declare function kennelConfigPath(): string;
|
|
20
30
|
export declare const PLUGIN_SOURCE_IGNORED: Set<string>;
|
|
@@ -25,6 +35,28 @@ export declare function readExternalPlugins(): ExternalPluginEntry[];
|
|
|
25
35
|
export declare function installExternalPlugin(url: string, options?: {
|
|
26
36
|
directory?: string;
|
|
27
37
|
}): Promise<ExternalPluginEntry>;
|
|
38
|
+
/** Installs a declared skill from local source or the pinned cached checkout. */
|
|
39
|
+
export declare function installPluginSkill(id: string, options?: {
|
|
40
|
+
force?: boolean;
|
|
41
|
+
}): {
|
|
42
|
+
duplicates: string[];
|
|
43
|
+
path: string;
|
|
44
|
+
commit: string;
|
|
45
|
+
digest: string;
|
|
46
|
+
name: string;
|
|
47
|
+
};
|
|
48
|
+
export type PluginStatus = {
|
|
49
|
+
id: string;
|
|
50
|
+
packageName: string;
|
|
51
|
+
commit: string;
|
|
52
|
+
skill: {
|
|
53
|
+
name?: string;
|
|
54
|
+
state: SkillState;
|
|
55
|
+
path?: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/** Reports installed plugins and the real state of each declared skill. */
|
|
59
|
+
export declare function listPluginStatus(): PluginStatus[];
|
|
28
60
|
/** The compiled host web app this CLI serves. */
|
|
29
61
|
export declare function webRoot(): string;
|
|
30
62
|
/**
|
package/dist/plugins/external.js
CHANGED
|
@@ -14,6 +14,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve, } from "node:pa
|
|
|
14
14
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
16
16
|
import { z } from "zod";
|
|
17
|
+
import { duplicateSkills, readPluginSkill, skillDestination, skillState, writeSkill } from "./skills.js";
|
|
17
18
|
const CONFIG_FILE = join("kennel", "config.json");
|
|
18
19
|
const VALID_ID = /^[a-z][a-z0-9-]*$/u;
|
|
19
20
|
const CORE_IDS = new Set(["agents", "notes"]);
|
|
@@ -142,9 +143,13 @@ function resolveCommit(url, requestedRef) {
|
|
|
142
143
|
throw new Error(`Could not resolve ref ${requestedRef} for ${url}`);
|
|
143
144
|
return commit;
|
|
144
145
|
}
|
|
145
|
-
function
|
|
146
|
+
function sourceDir(url) {
|
|
146
147
|
const dir = join(cacheRoot(), "plugins", "sources", hash(url), "repo");
|
|
147
148
|
assertUnderCache(dir);
|
|
149
|
+
return dir;
|
|
150
|
+
}
|
|
151
|
+
function sourceCheckout(url, commit) {
|
|
152
|
+
const dir = sourceDir(url);
|
|
148
153
|
mkdirSync(dirname(dir), { recursive: true });
|
|
149
154
|
if (!existsSync(join(dir, ".git")))
|
|
150
155
|
run("git", ["clone", "--no-checkout", url, dir]);
|
|
@@ -400,6 +405,7 @@ export async function validateExternalPlugin(inputUrl, options = {}) {
|
|
|
400
405
|
const api = entryByConvention(root, "api.ts", false);
|
|
401
406
|
const cssValue = metadata?.exports?.css;
|
|
402
407
|
const css = cssValue === undefined ? undefined : safeRelativeFile(root, cssValue, "css export path");
|
|
408
|
+
const skill = metadata?.skill === undefined ? undefined : readPluginSkill(root, metadata.skill);
|
|
403
409
|
const entry = {
|
|
404
410
|
url: source.url,
|
|
405
411
|
...(source.requestedRef ? { requestedRef: source.requestedRef } : {}),
|
|
@@ -417,6 +423,7 @@ export async function validateExternalPlugin(inputUrl, options = {}) {
|
|
|
417
423
|
...(api ? { api } : {}),
|
|
418
424
|
...(css ? { css } : {}),
|
|
419
425
|
},
|
|
426
|
+
...(skill ? { skill: { entry: skill.entry, name: skill.name } } : {}),
|
|
420
427
|
};
|
|
421
428
|
installPluginDependencies(root, pkg.dependencies);
|
|
422
429
|
await validateImports(entry);
|
|
@@ -449,6 +456,58 @@ export async function installExternalPlugin(url, options = {}) {
|
|
|
449
456
|
}
|
|
450
457
|
return entry;
|
|
451
458
|
}
|
|
459
|
+
/** Uses local source or checks out the pinned cached commit without fetching. */
|
|
460
|
+
function pinnedPackageRoot(plugin) {
|
|
461
|
+
if (plugin.path)
|
|
462
|
+
return pluginRoot(realpathSync(plugin.path), plugin.directory);
|
|
463
|
+
const dir = sourceDir(plugin.url);
|
|
464
|
+
if (!existsSync(join(dir, ".git")))
|
|
465
|
+
throw new Error(`Cached source for ${plugin.id} is missing; run \`kennel plugin add\` again`);
|
|
466
|
+
run("git", ["checkout", "--force", "--quiet", plugin.commit], { cwd: dir });
|
|
467
|
+
if (run("git", ["rev-parse", "HEAD"], { cwd: dir }) !== plugin.commit)
|
|
468
|
+
throw new Error(`Cached source for ${plugin.id} is not at ${plugin.commit}`);
|
|
469
|
+
return pluginRoot(realpathSync(dir), plugin.directory);
|
|
470
|
+
}
|
|
471
|
+
/** Installs a declared skill from local source or the pinned cached checkout. */
|
|
472
|
+
export function installPluginSkill(id, options = {}) {
|
|
473
|
+
const path = kennelConfigPath();
|
|
474
|
+
const config = readJson(path);
|
|
475
|
+
const plugins = config.externalPlugins ?? [];
|
|
476
|
+
const plugin = plugins.find((item) => item.id === id);
|
|
477
|
+
if (!plugin)
|
|
478
|
+
throw new Error(`No installed plugin with id ${id}`);
|
|
479
|
+
if (!plugin.skill)
|
|
480
|
+
throw new Error(`Plugin ${id} declares no skill (kennel.plugin.skill or kennel.plugin.json > skill)`);
|
|
481
|
+
const skill = readPluginSkill(pinnedPackageRoot(plugin), plugin.skill.entry);
|
|
482
|
+
if (skill.name !== plugin.skill.name)
|
|
483
|
+
throw new Error(`Pinned skill name ${skill.name} does not match recorded name ${plugin.skill.name}`);
|
|
484
|
+
const duplicates = duplicateSkills(skill.name);
|
|
485
|
+
const commit = plugin.path ? contentHash(pluginRoot(plugin.path, plugin.directory)) : plugin.commit;
|
|
486
|
+
const written = writeSkill(skill, options);
|
|
487
|
+
const installed = { path: written.path, commit, digest: written.digest };
|
|
488
|
+
const nextConfig = {
|
|
489
|
+
...config,
|
|
490
|
+
externalPlugins: plugins.map((item) => item.id === id ? { ...item, skill: { entry: skill.entry, name: skill.name, installed } } : item),
|
|
491
|
+
};
|
|
492
|
+
assertUserConfig(nextConfig, path);
|
|
493
|
+
writeJsonAtomic(path, nextConfig);
|
|
494
|
+
return { name: skill.name, ...installed, duplicates };
|
|
495
|
+
}
|
|
496
|
+
/** Reports installed plugins and the real state of each declared skill. */
|
|
497
|
+
export function listPluginStatus() {
|
|
498
|
+
return readExternalPlugins().map((plugin) => ({
|
|
499
|
+
id: plugin.id,
|
|
500
|
+
packageName: plugin.packageName,
|
|
501
|
+
commit: plugin.commit,
|
|
502
|
+
skill: plugin.skill
|
|
503
|
+
? {
|
|
504
|
+
name: plugin.skill.name,
|
|
505
|
+
state: skillState(plugin.skill.name, plugin.skill.installed?.digest),
|
|
506
|
+
path: skillDestination(plugin.skill.name),
|
|
507
|
+
}
|
|
508
|
+
: { state: "missing" },
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
452
511
|
function cliRoot() {
|
|
453
512
|
return resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
454
513
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type PluginSkill = {
|
|
2
|
+
entry: string;
|
|
3
|
+
name: string;
|
|
4
|
+
directory: string;
|
|
5
|
+
};
|
|
6
|
+
export type SkillState = "missing" | "installed" | "conflict";
|
|
7
|
+
/** Canonical global agent store: `${AGENTS_HOME:-$HOME/.agents}/skills`. */
|
|
8
|
+
export declare function agentSkillsRoot(): string;
|
|
9
|
+
export declare function skillDestination(name: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves `package.json > kennel.plugin.skill` inside a plugin package root.
|
|
12
|
+
* The entry must be a contained, non-symlinked `SKILL.md` with a valid name and description.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readPluginSkill(packageRoot: string, entry: unknown): PluginSkill;
|
|
15
|
+
/** Content digest of a skill directory: relative paths plus file bytes. */
|
|
16
|
+
export declare function skillDigest(directory: string): string;
|
|
17
|
+
/** Reports the installed skill state at the canonical destination. */
|
|
18
|
+
export declare function skillState(name: string, digest?: string): SkillState;
|
|
19
|
+
/** Same-name skills in known agent stores other than the canonical one. Read only. */
|
|
20
|
+
export declare function duplicateSkills(name: string): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Copies a resolved skill directory into the canonical agent store.
|
|
23
|
+
* An existing destination is a conflict unless `force` is set.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeSkill(skill: PluginSkill, options?: {
|
|
26
|
+
force?: boolean;
|
|
27
|
+
}): {
|
|
28
|
+
path: string;
|
|
29
|
+
digest: string;
|
|
30
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { parse as parseYaml } from "yaml";
|
|
6
|
+
/** Agent skill folder names follow the Agent Skills naming rule. */
|
|
7
|
+
const VALID_SKILL_NAME = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
|
8
|
+
/** Never copy repository metadata when a skill lives at a repository root. */
|
|
9
|
+
const SKIPPED_ENTRIES = new Set([".git"]);
|
|
10
|
+
function isInside(root, target) {
|
|
11
|
+
const rel = relative(root, target);
|
|
12
|
+
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
13
|
+
}
|
|
14
|
+
/** Canonical global agent store: `${AGENTS_HOME:-$HOME/.agents}/skills`. */
|
|
15
|
+
export function agentSkillsRoot() {
|
|
16
|
+
const home = process.env.AGENTS_HOME?.trim() || join(homedir(), ".agents");
|
|
17
|
+
if (!isAbsolute(home))
|
|
18
|
+
throw new Error(`AGENTS_HOME must be an absolute path: ${home}`);
|
|
19
|
+
return resolve(home, "skills");
|
|
20
|
+
}
|
|
21
|
+
export function skillDestination(name) {
|
|
22
|
+
if (!VALID_SKILL_NAME.test(name))
|
|
23
|
+
throw new Error(`Invalid skill name: ${name}`);
|
|
24
|
+
const root = agentSkillsRoot();
|
|
25
|
+
const target = resolve(root, name);
|
|
26
|
+
if (dirname(target) !== root)
|
|
27
|
+
throw new Error(`Refusing skill path outside ${root}: ${name}`);
|
|
28
|
+
return target;
|
|
29
|
+
}
|
|
30
|
+
function frontmatter(path) {
|
|
31
|
+
const text = readFileSync(path, "utf8");
|
|
32
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(text);
|
|
33
|
+
if (!match)
|
|
34
|
+
throw new Error(`Skill ${path} has no YAML frontmatter`);
|
|
35
|
+
const data = parseYaml(match[1] ?? "");
|
|
36
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
37
|
+
throw new Error(`Skill ${path} frontmatter is not a mapping`);
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolves `package.json > kennel.plugin.skill` inside a plugin package root.
|
|
42
|
+
* The entry must be a contained, non-symlinked `SKILL.md` with a valid name and description.
|
|
43
|
+
*/
|
|
44
|
+
export function readPluginSkill(packageRoot, entry) {
|
|
45
|
+
if (typeof entry !== "string" || !entry.trim())
|
|
46
|
+
throw new Error("kennel.plugin.skill must be a relative path to SKILL.md");
|
|
47
|
+
if (isAbsolute(entry) || entry.includes("\0") || entry.includes("\\"))
|
|
48
|
+
throw new Error(`Unsafe skill path: ${entry}`);
|
|
49
|
+
if (basename(entry) !== "SKILL.md")
|
|
50
|
+
throw new Error(`Skill path must name a SKILL.md file: ${entry}`);
|
|
51
|
+
const root = realpathSync(packageRoot);
|
|
52
|
+
const file = resolve(root, entry);
|
|
53
|
+
if (!isInside(root, file) || file === root)
|
|
54
|
+
throw new Error(`Unsafe skill path: ${entry}`);
|
|
55
|
+
if (!existsSync(file))
|
|
56
|
+
throw new Error(`Missing skill file: ${entry}`);
|
|
57
|
+
const directory = realpathSync(dirname(file));
|
|
58
|
+
if (!isInside(root, directory))
|
|
59
|
+
throw new Error(`Skill directory escapes the plugin package: ${entry}`);
|
|
60
|
+
const stat = lstatSync(join(directory, "SKILL.md"));
|
|
61
|
+
if (!stat.isFile())
|
|
62
|
+
throw new Error(`Skill path is not a regular file: ${entry}`);
|
|
63
|
+
const data = frontmatter(join(directory, "SKILL.md"));
|
|
64
|
+
const name = typeof data.name === "string" ? data.name.trim() : "";
|
|
65
|
+
const description = typeof data.description === "string" ? data.description.trim() : "";
|
|
66
|
+
if (!name)
|
|
67
|
+
throw new Error(`Skill ${entry} frontmatter needs a nonempty name`);
|
|
68
|
+
if (!description)
|
|
69
|
+
throw new Error(`Skill ${entry} frontmatter needs a nonempty description`);
|
|
70
|
+
if (!VALID_SKILL_NAME.test(name))
|
|
71
|
+
throw new Error(`Skill ${entry} frontmatter name must match ${VALID_SKILL_NAME}: ${name}`);
|
|
72
|
+
return { entry: relative(root, join(directory, "SKILL.md")).split(sep).join("/"), name, directory };
|
|
73
|
+
}
|
|
74
|
+
/** Lists regular files under a skill directory. Symlinks and special files are refused. */
|
|
75
|
+
function skillFiles(directory, prefix = "") {
|
|
76
|
+
return readdirSync(join(directory, prefix), { withFileTypes: true })
|
|
77
|
+
.filter((item) => !SKIPPED_ENTRIES.has(item.name))
|
|
78
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
79
|
+
.flatMap((item) => {
|
|
80
|
+
const rel = prefix ? join(prefix, item.name) : item.name;
|
|
81
|
+
if (item.isDirectory())
|
|
82
|
+
return skillFiles(directory, rel);
|
|
83
|
+
if (item.isFile())
|
|
84
|
+
return [rel];
|
|
85
|
+
throw new Error(`Skill contains an unsupported entry (symlink or special file): ${join(directory, rel)}`);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Content digest of a skill directory: relative paths plus file bytes. */
|
|
89
|
+
export function skillDigest(directory) {
|
|
90
|
+
const hash = createHash("sha256");
|
|
91
|
+
for (const file of skillFiles(directory)) {
|
|
92
|
+
hash.update(file.split(sep).join("/")).update("\0").update(readFileSync(join(directory, file))).update("\0");
|
|
93
|
+
}
|
|
94
|
+
return hash.digest("hex");
|
|
95
|
+
}
|
|
96
|
+
/** Reports the installed skill state at the canonical destination. */
|
|
97
|
+
export function skillState(name, digest) {
|
|
98
|
+
const target = skillDestination(name);
|
|
99
|
+
if (!existsSync(target) && !isSymlink(target))
|
|
100
|
+
return "missing";
|
|
101
|
+
try {
|
|
102
|
+
if (!lstatSync(target).isDirectory())
|
|
103
|
+
return "conflict";
|
|
104
|
+
return digest && skillDigest(target) === digest ? "installed" : "conflict";
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return "conflict";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function isSymlink(path) {
|
|
111
|
+
try {
|
|
112
|
+
return lstatSync(path).isSymbolicLink();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Same-name skills in known agent stores other than the canonical one. Read only. */
|
|
119
|
+
export function duplicateSkills(name) {
|
|
120
|
+
const canonical = agentSkillsRoot();
|
|
121
|
+
const roots = [
|
|
122
|
+
process.env.CODEX_HOME?.trim() ? join(process.env.CODEX_HOME.trim(), "skills") : undefined,
|
|
123
|
+
join(homedir(), ".codex", "skills"),
|
|
124
|
+
join(homedir(), ".claude", "skills"),
|
|
125
|
+
].filter((root) => Boolean(root));
|
|
126
|
+
const canonicalReal = existsSync(canonical) ? realpathSync(canonical) : canonical;
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
return roots.flatMap((root) => {
|
|
129
|
+
const real = existsSync(root) ? realpathSync(root) : resolve(root);
|
|
130
|
+
if (real === canonicalReal || seen.has(real))
|
|
131
|
+
return [];
|
|
132
|
+
seen.add(real);
|
|
133
|
+
const path = join(root, name);
|
|
134
|
+
return existsSync(path) || isSymlink(path) ? [path] : [];
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Copies a resolved skill directory into the canonical agent store.
|
|
139
|
+
* An existing destination is a conflict unless `force` is set.
|
|
140
|
+
*/
|
|
141
|
+
export function writeSkill(skill, options = {}) {
|
|
142
|
+
const root = agentSkillsRoot();
|
|
143
|
+
const target = skillDestination(skill.name);
|
|
144
|
+
const exists = existsSync(target) || isSymlink(target);
|
|
145
|
+
if (exists && !options.force) {
|
|
146
|
+
throw new Error(`Skill conflict: ${target} already exists. Re-run with --force to replace it.`);
|
|
147
|
+
}
|
|
148
|
+
const files = skillFiles(skill.directory);
|
|
149
|
+
mkdirSync(root, { recursive: true });
|
|
150
|
+
const temp = join(root, `.${skill.name}.kennel-${process.pid}-${Date.now()}`);
|
|
151
|
+
try {
|
|
152
|
+
for (const file of files) {
|
|
153
|
+
const from = join(skill.directory, file);
|
|
154
|
+
const to = join(temp, file);
|
|
155
|
+
if (!isInside(skill.directory, from) || !isInside(temp, to))
|
|
156
|
+
throw new Error(`Unsafe skill file: ${file}`);
|
|
157
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
158
|
+
cpSync(from, to, { errorOnExist: true, force: false, preserveTimestamps: true });
|
|
159
|
+
}
|
|
160
|
+
const digest = skillDigest(temp);
|
|
161
|
+
if (exists) {
|
|
162
|
+
const previous = `${temp}.previous`;
|
|
163
|
+
renameSync(target, previous);
|
|
164
|
+
renameSync(temp, target);
|
|
165
|
+
rmSync(previous, { force: true, recursive: true });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
renameSync(temp, target);
|
|
169
|
+
}
|
|
170
|
+
return { path: target, digest };
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
rmSync(temp, { force: true, recursive: true });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -20,13 +20,6 @@ export declare function assetKey(value: Buffer): string;
|
|
|
20
20
|
export declare function encodeBase64Url(value: string): string;
|
|
21
21
|
export declare function decodeBase64Url(value: string): string;
|
|
22
22
|
export declare function iconUrl(folder: string, hash: string, mediaType: IconMediaType): string;
|
|
23
|
-
/**
|
|
24
|
-
* Nearest ancestor icon for a folder, walking up parent directories so
|
|
25
|
-
* nested folders inherit the closest icon above them. The bundled
|
|
26
|
-
* `@raggle-ai/local` does not export the ancestor-aware discovery helper
|
|
27
|
-
* yet, so the walk is implemented here over `discoverProjectIcon`.
|
|
28
|
-
*/
|
|
29
|
-
export declare function discoverAncestorIcon(folder: string): string | undefined;
|
|
30
23
|
export declare function folderIcon(folder: string, assets: Record<string, IconAsset>, assetFiles: Map<string, {
|
|
31
24
|
mediaType: IconMediaType;
|
|
32
25
|
path: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { readFile, stat } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { extname } from 'node:path';
|
|
4
|
+
import { discoverFolderIcon } from '@raggle-ai/local';
|
|
5
5
|
/**
|
|
6
6
|
* Folder icons are served as immutable, content-addressed assets, so the
|
|
7
7
|
* cap is generous: it only guards against pathological files, not normal
|
|
@@ -34,26 +34,8 @@ export function decodeBase64Url(value) {
|
|
|
34
34
|
export function iconUrl(folder, hash, mediaType) {
|
|
35
35
|
return `/api/folders/${encodeBase64Url(folder)}/assets/${hash}.${iconExtensions.get(mediaType)}`;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Nearest ancestor icon for a folder, walking up parent directories so
|
|
39
|
-
* nested folders inherit the closest icon above them. The bundled
|
|
40
|
-
* `@raggle-ai/local` does not export the ancestor-aware discovery helper
|
|
41
|
-
* yet, so the walk is implemented here over `discoverProjectIcon`.
|
|
42
|
-
*/
|
|
43
|
-
export function discoverAncestorIcon(folder) {
|
|
44
|
-
let current = folder;
|
|
45
|
-
while (true) {
|
|
46
|
-
const icon = discoverProjectIcon(current);
|
|
47
|
-
if (icon)
|
|
48
|
-
return icon;
|
|
49
|
-
const parent = dirname(current);
|
|
50
|
-
if (parent === current)
|
|
51
|
-
return undefined;
|
|
52
|
-
current = parent;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
37
|
export async function folderIcon(folder, assets, assetFiles, fallback) {
|
|
56
|
-
const icon =
|
|
38
|
+
const icon = discoverFolderIcon(folder);
|
|
57
39
|
const mediaType = icon ? iconTypes.get(extname(icon).toLowerCase()) : null;
|
|
58
40
|
const details = icon && mediaType ? await stat(icon).catch(() => undefined) : undefined;
|
|
59
41
|
if (!icon || !mediaType || !details?.size || details.size > maxIconBytes) {
|
package/dist/site/hot-viewer.js
CHANGED
|
@@ -201,7 +201,7 @@ export async function hotViewer(options) {
|
|
|
201
201
|
env: {
|
|
202
202
|
...process.env,
|
|
203
203
|
// Explicit remote bindings (--lan) switch the Vite plugins into
|
|
204
|
-
// remote mode: realpath root scope, host actions disabled
|
|
204
|
+
// remote mode: realpath root scope, host actions disabled.
|
|
205
205
|
// KENNEL_VIEWER_HOSTS tells the config which Host headers are legit
|
|
206
206
|
// (DNS-rebinding guard; loopback is always allowed).
|
|
207
207
|
KENNEL_VIEWER_ROOT: options.root,
|
package/dist/site/kinds.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { previewKind } from "kennel-api/shared/document-kinds";
|
package/dist/site/kinds.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// packages/
|
|
1
|
+
// packages/api/src/shared/document-kinds.ts
|
|
2
2
|
var previewTextKinds = new Set([
|
|
3
3
|
"code",
|
|
4
4
|
"eml",
|
|
@@ -7,9 +7,6 @@ var previewTextKinds = new Set([
|
|
|
7
7
|
"table",
|
|
8
8
|
"text"
|
|
9
9
|
]);
|
|
10
|
-
function previewTextKind(kind) {
|
|
11
|
-
return previewTextKinds.has(kind);
|
|
12
|
-
}
|
|
13
10
|
var codeExtensions = new Set([
|
|
14
11
|
".astro",
|
|
15
12
|
".bash",
|
|
@@ -100,60 +97,6 @@ function previewKind(path) {
|
|
|
100
97
|
return "binary";
|
|
101
98
|
return;
|
|
102
99
|
}
|
|
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
100
|
export {
|
|
154
|
-
|
|
155
|
-
previewKind,
|
|
156
|
-
previewRawMime,
|
|
157
|
-
previewTextKind,
|
|
158
|
-
previewTextKinds
|
|
101
|
+
previewKind
|
|
159
102
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kennel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.25",
|
|
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",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "node scripts/build.mjs",
|
|
51
51
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
52
|
-
"check": "npm run build && node --check bin/kennel.mjs && node --check dist/main.js",
|
|
52
|
+
"check": "npm run build && node --check bin/kennel.mjs && node --check dist/main.js && npm run smoke",
|
|
53
|
+
"smoke": "node --input-type=module -e \"await import('./dist/mcp/generate.js')\"",
|
|
53
54
|
"prepack": "node scripts/build.mjs",
|
|
54
55
|
"pack:check": "bun pm pack --dry-run",
|
|
55
56
|
"publish:public": "npm publish --access public --registry=https://registry.npmjs.org --@raggle-ai:registry=https://registry.npmjs.org",
|
|
@@ -65,12 +66,10 @@
|
|
|
65
66
|
"date-fns": "^4.1.0",
|
|
66
67
|
"effect": "^3.19.7",
|
|
67
68
|
"esbuild": "^0.27.0",
|
|
68
|
-
"kennel-knowledge": "https://registry.npmjs.org/%40raggle-ai%2fkennel-knowledge/-/kennel-knowledge-0.1.0.tgz",
|
|
69
69
|
"kennel-mcp": "https://registry.npmjs.org/%40raggle-ai%2fkennel-mcp/-/kennel-mcp-0.1.8.tgz",
|
|
70
|
-
"kennel-plugins": "0.1.
|
|
70
|
+
"kennel-plugins": "0.1.11",
|
|
71
71
|
"koffi": "^3.3.1",
|
|
72
72
|
"lucide-react": "^0.562.0",
|
|
73
|
-
"pi-acp": "0.0.33",
|
|
74
73
|
"radix-ui": "^1.6.7",
|
|
75
74
|
"react": "19.1.0",
|
|
76
75
|
"react-dom": "19.1.0",
|
|
@@ -84,6 +83,7 @@
|
|
|
84
83
|
},
|
|
85
84
|
"devDependencies": {
|
|
86
85
|
"@types/node": "^22.13.4",
|
|
86
|
+
"kennel-api": "0.1.0",
|
|
87
87
|
"kennel-preview": "0.1.0",
|
|
88
88
|
"typescript": "^5.9.3"
|
|
89
89
|
},
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"type": "string",
|
|
80
80
|
"pattern": "^(?!\\.{1,2}$)[^/\\\\]+$"
|
|
81
81
|
},
|
|
82
|
+
"skill": { "$ref": "#/$defs/pluginSkill" },
|
|
82
83
|
"entries": {
|
|
83
84
|
"description": "Resolved absolute convention entry files. api is optional.",
|
|
84
85
|
"oneOf": [
|
|
@@ -88,6 +89,35 @@
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
},
|
|
92
|
+
"pluginSkill": {
|
|
93
|
+
"description": "Agent skill declared by package.json > kennel.plugin.skill or kennel.plugin.json > skill. Managed by `kennel plugin add` and `kennel plugin skill install`.",
|
|
94
|
+
"type": "object",
|
|
95
|
+
"additionalProperties": false,
|
|
96
|
+
"required": ["entry", "name"],
|
|
97
|
+
"properties": {
|
|
98
|
+
"entry": {
|
|
99
|
+
"description": "SKILL.md path relative to the plugin package root.",
|
|
100
|
+
"type": "string",
|
|
101
|
+
"pattern": "^(?!/)(?:[^\\\\]*/)?SKILL\\.md$"
|
|
102
|
+
},
|
|
103
|
+
"name": {
|
|
104
|
+
"description": "Skill frontmatter name; the agent-store folder name.",
|
|
105
|
+
"type": "string",
|
|
106
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
|
|
107
|
+
},
|
|
108
|
+
"installed": {
|
|
109
|
+
"description": "Last install into ${AGENTS_HOME:-$HOME/.agents}/skills.",
|
|
110
|
+
"type": "object",
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"required": ["path", "commit", "digest"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"path": { "$ref": "#/$defs/absolutePath" },
|
|
115
|
+
"commit": { "description": "Pinned Git commit or local source SHA-256.", "type": "string", "pattern": "^(?:[0-9a-f]{40}|[0-9a-f]{64})$" },
|
|
116
|
+
"digest": { "description": "SHA-256 of the installed skill files.", "type": "string", "pattern": "^[0-9a-f]{64}$" }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
91
121
|
"entriesWithoutApi": {
|
|
92
122
|
"type": "object",
|
|
93
123
|
"additionalProperties": false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-BSlB5ICN.js";import{p as f}from"./chunk-JWPE2WC7-DotlQGla.js";import{_ as n,l as o}from"./mermaid.core-BIJiZKob.js";import{b as c,M as d}from"./cynefin-VYW2F7L2-D-wd85t1.js";import"./index-DmJQJIC1.js";import"./react-DELVxLrX.js";import"./markdown-BP-54rPU.js";import"./tanstack-CRaSxWtK.js";var v=c().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),m=t?parseInt(t,10):0,l=s?parseInt(s,10):1/0;return{min:m,max:l}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new d(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},C={parser:R,db:a,renderer:u,styles:p};export{C as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-
|
|
1
|
+
import{Y as ln,$ as an,a0 as F,a1 as q,a2 as j,a3 as un,a4 as y,a5 as tn,a6 as J,a7 as _,a8 as rn,a9 as o,aa as sn,ab as on,ac as fn}from"./mermaid.core-BIJiZKob.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(on(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,B=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<an)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(fn((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|