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.
Files changed (168) hide show
  1. package/README.md +36 -87
  2. package/bin/kennel-native.mjs +5 -0
  3. package/dist/cli/dispatch.js +25 -16
  4. package/dist/cli/usage.js +9 -3
  5. package/dist/deploy/cloudflare.js +10 -1
  6. package/dist/deploy/smoke-tests.d.ts +1 -0
  7. package/dist/deploy/smoke-tests.js +21 -0
  8. package/dist/deploy/vercel.d.ts +3 -0
  9. package/dist/deploy/vercel.js +67 -0
  10. package/dist/mcp/config.js +14 -0
  11. package/dist/mcp/function-source.js +3 -1
  12. package/dist/mcp/generate.js +3 -3
  13. package/dist/mcp/install-page.d.ts +12 -1
  14. package/dist/mcp/install-page.js +3 -358
  15. package/dist/notifications/email-worker.js +1 -1
  16. package/dist/publish/cloudflare-init.js +5 -2
  17. package/dist/publish/config.d.ts +1 -1
  18. package/dist/publish/config.js +3 -15
  19. package/dist/publish/vercel-init.d.ts +9 -0
  20. package/dist/publish/vercel-init.js +52 -0
  21. package/dist/site/dev.js +2 -4
  22. package/dist/site/extension.d.ts +8 -0
  23. package/dist/site/extension.js +114 -0
  24. package/dist/site/renderers.d.ts +61 -0
  25. package/dist/site/renderers.js +219 -0
  26. package/dist/site/viewer.d.ts +9 -0
  27. package/dist/site/viewer.js +192 -0
  28. package/dist/site/vitepress.d.ts +1 -21
  29. package/dist/site/vitepress.js +1 -66
  30. package/dist/sources/commands.d.ts +1 -0
  31. package/dist/sources/commands.js +88 -0
  32. package/package.json +8 -8
  33. package/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  34. package/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  35. package/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  36. package/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  37. package/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  38. package/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  39. package/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  40. package/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  41. package/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  42. package/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  43. package/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  44. package/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  45. package/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  46. package/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  47. package/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  48. package/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  49. package/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  50. package/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  51. package/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  52. package/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  53. package/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  54. package/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  55. package/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  56. package/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  57. package/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  58. package/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  59. package/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  60. package/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  61. package/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  62. package/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  63. package/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  64. package/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  65. package/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  66. package/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  67. package/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  68. package/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  69. package/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  70. package/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  71. package/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  72. package/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  73. package/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  74. package/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  75. package/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  76. package/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  77. package/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  78. package/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  79. package/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  80. package/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  81. package/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  82. package/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  83. package/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  84. package/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  85. package/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  86. package/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  87. package/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  88. package/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  89. package/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  90. package/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  91. package/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  92. package/web/assets/abnfDiagram-VRR7QNED-Dizo1F0V.js +1 -0
  93. package/web/assets/arc-CraBhW5A.js +1 -0
  94. package/web/assets/architectureDiagram-ZJ3FMSHR-DrcQCjV4.js +36 -0
  95. package/web/assets/blockDiagram-677ZJIJ3-iGvS8HCJ.js +132 -0
  96. package/web/assets/c4Diagram-LMCZKHZV-B9ueRzOf.js +10 -0
  97. package/web/assets/channel-DjVeob-k.js +1 -0
  98. package/web/assets/chunk-2Q5K7J3B-Dg02y5Go.js +1 -0
  99. package/web/assets/chunk-32BRIVSS-DHUNfpw6.js +1 -0
  100. package/web/assets/chunk-5VM5RSS4-B6XJu9sr.js +15 -0
  101. package/web/assets/chunk-EX3LRPZG-DywY4I3l.js +231 -0
  102. package/web/assets/chunk-JWPE2WC7-C9-XkXDg.js +1 -0
  103. package/web/assets/chunk-MOJQB5TN-CXQXOtY9.js +88 -0
  104. package/web/assets/chunk-RYQCIY6F-DJwv9114.js +1 -0
  105. package/web/assets/chunk-V7JOEXUC-_b44nhwN.js +206 -0
  106. package/web/assets/chunk-VR4S4FIN-B_bGSDri.js +1 -0
  107. package/web/assets/chunk-XXDRQBXY-Bf-JPvQi.js +1 -0
  108. package/web/assets/classDiagram-OUVF2IWQ-4Od2_pq4.js +1 -0
  109. package/web/assets/classDiagram-v2-EOCWNBFH-4Od2_pq4.js +1 -0
  110. package/web/assets/cose-bilkent-JH36ORCC-Bcssb1jB.js +1 -0
  111. package/web/assets/cynefin-VYW2F7L2-t9uGqnJ4.js +166 -0
  112. package/web/assets/cynefinDiagram-TSTJHNR4-D0RvbNfG.js +62 -0
  113. package/web/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
  114. package/web/assets/dagre-VKFMJZFB-DmmlPxN_.js +4 -0
  115. package/web/assets/defaultLocale-C4B-KCzX.js +1 -0
  116. package/web/assets/diagram-FQU43EPY-DtDak7gR.js +3 -0
  117. package/web/assets/diagram-G47NLZAW-Cn07lZ4L.js +24 -0
  118. package/web/assets/diagram-NH7WQ7WH-BTD9-weZ.js +24 -0
  119. package/web/assets/diagram-OA4YK3LP-fjOdFedY.js +30 -0
  120. package/web/assets/diagram-WEI45ONY-DSBYO7Nc.js +41 -0
  121. package/web/assets/ebnfDiagram-CCIWWBDH-B8km6QWT.js +1 -0
  122. package/web/assets/erDiagram-Q63AITRT-CHs2HT-U.js +85 -0
  123. package/web/assets/flowDiagram-23GEKE2U-CxjQgYus.js +156 -0
  124. package/web/assets/ganttDiagram-NO4QXBWP-CHkGy5_G.js +292 -0
  125. package/web/assets/gitGraphDiagram-IHSO6WYX-BdtSK3Q5.js +106 -0
  126. package/web/assets/graph-BvS0Xx32.js +1 -0
  127. package/web/assets/index-BiSkqJri.css +1 -0
  128. package/web/assets/index-sG3Cxc28.js +697 -0
  129. package/web/assets/infoDiagram-FWYZ7A6U-B0feeRLu.js +2 -0
  130. package/web/assets/init-Gi6I4Gst.js +1 -0
  131. package/web/assets/ishikawaDiagram-FXEZZL3T-DGCSsJ4r.js +70 -0
  132. package/web/assets/journeyDiagram-5HDEW3XC-D9xtvo9Z.js +139 -0
  133. package/web/assets/kanban-definition-HUTT4EX6-BqVdsgmq.js +89 -0
  134. package/web/assets/layout-D4ui-CWC.js +1 -0
  135. package/web/assets/linear-BopdAHdJ.js +1 -0
  136. package/web/assets/map-7eTsl7HP.js +1 -0
  137. package/web/assets/mermaid.core-DWAi8MqL.js +308 -0
  138. package/web/assets/mindmap-definition-LN4V7U3C-CsV_gb2j.js +96 -0
  139. package/web/assets/ordinal-Cboi1Yqb.js +1 -0
  140. package/web/assets/pegDiagram-2B236MQR-BmDNuFL1.js +1 -0
  141. package/web/assets/pieDiagram-ENE6RG2P-CBPZlXZP.js +39 -0
  142. package/web/assets/quadrantDiagram-ABIIQ3AL-CbUemD3z.js +7 -0
  143. package/web/assets/railroadDiagram-RFXS5EU6-Cyp4W7Hm.js +1 -0
  144. package/web/assets/requirementDiagram-TGXJPOKE-Dz35_qtT.js +84 -0
  145. package/web/assets/sankeyDiagram-HTMAVEWB-biUDOCWc.js +40 -0
  146. package/web/assets/sequenceDiagram-DBY2YBRQ-Bk4X1-E4.js +162 -0
  147. package/web/assets/sizeCapture-X5ZJPWSS-CDPSVeoQ.js +1 -0
  148. package/web/assets/stateDiagram-2N3HPSRC-Cx_G_F7H.js +1 -0
  149. package/web/assets/stateDiagram-v2-6OUMAXLB-DG9cZ_-X.js +1 -0
  150. package/web/assets/swimlanes-5IMT3BWC-KfhGp3gJ.js +2 -0
  151. package/web/assets/swimlanesDiagram-G3AALYLV-Bj6WXCUy.js +8 -0
  152. package/web/assets/timeline-definition-FHXFAJF6-Dp6wlW_7.js +120 -0
  153. package/web/assets/vennDiagram-L72KCM5P-CjdOdrf1.js +34 -0
  154. package/web/assets/wardleyDiagram-EHGQE667-DYl5GkQs.js +78 -0
  155. package/web/assets/xychartDiagram-FW5EYKEG-rPJ1RPYD.js +7 -0
  156. package/web/index.html +14 -0
  157. package/dist/mcp/data.d.ts +0 -14
  158. package/dist/mcp/data.js +0 -120
  159. package/dist/schema/doctor.d.ts +0 -1
  160. package/dist/schema/doctor.js +0 -69
  161. package/dist/schema/documents.d.ts +0 -18
  162. package/dist/schema/documents.js +0 -127
  163. package/dist/schema/pointers.d.ts +0 -5
  164. package/dist/schema/pointers.js +0 -48
  165. package/dist/schema/types.d.ts +0 -36
  166. package/dist/schema/types.js +0 -1
  167. package/dist/schema/validation.d.ts +0 -8
  168. package/dist/schema/validation.js +0 -212
