kennel 0.1.8 → 0.1.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 +36 -87
- package/bin/kennel-native.mjs +5 -0
- package/dist/cli/dispatch.js +25 -16
- package/dist/cli/usage.js +9 -3
- package/dist/deploy/cloudflare.js +10 -1
- package/dist/deploy/smoke-tests.d.ts +1 -0
- package/dist/deploy/smoke-tests.js +21 -0
- package/dist/deploy/vercel.d.ts +3 -0
- package/dist/deploy/vercel.js +67 -0
- package/dist/mcp/config.js +14 -0
- package/dist/mcp/function-source.js +3 -1
- package/dist/mcp/generate.js +3 -3
- package/dist/mcp/install-page.d.ts +12 -1
- package/dist/mcp/install-page.js +3 -358
- package/dist/notifications/email-worker.js +1 -1
- package/dist/publish/cloudflare-init.js +5 -2
- package/dist/publish/config.d.ts +1 -1
- package/dist/publish/config.js +3 -15
- package/dist/publish/vercel-init.d.ts +9 -0
- package/dist/publish/vercel-init.js +52 -0
- package/dist/site/dev.js +2 -4
- package/dist/site/extension.d.ts +8 -0
- package/dist/site/extension.js +114 -0
- package/dist/site/renderers.d.ts +61 -0
- package/dist/site/renderers.js +219 -0
- package/dist/site/viewer.d.ts +9 -0
- package/dist/site/viewer.js +192 -0
- package/dist/site/vitepress.d.ts +1 -21
- package/dist/site/vitepress.js +1 -66
- package/dist/sources/commands.d.ts +1 -0
- package/dist/sources/commands.js +88 -0
- package/package.json +8 -8
- package/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/assets/abnfDiagram-VRR7QNED-Dizo1F0V.js +1 -0
- package/web/assets/arc-CraBhW5A.js +1 -0
- package/web/assets/architectureDiagram-ZJ3FMSHR-DrcQCjV4.js +36 -0
- package/web/assets/blockDiagram-677ZJIJ3-iGvS8HCJ.js +132 -0
- package/web/assets/c4Diagram-LMCZKHZV-B9ueRzOf.js +10 -0
- package/web/assets/channel-DjVeob-k.js +1 -0
- package/web/assets/chunk-2Q5K7J3B-Dg02y5Go.js +1 -0
- package/web/assets/chunk-32BRIVSS-DHUNfpw6.js +1 -0
- package/web/assets/chunk-5VM5RSS4-B6XJu9sr.js +15 -0
- package/web/assets/chunk-EX3LRPZG-DywY4I3l.js +231 -0
- package/web/assets/chunk-JWPE2WC7-C9-XkXDg.js +1 -0
- package/web/assets/chunk-MOJQB5TN-CXQXOtY9.js +88 -0
- package/web/assets/chunk-RYQCIY6F-DJwv9114.js +1 -0
- package/web/assets/chunk-V7JOEXUC-_b44nhwN.js +206 -0
- package/web/assets/chunk-VR4S4FIN-B_bGSDri.js +1 -0
- package/web/assets/chunk-XXDRQBXY-Bf-JPvQi.js +1 -0
- package/web/assets/classDiagram-OUVF2IWQ-4Od2_pq4.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-4Od2_pq4.js +1 -0
- package/web/assets/cose-bilkent-JH36ORCC-Bcssb1jB.js +1 -0
- package/web/assets/cynefin-VYW2F7L2-t9uGqnJ4.js +166 -0
- package/web/assets/cynefinDiagram-TSTJHNR4-D0RvbNfG.js +62 -0
- package/web/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
- package/web/assets/dagre-VKFMJZFB-DmmlPxN_.js +4 -0
- package/web/assets/defaultLocale-C4B-KCzX.js +1 -0
- package/web/assets/diagram-FQU43EPY-DtDak7gR.js +3 -0
- package/web/assets/diagram-G47NLZAW-Cn07lZ4L.js +24 -0
- package/web/assets/diagram-NH7WQ7WH-BTD9-weZ.js +24 -0
- package/web/assets/diagram-OA4YK3LP-fjOdFedY.js +30 -0
- package/web/assets/diagram-WEI45ONY-DSBYO7Nc.js +41 -0
- package/web/assets/ebnfDiagram-CCIWWBDH-B8km6QWT.js +1 -0
- package/web/assets/erDiagram-Q63AITRT-CHs2HT-U.js +85 -0
- package/web/assets/flowDiagram-23GEKE2U-CxjQgYus.js +156 -0
- package/web/assets/ganttDiagram-NO4QXBWP-CHkGy5_G.js +292 -0
- package/web/assets/gitGraphDiagram-IHSO6WYX-BdtSK3Q5.js +106 -0
- package/web/assets/graph-BvS0Xx32.js +1 -0
- package/web/assets/index-BiSkqJri.css +1 -0
- package/web/assets/index-sG3Cxc28.js +697 -0
- package/web/assets/infoDiagram-FWYZ7A6U-B0feeRLu.js +2 -0
- package/web/assets/init-Gi6I4Gst.js +1 -0
- package/web/assets/ishikawaDiagram-FXEZZL3T-DGCSsJ4r.js +70 -0
- package/web/assets/journeyDiagram-5HDEW3XC-D9xtvo9Z.js +139 -0
- package/web/assets/kanban-definition-HUTT4EX6-BqVdsgmq.js +89 -0
- package/web/assets/layout-D4ui-CWC.js +1 -0
- package/web/assets/linear-BopdAHdJ.js +1 -0
- package/web/assets/map-7eTsl7HP.js +1 -0
- package/web/assets/mermaid.core-DWAi8MqL.js +308 -0
- package/web/assets/mindmap-definition-LN4V7U3C-CsV_gb2j.js +96 -0
- package/web/assets/ordinal-Cboi1Yqb.js +1 -0
- package/web/assets/pegDiagram-2B236MQR-BmDNuFL1.js +1 -0
- package/web/assets/pieDiagram-ENE6RG2P-CBPZlXZP.js +39 -0
- package/web/assets/quadrantDiagram-ABIIQ3AL-CbUemD3z.js +7 -0
- package/web/assets/railroadDiagram-RFXS5EU6-Cyp4W7Hm.js +1 -0
- package/web/assets/requirementDiagram-TGXJPOKE-Dz35_qtT.js +84 -0
- package/web/assets/sankeyDiagram-HTMAVEWB-biUDOCWc.js +40 -0
- package/web/assets/sequenceDiagram-DBY2YBRQ-Bk4X1-E4.js +162 -0
- package/web/assets/sizeCapture-X5ZJPWSS-CDPSVeoQ.js +1 -0
- package/web/assets/stateDiagram-2N3HPSRC-Cx_G_F7H.js +1 -0
- package/web/assets/stateDiagram-v2-6OUMAXLB-DG9cZ_-X.js +1 -0
- package/web/assets/swimlanes-5IMT3BWC-KfhGp3gJ.js +2 -0
- package/web/assets/swimlanesDiagram-G3AALYLV-Bj6WXCUy.js +8 -0
- package/web/assets/timeline-definition-FHXFAJF6-Dp6wlW_7.js +120 -0
- package/web/assets/vennDiagram-L72KCM5P-CjdOdrf1.js +34 -0
- package/web/assets/wardleyDiagram-EHGQE667-DYl5GkQs.js +78 -0
- package/web/assets/xychartDiagram-FW5EYKEG-rPJ1RPYD.js +7 -0
- package/web/index.html +14 -0
- package/dist/mcp/data.d.ts +0 -14
- package/dist/mcp/data.js +0 -120
- package/dist/schema/doctor.d.ts +0 -1
- package/dist/schema/doctor.js +0 -69
- package/dist/schema/documents.d.ts +0 -18
- package/dist/schema/documents.js +0 -127
- package/dist/schema/pointers.d.ts +0 -5
- package/dist/schema/pointers.js +0 -48
- package/dist/schema/types.d.ts +0 -36
- package/dist/schema/types.js +0 -1
- package/dist/schema/validation.d.ts +0 -8
- package/dist/schema/validation.js +0 -212
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ChildCommand } from '../shared/process.js';
|
|
2
|
+
export type PackageJson = {
|
|
3
|
+
name?: string;
|
|
4
|
+
scripts?: Record<string, string>;
|
|
5
|
+
dependencies?: Record<string, unknown>;
|
|
6
|
+
devDependencies?: Record<string, unknown>;
|
|
7
|
+
peerDependencies?: Record<string, unknown>;
|
|
8
|
+
optionalDependencies?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export type SitePlatform = 'vitepress' | 'quartz' | 'script';
|
|
11
|
+
export type RendererCapabilities = {
|
|
12
|
+
accessControl: boolean;
|
|
13
|
+
backlinks: boolean;
|
|
14
|
+
graph: boolean;
|
|
15
|
+
llms: boolean;
|
|
16
|
+
mcp: boolean;
|
|
17
|
+
search: boolean;
|
|
18
|
+
staticOutput: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type RendererDeploymentDescriptor = {
|
|
21
|
+
capabilities: RendererCapabilities;
|
|
22
|
+
cleanUrls: boolean;
|
|
23
|
+
outputDirectory: string;
|
|
24
|
+
platform: SitePlatform;
|
|
25
|
+
};
|
|
26
|
+
export type RendererAdapter = {
|
|
27
|
+
buildCommand(root: string, pkg: PackageJson): ChildCommand;
|
|
28
|
+
capabilities: RendererCapabilities;
|
|
29
|
+
cleanUrls: boolean;
|
|
30
|
+
detect(root: string, pkg: PackageJson): boolean;
|
|
31
|
+
devCommand(root: string, options: {
|
|
32
|
+
script: string | null;
|
|
33
|
+
}, scriptArgs: string[], toolRoot?: string): ChildCommand;
|
|
34
|
+
outputDirectory(root: string): string;
|
|
35
|
+
platform: SitePlatform;
|
|
36
|
+
};
|
|
37
|
+
export declare function vitepressConfig(root: string): string | null;
|
|
38
|
+
export declare function vitepressOutputDirectory(root: string): string;
|
|
39
|
+
export declare function quartzConfig(root: string): string | null;
|
|
40
|
+
export declare function quartzOutputDirectory(root: string): string;
|
|
41
|
+
export declare function displayBasePath(config: {
|
|
42
|
+
base?: unknown;
|
|
43
|
+
}): string;
|
|
44
|
+
export declare function buildScriptDevCommand(root: string, pkg: PackageJson, options: {
|
|
45
|
+
script: string | null;
|
|
46
|
+
}, scriptArgs: string[]): ChildCommand;
|
|
47
|
+
export declare function buildVitePressDevCommand(root: string, _options: {
|
|
48
|
+
script: string | null;
|
|
49
|
+
}, scriptArgs: string[], toolRoot?: string): ChildCommand;
|
|
50
|
+
export declare function buildQuartzDevCommand(root: string, _options: {
|
|
51
|
+
script: string | null;
|
|
52
|
+
}, scriptArgs: string[], toolRoot?: string): ChildCommand;
|
|
53
|
+
export declare const rendererRegistry: RendererAdapter[];
|
|
54
|
+
export declare function rendererFor(root: string, pkg: PackageJson): RendererAdapter;
|
|
55
|
+
export declare function detectedPlatform(root: string, pkg: PackageJson): SitePlatform;
|
|
56
|
+
export declare function rendererDeploymentDescriptor(root: string, pkg?: PackageJson): RendererDeploymentDescriptor;
|
|
57
|
+
export declare function siteOutputDirectory(root: string, pkg?: PackageJson): string;
|
|
58
|
+
export declare function rendererDevCommand(root: string, pkg: PackageJson, platform: SitePlatform, options: {
|
|
59
|
+
script: string | null;
|
|
60
|
+
}, scriptArgs: string[], toolRoot?: string): ChildCommand;
|
|
61
|
+
export declare function rendererBuildCommand(root: string, pkg: PackageJson): ChildCommand;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { hasDependency, localBin, packageManagerFor, runArgsFor } from '../shared/package-manager.js';
|
|
4
|
+
function rendererCapabilities(capabilities) {
|
|
5
|
+
return capabilities;
|
|
6
|
+
}
|
|
7
|
+
export function vitepressConfig(root) {
|
|
8
|
+
const configNames = [
|
|
9
|
+
'.vitepress/config.mts',
|
|
10
|
+
'.vitepress/config.ts',
|
|
11
|
+
'.vitepress/config.mjs',
|
|
12
|
+
'.vitepress/config.js',
|
|
13
|
+
'docs/.vitepress/config.mts',
|
|
14
|
+
'docs/.vitepress/config.ts',
|
|
15
|
+
'docs/.vitepress/config.mjs',
|
|
16
|
+
'docs/.vitepress/config.js',
|
|
17
|
+
];
|
|
18
|
+
return configNames.find((configName) => existsSync(join(root, configName))) ?? null;
|
|
19
|
+
}
|
|
20
|
+
export function vitepressOutputDirectory(root) {
|
|
21
|
+
const configPath = vitepressConfig(root);
|
|
22
|
+
if (configPath?.startsWith('docs/'))
|
|
23
|
+
return 'docs/.vitepress/dist';
|
|
24
|
+
return '.vitepress/dist';
|
|
25
|
+
}
|
|
26
|
+
export function quartzConfig(root) {
|
|
27
|
+
const configNames = [
|
|
28
|
+
'quartz.config.ts',
|
|
29
|
+
'quartz.config.js',
|
|
30
|
+
'docs/quartz.config.ts',
|
|
31
|
+
'docs/quartz.config.js',
|
|
32
|
+
];
|
|
33
|
+
return configNames.find((configName) => existsSync(join(root, configName))) ?? null;
|
|
34
|
+
}
|
|
35
|
+
export function quartzOutputDirectory(root) {
|
|
36
|
+
const configPath = quartzConfig(root);
|
|
37
|
+
if (configPath?.startsWith('docs/'))
|
|
38
|
+
return 'docs/public';
|
|
39
|
+
return 'public';
|
|
40
|
+
}
|
|
41
|
+
export function displayBasePath(config) {
|
|
42
|
+
if (typeof config.base !== 'string' || config.base.trim() === '')
|
|
43
|
+
return '/';
|
|
44
|
+
const base = config.base.trim();
|
|
45
|
+
const normalized = base.replace(/^\/+|\/+$/g, '');
|
|
46
|
+
if (!normalized)
|
|
47
|
+
return '/';
|
|
48
|
+
return `/${normalized}/`;
|
|
49
|
+
}
|
|
50
|
+
export function buildScriptDevCommand(root, pkg, options, scriptArgs) {
|
|
51
|
+
const scripts = pkg.scripts ?? {};
|
|
52
|
+
const script = options.script ?? (scripts['docs:dev'] ? 'docs:dev' : 'dev');
|
|
53
|
+
if (typeof scripts[script] !== 'string') {
|
|
54
|
+
throw new Error(`${join(root, 'package.json')} does not define script "${script}"`);
|
|
55
|
+
}
|
|
56
|
+
const packageManager = packageManagerFor(root);
|
|
57
|
+
return {
|
|
58
|
+
command: packageManager,
|
|
59
|
+
args: runArgsFor(packageManager, script, scriptArgs),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function buildVitePressDevCommand(root, _options, scriptArgs, toolRoot = root) {
|
|
63
|
+
const siteRoot = toolRoot === root ? '.' : root;
|
|
64
|
+
const vitepress = localBin(toolRoot, 'vitepress');
|
|
65
|
+
if (vitepress) {
|
|
66
|
+
const args = ['dev', siteRoot];
|
|
67
|
+
args.push(...scriptArgs);
|
|
68
|
+
return { command: vitepress, args, cwd: toolRoot };
|
|
69
|
+
}
|
|
70
|
+
const packageManager = packageManagerFor(toolRoot);
|
|
71
|
+
if (packageManager === 'npm') {
|
|
72
|
+
const args = ['exec', '--', 'vitepress', 'dev', siteRoot];
|
|
73
|
+
args.push(...scriptArgs);
|
|
74
|
+
return { command: packageManager, args, cwd: toolRoot };
|
|
75
|
+
}
|
|
76
|
+
const args = ['x', 'vitepress', 'dev', siteRoot];
|
|
77
|
+
args.push(...scriptArgs);
|
|
78
|
+
return { command: packageManager, args, cwd: toolRoot };
|
|
79
|
+
}
|
|
80
|
+
export function buildQuartzDevCommand(root, _options, scriptArgs, toolRoot = root) {
|
|
81
|
+
const scriptsRoot = toolRoot === root ? '.' : root;
|
|
82
|
+
const quartz = localBin(toolRoot, 'quartz');
|
|
83
|
+
if (quartz) {
|
|
84
|
+
const args = ['build', '--serve', '--directory', scriptsRoot];
|
|
85
|
+
args.push(...scriptArgs);
|
|
86
|
+
return { command: quartz, args, cwd: toolRoot };
|
|
87
|
+
}
|
|
88
|
+
const packageManager = packageManagerFor(toolRoot);
|
|
89
|
+
if (packageManager === 'npm') {
|
|
90
|
+
const args = ['exec', '--', 'quartz', 'build', '--serve', '--directory', scriptsRoot];
|
|
91
|
+
args.push(...scriptArgs);
|
|
92
|
+
return { command: packageManager, args, cwd: toolRoot };
|
|
93
|
+
}
|
|
94
|
+
const args = ['x', 'quartz', 'build', '--serve', '--directory', scriptsRoot];
|
|
95
|
+
args.push(...scriptArgs);
|
|
96
|
+
return { command: packageManager, args, cwd: toolRoot };
|
|
97
|
+
}
|
|
98
|
+
function buildVitePressCommand(root, _pkg) {
|
|
99
|
+
const packageManager = packageManagerFor(root);
|
|
100
|
+
const vitepress = localBin(root, 'vitepress');
|
|
101
|
+
if (vitepress)
|
|
102
|
+
return { command: vitepress, args: ['build', '.'] };
|
|
103
|
+
if (packageManager === 'npm')
|
|
104
|
+
return { command: packageManager, args: ['exec', '--', 'vitepress', 'build', '.'] };
|
|
105
|
+
return { command: packageManager, args: ['x', 'vitepress', 'build', '.'] };
|
|
106
|
+
}
|
|
107
|
+
function buildQuartzCommand(root, _pkg) {
|
|
108
|
+
const packageManager = packageManagerFor(root);
|
|
109
|
+
const quartz = localBin(root, 'quartz');
|
|
110
|
+
const directory = quartzConfig(root)?.startsWith('docs/') ? 'docs' : '.';
|
|
111
|
+
if (quartz)
|
|
112
|
+
return { command: quartz, args: ['build', '--directory', directory] };
|
|
113
|
+
if (packageManager === 'npm')
|
|
114
|
+
return { command: packageManager, args: ['exec', '--', 'quartz', 'build', '--directory', directory] };
|
|
115
|
+
return { command: packageManager, args: ['x', 'quartz', 'build', '--directory', directory] };
|
|
116
|
+
}
|
|
117
|
+
function buildScriptCommand(root, pkg) {
|
|
118
|
+
const scripts = pkg.scripts ?? {};
|
|
119
|
+
const packageManager = packageManagerFor(root);
|
|
120
|
+
if (typeof scripts['docs:build'] === 'string') {
|
|
121
|
+
return { command: packageManager, args: runArgsFor(packageManager, 'docs:build', []) };
|
|
122
|
+
}
|
|
123
|
+
if (typeof scripts.build === 'string') {
|
|
124
|
+
return { command: packageManager, args: runArgsFor(packageManager, 'build', []) };
|
|
125
|
+
}
|
|
126
|
+
return buildVitePressCommand(root, pkg);
|
|
127
|
+
}
|
|
128
|
+
export const rendererRegistry = [
|
|
129
|
+
{
|
|
130
|
+
buildCommand: buildVitePressCommand,
|
|
131
|
+
capabilities: rendererCapabilities({
|
|
132
|
+
accessControl: true,
|
|
133
|
+
backlinks: false,
|
|
134
|
+
graph: false,
|
|
135
|
+
llms: true,
|
|
136
|
+
mcp: true,
|
|
137
|
+
search: true,
|
|
138
|
+
staticOutput: true,
|
|
139
|
+
}),
|
|
140
|
+
cleanUrls: true,
|
|
141
|
+
detect: (root, pkg) => Boolean(vitepressConfig(root)) || hasDependency(pkg, 'vitepress'),
|
|
142
|
+
devCommand: buildVitePressDevCommand,
|
|
143
|
+
outputDirectory: vitepressOutputDirectory,
|
|
144
|
+
platform: 'vitepress',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
buildCommand: buildQuartzCommand,
|
|
148
|
+
capabilities: rendererCapabilities({
|
|
149
|
+
accessControl: true,
|
|
150
|
+
backlinks: true,
|
|
151
|
+
graph: true,
|
|
152
|
+
llms: false,
|
|
153
|
+
mcp: true,
|
|
154
|
+
search: true,
|
|
155
|
+
staticOutput: true,
|
|
156
|
+
}),
|
|
157
|
+
cleanUrls: false,
|
|
158
|
+
detect: (root, pkg) => Boolean(quartzConfig(root)) || hasDependency(pkg, '@jackyzha0/quartz'),
|
|
159
|
+
devCommand: buildQuartzDevCommand,
|
|
160
|
+
outputDirectory: quartzOutputDirectory,
|
|
161
|
+
platform: 'quartz',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
buildCommand: buildScriptCommand,
|
|
165
|
+
capabilities: rendererCapabilities({
|
|
166
|
+
accessControl: true,
|
|
167
|
+
backlinks: false,
|
|
168
|
+
graph: false,
|
|
169
|
+
llms: false,
|
|
170
|
+
mcp: true,
|
|
171
|
+
search: false,
|
|
172
|
+
staticOutput: true,
|
|
173
|
+
}),
|
|
174
|
+
cleanUrls: false,
|
|
175
|
+
detect: () => true,
|
|
176
|
+
devCommand: () => {
|
|
177
|
+
throw new Error('script dev commands require package.json and must use buildScriptDevCommand');
|
|
178
|
+
},
|
|
179
|
+
outputDirectory: () => 'dist',
|
|
180
|
+
platform: 'script',
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
export function rendererFor(root, pkg) {
|
|
184
|
+
return rendererRegistry.find((renderer) => renderer.detect(root, pkg)) ?? rendererRegistry.at(-1);
|
|
185
|
+
}
|
|
186
|
+
export function detectedPlatform(root, pkg) {
|
|
187
|
+
return rendererFor(root, pkg).platform;
|
|
188
|
+
}
|
|
189
|
+
export function rendererDeploymentDescriptor(root, pkg) {
|
|
190
|
+
const renderer = pkg
|
|
191
|
+
? rendererFor(root, pkg)
|
|
192
|
+
: rendererRegistry.find((entry) => entry.platform !== 'script' && entry.detect(root, {})) ?? rendererRegistry.at(-1);
|
|
193
|
+
return {
|
|
194
|
+
capabilities: renderer.capabilities,
|
|
195
|
+
cleanUrls: renderer.cleanUrls,
|
|
196
|
+
outputDirectory: renderer.outputDirectory(root),
|
|
197
|
+
platform: renderer.platform,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
export function siteOutputDirectory(root, pkg) {
|
|
201
|
+
return rendererDeploymentDescriptor(root, pkg).outputDirectory;
|
|
202
|
+
}
|
|
203
|
+
export function rendererDevCommand(root, pkg, platform, options, scriptArgs, toolRoot = root) {
|
|
204
|
+
if (platform === 'script')
|
|
205
|
+
return buildScriptDevCommand(root, pkg, options, scriptArgs);
|
|
206
|
+
return rendererRegistry.find((renderer) => renderer.platform === platform)?.devCommand(root, options, scriptArgs, toolRoot)
|
|
207
|
+
?? buildScriptDevCommand(root, pkg, options, scriptArgs);
|
|
208
|
+
}
|
|
209
|
+
export function rendererBuildCommand(root, pkg) {
|
|
210
|
+
const scripts = pkg.scripts ?? {};
|
|
211
|
+
const packageManager = packageManagerFor(root);
|
|
212
|
+
if (typeof scripts['docs:build'] === 'string') {
|
|
213
|
+
return { command: packageManager, args: runArgsFor(packageManager, 'docs:build', []) };
|
|
214
|
+
}
|
|
215
|
+
if (typeof scripts.build === 'string') {
|
|
216
|
+
return { command: packageManager, args: runArgsFor(packageManager, 'build', []) };
|
|
217
|
+
}
|
|
218
|
+
return rendererFor(root, pkg).buildCommand(root, pkg);
|
|
219
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { execFile, execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, statSync } from 'node:fs';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { dirname, extname, relative, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { readFolder } from '@raggle-ai/barkdown/node';
|
|
8
|
+
const mime = {
|
|
9
|
+
'.css': 'text/css; charset=utf-8',
|
|
10
|
+
'.html': 'text/html; charset=utf-8',
|
|
11
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
12
|
+
'.json': 'application/json; charset=utf-8',
|
|
13
|
+
'.svg': 'image/svg+xml',
|
|
14
|
+
'.woff': 'font/woff',
|
|
15
|
+
'.woff2': 'font/woff2',
|
|
16
|
+
};
|
|
17
|
+
export function splitViewerArgs(input) {
|
|
18
|
+
let hasHost = false;
|
|
19
|
+
let hasRoot = false;
|
|
20
|
+
const options = {
|
|
21
|
+
host: '127.0.0.1',
|
|
22
|
+
lan: false,
|
|
23
|
+
root: process.cwd(),
|
|
24
|
+
port: '5173',
|
|
25
|
+
tailscale: false,
|
|
26
|
+
};
|
|
27
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
28
|
+
const arg = input[index];
|
|
29
|
+
if (arg === '--port') {
|
|
30
|
+
options.port = input[index + 1] ?? '';
|
|
31
|
+
index += 1;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg.startsWith('--port=')) {
|
|
35
|
+
options.port = arg.slice('--port='.length);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (arg === '--host') {
|
|
39
|
+
options.host = input[index + 1] ?? '';
|
|
40
|
+
hasHost = true;
|
|
41
|
+
index += 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (arg.startsWith('--host=')) {
|
|
45
|
+
options.host = arg.slice('--host='.length);
|
|
46
|
+
hasHost = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (arg === '--lan') {
|
|
50
|
+
options.lan = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (arg === '--tailscale') {
|
|
54
|
+
options.tailscale = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (arg.startsWith('-'))
|
|
58
|
+
throw new Error(`Unknown viewer option: ${arg}`);
|
|
59
|
+
if (hasRoot)
|
|
60
|
+
throw new Error('kennel accepts one folder');
|
|
61
|
+
options.root = resolve(arg);
|
|
62
|
+
hasRoot = true;
|
|
63
|
+
}
|
|
64
|
+
if (!options.port || !/^\d+$/.test(options.port)) {
|
|
65
|
+
throw new Error('kennel requires a numeric port');
|
|
66
|
+
}
|
|
67
|
+
if (!options.host)
|
|
68
|
+
throw new Error('kennel requires a host');
|
|
69
|
+
if ((options.lan || options.tailscale) && hasHost) {
|
|
70
|
+
throw new Error('kennel does not accept --host with --lan or --tailscale');
|
|
71
|
+
}
|
|
72
|
+
if (options.lan && options.tailscale) {
|
|
73
|
+
throw new Error('kennel accepts one remote access mode');
|
|
74
|
+
}
|
|
75
|
+
if (options.lan)
|
|
76
|
+
options.host = lanHost();
|
|
77
|
+
if (!existsSync(options.root) || !statSync(options.root).isDirectory()) {
|
|
78
|
+
throw new Error(`Folder not found: ${options.root}`);
|
|
79
|
+
}
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
function lanHost() {
|
|
83
|
+
const route = execFileSync('/sbin/route', ['-n', 'get', 'default'], { encoding: 'utf8' });
|
|
84
|
+
const device = route.match(/interface:\s+(\S+)/)?.[1];
|
|
85
|
+
if (!device)
|
|
86
|
+
throw new Error('kennel could not find the local network interface');
|
|
87
|
+
const host = execFileSync('/usr/sbin/ipconfig', ['getifaddr', device], {
|
|
88
|
+
encoding: 'utf8',
|
|
89
|
+
}).trim();
|
|
90
|
+
if (!host)
|
|
91
|
+
throw new Error(`kennel could not find an address for ${device}`);
|
|
92
|
+
return host;
|
|
93
|
+
}
|
|
94
|
+
function tailscaleHost() {
|
|
95
|
+
const status = JSON.parse(execFileSync('tailscale', ['status', '--json'], { encoding: 'utf8' }));
|
|
96
|
+
const host = typeof status.Self?.DNSName === 'string' ? status.Self.DNSName.replace(/\.$/, '') : '';
|
|
97
|
+
if (host)
|
|
98
|
+
return host;
|
|
99
|
+
throw new Error('kennel could not find the Tailscale DNS name');
|
|
100
|
+
}
|
|
101
|
+
export async function viewer(input) {
|
|
102
|
+
const options = splitViewerArgs(input);
|
|
103
|
+
const web = resolve(dirname(fileURLToPath(import.meta.url)), '../../web');
|
|
104
|
+
if (!existsSync(resolve(web, 'index.html'))) {
|
|
105
|
+
throw new Error(`Kennel web app not found. Run the CLI build first.`);
|
|
106
|
+
}
|
|
107
|
+
const server = createServer(async (request, response) => {
|
|
108
|
+
try {
|
|
109
|
+
const url = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
110
|
+
if (url.pathname === '/api/health') {
|
|
111
|
+
response.writeHead(200, {
|
|
112
|
+
'Cache-Control': 'no-store',
|
|
113
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
114
|
+
});
|
|
115
|
+
response.end(JSON.stringify({ root: options.root }));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (url.pathname === '/api/documents') {
|
|
119
|
+
const root = resolve(url.searchParams.get('path') ?? options.root);
|
|
120
|
+
const body = JSON.stringify(await readFolder(root));
|
|
121
|
+
response.writeHead(200, {
|
|
122
|
+
'Cache-Control': 'no-store',
|
|
123
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
124
|
+
});
|
|
125
|
+
response.end(body);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (url.pathname === '/api/open-folder' && request.method === 'POST') {
|
|
129
|
+
const root = resolve(url.searchParams.get('path') ?? options.root);
|
|
130
|
+
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
131
|
+
response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
132
|
+
response.end(`Folder not found: ${root}`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
execFile('/usr/bin/open', [root], (error) => {
|
|
136
|
+
if (error) {
|
|
137
|
+
response.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
138
|
+
response.end('Could not open folder');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
response.writeHead(204);
|
|
142
|
+
response.end();
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const requested = url.pathname === '/' ? 'index.html' : decodeURIComponent(url.pathname.slice(1));
|
|
147
|
+
const path = resolve(web, requested);
|
|
148
|
+
const escaped = relative(web, path).startsWith('..');
|
|
149
|
+
if (escaped) {
|
|
150
|
+
response.writeHead(404);
|
|
151
|
+
response.end('Not found');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const appRoute = !extname(path);
|
|
155
|
+
const file = appRoute ? resolve(web, 'index.html') : path;
|
|
156
|
+
const body = await readFile(file);
|
|
157
|
+
response.writeHead(200, {
|
|
158
|
+
'Cache-Control': appRoute || file.endsWith('index.html') ? 'no-store' : 'public, max-age=3600',
|
|
159
|
+
'Content-Type': mime[extname(file)] ?? 'application/octet-stream',
|
|
160
|
+
});
|
|
161
|
+
response.end(body);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (response.headersSent) {
|
|
165
|
+
response.destroy(error instanceof Error ? error : undefined);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
response.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
169
|
+
response.end(error instanceof Error ? error.message : 'Not found');
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
await new Promise((done, fail) => {
|
|
173
|
+
server.once('error', fail);
|
|
174
|
+
server.listen(Number(options.port), options.host, done);
|
|
175
|
+
});
|
|
176
|
+
console.log(`Reading documents from ${options.root}`);
|
|
177
|
+
if (options.tailscale) {
|
|
178
|
+
execFileSync('tailscale', ['serve', '--bg', '--yes', options.port]);
|
|
179
|
+
console.log(`Kennel is running at https://${tailscaleHost()}`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log(`Kennel is running at http://${options.host}:${options.port}`);
|
|
183
|
+
}
|
|
184
|
+
console.log('Press Ctrl+C to stop.');
|
|
185
|
+
const close = () => server.close();
|
|
186
|
+
process.once('SIGINT', close);
|
|
187
|
+
process.once('SIGTERM', close);
|
|
188
|
+
await new Promise((done) => server.once('close', done));
|
|
189
|
+
if (options.tailscale) {
|
|
190
|
+
execFileSync('tailscale', ['serve', '--https=443', 'off']);
|
|
191
|
+
}
|
|
192
|
+
}
|
package/dist/site/vitepress.d.ts
CHANGED
|
@@ -1,21 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export type PackageJson = {
|
|
3
|
-
name?: string;
|
|
4
|
-
scripts?: Record<string, string>;
|
|
5
|
-
dependencies?: Record<string, unknown>;
|
|
6
|
-
devDependencies?: Record<string, unknown>;
|
|
7
|
-
peerDependencies?: Record<string, unknown>;
|
|
8
|
-
optionalDependencies?: Record<string, unknown>;
|
|
9
|
-
};
|
|
10
|
-
export declare function vitepressConfig(root: string): string | null;
|
|
11
|
-
export declare function vitepressOutputDirectory(root: string): string;
|
|
12
|
-
export declare function detectedPlatform(root: string, pkg: PackageJson): 'vitepress' | 'script';
|
|
13
|
-
export declare function displayBasePath(config: {
|
|
14
|
-
base?: unknown;
|
|
15
|
-
}): string;
|
|
16
|
-
export declare function buildScriptDevCommand(root: string, pkg: PackageJson, options: {
|
|
17
|
-
script: string | null;
|
|
18
|
-
}, scriptArgs: string[]): ChildCommand;
|
|
19
|
-
export declare function buildVitePressDevCommand(root: string, _options: {
|
|
20
|
-
script: string | null;
|
|
21
|
-
}, scriptArgs: string[], toolRoot?: string): ChildCommand;
|
|
1
|
+
export * from './renderers.js';
|
package/dist/site/vitepress.js
CHANGED
|
@@ -1,66 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { hasDependency, localBin, packageManagerFor, runArgsFor } from '../shared/package-manager.js';
|
|
4
|
-
export function vitepressConfig(root) {
|
|
5
|
-
const configNames = [
|
|
6
|
-
'.vitepress/config.mts',
|
|
7
|
-
'.vitepress/config.ts',
|
|
8
|
-
'.vitepress/config.mjs',
|
|
9
|
-
'.vitepress/config.js',
|
|
10
|
-
'docs/.vitepress/config.mts',
|
|
11
|
-
'docs/.vitepress/config.ts',
|
|
12
|
-
'docs/.vitepress/config.mjs',
|
|
13
|
-
'docs/.vitepress/config.js',
|
|
14
|
-
];
|
|
15
|
-
return configNames.find((configName) => existsSync(join(root, configName))) ?? null;
|
|
16
|
-
}
|
|
17
|
-
export function vitepressOutputDirectory(root) {
|
|
18
|
-
const configPath = vitepressConfig(root);
|
|
19
|
-
if (configPath?.startsWith('docs/'))
|
|
20
|
-
return 'docs/.vitepress/dist';
|
|
21
|
-
return '.vitepress/dist';
|
|
22
|
-
}
|
|
23
|
-
export function detectedPlatform(root, pkg) {
|
|
24
|
-
if (vitepressConfig(root) || hasDependency(pkg, 'vitepress'))
|
|
25
|
-
return 'vitepress';
|
|
26
|
-
return 'script';
|
|
27
|
-
}
|
|
28
|
-
export function displayBasePath(config) {
|
|
29
|
-
if (typeof config.base !== 'string' || config.base.trim() === '')
|
|
30
|
-
return '/';
|
|
31
|
-
const base = config.base.trim();
|
|
32
|
-
const normalized = base.replace(/^\/+|\/+$/g, '');
|
|
33
|
-
if (!normalized)
|
|
34
|
-
return '/';
|
|
35
|
-
return `/${normalized}/`;
|
|
36
|
-
}
|
|
37
|
-
export function buildScriptDevCommand(root, pkg, options, scriptArgs) {
|
|
38
|
-
const scripts = pkg.scripts ?? {};
|
|
39
|
-
const script = options.script ?? (scripts['docs:dev'] ? 'docs:dev' : 'dev');
|
|
40
|
-
if (typeof scripts[script] !== 'string') {
|
|
41
|
-
throw new Error(`${join(root, 'package.json')} does not define script "${script}"`);
|
|
42
|
-
}
|
|
43
|
-
const packageManager = packageManagerFor(root);
|
|
44
|
-
return {
|
|
45
|
-
command: packageManager,
|
|
46
|
-
args: runArgsFor(packageManager, script, scriptArgs),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
export function buildVitePressDevCommand(root, _options, scriptArgs, toolRoot = root) {
|
|
50
|
-
const siteRoot = toolRoot === root ? '.' : root;
|
|
51
|
-
const vitepress = localBin(toolRoot, 'vitepress');
|
|
52
|
-
if (vitepress) {
|
|
53
|
-
const args = ['dev', siteRoot];
|
|
54
|
-
args.push(...scriptArgs);
|
|
55
|
-
return { command: vitepress, args, cwd: toolRoot };
|
|
56
|
-
}
|
|
57
|
-
const packageManager = packageManagerFor(toolRoot);
|
|
58
|
-
if (packageManager === 'npm') {
|
|
59
|
-
const args = ['exec', '--', 'vitepress', 'dev', siteRoot];
|
|
60
|
-
args.push(...scriptArgs);
|
|
61
|
-
return { command: packageManager, args, cwd: toolRoot };
|
|
62
|
-
}
|
|
63
|
-
const args = ['x', 'vitepress', 'dev', siteRoot];
|
|
64
|
-
args.push(...scriptArgs);
|
|
65
|
-
return { command: packageManager, args, cwd: toolRoot };
|
|
66
|
-
}
|
|
1
|
+
export * from './renderers.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sourceCommand(input: string[]): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { collectMcpData } from '@raggle-ai/kennel-mcp/data';
|
|
4
|
+
import { mcpDataConfig } from '../mcp/config.js';
|
|
5
|
+
import { readJson } from '../shared/json.js';
|
|
6
|
+
import { projectConfigPath } from '../shared/project-config.js';
|
|
7
|
+
export function sourceCommand(input) {
|
|
8
|
+
const [commandValue, ...args] = input;
|
|
9
|
+
if (!isSourceCommand(commandValue))
|
|
10
|
+
throw new Error(`Unknown source command: ${commandValue ?? ''}`);
|
|
11
|
+
const options = parseSourceArgs(commandValue, args);
|
|
12
|
+
const configPath = projectConfigPath(options.targetPath, options.configFile);
|
|
13
|
+
const root = dirname(configPath);
|
|
14
|
+
const config = readJson(configPath);
|
|
15
|
+
const data = collectMcpData(root, mcpDataConfig(config));
|
|
16
|
+
if (commandValue === 'list')
|
|
17
|
+
return print(data.sources, options.json);
|
|
18
|
+
if (commandValue === 'describe') {
|
|
19
|
+
const source = sourceById(data.sources, options.id);
|
|
20
|
+
return print({ source, records: data.records.filter((record) => record.sourceId === source.id) }, options.json);
|
|
21
|
+
}
|
|
22
|
+
if (commandValue === 'query') {
|
|
23
|
+
sourceById(data.sources, options.id);
|
|
24
|
+
return print(data.records.filter((record) => record.sourceId === options.id).slice(0, options.limit), options.json);
|
|
25
|
+
}
|
|
26
|
+
if (commandValue === 'doctor') {
|
|
27
|
+
const sources = data.sources.map((source) => {
|
|
28
|
+
const actualRecordCount = data.records.filter((record) => record.sourceId === source.id).length;
|
|
29
|
+
return {
|
|
30
|
+
...source,
|
|
31
|
+
actualRecordCount,
|
|
32
|
+
ok: source.healthy && (source.recordCount === undefined || source.recordCount === actualRecordCount),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
return print({ ok: sources.every((source) => source.ok), sources }, options.json);
|
|
36
|
+
}
|
|
37
|
+
const target = join(root, '.kennel', 'generated', 'source-snapshot.json');
|
|
38
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
39
|
+
writeFileSync(target, `${JSON.stringify({ records: data.records, sources: data.sources }, null, 2)}\n`);
|
|
40
|
+
console.log(options.json ? JSON.stringify({ path: target }) : target);
|
|
41
|
+
}
|
|
42
|
+
function parseSourceArgs(command, input) {
|
|
43
|
+
const positional = [];
|
|
44
|
+
let configFile = 'raggle.json';
|
|
45
|
+
let json = false;
|
|
46
|
+
let limit = 50;
|
|
47
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
48
|
+
const arg = input[index] ?? '';
|
|
49
|
+
if (arg === '--config') {
|
|
50
|
+
configFile = input[index + 1] ?? '';
|
|
51
|
+
index += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (arg === '--json') {
|
|
55
|
+
json = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (arg === '--limit') {
|
|
59
|
+
limit = Math.max(1, Number(input[index + 1] ?? 50));
|
|
60
|
+
index += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (arg.startsWith('-'))
|
|
64
|
+
throw new Error(`Unknown source option: ${arg}`);
|
|
65
|
+
positional.push(arg);
|
|
66
|
+
}
|
|
67
|
+
const needsId = command === 'describe' || command === 'query';
|
|
68
|
+
const id = needsId ? positional.shift() ?? '' : '';
|
|
69
|
+
if (needsId && !id)
|
|
70
|
+
throw new Error(`source ${command} requires a source ID`);
|
|
71
|
+
return { configFile, id, json, limit, targetPath: positional[0] ?? '.' };
|
|
72
|
+
}
|
|
73
|
+
function sourceById(sources, id) {
|
|
74
|
+
const source = sources.find((entry) => entry.id === id);
|
|
75
|
+
if (!source)
|
|
76
|
+
throw new Error(`Knowledge source not found: ${id}`);
|
|
77
|
+
return source;
|
|
78
|
+
}
|
|
79
|
+
function print(value, json) {
|
|
80
|
+
if (json) {
|
|
81
|
+
console.log(JSON.stringify(value));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(JSON.stringify(value, null, 2));
|
|
85
|
+
}
|
|
86
|
+
function isSourceCommand(value) {
|
|
87
|
+
return value === 'describe' || value === 'doctor' || value === 'list' || value === 'query' || value === 'snapshot';
|
|
88
|
+
}
|