@kitnai/cli 0.1.10 → 0.1.12
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 +4 -5
- package/dist/index.js +376 -325
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,9 @@ var __export = (target, all) => {
|
|
|
13
13
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
14
14
|
import { join as join2 } from "path";
|
|
15
15
|
import { z } from "zod";
|
|
16
|
+
function getRegistryUrl(entry) {
|
|
17
|
+
return typeof entry === "string" ? entry : entry.url;
|
|
18
|
+
}
|
|
16
19
|
async function readConfig(projectDir) {
|
|
17
20
|
try {
|
|
18
21
|
const raw = await readFile2(join2(projectDir, CONFIG_FILE), "utf-8");
|
|
@@ -25,7 +28,16 @@ async function writeConfig(projectDir, config) {
|
|
|
25
28
|
const data = { $schema: "https://kitn.dev/schema/config.json", ...config };
|
|
26
29
|
await writeFile2(join2(projectDir, CONFIG_FILE), JSON.stringify(data, null, 2) + "\n");
|
|
27
30
|
}
|
|
28
|
-
|
|
31
|
+
function getInstallPath(config, type, fileName, namespace) {
|
|
32
|
+
const aliasKey = typeToAliasKey[type];
|
|
33
|
+
const base = config.aliases[aliasKey];
|
|
34
|
+
if (namespace && namespace !== "@kitn") {
|
|
35
|
+
const nsDir = namespace.replace("@", "");
|
|
36
|
+
return join2(base, nsDir, fileName);
|
|
37
|
+
}
|
|
38
|
+
return join2(base, fileName);
|
|
39
|
+
}
|
|
40
|
+
var componentType, installedComponentSchema, registryEntrySchema, registryValueSchema, configSchema, CONFIG_FILE, typeToAliasKey;
|
|
29
41
|
var init_config = __esm({
|
|
30
42
|
"src/utils/config.ts"() {
|
|
31
43
|
"use strict";
|
|
@@ -37,6 +49,12 @@ var init_config = __esm({
|
|
|
37
49
|
files: z.array(z.string()),
|
|
38
50
|
hash: z.string()
|
|
39
51
|
});
|
|
52
|
+
registryEntrySchema = z.object({
|
|
53
|
+
url: z.string(),
|
|
54
|
+
homepage: z.string().optional(),
|
|
55
|
+
description: z.string().optional()
|
|
56
|
+
});
|
|
57
|
+
registryValueSchema = z.union([z.string(), registryEntrySchema]);
|
|
40
58
|
configSchema = z.object({
|
|
41
59
|
$schema: z.string().optional(),
|
|
42
60
|
runtime: z.enum(["bun", "node", "deno"]),
|
|
@@ -48,105 +66,103 @@ var init_config = __esm({
|
|
|
48
66
|
skills: z.string(),
|
|
49
67
|
storage: z.string()
|
|
50
68
|
}),
|
|
51
|
-
registries: z.record(z.string(),
|
|
69
|
+
registries: z.record(z.string(), registryValueSchema),
|
|
52
70
|
installed: z.record(z.string(), installedComponentSchema).optional()
|
|
53
71
|
});
|
|
54
72
|
CONFIG_FILE = "kitn.json";
|
|
73
|
+
typeToAliasKey = {
|
|
74
|
+
"kitn:agent": "agents",
|
|
75
|
+
"kitn:tool": "tools",
|
|
76
|
+
"kitn:skill": "skills",
|
|
77
|
+
"kitn:storage": "storage"
|
|
78
|
+
};
|
|
55
79
|
}
|
|
56
80
|
});
|
|
57
81
|
|
|
58
|
-
// src/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.exit(0);
|
|
82
|
+
// src/installers/tsconfig-patcher.ts
|
|
83
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
84
|
+
import { join as join3 } from "path";
|
|
85
|
+
function stripJsonc(text3) {
|
|
86
|
+
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
87
|
+
}
|
|
88
|
+
function patchTsconfig(tsconfigContent, paths, removePrefixes) {
|
|
89
|
+
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
90
|
+
if (!config.compilerOptions) {
|
|
91
|
+
config.compilerOptions = {};
|
|
92
|
+
}
|
|
93
|
+
if (!config.compilerOptions.paths) {
|
|
94
|
+
config.compilerOptions.paths = {};
|
|
95
|
+
}
|
|
96
|
+
if (removePrefixes) {
|
|
97
|
+
for (const key of Object.keys(config.compilerOptions.paths)) {
|
|
98
|
+
if (removePrefixes.some((prefix) => key.startsWith(prefix))) {
|
|
99
|
+
delete config.compilerOptions.paths[key];
|
|
100
|
+
}
|
|
78
101
|
}
|
|
79
102
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
options: [
|
|
83
|
-
{ value: "bun", label: "Bun", hint: "recommended" },
|
|
84
|
-
{ value: "node", label: "Node.js" },
|
|
85
|
-
{ value: "deno", label: "Deno" }
|
|
86
|
-
]
|
|
87
|
-
});
|
|
88
|
-
if (p.isCancel(runtime)) {
|
|
89
|
-
p.cancel("Init cancelled.");
|
|
90
|
-
process.exit(0);
|
|
103
|
+
for (const [key, value] of Object.entries(paths)) {
|
|
104
|
+
config.compilerOptions.paths[key] = value;
|
|
91
105
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
if (p.isCancel(framework)) {
|
|
103
|
-
p.cancel("Init cancelled.");
|
|
104
|
-
process.exit(0);
|
|
106
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
107
|
+
}
|
|
108
|
+
async function patchProjectTsconfig(projectDir, paths, removePrefixes) {
|
|
109
|
+
const tsconfigPath = join3(projectDir, "tsconfig.json");
|
|
110
|
+
let content;
|
|
111
|
+
try {
|
|
112
|
+
content = await readFile3(tsconfigPath, "utf-8");
|
|
113
|
+
} catch {
|
|
114
|
+
content = "{}";
|
|
105
115
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
p.cancel("Init cancelled.");
|
|
113
|
-
process.exit(0);
|
|
116
|
+
const patched = patchTsconfig(content, paths, removePrefixes);
|
|
117
|
+
await writeFile3(tsconfigPath, patched);
|
|
118
|
+
}
|
|
119
|
+
var init_tsconfig_patcher = __esm({
|
|
120
|
+
"src/installers/tsconfig-patcher.ts"() {
|
|
121
|
+
"use strict";
|
|
114
122
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
tools: `${baseDir}/tools`,
|
|
123
|
-
skills: `${baseDir}/skills`,
|
|
124
|
-
storage: `${baseDir}/storage`
|
|
125
|
-
},
|
|
126
|
-
registries: {
|
|
127
|
-
"@kitn": "https://kitn-ai.github.io/registry/r/{type}/{name}.json"
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
const s = p.spinner();
|
|
131
|
-
s.start("Writing kitn.json");
|
|
132
|
-
await writeConfig(cwd, config);
|
|
133
|
-
s.stop("Created kitn.json");
|
|
134
|
-
p.outro(pc2.green("Done! Run `kitn add core` to install the engine, then `kitn add routes` for HTTP routes."));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// src/installers/barrel-manager.ts
|
|
126
|
+
function createBarrelFile() {
|
|
127
|
+
return `${BARREL_COMMENT}
|
|
128
|
+
${EXPORT_LINE}
|
|
129
|
+
`;
|
|
135
130
|
}
|
|
136
|
-
|
|
137
|
-
"
|
|
131
|
+
function addImportToBarrel(content, importPath) {
|
|
132
|
+
const importLine = `import "${importPath}";`;
|
|
133
|
+
if (content.includes(importLine)) return content;
|
|
134
|
+
const exportIndex = content.indexOf(EXPORT_LINE);
|
|
135
|
+
if (exportIndex === -1) {
|
|
136
|
+
return `${content.trimEnd()}
|
|
137
|
+
${importLine}
|
|
138
|
+
${EXPORT_LINE}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
const before = content.slice(0, exportIndex);
|
|
142
|
+
const after = content.slice(exportIndex);
|
|
143
|
+
return `${before}${importLine}
|
|
144
|
+
${after}`;
|
|
145
|
+
}
|
|
146
|
+
function removeImportFromBarrel(content, importPath) {
|
|
147
|
+
const importLine = `import "${importPath}";`;
|
|
148
|
+
return content.split("\n").filter((line) => line.trim() !== importLine).join("\n");
|
|
149
|
+
}
|
|
150
|
+
var EXPORT_LINE, BARREL_COMMENT;
|
|
151
|
+
var init_barrel_manager = __esm({
|
|
152
|
+
"src/installers/barrel-manager.ts"() {
|
|
138
153
|
"use strict";
|
|
139
|
-
|
|
154
|
+
EXPORT_LINE = 'export { registerWithPlugin } from "@kitn/core";';
|
|
155
|
+
BARREL_COMMENT = "// Managed by kitn CLI \u2014 components auto-imported below";
|
|
140
156
|
}
|
|
141
157
|
});
|
|
142
158
|
|
|
143
159
|
// src/utils/detect.ts
|
|
144
160
|
import { access } from "fs/promises";
|
|
145
|
-
import { join as
|
|
161
|
+
import { join as join4 } from "path";
|
|
146
162
|
async function detectPackageManager(dir) {
|
|
147
163
|
for (const [lockfile, pm] of LOCKFILE_MAP) {
|
|
148
164
|
try {
|
|
149
|
-
await access(
|
|
165
|
+
await access(join4(dir, lockfile));
|
|
150
166
|
return pm;
|
|
151
167
|
} catch {
|
|
152
168
|
}
|
|
@@ -168,6 +184,9 @@ var init_detect = __esm({
|
|
|
168
184
|
});
|
|
169
185
|
|
|
170
186
|
// src/registry/fetcher.ts
|
|
187
|
+
function urlOf(entry) {
|
|
188
|
+
return typeof entry === "string" ? entry : entry.url;
|
|
189
|
+
}
|
|
171
190
|
var RegistryFetcher;
|
|
172
191
|
var init_fetcher = __esm({
|
|
173
192
|
"src/registry/fetcher.ts"() {
|
|
@@ -181,8 +200,9 @@ var init_fetcher = __esm({
|
|
|
181
200
|
this.fetchFn = fetchFn ?? this.defaultFetch;
|
|
182
201
|
}
|
|
183
202
|
resolveUrl(name, typeDir, namespace = "@kitn", version) {
|
|
184
|
-
const
|
|
185
|
-
if (!
|
|
203
|
+
const entry = this.registries[namespace];
|
|
204
|
+
if (!entry) throw new Error(`No registry configured for ${namespace}`);
|
|
205
|
+
const template = urlOf(entry);
|
|
186
206
|
const fileName = version ? `${name}@${version}` : name;
|
|
187
207
|
return template.replace("{name}", fileName).replace("{type}", typeDir);
|
|
188
208
|
}
|
|
@@ -194,8 +214,9 @@ var init_fetcher = __esm({
|
|
|
194
214
|
return this.cache.get(url);
|
|
195
215
|
}
|
|
196
216
|
async fetchIndex(namespace = "@kitn") {
|
|
197
|
-
const
|
|
198
|
-
if (!
|
|
217
|
+
const entry = this.registries[namespace];
|
|
218
|
+
if (!entry) throw new Error(`No registry configured for ${namespace}`);
|
|
219
|
+
const template = urlOf(entry);
|
|
199
220
|
const baseUrl = template.replace("{type}/{name}.json", "registry.json");
|
|
200
221
|
const res = await fetch(baseUrl);
|
|
201
222
|
if (!res.ok) throw new Error(`Failed to fetch registry index: ${res.statusText}`);
|
|
@@ -269,7 +290,7 @@ var init_resolver = __esm({
|
|
|
269
290
|
});
|
|
270
291
|
|
|
271
292
|
// src/installers/file-writer.ts
|
|
272
|
-
import { readFile as
|
|
293
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir2, access as access2 } from "fs/promises";
|
|
273
294
|
import { dirname } from "path";
|
|
274
295
|
import { createPatch } from "diff";
|
|
275
296
|
async function checkFileStatus(filePath, newContent) {
|
|
@@ -278,7 +299,7 @@ async function checkFileStatus(filePath, newContent) {
|
|
|
278
299
|
} catch {
|
|
279
300
|
return "new" /* New */;
|
|
280
301
|
}
|
|
281
|
-
const existing = await
|
|
302
|
+
const existing = await readFile4(filePath, "utf-8");
|
|
282
303
|
return existing === newContent ? "identical" /* Identical */ : "different" /* Different */;
|
|
283
304
|
}
|
|
284
305
|
function generateDiff(filePath, oldContent, newContent) {
|
|
@@ -286,14 +307,14 @@ function generateDiff(filePath, oldContent, newContent) {
|
|
|
286
307
|
}
|
|
287
308
|
async function readExistingFile(filePath) {
|
|
288
309
|
try {
|
|
289
|
-
return await
|
|
310
|
+
return await readFile4(filePath, "utf-8");
|
|
290
311
|
} catch {
|
|
291
312
|
return null;
|
|
292
313
|
}
|
|
293
314
|
}
|
|
294
315
|
async function writeComponentFile(filePath, content) {
|
|
295
316
|
await mkdir2(dirname(filePath), { recursive: true });
|
|
296
|
-
await
|
|
317
|
+
await writeFile4(filePath, content);
|
|
297
318
|
}
|
|
298
319
|
var init_file_writer = __esm({
|
|
299
320
|
"src/installers/file-writer.ts"() {
|
|
@@ -327,10 +348,10 @@ var init_dep_installer = __esm({
|
|
|
327
348
|
});
|
|
328
349
|
|
|
329
350
|
// src/installers/env-writer.ts
|
|
330
|
-
import * as
|
|
331
|
-
import
|
|
332
|
-
import { readFile as
|
|
333
|
-
import { join as
|
|
351
|
+
import * as p from "@clack/prompts";
|
|
352
|
+
import pc2 from "picocolors";
|
|
353
|
+
import { readFile as readFile5, writeFile as writeFile5, access as access3 } from "fs/promises";
|
|
354
|
+
import { join as join5 } from "path";
|
|
334
355
|
function parseEnvKeys(content) {
|
|
335
356
|
const keys = /* @__PURE__ */ new Set();
|
|
336
357
|
for (const line of content.split("\n")) {
|
|
@@ -345,7 +366,7 @@ function parseEnvKeys(content) {
|
|
|
345
366
|
}
|
|
346
367
|
async function readEnvFile(path) {
|
|
347
368
|
try {
|
|
348
|
-
return await
|
|
369
|
+
return await readFile5(path, "utf-8");
|
|
349
370
|
} catch {
|
|
350
371
|
return "";
|
|
351
372
|
}
|
|
@@ -362,8 +383,8 @@ function collectEnvVars(items) {
|
|
|
362
383
|
async function handleEnvVars(cwd, envVars) {
|
|
363
384
|
const keys = Object.keys(envVars);
|
|
364
385
|
if (keys.length === 0) return;
|
|
365
|
-
const envPath =
|
|
366
|
-
const examplePath =
|
|
386
|
+
const envPath = join5(cwd, ".env");
|
|
387
|
+
const examplePath = join5(cwd, ".env.example");
|
|
367
388
|
const envContent = await readEnvFile(envPath);
|
|
368
389
|
const exampleContent = await readEnvFile(examplePath);
|
|
369
390
|
const envKeys = parseEnvKeys(envContent);
|
|
@@ -378,25 +399,25 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
378
399
|
lines.push(`# ${config.description}${config.url ? ` (${config.url})` : ""}`);
|
|
379
400
|
lines.push(`${key}=`);
|
|
380
401
|
}
|
|
381
|
-
await
|
|
382
|
-
|
|
402
|
+
await writeFile5(examplePath, exampleContent + lines.join("\n") + "\n");
|
|
403
|
+
p.log.info(`Updated ${pc2.cyan(".env.example")} with ${missingFromExample.length} variable(s)`);
|
|
383
404
|
}
|
|
384
405
|
if (missingFromEnv.length === 0) return;
|
|
385
|
-
|
|
386
|
-
|
|
406
|
+
p.log.message("");
|
|
407
|
+
p.log.warn(
|
|
387
408
|
`${missingFromEnv.length} environment variable(s) needed:`
|
|
388
409
|
);
|
|
389
410
|
for (const key of missingFromEnv) {
|
|
390
411
|
const config = envVars[key];
|
|
391
|
-
const req = config.required !== false ?
|
|
392
|
-
|
|
412
|
+
const req = config.required !== false ? pc2.red("*") : "";
|
|
413
|
+
p.log.message(` ${pc2.yellow(key)}${req}: ${config.description}${config.url ? pc2.dim(` -> ${config.url}`) : ""}`);
|
|
393
414
|
}
|
|
394
|
-
const shouldPrompt = await
|
|
415
|
+
const shouldPrompt = await p.confirm({
|
|
395
416
|
message: "Would you like to enter values now?",
|
|
396
417
|
initialValue: true
|
|
397
418
|
});
|
|
398
|
-
if (
|
|
399
|
-
|
|
419
|
+
if (p.isCancel(shouldPrompt) || !shouldPrompt) {
|
|
420
|
+
p.log.info(`Add them to ${pc2.cyan(".env")} when ready.`);
|
|
400
421
|
return;
|
|
401
422
|
}
|
|
402
423
|
const newEntries = [];
|
|
@@ -405,17 +426,17 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
405
426
|
const isSecret = config.secret !== false;
|
|
406
427
|
let value;
|
|
407
428
|
if (isSecret) {
|
|
408
|
-
value = await
|
|
429
|
+
value = await p.password({
|
|
409
430
|
message: `${key}:`
|
|
410
431
|
});
|
|
411
432
|
} else {
|
|
412
|
-
value = await
|
|
433
|
+
value = await p.text({
|
|
413
434
|
message: `${key}:`,
|
|
414
435
|
placeholder: config.description
|
|
415
436
|
});
|
|
416
437
|
}
|
|
417
|
-
if (
|
|
418
|
-
|
|
438
|
+
if (p.isCancel(value)) {
|
|
439
|
+
p.log.info(`Skipped remaining variables. Add them to ${pc2.cyan(".env")} when ready.`);
|
|
419
440
|
break;
|
|
420
441
|
}
|
|
421
442
|
if (value) {
|
|
@@ -427,8 +448,8 @@ async function handleEnvVars(cwd, envVars) {
|
|
|
427
448
|
const lines = [];
|
|
428
449
|
if (existingEnv && !existingEnv.endsWith("\n")) lines.push("");
|
|
429
450
|
lines.push(...newEntries);
|
|
430
|
-
await
|
|
431
|
-
|
|
451
|
+
await writeFile5(envPath, existingEnv + lines.join("\n") + "\n");
|
|
452
|
+
p.log.success(`Wrote ${newEntries.length} variable(s) to ${pc2.cyan(".env")}`);
|
|
432
453
|
}
|
|
433
454
|
}
|
|
434
455
|
var init_env_writer = __esm({
|
|
@@ -438,7 +459,7 @@ var init_env_writer = __esm({
|
|
|
438
459
|
});
|
|
439
460
|
|
|
440
461
|
// src/installers/import-rewriter.ts
|
|
441
|
-
import { relative, join as
|
|
462
|
+
import { relative, join as join6 } from "path";
|
|
442
463
|
function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
443
464
|
const sourceAliasKey = TYPE_TO_ALIAS_KEY[fileType];
|
|
444
465
|
if (!sourceAliasKey) return content;
|
|
@@ -450,7 +471,7 @@ function rewriteKitnImports(content, fileType, fileName, aliases) {
|
|
|
450
471
|
return `${prefix}@kitn/${type}/${targetPath}${quote}`;
|
|
451
472
|
}
|
|
452
473
|
const targetDir = aliases[type];
|
|
453
|
-
const targetFile =
|
|
474
|
+
const targetFile = join6(targetDir, targetPath);
|
|
454
475
|
let rel = relative(sourceDir, targetFile);
|
|
455
476
|
rel = rel.split("\\").join("/");
|
|
456
477
|
if (!rel.startsWith(".")) {
|
|
@@ -474,76 +495,6 @@ var init_import_rewriter = __esm({
|
|
|
474
495
|
}
|
|
475
496
|
});
|
|
476
497
|
|
|
477
|
-
// src/installers/tsconfig-patcher.ts
|
|
478
|
-
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
479
|
-
import { join as join6 } from "path";
|
|
480
|
-
function stripJsonc(text3) {
|
|
481
|
-
return text3.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([}\]])/g, "$1");
|
|
482
|
-
}
|
|
483
|
-
function patchTsconfig(tsconfigContent, paths) {
|
|
484
|
-
const config = JSON.parse(stripJsonc(tsconfigContent));
|
|
485
|
-
if (!config.compilerOptions) {
|
|
486
|
-
config.compilerOptions = {};
|
|
487
|
-
}
|
|
488
|
-
if (!config.compilerOptions.paths) {
|
|
489
|
-
config.compilerOptions.paths = {};
|
|
490
|
-
}
|
|
491
|
-
for (const [key, value] of Object.entries(paths)) {
|
|
492
|
-
config.compilerOptions.paths[key] = value;
|
|
493
|
-
}
|
|
494
|
-
return JSON.stringify(config, null, 2) + "\n";
|
|
495
|
-
}
|
|
496
|
-
async function patchProjectTsconfig(projectDir, paths) {
|
|
497
|
-
const tsconfigPath = join6(projectDir, "tsconfig.json");
|
|
498
|
-
let content;
|
|
499
|
-
try {
|
|
500
|
-
content = await readFile5(tsconfigPath, "utf-8");
|
|
501
|
-
} catch {
|
|
502
|
-
content = "{}";
|
|
503
|
-
}
|
|
504
|
-
const patched = patchTsconfig(content, paths);
|
|
505
|
-
await writeFile5(tsconfigPath, patched);
|
|
506
|
-
}
|
|
507
|
-
var init_tsconfig_patcher = __esm({
|
|
508
|
-
"src/installers/tsconfig-patcher.ts"() {
|
|
509
|
-
"use strict";
|
|
510
|
-
}
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
// src/installers/barrel-manager.ts
|
|
514
|
-
function createBarrelFile() {
|
|
515
|
-
return `${BARREL_COMMENT}
|
|
516
|
-
${EXPORT_LINE}
|
|
517
|
-
`;
|
|
518
|
-
}
|
|
519
|
-
function addImportToBarrel(content, importPath) {
|
|
520
|
-
const importLine = `import "${importPath}";`;
|
|
521
|
-
if (content.includes(importLine)) return content;
|
|
522
|
-
const exportIndex = content.indexOf(EXPORT_LINE);
|
|
523
|
-
if (exportIndex === -1) {
|
|
524
|
-
return `${content.trimEnd()}
|
|
525
|
-
${importLine}
|
|
526
|
-
${EXPORT_LINE}
|
|
527
|
-
`;
|
|
528
|
-
}
|
|
529
|
-
const before = content.slice(0, exportIndex);
|
|
530
|
-
const after = content.slice(exportIndex);
|
|
531
|
-
return `${before}${importLine}
|
|
532
|
-
${after}`;
|
|
533
|
-
}
|
|
534
|
-
function removeImportFromBarrel(content, importPath) {
|
|
535
|
-
const importLine = `import "${importPath}";`;
|
|
536
|
-
return content.split("\n").filter((line) => line.trim() !== importLine).join("\n");
|
|
537
|
-
}
|
|
538
|
-
var EXPORT_LINE, BARREL_COMMENT;
|
|
539
|
-
var init_barrel_manager = __esm({
|
|
540
|
-
"src/installers/barrel-manager.ts"() {
|
|
541
|
-
"use strict";
|
|
542
|
-
EXPORT_LINE = 'export { registerWithPlugin } from "@kitnai/core";';
|
|
543
|
-
BARREL_COMMENT = "// Managed by kitn CLI \u2014 components auto-imported below";
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
|
|
547
498
|
// src/utils/hash.ts
|
|
548
499
|
import { createHash } from "crypto";
|
|
549
500
|
function contentHash(content) {
|
|
@@ -674,22 +625,22 @@ var add_exports = {};
|
|
|
674
625
|
__export(add_exports, {
|
|
675
626
|
addCommand: () => addCommand
|
|
676
627
|
});
|
|
677
|
-
import * as
|
|
678
|
-
import
|
|
628
|
+
import * as p2 from "@clack/prompts";
|
|
629
|
+
import pc3 from "picocolors";
|
|
679
630
|
import { join as join7 } from "path";
|
|
680
631
|
import { existsSync } from "fs";
|
|
681
632
|
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir3 } from "fs/promises";
|
|
682
633
|
import { relative as relative2 } from "path";
|
|
683
634
|
async function addCommand(components, opts) {
|
|
684
|
-
|
|
635
|
+
p2.intro(pc3.bgCyan(pc3.black(" kitn add ")));
|
|
685
636
|
const cwd = process.cwd();
|
|
686
637
|
const config = await readConfig(cwd);
|
|
687
638
|
if (!config) {
|
|
688
|
-
|
|
639
|
+
p2.log.error("No kitn.json found. Run `kitn init` first.");
|
|
689
640
|
process.exit(1);
|
|
690
641
|
}
|
|
691
642
|
if (components.length === 0) {
|
|
692
|
-
|
|
643
|
+
p2.log.error("Please specify at least one component to add.");
|
|
693
644
|
process.exit(1);
|
|
694
645
|
}
|
|
695
646
|
const resolvedComponents = components.map((c) => {
|
|
@@ -701,7 +652,7 @@ async function addCommand(components, opts) {
|
|
|
701
652
|
});
|
|
702
653
|
const refs = resolvedComponents.map(parseComponentRef);
|
|
703
654
|
const fetcher = new RegistryFetcher(config.registries);
|
|
704
|
-
const s =
|
|
655
|
+
const s = p2.spinner();
|
|
705
656
|
s.start("Resolving dependencies...");
|
|
706
657
|
let resolved;
|
|
707
658
|
try {
|
|
@@ -714,16 +665,16 @@ async function addCommand(components, opts) {
|
|
|
714
665
|
return fetcher.fetchItem(name, dir, ref.namespace, ref.version);
|
|
715
666
|
});
|
|
716
667
|
} catch (err) {
|
|
717
|
-
s.stop(
|
|
718
|
-
|
|
668
|
+
s.stop(pc3.red("Failed to resolve dependencies"));
|
|
669
|
+
p2.log.error(err.message);
|
|
719
670
|
process.exit(1);
|
|
720
671
|
}
|
|
721
672
|
s.stop(`Resolved ${resolved.length} component(s)`);
|
|
722
|
-
|
|
673
|
+
p2.log.info("Components to install:");
|
|
723
674
|
for (const item of resolved) {
|
|
724
675
|
const isExplicit = resolvedComponents.includes(item.name) || components.includes(item.name);
|
|
725
|
-
const label = isExplicit ? item.name : `${item.name} ${
|
|
726
|
-
|
|
676
|
+
const label = isExplicit ? item.name : `${item.name} ${pc3.dim("(dependency)")}`;
|
|
677
|
+
p2.log.message(` ${pc3.cyan(label)}`);
|
|
727
678
|
}
|
|
728
679
|
const created = [];
|
|
729
680
|
const updated = [];
|
|
@@ -752,15 +703,15 @@ async function addCommand(components, opts) {
|
|
|
752
703
|
} else {
|
|
753
704
|
const existing = await readExistingFile(targetPath);
|
|
754
705
|
const diff = generateDiff(relativePath, existing ?? "", file.content);
|
|
755
|
-
|
|
756
|
-
const action = await
|
|
706
|
+
p2.log.message(pc3.dim(diff));
|
|
707
|
+
const action = await p2.select({
|
|
757
708
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
758
709
|
options: [
|
|
759
710
|
{ value: "skip", label: "Keep local version" },
|
|
760
711
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
761
712
|
]
|
|
762
713
|
});
|
|
763
|
-
if (!
|
|
714
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
764
715
|
await writeComponentFile(targetPath, file.content);
|
|
765
716
|
updated.push(relativePath);
|
|
766
717
|
} else {
|
|
@@ -770,15 +721,6 @@ async function addCommand(components, opts) {
|
|
|
770
721
|
break;
|
|
771
722
|
}
|
|
772
723
|
}
|
|
773
|
-
if (item.tsconfig) {
|
|
774
|
-
const resolvedPaths = {};
|
|
775
|
-
const installDir = item.installDir ?? item.name;
|
|
776
|
-
for (const [key, values] of Object.entries(item.tsconfig)) {
|
|
777
|
-
resolvedPaths[key] = values.map((v) => `./${join7(baseDir2, installDir, v)}`);
|
|
778
|
-
}
|
|
779
|
-
await patchProjectTsconfig(cwd, resolvedPaths);
|
|
780
|
-
p3.log.info(`Patched tsconfig.json with paths: ${Object.keys(resolvedPaths).join(", ")}`);
|
|
781
|
-
}
|
|
782
724
|
const installed = config.installed ?? {};
|
|
783
725
|
const allContent = item.files.map((f) => f.content).join("\n");
|
|
784
726
|
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
@@ -792,6 +734,8 @@ async function addCommand(components, opts) {
|
|
|
792
734
|
};
|
|
793
735
|
config.installed = installed;
|
|
794
736
|
} else {
|
|
737
|
+
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
738
|
+
const ns = ref.namespace;
|
|
795
739
|
for (const file of item.files) {
|
|
796
740
|
const aliasKey = (() => {
|
|
797
741
|
switch (item.type) {
|
|
@@ -806,8 +750,9 @@ async function addCommand(components, opts) {
|
|
|
806
750
|
}
|
|
807
751
|
})();
|
|
808
752
|
const fileName = file.path.split("/").pop();
|
|
809
|
-
const
|
|
810
|
-
const
|
|
753
|
+
const installPath = getInstallPath(config, item.type, fileName, ns);
|
|
754
|
+
const targetPath = join7(cwd, installPath);
|
|
755
|
+
const relativePath = installPath;
|
|
811
756
|
const content = rewriteKitnImports(file.content, item.type, fileName, config.aliases);
|
|
812
757
|
const status = await checkFileStatus(targetPath, content);
|
|
813
758
|
switch (status) {
|
|
@@ -825,15 +770,15 @@ async function addCommand(components, opts) {
|
|
|
825
770
|
} else {
|
|
826
771
|
const existing = await readExistingFile(targetPath);
|
|
827
772
|
const diff = generateDiff(relativePath, existing ?? "", content);
|
|
828
|
-
|
|
829
|
-
const action = await
|
|
773
|
+
p2.log.message(pc3.dim(diff));
|
|
774
|
+
const action = await p2.select({
|
|
830
775
|
message: `${relativePath} already exists and differs. What to do?`,
|
|
831
776
|
options: [
|
|
832
777
|
{ value: "skip", label: "Keep local version" },
|
|
833
778
|
{ value: "overwrite", label: "Overwrite with registry version" }
|
|
834
779
|
]
|
|
835
780
|
});
|
|
836
|
-
if (!
|
|
781
|
+
if (!p2.isCancel(action) && action === "overwrite") {
|
|
837
782
|
await writeComponentFile(targetPath, content);
|
|
838
783
|
updated.push(relativePath);
|
|
839
784
|
} else {
|
|
@@ -848,27 +793,14 @@ async function addCommand(components, opts) {
|
|
|
848
793
|
const fn = f.path.split("/").pop();
|
|
849
794
|
return rewriteKitnImports(f.content, item.type, fn, config.aliases);
|
|
850
795
|
}).join("\n");
|
|
851
|
-
const
|
|
852
|
-
const installedKey = ref.namespace === "@kitn" ? item.name : `${ref.namespace}/${item.name}`;
|
|
796
|
+
const installedKey = ns === "@kitn" ? item.name : `${ns}/${item.name}`;
|
|
853
797
|
installed[installedKey] = {
|
|
854
|
-
registry:
|
|
798
|
+
registry: ns,
|
|
855
799
|
version: item.version ?? "1.0.0",
|
|
856
800
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
857
801
|
files: item.files.map((f) => {
|
|
858
|
-
const aliasKey = (() => {
|
|
859
|
-
switch (item.type) {
|
|
860
|
-
case "kitn:agent":
|
|
861
|
-
return "agents";
|
|
862
|
-
case "kitn:tool":
|
|
863
|
-
return "tools";
|
|
864
|
-
case "kitn:skill":
|
|
865
|
-
return "skills";
|
|
866
|
-
case "kitn:storage":
|
|
867
|
-
return "storage";
|
|
868
|
-
}
|
|
869
|
-
})();
|
|
870
802
|
const fileName = f.path.split("/").pop();
|
|
871
|
-
return
|
|
803
|
+
return getInstallPath(config, item.type, fileName, ns);
|
|
872
804
|
}),
|
|
873
805
|
hash: contentHash(allContent)
|
|
874
806
|
};
|
|
@@ -882,19 +814,11 @@ async function addCommand(components, opts) {
|
|
|
882
814
|
const barrelImports = [];
|
|
883
815
|
for (const item of resolved) {
|
|
884
816
|
if (!BARREL_ELIGIBLE.has(item.type)) continue;
|
|
817
|
+
const ref = refs.find((r) => r.name === item.name) ?? { namespace: "@kitn", name: item.name, version: void 0 };
|
|
885
818
|
for (const file of item.files) {
|
|
886
|
-
const aliasKey = (() => {
|
|
887
|
-
switch (item.type) {
|
|
888
|
-
case "kitn:agent":
|
|
889
|
-
return "agents";
|
|
890
|
-
case "kitn:tool":
|
|
891
|
-
return "tools";
|
|
892
|
-
case "kitn:skill":
|
|
893
|
-
return "skills";
|
|
894
|
-
}
|
|
895
|
-
})();
|
|
896
819
|
const fileName = file.path.split("/").pop();
|
|
897
|
-
const
|
|
820
|
+
const installPath = getInstallPath(config, item.type, fileName, ref.namespace);
|
|
821
|
+
const filePath = join7(cwd, installPath);
|
|
898
822
|
const importPath = "./" + relative2(barrelDir, filePath).replace(/\\/g, "/");
|
|
899
823
|
barrelImports.push(importPath);
|
|
900
824
|
}
|
|
@@ -912,21 +836,16 @@ async function addCommand(components, opts) {
|
|
|
912
836
|
barrelContent = addImportToBarrel(barrelContent, importPath);
|
|
913
837
|
}
|
|
914
838
|
await writeFile6(barrelPath, barrelContent);
|
|
915
|
-
|
|
839
|
+
p2.log.info(`Updated barrel file: ${join7(baseDir, "index.ts")}`);
|
|
916
840
|
if (!barrelExisted) {
|
|
917
|
-
|
|
841
|
+
p2.note(
|
|
918
842
|
[
|
|
919
|
-
`import {
|
|
920
|
-
`import { registerWithPlugin } from "./ai";`,
|
|
921
|
-
``,
|
|
922
|
-
`const plugin = createAIPlugin({`,
|
|
923
|
-
` model: (model) => yourProvider(model ?? "default-model"),`,
|
|
924
|
-
`});`,
|
|
843
|
+
`import { ai } from "./${baseDir}/plugin.js";`,
|
|
925
844
|
``,
|
|
926
|
-
`
|
|
927
|
-
`
|
|
845
|
+
`app.route("/api", ai.router);`,
|
|
846
|
+
`await ai.initialize();`
|
|
928
847
|
].join("\n"),
|
|
929
|
-
"Add this to your
|
|
848
|
+
"Add this to your server entry point"
|
|
930
849
|
);
|
|
931
850
|
}
|
|
932
851
|
}
|
|
@@ -940,58 +859,49 @@ async function addCommand(components, opts) {
|
|
|
940
859
|
installDependencies(pm, uniqueDeps, cwd);
|
|
941
860
|
s.stop("Dependencies installed");
|
|
942
861
|
} catch {
|
|
943
|
-
s.stop(
|
|
862
|
+
s.stop(pc3.yellow("Some dependencies failed to install"));
|
|
944
863
|
}
|
|
945
864
|
}
|
|
946
865
|
}
|
|
947
866
|
if (created.length > 0) {
|
|
948
|
-
|
|
949
|
-
for (const f of created)
|
|
867
|
+
p2.log.success(`Created ${created.length} file(s):`);
|
|
868
|
+
for (const f of created) p2.log.message(` ${pc3.green("+")} ${f}`);
|
|
950
869
|
}
|
|
951
870
|
if (updated.length > 0) {
|
|
952
|
-
|
|
953
|
-
for (const f of updated)
|
|
871
|
+
p2.log.success(`Updated ${updated.length} file(s):`);
|
|
872
|
+
for (const f of updated) p2.log.message(` ${pc3.yellow("~")} ${f}`);
|
|
954
873
|
}
|
|
955
874
|
if (skipped.length > 0) {
|
|
956
|
-
|
|
957
|
-
for (const f of skipped)
|
|
875
|
+
p2.log.info(`Skipped ${skipped.length} file(s):`);
|
|
876
|
+
for (const f of skipped) p2.log.message(` ${pc3.dim("-")} ${f}`);
|
|
958
877
|
}
|
|
959
878
|
const allEnvVars = collectEnvVars(resolved);
|
|
960
879
|
await handleEnvVars(cwd, allEnvVars);
|
|
961
880
|
for (const item of resolved) {
|
|
962
881
|
if (item.docs) {
|
|
963
|
-
|
|
882
|
+
p2.log.info(`${pc3.bold(item.name)}: ${item.docs}`);
|
|
964
883
|
}
|
|
965
884
|
}
|
|
966
885
|
const installedNames = new Set(resolved.map((r) => r.name));
|
|
967
886
|
const hints = [];
|
|
968
887
|
if (installedNames.has("core") && !installedNames.has(config.framework ?? "hono")) {
|
|
969
|
-
hints.push(`Run ${
|
|
888
|
+
hints.push(`Run ${pc3.cyan(`kitn add routes`)} to install the HTTP adapter.`);
|
|
970
889
|
}
|
|
971
890
|
const fw = config.framework ?? "hono";
|
|
972
891
|
if (installedNames.has(fw) || installedNames.has("core") && installedNames.has(fw)) {
|
|
973
|
-
hints.push(`
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
hints.push(pc4.dim(` const plugin = createAIPlugin({`));
|
|
981
|
-
hints.push(pc4.dim(` model: (model) => yourProvider(model ?? "default-model"),`));
|
|
982
|
-
hints.push(pc4.dim(` });`));
|
|
983
|
-
hints.push(pc4.dim(``));
|
|
984
|
-
hints.push(pc4.dim(` const app = new Hono();`));
|
|
985
|
-
hints.push(pc4.dim(` app.route("/api", plugin.app);`));
|
|
986
|
-
hints.push(pc4.dim(` await plugin.initialize();`));
|
|
987
|
-
hints.push("");
|
|
988
|
-
}
|
|
892
|
+
hints.push(`Configure your AI provider in ${pc3.bold(baseDir + "/plugin.ts")}, then add to your server:`);
|
|
893
|
+
hints.push("");
|
|
894
|
+
hints.push(pc3.dim(` import { ai } from "./${baseDir}/plugin.js";`));
|
|
895
|
+
hints.push(pc3.dim(``));
|
|
896
|
+
hints.push(pc3.dim(` app.route("/api", ai.router);`));
|
|
897
|
+
hints.push(pc3.dim(` await ai.initialize();`));
|
|
898
|
+
hints.push("");
|
|
989
899
|
}
|
|
990
900
|
if (hints.length > 0) {
|
|
991
|
-
|
|
992
|
-
for (const hint of hints)
|
|
901
|
+
p2.log.message(pc3.bold("\nNext steps:"));
|
|
902
|
+
for (const hint of hints) p2.log.message(hint);
|
|
993
903
|
}
|
|
994
|
-
|
|
904
|
+
p2.outro(pc3.green("Done!"));
|
|
995
905
|
}
|
|
996
906
|
var init_add = __esm({
|
|
997
907
|
"src/commands/add.ts"() {
|
|
@@ -1004,7 +914,6 @@ var init_add = __esm({
|
|
|
1004
914
|
init_dep_installer();
|
|
1005
915
|
init_env_writer();
|
|
1006
916
|
init_import_rewriter();
|
|
1007
|
-
init_tsconfig_patcher();
|
|
1008
917
|
init_barrel_manager();
|
|
1009
918
|
init_hash();
|
|
1010
919
|
init_parse_ref();
|
|
@@ -1012,6 +921,130 @@ var init_add = __esm({
|
|
|
1012
921
|
}
|
|
1013
922
|
});
|
|
1014
923
|
|
|
924
|
+
// src/commands/init.ts
|
|
925
|
+
var init_exports = {};
|
|
926
|
+
__export(init_exports, {
|
|
927
|
+
initCommand: () => initCommand
|
|
928
|
+
});
|
|
929
|
+
import * as p3 from "@clack/prompts";
|
|
930
|
+
import pc4 from "picocolors";
|
|
931
|
+
import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
|
|
932
|
+
import { join as join8 } from "path";
|
|
933
|
+
async function initCommand() {
|
|
934
|
+
p3.intro(pc4.bgCyan(pc4.black(" kitn init ")));
|
|
935
|
+
const cwd = process.cwd();
|
|
936
|
+
const existing = await readConfig(cwd);
|
|
937
|
+
if (existing) {
|
|
938
|
+
p3.log.warn("kitn.json already exists in this directory.");
|
|
939
|
+
const shouldContinue = await p3.confirm({
|
|
940
|
+
message: "Overwrite existing configuration?",
|
|
941
|
+
initialValue: false
|
|
942
|
+
});
|
|
943
|
+
if (p3.isCancel(shouldContinue) || !shouldContinue) {
|
|
944
|
+
p3.cancel("Init cancelled.");
|
|
945
|
+
process.exit(0);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
const runtime = await p3.select({
|
|
949
|
+
message: "Which runtime do you use?",
|
|
950
|
+
options: [
|
|
951
|
+
{ value: "bun", label: "Bun", hint: "recommended" },
|
|
952
|
+
{ value: "node", label: "Node.js" },
|
|
953
|
+
{ value: "deno", label: "Deno" }
|
|
954
|
+
]
|
|
955
|
+
});
|
|
956
|
+
if (p3.isCancel(runtime)) {
|
|
957
|
+
p3.cancel("Init cancelled.");
|
|
958
|
+
process.exit(0);
|
|
959
|
+
}
|
|
960
|
+
const base = await p3.text({
|
|
961
|
+
message: "Where should kitn components be installed?",
|
|
962
|
+
initialValue: "src/ai",
|
|
963
|
+
placeholder: "src/ai"
|
|
964
|
+
});
|
|
965
|
+
if (p3.isCancel(base)) {
|
|
966
|
+
p3.cancel("Init cancelled.");
|
|
967
|
+
process.exit(0);
|
|
968
|
+
}
|
|
969
|
+
const baseDir = base;
|
|
970
|
+
const config = {
|
|
971
|
+
runtime,
|
|
972
|
+
framework: "hono",
|
|
973
|
+
aliases: {
|
|
974
|
+
base: baseDir,
|
|
975
|
+
agents: `${baseDir}/agents`,
|
|
976
|
+
tools: `${baseDir}/tools`,
|
|
977
|
+
skills: `${baseDir}/skills`,
|
|
978
|
+
storage: `${baseDir}/storage`
|
|
979
|
+
},
|
|
980
|
+
registries: {
|
|
981
|
+
"@kitn": {
|
|
982
|
+
url: "https://kitn-ai.github.io/registry/r/{type}/{name}.json",
|
|
983
|
+
homepage: "https://kitn.ai",
|
|
984
|
+
description: "Official kitn AI agent components"
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const s = p3.spinner();
|
|
989
|
+
s.start("Writing kitn.json");
|
|
990
|
+
await writeConfig(cwd, config);
|
|
991
|
+
s.stop("Created kitn.json");
|
|
992
|
+
await patchProjectTsconfig(
|
|
993
|
+
cwd,
|
|
994
|
+
{ "@kitn/*": [`./${baseDir}/*`] },
|
|
995
|
+
["@kitn", "@kitnai"]
|
|
996
|
+
);
|
|
997
|
+
p3.log.info(`Patched tsconfig.json with path: ${pc4.bold("@kitn/*")}`);
|
|
998
|
+
p3.log.info("Installing core engine and routes...");
|
|
999
|
+
await addCommand(["core", "routes"], { overwrite: true });
|
|
1000
|
+
const aiDir = join8(cwd, baseDir);
|
|
1001
|
+
await mkdir4(aiDir, { recursive: true });
|
|
1002
|
+
const barrelPath = join8(aiDir, "index.ts");
|
|
1003
|
+
await writeFile7(barrelPath, createBarrelFile());
|
|
1004
|
+
const pluginPath = join8(aiDir, "plugin.ts");
|
|
1005
|
+
await writeFile7(pluginPath, PLUGIN_TEMPLATE);
|
|
1006
|
+
p3.log.success(`Created ${pc4.bold(baseDir + "/plugin.ts")} \u2014 configure your AI provider there`);
|
|
1007
|
+
p3.note(
|
|
1008
|
+
[
|
|
1009
|
+
`import { ai } from "./${baseDir}/plugin.js";`,
|
|
1010
|
+
``,
|
|
1011
|
+
`app.route("/api", ai.router);`,
|
|
1012
|
+
`await ai.initialize();`
|
|
1013
|
+
].join("\n"),
|
|
1014
|
+
"Add this to your server entry point:"
|
|
1015
|
+
);
|
|
1016
|
+
p3.outro("Done!");
|
|
1017
|
+
}
|
|
1018
|
+
var PLUGIN_TEMPLATE;
|
|
1019
|
+
var init_init = __esm({
|
|
1020
|
+
"src/commands/init.ts"() {
|
|
1021
|
+
"use strict";
|
|
1022
|
+
init_config();
|
|
1023
|
+
init_tsconfig_patcher();
|
|
1024
|
+
init_barrel_manager();
|
|
1025
|
+
init_add();
|
|
1026
|
+
PLUGIN_TEMPLATE = `import { createAIPlugin } from "@kitn/hono-routes";
|
|
1027
|
+
import { registerWithPlugin } from "./index.js";
|
|
1028
|
+
|
|
1029
|
+
export const ai = createAIPlugin({
|
|
1030
|
+
// To enable agent chat, add an AI provider:
|
|
1031
|
+
// https://sdk.vercel.ai/providers/ai-sdk-providers
|
|
1032
|
+
//
|
|
1033
|
+
// Example with OpenRouter (access to many models):
|
|
1034
|
+
// import { openrouter } from "@openrouter/ai-sdk-provider";
|
|
1035
|
+
// model: (id) => openrouter(id ?? "openai/gpt-4o-mini"),
|
|
1036
|
+
//
|
|
1037
|
+
// Example with OpenAI directly:
|
|
1038
|
+
// import { openai } from "@ai-sdk/openai";
|
|
1039
|
+
// model: (id) => openai(id ?? "gpt-4o-mini"),
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// Flush all auto-registered components into the plugin
|
|
1043
|
+
registerWithPlugin(ai);
|
|
1044
|
+
`;
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1015
1048
|
// src/commands/list.ts
|
|
1016
1049
|
var list_exports = {};
|
|
1017
1050
|
__export(list_exports, {
|
|
@@ -1066,6 +1099,7 @@ async function listCommand(typeFilter, opts) {
|
|
|
1066
1099
|
for (const item of allItems) {
|
|
1067
1100
|
const group = item.type.replace("kitn:", "");
|
|
1068
1101
|
if (resolvedType && group !== resolvedType) continue;
|
|
1102
|
+
if (!resolvedType && group === "package") continue;
|
|
1069
1103
|
if (!typeGroups.has(group)) typeGroups.set(group, []);
|
|
1070
1104
|
typeGroups.get(group).push(item);
|
|
1071
1105
|
}
|
|
@@ -1121,7 +1155,8 @@ async function listCommand(typeFilter, opts) {
|
|
|
1121
1155
|
console.log(pc5.dim(`
|
|
1122
1156
|
No ${resolvedType} components found.`));
|
|
1123
1157
|
}
|
|
1124
|
-
const
|
|
1158
|
+
const totalShown = [...typeGroups.values()].reduce((sum, items) => sum + items.length, 0);
|
|
1159
|
+
const parts = [`${installedCount} installed`, `${totalShown - installedCount} available`];
|
|
1125
1160
|
if (updateCount > 0) parts.push(pc5.yellow(`${updateCount} update${updateCount === 1 ? "" : "s"}`));
|
|
1126
1161
|
console.log(`
|
|
1127
1162
|
${pc5.dim(parts.join(" \xB7 "))}
|
|
@@ -1154,7 +1189,7 @@ __export(diff_exports, {
|
|
|
1154
1189
|
diffCommand: () => diffCommand
|
|
1155
1190
|
});
|
|
1156
1191
|
import * as p5 from "@clack/prompts";
|
|
1157
|
-
import { join as
|
|
1192
|
+
import { join as join9 } from "path";
|
|
1158
1193
|
async function diffCommand(componentName) {
|
|
1159
1194
|
const cwd = process.cwd();
|
|
1160
1195
|
const config = await readConfig(cwd);
|
|
@@ -1184,8 +1219,8 @@ async function diffCommand(componentName) {
|
|
|
1184
1219
|
for (const file of registryItem.files) {
|
|
1185
1220
|
if (indexItem.type === "kitn:package") {
|
|
1186
1221
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1187
|
-
const localPath =
|
|
1188
|
-
const relativePath =
|
|
1222
|
+
const localPath = join9(cwd, baseDir, file.path);
|
|
1223
|
+
const relativePath = join9(baseDir, file.path);
|
|
1189
1224
|
const localContent = await readExistingFile(localPath);
|
|
1190
1225
|
if (localContent === null) {
|
|
1191
1226
|
p5.log.warn(`${relativePath}: file missing locally`);
|
|
@@ -1209,7 +1244,7 @@ async function diffCommand(componentName) {
|
|
|
1209
1244
|
return "storage";
|
|
1210
1245
|
}
|
|
1211
1246
|
})();
|
|
1212
|
-
const localPath =
|
|
1247
|
+
const localPath = join9(cwd, config.aliases[aliasKey], fileName);
|
|
1213
1248
|
const localContent = await readExistingFile(localPath);
|
|
1214
1249
|
if (localContent === null) {
|
|
1215
1250
|
p5.log.warn(`${fileName}: file missing locally`);
|
|
@@ -1243,8 +1278,8 @@ __export(remove_exports, {
|
|
|
1243
1278
|
});
|
|
1244
1279
|
import * as p6 from "@clack/prompts";
|
|
1245
1280
|
import pc6 from "picocolors";
|
|
1246
|
-
import { join as
|
|
1247
|
-
import { unlink, readFile as readFile7, writeFile as
|
|
1281
|
+
import { join as join10, relative as relative3, dirname as dirname3 } from "path";
|
|
1282
|
+
import { unlink, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1248
1283
|
import { existsSync as existsSync2 } from "fs";
|
|
1249
1284
|
async function removeCommand(componentName) {
|
|
1250
1285
|
const cwd = process.cwd();
|
|
@@ -1272,15 +1307,15 @@ async function removeCommand(componentName) {
|
|
|
1272
1307
|
const deleted = [];
|
|
1273
1308
|
for (const filePath of installed.files) {
|
|
1274
1309
|
try {
|
|
1275
|
-
await unlink(
|
|
1310
|
+
await unlink(join10(cwd, filePath));
|
|
1276
1311
|
deleted.push(filePath);
|
|
1277
1312
|
} catch {
|
|
1278
1313
|
p6.log.warn(`Could not delete ${filePath} (may have been moved or renamed)`);
|
|
1279
1314
|
}
|
|
1280
1315
|
}
|
|
1281
1316
|
const baseDir = config.aliases.base ?? "src/ai";
|
|
1282
|
-
const barrelPath =
|
|
1283
|
-
const barrelDir =
|
|
1317
|
+
const barrelPath = join10(cwd, baseDir, "index.ts");
|
|
1318
|
+
const barrelDir = join10(cwd, baseDir);
|
|
1284
1319
|
const barrelEligibleDirs = /* @__PURE__ */ new Set([
|
|
1285
1320
|
config.aliases.agents,
|
|
1286
1321
|
config.aliases.tools,
|
|
@@ -1292,7 +1327,7 @@ async function removeCommand(componentName) {
|
|
|
1292
1327
|
for (const filePath of deleted) {
|
|
1293
1328
|
const fileDir = dirname3(filePath);
|
|
1294
1329
|
if (!barrelEligibleDirs.has(fileDir)) continue;
|
|
1295
|
-
const importPath = "./" + relative3(barrelDir,
|
|
1330
|
+
const importPath = "./" + relative3(barrelDir, join10(cwd, filePath)).replace(/\\/g, "/");
|
|
1296
1331
|
const updated = removeImportFromBarrel(barrelContent, importPath);
|
|
1297
1332
|
if (updated !== barrelContent) {
|
|
1298
1333
|
barrelContent = updated;
|
|
@@ -1300,8 +1335,8 @@ async function removeCommand(componentName) {
|
|
|
1300
1335
|
}
|
|
1301
1336
|
}
|
|
1302
1337
|
if (barrelChanged) {
|
|
1303
|
-
await
|
|
1304
|
-
p6.log.info(`Updated barrel file: ${
|
|
1338
|
+
await writeFile8(barrelPath, barrelContent);
|
|
1339
|
+
p6.log.info(`Updated barrel file: ${join10(baseDir, "index.ts")}`);
|
|
1305
1340
|
}
|
|
1306
1341
|
}
|
|
1307
1342
|
delete config.installed[installedKey];
|
|
@@ -1355,8 +1390,8 @@ var init_update = __esm({
|
|
|
1355
1390
|
});
|
|
1356
1391
|
|
|
1357
1392
|
// src/registry/build-output.ts
|
|
1358
|
-
import { readdir, writeFile as
|
|
1359
|
-
import { join as
|
|
1393
|
+
import { readdir, writeFile as writeFile9, mkdir as mkdir5, access as access4 } from "fs/promises";
|
|
1394
|
+
import { join as join11, resolve } from "path";
|
|
1360
1395
|
async function fileExists(path) {
|
|
1361
1396
|
try {
|
|
1362
1397
|
await access4(path);
|
|
@@ -1373,13 +1408,13 @@ async function walkForRegistryJson(dir) {
|
|
|
1373
1408
|
} catch {
|
|
1374
1409
|
return results;
|
|
1375
1410
|
}
|
|
1376
|
-
if (await fileExists(
|
|
1411
|
+
if (await fileExists(join11(dir, "registry.json"))) {
|
|
1377
1412
|
results.push(dir);
|
|
1378
1413
|
return results;
|
|
1379
1414
|
}
|
|
1380
1415
|
for (const entry of entries) {
|
|
1381
1416
|
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
1382
|
-
const subResults = await walkForRegistryJson(
|
|
1417
|
+
const subResults = await walkForRegistryJson(join11(dir, entry.name));
|
|
1383
1418
|
results.push(...subResults);
|
|
1384
1419
|
}
|
|
1385
1420
|
}
|
|
@@ -1391,7 +1426,7 @@ async function scanForComponents(cwd, paths) {
|
|
|
1391
1426
|
const results = [];
|
|
1392
1427
|
for (const p12 of paths) {
|
|
1393
1428
|
const absPath = resolve(resolvedCwd, p12);
|
|
1394
|
-
if (await fileExists(
|
|
1429
|
+
if (await fileExists(join11(absPath, "registry.json"))) {
|
|
1395
1430
|
results.push(absPath);
|
|
1396
1431
|
continue;
|
|
1397
1432
|
}
|
|
@@ -1403,8 +1438,8 @@ async function scanForComponents(cwd, paths) {
|
|
|
1403
1438
|
}
|
|
1404
1439
|
for (const entry of entries) {
|
|
1405
1440
|
if (entry.isDirectory()) {
|
|
1406
|
-
const subDir =
|
|
1407
|
-
if (await fileExists(
|
|
1441
|
+
const subDir = join11(absPath, entry.name);
|
|
1442
|
+
if (await fileExists(join11(subDir, "registry.json"))) {
|
|
1408
1443
|
results.push(subDir);
|
|
1409
1444
|
}
|
|
1410
1445
|
}
|
|
@@ -1429,21 +1464,21 @@ async function writeRegistryOutput(outputDir, items) {
|
|
|
1429
1464
|
const indexItems = [];
|
|
1430
1465
|
for (const item of items) {
|
|
1431
1466
|
const dir = typeToDir[item.type];
|
|
1432
|
-
const typeDir =
|
|
1433
|
-
await
|
|
1467
|
+
const typeDir = join11(resolvedOutput, dir);
|
|
1468
|
+
await mkdir5(typeDir, { recursive: true });
|
|
1434
1469
|
const itemJson = JSON.stringify(item, null, 2);
|
|
1435
|
-
const latestPath =
|
|
1470
|
+
const latestPath = join11(typeDir, `${item.name}.json`);
|
|
1436
1471
|
const latestRelative = `${dir}/${item.name}.json`;
|
|
1437
|
-
await
|
|
1472
|
+
await writeFile9(latestPath, itemJson, "utf-8");
|
|
1438
1473
|
written.push(latestRelative);
|
|
1439
1474
|
if (item.version) {
|
|
1440
1475
|
const versionedFilename = `${item.name}@${item.version}.json`;
|
|
1441
|
-
const versionedPath =
|
|
1476
|
+
const versionedPath = join11(typeDir, versionedFilename);
|
|
1442
1477
|
const versionedRelative = `${dir}/${versionedFilename}`;
|
|
1443
1478
|
if (await fileExists(versionedPath)) {
|
|
1444
1479
|
skipped.push(versionedRelative);
|
|
1445
1480
|
} else {
|
|
1446
|
-
await
|
|
1481
|
+
await writeFile9(versionedPath, itemJson, "utf-8");
|
|
1447
1482
|
written.push(versionedRelative);
|
|
1448
1483
|
}
|
|
1449
1484
|
}
|
|
@@ -1478,8 +1513,8 @@ async function writeRegistryOutput(outputDir, items) {
|
|
|
1478
1513
|
version: "1",
|
|
1479
1514
|
items: indexItems
|
|
1480
1515
|
};
|
|
1481
|
-
const indexPath =
|
|
1482
|
-
await
|
|
1516
|
+
const indexPath = join11(resolvedOutput, "registry.json");
|
|
1517
|
+
await writeFile9(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
1483
1518
|
written.push("registry.json");
|
|
1484
1519
|
return { written, skipped };
|
|
1485
1520
|
}
|
|
@@ -1502,7 +1537,7 @@ var init_build_output = __esm({
|
|
|
1502
1537
|
|
|
1503
1538
|
// src/registry/builder.ts
|
|
1504
1539
|
import { readFile as readFile9, readdir as readdir2 } from "fs/promises";
|
|
1505
|
-
import { join as
|
|
1540
|
+
import { join as join12, relative as relative5 } from "path";
|
|
1506
1541
|
function isExcludedDevDep(name) {
|
|
1507
1542
|
return EXCLUDED_DEV_DEPS.has(name) || name.startsWith("@types/");
|
|
1508
1543
|
}
|
|
@@ -1514,7 +1549,7 @@ async function readTsFiles(dir, baseDir, exclude) {
|
|
|
1514
1549
|
const results = [];
|
|
1515
1550
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
1516
1551
|
for (const entry of entries) {
|
|
1517
|
-
const fullPath =
|
|
1552
|
+
const fullPath = join12(dir, entry.name);
|
|
1518
1553
|
const relPath = relative5(baseDir, fullPath);
|
|
1519
1554
|
if (entry.isDirectory()) {
|
|
1520
1555
|
const nested = await readTsFiles(fullPath, baseDir, exclude);
|
|
@@ -1532,7 +1567,7 @@ async function readTsFiles(dir, baseDir, exclude) {
|
|
|
1532
1567
|
async function buildComponent(componentDir) {
|
|
1533
1568
|
let rawConfig;
|
|
1534
1569
|
try {
|
|
1535
|
-
rawConfig = await readFile9(
|
|
1570
|
+
rawConfig = await readFile9(join12(componentDir, "registry.json"), "utf-8");
|
|
1536
1571
|
} catch {
|
|
1537
1572
|
throw new Error(
|
|
1538
1573
|
`No registry.json found in ${componentDir}. Every component must have a registry.json file.`
|
|
@@ -1548,7 +1583,7 @@ async function buildComponent(componentDir) {
|
|
|
1548
1583
|
}
|
|
1549
1584
|
let pkg = null;
|
|
1550
1585
|
try {
|
|
1551
|
-
const rawPkg = await readFile9(
|
|
1586
|
+
const rawPkg = await readFile9(join12(componentDir, "package.json"), "utf-8");
|
|
1552
1587
|
pkg = JSON.parse(rawPkg);
|
|
1553
1588
|
} catch {
|
|
1554
1589
|
}
|
|
@@ -1605,7 +1640,7 @@ async function buildComponent(componentDir) {
|
|
|
1605
1640
|
let files;
|
|
1606
1641
|
if (isPackage) {
|
|
1607
1642
|
const sourceDir = config.sourceDir ?? "src";
|
|
1608
|
-
const sourcePath =
|
|
1643
|
+
const sourcePath = join12(componentDir, sourceDir);
|
|
1609
1644
|
const exclude = config.exclude ?? [];
|
|
1610
1645
|
let tsFiles;
|
|
1611
1646
|
try {
|
|
@@ -1628,7 +1663,7 @@ async function buildComponent(componentDir) {
|
|
|
1628
1663
|
}
|
|
1629
1664
|
files = await Promise.all(
|
|
1630
1665
|
config.files.map(async (filePath) => {
|
|
1631
|
-
const fullPath =
|
|
1666
|
+
const fullPath = join12(componentDir, filePath);
|
|
1632
1667
|
let content;
|
|
1633
1668
|
try {
|
|
1634
1669
|
content = await readFile9(fullPath, "utf-8");
|
|
@@ -1764,8 +1799,8 @@ __export(create_exports, {
|
|
|
1764
1799
|
});
|
|
1765
1800
|
import * as p9 from "@clack/prompts";
|
|
1766
1801
|
import pc8 from "picocolors";
|
|
1767
|
-
import { join as
|
|
1768
|
-
import { mkdir as
|
|
1802
|
+
import { join as join13 } from "path";
|
|
1803
|
+
import { mkdir as mkdir6, writeFile as writeFile10 } from "fs/promises";
|
|
1769
1804
|
function toCamelCase(str) {
|
|
1770
1805
|
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1771
1806
|
}
|
|
@@ -1791,7 +1826,7 @@ function generateRegistryJson(type, name, sourceFile) {
|
|
|
1791
1826
|
}
|
|
1792
1827
|
function generateAgentSource(name) {
|
|
1793
1828
|
const camel = toCamelCase(name);
|
|
1794
|
-
return `import { registerAgent } from "@
|
|
1829
|
+
return `import { registerAgent } from "@kitn/core";
|
|
1795
1830
|
|
|
1796
1831
|
const SYSTEM_PROMPT = "You are a helpful assistant.";
|
|
1797
1832
|
|
|
@@ -1805,7 +1840,7 @@ registerAgent({
|
|
|
1805
1840
|
}
|
|
1806
1841
|
function generateToolSource(name) {
|
|
1807
1842
|
const camel = toCamelCase(name);
|
|
1808
|
-
return `import { registerTool } from "@
|
|
1843
|
+
return `import { registerTool } from "@kitn/core";
|
|
1809
1844
|
import { tool } from "ai";
|
|
1810
1845
|
import { z } from "zod";
|
|
1811
1846
|
|
|
@@ -1842,7 +1877,7 @@ Describe what this skill does and how to use it.
|
|
|
1842
1877
|
}
|
|
1843
1878
|
function generateStorageSource(name) {
|
|
1844
1879
|
const camel = toCamelCase("create-" + name);
|
|
1845
|
-
return `import type { StorageProvider } from "@
|
|
1880
|
+
return `import type { StorageProvider } from "@kitn/core";
|
|
1846
1881
|
|
|
1847
1882
|
export function ${camel}(config?: Record<string, unknown>): StorageProvider {
|
|
1848
1883
|
// TODO: implement storage provider
|
|
@@ -1866,16 +1901,16 @@ async function createComponent(type, name, opts) {
|
|
|
1866
1901
|
);
|
|
1867
1902
|
}
|
|
1868
1903
|
const cwd = opts?.cwd ?? process.cwd();
|
|
1869
|
-
const dir =
|
|
1904
|
+
const dir = join13(cwd, name);
|
|
1870
1905
|
if (await dirExists(dir)) {
|
|
1871
1906
|
throw new Error(`Directory "${name}" already exists`);
|
|
1872
1907
|
}
|
|
1873
|
-
await
|
|
1908
|
+
await mkdir6(dir, { recursive: true });
|
|
1874
1909
|
const validType = type;
|
|
1875
1910
|
const sourceFile = validType === "skill" ? "README.md" : `${name}.ts`;
|
|
1876
1911
|
const registryJson = generateRegistryJson(validType, name, sourceFile);
|
|
1877
|
-
await
|
|
1878
|
-
|
|
1912
|
+
await writeFile10(
|
|
1913
|
+
join13(dir, "registry.json"),
|
|
1879
1914
|
JSON.stringify(registryJson, null, 2) + "\n"
|
|
1880
1915
|
);
|
|
1881
1916
|
let source;
|
|
@@ -1893,7 +1928,7 @@ async function createComponent(type, name, opts) {
|
|
|
1893
1928
|
source = generateStorageSource(name);
|
|
1894
1929
|
break;
|
|
1895
1930
|
}
|
|
1896
|
-
await
|
|
1931
|
+
await writeFile10(join13(dir, sourceFile), source);
|
|
1897
1932
|
return { dir, files: ["registry.json", sourceFile] };
|
|
1898
1933
|
}
|
|
1899
1934
|
async function createCommand(type, name) {
|
|
@@ -2063,10 +2098,19 @@ async function registryAddCommand(namespace, url, opts = {}) {
|
|
|
2063
2098
|
if (config.registries[namespace] && !opts.overwrite) {
|
|
2064
2099
|
throw new Error(`Registry '${namespace}' is already configured. Use --overwrite to replace.`);
|
|
2065
2100
|
}
|
|
2066
|
-
|
|
2101
|
+
if (opts.homepage || opts.description) {
|
|
2102
|
+
const entry = { url };
|
|
2103
|
+
if (opts.homepage) entry.homepage = opts.homepage;
|
|
2104
|
+
if (opts.description) entry.description = opts.description;
|
|
2105
|
+
config.registries[namespace] = entry;
|
|
2106
|
+
} else {
|
|
2107
|
+
config.registries[namespace] = url;
|
|
2108
|
+
}
|
|
2067
2109
|
await writeConfig(cwd, config);
|
|
2068
2110
|
p11.log.success(`Added registry ${pc10.bold(namespace)}`);
|
|
2069
2111
|
p11.log.message(pc10.dim(` ${url}`));
|
|
2112
|
+
if (opts.homepage) p11.log.message(pc10.dim(` Homepage: ${opts.homepage}`));
|
|
2113
|
+
if (opts.description) p11.log.message(pc10.dim(` ${opts.description}`));
|
|
2070
2114
|
}
|
|
2071
2115
|
async function registryRemoveCommand(namespace, opts = {}) {
|
|
2072
2116
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -2101,12 +2145,19 @@ async function registryListCommand(opts = {}) {
|
|
|
2101
2145
|
const cwd = opts.cwd ?? process.cwd();
|
|
2102
2146
|
const config = await readConfig(cwd);
|
|
2103
2147
|
if (!config) throw new Error("No kitn.json found. Run `kitn init` first.");
|
|
2104
|
-
const entries = Object.entries(config.registries).map(([namespace,
|
|
2148
|
+
const entries = Object.entries(config.registries).map(([namespace, value]) => {
|
|
2149
|
+
const url = getRegistryUrl(value);
|
|
2150
|
+
const homepage = typeof value === "object" ? value.homepage : void 0;
|
|
2151
|
+
const description = typeof value === "object" ? value.description : void 0;
|
|
2152
|
+
return { namespace, url, homepage, description };
|
|
2153
|
+
});
|
|
2105
2154
|
if (entries.length === 0) {
|
|
2106
2155
|
p11.log.message(pc10.dim(" No registries configured."));
|
|
2107
2156
|
} else {
|
|
2108
|
-
for (const { namespace, url } of entries) {
|
|
2157
|
+
for (const { namespace, url, homepage, description } of entries) {
|
|
2109
2158
|
p11.log.message(` ${pc10.bold(namespace.padEnd(16))} ${pc10.dim(url)}`);
|
|
2159
|
+
if (description) p11.log.message(` ${" ".repeat(16)} ${description}`);
|
|
2160
|
+
if (homepage) p11.log.message(` ${" ".repeat(16)} ${pc10.dim(homepage)}`);
|
|
2110
2161
|
}
|
|
2111
2162
|
}
|
|
2112
2163
|
return entries;
|
|
@@ -2196,7 +2247,7 @@ function startUpdateCheck(currentVersion) {
|
|
|
2196
2247
|
}
|
|
2197
2248
|
|
|
2198
2249
|
// src/index.ts
|
|
2199
|
-
var VERSION = true ? "0.1.
|
|
2250
|
+
var VERSION = true ? "0.1.12" : "0.0.0-dev";
|
|
2200
2251
|
var printUpdateNotice = startUpdateCheck(VERSION);
|
|
2201
2252
|
var program = new Command().name("kitn").description("Install AI agent components from the kitn registry").version(VERSION);
|
|
2202
2253
|
program.command("init").description("Initialize kitn in your project").action(async () => {
|
|
@@ -2236,7 +2287,7 @@ program.command("info").description("Show details about a component").argument("
|
|
|
2236
2287
|
await infoCommand2(component);
|
|
2237
2288
|
});
|
|
2238
2289
|
var registry = program.command("registry").description("Manage component registries");
|
|
2239
|
-
registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").action(async (namespace, url, opts) => {
|
|
2290
|
+
registry.command("add").description("Add a component registry").argument("<namespace>", "registry namespace (e.g. @myteam)").argument("<url>", "URL template with {type} and {name} placeholders").option("-o, --overwrite", "overwrite if namespace already exists").option("--homepage <url>", "registry homepage URL").option("--description <text>", "short description of the registry").action(async (namespace, url, opts) => {
|
|
2240
2291
|
const { registryAddCommand: registryAddCommand2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
2241
2292
|
await registryAddCommand2(namespace, url, opts);
|
|
2242
2293
|
});
|