package/README.md CHANGED
@@ -1,8 +1,6 @@
1
- # Kennel CLI
1
+ # Kennel Platform CLI
2
2
 
3
- Schema-backed folder ledger validator for structured workspaces.
4
-
5
- Kennel keeps structured folders easy to inspect and validate. It looks upward from the target path for the nearest `schema.json` or `_schema.json`, then validates schema-declared YAML files or Markdown frontmatter documents.
3
+ Publishing, MCP generation, and knowledge-source commands for Kennel workspaces. The compact schema validator has moved to the private [`raggle-ai/kennel-compact`](https://github.com/raggle-ai/kennel-compact) repository.
6
4
 
7
5
  ## Usage
8
6
 
@@ -12,43 +10,23 @@ npm test
12
10
  node bin/kennel.mjs --help
13
11
  ```
14
12
 
15
- Install locally from this checkout:
16
-
17
- ```bash
18
- npm install /Users/andrew/Documents/GitHub/kennel/cli
19
- kennel --help
20
- ```
21
-
22
- Install from npm after publication:
13
+ Install from npm:
23
14
 
24
15
  ```bash
25
16
  npm install --global kennel
26
17
  kennel --help
27
18
  ```
28
19
 
29
- Publish to npm:
20
+ Run platform commands:
30
21
 
31
22
  ```bash
32
- cd /Users/andrew/Documents/GitHub/kennel/cli
33
- npm publish --access public --registry=https://registry.npmjs.org
34
- ```
35
-
36
- Run against folder schemas:
37
-
38
- ```bash
39
- kennel schema clients/reapit
40
- kennel validate clients
41
- kennel validate projects
42
- kennel validate partners
43
- kennel doctor clients/reapit
44
23
  kennel dev /Users/andrewmagu/src/cdp-index --config index.json
24
+ kennel mcp generate . --config index.json
45
25
  kennel publish init cloudflare .
46
26
  kennel deploy .
47
27
  kennel dp .
48
28
  ```
49
29
 
50
- Kennel does not create schemas or new records. Manage `schema.json` or `_schema.json` files and folder templates outside the CLI, then use Kennel to inspect and validate them.
51
-
52
30
  `kennel publish init cloudflare` is the exception for deploy wiring: it scaffolds
53
31
  a Cloudflare Worker email-code gate for an existing docs workspace by writing
54
32
  `publication` in `raggle.json` by default, then writing generated Cloudflare
@@ -67,65 +45,6 @@ Kennel loads `CLOUDFLARE_API_TOKEN` from Doppler only for the publish command.
67
45
  `kennel publish cloudflare` remains available when the provider should be
68
46
  explicit.
69
47
 
70
- ## Folder Shape
71
-
72
- ```text
73
- clients/
74
- schema.json
75
- example-client/
76
- client.yml
77
- people/
78
- notes/
79
- comms/
80
- evidence/
81
- ```
82
-
83
- The nearest parent `schema.json` or `_schema.json` owns the expected shape for folders below it. When both names exist in the same folder, `schema.json` is preferred.
84
-
85
- `kennel validate` discovers files from the nearest schema's `x-kennel.documents` declaration. If a schema omits that declaration, Kennel keeps the compatibility default of `client.yml` and `index.md`.
86
-
87
- Markdown files are validated using YAML frontmatter. YAML files are parsed as YAML.
88
-
89
- ## Folder Requirements
90
-
91
- Schemas can include a Kennel-only extension for folder shape checks:
92
-
93
- ```json
94
- {
95
- "x-kennel": {
96
- "documents": [
97
- "client.yml",
98
- { "path": "index.md", "parser": "markdown-frontmatter" },
99
- { "glob": "*.yml", "parser": "yaml" }
100
- ],
101
- "requiredDirectories": ["people", "notes", "comms", "evidence"],
102
- "requiredFiles": ["README.md"],
103
- "directoryContents": [
104
- {
105
- "path": "people",
106
- "glob": "*.md",
107
- "minFiles": 1,
108
- "whenPointerMinItems": "/contacts"
109
- }
110
- ],
111
- "fileReferences": [
112
- {
113
- "pointer": "/contacts/*/path",
114
- "required": true,
115
- "whenPointerMinItems": "/contacts"
116
- }
117
- ]
118
- }
119
- }
120
- ```
121
-
122
- String document entries match a file name anywhere under the schema root. Object entries can use `path` or `glob` plus an optional parser.
123
-
124
- `requiredDirectories` and `requiredFiles` are checked next to each validated document and by `kennel doctor`.
125
-
126
- `directoryContents` checks that a sibling directory contains enough matching files. Use `whenPointerMinItems` to apply the rule only when an array in the document is non-empty, or `whenPointerIn` with `{ "pointer": "/client/status", "values": ["active-client"] }` to apply it for selected field values.
127
-
128
- `fileReferences` checks that document path fields point to existing files under the same folder. It supports JSON pointers with `*` wildcards, such as `/contacts/*/path`.
129
48
 
130
49
  ## MCP Analytics
131
50
 
@@ -158,7 +77,7 @@ The Raggle provider uses the `raggle-otel` package and defaults to `RAGGLE_OTEL_
158
77
 
159
78
  ## MCP Repo Integrations
160
79
 
161
- `kennel mcp generate` also emits an MCP server runtime (`@raggle-ai/kennel-mcp`) with core tools for searching index docs and tracked repositories. The runtime additionally exposes repo integration tools that let an agent run basic GitHub or GitLab commands against a repository linked from the index registry (`repos/index.yaml`):
80
+ `kennel mcp generate` also emits an MCP server runtime from `@raggle-ai/kennel/mcp` with core tools for searching index docs and tracked repositories. The runtime additionally exposes repo integration tools that let an agent run basic GitHub or GitLab commands against a repository linked from the index registry (`repos/index.yaml`):
162
81
 
163
82
  - `submit_repo_feature_request` — open a feature request issue on a tracked repo, submitted on behalf of a user.
164
83
  - `list_repo_issues` — list recent issues on a tracked repo.
@@ -190,6 +109,36 @@ To let people supply their own GitHub or GitLab setup (custom token env, self-ho
190
109
 
191
110
  `allowedRepos` is an optional allowlist of registry repo names; when omitted, every registry repo is available. Set `enabled: false` to disable the repo integration tools while keeping the rest of the MCP server active.
192
111
 
112
+ ## MCP Tool Catalog Profiles
113
+
114
+ Large MCP servers can expose a small eager tool surface while keeping specialised tools discoverable. Add `mcp.toolCatalog` to select a default profile. Tools not selected as eager are deferred automatically; `search_index_tools`, `describe_index_tool`, and `call_index_tool` let an agent discover a deferred schema and invoke it through the catalog dispatcher.
115
+
116
+ ```json
117
+ {
118
+ "mcp": {
119
+ "toolCatalog": {
120
+ "defaultProfile": "minimal",
121
+ "allowClientProfile": true,
122
+ "profiles": {
123
+ "minimal": {
124
+ "eagerTools": [
125
+ "search_index",
126
+ "search_index_tools",
127
+ "describe_index_tool",
128
+ "call_index_tool"
129
+ ]
130
+ },
131
+ "complete": {
132
+ "eagerNamespaces": ["*"]
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ When `allowClientProfile` is enabled, an MCP client or gateway can select a configured view with the `x-kennel-mcp-profile` header. Profile selection only narrows catalog exposure: role checks and mutating-tool policy still run for direct and dispatched calls. If `toolCatalog` is omitted, the runtime retains the legacy behavior and lists every available tool.
141
+
193
142
  Registry entries that should support issue creation need a `url` (and optionally `provider`):
194
143
 
195
144
  ```yaml
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { extensionHost } from '../dist/site/extension.js';
4
+
5
+ extensionHost();
@@ -1,51 +1,60 @@
1
- import { deploy, publish } from '../deploy/cloudflare.js';
2
- import { generateMcp } from '../mcp/generate.js';
3
- import { doctor } from '../schema/doctor.js';
4
- import { schemaFor, validateTarget } from '../schema/validation.js';
5
- import { dev } from '../site/dev.js';
6
- import { publishInitCloudflare } from '../publish/cloudflare-init.js';
7
1
  import { usage } from './usage.js';
8
2
  export async function dispatch(argv) {
9
3
  const [, , command, ...args] = argv;
10
- if (!command || command === '-h' || command === '--help') {
4
+ if (command === '-h' || command === '--help') {
11
5
  usage();
12
6
  return;
13
7
  }
14
- if (command === 'schema') {
15
- schemaFor(args[0] ?? '.');
8
+ if (!command) {
9
+ const { viewer } = await import('../site/viewer.js');
10
+ await viewer([]);
16
11
  return;
17
12
  }
18
- if (command === 'validate') {
19
- const valid = validateTarget(args[0] ?? '.');
20
- process.exit(valid ? 0 : 1);
21
- }
22
- if (command === 'doctor') {
23
- doctor(args[0] ?? '.');
13
+ if (command === 'source') {
14
+ const { sourceCommand } = await import('../sources/commands.js');
15
+ sourceCommand(args);
24
16
  return;
25
17
  }
26
18
  if (command === 'dev') {
19
+ const { dev } = await import('../site/dev.js');
27
20
  dev(args);
28
21
  return;
29
22
  }
23
+ if (command === 'extension' && args[0] === 'install') {
24
+ const { extensionInstall } = await import('../site/extension.js');
25
+ await extensionInstall();
26
+ return;
27
+ }
30
28
  if (command === 'mcp' && args[0] === 'generate') {
29
+ const { generateMcp } = await import('../mcp/generate.js');
31
30
  await generateMcp(args.slice(1));
32
31
  return;
33
32
  }
34
33
  if (command === 'publish' && args[0] === 'init' && args[1] === 'cloudflare') {
34
+ const { publishInitCloudflare } = await import('../publish/cloudflare-init.js');
35
35
  publishInitCloudflare(args.slice(2));
36
36
  return;
37
37
  }
38
+ if (command === 'publish' && args[0] === 'init' && args[1] === 'vercel') {
39
+ const { publishInitVercel } = await import('../publish/vercel-init.js');
40
+ publishInitVercel(args.slice(2));
41
+ return;
42
+ }
38
43
  if (command === 'publish') {
44
+ const { publish } = await import('../deploy/cloudflare.js');
39
45
  publish(args);
40
46
  return;
41
47
  }
42
48
  if (command === 'deploy') {
49
+ const { deploy } = await import('../deploy/cloudflare.js');
43
50
  deploy(args);
44
51
  return;
45
52
  }
46
53
  if (command === 'dp') {
54
+ const { deploy } = await import('../deploy/cloudflare.js');
47
55
  deploy(args);
48
56
  return;
49
57
  }
50
- throw new Error(`Unknown command: ${command}`);
58
+ const { viewer } = await import('../site/viewer.js');
59
+ await viewer([command, ...args]);
51
60
  }
package/dist/cli/usage.js CHANGED
@@ -1,13 +1,19 @@
1
1
  export function usage() {
2
2
  console.log(`Usage:
3
- kennel schema <path>
4
- kennel validate <path>
5
- kennel doctor <path>
3
+ kennel [folder] [--host <host>] [--port <port>] [--lan|--tailscale]
4
+ kennel source list [path] [--config <json-file>] [--json]
5
+ kennel source describe <id> [path] [--config <json-file>] [--json]
6
+ kennel source doctor [path] [--config <json-file>] [--json]
7
+ kennel source query <id> [path] [--config <json-file>] [--limit <n>] [--json]
8
+ kennel source snapshot [path] [--config <json-file>] [--json]
6
9
  kennel dev [path] [--config <json-file>] [--port <port>] [--script <script>] [--dry-run] [-- <args...>]
10
+ kennel extension install
7
11
  kennel mcp generate [path] [--config <json-file>] [--data-only|--function-only]
8
12
  kennel publish init cloudflare [path] [--config <json-file>] [--force] [--hostname <hostname>]
13
+ kennel publish init vercel [path] [--config <json-file>] [--force] [--hostname <hostname>]
9
14
  kennel publish [path] [--config <json-file>] [--dry-run] [--force] [--hostname <hostname>] [--test-email <email>] [--skip-test] [--use-doppler-token]
10
15
  kennel publish cloudflare [path] [--config <json-file>] [--dry-run] [--force] [--hostname <hostname>] [--test-email <email>] [--skip-test] [--use-doppler-token]
16
+ kennel publish vercel [path] [--config <json-file>] [--dry-run] [--force] [--hostname <hostname>] [--skip-test]
11
17
  kennel deploy [path] [--config <json-file>] [legacy alias for publish]
12
18
  kennel dp [path] [--config <json-file>] [legacy alias for publish]
13
19
  `);
@@ -4,13 +4,15 @@ import { existsSync } from 'node:fs';
4
4
  import { relative } from 'node:path';
5
5
  import { readJson } from '../shared/json.js';
6
6
  import { localBin } from '../shared/package-manager.js';
7
+ import { projectConfigPath } from '../shared/project-config.js';
7
8
  import { commandLine } from '../shared/process.js';
8
- import { packageJsonPath, publishBuildCommand, publishTestUrl, publishWranglerArgs, publishWranglerCommand, publicationAccess, } from '../publish/config.js';
9
+ import { packageJsonPath, publishBuildCommand, publishTestUrl, publishWranglerArgs, publishWranglerCommand, publicationAccess, publicationConfig, } from '../publish/config.js';
9
10
  import { publishInitCloudflare } from '../publish/cloudflare-init.js';
10
11
  import { splitPublishArgs } from '../publish/options.js';
11
12
  import { withCloudflareEnvironment } from './environment.js';
12
13
  import { runAccessHttpTest, runHttpTest } from './smoke-tests.js';
13
14
  import { syncTriggerDev } from './trigger-dev.js';
15
+ import { publishVercel } from './vercel.js';
14
16
  export function runPublishCommand(root, child, options) {
15
17
  console.log(commandLine(child.command, child.args));
16
18
  if (options.dryRun)
@@ -84,6 +86,13 @@ export function publish(input) {
84
86
  if (input[0] === 'cloudflare' || input[0] === 'cloudflare-workers') {
85
87
  return publishCloudflare(input.slice(1));
86
88
  }
89
+ if (input[0] === 'vercel')
90
+ return publishVercel(input.slice(1));
91
+ const options = splitPublishArgs(input);
92
+ const config = readJson(projectConfigPath(options.targetPath, options.configFile));
93
+ const provider = publicationConfig(config).provider;
94
+ if (provider === 'vercel')
95
+ return publishVercel(input);
87
96
  return publishCloudflare(input);
88
97
  }
89
98
  export function deploy(input) {
@@ -1,3 +1,4 @@
1
1
  import type { PublishOptions } from '../publish/options.js';
2
2
  export declare function runHttpTest(root: string, hostname: string, testEmail: string, options: PublishOptions): void;
3
+ export declare function runStaticHttpTest(root: string, hostname: string, options: PublishOptions): void;
3
4
  export declare function runAccessHttpTest(root: string, hostname: string, options: PublishOptions): void;
@@ -47,6 +47,27 @@ export function runHttpTest(root, hostname, testEmail, options) {
47
47
  }
48
48
  }
49
49
  }
50
+ export function runStaticHttpTest(root, hostname, options) {
51
+ const baseUrl = publishTestUrl(hostname);
52
+ if (!baseUrl) {
53
+ console.log('skip test: no hostname configured');
54
+ return;
55
+ }
56
+ if (!commandExists('curl')) {
57
+ console.log('skip test: curl is not available');
58
+ return;
59
+ }
60
+ const rootTest = {
61
+ command: 'curl',
62
+ args: ['-fsS', '-H', 'Cache-Control: no-cache', baseUrl],
63
+ };
64
+ console.log(`${commandLine(rootTest.command, rootTest.args)} | returns 2xx`);
65
+ if (!options.dryRun) {
66
+ const result = spawnSync(rootTest.command, rootTest.args, { cwd: root, stdio: 'ignore' });
67
+ if (result.status !== 0)
68
+ throw new Error(`${baseUrl} did not return a successful response`);
69
+ }
70
+ }
50
71
  export function runAccessHttpTest(root, hostname, options) {
51
72
  const baseUrl = publishTestUrl(hostname);
52
73
  if (!baseUrl) {
@@ -0,0 +1,3 @@
1
+ import { type ChildCommand } from '../shared/process.js';
2
+ export declare function publishVercelCommand(root: string, projectName: string): ChildCommand;
3
+ export declare function publishVercel(input: string[]): void;
@@ -0,0 +1,67 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { readJson } from '../shared/json.js';
4
+ import { localBin } from '../shared/package-manager.js';
5
+ import { commandLine } from '../shared/process.js';
6
+ import { packageJsonPath, publicationConfig, publicationDomains, publishBuildCommand, publishTestUrl, } from '../publish/config.js';
7
+ import { splitPublishArgs } from '../publish/options.js';
8
+ import { publishInitVercel } from '../publish/vercel-init.js';
9
+ import { runStaticHttpTest } from './smoke-tests.js';
10
+ function runVercelCommand(root, child, options) {
11
+ console.log(commandLine(child.command, child.args));
12
+ if (options.dryRun)
13
+ return;
14
+ const result = spawnSync(child.command, child.args, {
15
+ cwd: root,
16
+ stdio: 'inherit',
17
+ env: process.env,
18
+ });
19
+ if (result.status !== 0)
20
+ process.exit(result.status ?? 1);
21
+ }
22
+ export function publishVercelCommand(root, projectName) {
23
+ const args = ['deploy', '--prod', '--yes', '--project', projectName];
24
+ const vercel = localBin(root, 'vercel');
25
+ if (vercel)
26
+ return { command: vercel, args };
27
+ return { command: 'npx', args: ['vercel', ...args] };
28
+ }
29
+ function vercelHostname(config, fallback) {
30
+ const publication = publicationConfig(config);
31
+ const domains = publicationDomains(config);
32
+ const hostname = fallback ||
33
+ domains.primary ||
34
+ publication.hostname ||
35
+ publication.domain ||
36
+ publication.url;
37
+ return typeof hostname === 'string' ? hostname : '';
38
+ }
39
+ export function publishVercel(input) {
40
+ const options = splitPublishArgs(input);
41
+ const setup = publishInitVercel([
42
+ '--force',
43
+ '--config',
44
+ options.configFile,
45
+ ...(options.hostname ? ['--hostname', options.hostname] : []),
46
+ options.targetPath,
47
+ ]);
48
+ const { root } = setup;
49
+ const packagePath = packageJsonPath(root);
50
+ if (!existsSync(packagePath))
51
+ throw new Error(`No package.json found next to ${setup.configPath}`);
52
+ const pkg = readJson(packagePath);
53
+ if (options.build)
54
+ runVercelCommand(root, publishBuildCommand(root, pkg), options);
55
+ const publication = publicationConfig(setup.config);
56
+ const projectName = typeof publication.projectName === 'string'
57
+ ? publication.projectName
58
+ : 'kennel-docs';
59
+ if (options.deploy)
60
+ runVercelCommand(root, publishVercelCommand(root, projectName), options);
61
+ const hostname = vercelHostname(setup.config, setup.hostname);
62
+ if (hostname && !options.skipTest)
63
+ runStaticHttpTest(root, hostname, options);
64
+ const url = publishTestUrl(hostname);
65
+ if (url)
66
+ console.log(`url ${url}`);
67
+ }
@@ -152,6 +152,18 @@ function toolPacks(config) {
152
152
  return undefined;
153
153
  return mcp.toolPacks;
154
154
  }
155
+ function toolCatalog(config) {
156
+ const mcp = runtimeMcpConfig(config);
157
+ if (!isObject(mcp.toolCatalog))
158
+ return undefined;
159
+ return mcp.toolCatalog;
160
+ }
161
+ function docs(config) {
162
+ const mcp = runtimeMcpConfig(config);
163
+ if (!isObject(mcp.docs))
164
+ return undefined;
165
+ return mcp.docs;
166
+ }
155
167
  export function serverConfig(config) {
156
168
  const mcp = runtimeMcpConfig(config);
157
169
  const serverName = String(mcp.serverName ?? config.name ?? 'index');
@@ -159,11 +171,13 @@ export function serverConfig(config) {
159
171
  ...(access(config) ? { access: access(config) } : {}),
160
172
  ...(isObject(mcp.analytics) ? { analytics: mcp.analytics } : {}),
161
173
  ...(isObject(mcp.conversationSnapshots) ? { conversationSnapshots: mcp.conversationSnapshots } : {}),
174
+ ...(docs(config) ? { docs: docs(config) } : {}),
162
175
  instructions: typeof mcp.instructions === 'string' && mcp.instructions.trim()
163
176
  ? mcp.instructions.trim()
164
177
  : "Find relevant Index documents and repo entries for the user's topic. Use search_index_docs and search_index_repos for topic lookup, then read index://doc/{slug} or index://repo/{name} when more detail is needed.",
165
178
  name: mcp.runtimeName ?? `${serverName}-mcp`,
166
179
  ...(repoIntegrations(config) ? { repoIntegrations: repoIntegrations(config) } : {}),
180
+ ...(toolCatalog(config) ? { toolCatalog: toolCatalog(config) } : {}),
167
181
  toolAliases: toolAliases(config),
168
182
  ...(toolPacks(config) ? { toolPacks: toolPacks(config) } : {}),
169
183
  version: mcp.version ?? '1.0.0',
@@ -16,7 +16,7 @@ export function mcpFunctionSource(options) {
16
16
  ].join(', ')}];\n`
17
17
  : '';
18
18
  const customToolOption = customToolNames.length || toolModuleNames.length ? ', customTools' : '';
19
- return `import { createMcpHttpHandler } from "@raggle-ai/kennel-mcp";
19
+ return `import { createMcpHttpHandler } from "@raggle-ai/kennel/mcp";
20
20
  import data from "./_generated/mcp-data.json";
21
21
  ${customToolImports ? `${customToolImports}\n` : ''}${toolModuleImports ? `${toolModuleImports}\n` : ''}
22
22
 
@@ -24,8 +24,10 @@ const serverConfig = ${JSON.stringify(options.serverConfig, null, 2)} as {
24
24
  access?: Record<string, unknown>;
25
25
  analytics?: Record<string, unknown>;
26
26
  conversationSnapshots?: Record<string, unknown>;
27
+ docs?: Record<string, unknown>;
27
28
  instructions: string;
28
29
  name: string;
30
+ toolCatalog?: Record<string, unknown>;
29
31
  toolAliases: Record<string, string>;
30
32
  toolPacks?: Record<string, unknown>;
31
33
  version: string;
@@ -22,11 +22,11 @@ export async function generateMcp(input) {
22
22
  };
23
23
  const agentDirectory = agentToolsDirectory(config);
24
24
  const agentTools = agentDirectory ? collectAgentTools(root, agentDirectory) : [];
25
- const { docs, health, repos } = collectMcpData(root, dataConfig);
25
+ const { docs, health, records, repos, sources } = collectMcpData(root, dataConfig);
26
26
  const target = outputPath(root, config);
27
27
  mkdirSync(dirname(target), { recursive: true });
28
- writeMcpData(target, docs, repos, agentTools, health);
29
- console.log(`ok ${relative(root, target)} (${docs.length} docs, ${repos.length} repos, ${agentTools.length} agent tools)`);
28
+ writeMcpData(target, docs, repos, agentTools, health, records, sources);
29
+ console.log(`ok ${relative(root, target)} (${docs.length} docs, ${repos.length} repos, ${agentTools.length} agent tools, ${records.length} records, ${sources.length} sources)`);
30
30
  }
31
31
  if (!options.dataOnly) {
32
32
  const target = functionPath(root, config);
@@ -1 +1,12 @@
1
- export declare function writeInstallPage(root: string, config: any): string | undefined;
1
+ import { installPageLink, installPageMarkdownPath, renderInstallPage } from '@raggle-ai/kennel-knowledge/install-page';
2
+ export { installPageLink, installPageMarkdownPath, renderInstallPage, };
3
+ export type InstallModel = {
4
+ description: string;
5
+ displayName: string;
6
+ installMarkdownUrl?: string;
7
+ protectedUrl?: string;
8
+ publicUrl: string;
9
+ serverName: string;
10
+ tools: string[];
11
+ };
12
+ export declare function writeInstallPage(root: string, config: Record<string, unknown>): string | undefined;