@agentskit/chat-cli 0.1.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/LICENSE +22 -0
- package/README.md +12 -0
- package/dist/bin.cjs +407 -0
- package/dist/bin.d.cts +2 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +79 -0
- package/dist/chunk-SWALGE4D.js +320 -0
- package/dist/index.cjs +358 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +17 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentsKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @agentskit/chat-cli
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
agentskit-chat init my-chat --renderer react --yes
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Supported targets are `react`, `react-native`, `ink`, `vue`, `svelte`, `solid`,
|
|
8
|
+
and `angular`. The command detects a host package manifest when unambiguous,
|
|
9
|
+
prompts only on an interactive TTY, and atomically publishes only to a target
|
|
10
|
+
path that does not already exist.
|
|
11
|
+
|
|
12
|
+
Generated projects contain a shared definition, Web-standard server handler, native renderer, test, and architecture README. They use only published AgentsKit and AgentsKit Chat packages.
|
package/dist/bin.cjs
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/bin.ts
|
|
27
|
+
var import_node_util = require("util");
|
|
28
|
+
var import_promises2 = require("readline/promises");
|
|
29
|
+
var import_node_process = require("process");
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var import_promises = require("fs/promises");
|
|
33
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
34
|
+
var import_chat = require("@agentskit/chat");
|
|
35
|
+
var import_zod = require("zod");
|
|
36
|
+
var CHAT_RENDERERS = ["react", "react-native", "ink", "vue", "svelte", "solid", "angular"];
|
|
37
|
+
var ChatRendererSchema = import_zod.z.enum(CHAT_RENDERERS);
|
|
38
|
+
var ChatCliError = class extends Error {
|
|
39
|
+
constructor(code, message) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = "ChatCliError";
|
|
43
|
+
}
|
|
44
|
+
code;
|
|
45
|
+
};
|
|
46
|
+
var packageJson = (renderer) => JSON.stringify({
|
|
47
|
+
name: "agentskit-chat-app",
|
|
48
|
+
private: true,
|
|
49
|
+
version: "0.0.0",
|
|
50
|
+
type: "module",
|
|
51
|
+
...renderer === "react-native" ? { main: "expo/AppEntry.js" } : {},
|
|
52
|
+
scripts: { typecheck: renderer === "svelte" ? "svelte-check --tsconfig ./tsconfig.json" : "tsc --noEmit", test: "vitest run", ...["react", "vue", "svelte", "solid", "angular"].includes(renderer) ? { dev: "vite", build: "vite build" } : renderer === "react-native" ? { dev: "expo start", build: "expo export --platform web" } : { dev: "tsx src/index.tsx" } },
|
|
53
|
+
dependencies: {
|
|
54
|
+
"@agentskit/chat": "latest",
|
|
55
|
+
"@agentskit/chat-protocol": "latest",
|
|
56
|
+
"@agentskit/chat-server": "latest",
|
|
57
|
+
"@agentskit/core": "^1.12.2",
|
|
58
|
+
zod: "^4.3.6",
|
|
59
|
+
...renderer === "react" ? { "@agentskit/chat-react": "latest", "@agentskit/react": "^0.7.1", react: "^19.0.0", "react-dom": "^19.0.0" } : renderer === "react-native" ? { "@agentskit/chat-react-native": "latest", "@agentskit/react-native": "^0.4.4", expo: "^57.0.4", react: "19.2.3", "react-dom": "19.2.3", "react-native": "^0.86.0", "react-native-web": "^0.21.2" } : renderer === "ink" ? { "@agentskit/chat-ink": "latest", "@agentskit/ink": "^0.10.1", ink: "^7.0.0", react: "^19.0.0" } : renderer === "vue" ? { "@agentskit/chat-vue": "latest", "@agentskit/vue": "^0.4.4", vue: "^3.5.0" } : renderer === "svelte" ? { "@agentskit/chat-svelte": "latest", "@agentskit/svelte": "^0.4.4", svelte: "^5.0.0" } : renderer === "solid" ? { "@agentskit/chat-solid": "latest", "@agentskit/solid": "^0.4.4", "solid-js": "^1.9.0" } : { "@agentskit/chat-angular": "latest", "@agentskit/angular": "^0.4.6", "@angular/common": "^21.0.0", "@angular/compiler": "^21.0.0", "@angular/core": "^21.0.0", "@angular/platform-browser": "^21.0.0", rxjs: "^7.8.0", tslib: "^2.8.0", "zone.js": "^0.16.0" }
|
|
60
|
+
},
|
|
61
|
+
devDependencies: { "@types/node": "^25.0.0", tsx: "^4.20.0", typescript: renderer === "angular" ? "^5.9.0" : "^6.0.0", vitest: "^4.0.0", ...["react", "react-native", "ink"].includes(renderer) ? { "@types/react": "^19.0.0" } : {}, ...renderer === "react" ? { "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^5.0.0", vite: "^8.0.0" } : renderer === "vue" ? { "@vitejs/plugin-vue": "^6.0.0", vite: "^8.0.0" } : renderer === "svelte" ? { "@sveltejs/vite-plugin-svelte": "^7.0.0", "svelte-check": "^4.0.0", vite: "^8.0.0" } : renderer === "solid" ? { "vite-plugin-solid": "^2.11.0", vite: "^8.0.0" } : renderer === "angular" ? { vite: "^8.0.0" } : {} }
|
|
62
|
+
}, null, 2) + "\n";
|
|
63
|
+
var sharedFiles = () => ({
|
|
64
|
+
"src/chat.ts": `import type { AdapterFactory } from '@agentskit/core'
|
|
65
|
+
import { defineChat } from '@agentskit/chat'
|
|
66
|
+
|
|
67
|
+
const demoAdapter: AdapterFactory = {
|
|
68
|
+
createSource: request => ({
|
|
69
|
+
async *stream() {
|
|
70
|
+
const input = request.messages.filter(message => message.role === 'user').at(-1)?.content ?? ''
|
|
71
|
+
yield { type: 'text', content: \`Echo: \${input}\` }
|
|
72
|
+
yield { type: 'done' }
|
|
73
|
+
},
|
|
74
|
+
abort() {},
|
|
75
|
+
}),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const chat = defineChat({ id: 'starter', chat: { adapter: demoAdapter } })
|
|
79
|
+
`,
|
|
80
|
+
"src/server.ts": `import type { SessionSnapshot } from '@agentskit/chat-protocol'
|
|
81
|
+
import type { SessionStorage } from '@agentskit/chat'
|
|
82
|
+
import { createChatHandler } from '@agentskit/chat-server'
|
|
83
|
+
import { chat } from './chat.js'
|
|
84
|
+
|
|
85
|
+
let snapshot: SessionSnapshot | undefined
|
|
86
|
+
const storage: SessionStorage = {
|
|
87
|
+
load: () => snapshot,
|
|
88
|
+
save: (next, expected) => {
|
|
89
|
+
if (snapshot?.cursor !== expected) return false
|
|
90
|
+
snapshot = structuredClone(next)
|
|
91
|
+
return true
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const handleChat = createChatHandler({ resolveDefinition: () => chat, sessionStorage: () => storage })
|
|
96
|
+
`,
|
|
97
|
+
"tests/chat.test.ts": `import { describe, expect, it } from 'vitest'
|
|
98
|
+
import { chat } from '../src/chat.js'
|
|
99
|
+
import { handleChat } from '../src/server.js'
|
|
100
|
+
|
|
101
|
+
describe('generated chat', () => {
|
|
102
|
+
it('exports one runnable shared definition', () => expect(chat.id).toBe('starter'))
|
|
103
|
+
it('serves one complete turn through the Web handler', async () => {
|
|
104
|
+
const event = { protocol: 'agentskit.chat.turn', version: 1, eventId: 'submit', sessionId: 'session', turnId: 'turn', sequence: 0, emittedAt: new Date(0).toISOString(), event: 'client.turn.submit', payload: { input: 'hello' } }
|
|
105
|
+
const response = await handleChat(new Request('http://localhost/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(event) }))
|
|
106
|
+
expect(response.status).toBe(200)
|
|
107
|
+
expect(await response.text()).toContain('Echo: hello')
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
`,
|
|
111
|
+
"README.md": `# AgentsKit Chat starter
|
|
112
|
+
|
|
113
|
+
The shared definition lives in \`src/chat.ts\`, the Web-standard server seam in \`src/server.ts\`, and the native renderer entry under \`src\`.
|
|
114
|
+
|
|
115
|
+
Run \`pnpm typecheck && pnpm test\`, then \`pnpm dev\`. Replace the demo adapter in the shared definition with any published AgentsKit adapter.
|
|
116
|
+
`,
|
|
117
|
+
".gitignore": "node_modules\ndist\n.expo\n.env\n",
|
|
118
|
+
"vitest.config.ts": `import { defineConfig } from 'vitest/config'
|
|
119
|
+
export default defineConfig({ test: { environment: 'node' } })
|
|
120
|
+
`
|
|
121
|
+
});
|
|
122
|
+
var rendererFiles = (renderer) => renderer === "react" ? {
|
|
123
|
+
"index.html": '<!doctype html><html><body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body></html>\n',
|
|
124
|
+
"src/main.tsx": `import '@agentskit/react/theme'
|
|
125
|
+
import { StrictMode } from 'react'
|
|
126
|
+
import { createRoot } from 'react-dom/client'
|
|
127
|
+
import { AgentChat } from '@agentskit/chat-react'
|
|
128
|
+
import { chat } from './chat.js'
|
|
129
|
+
|
|
130
|
+
createRoot(document.getElementById('root')!).render(<StrictMode><AgentChat definition={chat} /></StrictMode>)
|
|
131
|
+
`,
|
|
132
|
+
"src/vite-env.d.ts": `/// <reference types="vite/client" />
|
|
133
|
+
declare module '@agentskit/react/theme'
|
|
134
|
+
`,
|
|
135
|
+
"vite.config.ts": `import react from '@vitejs/plugin-react'
|
|
136
|
+
import { defineConfig } from 'vite'
|
|
137
|
+
export default defineConfig({ plugins: [react()] })
|
|
138
|
+
`
|
|
139
|
+
} : renderer === "react-native" ? {
|
|
140
|
+
"App.tsx": `import { AgentChatNative } from '@agentskit/chat-react-native'
|
|
141
|
+
import { chat } from './src/chat'
|
|
142
|
+
export default function App() { return <AgentChatNative definition={chat} /> }
|
|
143
|
+
`,
|
|
144
|
+
"app.json": JSON.stringify({ expo: { name: "AgentsKit Chat", slug: "agentskit-chat" } }, null, 2) + "\n"
|
|
145
|
+
} : renderer === "ink" ? {
|
|
146
|
+
"src/index.tsx": `import { AgentChat } from '@agentskit/chat-ink'
|
|
147
|
+
import { render } from 'ink'
|
|
148
|
+
import { chat } from './chat.js'
|
|
149
|
+
const app = render(<AgentChat definition={chat} />)
|
|
150
|
+
if (process.env.CI) setTimeout(() => app.unmount(), 0)
|
|
151
|
+
`
|
|
152
|
+
} : renderer === "vue" ? {
|
|
153
|
+
"index.html": '<!doctype html><html><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body></html>\n',
|
|
154
|
+
"src/main.ts": `import { createApp, h } from 'vue'
|
|
155
|
+
import { AgentChat } from '@agentskit/chat-vue'
|
|
156
|
+
import { chat } from './chat.js'
|
|
157
|
+
createApp({ render: () => h(AgentChat, { definition: chat }) }).mount('#app')
|
|
158
|
+
`,
|
|
159
|
+
"vite.config.ts": `import vue from '@vitejs/plugin-vue'
|
|
160
|
+
import { defineConfig } from 'vite'
|
|
161
|
+
export default defineConfig({ plugins: [vue()] })
|
|
162
|
+
`
|
|
163
|
+
} : renderer === "svelte" ? {
|
|
164
|
+
"index.html": '<!doctype html><html><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body></html>\n',
|
|
165
|
+
"src/App.svelte": `<script lang="ts">
|
|
166
|
+
import { AgentChat } from '@agentskit/chat-svelte'
|
|
167
|
+
import { chat } from './chat.js'
|
|
168
|
+
</script>
|
|
169
|
+
<AgentChat definition={chat} />
|
|
170
|
+
`,
|
|
171
|
+
"src/main.ts": `import { mount } from 'svelte'
|
|
172
|
+
import App from './App.svelte'
|
|
173
|
+
mount(App, { target: document.getElementById('app')! })
|
|
174
|
+
`,
|
|
175
|
+
"src/vite-env.d.ts": `/// <reference types="svelte" />
|
|
176
|
+
`,
|
|
177
|
+
"vite.config.ts": `import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
178
|
+
import { defineConfig } from 'vite'
|
|
179
|
+
export default defineConfig({ plugins: [svelte()] })
|
|
180
|
+
`
|
|
181
|
+
} : renderer === "solid" ? {
|
|
182
|
+
"index.html": '<!doctype html><html><body><div id="app"></div><script type="module" src="/src/main.tsx"></script></body></html>\n',
|
|
183
|
+
"src/main.tsx": `import { render } from 'solid-js/web'
|
|
184
|
+
import { AgentChat } from '@agentskit/chat-solid'
|
|
185
|
+
import { chat } from './chat.js'
|
|
186
|
+
render(() => <AgentChat definition={chat} />, document.getElementById('app')!)
|
|
187
|
+
`,
|
|
188
|
+
"vite.config.ts": `import { defineConfig } from 'vite'
|
|
189
|
+
import solid from 'vite-plugin-solid'
|
|
190
|
+
export default defineConfig({ plugins: [solid()] })
|
|
191
|
+
`
|
|
192
|
+
} : {
|
|
193
|
+
"index.html": '<!doctype html><html><body><app-root></app-root><script type="module" src="/src/main.ts"></script></body></html>\n',
|
|
194
|
+
"src/main.ts": `import 'zone.js'
|
|
195
|
+
import '@angular/compiler'
|
|
196
|
+
import { Component } from '@angular/core'
|
|
197
|
+
import { bootstrapApplication } from '@angular/platform-browser'
|
|
198
|
+
import { AgentChatComponent } from '@agentskit/chat-angular'
|
|
199
|
+
import { chat } from './chat.js'
|
|
200
|
+
@Component({ selector: 'app-root', standalone: true, imports: [AgentChatComponent], template: '<ak-agent-chat [definition]="chat" />' })
|
|
201
|
+
class AppComponent { readonly chat = chat }
|
|
202
|
+
void bootstrapApplication(AppComponent)
|
|
203
|
+
`
|
|
204
|
+
};
|
|
205
|
+
var tsconfig = (renderer) => JSON.stringify({ compilerOptions: {
|
|
206
|
+
target: "ES2022",
|
|
207
|
+
module: "ESNext",
|
|
208
|
+
moduleResolution: "bundler",
|
|
209
|
+
jsx: "react-jsx",
|
|
210
|
+
strict: true,
|
|
211
|
+
noEmit: true,
|
|
212
|
+
skipLibCheck: true,
|
|
213
|
+
...renderer === "solid" ? { jsx: "preserve", jsxImportSource: "solid-js" } : {},
|
|
214
|
+
...["react", "vue", "svelte", "solid", "angular"].includes(renderer) ? { lib: ["ES2022", "DOM"] } : {},
|
|
215
|
+
...renderer === "ink" ? { types: ["node"] } : {}
|
|
216
|
+
}, include: renderer === "react-native" ? ["App.tsx", "src", "tests"] : ["src", "tests"] }, null, 2) + "\n";
|
|
217
|
+
var detectRenderer = async (targetDir) => {
|
|
218
|
+
try {
|
|
219
|
+
const input = JSON.parse(await (0, import_promises.readFile)(import_node_path.default.join(targetDir, "package.json"), "utf8"));
|
|
220
|
+
const manifest = import_zod.z.object({ dependencies: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(), devDependencies: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional() }).passthrough().parse(input);
|
|
221
|
+
const dependencies = { ...manifest.dependencies, ...manifest.devDependencies };
|
|
222
|
+
if (dependencies.expo || dependencies["react-native"]) return "react-native";
|
|
223
|
+
if (dependencies.ink) return "ink";
|
|
224
|
+
if (dependencies["@angular/core"]) return "angular";
|
|
225
|
+
if (dependencies.svelte) return "svelte";
|
|
226
|
+
if (dependencies.vue) return "vue";
|
|
227
|
+
if (dependencies["solid-js"]) return "solid";
|
|
228
|
+
return dependencies.react ? "react" : void 0;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (error.code === "ENOENT") return void 0;
|
|
231
|
+
throw new ChatCliError("MANIFEST_INVALID", "Could not read a valid package.json for renderer detection.");
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
var initChatProject = async (options) => {
|
|
235
|
+
const targetDir = import_node_path.default.resolve(options.targetDir);
|
|
236
|
+
const candidate = options.renderer ?? await detectRenderer(options.detectFromDir ?? targetDir);
|
|
237
|
+
if (!candidate) throw new ChatCliError("RENDERER_REQUIRED", `Choose a renderer with --renderer ${CHAT_RENDERERS.join(", ")}.`);
|
|
238
|
+
const renderer = ChatRendererSchema.parse(candidate);
|
|
239
|
+
const files = { ...sharedFiles(), ...rendererFiles(renderer), "package.json": packageJson(renderer), "tsconfig.json": tsconfig(renderer) };
|
|
240
|
+
const parent = import_node_path.default.dirname(targetDir);
|
|
241
|
+
await (0, import_promises.mkdir)(parent, { recursive: true });
|
|
242
|
+
const staging = await (0, import_promises.mkdtemp)(import_node_path.default.join(parent, `.${import_node_path.default.basename(targetDir)}-`));
|
|
243
|
+
try {
|
|
244
|
+
const writes = await Promise.allSettled(Object.entries(files).map(async ([relative, content]) => {
|
|
245
|
+
const destination = import_node_path.default.join(staging, relative);
|
|
246
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(destination), { recursive: true });
|
|
247
|
+
await (0, import_promises.writeFile)(destination, content, { encoding: "utf8", flag: "wx" });
|
|
248
|
+
}));
|
|
249
|
+
const failure = writes.find((result) => result.status === "rejected");
|
|
250
|
+
if (failure?.status === "rejected") throw failure.reason;
|
|
251
|
+
await (0, import_promises.rename)(staging, targetDir);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
await (0, import_promises.rm)(staging, { recursive: true, force: true });
|
|
254
|
+
const code = error.code;
|
|
255
|
+
if (code === "EEXIST" || code === "ENOTEMPTY" || code === "EISDIR" || code === "ENOTDIR") throw new ChatCliError("TARGET_EXISTS", `Refusing to overwrite existing path: ${targetDir}`);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
return Object.keys(files).sort();
|
|
259
|
+
};
|
|
260
|
+
var componentName = (input) => {
|
|
261
|
+
const kebab = input.trim().replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
262
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(kebab)) throw new ChatCliError("COMPONENT_INVALID", "Component name must start with a letter and contain only letters, numbers, or dashes.");
|
|
263
|
+
if (import_chat.STANDARD_COMPONENT_KEYS.includes(kebab)) throw new ChatCliError("COMPONENT_INVALID", "Component name is reserved by the standard catalog.");
|
|
264
|
+
return { kebab, pascal: kebab.split("-").map((part) => part[0].toUpperCase() + part.slice(1)).join("") };
|
|
265
|
+
};
|
|
266
|
+
var componentFiles = (name, renderers) => {
|
|
267
|
+
const { kebab, pascal } = componentName(name);
|
|
268
|
+
const shared = `import { z } from 'zod'
|
|
269
|
+
import type { ComponentDefinition } from '@agentskit/chat'
|
|
270
|
+
|
|
271
|
+
export const ${pascal}PropsSchema = z.object({ label: z.string().min(1).max(256) }).strict().readonly()
|
|
272
|
+
export type ${pascal}Props = z.infer<typeof ${pascal}PropsSchema>
|
|
273
|
+
export const ${pascal}Component = { key: '${kebab}', propsSchema: ${pascal}PropsSchema, events: [], accessibility: { role: 'group', keyboard: false, live: 'none' }, capabilities: ['display'], fallback: props => props.label } satisfies ComponentDefinition<${pascal}Props>
|
|
274
|
+
`;
|
|
275
|
+
const files = { [`src/components/${kebab}.ts`]: shared };
|
|
276
|
+
for (const renderer of [...new Set(renderers)]) {
|
|
277
|
+
const relative = `src/components/${renderer}/${kebab}.${renderer === "svelte" ? "svelte" : renderer === "vue" || renderer === "angular" ? "ts" : "tsx"}`;
|
|
278
|
+
files[relative] = renderer === "svelte" ? `<script lang="ts">
|
|
279
|
+
import { StandardComponent } from '@agentskit/chat-svelte'
|
|
280
|
+
import type { ComponentManifest } from '@agentskit/chat'
|
|
281
|
+
import type { ComponentInteractionEvent, ComponentRenderFrame } from '@agentskit/chat-protocol'
|
|
282
|
+
import { ${pascal}PropsSchema } from '../${kebab}.js'
|
|
283
|
+
let { frame, manifest, onInteract, disabled = false }: { frame: ComponentRenderFrame; manifest: ComponentManifest; onInteract: (event: ComponentInteractionEvent) => void; disabled?: boolean } = $props()
|
|
284
|
+
const value = $derived(frame.componentKey === '${kebab}' ? ${pascal}PropsSchema.parse(frame.props) : undefined)
|
|
285
|
+
</script>
|
|
286
|
+
{#if value}<section aria-label={value.label}>{value.label}</section>{:else}<StandardComponent {frame} {manifest} {onInteract} {disabled} />{/if}
|
|
287
|
+
` : renderer === "vue" ? `import { h, type VNode } from 'vue'
|
|
288
|
+
import { StandardComponent, type StandardComponentProps } from '@agentskit/chat-vue'
|
|
289
|
+
import { ${pascal}PropsSchema } from '../${kebab}.js'
|
|
290
|
+
export const ${pascal} = (slot: StandardComponentProps): VNode[] => { if (slot.frame.componentKey !== '${kebab}') return [h(StandardComponent, slot)]; const props = ${pascal}PropsSchema.parse(slot.frame.props); return [h('section', { 'aria-label': props.label }, props.label)] }
|
|
291
|
+
` : renderer === "angular" ? `import { Component, Input } from '@angular/core'
|
|
292
|
+
import { StandardComponentComponent } from '@agentskit/chat-angular'
|
|
293
|
+
import type { ComponentManifest } from '@agentskit/chat'
|
|
294
|
+
import type { ComponentInteractionEvent, ComponentRenderFrame } from '@agentskit/chat-protocol'
|
|
295
|
+
import { ${pascal}PropsSchema } from '../${kebab}.js'
|
|
296
|
+
@Component({ selector: 'ak-${kebab}', standalone: true, imports: [StandardComponentComponent], template: '@if (value; as item) { <section [attr.aria-label]="item.label">{{ item.label }}</section> } @else { <ak-standard-component [frame]="frame" [manifest]="manifest" [onInteract]="onInteract" [disabled]="disabled" /> }' })
|
|
297
|
+
export class ${pascal}Component { @Input({ required: true }) frame!: ComponentRenderFrame; @Input({ required: true }) manifest!: ComponentManifest; @Input({ required: true }) onInteract!: (event: ComponentInteractionEvent) => void; @Input() disabled = false; get value() { return this.frame.componentKey === '${kebab}' ? ${pascal}PropsSchema.parse(this.frame.props) : undefined } }
|
|
298
|
+
` : renderer === "solid" ? `import { StandardComponent, type StandardComponentProps } from '@agentskit/chat-solid'
|
|
299
|
+
import { ${pascal}PropsSchema } from '../${kebab}.js'
|
|
300
|
+
export const ${pascal} = (slot: StandardComponentProps) => { if (slot.frame.componentKey !== '${kebab}') return <StandardComponent {...slot} />; const props = ${pascal}PropsSchema.parse(slot.frame.props); return <section aria-label={props.label}>{props.label}</section> }
|
|
301
|
+
` : `import { ${renderer === "react-native" ? "StandardComponentNative as DefaultStandardComponent, type StandardComponentNativeProps" : "StandardComponent as DefaultStandardComponent, type StandardComponentProps"} } from '@agentskit/chat-${renderer}'
|
|
302
|
+
import { ${pascal}PropsSchema } from '../${kebab}.js'
|
|
303
|
+
${renderer === "react-native" ? "import { Text } from 'react-native'\n" : renderer === "ink" ? "import { Text } from 'ink'\n" : ""}export const ${pascal} = (slot: ${renderer === "react-native" ? "StandardComponentNativeProps" : "StandardComponentProps"}) => { if (slot.frame.componentKey !== '${kebab}') return <DefaultStandardComponent {...slot} />; const props = ${pascal}PropsSchema.parse(slot.frame.props); return <${renderer === "react-native" || renderer === "ink" ? "Text" : "section"}${renderer === "react-native" ? " accessibilityLabel={props.label}" : renderer === "ink" ? "" : " aria-label={props.label}"}>{props.label}</${renderer === "react-native" || renderer === "ink" ? "Text" : "section"}> }
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
return files;
|
|
307
|
+
};
|
|
308
|
+
var addChatComponent = async (options) => {
|
|
309
|
+
const projectDir = await (0, import_promises.realpath)(import_node_path.default.resolve(options.projectDir)).catch(() => {
|
|
310
|
+
throw new ChatCliError("MANIFEST_INVALID", "Component generation requires an existing project directory.");
|
|
311
|
+
});
|
|
312
|
+
const renderers = import_zod.z.array(ChatRendererSchema).min(1).parse(options.renderers);
|
|
313
|
+
const files = componentFiles(options.name, renderers);
|
|
314
|
+
const destinations = Object.keys(files).map((relative) => import_node_path.default.join(projectDir, relative));
|
|
315
|
+
for (const destination of destinations) {
|
|
316
|
+
let current = import_node_path.default.dirname(destination);
|
|
317
|
+
while (current !== projectDir) {
|
|
318
|
+
const status = await (0, import_promises.lstat)(current).catch((error) => error.code === "ENOENT" ? void 0 : Promise.reject(error));
|
|
319
|
+
if (status?.isSymbolicLink()) throw new ChatCliError("MANIFEST_INVALID", "Refusing to write through a symbolic-link component directory.");
|
|
320
|
+
current = import_node_path.default.dirname(current);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const checks = await Promise.all(destinations.map((destination) => (0, import_promises.readFile)(destination).then(() => true, (error) => error.code === "ENOENT" ? false : Promise.reject(error))));
|
|
324
|
+
if (checks.some(Boolean)) throw new ChatCliError("COMPONENT_EXISTS", "Refusing to overwrite an existing component file.");
|
|
325
|
+
const created = [];
|
|
326
|
+
try {
|
|
327
|
+
for (const [relative, content] of Object.entries(files)) {
|
|
328
|
+
const destination = import_node_path.default.join(projectDir, relative);
|
|
329
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(destination), { recursive: true });
|
|
330
|
+
await (0, import_promises.writeFile)(destination, content, { encoding: "utf8", flag: "wx" });
|
|
331
|
+
created.push(destination);
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
await Promise.all(created.map((destination) => (0, import_promises.rm)(destination, { force: true })));
|
|
335
|
+
if (error.code === "EEXIST") throw new ChatCliError("COMPONENT_EXISTS", "Refusing to overwrite an existing component file.");
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
return Object.keys(files).sort();
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/bin.ts
|
|
342
|
+
var help = `Usage: agentskit-chat init [directory] [--renderer ${CHAT_RENDERERS.join("|")}] [--yes]
|
|
343
|
+
agentskit-chat add component <name> --renderer react,vue [--directory .] [--yes]
|
|
344
|
+
agentskit-chat completion bash|zsh|fish
|
|
345
|
+
agentskit-chat --help
|
|
346
|
+
agentskit-chat --version
|
|
347
|
+
`;
|
|
348
|
+
var completion = (shell) => {
|
|
349
|
+
if (shell === "bash") return `_agentskit_chat_complete() { local words="--renderer --directory --yes"; [[ $COMP_CWORD == 1 ]] && words="init add completion --help --version"; [[ \${COMP_WORDS[COMP_CWORD-1]} == add ]] && words="component"; [[ \${COMP_WORDS[COMP_CWORD-1]} == --renderer ]] && words="${CHAT_RENDERERS.join(" ")}"; COMPREPLY=($(compgen -W "$words" -- "\${COMP_WORDS[COMP_CWORD]}")); }
|
|
350
|
+
complete -F _agentskit_chat_complete agentskit-chat
|
|
351
|
+
`;
|
|
352
|
+
if (shell === "zsh") return `#compdef agentskit-chat
|
|
353
|
+
_arguments "1:command:(init add completion)" "2:subcommand:(component)" "3:component name:" "--renderer[renderer]:renderer:(${CHAT_RENDERERS.join(" ")})" "--directory[project directory]:directory:_directories" "--yes[non-interactive]"
|
|
354
|
+
`;
|
|
355
|
+
if (shell === "fish") return `complete -c agentskit-chat -f -n '__fish_use_subcommand' -a 'init add completion'
|
|
356
|
+
complete -c agentskit-chat -f -n '__fish_seen_subcommand_from add' -a 'component'
|
|
357
|
+
complete -c agentskit-chat -l renderer -a "${CHAT_RENDERERS.join(" ")}"
|
|
358
|
+
complete -c agentskit-chat -l directory -r
|
|
359
|
+
complete -c agentskit-chat -l yes
|
|
360
|
+
`;
|
|
361
|
+
throw new Error(`Expected completion shell bash, zsh, or fish.
|
|
362
|
+
${help}`);
|
|
363
|
+
};
|
|
364
|
+
var main = async () => {
|
|
365
|
+
const parsed = (0, import_node_util.parseArgs)({ allowPositionals: true, strict: true, options: { renderer: { type: "string" }, directory: { type: "string", short: "d" }, yes: { type: "boolean", short: "y" }, help: { type: "boolean", short: "h" }, version: { type: "boolean", short: "v" } } });
|
|
366
|
+
if (parsed.values.help) return void import_node_process.stdout.write(help);
|
|
367
|
+
if (parsed.values.version) return void import_node_process.stdout.write(`${"0.1.0"}
|
|
368
|
+
`);
|
|
369
|
+
if (parsed.positionals[0] === "completion") {
|
|
370
|
+
if (parsed.positionals.length !== 2) throw new Error(`Expected one completion shell.
|
|
371
|
+
${help}`);
|
|
372
|
+
return void import_node_process.stdout.write(completion(parsed.positionals[1]));
|
|
373
|
+
}
|
|
374
|
+
if (parsed.positionals[0] === "add") {
|
|
375
|
+
if (parsed.positionals[1] !== "component" || parsed.positionals.length !== 3) throw new Error(`Expected add component <name>.
|
|
376
|
+
${help}`);
|
|
377
|
+
const renderers = parsed.values.renderer?.split(",").filter(Boolean).map((renderer2) => ChatRendererSchema.parse(renderer2));
|
|
378
|
+
if (!renderers?.length) throw new Error(`Expected --renderer with one or more comma-separated targets.
|
|
379
|
+
${help}`);
|
|
380
|
+
const files2 = await addChatComponent({ projectDir: parsed.values.directory ?? process.cwd(), name: parsed.positionals[2], renderers });
|
|
381
|
+
return void import_node_process.stdout.write(`${JSON.stringify({ ok: true, files: files2 })}
|
|
382
|
+
`);
|
|
383
|
+
}
|
|
384
|
+
if (parsed.positionals[0] !== "init") throw new Error(`Expected init command.
|
|
385
|
+
${help}`);
|
|
386
|
+
if (parsed.values.directory !== void 0) throw new Error(`--directory is only valid with add component.
|
|
387
|
+
${help}`);
|
|
388
|
+
if (parsed.positionals.length > 2) throw new Error(`Expected at most one target directory.
|
|
389
|
+
${help}`);
|
|
390
|
+
let renderer = parsed.values.renderer ?? await detectRenderer(process.cwd());
|
|
391
|
+
if (!renderer && !parsed.values.yes && import_node_process.stdin.isTTY && import_node_process.stdout.isTTY) {
|
|
392
|
+
const prompt = (0, import_promises2.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
393
|
+
try {
|
|
394
|
+
renderer = await prompt.question(`Renderer (${CHAT_RENDERERS.join(", ")}): `);
|
|
395
|
+
} finally {
|
|
396
|
+
prompt.close();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const files = await initChatProject({ targetDir: parsed.positionals[1] ?? "agentskit-chat-app", detectFromDir: process.cwd(), ...renderer ? { renderer: ChatRendererSchema.parse(renderer) } : {} });
|
|
400
|
+
import_node_process.stdout.write(`${JSON.stringify({ ok: true, files })}
|
|
401
|
+
`);
|
|
402
|
+
};
|
|
403
|
+
main().catch((error) => {
|
|
404
|
+
import_node_process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
405
|
+
`);
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
});
|
package/dist/bin.d.cts
ADDED
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CHAT_RENDERERS,
|
|
4
|
+
ChatRendererSchema,
|
|
5
|
+
addChatComponent,
|
|
6
|
+
detectRenderer,
|
|
7
|
+
initChatProject
|
|
8
|
+
} from "./chunk-SWALGE4D.js";
|
|
9
|
+
|
|
10
|
+
// src/bin.ts
|
|
11
|
+
import { parseArgs } from "util";
|
|
12
|
+
import { createInterface } from "readline/promises";
|
|
13
|
+
import { stdin, stderr, stdout } from "process";
|
|
14
|
+
var help = `Usage: agentskit-chat init [directory] [--renderer ${CHAT_RENDERERS.join("|")}] [--yes]
|
|
15
|
+
agentskit-chat add component <name> --renderer react,vue [--directory .] [--yes]
|
|
16
|
+
agentskit-chat completion bash|zsh|fish
|
|
17
|
+
agentskit-chat --help
|
|
18
|
+
agentskit-chat --version
|
|
19
|
+
`;
|
|
20
|
+
var completion = (shell) => {
|
|
21
|
+
if (shell === "bash") return `_agentskit_chat_complete() { local words="--renderer --directory --yes"; [[ $COMP_CWORD == 1 ]] && words="init add completion --help --version"; [[ \${COMP_WORDS[COMP_CWORD-1]} == add ]] && words="component"; [[ \${COMP_WORDS[COMP_CWORD-1]} == --renderer ]] && words="${CHAT_RENDERERS.join(" ")}"; COMPREPLY=($(compgen -W "$words" -- "\${COMP_WORDS[COMP_CWORD]}")); }
|
|
22
|
+
complete -F _agentskit_chat_complete agentskit-chat
|
|
23
|
+
`;
|
|
24
|
+
if (shell === "zsh") return `#compdef agentskit-chat
|
|
25
|
+
_arguments "1:command:(init add completion)" "2:subcommand:(component)" "3:component name:" "--renderer[renderer]:renderer:(${CHAT_RENDERERS.join(" ")})" "--directory[project directory]:directory:_directories" "--yes[non-interactive]"
|
|
26
|
+
`;
|
|
27
|
+
if (shell === "fish") return `complete -c agentskit-chat -f -n '__fish_use_subcommand' -a 'init add completion'
|
|
28
|
+
complete -c agentskit-chat -f -n '__fish_seen_subcommand_from add' -a 'component'
|
|
29
|
+
complete -c agentskit-chat -l renderer -a "${CHAT_RENDERERS.join(" ")}"
|
|
30
|
+
complete -c agentskit-chat -l directory -r
|
|
31
|
+
complete -c agentskit-chat -l yes
|
|
32
|
+
`;
|
|
33
|
+
throw new Error(`Expected completion shell bash, zsh, or fish.
|
|
34
|
+
${help}`);
|
|
35
|
+
};
|
|
36
|
+
var main = async () => {
|
|
37
|
+
const parsed = parseArgs({ allowPositionals: true, strict: true, options: { renderer: { type: "string" }, directory: { type: "string", short: "d" }, yes: { type: "boolean", short: "y" }, help: { type: "boolean", short: "h" }, version: { type: "boolean", short: "v" } } });
|
|
38
|
+
if (parsed.values.help) return void stdout.write(help);
|
|
39
|
+
if (parsed.values.version) return void stdout.write(`${"0.1.0"}
|
|
40
|
+
`);
|
|
41
|
+
if (parsed.positionals[0] === "completion") {
|
|
42
|
+
if (parsed.positionals.length !== 2) throw new Error(`Expected one completion shell.
|
|
43
|
+
${help}`);
|
|
44
|
+
return void stdout.write(completion(parsed.positionals[1]));
|
|
45
|
+
}
|
|
46
|
+
if (parsed.positionals[0] === "add") {
|
|
47
|
+
if (parsed.positionals[1] !== "component" || parsed.positionals.length !== 3) throw new Error(`Expected add component <name>.
|
|
48
|
+
${help}`);
|
|
49
|
+
const renderers = parsed.values.renderer?.split(",").filter(Boolean).map((renderer2) => ChatRendererSchema.parse(renderer2));
|
|
50
|
+
if (!renderers?.length) throw new Error(`Expected --renderer with one or more comma-separated targets.
|
|
51
|
+
${help}`);
|
|
52
|
+
const files2 = await addChatComponent({ projectDir: parsed.values.directory ?? process.cwd(), name: parsed.positionals[2], renderers });
|
|
53
|
+
return void stdout.write(`${JSON.stringify({ ok: true, files: files2 })}
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
if (parsed.positionals[0] !== "init") throw new Error(`Expected init command.
|
|
57
|
+
${help}`);
|
|
58
|
+
if (parsed.values.directory !== void 0) throw new Error(`--directory is only valid with add component.
|
|
59
|
+
${help}`);
|
|
60
|
+
if (parsed.positionals.length > 2) throw new Error(`Expected at most one target directory.
|
|
61
|
+
${help}`);
|
|
62
|
+
let renderer = parsed.values.renderer ?? await detectRenderer(process.cwd());
|
|
63
|
+
if (!renderer && !parsed.values.yes && stdin.isTTY && stdout.isTTY) {
|
|
64
|
+
const prompt = createInterface({ input: stdin, output: stdout });
|
|
65
|
+
try {
|
|
66
|
+
renderer = await prompt.question(`Renderer (${CHAT_RENDERERS.join(", ")}): `);
|
|
67
|
+
} finally {
|
|
68
|
+
prompt.close();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const files = await initChatProject({ targetDir: parsed.positionals[1] ?? "agentskit-chat-app", detectFromDir: process.cwd(), ...renderer ? { renderer: ChatRendererSchema.parse(renderer) } : {} });
|
|
72
|
+
stdout.write(`${JSON.stringify({ ok: true, files })}
|
|
73
|
+
`);
|
|
74
|
+
};
|
|
75
|
+
main().catch((error) => {
|
|
76
|
+
stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
77
|
+
`);
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
});
|