@velajs/cli 0.3.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +14 -10
- package/dist/config.js +33 -36
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +114 -10
- package/dist/index.js +478 -15
- package/dist/index.js.map +1 -0
- package/package.json +46 -29
- package/dist/commands/introspect.commands.d.ts +0 -40
- package/dist/commands/introspect.commands.js +0 -165
- package/dist/commands/mcp.command.d.ts +0 -17
- package/dist/commands/mcp.command.js +0 -166
- package/dist/commands/seed.command.d.ts +0 -9
- package/dist/commands/seed.command.js +0 -40
- package/dist/format.d.ts +0 -8
- package/dist/format.js +0 -32
- package/dist/introspect.d.ts +0 -32
- package/dist/introspect.js +0 -103
package/package.json
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velajs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "CLI for Vela apps — seeding and project tasks (Node-side; not bundled into the edge Worker)",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"cloudflare-workers",
|
|
8
|
+
"edge",
|
|
9
|
+
"seeder",
|
|
10
|
+
"vela"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/velajs/cli.git"
|
|
16
|
+
},
|
|
6
17
|
"bin": {
|
|
7
18
|
"vela": "./dist/index.js"
|
|
8
19
|
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
9
25
|
"main": "./dist/index.js",
|
|
10
26
|
"types": "./dist/index.d.ts",
|
|
11
27
|
"exports": {
|
|
@@ -18,44 +34,45 @@
|
|
|
18
34
|
"import": "./dist/config.js"
|
|
19
35
|
}
|
|
20
36
|
},
|
|
21
|
-
"files": [
|
|
22
|
-
"dist",
|
|
23
|
-
"README.md"
|
|
24
|
-
],
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=20"
|
|
27
|
-
},
|
|
28
37
|
"dependencies": {
|
|
29
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
39
|
"clipanion": "^4.0.0-rc.4",
|
|
31
40
|
"zod": "^3.25.76"
|
|
32
41
|
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@velajs/vela": ">=1.15.0"
|
|
35
|
-
},
|
|
36
42
|
"devDependencies": {
|
|
43
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
44
|
+
"@changesets/cli": "^2.31.0",
|
|
37
45
|
"@swc/core": "^1.15.43",
|
|
38
|
-
"@types/node": "^
|
|
39
|
-
"@velajs/vela": "^1.
|
|
40
|
-
"
|
|
46
|
+
"@types/node": "^24.13.3",
|
|
47
|
+
"@velajs/vela": "^1.21.0",
|
|
48
|
+
"oxfmt": "^0.58.0",
|
|
49
|
+
"oxlint": "^1.73.0",
|
|
50
|
+
"publint": "^0.3.21",
|
|
51
|
+
"tsdown": "^0.22.4",
|
|
52
|
+
"typescript": "^7.0.2",
|
|
41
53
|
"unplugin-swc": "^1.5.9",
|
|
42
|
-
"vitest": "^4.1.
|
|
54
|
+
"vitest": "^4.1.10"
|
|
43
55
|
},
|
|
44
|
-
"
|
|
45
|
-
"vela"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"cloudflare-workers"
|
|
50
|
-
],
|
|
51
|
-
"license": "MIT",
|
|
52
|
-
"repository": {
|
|
53
|
-
"type": "git",
|
|
54
|
-
"url": "git+https://github.com/velajs/cli.git"
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@velajs/vela": ">=1.21.0 <2"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=24"
|
|
55
61
|
},
|
|
56
62
|
"scripts": {
|
|
57
|
-
"build": "
|
|
63
|
+
"build": "tsdown",
|
|
58
64
|
"typecheck": "tsc --noEmit",
|
|
59
|
-
"test": "vitest run"
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"lint": "oxlint .",
|
|
67
|
+
"format": "oxfmt .",
|
|
68
|
+
"format:check": "oxfmt --check .",
|
|
69
|
+
"publint": "publint",
|
|
70
|
+
"attw": "attw --pack . --profile esm-only",
|
|
71
|
+
"changeset": "changeset",
|
|
72
|
+
"version-packages": "changeset version",
|
|
73
|
+
"release:preflight": "npm view @velajs/vela@1.21.0 version",
|
|
74
|
+
"release:check": "node scripts/check-release-lock.mjs && pnpm verify && pnpm audit --audit-level=high",
|
|
75
|
+
"release": "pnpm release:preflight && pnpm release:check && changeset publish",
|
|
76
|
+
"verify": "pnpm lint && pnpm format:check && pnpm build && pnpm typecheck && pnpm test && pnpm publint && pnpm attw"
|
|
60
77
|
}
|
|
61
78
|
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { VelaApplication } from '@velajs/vela';
|
|
2
|
-
import { Command } from 'clipanion';
|
|
3
|
-
/** Shared shell: load config → createApp → run → best-effort dispose. */
|
|
4
|
-
declare abstract class AppCommand extends Command {
|
|
5
|
-
config: string | undefined;
|
|
6
|
-
json: boolean;
|
|
7
|
-
protected abstract run(app: VelaApplication): Promise<number>;
|
|
8
|
-
execute(): Promise<number>;
|
|
9
|
-
protected print(text: string): void;
|
|
10
|
-
}
|
|
11
|
-
/** `vela route list` — the app's HTTP route table. */
|
|
12
|
-
export declare class RouteListCommand extends AppCommand {
|
|
13
|
-
static paths: string[][];
|
|
14
|
-
static usage: import("clipanion").Usage;
|
|
15
|
-
protected run(app: VelaApplication): Promise<number>;
|
|
16
|
-
}
|
|
17
|
-
/** `vela module graph` — the loaded module graph. */
|
|
18
|
-
export declare class ModuleGraphCommand extends AppCommand {
|
|
19
|
-
static paths: string[][];
|
|
20
|
-
static usage: import("clipanion").Usage;
|
|
21
|
-
protected run(app: VelaApplication): Promise<number>;
|
|
22
|
-
}
|
|
23
|
-
/** `vela entrypoint list` — declared entrypoint kinds and their entries. */
|
|
24
|
-
export declare class EntrypointListCommand extends AppCommand {
|
|
25
|
-
static paths: string[][];
|
|
26
|
-
static usage: import("clipanion").Usage;
|
|
27
|
-
protected run(app: VelaApplication): Promise<number>;
|
|
28
|
-
}
|
|
29
|
-
/** `vela openapi dump` — emit the OpenAPI document. */
|
|
30
|
-
export declare class OpenApiDumpCommand extends Command {
|
|
31
|
-
static paths: string[][];
|
|
32
|
-
static usage: import("clipanion").Usage;
|
|
33
|
-
config: string | undefined;
|
|
34
|
-
out: string | undefined;
|
|
35
|
-
title: string | undefined;
|
|
36
|
-
apiVersion: string | undefined;
|
|
37
|
-
globalPrefix: string | undefined;
|
|
38
|
-
execute(): Promise<number>;
|
|
39
|
-
}
|
|
40
|
-
export {};
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { writeFile } from 'node:fs/promises';
|
|
2
|
-
import { createOpenApiDocument } from '@velajs/vela';
|
|
3
|
-
import { Command, Option } from 'clipanion';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
5
|
-
import { renderTable } from '../format.js';
|
|
6
|
-
import { collectEntrypoints, collectModules, collectRoutes, renderModuleTree } from '../introspect.js';
|
|
7
|
-
/** Shared shell: load config → createApp → run → best-effort dispose. */
|
|
8
|
-
class AppCommand extends Command {
|
|
9
|
-
config = Option.String('--config', { description: 'Path to the vela config file.' });
|
|
10
|
-
json = Option.Boolean('--json', false, { description: 'Emit machine-readable JSON.' });
|
|
11
|
-
async execute() {
|
|
12
|
-
const velaConfig = await loadConfig(process.cwd(), this.config);
|
|
13
|
-
const app = await velaConfig.createApp();
|
|
14
|
-
try {
|
|
15
|
-
return await this.run(app);
|
|
16
|
-
}
|
|
17
|
-
finally {
|
|
18
|
-
const dispose = app.dispose;
|
|
19
|
-
if (typeof dispose === 'function') {
|
|
20
|
-
try {
|
|
21
|
-
await dispose.call(app);
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
this.context.stderr.write(`Warning: teardown failed: ${String(error)}\n`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
print(text) {
|
|
30
|
-
this.context.stdout.write(`${text}\n`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/** `vela route list` — the app's HTTP route table. */
|
|
34
|
-
export class RouteListCommand extends AppCommand {
|
|
35
|
-
static paths = [['route', 'list']];
|
|
36
|
-
static usage = Command.Usage({
|
|
37
|
-
category: 'Introspection',
|
|
38
|
-
description: 'List the HTTP routes of the Vela app.',
|
|
39
|
-
details: 'Framework-composed controller routes (method, full path, controller#handler) plus ' +
|
|
40
|
-
'everything else mounted on the router (CRUD/contributed routes, doc UIs) labeled (mounted).',
|
|
41
|
-
examples: [['List routes', 'vela route list'], ['As JSON', 'vela route list --json']],
|
|
42
|
-
});
|
|
43
|
-
async run(app) {
|
|
44
|
-
const rows = collectRoutes(app);
|
|
45
|
-
if (rows === null) {
|
|
46
|
-
this.print('This app builds no HTTP routes — nothing to list.');
|
|
47
|
-
return 0;
|
|
48
|
-
}
|
|
49
|
-
if (this.json) {
|
|
50
|
-
this.print(JSON.stringify(rows, null, 2));
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
for (const line of renderTable(['METHOD', 'PATH', 'HANDLER'], rows.map((r) => [r.method, r.path, r.handler]))) {
|
|
54
|
-
this.print(line);
|
|
55
|
-
}
|
|
56
|
-
return 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/** `vela module graph` — the loaded module graph. */
|
|
60
|
-
export class ModuleGraphCommand extends AppCommand {
|
|
61
|
-
static paths = [['module', 'graph']];
|
|
62
|
-
static usage = Command.Usage({
|
|
63
|
-
category: 'Introspection',
|
|
64
|
-
description: 'Print the module graph of the Vela app.',
|
|
65
|
-
details: 'Module instances with their imports (indented tree), global/lazy flags, and provider ' +
|
|
66
|
-
'counts. --json emits the raw descriptions (providers, exports, imports per module).',
|
|
67
|
-
examples: [['Print the graph', 'vela module graph'], ['As JSON', 'vela module graph --json']],
|
|
68
|
-
});
|
|
69
|
-
async run(app) {
|
|
70
|
-
const modules = collectModules(app);
|
|
71
|
-
if (this.json) {
|
|
72
|
-
this.print(JSON.stringify(modules, null, 2));
|
|
73
|
-
return 0;
|
|
74
|
-
}
|
|
75
|
-
for (const line of renderModuleTree(modules))
|
|
76
|
-
this.print(line);
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
/** `vela entrypoint list` — declared entrypoint kinds and their entries. */
|
|
81
|
-
export class EntrypointListCommand extends AppCommand {
|
|
82
|
-
static paths = [['entrypoint', 'list']];
|
|
83
|
-
static usage = Command.Usage({
|
|
84
|
-
category: 'Introspection',
|
|
85
|
-
description: 'List entrypoint kinds and entries (websocket, queue, cron, …).',
|
|
86
|
-
details: 'Every declared kind — including kinds with zero entries — with the contributing ' +
|
|
87
|
-
'class (and method for method-level kinds) and its metadata.',
|
|
88
|
-
examples: [['List entrypoints', 'vela entrypoint list']],
|
|
89
|
-
});
|
|
90
|
-
async run(app) {
|
|
91
|
-
const rows = collectEntrypoints(app);
|
|
92
|
-
if (this.json) {
|
|
93
|
-
this.print(JSON.stringify(rows, null, 2));
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
96
|
-
for (const line of renderTable(['KIND', 'TARGET', 'META'], rows.map((r) => [r.kind, r.target, r.meta]))) {
|
|
97
|
-
this.print(line);
|
|
98
|
-
}
|
|
99
|
-
return 0;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/** `vela openapi dump` — emit the OpenAPI document. */
|
|
103
|
-
export class OpenApiDumpCommand extends Command {
|
|
104
|
-
static paths = [['openapi', 'dump']];
|
|
105
|
-
static usage = Command.Usage({
|
|
106
|
-
category: 'Introspection',
|
|
107
|
-
description: 'Emit the OpenAPI document for the Vela app.',
|
|
108
|
-
details: "Requires `rootModule` in vela.config (createOpenApiDocument works from the module " +
|
|
109
|
-
"class). The app's global prefix is applied automatically; --global-prefix overrides.",
|
|
110
|
-
examples: [
|
|
111
|
-
['Print to stdout', 'vela openapi dump'],
|
|
112
|
-
['Write to a file', 'vela openapi dump --out openapi.json'],
|
|
113
|
-
],
|
|
114
|
-
});
|
|
115
|
-
config = Option.String('--config', { description: 'Path to the vela config file.' });
|
|
116
|
-
out = Option.String('--out', { description: 'Write the document to this file instead of stdout.' });
|
|
117
|
-
title = Option.String('--title', { description: 'info.title override.' });
|
|
118
|
-
apiVersion = Option.String('--api-version', { description: 'info.version override.' });
|
|
119
|
-
globalPrefix = Option.String('--global-prefix', {
|
|
120
|
-
description: "Path prefix override (defaults to the app's global prefix).",
|
|
121
|
-
});
|
|
122
|
-
async execute() {
|
|
123
|
-
const velaConfig = await loadConfig(process.cwd(), this.config);
|
|
124
|
-
if (!velaConfig.rootModule) {
|
|
125
|
-
this.context.stderr.write("openapi dump needs the root module. Add it to your vela.config:\n\n" +
|
|
126
|
-
' export default defineVelaConfig({\n' +
|
|
127
|
-
' rootModule: AppModule,\n' +
|
|
128
|
-
' async createApp() { ... },\n' +
|
|
129
|
-
' });\n');
|
|
130
|
-
return 1;
|
|
131
|
-
}
|
|
132
|
-
const app = await velaConfig.createApp();
|
|
133
|
-
try {
|
|
134
|
-
const info = {};
|
|
135
|
-
if (this.title)
|
|
136
|
-
info.title = this.title;
|
|
137
|
-
if (this.apiVersion)
|
|
138
|
-
info.version = this.apiVersion;
|
|
139
|
-
const document = createOpenApiDocument(velaConfig.rootModule, {
|
|
140
|
-
globalPrefix: this.globalPrefix ?? app.getGlobalPrefix(),
|
|
141
|
-
...(Object.keys(info).length > 0 ? { info } : {}),
|
|
142
|
-
});
|
|
143
|
-
const text = JSON.stringify(document, null, 2);
|
|
144
|
-
if (this.out) {
|
|
145
|
-
await writeFile(this.out, `${text}\n`, 'utf8');
|
|
146
|
-
this.context.stdout.write(`Wrote ${this.out}\n`);
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
this.context.stdout.write(`${text}\n`);
|
|
150
|
-
}
|
|
151
|
-
return 0;
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
const dispose = app.dispose;
|
|
155
|
-
if (typeof dispose === 'function') {
|
|
156
|
-
try {
|
|
157
|
-
await dispose.call(app);
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
this.context.stderr.write(`Warning: teardown failed: ${String(error)}\n`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Command } from 'clipanion';
|
|
2
|
-
/**
|
|
3
|
-
* `vela mcp serve` — an MCP stdio server exposing the same READ-ONLY
|
|
4
|
-
* introspection as the `route`/`module`/`entrypoint`/`openapi` commands, so an
|
|
5
|
-
* AI agent can query a Vela app's shape over the Model Context Protocol.
|
|
6
|
-
*
|
|
7
|
-
* Deliberately does NOT extend `AppCommand`: that base disposes the app in its
|
|
8
|
-
* `finally` the moment `run()` returns, but an MCP server must stay alive until
|
|
9
|
-
* the transport closes. stdout is reserved for JSON-RPC framing; every human
|
|
10
|
-
* message goes to stderr.
|
|
11
|
-
*/
|
|
12
|
-
export declare class McpServeCommand extends Command {
|
|
13
|
-
static paths: string[][];
|
|
14
|
-
static usage: import("clipanion").Usage;
|
|
15
|
-
config: string | undefined;
|
|
16
|
-
execute(): Promise<number>;
|
|
17
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { createOpenApiDocument } from '@velajs/vela';
|
|
5
|
-
import { Command, Option } from 'clipanion';
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import { loadConfig } from '../config.js';
|
|
8
|
-
import { collectEntrypoints, collectModules, collectRoutes, renderModuleTree } from '../introspect.js';
|
|
9
|
-
const OPENAPI_URI = 'vela://openapi';
|
|
10
|
-
/** A single JSON text block — the shape every tool/resource result uses. */
|
|
11
|
-
function jsonText(data) {
|
|
12
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
13
|
-
}
|
|
14
|
-
/** An MCP tool error result (JSON-RPC stays 2.0; the failure is in-band). */
|
|
15
|
-
function toolError(message) {
|
|
16
|
-
return { isError: true, content: [{ type: 'text', text: message }] };
|
|
17
|
-
}
|
|
18
|
-
/** Name + version for the MCP server handshake, read from the CLI's own package.json. */
|
|
19
|
-
async function readCliIdentity() {
|
|
20
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
const pkgPath = join(here, '..', '..', 'package.json');
|
|
22
|
-
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
|
|
23
|
-
return { name: pkg.name ?? '@velajs/cli', version: pkg.version ?? '0.0.0' };
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* String-label lookup for a DI token across the module graph. Reads only the
|
|
27
|
-
* serializable descriptions (`collectModules`) — never resolves the token or
|
|
28
|
-
* constructs anything. Reports which modules provide/export it and their scope
|
|
29
|
-
* flags, plus whether the string names a module itself.
|
|
30
|
-
*/
|
|
31
|
-
function describeToken(app, token) {
|
|
32
|
-
const modules = collectModules(app);
|
|
33
|
-
const providedBy = modules
|
|
34
|
-
.filter((m) => m.providers.includes(token))
|
|
35
|
-
.map((m) => ({ moduleId: m.moduleId, isGlobal: m.isGlobal, lazy: m.lazy, exported: m.exports.includes(token) }));
|
|
36
|
-
const matchesModule = modules.find((m) => m.moduleId === token);
|
|
37
|
-
return {
|
|
38
|
-
token,
|
|
39
|
-
found: providedBy.length > 0 || matchesModule !== undefined,
|
|
40
|
-
providedBy,
|
|
41
|
-
module: matchesModule
|
|
42
|
-
? { moduleId: matchesModule.moduleId, isGlobal: matchesModule.isGlobal, lazy: matchesModule.lazy }
|
|
43
|
-
: null,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* `vela mcp serve` — an MCP stdio server exposing the same READ-ONLY
|
|
48
|
-
* introspection as the `route`/`module`/`entrypoint`/`openapi` commands, so an
|
|
49
|
-
* AI agent can query a Vela app's shape over the Model Context Protocol.
|
|
50
|
-
*
|
|
51
|
-
* Deliberately does NOT extend `AppCommand`: that base disposes the app in its
|
|
52
|
-
* `finally` the moment `run()` returns, but an MCP server must stay alive until
|
|
53
|
-
* the transport closes. stdout is reserved for JSON-RPC framing; every human
|
|
54
|
-
* message goes to stderr.
|
|
55
|
-
*/
|
|
56
|
-
export class McpServeCommand extends Command {
|
|
57
|
-
static paths = [['mcp', 'serve']];
|
|
58
|
-
static usage = Command.Usage({
|
|
59
|
-
category: 'Introspection',
|
|
60
|
-
description: 'Serve Vela introspection as MCP tools over stdio (for AI agents).',
|
|
61
|
-
details: 'Builds the app from vela.config and runs a Model Context Protocol stdio server. Exposes ' +
|
|
62
|
-
'read-only tools (route_list, module_graph, entrypoint_list, openapi_dump, token_describe) ' +
|
|
63
|
-
'and — when the config declares a rootModule — a `vela://openapi` resource. stdout carries ' +
|
|
64
|
-
'only JSON-RPC; all logging goes to stderr. The server runs until the client disconnects.',
|
|
65
|
-
examples: [
|
|
66
|
-
['Serve over stdio', 'vela mcp serve'],
|
|
67
|
-
['Use a specific config', 'vela mcp serve --config ./config/vela.config.js'],
|
|
68
|
-
],
|
|
69
|
-
});
|
|
70
|
-
config = Option.String('--config', { description: 'Path to the vela config file.' });
|
|
71
|
-
async execute() {
|
|
72
|
-
const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
|
|
73
|
-
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
74
|
-
const log = (message) => {
|
|
75
|
-
this.context.stderr.write(`${message}\n`);
|
|
76
|
-
};
|
|
77
|
-
const velaConfig = await loadConfig(process.cwd(), this.config);
|
|
78
|
-
const app = await velaConfig.createApp();
|
|
79
|
-
const rootModule = velaConfig.rootModule;
|
|
80
|
-
try {
|
|
81
|
-
const identity = await readCliIdentity();
|
|
82
|
-
const server = new McpServer(identity);
|
|
83
|
-
server.registerTool('route_list', {
|
|
84
|
-
description: 'The app\'s HTTP route table: framework-composed controller routes (method, full ' +
|
|
85
|
-
'path, Controller#handler) plus everything else mounted on the router, labeled ' +
|
|
86
|
-
'(mounted). Empty when the app builds no HTTP routes.',
|
|
87
|
-
inputSchema: {},
|
|
88
|
-
}, () => jsonText(collectRoutes(app) ?? []));
|
|
89
|
-
server.registerTool('module_graph', {
|
|
90
|
-
description: 'The loaded module graph as serializable descriptions (providers, exports, imports, ' +
|
|
91
|
-
'global/lazy flags). Pass tree=true to also get the rendered import tree lines.',
|
|
92
|
-
inputSchema: { tree: z.boolean().optional() },
|
|
93
|
-
}, ({ tree }) => {
|
|
94
|
-
const modules = collectModules(app);
|
|
95
|
-
return jsonText(tree ? { modules, tree: renderModuleTree(modules) } : modules);
|
|
96
|
-
});
|
|
97
|
-
server.registerTool('entrypoint_list', {
|
|
98
|
-
description: 'Every declared entrypoint kind (websocket, queue, cron, …) with its entries and ' +
|
|
99
|
-
'metadata — including kinds with zero entries. Lazy modules stay unmaterialized.',
|
|
100
|
-
inputSchema: {},
|
|
101
|
-
}, () => jsonText(collectEntrypoints(app)));
|
|
102
|
-
server.registerTool('openapi_dump', {
|
|
103
|
-
description: 'The OpenAPI 3.1 document for the app. Requires a rootModule in vela.config. ' +
|
|
104
|
-
'globalPrefix/title/apiVersion override the defaults (the app global prefix and ' +
|
|
105
|
-
'the module-derived info).',
|
|
106
|
-
inputSchema: {
|
|
107
|
-
globalPrefix: z.string().optional(),
|
|
108
|
-
title: z.string().optional(),
|
|
109
|
-
apiVersion: z.string().optional(),
|
|
110
|
-
},
|
|
111
|
-
}, ({ globalPrefix, title, apiVersion }) => {
|
|
112
|
-
if (!rootModule) {
|
|
113
|
-
return toolError('openapi_dump needs the root module. Add `rootModule: AppModule` to your vela.config.');
|
|
114
|
-
}
|
|
115
|
-
const info = {};
|
|
116
|
-
if (title)
|
|
117
|
-
info.title = title;
|
|
118
|
-
if (apiVersion)
|
|
119
|
-
info.version = apiVersion;
|
|
120
|
-
const document = createOpenApiDocument(rootModule, {
|
|
121
|
-
globalPrefix: globalPrefix ?? app.getGlobalPrefix(),
|
|
122
|
-
...(Object.keys(info).length > 0 ? { info } : {}),
|
|
123
|
-
});
|
|
124
|
-
return jsonText(document);
|
|
125
|
-
});
|
|
126
|
-
server.registerTool('token_describe', {
|
|
127
|
-
description: 'Look a DI token STRING LABEL up across the module graph: which modules provide/export ' +
|
|
128
|
-
'it and their scope flags, plus whether the string names a module. Read-only string ' +
|
|
129
|
-
'match — does not resolve or construct the token.',
|
|
130
|
-
inputSchema: { token: z.string() },
|
|
131
|
-
}, ({ token }) => jsonText(describeToken(app, token)));
|
|
132
|
-
if (rootModule) {
|
|
133
|
-
server.registerResource('openapi', OPENAPI_URI, { description: 'The OpenAPI 3.1 document for the app.', mimeType: 'application/json' }, () => ({
|
|
134
|
-
contents: [
|
|
135
|
-
{
|
|
136
|
-
uri: OPENAPI_URI,
|
|
137
|
-
mimeType: 'application/json',
|
|
138
|
-
text: JSON.stringify(createOpenApiDocument(rootModule, { globalPrefix: app.getGlobalPrefix() }), null, 2),
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
}));
|
|
142
|
-
}
|
|
143
|
-
const transport = new StdioServerTransport();
|
|
144
|
-
const closed = new Promise((resolvePromise) => {
|
|
145
|
-
transport.onclose = resolvePromise;
|
|
146
|
-
});
|
|
147
|
-
await server.connect(transport);
|
|
148
|
-
log(`vela mcp serve — ready (5 tools${rootModule ? ' + vela://openapi resource' : ''}). ` +
|
|
149
|
-
'Awaiting client on stdio; stdout is JSON-RPC only.');
|
|
150
|
-
// Keep the process alive until the client disconnects; only then dispose.
|
|
151
|
-
await closed;
|
|
152
|
-
return 0;
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
const dispose = app.dispose;
|
|
156
|
-
if (typeof dispose === 'function') {
|
|
157
|
-
try {
|
|
158
|
-
await dispose.call(app);
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
this.context.stderr.write(`Warning: teardown failed: ${String(error)}\n`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Command } from 'clipanion';
|
|
2
|
-
/** `vela db seed` — build the app from vela.config and run its seeders. */
|
|
3
|
-
export declare class SeedCommand extends Command {
|
|
4
|
-
static paths: string[][];
|
|
5
|
-
static usage: import("clipanion").Usage;
|
|
6
|
-
config: string | undefined;
|
|
7
|
-
continueOnError: boolean;
|
|
8
|
-
execute(): Promise<number>;
|
|
9
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { runSeeders } from '@velajs/vela/seeder';
|
|
2
|
-
import { Command, Option } from 'clipanion';
|
|
3
|
-
import { loadConfig } from '../config.js';
|
|
4
|
-
import { formatSeedResults } from '../format.js';
|
|
5
|
-
/** `vela db seed` — build the app from vela.config and run its seeders. */
|
|
6
|
-
export class SeedCommand extends Command {
|
|
7
|
-
static paths = [['db', 'seed']];
|
|
8
|
-
static usage = Command.Usage({
|
|
9
|
-
category: 'Database',
|
|
10
|
-
description: 'Run database seeders for the Vela app.',
|
|
11
|
-
details: 'Loads vela.config.{js,mjs,ts}, builds the app, and runs all @Seeder() classes in order.',
|
|
12
|
-
examples: [
|
|
13
|
-
['Run all seeders', 'vela db seed'],
|
|
14
|
-
['Use a specific config', 'vela db seed --config ./config/vela.config.js'],
|
|
15
|
-
],
|
|
16
|
-
});
|
|
17
|
-
config = Option.String('--config', { description: 'Path to the vela config file.' });
|
|
18
|
-
continueOnError = Option.Boolean('--continue-on-error', false, {
|
|
19
|
-
description: 'Run all seeders even if one fails.',
|
|
20
|
-
});
|
|
21
|
-
async execute() {
|
|
22
|
-
const { createApp } = await loadConfig(process.cwd(), this.config);
|
|
23
|
-
const app = await createApp();
|
|
24
|
-
this.context.stdout.write('Running seeders…\n');
|
|
25
|
-
const results = await runSeeders(app, { stopOnError: !this.continueOnError });
|
|
26
|
-
const code = formatSeedResults(results, (message) => this.context.stdout.write(`${message}\n`));
|
|
27
|
-
// Best-effort teardown (VelaApplication.dispose exists on recent versions).
|
|
28
|
-
// Must not clobber the computed exit code if a shutdown hook throws.
|
|
29
|
-
const dispose = app.dispose;
|
|
30
|
-
if (typeof dispose === 'function') {
|
|
31
|
-
try {
|
|
32
|
-
await dispose.call(app);
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
this.context.stderr.write(`Warning: teardown failed after seeding: ${String(error)}\n`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return code;
|
|
39
|
-
}
|
|
40
|
-
}
|
package/dist/format.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { SeederResult } from '@velajs/vela/seeder';
|
|
2
|
-
/**
|
|
3
|
-
* Render seeder results to a logger and return a process exit code
|
|
4
|
-
* (0 = all ran, 1 = at least one failed). Pure — no I/O beyond the logger.
|
|
5
|
-
*/
|
|
6
|
-
export declare function formatSeedResults(results: SeederResult[], log?: (message: string) => void): number;
|
|
7
|
-
/** Aligned plain-text table. Pure; returns lines. */
|
|
8
|
-
export declare function renderTable(headers: string[], rows: string[][]): string[];
|
package/dist/format.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Render seeder results to a logger and return a process exit code
|
|
3
|
-
* (0 = all ran, 1 = at least one failed). Pure — no I/O beyond the logger.
|
|
4
|
-
*/
|
|
5
|
-
export function formatSeedResults(results, log = (m) => console.log(m)) {
|
|
6
|
-
if (results.length === 0) {
|
|
7
|
-
log('No seeders found.');
|
|
8
|
-
return 0;
|
|
9
|
-
}
|
|
10
|
-
let failed = 0;
|
|
11
|
-
for (const result of results) {
|
|
12
|
-
if (result.ok) {
|
|
13
|
-
log(` ✓ ${result.name}`);
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
failed++;
|
|
17
|
-
log(` ✗ ${result.name}${result.error ? `: ${errorMessage(result.error)}` : ''}`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const total = results.length;
|
|
21
|
-
log(`\n${total - failed}/${total} seeders ran successfully.`);
|
|
22
|
-
return failed > 0 ? 1 : 0;
|
|
23
|
-
}
|
|
24
|
-
function errorMessage(error) {
|
|
25
|
-
return error instanceof Error ? error.message : String(error);
|
|
26
|
-
}
|
|
27
|
-
/** Aligned plain-text table. Pure; returns lines. */
|
|
28
|
-
export function renderTable(headers, rows) {
|
|
29
|
-
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)));
|
|
30
|
-
const line = (cells) => cells.map((c, i) => (c ?? '').padEnd(widths[i])).join(' ').trimEnd();
|
|
31
|
-
return [line(headers), line(widths.map((w) => '-'.repeat(w))), ...rows.map(line)];
|
|
32
|
-
}
|
package/dist/introspect.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { VelaApplication } from '@velajs/vela';
|
|
2
|
-
import type { ModuleDescription } from '@velajs/vela';
|
|
3
|
-
/** One row of `vela route list`. */
|
|
4
|
-
export interface RouteRow {
|
|
5
|
-
method: string;
|
|
6
|
-
path: string;
|
|
7
|
-
/** `Controller#handler`, or `(mounted)` for routes vela did not compose
|
|
8
|
-
* itself (RouteContributor/CRUD, OpenAPI UI mounts, manual Hono routes). */
|
|
9
|
-
handler: string;
|
|
10
|
-
source: 'controller' | 'mounted';
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* The app's route table: `describeRoutes()` rows (framework-composed truth)
|
|
14
|
-
* plus everything else present on the Hono router, deduped and labeled
|
|
15
|
-
* `(mounted)`. Returns null when the app never built HTTP routes.
|
|
16
|
-
*/
|
|
17
|
-
export declare function collectRoutes(app: VelaApplication): RouteRow[] | null;
|
|
18
|
-
/** `vela module graph` tree lines (or raw descriptions for --json). */
|
|
19
|
-
export declare function collectModules(app: VelaApplication): ModuleDescription[];
|
|
20
|
-
export declare function renderModuleTree(modules: ModuleDescription[]): string[];
|
|
21
|
-
/** One row of `vela entrypoint list`. */
|
|
22
|
-
export interface EntrypointRow {
|
|
23
|
-
kind: string;
|
|
24
|
-
target: string;
|
|
25
|
-
meta: string;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Every DECLARED entrypoint kind (from the global kind store — includes kinds
|
|
29
|
-
* with zero entries) joined with the app's entries. Metadata-only entries of
|
|
30
|
-
* lazy modules list fine; nothing materializes.
|
|
31
|
-
*/
|
|
32
|
-
export declare function collectEntrypoints(app: VelaApplication): EntrypointRow[];
|