@otomus/nerva-cli 0.1.1
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/dist/commands/dev.d.ts +42 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +257 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/generate.d.ts +24 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +162 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/list.d.ts +19 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +96 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +23 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +138 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/plugin.d.ts +79 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +319 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/test.d.ts +52 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +110 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/trace-ui.d.ts +16 -0
- package/dist/commands/trace-ui.d.ts.map +1 -0
- package/dist/commands/trace-ui.js +117 -0
- package/dist/commands/trace-ui.js.map +1 -0
- package/dist/config/nerva-yaml.d.ts +70 -0
- package/dist/config/nerva-yaml.d.ts.map +1 -0
- package/dist/config/nerva-yaml.js +185 -0
- package/dist/config/nerva-yaml.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/plugin-registry.d.ts +30 -0
- package/dist/registry/plugin-registry.d.ts.map +1 -0
- package/dist/registry/plugin-registry.js +134 -0
- package/dist/registry/plugin-registry.js.map +1 -0
- package/dist/templates/render.d.ts +36 -0
- package/dist/templates/render.d.ts.map +1 -0
- package/dist/templates/render.js +52 -0
- package/dist/templates/render.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva dev` command.
|
|
3
|
+
*
|
|
4
|
+
* Starts a development server with hot reload, watching agent/tool/middleware
|
|
5
|
+
* directories for changes and serving diagnostic endpoints.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { type Server } from 'node:http';
|
|
9
|
+
import { type FSWatcher } from 'node:fs';
|
|
10
|
+
import { type NervaConfig } from '../config/nerva-yaml.js';
|
|
11
|
+
/** A single trace event stored in memory. */
|
|
12
|
+
export interface TraceEvent {
|
|
13
|
+
timestamp: string;
|
|
14
|
+
channel: string;
|
|
15
|
+
message: string;
|
|
16
|
+
children?: TraceEvent[];
|
|
17
|
+
}
|
|
18
|
+
/** Runtime state for the dev server, exposed for testability. */
|
|
19
|
+
export interface DevServerHandle {
|
|
20
|
+
server: Server;
|
|
21
|
+
watchers: FSWatcher[];
|
|
22
|
+
traces: TraceEvent[];
|
|
23
|
+
config: NervaConfig | null;
|
|
24
|
+
close: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Starts the dev server and file watchers.
|
|
28
|
+
*
|
|
29
|
+
* Exported for testing — callers should call `handle.close()` to shut down.
|
|
30
|
+
*
|
|
31
|
+
* @param projectDir - Absolute path to project root
|
|
32
|
+
* @param port - Port number to listen on
|
|
33
|
+
* @returns DevServerHandle with server, watchers, and close method
|
|
34
|
+
*/
|
|
35
|
+
export declare function startDevServer(projectDir: string, port: number): Promise<DevServerHandle>;
|
|
36
|
+
/**
|
|
37
|
+
* Registers the `nerva dev` command with the CLI program.
|
|
38
|
+
*
|
|
39
|
+
* @param program - The root commander program
|
|
40
|
+
*/
|
|
41
|
+
export declare function registerDevCommand(program: Command): void;
|
|
42
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACjG,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAGhD,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAY5E,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;CACzB;AAOD,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AA0LD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CA0C1B;AAmBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAezD"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva dev` command.
|
|
3
|
+
*
|
|
4
|
+
* Starts a development server with hot reload, watching agent/tool/middleware
|
|
5
|
+
* directories for changes and serving diagnostic endpoints.
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from 'node:http';
|
|
8
|
+
import { watch } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { stat } from 'node:fs/promises';
|
|
11
|
+
import { readNervaConfig } from '../config/nerva-yaml.js';
|
|
12
|
+
import { buildTraceHtml } from './trace-ui.js';
|
|
13
|
+
/** Default port for the dev server. */
|
|
14
|
+
const DEFAULT_PORT = 3000;
|
|
15
|
+
/** Maximum number of trace entries kept in memory. */
|
|
16
|
+
const MAX_TRACES = 100;
|
|
17
|
+
/** Directories watched for file changes, relative to project root. */
|
|
18
|
+
const WATCHED_DIRS = ['agents', 'tools', 'middleware'];
|
|
19
|
+
/**
|
|
20
|
+
* Reads the request body as a UTF-8 string.
|
|
21
|
+
*
|
|
22
|
+
* @param req - Incoming HTTP request
|
|
23
|
+
* @returns Resolved body string
|
|
24
|
+
*/
|
|
25
|
+
function readBody(req) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const chunks = [];
|
|
28
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
29
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
30
|
+
req.on('error', reject);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Sends a JSON response with the given status code.
|
|
35
|
+
*
|
|
36
|
+
* @param res - Server response object
|
|
37
|
+
* @param statusCode - HTTP status code
|
|
38
|
+
* @param body - Object to serialize as JSON
|
|
39
|
+
*/
|
|
40
|
+
function sendJson(res, statusCode, body) {
|
|
41
|
+
const payload = JSON.stringify(body);
|
|
42
|
+
res.writeHead(statusCode, {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
45
|
+
});
|
|
46
|
+
res.end(payload);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Sends an HTML response with the given status code.
|
|
50
|
+
*
|
|
51
|
+
* @param res - Server response object
|
|
52
|
+
* @param statusCode - HTTP status code
|
|
53
|
+
* @param html - HTML string to send
|
|
54
|
+
*/
|
|
55
|
+
function sendHtml(res, statusCode, html) {
|
|
56
|
+
res.writeHead(statusCode, {
|
|
57
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
58
|
+
'Content-Length': Buffer.byteLength(html),
|
|
59
|
+
});
|
|
60
|
+
res.end(html);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Adds a trace event to the in-memory store, evicting the oldest if at capacity.
|
|
64
|
+
*
|
|
65
|
+
* @param traces - Mutable trace array
|
|
66
|
+
* @param event - Event to add
|
|
67
|
+
*/
|
|
68
|
+
function addTrace(traces, event) {
|
|
69
|
+
traces.push(event);
|
|
70
|
+
if (traces.length > MAX_TRACES) {
|
|
71
|
+
traces.shift();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Handles an incoming HTTP request by routing to the appropriate endpoint.
|
|
76
|
+
*
|
|
77
|
+
* @param req - Incoming request
|
|
78
|
+
* @param res - Server response
|
|
79
|
+
* @param handle - Dev server runtime state
|
|
80
|
+
*/
|
|
81
|
+
async function handleRequest(req, res, handle) {
|
|
82
|
+
const url = req.url ?? '/';
|
|
83
|
+
const method = req.method ?? 'GET';
|
|
84
|
+
if (method === 'GET' && url === '/health') {
|
|
85
|
+
sendJson(res, 200, { status: 'ok', uptime: process.uptime() });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (method === 'GET' && url === '/components') {
|
|
89
|
+
if (!handle.config) {
|
|
90
|
+
sendJson(res, 503, { error: 'Config not loaded' });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
sendJson(res, 200, {
|
|
94
|
+
agents: handle.config.agents,
|
|
95
|
+
tools: handle.config.tools,
|
|
96
|
+
middleware: handle.config.middleware,
|
|
97
|
+
routers: handle.config.routers,
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (method === 'GET' && url === '/traces') {
|
|
102
|
+
sendJson(res, 200, handle.traces);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (method === 'GET' && url === '/traces/ui') {
|
|
106
|
+
sendHtml(res, 200, buildTraceHtml());
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (method === 'POST' && url === '/chat') {
|
|
110
|
+
const body = await readBody(req);
|
|
111
|
+
let message = '';
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(body);
|
|
114
|
+
message = parsed.message ?? '';
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
sendJson(res, 400, { error: 'Invalid JSON body' });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
addTrace(handle.traces, {
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
channel: 'system',
|
|
123
|
+
message: `chat: ${message}`,
|
|
124
|
+
});
|
|
125
|
+
sendJson(res, 200, {
|
|
126
|
+
reply: `[mock] Received: ${message}`,
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
sendJson(res, 404, { error: 'Not found' });
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Attempts to reload nerva.yaml and logs what changed.
|
|
135
|
+
*
|
|
136
|
+
* @param projectDir - Project root directory
|
|
137
|
+
* @param handle - Dev server runtime state
|
|
138
|
+
* @param changedPath - The file path that triggered the reload
|
|
139
|
+
*/
|
|
140
|
+
async function reloadConfig(projectDir, handle, changedPath) {
|
|
141
|
+
try {
|
|
142
|
+
handle.config = await readNervaConfig(projectDir);
|
|
143
|
+
console.log(`[nerva dev] Reloaded config (changed: ${changedPath})`);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
147
|
+
console.error(`[nerva dev] Failed to reload config: ${message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Sets up file watchers on the standard component directories.
|
|
152
|
+
*
|
|
153
|
+
* @param projectDir - Project root directory
|
|
154
|
+
* @param handle - Dev server runtime state
|
|
155
|
+
* @returns Array of active FSWatcher instances
|
|
156
|
+
*/
|
|
157
|
+
async function setupWatchers(projectDir, handle) {
|
|
158
|
+
const watchers = [];
|
|
159
|
+
for (const dir of WATCHED_DIRS) {
|
|
160
|
+
const dirPath = join(projectDir, dir);
|
|
161
|
+
const exists = await stat(dirPath).then(() => true).catch(() => false);
|
|
162
|
+
if (!exists) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const watcher = watch(dirPath, { recursive: true }, (_eventType, filename) => {
|
|
166
|
+
const changed = filename ? join(dir, filename) : dir;
|
|
167
|
+
console.log(`[nerva dev] Change detected: ${changed}`);
|
|
168
|
+
void reloadConfig(projectDir, handle, changed);
|
|
169
|
+
});
|
|
170
|
+
watchers.push(watcher);
|
|
171
|
+
}
|
|
172
|
+
return watchers;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Starts the dev server and file watchers.
|
|
176
|
+
*
|
|
177
|
+
* Exported for testing — callers should call `handle.close()` to shut down.
|
|
178
|
+
*
|
|
179
|
+
* @param projectDir - Absolute path to project root
|
|
180
|
+
* @param port - Port number to listen on
|
|
181
|
+
* @returns DevServerHandle with server, watchers, and close method
|
|
182
|
+
*/
|
|
183
|
+
export async function startDevServer(projectDir, port) {
|
|
184
|
+
let config = null;
|
|
185
|
+
try {
|
|
186
|
+
config = await readNervaConfig(projectDir);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
console.warn('[nerva dev] No nerva.yaml found — starting without config');
|
|
190
|
+
}
|
|
191
|
+
const handle = {
|
|
192
|
+
server: null,
|
|
193
|
+
watchers: [],
|
|
194
|
+
traces: [],
|
|
195
|
+
config,
|
|
196
|
+
close: async () => {
|
|
197
|
+
for (const w of handle.watchers) {
|
|
198
|
+
w.close();
|
|
199
|
+
}
|
|
200
|
+
await new Promise((resolve, reject) => {
|
|
201
|
+
handle.server.close((err) => (err ? reject(err) : resolve()));
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
const server = createServer((req, res) => {
|
|
206
|
+
handleRequest(req, res, handle).catch((err) => {
|
|
207
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
208
|
+
console.error(`[nerva dev] Request error: ${message}`);
|
|
209
|
+
if (!res.headersSent) {
|
|
210
|
+
sendJson(res, 500, { error: 'Internal server error' });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
handle.server = server;
|
|
215
|
+
handle.watchers = await setupWatchers(projectDir, handle);
|
|
216
|
+
await new Promise((resolve) => {
|
|
217
|
+
server.listen(port, () => resolve());
|
|
218
|
+
});
|
|
219
|
+
return handle;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Prints the startup banner showing URL and watched directories.
|
|
223
|
+
*
|
|
224
|
+
* @param port - Port the server is listening on
|
|
225
|
+
* @param watchedDirs - Directories being watched for changes
|
|
226
|
+
*/
|
|
227
|
+
function printBanner(port, watchedDirs) {
|
|
228
|
+
console.log('');
|
|
229
|
+
console.log(' Nerva Dev Server');
|
|
230
|
+
console.log(' ================');
|
|
231
|
+
console.log(` URL: http://localhost:${port}`);
|
|
232
|
+
console.log(` Health: http://localhost:${port}/health`);
|
|
233
|
+
console.log(` Traces: http://localhost:${port}/traces/ui`);
|
|
234
|
+
console.log(` Watching: ${watchedDirs.join(', ')}`);
|
|
235
|
+
console.log('');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Registers the `nerva dev` command with the CLI program.
|
|
239
|
+
*
|
|
240
|
+
* @param program - The root commander program
|
|
241
|
+
*/
|
|
242
|
+
export function registerDevCommand(program) {
|
|
243
|
+
program
|
|
244
|
+
.command('dev')
|
|
245
|
+
.description('Start development server with hot reload')
|
|
246
|
+
.option('-p, --port <port>', 'Port to listen on', String(DEFAULT_PORT))
|
|
247
|
+
.action(async (options) => {
|
|
248
|
+
const port = parseInt(options.port, 10);
|
|
249
|
+
if (isNaN(port) || port < 0 || port > 65535) {
|
|
250
|
+
throw new Error(`Invalid port: "${options.port}"`);
|
|
251
|
+
}
|
|
252
|
+
const projectDir = process.cwd();
|
|
253
|
+
await startDevServer(projectDir, port);
|
|
254
|
+
printBanner(port, WATCHED_DIRS);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva generate <type> <name>` command (alias: `nerva g`).
|
|
3
|
+
*
|
|
4
|
+
* Generates a new component (agent, tool, middleware, or router),
|
|
5
|
+
* creates its test file, and registers it in nerva.yaml.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
/**
|
|
9
|
+
* Registers the `nerva generate` command (and `g` alias) with the CLI program.
|
|
10
|
+
*
|
|
11
|
+
* @param program - The root commander program
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerGenerateCommand(program: Command): void;
|
|
14
|
+
/**
|
|
15
|
+
* Executes the component generation logic.
|
|
16
|
+
*
|
|
17
|
+
* Creates the component file, its test file, and updates nerva.yaml.
|
|
18
|
+
*
|
|
19
|
+
* @param rawType - Component type string from CLI
|
|
20
|
+
* @param name - Component name
|
|
21
|
+
* @throws {Error} If type is invalid or nerva.yaml cannot be read
|
|
22
|
+
*/
|
|
23
|
+
export declare function executeGenerate(rawType: string, name: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmHpC;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAU9D;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqClF"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva generate <type> <name>` command (alias: `nerva g`).
|
|
3
|
+
*
|
|
4
|
+
* Generates a new component (agent, tool, middleware, or router),
|
|
5
|
+
* creates its test file, and registers it in nerva.yaml.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { join, dirname } from 'node:path';
|
|
9
|
+
import { readNervaConfig, writeNervaConfig, pluralizeType, } from '../config/nerva-yaml.js';
|
|
10
|
+
import { loadAndRender, toPascalCase } from '../templates/render.js';
|
|
11
|
+
/** Valid component types the generate command accepts. */
|
|
12
|
+
const VALID_TYPES = new Set(['agent', 'tool', 'middleware', 'router']);
|
|
13
|
+
/** File extension for each language. */
|
|
14
|
+
const EXTENSIONS = {
|
|
15
|
+
python: '.py',
|
|
16
|
+
typescript: '.ts',
|
|
17
|
+
};
|
|
18
|
+
/** Source directory prefix for each language. */
|
|
19
|
+
const SOURCE_PREFIX = {
|
|
20
|
+
python: '',
|
|
21
|
+
typescript: 'src/',
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the directory where a component file should be created.
|
|
25
|
+
*
|
|
26
|
+
* @param lang - Project language
|
|
27
|
+
* @param type - Component type
|
|
28
|
+
* @returns Relative directory path from project root
|
|
29
|
+
*/
|
|
30
|
+
function resolveComponentDir(lang, type) {
|
|
31
|
+
const prefix = SOURCE_PREFIX[lang] ?? '';
|
|
32
|
+
return `${prefix}${pluralizeType(type)}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the directory where a test file should be created.
|
|
36
|
+
*
|
|
37
|
+
* @returns Relative test directory path from project root
|
|
38
|
+
*/
|
|
39
|
+
function resolveTestDir() {
|
|
40
|
+
return 'tests';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Builds the template filename for a given component type and language.
|
|
44
|
+
*
|
|
45
|
+
* @param type - Component type
|
|
46
|
+
* @param lang - Project language
|
|
47
|
+
* @returns Template filename (e.g. "agent.py.tmpl")
|
|
48
|
+
*/
|
|
49
|
+
function templateFileName(type, lang) {
|
|
50
|
+
const ext = EXTENSIONS[lang] ?? '.py';
|
|
51
|
+
return `${type}${ext}.tmpl`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Builds the test template filename for a given language.
|
|
55
|
+
*
|
|
56
|
+
* @param lang - Project language
|
|
57
|
+
* @returns Test template filename
|
|
58
|
+
*/
|
|
59
|
+
function testTemplateFileName(lang) {
|
|
60
|
+
const ext = EXTENSIONS[lang] ?? '.py';
|
|
61
|
+
return `test${ext}.tmpl`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds template variables from component name and type.
|
|
65
|
+
*
|
|
66
|
+
* @param name - Component name (kebab-case)
|
|
67
|
+
* @param type - Component type
|
|
68
|
+
* @returns Template variable map
|
|
69
|
+
*/
|
|
70
|
+
function buildTemplateVars(name, type) {
|
|
71
|
+
return {
|
|
72
|
+
name,
|
|
73
|
+
class_name: toPascalCase(name),
|
|
74
|
+
type,
|
|
75
|
+
description: `A ${type} named ${name}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Registers a new component in the nerva.yaml configuration.
|
|
80
|
+
*
|
|
81
|
+
* @param config - Current config
|
|
82
|
+
* @param type - Component type to register
|
|
83
|
+
* @param entry - The component entry to add
|
|
84
|
+
* @returns Updated config with the new component
|
|
85
|
+
*/
|
|
86
|
+
function addComponentToConfig(config, type, entry) {
|
|
87
|
+
const key = pluralizeType(type);
|
|
88
|
+
const existing = config[key];
|
|
89
|
+
const isDuplicate = existing.some((e) => e.name === entry.name);
|
|
90
|
+
if (isDuplicate) {
|
|
91
|
+
return config;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
...config,
|
|
95
|
+
[key]: [...existing, entry],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Registers the `nerva generate` command (and `g` alias) with the CLI program.
|
|
100
|
+
*
|
|
101
|
+
* @param program - The root commander program
|
|
102
|
+
*/
|
|
103
|
+
export function registerGenerateCommand(program) {
|
|
104
|
+
const cmd = program
|
|
105
|
+
.command('generate')
|
|
106
|
+
.alias('g')
|
|
107
|
+
.description('Generate a new component (agent, tool, middleware, router)')
|
|
108
|
+
.argument('<type>', 'Component type: agent, tool, middleware, router')
|
|
109
|
+
.argument('<name>', 'Component name (kebab-case)')
|
|
110
|
+
.action(async (type, name) => {
|
|
111
|
+
await executeGenerate(type, name);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Executes the component generation logic.
|
|
116
|
+
*
|
|
117
|
+
* Creates the component file, its test file, and updates nerva.yaml.
|
|
118
|
+
*
|
|
119
|
+
* @param rawType - Component type string from CLI
|
|
120
|
+
* @param name - Component name
|
|
121
|
+
* @throws {Error} If type is invalid or nerva.yaml cannot be read
|
|
122
|
+
*/
|
|
123
|
+
export async function executeGenerate(rawType, name) {
|
|
124
|
+
if (!VALID_TYPES.has(rawType)) {
|
|
125
|
+
throw new Error(`Unknown component type: "${rawType}". Valid types: ${[...VALID_TYPES].join(', ')}`);
|
|
126
|
+
}
|
|
127
|
+
const type = rawType;
|
|
128
|
+
const projectDir = process.cwd();
|
|
129
|
+
const config = await readNervaConfig(projectDir);
|
|
130
|
+
const lang = config.lang;
|
|
131
|
+
const ext = EXTENSIONS[lang];
|
|
132
|
+
const vars = buildTemplateVars(name, type);
|
|
133
|
+
// Generate component file
|
|
134
|
+
const componentDir = resolveComponentDir(lang, type);
|
|
135
|
+
const componentPath = join(componentDir, `${name}${ext}`);
|
|
136
|
+
const componentContent = await loadAndRender(lang, templateFileName(type, lang), vars);
|
|
137
|
+
await ensureDirForFile(join(projectDir, componentPath));
|
|
138
|
+
await writeFile(join(projectDir, componentPath), componentContent, 'utf-8');
|
|
139
|
+
// Generate test file
|
|
140
|
+
const testDir = resolveTestDir();
|
|
141
|
+
const testFileName = lang === 'python' ? `test_${name}${ext}` : `${name}.test${ext}`;
|
|
142
|
+
const testPath = join(testDir, testFileName);
|
|
143
|
+
const testContent = await loadAndRender(lang, testTemplateFileName(lang), vars);
|
|
144
|
+
await ensureDirForFile(join(projectDir, testPath));
|
|
145
|
+
await writeFile(join(projectDir, testPath), testContent, 'utf-8');
|
|
146
|
+
// Update nerva.yaml
|
|
147
|
+
const entry = { name, path: componentPath };
|
|
148
|
+
const updatedConfig = addComponentToConfig(config, type, entry);
|
|
149
|
+
await writeNervaConfig(projectDir, updatedConfig);
|
|
150
|
+
console.log(`Generated ${type}: ${componentPath}`);
|
|
151
|
+
console.log(`Generated test: ${testPath}`);
|
|
152
|
+
console.log(`Updated nerva.yaml`);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Ensures the parent directory of a file path exists.
|
|
156
|
+
*
|
|
157
|
+
* @param filePath - Absolute file path
|
|
158
|
+
*/
|
|
159
|
+
async function ensureDirForFile(filePath) {
|
|
160
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva list` command.
|
|
3
|
+
*
|
|
4
|
+
* Reads nerva.yaml and prints all registered components in a table.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
/**
|
|
8
|
+
* Registers the `nerva list` command with the CLI program.
|
|
9
|
+
*
|
|
10
|
+
* @param program - The root commander program
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerListCommand(program: Command): void;
|
|
13
|
+
/**
|
|
14
|
+
* Executes the list logic: reads nerva.yaml and prints component tables.
|
|
15
|
+
*
|
|
16
|
+
* @throws {Error} If nerva.yaml cannot be read
|
|
17
|
+
*/
|
|
18
|
+
export declare function executeList(): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiEpC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAQ1D;AAED;;;;GAIG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAyBjD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva list` command.
|
|
3
|
+
*
|
|
4
|
+
* Reads nerva.yaml and prints all registered components in a table.
|
|
5
|
+
*/
|
|
6
|
+
import { readNervaConfig } from '../config/nerva-yaml.js';
|
|
7
|
+
/** Section labels for display. */
|
|
8
|
+
const SECTION_LABELS = {
|
|
9
|
+
agent: 'Agents',
|
|
10
|
+
tool: 'Tools',
|
|
11
|
+
middleware: 'Middleware',
|
|
12
|
+
router: 'Routers',
|
|
13
|
+
};
|
|
14
|
+
/** All component types in display order. */
|
|
15
|
+
const DISPLAY_ORDER = ['agent', 'tool', 'middleware', 'router'];
|
|
16
|
+
/** Minimum column widths for the table. */
|
|
17
|
+
const MIN_NAME_WIDTH = 20;
|
|
18
|
+
const MIN_PATH_WIDTH = 40;
|
|
19
|
+
/**
|
|
20
|
+
* Formats a list of component entries as padded table rows.
|
|
21
|
+
*
|
|
22
|
+
* @param entries - Components to format
|
|
23
|
+
* @param nameWidth - Column width for the name field
|
|
24
|
+
* @param pathWidth - Column width for the path field
|
|
25
|
+
* @returns Formatted row strings
|
|
26
|
+
*/
|
|
27
|
+
function formatEntries(entries, nameWidth, pathWidth) {
|
|
28
|
+
if (entries.length === 0) {
|
|
29
|
+
return [' (none)'];
|
|
30
|
+
}
|
|
31
|
+
return entries.map((entry) => {
|
|
32
|
+
const name = entry.name.padEnd(nameWidth);
|
|
33
|
+
const path = entry.path.padEnd(pathWidth);
|
|
34
|
+
const desc = entry.description ?? '';
|
|
35
|
+
return ` ${name} ${path} ${desc}`;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Computes the widest name and path across all entries.
|
|
40
|
+
*
|
|
41
|
+
* @param sections - Map of component type to entries
|
|
42
|
+
* @returns Tuple of [nameWidth, pathWidth]
|
|
43
|
+
*/
|
|
44
|
+
function computeColumnWidths(sections) {
|
|
45
|
+
let maxName = MIN_NAME_WIDTH;
|
|
46
|
+
let maxPath = MIN_PATH_WIDTH;
|
|
47
|
+
for (const entries of Object.values(sections)) {
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
maxName = Math.max(maxName, entry.name.length);
|
|
50
|
+
maxPath = Math.max(maxPath, entry.path.length);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [maxName, maxPath];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Registers the `nerva list` command with the CLI program.
|
|
57
|
+
*
|
|
58
|
+
* @param program - The root commander program
|
|
59
|
+
*/
|
|
60
|
+
export function registerListCommand(program) {
|
|
61
|
+
program
|
|
62
|
+
.command('list')
|
|
63
|
+
.alias('ls')
|
|
64
|
+
.description('List all registered components')
|
|
65
|
+
.action(async () => {
|
|
66
|
+
await executeList();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Executes the list logic: reads nerva.yaml and prints component tables.
|
|
71
|
+
*
|
|
72
|
+
* @throws {Error} If nerva.yaml cannot be read
|
|
73
|
+
*/
|
|
74
|
+
export async function executeList() {
|
|
75
|
+
const projectDir = process.cwd();
|
|
76
|
+
const config = await readNervaConfig(projectDir);
|
|
77
|
+
const sections = {
|
|
78
|
+
agent: config.agents,
|
|
79
|
+
tool: config.tools,
|
|
80
|
+
middleware: config.middleware,
|
|
81
|
+
router: config.routers,
|
|
82
|
+
};
|
|
83
|
+
console.log(`\nProject: ${config.name} (${config.lang})\n`);
|
|
84
|
+
const [nameWidth, pathWidth] = computeColumnWidths(sections);
|
|
85
|
+
for (const type of DISPLAY_ORDER) {
|
|
86
|
+
const label = SECTION_LABELS[type];
|
|
87
|
+
const entries = sections[type];
|
|
88
|
+
console.log(`${label}:`);
|
|
89
|
+
const rows = formatEntries(entries, nameWidth, pathWidth);
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
console.log(row);
|
|
92
|
+
}
|
|
93
|
+
console.log('');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAA2C,MAAM,yBAAyB,CAAC;AAEnG,kCAAkC;AAClC,MAAM,cAAc,GAAkC;IACpD,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,4CAA4C;AAC5C,MAAM,aAAa,GAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;AAE1F,2CAA2C;AAC3C,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B;;;;;;;GAOG;AACH,SAAS,aAAa,CACpB,OAAyB,EACzB,SAAiB,EACjB,SAAiB;IAEjB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;QACrC,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAC1B,QAA0C;IAE1C,IAAI,OAAO,GAAG,cAAc,CAAC;IAC7B,IAAI,OAAO,GAAG,cAAc,CAAC;IAE7B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC/C,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,KAAK,CAAC,IAAI,CAAC;SACX,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,WAAW,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAqC;QACjD,KAAK,EAAE,MAAM,CAAC,MAAM;QACpB,IAAI,EAAE,MAAM,CAAC,KAAK;QAClB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,MAAM,EAAE,MAAM,CAAC,OAAO;KACvB,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;IAE5D,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAE7D,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;QACzB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC1D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva new <name>` command.
|
|
3
|
+
*
|
|
4
|
+
* Scaffolds a new Nerva project directory with the correct structure
|
|
5
|
+
* for either Python or TypeScript.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { type ProjectLang } from '../config/nerva-yaml.js';
|
|
9
|
+
/**
|
|
10
|
+
* Registers the `nerva new` command with the CLI program.
|
|
11
|
+
*
|
|
12
|
+
* @param program - The root commander program
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerNewCommand(program: Command): void;
|
|
15
|
+
/**
|
|
16
|
+
* Executes the project scaffolding logic.
|
|
17
|
+
*
|
|
18
|
+
* @param name - Project name (used as directory name)
|
|
19
|
+
* @param lang - Target language
|
|
20
|
+
* @throws {Error} If the language is unsupported
|
|
21
|
+
*/
|
|
22
|
+
export declare function executeNew(name: string, lang: ProjectLang): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=new.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAA0C,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsInG;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CASzD;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB/E"}
|