@runa-cmd/core 0.2.0-beta.0 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -23
- package/dist/cli.d.ts +20 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +370 -123
- package/dist/cli.js.map +1 -1
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +1 -0
- package/dist/command.js.map +1 -1
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +72 -6
- package/dist/errors.js.map +1 -1
- package/dist/flag-name.d.ts +12 -0
- package/dist/flag-name.d.ts.map +1 -0
- package/dist/flag-name.js +19 -0
- package/dist/flag-name.js.map +1 -0
- package/dist/help.d.ts +21 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +152 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +10 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +9 -0
- package/dist/internal.js.map +1 -0
- package/dist/introspect.d.ts.map +1 -1
- package/dist/introspect.js +12 -3
- package/dist/introspect.js.map +1 -1
- package/dist/lifecycle.js +1 -1
- package/dist/lifecycle.js.map +1 -1
- package/dist/parse/adapters/zod-v4.d.ts.map +1 -1
- package/dist/parse/adapters/zod-v4.js +6 -3
- package/dist/parse/adapters/zod-v4.js.map +1 -1
- package/dist/parse/deprecated.d.ts +12 -0
- package/dist/parse/deprecated.d.ts.map +1 -0
- package/dist/parse/deprecated.js +21 -0
- package/dist/parse/deprecated.js.map +1 -0
- package/dist/parse/parseargs-bridge.d.ts.map +1 -1
- package/dist/parse/parseargs-bridge.js +14 -10
- package/dist/parse/parseargs-bridge.js.map +1 -1
- package/dist/parse/resolve.d.ts +34 -4
- package/dist/parse/resolve.d.ts.map +1 -1
- package/dist/parse/resolve.js +134 -54
- package/dist/parse/resolve.js.map +1 -1
- package/dist/parse/schema-adapter.d.ts +5 -3
- package/dist/parse/schema-adapter.d.ts.map +1 -1
- package/dist/parse/schema-adapter.js.map +1 -1
- package/dist/parse/schema-walker.d.ts.map +1 -1
- package/dist/parse/schema-walker.js +1 -0
- package/dist/parse/schema-walker.js.map +1 -1
- package/dist/parse/strict.d.ts +36 -2
- package/dist/parse/strict.d.ts.map +1 -1
- package/dist/parse/strict.js +53 -15
- package/dist/parse/strict.js.map +1 -1
- package/dist/plugin.d.ts +12 -3
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +60 -9
- package/dist/plugin.js.map +1 -1
- package/dist/stop.d.ts +25 -0
- package/dist/stop.d.ts.map +1 -0
- package/dist/stop.js +110 -0
- package/dist/stop.js.map +1 -0
- package/dist/suggest.d.ts +3 -2
- package/dist/suggest.d.ts.map +1 -1
- package/dist/suggest.js +5 -4
- package/dist/suggest.js.map +1 -1
- package/dist/tool-channel.d.ts +48 -0
- package/dist/tool-channel.d.ts.map +1 -0
- package/dist/tool-channel.js +46 -0
- package/dist/tool-channel.js.map +1 -0
- package/dist/types.d.ts +51 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# @runa-cmd/core
|
|
2
2
|
|
|
3
|
-
The engine of the Runa CLI framework. Schema-driven, TypeScript-first, zero runtime dependencies.
|
|
3
|
+
The engine of the Runa CLI framework. Schema-driven, TypeScript-first, with zero runtime dependencies besides Zod as a required peer.
|
|
4
4
|
|
|
5
|
-
Define your CLI with Zod schemas and get full type inference, validation, help
|
|
5
|
+
Define your CLI with Zod schemas and get full type inference, validation, and metadata for help, completions and MCP plugins — from a single source of truth.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @runa-cmd/core zod
|
|
10
|
+
pnpm add @runa-cmd/core@beta zod
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
> Requires Node
|
|
13
|
+
> Requires Node 24+. Zod v4 is a required peer dependency. Without `@beta`, npm installs the older `0.1.0`.
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
@@ -30,7 +30,7 @@ runCLI(greet);
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
```
|
|
33
|
-
$
|
|
33
|
+
$ greet world
|
|
34
34
|
Hello, world!
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -47,7 +47,7 @@ const deploy = defineCommand({
|
|
|
47
47
|
description: 'Deploy to an environment',
|
|
48
48
|
options: {
|
|
49
49
|
env: { alias: ['-e'], env: 'DEPLOY_ENV' },
|
|
50
|
-
replicas: { group: 'Scaling'
|
|
50
|
+
replicas: { group: 'Scaling' },
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
args: {
|
|
@@ -80,7 +80,7 @@ const deploy = defineCommand({
|
|
|
80
80
|
| `meta.name` | `string` | Yes | Command name |
|
|
81
81
|
| `meta.description` | `string` | Yes | One-line description |
|
|
82
82
|
| `meta.version` | `string` | No | Command version |
|
|
83
|
-
| `meta.options` | `Record<string, OptionMeta>` | No | Per-option metadata (alias, env, group, deprecated
|
|
83
|
+
| `meta.options` | `Record<string, OptionMeta>` | No | Per-option metadata (alias, env, group, deprecated) |
|
|
84
84
|
| `args` | `Record<string, ZodType>` | No | Positional arguments (insertion order matters) |
|
|
85
85
|
| `options` | `Record<string, ZodType>` | No | Named options (--flag style) |
|
|
86
86
|
| `output` | `ZodType` | No | Output schema — if set, `run()` must return matching data |
|
|
@@ -95,8 +95,8 @@ Operational metadata lives in `meta.options`, separate from the Zod schema:
|
|
|
95
95
|
| `alias` | `string[]` | Short flags, e.g. `['-e']` (must include dash prefix) |
|
|
96
96
|
| `env` | `string` | Environment variable name, e.g. `'DEPLOY_ENV'` |
|
|
97
97
|
| `group` | `string` | Group name for help display, e.g. `'Scaling'` |
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
98
|
+
| `valueName` | `string` | What help calls the value, e.g. `'hex'` for `--color <hex>`. Defaults to its type |
|
|
99
|
+
| `deprecated` | `string` | Deprecation notice, shown in help and as a stderr warning when the option is used |
|
|
100
100
|
|
|
101
101
|
#### Run Context
|
|
102
102
|
|
|
@@ -109,6 +109,8 @@ interface RunContext {
|
|
|
109
109
|
globalOptions: Record<string, unknown>;
|
|
110
110
|
command: CommandMeta; // Current command metadata
|
|
111
111
|
rawArgs: string[]; // Original argv
|
|
112
|
+
rest: string[]; // With `rest: true`, what's left after `--`. Unvalidated
|
|
113
|
+
signal: AbortSignal; // Aborted when the run must stop
|
|
112
114
|
}
|
|
113
115
|
```
|
|
114
116
|
|
|
@@ -151,22 +153,101 @@ cli.run();
|
|
|
151
153
|
| `plugins` | `PluginConfig[]` | No | Plugins to load |
|
|
152
154
|
| `middleware` | `Middleware[]` | No | Global middleware chain |
|
|
153
155
|
| `globalOptions` | `Record<string, ZodType>` | No | Options available to all commands |
|
|
154
|
-
| `globalMeta` | `Record<string, OptionMeta
|
|
156
|
+
| `globalMeta` | `{ options?: Record<string, OptionMeta> }` | No | Metadata for global options |
|
|
155
157
|
| `config` | `CLIConfigOptions` | No | Config file loading settings |
|
|
158
|
+
| `strictOptions` | `boolean` | No | Reject unknown options. Default: `true` |
|
|
156
159
|
|
|
157
|
-
### `runCLI(command,
|
|
160
|
+
### `runCLI(command, options?)`
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
The API for a CLI made of one command. It takes the same options as `defineCLI`, except `meta` and `commands`, which come from the command, plus `argv`.
|
|
160
163
|
|
|
161
164
|
```ts
|
|
162
|
-
runCLI(myCommand
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
runCLI(myCommand, {
|
|
166
|
+
plugins: [helpPlugin(), mcpPlugin()],
|
|
167
|
+
argv: ['hello'], // Default: process.argv.slice(2)
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The command is the root of the CLI, so a value equal to its name is data: `tool tool` passes `"tool"`. If a plugin adds a subcommand, that name routes to it, and `--` passes it as data: `tool -- completions`.
|
|
172
|
+
|
|
173
|
+
`defineCLI` with a single command also works, but it's deprecated: adding a plugin that registers a command silently changes how it routes.
|
|
174
|
+
|
|
175
|
+
### Options that repeat
|
|
176
|
+
|
|
177
|
+
A `z.array()` option repeats, and each element is converted to its type: `--port 80 --port 81` gives `[80, 81]` with `z.array(z.number())`.
|
|
178
|
+
|
|
179
|
+
| Source | Form | Result |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| Terminal | `--tag a --tag b` | `['a', 'b']` |
|
|
182
|
+
| Terminal | `--tag a,b` | `['a,b']` |
|
|
183
|
+
| Env | `TAGS=a, b` | `['a', 'b']` |
|
|
184
|
+
| Config | `{ "tag": ["a", "b"] }` | `['a', 'b']` |
|
|
185
|
+
|
|
186
|
+
Each source replaces the ones below it, without adding up. A value with a comma can't go through env: use the config file.
|
|
187
|
+
|
|
188
|
+
### Option names
|
|
189
|
+
|
|
190
|
+
Write `darkColor` in code and `--dark-color` at the terminal. `run()`, config files and MCP tools keep the key.
|
|
191
|
+
|
|
192
|
+
| Key | Flag |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `darkColor` | `--dark-color` |
|
|
195
|
+
| `outputURL` | `--output-url` |
|
|
196
|
+
| `dark-color` | `--dark-color`, a key with hyphens stays as written |
|
|
197
|
+
|
|
198
|
+
`--darkColor` is a usage error that points to `--dark-color`. `--no-dark-mode` negates `darkMode`.
|
|
199
|
+
|
|
200
|
+
A boolean named `noCache` stops the CLI at startup, because `--no-cache` already negates `cache`. Declare `cache` with `.default(true)` instead.
|
|
201
|
+
|
|
202
|
+
### `--help`
|
|
203
|
+
|
|
204
|
+
Every CLI has `--help` and `-h`, with no plugin: `tool --help` shows the CLI or the root command, and `tool db migrate --help` shows that command, with the global options that work on it.
|
|
205
|
+
|
|
206
|
+
Each flag yields separately. If a command declares `-h`, for `--host` say, it keeps it and `--help` still shows help. For themes and layout, add `@runa-cmd/help`. To show help your own way from a plugin, declare the `replaceHelp` capability:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
definePlugin({
|
|
210
|
+
meta: { name: 'my-help' },
|
|
211
|
+
capabilities: { replaceHelp: true },
|
|
212
|
+
setup(api) {
|
|
213
|
+
api.replaceHelp((target, output) => output.write(`${render(target)}\n`));
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`target` is a `CLISchema` or a `CommandSchema`. `output` is stdout, or stderr when there was nothing to run. Only one plugin can replace the help.
|
|
219
|
+
|
|
220
|
+
With nothing to run, as in `tool` on a CLI with several commands or `tool db` on a group, help goes to stderr and the exit code is 2. A script with an empty command fails instead of passing. A name that doesn't exist is still a 127, with a suggestion.
|
|
221
|
+
|
|
222
|
+
`--help` and `--version` work even if a required global option is missing.
|
|
223
|
+
|
|
224
|
+
### `--version`
|
|
225
|
+
|
|
226
|
+
Printed when `meta.version` is set, from `defineCLI` or from the command in `runCLI`. It has no short alias. If the command declares its own `--version` option, that option wins.
|
|
227
|
+
|
|
228
|
+
### The `--` separator
|
|
229
|
+
|
|
230
|
+
Everything after the first `--` is never read as a flag. Those tokens fill the positionals still empty, where a variadic counts as one.
|
|
231
|
+
|
|
232
|
+
What's left is a usage error, so nothing gets lost in silence. A command that wraps another one declares `rest: true` to receive it in `ctx.rest`:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const run = defineCommand({
|
|
236
|
+
meta: { name: 'run', description: 'Run a program' },
|
|
237
|
+
rest: true,
|
|
238
|
+
run({ rest }) {
|
|
239
|
+
spawn(rest[0], rest.slice(1));
|
|
240
|
+
},
|
|
241
|
+
});
|
|
168
242
|
```
|
|
169
243
|
|
|
244
|
+
| Input | Without `rest: true` | With `rest: true` |
|
|
245
|
+
|---|---|---|
|
|
246
|
+
| `tool -- -text` | `args.content` is `-text` | Same |
|
|
247
|
+
| `lint -- -a -b` | `args.files` is `['-a', '-b']` | Same |
|
|
248
|
+
| `copy a b -- -c` | Exit 2 | `ctx.rest` is `['-c']` |
|
|
249
|
+
| `run -- npm test` (no positionals) | Exit 2 | `ctx.rest` is `['npm', 'test']` |
|
|
250
|
+
|
|
170
251
|
### `defineMiddleware(handler)`
|
|
171
252
|
|
|
172
253
|
Creates middleware using the onion model (like Koa/Hono):
|
|
@@ -214,6 +295,7 @@ The `setup(api)` function receives:
|
|
|
214
295
|
| `api.hook(name, handler)` | Register a lifecycle hook handler |
|
|
215
296
|
| `api.getSchema()` | Get the introspected CLI schema |
|
|
216
297
|
| `api.getCommands()` | Get the live command tree reference |
|
|
298
|
+
| `api.getConfigValues()` | Get the values of the CLI's config file, if it has one |
|
|
217
299
|
|
|
218
300
|
### `getSchema(config)`
|
|
219
301
|
|
|
@@ -228,15 +310,15 @@ const schema = getSchema(cliConfig);
|
|
|
228
310
|
|
|
229
311
|
### `jsonLoader()`
|
|
230
312
|
|
|
231
|
-
Built-in JSON config file loader:
|
|
313
|
+
Built-in JSON config file loader, always active when `config` is set:
|
|
232
314
|
|
|
233
315
|
```ts
|
|
234
316
|
const cli = defineCLI({
|
|
235
317
|
// ...
|
|
236
318
|
config: {
|
|
237
|
-
name: 'my-tool', // Searches for my-tool.config.json
|
|
238
|
-
|
|
239
|
-
|
|
319
|
+
name: 'my-tool', // Searches for my-tool.config.json
|
|
320
|
+
searchPaths: ['.', '~'], // Where to search. This is the default
|
|
321
|
+
// loaders: [...] // Extra formats. The first match wins
|
|
240
322
|
},
|
|
241
323
|
});
|
|
242
324
|
```
|
|
@@ -255,6 +337,36 @@ import {
|
|
|
255
337
|
throw new CommandError('Deployment failed', { code: 'DEPLOY_FAILED', exitCode: 3 });
|
|
256
338
|
```
|
|
257
339
|
|
|
340
|
+
#### How a failed run ends
|
|
341
|
+
|
|
342
|
+
No failure exits silently. The reason goes to stderr, `cleanup` runs, and then the process exits.
|
|
343
|
+
|
|
344
|
+
| Case | stderr | Exit code |
|
|
345
|
+
|---|---|---|
|
|
346
|
+
| `RunaError` with a message | The message, as written | The error's |
|
|
347
|
+
| Any other error | `Unexpected error in '<command>': <message>` and a hint | 1 |
|
|
348
|
+
| Error without a message | `'<command>' failed without an error message (<type>).` and a hint | The error's, or 1 |
|
|
349
|
+
| `onError` sets `ctx.handled` | Nothing from Runa | The error's, or 1 |
|
|
350
|
+
|
|
351
|
+
- Usage errors end with `Run '<cli> --help' for usage.`
|
|
352
|
+
- Throw `CommandError` for messages meant for people using your CLI.
|
|
353
|
+
- Set `RUNA_DEBUG=1` to add the stack trace, error code and cause.
|
|
354
|
+
- `ctx.handled` only stops the default output. If a plugin truly recovered, it sets `process.exitCode = 0` in `onError`.
|
|
355
|
+
|
|
356
|
+
#### How a run stops
|
|
357
|
+
|
|
358
|
+
On `SIGINT` or `SIGTERM`, Runa aborts `ctx.signal` and runs `cleanup`, and the process exits with 130 or 143. A command that ignores the signal stops being waited for after 2 seconds, and the process ends after 5 at most. A second signal leaves at once.
|
|
359
|
+
|
|
360
|
+
Pass `signal` to whatever the command starts, so the stop reaches it too:
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
async run({ signal }) {
|
|
364
|
+
await fetch(url, { signal });
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
A command that blocks the event loop can't react to a signal until it yields. Use `Ctrl+\` to end it at once.
|
|
369
|
+
|
|
258
370
|
### Constants
|
|
259
371
|
|
|
260
372
|
```ts
|
|
@@ -309,7 +421,7 @@ beforeRun
|
|
|
309
421
|
↓
|
|
310
422
|
afterRun
|
|
311
423
|
↓
|
|
312
|
-
cleanup ← (
|
|
424
|
+
cleanup ← (Runs on errors and on signals, within the stop deadline)
|
|
313
425
|
|
|
314
426
|
On error → onError hook fires before cleanup
|
|
315
427
|
```
|
|
@@ -328,6 +440,8 @@ For example, if an option has `env: 'PORT'` and `z.number().default(3000)`:
|
|
|
328
440
|
3. Config file value wins over default
|
|
329
441
|
4. `3000` is the fallback
|
|
330
442
|
|
|
443
|
+
Boolean options accept `true`, `1`, `false` and `0`, in any case, from every source: `MINIFY=1`, `--minify=false`, or `"minify": true` in a config file. Any other value is a usage error.
|
|
444
|
+
|
|
331
445
|
## Types
|
|
332
446
|
|
|
333
447
|
All types are exported for consumers who need them:
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import type { CLI, CLIConfig, Command } from './types.js';
|
|
1
|
+
import type { CLI, CLIConfig, Command, RunCLIOptions } from './types.js';
|
|
2
|
+
/** A command to run from an object, with no argv to parse (ADR-0030). */
|
|
3
|
+
export interface ToolCall {
|
|
4
|
+
/** Command path in the tree, e.g. ['config', 'set'] */
|
|
5
|
+
path: string[];
|
|
6
|
+
input: Record<string, unknown>;
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* Create a CLI from a configuration.
|
|
4
10
|
* Returns a branded CLI object with a .run() method.
|
|
5
11
|
*/
|
|
6
12
|
export declare function defineCLI(config: CLIConfig): CLI;
|
|
7
13
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
14
|
+
* Run a CLI made of one command. Accepts everything defineCLI() does except
|
|
15
|
+
* meta and commands, which come from the command: plugins, middleware, config.
|
|
10
16
|
*/
|
|
11
|
-
export declare function runCLI(command: Command,
|
|
17
|
+
export declare function runCLI(command: Command, options?: RunCLIOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Run a command from an object, through the same lifecycle as a CLI run.
|
|
20
|
+
* Internal, for @runa-cmd/mcp: not exported from the package barrel.
|
|
21
|
+
*
|
|
22
|
+
* @param options.argv - Startup arguments, read for global options only
|
|
23
|
+
*/
|
|
24
|
+
export declare function runToolCall(config: CLIConfig, call: ToolCall, options?: {
|
|
25
|
+
argv?: string[];
|
|
26
|
+
rootName?: string;
|
|
27
|
+
}): Promise<void>;
|
|
12
28
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EACX,GAAG,EACH,SAAS,EAET,OAAO,EAOP,aAAa,EACb,MAAM,YAAY,CAAC;AA0IpB,yEAAyE;AACzE,MAAM,WAAW,QAAQ;IACxB,uDAAuD;IACvD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AA4nBD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,GAAG,CAQhD;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CASzF;AAkBD;;;;;GAKG;AACH,wBAAsB,WAAW,CAChC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,IAAI,CAAC,CAEf"}
|