@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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nerva test` command.
|
|
3
|
+
*
|
|
4
|
+
* Detects the project language from nerva.yaml and runs the appropriate
|
|
5
|
+
* test runner (pytest for Python, vitest for TypeScript).
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from 'node:child_process';
|
|
8
|
+
import { readNervaConfig } from '../config/nerva-yaml.js';
|
|
9
|
+
/** Test runner command and arguments for each supported language. */
|
|
10
|
+
const RUNNER_CONFIG = {
|
|
11
|
+
python: { cmd: 'python', args: ['-m', 'pytest'] },
|
|
12
|
+
typescript: { cmd: 'npx', args: ['vitest', 'run'] },
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Builds the full argument list for the test runner, including optional coverage flag.
|
|
16
|
+
*
|
|
17
|
+
* @param lang - Project language
|
|
18
|
+
* @param coverage - Whether to enable coverage reporting
|
|
19
|
+
* @returns Tuple of [command, args]
|
|
20
|
+
*/
|
|
21
|
+
export function buildTestCommand(lang, coverage) {
|
|
22
|
+
const config = RUNNER_CONFIG[lang];
|
|
23
|
+
if (!config) {
|
|
24
|
+
throw new Error(`Unsupported language for testing: "${lang}"`);
|
|
25
|
+
}
|
|
26
|
+
const args = [...config.args];
|
|
27
|
+
if (coverage) {
|
|
28
|
+
args.push('--coverage');
|
|
29
|
+
}
|
|
30
|
+
return { cmd: config.cmd, args };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detects the project language from nerva.yaml in the given directory.
|
|
34
|
+
*
|
|
35
|
+
* @param projectDir - Absolute path to project root
|
|
36
|
+
* @returns Detected project language
|
|
37
|
+
* @throws {Error} If nerva.yaml is missing or invalid
|
|
38
|
+
*/
|
|
39
|
+
export async function detectLanguage(projectDir) {
|
|
40
|
+
const config = await readNervaConfig(projectDir);
|
|
41
|
+
return config.lang;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Runs the test command as a child process and returns its exit code.
|
|
45
|
+
*
|
|
46
|
+
* @param projectDir - Working directory for the test runner
|
|
47
|
+
* @param cmd - Command to execute
|
|
48
|
+
* @param args - Arguments to pass
|
|
49
|
+
* @returns Exit code from the test runner (0 = success)
|
|
50
|
+
*/
|
|
51
|
+
export function runTestProcess(projectDir, cmd, args) {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
const child = spawn(cmd, args, {
|
|
54
|
+
cwd: projectDir,
|
|
55
|
+
stdio: 'inherit',
|
|
56
|
+
shell: true,
|
|
57
|
+
});
|
|
58
|
+
child.on('close', (code) => {
|
|
59
|
+
resolve(code ?? 1);
|
|
60
|
+
});
|
|
61
|
+
child.on('error', (err) => {
|
|
62
|
+
console.error(`[nerva test] Failed to start test runner: ${err.message}`);
|
|
63
|
+
resolve(1);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Executes the test command: detects language, runs the appropriate test runner,
|
|
69
|
+
* and prints a trace summary.
|
|
70
|
+
*
|
|
71
|
+
* @param projectDir - Absolute path to project root
|
|
72
|
+
* @param coverage - Whether to enable coverage
|
|
73
|
+
* @returns Exit code from the test runner
|
|
74
|
+
*/
|
|
75
|
+
export async function executeTest(projectDir, coverage) {
|
|
76
|
+
const lang = await detectLanguage(projectDir);
|
|
77
|
+
const { cmd, args } = buildTestCommand(lang, coverage);
|
|
78
|
+
console.log(`[nerva test] Detected language: ${lang}`);
|
|
79
|
+
console.log(`[nerva test] Running: ${cmd} ${args.join(' ')}`);
|
|
80
|
+
console.log('');
|
|
81
|
+
const startTime = Date.now();
|
|
82
|
+
const exitCode = await runTestProcess(projectDir, cmd, args);
|
|
83
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(' Nerva Test Summary');
|
|
86
|
+
console.log(' ==================');
|
|
87
|
+
console.log(` Language: ${lang}`);
|
|
88
|
+
console.log(` Runner: ${cmd} ${args.join(' ')}`);
|
|
89
|
+
console.log(` Duration: ${elapsed}s`);
|
|
90
|
+
console.log(` Exit: ${exitCode === 0 ? 'PASS' : 'FAIL'} (code ${exitCode})`);
|
|
91
|
+
console.log('');
|
|
92
|
+
return exitCode;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Registers the `nerva test` command with the CLI program.
|
|
96
|
+
*
|
|
97
|
+
* @param program - The root commander program
|
|
98
|
+
*/
|
|
99
|
+
export function registerTestCommand(program) {
|
|
100
|
+
program
|
|
101
|
+
.command('test')
|
|
102
|
+
.description('Run tests using the language-appropriate test runner')
|
|
103
|
+
.option('--coverage', 'Enable coverage reporting', false)
|
|
104
|
+
.action(async (options) => {
|
|
105
|
+
const projectDir = process.cwd();
|
|
106
|
+
const exitCode = await executeTest(projectDir, options.coverage);
|
|
107
|
+
process.exitCode = exitCode;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace UI HTML page served by the dev server.
|
|
3
|
+
*
|
|
4
|
+
* Returns a self-contained HTML page that displays trace events as a tree,
|
|
5
|
+
* auto-refreshing every 2 seconds from the `/traces` endpoint.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Builds the trace UI HTML page as a string.
|
|
9
|
+
*
|
|
10
|
+
* The page fetches `/traces` every 2 seconds and renders spans
|
|
11
|
+
* as an indented, color-coded tree in a monospace font.
|
|
12
|
+
*
|
|
13
|
+
* @returns Complete HTML document string
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildTraceHtml(): string;
|
|
16
|
+
//# sourceMappingURL=trace-ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-ui.d.ts","sourceRoot":"","sources":["../../src/commands/trace-ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAqGvC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace UI HTML page served by the dev server.
|
|
3
|
+
*
|
|
4
|
+
* Returns a self-contained HTML page that displays trace events as a tree,
|
|
5
|
+
* auto-refreshing every 2 seconds from the `/traces` endpoint.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Builds the trace UI HTML page as a string.
|
|
9
|
+
*
|
|
10
|
+
* The page fetches `/traces` every 2 seconds and renders spans
|
|
11
|
+
* as an indented, color-coded tree in a monospace font.
|
|
12
|
+
*
|
|
13
|
+
* @returns Complete HTML document string
|
|
14
|
+
*/
|
|
15
|
+
export function buildTraceHtml() {
|
|
16
|
+
return `<!DOCTYPE html>
|
|
17
|
+
<html lang="en">
|
|
18
|
+
<head>
|
|
19
|
+
<meta charset="UTF-8">
|
|
20
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
21
|
+
<title>Nerva Trace UI</title>
|
|
22
|
+
<style>
|
|
23
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
24
|
+
body {
|
|
25
|
+
font-family: "SF Mono", "Menlo", "Consolas", monospace;
|
|
26
|
+
font-size: 13px;
|
|
27
|
+
background: #1a1a2e;
|
|
28
|
+
color: #e0e0e0;
|
|
29
|
+
padding: 24px;
|
|
30
|
+
}
|
|
31
|
+
h1 { color: #7fdbca; margin-bottom: 16px; font-size: 18px; }
|
|
32
|
+
.status { color: #888; margin-bottom: 16px; font-size: 11px; }
|
|
33
|
+
.trace-list { list-style: none; }
|
|
34
|
+
.trace-item { margin-bottom: 2px; white-space: pre; }
|
|
35
|
+
.channel-agent { color: #c792ea; }
|
|
36
|
+
.channel-tool { color: #82aaff; }
|
|
37
|
+
.channel-middleware { color: #ffcb6b; }
|
|
38
|
+
.channel-router { color: #f78c6c; }
|
|
39
|
+
.channel-system { color: #89ddff; }
|
|
40
|
+
.channel-unknown { color: #a0a0a0; }
|
|
41
|
+
.timestamp { color: #666; }
|
|
42
|
+
.empty { color: #555; font-style: italic; margin-top: 12px; }
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<h1>Nerva Trace UI</h1>
|
|
47
|
+
<div class="status" id="status">Connecting...</div>
|
|
48
|
+
<ul class="trace-list" id="traces"></ul>
|
|
49
|
+
|
|
50
|
+
<script>
|
|
51
|
+
const CHANNEL_COLORS = {
|
|
52
|
+
agent: 'channel-agent',
|
|
53
|
+
tool: 'channel-tool',
|
|
54
|
+
middleware: 'channel-middleware',
|
|
55
|
+
router: 'channel-router',
|
|
56
|
+
system: 'channel-system',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function channelClass(channel) {
|
|
60
|
+
return CHANNEL_COLORS[channel] || 'channel-unknown';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function escapeHtml(text) {
|
|
64
|
+
const div = document.createElement('div');
|
|
65
|
+
div.textContent = text;
|
|
66
|
+
return div.innerHTML;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function renderTrace(trace, depth) {
|
|
70
|
+
const indent = ' '.repeat(depth);
|
|
71
|
+
const ch = trace.channel || 'unknown';
|
|
72
|
+
const ts = trace.timestamp ? new Date(trace.timestamp).toISOString().slice(11, 23) : '';
|
|
73
|
+
const msg = escapeHtml(trace.message || trace.event || JSON.stringify(trace));
|
|
74
|
+
|
|
75
|
+
let html = '<li class="trace-item">';
|
|
76
|
+
html += '<span class="timestamp">' + escapeHtml(ts) + '</span> ';
|
|
77
|
+
html += indent;
|
|
78
|
+
html += '<span class="' + channelClass(ch) + '">[' + escapeHtml(ch) + ']</span> ';
|
|
79
|
+
html += msg;
|
|
80
|
+
html += '</li>';
|
|
81
|
+
|
|
82
|
+
if (Array.isArray(trace.children)) {
|
|
83
|
+
for (const child of trace.children) {
|
|
84
|
+
html += renderTrace(child, depth + 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return html;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function refresh() {
|
|
92
|
+
try {
|
|
93
|
+
const res = await fetch('/traces');
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
const list = document.getElementById('traces');
|
|
96
|
+
const status = document.getElementById('status');
|
|
97
|
+
|
|
98
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
99
|
+
list.innerHTML = '<li class="empty">No traces yet.</li>';
|
|
100
|
+
} else {
|
|
101
|
+
list.innerHTML = data.map(function(t) { return renderTrace(t, 0); }).join('');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
status.textContent = 'Last updated: ' + new Date().toLocaleTimeString() +
|
|
105
|
+
' (' + (data.length || 0) + ' traces)';
|
|
106
|
+
} catch (err) {
|
|
107
|
+
document.getElementById('status').textContent = 'Error: ' + err.message;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
refresh();
|
|
112
|
+
setInterval(refresh, 2000);
|
|
113
|
+
</script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>`;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=trace-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-ui.js","sourceRoot":"","sources":["../../src/commands/trace-ui.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAmGD,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nerva.yaml configuration parser and validator.
|
|
3
|
+
*
|
|
4
|
+
* Handles reading, writing, and validating the project-level nerva.yaml
|
|
5
|
+
* that declares agents, tools, middleware, and routers.
|
|
6
|
+
*/
|
|
7
|
+
/** A single registered component (agent, tool, middleware, or router). */
|
|
8
|
+
export interface ComponentEntry {
|
|
9
|
+
name: string;
|
|
10
|
+
path: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Supported project languages. */
|
|
14
|
+
export type ProjectLang = 'python' | 'typescript';
|
|
15
|
+
/** Top-level nerva.yaml schema. */
|
|
16
|
+
export interface NervaConfig {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
lang: ProjectLang;
|
|
20
|
+
agents: ComponentEntry[];
|
|
21
|
+
tools: ComponentEntry[];
|
|
22
|
+
middleware: ComponentEntry[];
|
|
23
|
+
routers: ComponentEntry[];
|
|
24
|
+
policy?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
/** Component types that can be registered in nerva.yaml. */
|
|
27
|
+
export type ComponentType = 'agent' | 'tool' | 'middleware' | 'router';
|
|
28
|
+
/** Keys used in nerva.yaml for each component type's list section. */
|
|
29
|
+
export type ConfigListKey = 'agents' | 'tools' | 'middleware' | 'routers';
|
|
30
|
+
/**
|
|
31
|
+
* Returns the config key used in nerva.yaml for a given component type.
|
|
32
|
+
*
|
|
33
|
+
* @param type - The singular component type
|
|
34
|
+
* @returns The yaml section key (e.g. 'agent' -> 'agents', 'middleware' -> 'middleware')
|
|
35
|
+
*/
|
|
36
|
+
export declare function pluralizeType(type: ComponentType): ConfigListKey;
|
|
37
|
+
/**
|
|
38
|
+
* Parses raw YAML-like text into a NervaConfig object.
|
|
39
|
+
*
|
|
40
|
+
* Uses a simple line-based parser for the flat nerva.yaml format.
|
|
41
|
+
* This avoids pulling in a full YAML library for a predictable schema.
|
|
42
|
+
*
|
|
43
|
+
* @param content - Raw file content
|
|
44
|
+
* @returns Parsed configuration
|
|
45
|
+
* @throws {Error} If required fields are missing or lang is invalid
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseNervaConfig(content: string): NervaConfig;
|
|
48
|
+
/**
|
|
49
|
+
* Reads and parses nerva.yaml from the given project directory.
|
|
50
|
+
*
|
|
51
|
+
* @param projectDir - Absolute path to project root
|
|
52
|
+
* @returns Parsed NervaConfig
|
|
53
|
+
* @throws {Error} If the file cannot be read or parsed
|
|
54
|
+
*/
|
|
55
|
+
export declare function readNervaConfig(projectDir: string): Promise<NervaConfig>;
|
|
56
|
+
/**
|
|
57
|
+
* Serializes a NervaConfig to YAML-formatted text.
|
|
58
|
+
*
|
|
59
|
+
* @param config - The configuration to serialize
|
|
60
|
+
* @returns YAML string
|
|
61
|
+
*/
|
|
62
|
+
export declare function serializeNervaConfig(config: NervaConfig): string;
|
|
63
|
+
/**
|
|
64
|
+
* Writes a NervaConfig back to nerva.yaml in the given directory.
|
|
65
|
+
*
|
|
66
|
+
* @param projectDir - Absolute path to project root
|
|
67
|
+
* @param config - Configuration to write
|
|
68
|
+
*/
|
|
69
|
+
export declare function writeNervaConfig(projectDir: string, config: NervaConfig): Promise<void>;
|
|
70
|
+
//# sourceMappingURL=nerva-yaml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nerva-yaml.d.ts","sourceRoot":"","sources":["../../src/config/nerva-yaml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,mCAAmC;AACnC,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;AAElD,mCAAmC;AACnC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEvE,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,CAAC;AAc1E;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CA+D7D;AAmDD;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAI9E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAuBhE;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7F"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nerva.yaml configuration parser and validator.
|
|
3
|
+
*
|
|
4
|
+
* Handles reading, writing, and validating the project-level nerva.yaml
|
|
5
|
+
* that declares agents, tools, middleware, and routers.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
const VALID_LANGS = new Set(['python', 'typescript']);
|
|
10
|
+
const REQUIRED_FIELDS = ['name', 'version', 'lang'];
|
|
11
|
+
const COMPONENT_LISTS = ['agent', 'tool', 'middleware', 'router'];
|
|
12
|
+
/** Maps singular component type to the nerva.yaml section key. */
|
|
13
|
+
const TYPE_TO_KEY = {
|
|
14
|
+
agent: 'agents',
|
|
15
|
+
tool: 'tools',
|
|
16
|
+
middleware: 'middleware',
|
|
17
|
+
router: 'routers',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Returns the config key used in nerva.yaml for a given component type.
|
|
21
|
+
*
|
|
22
|
+
* @param type - The singular component type
|
|
23
|
+
* @returns The yaml section key (e.g. 'agent' -> 'agents', 'middleware' -> 'middleware')
|
|
24
|
+
*/
|
|
25
|
+
export function pluralizeType(type) {
|
|
26
|
+
return TYPE_TO_KEY[type];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses raw YAML-like text into a NervaConfig object.
|
|
30
|
+
*
|
|
31
|
+
* Uses a simple line-based parser for the flat nerva.yaml format.
|
|
32
|
+
* This avoids pulling in a full YAML library for a predictable schema.
|
|
33
|
+
*
|
|
34
|
+
* @param content - Raw file content
|
|
35
|
+
* @returns Parsed configuration
|
|
36
|
+
* @throws {Error} If required fields are missing or lang is invalid
|
|
37
|
+
*/
|
|
38
|
+
export function parseNervaConfig(content) {
|
|
39
|
+
const lines = content.split('\n');
|
|
40
|
+
const config = {};
|
|
41
|
+
const listSections = {
|
|
42
|
+
agents: [],
|
|
43
|
+
tools: [],
|
|
44
|
+
middleware: [],
|
|
45
|
+
routers: [],
|
|
46
|
+
};
|
|
47
|
+
let currentSection = null;
|
|
48
|
+
let currentEntry = null;
|
|
49
|
+
for (const rawLine of lines) {
|
|
50
|
+
const line = rawLine.trimEnd();
|
|
51
|
+
if (line.trim() === '' || line.trim().startsWith('#')) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Top-level section header (e.g. "agents:")
|
|
55
|
+
const sectionMatch = line.match(/^(\w+):$/);
|
|
56
|
+
if (sectionMatch) {
|
|
57
|
+
flushEntry(currentSection, currentEntry, listSections);
|
|
58
|
+
currentEntry = null;
|
|
59
|
+
const key = sectionMatch[1];
|
|
60
|
+
if (key in listSections) {
|
|
61
|
+
currentSection = key;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
currentSection = null;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Top-level key: value pair (e.g. "name: my-project")
|
|
69
|
+
const kvMatch = line.match(/^(\w+):\s+(.+)$/);
|
|
70
|
+
if (kvMatch && !currentSection) {
|
|
71
|
+
config[kvMatch[1]] = kvMatch[2].trim();
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// List item start (e.g. " - name: my-agent")
|
|
75
|
+
const listItemMatch = line.match(/^\s+-\s+(\w+):\s+(.+)$/);
|
|
76
|
+
if (listItemMatch && currentSection) {
|
|
77
|
+
flushEntry(currentSection, currentEntry, listSections);
|
|
78
|
+
currentEntry = { [listItemMatch[1]]: listItemMatch[2].trim() };
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// Continuation of list item (e.g. " path: agents/my-agent.py")
|
|
82
|
+
const continuationMatch = line.match(/^\s+(\w+):\s+(.+)$/);
|
|
83
|
+
if (continuationMatch && currentSection && currentEntry) {
|
|
84
|
+
currentEntry[continuationMatch[1]] = continuationMatch[2].trim();
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
flushEntry(currentSection, currentEntry, listSections);
|
|
89
|
+
return validateConfig({
|
|
90
|
+
...config,
|
|
91
|
+
...listSections,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Flushes a partially-built component entry into the appropriate list.
|
|
96
|
+
*
|
|
97
|
+
* @param section - Current yaml section name
|
|
98
|
+
* @param entry - Partial entry to flush
|
|
99
|
+
* @param lists - Map of section name to component entries
|
|
100
|
+
*/
|
|
101
|
+
function flushEntry(section, entry, lists) {
|
|
102
|
+
if (!section || !entry || !entry.name || !entry.path) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
lists[section].push({ name: entry.name, path: entry.path, description: entry.description });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Validates a raw parsed object against the NervaConfig schema.
|
|
109
|
+
*
|
|
110
|
+
* @param raw - Unvalidated config object
|
|
111
|
+
* @returns Validated NervaConfig
|
|
112
|
+
* @throws {Error} If validation fails
|
|
113
|
+
*/
|
|
114
|
+
function validateConfig(raw) {
|
|
115
|
+
for (const field of REQUIRED_FIELDS) {
|
|
116
|
+
if (!raw[field] || typeof raw[field] !== 'string') {
|
|
117
|
+
throw new Error(`nerva.yaml: missing or invalid required field "${field}"`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const lang = raw['lang'];
|
|
121
|
+
if (!VALID_LANGS.has(lang)) {
|
|
122
|
+
throw new Error(`nerva.yaml: lang must be "python" or "typescript", got "${lang}"`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
name: raw['name'],
|
|
126
|
+
version: raw['version'],
|
|
127
|
+
lang: lang,
|
|
128
|
+
agents: raw['agents'] ?? [],
|
|
129
|
+
tools: raw['tools'] ?? [],
|
|
130
|
+
middleware: raw['middleware'] ?? [],
|
|
131
|
+
routers: raw['routers'] ?? [],
|
|
132
|
+
policy: raw['policy'] ?? undefined,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Reads and parses nerva.yaml from the given project directory.
|
|
137
|
+
*
|
|
138
|
+
* @param projectDir - Absolute path to project root
|
|
139
|
+
* @returns Parsed NervaConfig
|
|
140
|
+
* @throws {Error} If the file cannot be read or parsed
|
|
141
|
+
*/
|
|
142
|
+
export async function readNervaConfig(projectDir) {
|
|
143
|
+
const filePath = join(projectDir, 'nerva.yaml');
|
|
144
|
+
const content = await readFile(filePath, 'utf-8');
|
|
145
|
+
return parseNervaConfig(content);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Serializes a NervaConfig to YAML-formatted text.
|
|
149
|
+
*
|
|
150
|
+
* @param config - The configuration to serialize
|
|
151
|
+
* @returns YAML string
|
|
152
|
+
*/
|
|
153
|
+
export function serializeNervaConfig(config) {
|
|
154
|
+
const lines = [];
|
|
155
|
+
lines.push(`name: ${config.name}`);
|
|
156
|
+
lines.push(`version: ${config.version}`);
|
|
157
|
+
lines.push(`lang: ${config.lang}`);
|
|
158
|
+
for (const type of COMPONENT_LISTS) {
|
|
159
|
+
const plural = pluralizeType(type);
|
|
160
|
+
const entries = config[plural];
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push(`${plural}:`);
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
lines.push(` - name: ${entry.name}`);
|
|
165
|
+
lines.push(` path: ${entry.path}`);
|
|
166
|
+
if (entry.description) {
|
|
167
|
+
lines.push(` description: ${entry.description}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
lines.push('');
|
|
172
|
+
return lines.join('\n');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Writes a NervaConfig back to nerva.yaml in the given directory.
|
|
176
|
+
*
|
|
177
|
+
* @param projectDir - Absolute path to project root
|
|
178
|
+
* @param config - Configuration to write
|
|
179
|
+
*/
|
|
180
|
+
export async function writeNervaConfig(projectDir, config) {
|
|
181
|
+
const filePath = join(projectDir, 'nerva.yaml');
|
|
182
|
+
const content = serializeNervaConfig(config);
|
|
183
|
+
await writeFile(filePath, content, 'utf-8');
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=nerva-yaml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nerva-yaml.js","sourceRoot":"","sources":["../../src/config/nerva-yaml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA8BjC,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;AAC3E,MAAM,eAAe,GAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AACvE,MAAM,eAAe,GAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;AAE5F,kEAAkE;AAClE,MAAM,WAAW,GAAyC;IACxD,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,OAAO;IACb,UAAU,EAAE,YAAY;IACxB,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAmB;IAC/C,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAqC;QACrD,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,EAAE;KACZ,CAAC;IAEF,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,IAAI,YAAY,GAAmC,IAAI,CAAC;IAExD,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAE/B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACtD,SAAS;QACX,CAAC;QAED,4CAA4C;QAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,YAAY,EAAE,CAAC;YACjB,UAAU,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YACvD,YAAY,GAAG,IAAI,CAAC;YACpB,MAAM,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,GAAG,IAAI,YAAY,EAAE,CAAC;gBACxB,cAAc,GAAG,GAAG,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;YACD,SAAS;QACX,CAAC;QAED,sDAAsD;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC9C,IAAI,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACvC,SAAS;QACX,CAAC;QAED,8CAA8C;QAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC3D,IAAI,aAAa,IAAI,cAAc,EAAE,CAAC;YACpC,UAAU,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YACvD,YAAY,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/D,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAC3D,IAAI,iBAAiB,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;YACxD,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAyB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACzF,SAAS;QACX,CAAC;IACH,CAAC;IAED,UAAU,CAAC,cAAc,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IAEvD,OAAO,cAAc,CAAC;QACpB,GAAG,MAAM;QACT,GAAG,YAAY;KAChB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CACjB,OAAsB,EACtB,KAAqC,EACrC,KAAuC;IAEvC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,GAA4B;IAClD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,kDAAkD,KAAK,GAAG,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;IACnC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,2DAA2D,IAAI,GAAG,CAAC,CAAC;IACtF,CAAC;IAED,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,MAAM,CAAW;QAC3B,OAAO,EAAE,GAAG,CAAC,SAAS,CAAW;QACjC,IAAI,EAAE,IAAmB;QACzB,MAAM,EAAG,GAAG,CAAC,QAAQ,CAAsB,IAAI,EAAE;QACjD,KAAK,EAAG,GAAG,CAAC,OAAO,CAAsB,IAAI,EAAE;QAC/C,UAAU,EAAG,GAAG,CAAC,YAAY,CAAsB,IAAI,EAAE;QACzD,OAAO,EAAG,GAAG,CAAC,SAAS,CAAsB,IAAI,EAAE;QACnD,MAAM,EAAG,GAAG,CAAC,QAAQ,CAA6B,IAAI,SAAS;KAChE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAmB;IACtD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,MAAM,CAAC,MAA2B,CAAqB,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,CAAC,oBAAoB,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB,EAAE,MAAmB;IAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Nerva CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Registers all sub-commands and runs the CLI program.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
/**
|
|
9
|
+
* Creates and configures the root CLI program with all commands.
|
|
10
|
+
*
|
|
11
|
+
* @returns Configured Commander program
|
|
12
|
+
*/
|
|
13
|
+
export declare function createProgram(): Command;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAgBvC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Nerva CLI entry point.
|
|
4
|
+
*
|
|
5
|
+
* Registers all sub-commands and runs the CLI program.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { registerNewCommand } from './commands/new.js';
|
|
9
|
+
import { registerGenerateCommand } from './commands/generate.js';
|
|
10
|
+
import { registerListCommand } from './commands/list.js';
|
|
11
|
+
import { registerDevCommand } from './commands/dev.js';
|
|
12
|
+
import { registerTestCommand } from './commands/test.js';
|
|
13
|
+
import { registerPluginCommand } from './commands/plugin.js';
|
|
14
|
+
const VERSION = '0.1.0';
|
|
15
|
+
/**
|
|
16
|
+
* Creates and configures the root CLI program with all commands.
|
|
17
|
+
*
|
|
18
|
+
* @returns Configured Commander program
|
|
19
|
+
*/
|
|
20
|
+
export function createProgram() {
|
|
21
|
+
const program = new Command();
|
|
22
|
+
program
|
|
23
|
+
.name('nerva')
|
|
24
|
+
.description('CLI for the Nerva agent runtime')
|
|
25
|
+
.version(VERSION);
|
|
26
|
+
registerNewCommand(program);
|
|
27
|
+
registerGenerateCommand(program);
|
|
28
|
+
registerListCommand(program);
|
|
29
|
+
registerDevCommand(program);
|
|
30
|
+
registerTestCommand(program);
|
|
31
|
+
registerPluginCommand(program);
|
|
32
|
+
return program;
|
|
33
|
+
}
|
|
34
|
+
const program = createProgram();
|
|
35
|
+
program.parse(process.argv);
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,OAAO,CAAC;SACb,WAAW,CAAC,iCAAiC,CAAC;SAC9C,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAChC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Community plugin registry client.
|
|
3
|
+
*
|
|
4
|
+
* Searches the npm registry for packages with the `nerva-plugin` keyword
|
|
5
|
+
* and fetches package metadata. Uses `https.get` with no external dependencies.
|
|
6
|
+
*/
|
|
7
|
+
/** Summary of a plugin found in the npm registry. */
|
|
8
|
+
export interface RegistryPluginInfo {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
version: string;
|
|
12
|
+
downloads: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Searches the npm registry for packages matching a query and the `nerva-plugin` keyword.
|
|
16
|
+
*
|
|
17
|
+
* @param query - Free-text search query
|
|
18
|
+
* @returns Array of matching plugin summaries, sorted by relevance
|
|
19
|
+
* @throws {Error} If the npm registry is unreachable or returns invalid data
|
|
20
|
+
*/
|
|
21
|
+
export declare function searchPlugins(query: string): Promise<RegistryPluginInfo[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Fetches detailed package info from the npm registry for a specific package.
|
|
24
|
+
*
|
|
25
|
+
* @param name - Exact npm package name
|
|
26
|
+
* @returns Plugin info with name, description, latest version, and monthly downloads
|
|
27
|
+
* @throws {Error} If the package is not found or the registry is unreachable
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchPluginInfo(name: string): Promise<RegistryPluginInfo>;
|
|
30
|
+
//# sourceMappingURL=plugin-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-registry.d.ts","sourceRoot":"","sources":["../../src/registry/plugin-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4ED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAiBhF;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsB/E"}
|