@kitn.ai/ui 0.15.1 → 0.17.0
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 +37 -13
- package/bin/mcp.js +22 -0
- package/dist/bash-InADTalH.js +6 -1
- package/dist/components/artifact.d.ts +58 -0
- package/dist/components/attachments.d.ts +67 -0
- package/dist/components/card-fallback.d.ts +10 -0
- package/dist/components/card-renderer.d.ts +13 -0
- package/dist/components/card.d.ts +28 -0
- package/dist/components/chain-of-thought.d.ts +29 -0
- package/dist/components/chat-container.d.ts +19 -0
- package/dist/components/chat-scope-picker.d.ts +9 -0
- package/dist/components/chat-thread.d.ts +91 -0
- package/dist/components/checkpoint.d.ts +20 -0
- package/dist/components/choice-card.d.ts +78 -0
- package/dist/components/code-block.d.ts +16 -0
- package/dist/components/confirm-card.d.ts +57 -0
- package/dist/components/context.d.ts +65 -0
- package/dist/components/conversation-item.d.ts +8 -0
- package/dist/components/conversation-list.d.ts +11 -0
- package/dist/components/dismissed-stub.d.ts +22 -0
- package/dist/components/embed.d.ts +21 -0
- package/dist/components/empty.d.ts +43 -0
- package/dist/components/feedback-bar.d.ts +45 -0
- package/dist/components/file-tree.d.ts +57 -0
- package/dist/components/file-upload.d.ts +16 -0
- package/dist/components/form-widgets.d.ts +31 -0
- package/dist/components/form.d.ts +103 -0
- package/dist/components/image.d.ts +12 -0
- package/dist/components/link-preview.d.ts +23 -0
- package/dist/components/loader.d.ts +60 -0
- package/dist/components/markdown.d.ts +8 -0
- package/dist/components/message-skills.d.ts +15 -0
- package/dist/components/message.d.ts +111 -0
- package/dist/components/model-switcher.d.ts +8 -0
- package/dist/components/prompt-input.d.ts +38 -0
- package/dist/components/prompt-suggestion.d.ts +13 -0
- package/dist/components/reasoning.d.ts +20 -0
- package/dist/components/response-compare-types.d.ts +66 -0
- package/dist/components/response-compare.d.ts +51 -0
- package/dist/components/response-stream.d.ts +14 -0
- package/dist/components/scroll-button.d.ts +7 -0
- package/dist/components/slash-command.d.ts +15 -0
- package/dist/components/source.d.ts +23 -0
- package/dist/components/tasks-card.d.ts +72 -0
- package/dist/components/text-shimmer.d.ts +9 -0
- package/dist/components/thinking-bar.d.ts +10 -0
- package/dist/components/toast.d.ts +48 -0
- package/dist/components/tool.d.ts +16 -0
- package/dist/components/use-card-resolution.d.ts +16 -0
- package/dist/components/voice-input.d.ts +7 -0
- package/dist/core-AYMC6_lb.js +5874 -12
- package/dist/css-M7EaDHN_.js +6 -1
- package/dist/custom-elements.json +260 -11
- package/dist/elements/artifact.d.ts +1 -0
- package/dist/elements/artifact.js +1 -0
- package/dist/elements/attachments.d.ts +1 -0
- package/dist/elements/attachments.js +1 -0
- package/dist/elements/autoloader.d.ts +2 -0
- package/dist/elements/autoloader.js +1 -0
- package/dist/elements/card.d.ts +1 -0
- package/dist/elements/card.js +1 -0
- package/dist/elements/cards.d.ts +1 -0
- package/dist/elements/cards.js +1 -0
- package/dist/elements/chain-of-thought.d.ts +1 -0
- package/dist/elements/chain-of-thought.js +1 -0
- package/dist/elements/chat-scope-picker.d.ts +1 -0
- package/dist/elements/chat-scope-picker.js +1 -0
- package/dist/elements/chat-workspace.d.ts +1 -0
- package/dist/elements/chat-workspace.js +1 -0
- package/dist/elements/chat.d.ts +1 -0
- package/dist/elements/chat.js +1 -0
- package/dist/elements/checkpoint.d.ts +1 -0
- package/dist/elements/checkpoint.js +1 -0
- package/dist/elements/choice.d.ts +1 -0
- package/dist/elements/choice.js +1 -0
- package/dist/elements/chunks/Icon-D_Zmwmwh.js +1 -0
- package/dist/elements/chunks/action-icons-PVfIvcuG.js +1 -0
- package/dist/elements/chunks/attachments-Dp6vqtrc.js +1 -0
- package/dist/elements/chunks/bash-InADTalH.js +1 -0
- package/dist/elements/chunks/button-CKyYD9G4.js +1 -0
- package/dist/elements/chunks/card-Cjho-PYk.js +1 -0
- package/dist/elements/chunks/card-routing-C986KuUA.js +1 -0
- package/dist/elements/chunks/chat-thread-RT808iPf.js +1 -0
- package/dist/elements/chunks/check-CDYaMYrM.js +1 -0
- package/dist/elements/chunks/chevron-down-3SBiq8HF.js +1 -0
- package/dist/elements/chunks/chevron-right-k2UFlIVK.js +1 -0
- package/dist/elements/chunks/cn-Dogu6PbV.js +1 -0
- package/dist/elements/chunks/code-block-BYUmfFSd.js +1 -0
- package/dist/elements/chunks/collapsible-BiNwdOn6.js +1 -0
- package/dist/elements/chunks/context-18g6oi4J.js +1 -0
- package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +1 -0
- package/dist/elements/chunks/core-AYMC6_lb.js +12 -0
- package/dist/elements/chunks/css-M7EaDHN_.js +1 -0
- package/dist/elements/chunks/default-input-CSAoYYMd.js +1 -0
- package/dist/elements/chunks/download-D8K0Je8r.js +1 -0
- package/dist/elements/chunks/dropdown-bUs2iTia.js +1 -0
- package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +141 -0
- package/dist/elements/chunks/external-link-6mA0wxab.js +1 -0
- package/dist/elements/chunks/file-text-DnaYk3Dm.js +1 -0
- package/dist/elements/chunks/file-tree-CgtRLlxK.js +1 -0
- package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +1 -0
- package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
- package/dist/elements/chunks/hover-card-CIGw62gP.js +1 -0
- package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
- package/dist/elements/chunks/index-CTBEJeo6.js +1 -0
- package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
- package/dist/elements/chunks/json-DxJze_jm.js +1 -0
- package/dist/elements/chunks/link-BlGSeoPy.js +1 -0
- package/dist/elements/chunks/loader-DvhR4qa0.js +1 -0
- package/dist/elements/chunks/markdown-CMTGx9r8.js +64 -0
- package/dist/elements/chunks/message-CDZwXjhL.js +1 -0
- package/dist/elements/chunks/message-feedback-CYx81QiA.js +1 -0
- package/dist/elements/chunks/model-switcher-O_tUhoOT.js +1 -0
- package/dist/elements/chunks/overlay-DydXYMQW.js +1 -0
- package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +1 -0
- package/dist/elements/chunks/reasoning-2PUnHlmr.js +1 -0
- package/dist/elements/chunks/resizable-B5WNbSeb.js +1 -0
- package/dist/elements/chunks/star-t9tI-P0q.js +1 -0
- package/dist/elements/chunks/store-C6rIooZz.js +1 -0
- package/dist/elements/chunks/svelte--5p79yCD.js +1 -0
- package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +1 -0
- package/dist/elements/chunks/textarea-3nm6Ia9U.js +1 -0
- package/dist/elements/chunks/thumbs-up-BBtguGld.js +1 -0
- package/dist/elements/chunks/toast-store-D8cL3fWl.js +1 -0
- package/dist/elements/chunks/tool-GJ3vwp1U.js +1 -0
- package/dist/elements/chunks/tooltip-DwKAz4XR.js +1 -0
- package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +1 -0
- package/dist/elements/chunks/tsx-B8rCNbgL.js +1 -0
- package/dist/elements/chunks/typescript-RycA9KXf.js +1 -0
- package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +1 -0
- package/dist/elements/chunks/video-mVYiM99-.js +1 -0
- package/dist/elements/chunks/vue-BmIZj4XD.js +1 -0
- package/dist/elements/chunks/x-CzxL27Ch.js +1 -0
- package/dist/elements/code-block.d.ts +1 -0
- package/dist/elements/code-block.js +1 -0
- package/dist/elements/compare.d.ts +1 -0
- package/dist/elements/compare.js +1 -0
- package/dist/elements/confirm-card.d.ts +1 -0
- package/dist/elements/confirm-card.js +1 -0
- package/dist/elements/context-meter.d.ts +1 -0
- package/dist/elements/context-meter.js +1 -0
- package/dist/elements/conversation-list.d.ts +1 -0
- package/dist/elements/conversation-list.js +1 -0
- package/dist/elements/embed.d.ts +1 -0
- package/dist/elements/embed.js +1 -0
- package/dist/elements/empty.d.ts +1 -0
- package/dist/elements/empty.js +1 -0
- package/dist/elements/feedback-bar.d.ts +1 -0
- package/dist/elements/feedback-bar.js +1 -0
- package/dist/elements/file-tree.d.ts +1 -0
- package/dist/elements/file-tree.js +1 -0
- package/dist/elements/file-upload.d.ts +1 -0
- package/dist/elements/file-upload.js +1 -0
- package/dist/elements/form.d.ts +1 -0
- package/dist/elements/form.js +1 -0
- package/dist/elements/image.d.ts +1 -0
- package/dist/elements/image.js +1 -0
- package/dist/elements/link-preview.d.ts +1 -0
- package/dist/elements/link-preview.js +1 -0
- package/dist/elements/loader.d.ts +1 -0
- package/dist/elements/loader.js +1 -0
- package/dist/elements/markdown.d.ts +1 -0
- package/dist/elements/markdown.js +1 -0
- package/dist/elements/message-skills.d.ts +1 -0
- package/dist/elements/message-skills.js +1 -0
- package/dist/elements/message.d.ts +1 -0
- package/dist/elements/message.js +1 -0
- package/dist/elements/model-switcher.d.ts +1 -0
- package/dist/elements/model-switcher.js +1 -0
- package/dist/elements/popover.d.ts +1 -0
- package/dist/elements/popover.js +1 -0
- package/dist/elements/prompt-input.d.ts +1 -0
- package/dist/elements/prompt-input.js +1 -0
- package/dist/elements/prompt-suggestions.d.ts +1 -0
- package/dist/elements/prompt-suggestions.js +1 -0
- package/dist/elements/reasoning.d.ts +1 -0
- package/dist/elements/reasoning.js +1 -0
- package/dist/elements/resizable.d.ts +1 -0
- package/dist/elements/resizable.js +1 -0
- package/dist/elements/response-stream.d.ts +1 -0
- package/dist/elements/response-stream.js +14 -0
- package/dist/elements/scroll-button.d.ts +1 -0
- package/dist/elements/scroll-button.js +1 -0
- package/dist/elements/source.d.ts +1 -0
- package/dist/elements/source.js +1 -0
- package/dist/elements/switch.d.ts +1 -0
- package/dist/elements/switch.js +1 -0
- package/dist/elements/tasks.d.ts +1 -0
- package/dist/elements/tasks.js +1 -0
- package/dist/elements/text-shimmer.d.ts +1 -0
- package/dist/elements/text-shimmer.js +1 -0
- package/dist/elements/thinking-bar.d.ts +1 -0
- package/dist/elements/thinking-bar.js +1 -0
- package/dist/elements/toast.d.ts +1 -0
- package/dist/elements/toast.js +1 -0
- package/dist/elements/tool.d.ts +1 -0
- package/dist/elements/tool.js +1 -0
- package/dist/elements/voice-input.d.ts +1 -0
- package/dist/elements/voice-input.js +1 -0
- package/dist/elements.d.ts +768 -0
- package/dist/engine-javascript-vq0WuIJl.js +2516 -14
- package/dist/github-dark-dimmed-DUshB20C.js +4 -1
- package/dist/github-light-JYsPkUQd.js +4 -1
- package/dist/html-CPZ3oZQ7.js +10 -1
- package/dist/index.d.ts +124 -2
- package/dist/index.js +15593 -0
- package/dist/javascript-C25yR2R2.js +6 -1
- package/dist/json-DxJze_jm.js +6 -1
- package/dist/kitn-chat.es.js +1 -77
- package/dist/llms/llms-full.txt +57 -12
- package/dist/llms/llms.txt +3 -3
- package/dist/mcp.es.js +2296 -0
- package/dist/primitives/card-contract.d.ts +119 -0
- package/dist/primitives/card-host.d.ts +12 -0
- package/dist/primitives/card-recovery.d.ts +55 -0
- package/dist/primitives/card-registry.d.ts +17 -0
- package/dist/primitives/card-resolution.d.ts +8 -0
- package/dist/primitives/card-routing.d.ts +12 -0
- package/dist/primitives/card-validate.d.ts +25 -0
- package/dist/primitives/chat-config.d.ts +31 -0
- package/dist/primitives/controllable.d.ts +13 -0
- package/dist/primitives/embed-providers.d.ts +65 -0
- package/dist/primitives/highlighter.d.ts +23 -0
- package/dist/primitives/link-preview.d.ts +47 -0
- package/dist/primitives/message-feedback.d.ts +35 -0
- package/dist/primitives/pdf-preview.d.ts +50 -0
- package/dist/primitives/toast-store.d.ts +88 -0
- package/dist/primitives/use-auto-resize.d.ts +8 -0
- package/dist/primitives/use-resize-observer.d.ts +1 -0
- package/dist/primitives/use-stick-to-bottom.d.ts +5 -0
- package/dist/primitives/use-text-stream.d.ts +19 -0
- package/dist/primitives/use-voice-recorder.d.ts +9 -0
- package/dist/react/index.d.ts +1198 -0
- package/dist/react/runtime.d.ts +15 -0
- package/dist/react.js +275 -0
- package/dist/register-JyTglR6m.js +1 -0
- package/dist/register-impl-C6wlSsts.js +77 -0
- package/dist/remote/host-embed.d.ts +33 -0
- package/dist/remote/index.d.ts +2 -0
- package/dist/remote/origin.d.ts +5 -0
- package/dist/remote/provider-runtime.d.ts +27 -0
- package/dist/remote/provider.d.ts +2 -0
- package/dist/remote/validate.d.ts +4 -0
- package/dist/remote/version.d.ts +4 -0
- package/dist/remote/wire.d.ts +53 -0
- package/dist/schemas/choice.schema.json +5 -0
- package/dist/schemas/tasks.schema.json +5 -0
- package/dist/svelte--5p79yCD.js +15 -1
- package/dist/tsx-B8rCNbgL.js +6 -1
- package/dist/types.d.ts +42 -0
- package/dist/typescript-RycA9KXf.js +6 -1
- package/dist/ui/action-icons.d.ts +11 -0
- package/dist/ui/avatar.d.ts +8 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/button.d.ts +10 -0
- package/dist/ui/collapsible.d.ts +27 -0
- package/dist/ui/dropdown.d.ts +20 -0
- package/dist/ui/hover-card.d.ts +28 -0
- package/dist/ui/overlay.d.ts +67 -0
- package/dist/ui/popover.d.ts +32 -0
- package/dist/ui/resizable.d.ts +76 -0
- package/dist/ui/scroll-area.d.ts +5 -0
- package/dist/ui/separator.d.ts +5 -0
- package/dist/ui/skeleton.d.ts +5 -0
- package/dist/ui/switch.d.ts +19 -0
- package/dist/ui/textarea.d.ts +6 -0
- package/dist/ui/tooltip.d.ts +9 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/vue-BmIZj4XD.js +33 -1
- package/frameworks/react/index.tsx +67 -11
- package/frameworks/react/runtime.tsx +16 -2
- package/llms-full.txt +57 -12
- package/llms.txt +3 -3
- package/package.json +36 -14
- package/src/agent-tooling/archetypes.ts +46 -0
- package/src/agent-tooling/integrations/cloudflare.ts +88 -0
- package/src/agent-tooling/integrations/langgraph.ts +60 -0
- package/src/agent-tooling/integrations/mastra.ts +27 -0
- package/src/agent-tooling/integrations/mock.ts +34 -0
- package/src/agent-tooling/integrations/ollama.ts +47 -0
- package/src/agent-tooling/integrations/openrouter.ts +26 -0
- package/src/agent-tooling/integrations/pi.ts +40 -0
- package/src/agent-tooling/integrations/pydantic-ai.ts +52 -0
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +44 -0
- package/src/agent-tooling/mcp/manifest.ts +128 -0
- package/src/agent-tooling/mcp/server.ts +59 -0
- package/src/agent-tooling/mcp/stdio.ts +18 -0
- package/src/agent-tooling/mcp/tools/debug.ts +489 -0
- package/src/agent-tooling/mcp/tools/reference.ts +132 -0
- package/src/agent-tooling/mcp/tools/scaffold.ts +1759 -0
- package/src/agent-tooling/mcp/tools/theme.ts +340 -0
- package/src/agent-tooling/mcp/tools/types.ts +14 -0
- package/src/agent-tooling/registry.ts +41 -0
- package/src/agent-tooling/types.ts +30 -0
- package/src/components/chat-thread.tsx +27 -35
- package/src/components/choice-card.tsx +33 -4
- package/src/components/component-meta.json +250 -26
- package/src/components/confirm-card.tsx +15 -1
- package/src/components/dismissed-stub.tsx +80 -0
- package/src/components/form.tsx +21 -3
- package/src/components/message.tsx +151 -10
- package/src/components/response-compare-types.ts +127 -0
- package/src/components/response-compare.tsx +431 -0
- package/src/components/tasks-card.tsx +33 -3
- package/src/components/toast.tsx +398 -0
- package/src/components/use-card-resolution.ts +24 -8
- package/src/elements/autoloader.ts +89 -0
- package/src/elements/chat-types.ts +7 -0
- package/src/elements/chat-workspace.tsx +4 -2
- package/src/elements/chat.tsx +4 -2
- package/src/elements/compare.tsx +88 -0
- package/src/elements/compiled.css +1 -1
- package/src/elements/define.tsx +7 -1
- package/src/elements/element-manifest.json +182 -0
- package/src/elements/element-meta.json +191 -68
- package/src/elements/element-types.d.ts +43 -6
- package/src/elements/framework-usage.json +25 -0
- package/src/elements/message.tsx +27 -45
- package/src/elements/register-impl.ts +58 -0
- package/src/elements/register.ts +26 -51
- package/src/elements/remote.tsx +4 -0
- package/src/elements/styles.css +19 -0
- package/src/elements/toast.tsx +74 -0
- package/src/index.ts +30 -0
- package/src/primitives/card-contract.ts +14 -5
- package/src/primitives/card-recovery.ts +149 -0
- package/src/primitives/card-routing.ts +3 -0
- package/src/primitives/card-schemas/choice.schema.json +5 -0
- package/src/primitives/card-schemas/tasks.schema.json +5 -0
- package/src/primitives/message-feedback.ts +123 -0
- package/src/primitives/toast-store.ts +235 -0
- package/src/ui/tooltip.tsx +8 -2
- package/theme.css +4 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const cloudflare: Integration = {
|
|
4
|
+
id: 'cloudflare',
|
|
5
|
+
title: 'Cloudflare AI',
|
|
6
|
+
category: 'provider',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: ['CF_ACCOUNT_ID', 'CF_API_TOKEN'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
next: `// app/api/chat/route.ts — proxy Workers AI, keep the token server-side
|
|
12
|
+
export async function POST(req: Request) {
|
|
13
|
+
const { messages } = await req.json();
|
|
14
|
+
|
|
15
|
+
const upstream = await fetch(
|
|
16
|
+
\`https://api.cloudflare.com/client/v4/accounts/\${process.env.CF_ACCOUNT_ID}/ai/v1/chat/completions\`,
|
|
17
|
+
{
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: \`Bearer \${process.env.CF_API_TOKEN}\`,
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
model: '@cf/meta/llama-3.1-8b-instruct',
|
|
25
|
+
messages,
|
|
26
|
+
stream: true,
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Workers AI returns OpenAI-format SSE — pass it straight through.
|
|
32
|
+
return new Response(upstream.body, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
33
|
+
}`,
|
|
34
|
+
worker: `// Worker handler — env.AI is bound in wrangler.toml
|
|
35
|
+
// env.AI.run emits Cloudflare-native SSE (data: {"response":"<token>"}).
|
|
36
|
+
// The TransformStream below re-frames each chunk to OpenAI-format SSE so
|
|
37
|
+
// kai-chat's reader works without any client-side changes.
|
|
38
|
+
export default {
|
|
39
|
+
async fetch(req: Request, env: Env): Promise<Response> {
|
|
40
|
+
const { messages } = await req.json();
|
|
41
|
+
|
|
42
|
+
const nativeStream = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
|
|
43
|
+
messages,
|
|
44
|
+
stream: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Re-frame Cloudflare-native SSE → OpenAI-format SSE
|
|
48
|
+
const { readable, writable } = new TransformStream();
|
|
49
|
+
const writer = writable.getWriter();
|
|
50
|
+
const encoder = new TextEncoder();
|
|
51
|
+
const decoder = new TextDecoder();
|
|
52
|
+
|
|
53
|
+
(async () => {
|
|
54
|
+
const reader = (nativeStream as ReadableStream<Uint8Array>).getReader();
|
|
55
|
+
let buffer = '';
|
|
56
|
+
while (true) {
|
|
57
|
+
const { value, done } = await reader.read();
|
|
58
|
+
if (done) break;
|
|
59
|
+
buffer += decoder.decode(value, { stream: true });
|
|
60
|
+
const lines = buffer.split('\\n');
|
|
61
|
+
buffer = lines.pop() ?? '';
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
const s = line.trim();
|
|
64
|
+
if (!s.startsWith('data:')) continue;
|
|
65
|
+
const payload = s.slice(5).trim();
|
|
66
|
+
if (payload === '[DONE]') continue;
|
|
67
|
+
try {
|
|
68
|
+
const { response } = JSON.parse(payload) as { response?: string };
|
|
69
|
+
if (response == null) continue;
|
|
70
|
+
const openaiChunk = JSON.stringify({ choices: [{ delta: { content: response } }] });
|
|
71
|
+
await writer.write(encoder.encode(\`data: \${openaiChunk}\\n\\n\`));
|
|
72
|
+
} catch { /* skip malformed lines */ }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await writer.write(encoder.encode('data: [DONE]\\n\\n'));
|
|
76
|
+
await writer.close();
|
|
77
|
+
})();
|
|
78
|
+
|
|
79
|
+
return new Response(readable, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
80
|
+
},
|
|
81
|
+
};`,
|
|
82
|
+
},
|
|
83
|
+
streamMapping: "Workers AI via the OpenAI-compatible HTTP endpoint returns OpenAI-format SSE — pipe upstream.body straight to the browser; kai-chat's reader handles it. The native env.AI binding streams Cloudflare's own format (data: {\"response\":\"...token...\"}); the worker route template re-frames these chunks to OpenAI-format SSE via a TransformStream before returning.",
|
|
84
|
+
runNote: 'Set CF_ACCOUNT_ID and CF_API_TOKEN. Model ids are prefixed with @cf/, e.g. @cf/meta/llama-3.1-8b-instruct. For the AI binding (worker key), add an [ai] block with binding = "AI" in wrangler.toml.',
|
|
85
|
+
docsSlug: 'integrations/cloudflare-ai',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default cloudflare;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const langgraph: Integration = {
|
|
4
|
+
id: 'langgraph',
|
|
5
|
+
title: 'LangGraph',
|
|
6
|
+
category: 'framework',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: ['OPENAI_API_KEY'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
next: `// POST /api/chat — stream a compiled LangGraph agent to the browser
|
|
12
|
+
import { createReactAgent } from '@langchain/langgraph/prebuilt';
|
|
13
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
14
|
+
import { tool } from '@langchain/core/tools';
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
|
|
17
|
+
const getWeather = tool(
|
|
18
|
+
async ({ city }) => \`It's 18°C and clear in \${city}.\`,
|
|
19
|
+
{
|
|
20
|
+
name: 'get_weather',
|
|
21
|
+
description: 'Get the current weather for a city.',
|
|
22
|
+
schema: z.object({ city: z.string() }),
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const agent = createReactAgent({
|
|
27
|
+
llm: new ChatOpenAI({ model: 'gpt-4o' }),
|
|
28
|
+
tools: [getWeather],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export async function POST(req: Request) {
|
|
32
|
+
const { messages } = await req.json();
|
|
33
|
+
|
|
34
|
+
const stream = await agent.stream({ messages }, { streamMode: 'messages' });
|
|
35
|
+
|
|
36
|
+
const encoder = new TextEncoder();
|
|
37
|
+
const body = new ReadableStream({
|
|
38
|
+
async start(controller) {
|
|
39
|
+
const send = (obj: unknown) =>
|
|
40
|
+
controller.enqueue(encoder.encode(\`data: \${JSON.stringify(obj)}\\n\\n\`));
|
|
41
|
+
|
|
42
|
+
for await (const [chunk] of stream) {
|
|
43
|
+
if (typeof chunk.content === 'string' && chunk.content) {
|
|
44
|
+
send({ choices: [{ delta: { content: chunk.content } }] });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));
|
|
48
|
+
controller.close();
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return new Response(body, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
53
|
+
}`,
|
|
54
|
+
},
|
|
55
|
+
streamMapping: "Use graph.stream(input, { streamMode: 'messages' }) to get [messageChunk, metadata] tuples. Extract chunk.content (string) and forward as OpenAI-format SSE frames: data: {choices:[{delta:{content}}]}. Close with data: [DONE]. The kai-chat reader handles it.",
|
|
56
|
+
runNote: 'Set OPENAI_API_KEY (or the key for your chosen model provider). Install @langchain/langgraph, @langchain/openai, @langchain/core.',
|
|
57
|
+
docsSlug: 'integrations/langgraph',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default langgraph;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const mastra: Integration = {
|
|
4
|
+
id: 'mastra',
|
|
5
|
+
title: 'Mastra',
|
|
6
|
+
category: 'harness',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: ['MASTRA_URL'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
express: `// POST /api/chat — your server proxies a Mastra agent to the browser
|
|
12
|
+
import { MastraClient } from '@mastra/client-js';
|
|
13
|
+
|
|
14
|
+
const mastra = new MastraClient({ baseUrl: process.env.MASTRA_URL });
|
|
15
|
+
|
|
16
|
+
const stream = await mastra.getAgent('supportAgent').stream({ messages });
|
|
17
|
+
for await (const delta of stream.textStream) {
|
|
18
|
+
res.write(\`data: \${JSON.stringify({ choices: [{ delta: { content: delta } }] })}\\n\\n\`);
|
|
19
|
+
}
|
|
20
|
+
res.write('data: [DONE]\\n\\n');`,
|
|
21
|
+
},
|
|
22
|
+
streamMapping: "Mastra agents speak Vercel AI SDK v5 and expose stream.textStream (async iterable of string deltas). Iterate textStream and emit data: {choices:[{delta:{content}}]} frames; close with data: [DONE]. kai-chat's SSE reader handles it. For tool calls and reasoning, convert the agent to a UI message stream with @mastra/ai-sdk.",
|
|
23
|
+
runNote: 'Set MASTRA_URL to your Mastra server base URL (mastra dev exposes POST /api/agents/:agentId/stream on port 4111). Install @mastra/client-js.',
|
|
24
|
+
docsSlug: 'integrations/harnesses',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default mastra;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mock — the zero-config first-win.
|
|
5
|
+
*
|
|
6
|
+
* Not a real backend: there's no provider, no API key, no `/api` route. The
|
|
7
|
+
* scaffolder special-cases `integration === 'mock'` and emits a front-end whose
|
|
8
|
+
* `onSubmit` SIMULATES a streamed assistant reply CLIENT-SIDE (token-by-token,
|
|
9
|
+
* a new array/object reference per chunk — honouring the messages contract).
|
|
10
|
+
*
|
|
11
|
+
* This lets `scaffold(useCase, integration: 'mock', framework: 'react')` run with
|
|
12
|
+
* zero config so a developer sees a live, streaming chat before wiring a model.
|
|
13
|
+
* Swap `integration` for a real provider (openrouter, ollama, …) when ready.
|
|
14
|
+
*
|
|
15
|
+
* It carries an empty `routeTemplates` on purpose: there is no server route to
|
|
16
|
+
* emit. `category: 'mock'` keeps it out of the real provider/gateway/framework/
|
|
17
|
+
* harness catalogs.
|
|
18
|
+
*/
|
|
19
|
+
const mock: Integration = {
|
|
20
|
+
id: 'mock',
|
|
21
|
+
title: 'Mock (local preview)',
|
|
22
|
+
category: 'mock',
|
|
23
|
+
language: 'ts',
|
|
24
|
+
streamFormat: 'native',
|
|
25
|
+
envVars: [],
|
|
26
|
+
routeTemplates: {},
|
|
27
|
+
streamMapping:
|
|
28
|
+
'No backend. onSubmit streams a canned assistant reply client-side, one token at a time, reassigning messages (new array/object reference) per chunk so kai-chat re-renders. Swap integration for a real provider when ready.',
|
|
29
|
+
runNote:
|
|
30
|
+
'No backend or API key needed — replies stream locally for preview. Run the front-end as-is; swap `integration` for a real provider (e.g. openrouter, ollama) when ready.',
|
|
31
|
+
docsSlug: 'integrations/mock',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default mock;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const ollama: Integration = {
|
|
4
|
+
id: 'ollama',
|
|
5
|
+
title: 'Ollama',
|
|
6
|
+
category: 'provider',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: [],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
next: `// app/api/chat/route.ts — proxy the browser to local Ollama
|
|
12
|
+
export async function POST(req: Request) {
|
|
13
|
+
const { messages } = await req.json();
|
|
14
|
+
|
|
15
|
+
const upstream = await fetch('http://localhost:11434/v1/chat/completions', {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
body: JSON.stringify({ model: 'llama3.2', messages, stream: true }),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Ollama returns OpenAI-format SSE — stream it straight to the browser.
|
|
22
|
+
return new Response(upstream.body, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
23
|
+
}`,
|
|
24
|
+
html: `<kai-chat id="chat"></kai-chat>
|
|
25
|
+
|
|
26
|
+
<script type="module">
|
|
27
|
+
import '@kitn.ai/ui/elements';
|
|
28
|
+
const chat = document.getElementById('chat');
|
|
29
|
+
|
|
30
|
+
chat.addEventListener('kai-submit', async (e) => {
|
|
31
|
+
const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: e.detail.value }];
|
|
32
|
+
chat.messages = [...history, { id: crypto.randomUUID(), role: 'assistant', content: '' }];
|
|
33
|
+
await fetch('/api/chat', {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify({ messages: history }),
|
|
37
|
+
});
|
|
38
|
+
// stream the reply into the last message — see the Streaming recipe
|
|
39
|
+
});
|
|
40
|
+
</script>`,
|
|
41
|
+
},
|
|
42
|
+
streamMapping: "Ollama's OpenAI-compatible endpoint (http://localhost:11434/v1/chat/completions) returns OpenAI-format SSE — pipe upstream.body straight to the browser; kai-chat's reader handles it. No API key needed; pass any string if a client requires one (Ollama ignores it).",
|
|
43
|
+
runNote: 'No API key required. Run: ollama serve (starts on 127.0.0.1:11434), then ollama pull <model>. For browser-direct access, set OLLAMA_ORIGINS to allow the page origin; restart Ollama after any env change.',
|
|
44
|
+
docsSlug: 'integrations/ollama',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default ollama;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const openrouter: Integration = {
|
|
4
|
+
id: 'openrouter',
|
|
5
|
+
title: 'OpenRouter',
|
|
6
|
+
category: 'gateway',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: ['OPENROUTER_API_KEY'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
next: `export async function POST(req: Request) {
|
|
12
|
+
const { model, messages } = await req.json();
|
|
13
|
+
const upstream = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { Authorization: \`Bearer \${process.env.OPENROUTER_API_KEY}\`, 'Content-Type': 'application/json' },
|
|
16
|
+
body: JSON.stringify({ model, messages, stream: true }),
|
|
17
|
+
});
|
|
18
|
+
return new Response(upstream.body, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
19
|
+
}`,
|
|
20
|
+
},
|
|
21
|
+
streamMapping: 'OpenRouter returns OpenAI-format SSE — pipe upstream.body straight to the browser; the Streaming-recipe reader handles it.',
|
|
22
|
+
runNote: 'Set OPENROUTER_API_KEY. Model ids are vendor/model, e.g. openai/gpt-4o.',
|
|
23
|
+
docsSlug: 'integrations/connect-any-model',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default openrouter;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const pi: Integration = {
|
|
4
|
+
id: 'pi',
|
|
5
|
+
title: 'Pi',
|
|
6
|
+
category: 'harness',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'native',
|
|
9
|
+
envVars: [],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
express: `import { spawn } from 'node:child_process';
|
|
12
|
+
|
|
13
|
+
// POST /api/chat — bridge a Pi RPC session to the browser as SSE
|
|
14
|
+
const pi = spawn('pi', ['--mode', 'rpc', '--no-session']);
|
|
15
|
+
|
|
16
|
+
// Send the user's turn. Pi commands are { type, message }.
|
|
17
|
+
pi.stdin.write(JSON.stringify({ type: 'prompt', message: prompt }) + '\\n');
|
|
18
|
+
|
|
19
|
+
let buffer = '';
|
|
20
|
+
pi.stdout.on('data', (chunk) => {
|
|
21
|
+
buffer += chunk.toString();
|
|
22
|
+
const lines = buffer.split('\\n');
|
|
23
|
+
buffer = lines.pop(); // hold the partial line for the next chunk
|
|
24
|
+
for (const line of lines) {
|
|
25
|
+
if (!line) continue;
|
|
26
|
+
const event = JSON.parse(line);
|
|
27
|
+
const part = event.assistantMessageEvent;
|
|
28
|
+
if (event.type === 'message_update' && part?.type === 'text_delta') {
|
|
29
|
+
res.write(\`data: \${JSON.stringify({ choices: [{ delta: { content: part.delta } }] })}\\n\\n\`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
pi.on('close', () => { res.write('data: [DONE]\\n\\n'); res.end(); });`,
|
|
34
|
+
},
|
|
35
|
+
streamMapping: "Pi runs as a local stdio process in RPC mode (pi --mode rpc --no-session). It emits newline-delimited JSON events on stdout. Map message_update events where assistantMessageEvent.type === 'text_delta' to data: {choices:[{delta:{content:part.delta}}]} SSE frames; send data: [DONE] on close. Split stdout on \\n (not readline, which breaks on Unicode separators U+2028/U+2029). Pi also emits thinking_delta (map to reasoning) and toolcall_* events (map to tool calls).",
|
|
36
|
+
runNote: "Pi must be installed locally and available on PATH as 'pi'. No API key is required by the bridge itself; Pi uses its own credentials. Pi runs with full user permissions — sandbox before exposing to a public endpoint. See the RPC reference: https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/rpc.md",
|
|
37
|
+
docsSlug: 'integrations/harnesses',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default pi;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const pydanticAi: Integration = {
|
|
4
|
+
id: 'pydantic-ai',
|
|
5
|
+
title: 'Pydantic AI',
|
|
6
|
+
category: 'framework',
|
|
7
|
+
language: 'python',
|
|
8
|
+
streamFormat: 'openai-sse',
|
|
9
|
+
envVars: ['OPENAI_API_KEY'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
fastapi: `# main.py
|
|
12
|
+
import json
|
|
13
|
+
from fastapi import FastAPI
|
|
14
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
15
|
+
from fastapi.responses import StreamingResponse
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
from pydantic_ai import Agent
|
|
18
|
+
|
|
19
|
+
agent = Agent('openai:gpt-4o')
|
|
20
|
+
|
|
21
|
+
app = FastAPI()
|
|
22
|
+
app.add_middleware(
|
|
23
|
+
CORSMiddleware, allow_origins=['*'], allow_methods=['*'], allow_headers=['*']
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
class Message(BaseModel):
|
|
27
|
+
role: str
|
|
28
|
+
content: str
|
|
29
|
+
|
|
30
|
+
class ChatRequest(BaseModel):
|
|
31
|
+
messages: list[Message]
|
|
32
|
+
|
|
33
|
+
async def openai_sse(messages: list[Message]):
|
|
34
|
+
prompt = messages[-1].content if messages else ''
|
|
35
|
+
async with agent.run_stream(prompt) as result:
|
|
36
|
+
async for delta in result.stream_text(delta=True):
|
|
37
|
+
chunk = {'choices': [{'delta': {'content': delta}}]}
|
|
38
|
+
yield f'data: {json.dumps(chunk)}\\n\\n'
|
|
39
|
+
yield 'data: [DONE]\\n\\n'
|
|
40
|
+
|
|
41
|
+
@app.post('/api/chat')
|
|
42
|
+
async def chat(req: ChatRequest):
|
|
43
|
+
return StreamingResponse(openai_sse(req.messages), media_type='text/event-stream')`,
|
|
44
|
+
},
|
|
45
|
+
streamMapping:
|
|
46
|
+
"Pydantic AI's agent.run_stream() yields text deltas via result.stream_text(delta=True). Each delta is re-framed as a data: {choices:[{delta:{content}}]} SSE line and the stream closes with data: [DONE]. The kai-chat reader is unchanged — it sees standard OpenAI-format SSE.",
|
|
47
|
+
runNote:
|
|
48
|
+
'Install: pip install pydantic-ai fastapi uvicorn. Set OPENAI_API_KEY. Run: uvicorn main:app --reload (default port 8000). Point kai-chat at http://localhost:8000/api/chat.',
|
|
49
|
+
docsSlug: 'integrations/pydantic-ai',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default pydanticAi;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Integration } from '../types';
|
|
2
|
+
|
|
3
|
+
const vercelAiSdk: Integration = {
|
|
4
|
+
id: 'vercel-ai-sdk',
|
|
5
|
+
title: 'Vercel AI SDK',
|
|
6
|
+
category: 'framework',
|
|
7
|
+
language: 'ts',
|
|
8
|
+
streamFormat: 'ai-sdk',
|
|
9
|
+
envVars: ['AI_GATEWAY_API_KEY'],
|
|
10
|
+
routeTemplates: {
|
|
11
|
+
next: `// app/api/chat/route.ts
|
|
12
|
+
import { streamText } from 'ai';
|
|
13
|
+
|
|
14
|
+
export const maxDuration = 30; // allow long streaming responses
|
|
15
|
+
|
|
16
|
+
export async function POST(req: Request) {
|
|
17
|
+
const { messages } = await req.json();
|
|
18
|
+
|
|
19
|
+
const result = streamText({
|
|
20
|
+
model: 'openai/gpt-4o', // AI Gateway id; needs AI_GATEWAY_API_KEY
|
|
21
|
+
messages,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
const sse = new ReadableStream({
|
|
26
|
+
async start(controller) {
|
|
27
|
+
for await (const delta of result.textStream) {
|
|
28
|
+
const chunk = { choices: [{ delta: { content: delta } }] };
|
|
29
|
+
controller.enqueue(encoder.encode(\`data: \${JSON.stringify(chunk)}\\n\\n\`));
|
|
30
|
+
}
|
|
31
|
+
controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));
|
|
32
|
+
controller.close();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return new Response(sse, { headers: { 'Content-Type': 'text/event-stream' } });
|
|
37
|
+
}`,
|
|
38
|
+
},
|
|
39
|
+
streamMapping: "The Vercel AI SDK's toUIMessageStreamResponse() and toTextStreamResponse() don't emit OpenAI-format SSE. Wrap result.textStream manually: iterate text deltas and emit data: {choices:[{delta:{content}}]} frames, closing with data: [DONE]. The kai-chat SSE reader handles it.",
|
|
40
|
+
runNote: 'Set AI_GATEWAY_API_KEY for the AI Gateway (string model id form: creator/model-name). For direct provider access, import its provider package (e.g. @ai-sdk/openai) and set the corresponding key (e.g. OPENAI_API_KEY).',
|
|
41
|
+
docsSlug: 'integrations/vercel-ai-sdk',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default vercelAiSdk;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest.ts — reads dist/custom-elements.json (a Custom Elements Manifest)
|
|
3
|
+
* and exposes helpers for the component_reference tool.
|
|
4
|
+
*
|
|
5
|
+
* Resolution strategy (dual-context):
|
|
6
|
+
* 1. Bundled bin: dist/mcp.es.js lives in dist/, so custom-elements.json is
|
|
7
|
+
* a sibling → try ./custom-elements.json relative to import.meta.url.
|
|
8
|
+
* 2. Vitest (source): manifest.ts lives in src/agent-tooling/mcp/, so we walk
|
|
9
|
+
* up parent directories looking for <dir>/dist/custom-elements.json.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { join, dirname } from 'node:path';
|
|
15
|
+
|
|
16
|
+
// ── CEM types ────────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface CemType {
|
|
19
|
+
text: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CemMember {
|
|
23
|
+
kind: 'field' | 'method';
|
|
24
|
+
privacy?: 'public' | 'private' | 'protected';
|
|
25
|
+
name: string;
|
|
26
|
+
type?: CemType;
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CemAttribute {
|
|
31
|
+
name: string;
|
|
32
|
+
fieldName?: string;
|
|
33
|
+
type?: CemType;
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CemEvent {
|
|
38
|
+
name: string;
|
|
39
|
+
type?: CemType;
|
|
40
|
+
description?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CemCssProperty {
|
|
44
|
+
name: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
default?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Declaration {
|
|
50
|
+
tagName?: string;
|
|
51
|
+
name: string;
|
|
52
|
+
kind: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
members?: CemMember[];
|
|
55
|
+
attributes?: CemAttribute[];
|
|
56
|
+
events?: CemEvent[];
|
|
57
|
+
cssProperties?: CemCssProperty[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface CemModule {
|
|
61
|
+
path?: string;
|
|
62
|
+
declarations?: Declaration[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface CustomElementsManifest {
|
|
66
|
+
modules: CemModule[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── Manifest resolution ───────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
function resolveManifestPath(): string {
|
|
72
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
73
|
+
const thisDir = dirname(thisFile);
|
|
74
|
+
|
|
75
|
+
// 1. Bundled bin: sibling in the same directory
|
|
76
|
+
const sibling = join(thisDir, 'custom-elements.json');
|
|
77
|
+
if (existsSync(sibling)) {
|
|
78
|
+
return sibling;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 2. Source/Vitest: walk up parent directories looking for dist/custom-elements.json
|
|
82
|
+
let dir = thisDir;
|
|
83
|
+
for (let i = 0; i < 10; i++) {
|
|
84
|
+
const candidate = join(dir, 'dist', 'custom-elements.json');
|
|
85
|
+
if (existsSync(candidate)) {
|
|
86
|
+
return candidate;
|
|
87
|
+
}
|
|
88
|
+
const parent = dirname(dir);
|
|
89
|
+
if (parent === dir) break; // reached filesystem root
|
|
90
|
+
dir = parent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Could not find custom-elements.json. Searched from: ${thisDir}`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── Parsed manifest (module-level cache) ─────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
let _manifest: CustomElementsManifest | undefined;
|
|
101
|
+
|
|
102
|
+
function getManifest(): CustomElementsManifest {
|
|
103
|
+
if (!_manifest) {
|
|
104
|
+
const path = resolveManifestPath();
|
|
105
|
+
const raw = readFileSync(path, 'utf-8');
|
|
106
|
+
_manifest = JSON.parse(raw) as CustomElementsManifest;
|
|
107
|
+
}
|
|
108
|
+
return _manifest;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getDeclarations(): Declaration[] {
|
|
112
|
+
return getManifest().modules.flatMap((m) => m.declarations ?? []);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/** Returns the Declaration for a given custom-element tag, or undefined. */
|
|
118
|
+
export function getElement(tag: string): Declaration | undefined {
|
|
119
|
+
return getDeclarations().find((d) => d.tagName === tag);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Returns all custom-element tagNames, sorted alphabetically. */
|
|
123
|
+
export function listElements(): string[] {
|
|
124
|
+
return getDeclarations()
|
|
125
|
+
.filter((d) => d.tagName)
|
|
126
|
+
.map((d) => d.tagName!)
|
|
127
|
+
.sort();
|
|
128
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import {
|
|
3
|
+
CallToolRequestSchema,
|
|
4
|
+
ListToolsRequestSchema,
|
|
5
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { reference } from './tools/reference';
|
|
8
|
+
import { scaffold } from './tools/scaffold';
|
|
9
|
+
import { theme } from './tools/theme';
|
|
10
|
+
import { debug } from './tools/debug';
|
|
11
|
+
import type { Tool } from './tools/types';
|
|
12
|
+
|
|
13
|
+
const tools: Tool[] = [reference, scaffold, theme, debug];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The AI/UI MCP server. A stdio server (see ./stdio.ts) exposing the AI/UI tools
|
|
17
|
+
* to any MCP harness. Registers four tools; Tasks 2–5 fill in each handler.
|
|
18
|
+
*
|
|
19
|
+
* The returned `Server` is augmented with `__listToolsForTest()`, a test-only
|
|
20
|
+
* helper that returns the registered tool names without touching the transport.
|
|
21
|
+
*/
|
|
22
|
+
export interface AiUiServer extends Server {
|
|
23
|
+
__listToolsForTest(): string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createServer(): AiUiServer {
|
|
27
|
+
const server = new Server(
|
|
28
|
+
{ name: '@kitn.ai/ui', version: '0.15.0' },
|
|
29
|
+
{ capabilities: { tools: {} } },
|
|
30
|
+
) as AiUiServer;
|
|
31
|
+
|
|
32
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
33
|
+
|
|
34
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
35
|
+
tools: tools.map((t) => ({
|
|
36
|
+
name: t.name,
|
|
37
|
+
description: t.description,
|
|
38
|
+
// The protocol requires a JSON Schema. We use zod 4's native converter
|
|
39
|
+
// (the repo is on zod 4); the SDK-bundled zod-to-json-schema only
|
|
40
|
+
// understands zod 3 and emits an empty schema for our zod 4 objects.
|
|
41
|
+
inputSchema: z.toJSONSchema(t.inputSchema) as Record<string, unknown>,
|
|
42
|
+
})),
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
46
|
+
const tool = byName.get(request.params.name);
|
|
47
|
+
if (!tool) {
|
|
48
|
+
return {
|
|
49
|
+
isError: true,
|
|
50
|
+
content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return tool.handler(request.params.arguments ?? {});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
server.__listToolsForTest = () => tools.map((t) => t.name);
|
|
57
|
+
|
|
58
|
+
return server;
|
|
59
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
2
|
+
import { createServer } from './server';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build entry for the AI/UI MCP server. Connects the server (./server.ts) to a
|
|
6
|
+
* stdio transport so any MCP harness (Claude Code, Codex, …) can drive it over
|
|
7
|
+
* stdin/stdout. Compiled to dist/mcp.es.js by vite.config.mcp.ts and launched by
|
|
8
|
+
* bin/mcp.js, which handles fatal errors / exit (this file stays free of Node
|
|
9
|
+
* globals so it typechecks under the repo's vite/client-only tsconfig).
|
|
10
|
+
*/
|
|
11
|
+
export async function startStdioServer(): Promise<void> {
|
|
12
|
+
const server = createServer();
|
|
13
|
+
const transport = new StdioServerTransport();
|
|
14
|
+
await server.connect(transport);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Auto-start when imported as the entry (the only consumer is bin/mcp.js).
|
|
18
|
+
startStdioServer();
|