@runa-cmd/core 0.2.0-beta.0 → 0.2.0-beta.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/README.md +125 -23
- package/dist/cli.d.ts +20 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +361 -123
- package/dist/cli.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 +151 -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 +10 -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 +33 -2
- package/dist/parse/strict.d.ts.map +1 -1
- package/dist/parse/strict.js +39 -13
- 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 +43 -7
- 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,7 @@ 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
|
-
| `deprecated` | `string
|
|
99
|
-
| `hint` | `string` | Value hint for help, e.g. `'<count>'` |
|
|
98
|
+
| `deprecated` | `string` | Deprecation notice, shown in help and as a stderr warning when the option is used |
|
|
100
99
|
|
|
101
100
|
#### Run Context
|
|
102
101
|
|
|
@@ -109,6 +108,8 @@ interface RunContext {
|
|
|
109
108
|
globalOptions: Record<string, unknown>;
|
|
110
109
|
command: CommandMeta; // Current command metadata
|
|
111
110
|
rawArgs: string[]; // Original argv
|
|
111
|
+
rest: string[]; // After `--`, what didn't fill a positional. Unvalidated
|
|
112
|
+
signal: AbortSignal; // Aborted when the run must stop
|
|
112
113
|
}
|
|
113
114
|
```
|
|
114
115
|
|
|
@@ -151,22 +152,90 @@ cli.run();
|
|
|
151
152
|
| `plugins` | `PluginConfig[]` | No | Plugins to load |
|
|
152
153
|
| `middleware` | `Middleware[]` | No | Global middleware chain |
|
|
153
154
|
| `globalOptions` | `Record<string, ZodType>` | No | Options available to all commands |
|
|
154
|
-
| `globalMeta` | `Record<string, OptionMeta
|
|
155
|
+
| `globalMeta` | `{ options?: Record<string, OptionMeta> }` | No | Metadata for global options |
|
|
155
156
|
| `config` | `CLIConfigOptions` | No | Config file loading settings |
|
|
157
|
+
| `strictOptions` | `boolean` | No | Reject unknown options. Default: `true` |
|
|
156
158
|
|
|
157
|
-
### `runCLI(command,
|
|
159
|
+
### `runCLI(command, options?)`
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
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
162
|
|
|
161
163
|
```ts
|
|
162
|
-
runCLI(myCommand
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
runCLI(myCommand, {
|
|
165
|
+
plugins: [helpPlugin(), mcpPlugin()],
|
|
166
|
+
argv: ['hello'], // Default: process.argv.slice(2)
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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`.
|
|
171
|
+
|
|
172
|
+
`defineCLI` with a single command also works, but it's deprecated: adding a plugin that registers a command silently changes how it routes.
|
|
173
|
+
|
|
174
|
+
### Options that repeat
|
|
175
|
+
|
|
176
|
+
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())`.
|
|
177
|
+
|
|
178
|
+
| Source | Form | Result |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| Terminal | `--tag a --tag b` | `['a', 'b']` |
|
|
181
|
+
| Terminal | `--tag a,b` | `['a,b']` |
|
|
182
|
+
| Env | `TAGS=a, b` | `['a', 'b']` |
|
|
183
|
+
| Config | `{ "tag": ["a", "b"] }` | `['a', 'b']` |
|
|
184
|
+
|
|
185
|
+
Each source replaces the ones below it, without adding up. A value with a comma can't go through env: use the config file.
|
|
186
|
+
|
|
187
|
+
### Option names
|
|
188
|
+
|
|
189
|
+
Write `darkColor` in code and `--dark-color` at the terminal. `run()`, config files and MCP tools keep the key.
|
|
190
|
+
|
|
191
|
+
| Key | Flag |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `darkColor` | `--dark-color` |
|
|
194
|
+
| `outputURL` | `--output-url` |
|
|
195
|
+
| `dark-color` | `--dark-color`, a key with hyphens stays as written |
|
|
196
|
+
|
|
197
|
+
`--darkColor` is a usage error that points to `--dark-color`. `--no-dark-mode` negates `darkMode`.
|
|
198
|
+
|
|
199
|
+
A boolean named `noCache` stops the CLI at startup, because `--no-cache` already negates `cache`. Declare `cache` with `.default(true)` instead.
|
|
200
|
+
|
|
201
|
+
### `--help`
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
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:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
definePlugin({
|
|
209
|
+
meta: { name: 'my-help' },
|
|
210
|
+
capabilities: { replaceHelp: true },
|
|
211
|
+
setup(api) {
|
|
212
|
+
api.replaceHelp((target, output) => output.write(`${render(target)}\n`));
|
|
213
|
+
},
|
|
214
|
+
});
|
|
168
215
|
```
|
|
169
216
|
|
|
217
|
+
`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.
|
|
218
|
+
|
|
219
|
+
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.
|
|
220
|
+
|
|
221
|
+
`--help` and `--version` work even if a required global option is missing.
|
|
222
|
+
|
|
223
|
+
### `--version`
|
|
224
|
+
|
|
225
|
+
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.
|
|
226
|
+
|
|
227
|
+
### The `--` separator
|
|
228
|
+
|
|
229
|
+
Everything after the first `--` is never read as a flag. Those tokens fill the positionals still empty, where a variadic counts as one, and what's left arrives in `ctx.rest`.
|
|
230
|
+
|
|
231
|
+
| Input | Result |
|
|
232
|
+
|---|---|
|
|
233
|
+
| `tool -- -text` | `args.content` is `-text` |
|
|
234
|
+
| `copy a b -- -c` | `ctx.rest` is `['-c']` |
|
|
235
|
+
| `lint -- -a -b` | `args.files` is `['-a', '-b']` |
|
|
236
|
+
| `lint a.ts -- --fix` | `args.files` is `['a.ts']`, `ctx.rest` is `['--fix']` |
|
|
237
|
+
| `run -- npm test` (no positionals) | `ctx.rest` is `['npm', 'test']` |
|
|
238
|
+
|
|
170
239
|
### `defineMiddleware(handler)`
|
|
171
240
|
|
|
172
241
|
Creates middleware using the onion model (like Koa/Hono):
|
|
@@ -214,6 +283,7 @@ The `setup(api)` function receives:
|
|
|
214
283
|
| `api.hook(name, handler)` | Register a lifecycle hook handler |
|
|
215
284
|
| `api.getSchema()` | Get the introspected CLI schema |
|
|
216
285
|
| `api.getCommands()` | Get the live command tree reference |
|
|
286
|
+
| `api.getConfigValues()` | Get the values of the CLI's config file, if it has one |
|
|
217
287
|
|
|
218
288
|
### `getSchema(config)`
|
|
219
289
|
|
|
@@ -228,15 +298,15 @@ const schema = getSchema(cliConfig);
|
|
|
228
298
|
|
|
229
299
|
### `jsonLoader()`
|
|
230
300
|
|
|
231
|
-
Built-in JSON config file loader:
|
|
301
|
+
Built-in JSON config file loader, always active when `config` is set:
|
|
232
302
|
|
|
233
303
|
```ts
|
|
234
304
|
const cli = defineCLI({
|
|
235
305
|
// ...
|
|
236
306
|
config: {
|
|
237
|
-
name: 'my-tool', // Searches for my-tool.config.json
|
|
238
|
-
|
|
239
|
-
|
|
307
|
+
name: 'my-tool', // Searches for my-tool.config.json
|
|
308
|
+
searchPaths: ['.', '~'], // Where to search. This is the default
|
|
309
|
+
// loaders: [...] // Extra formats. The first match wins
|
|
240
310
|
},
|
|
241
311
|
});
|
|
242
312
|
```
|
|
@@ -255,6 +325,36 @@ import {
|
|
|
255
325
|
throw new CommandError('Deployment failed', { code: 'DEPLOY_FAILED', exitCode: 3 });
|
|
256
326
|
```
|
|
257
327
|
|
|
328
|
+
#### How a failed run ends
|
|
329
|
+
|
|
330
|
+
No failure exits silently. The reason goes to stderr, `cleanup` runs, and then the process exits.
|
|
331
|
+
|
|
332
|
+
| Case | stderr | Exit code |
|
|
333
|
+
|---|---|---|
|
|
334
|
+
| `RunaError` with a message | The message, as written | The error's |
|
|
335
|
+
| Any other error | `Unexpected error in '<command>': <message>` and a hint | 1 |
|
|
336
|
+
| Error without a message | `'<command>' failed without an error message (<type>).` and a hint | The error's, or 1 |
|
|
337
|
+
| `onError` sets `ctx.handled` | Nothing from Runa | The error's, or 1 |
|
|
338
|
+
|
|
339
|
+
- Usage errors end with `Run '<cli> --help' for usage.`
|
|
340
|
+
- Throw `CommandError` for messages meant for people using your CLI.
|
|
341
|
+
- Set `RUNA_DEBUG=1` to add the stack trace, error code and cause.
|
|
342
|
+
- `ctx.handled` only stops the default output. If a plugin truly recovered, it sets `process.exitCode = 0` in `onError`.
|
|
343
|
+
|
|
344
|
+
#### How a run stops
|
|
345
|
+
|
|
346
|
+
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.
|
|
347
|
+
|
|
348
|
+
Pass `signal` to whatever the command starts, so the stop reaches it too:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
async run({ signal }) {
|
|
352
|
+
await fetch(url, { signal });
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
A command that blocks the event loop can't react to a signal until it yields. Use `Ctrl+\` to end it at once.
|
|
357
|
+
|
|
258
358
|
### Constants
|
|
259
359
|
|
|
260
360
|
```ts
|
|
@@ -309,7 +409,7 @@ beforeRun
|
|
|
309
409
|
↓
|
|
310
410
|
afterRun
|
|
311
411
|
↓
|
|
312
|
-
cleanup ← (
|
|
412
|
+
cleanup ← (Runs on errors and on signals, within the stop deadline)
|
|
313
413
|
|
|
314
414
|
On error → onError hook fires before cleanup
|
|
315
415
|
```
|
|
@@ -328,6 +428,8 @@ For example, if an option has `env: 'PORT'` and `z.number().default(3000)`:
|
|
|
328
428
|
3. Config file value wins over default
|
|
329
429
|
4. `3000` is the fallback
|
|
330
430
|
|
|
431
|
+
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.
|
|
432
|
+
|
|
331
433
|
## Types
|
|
332
434
|
|
|
333
435
|
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;AAmnBD;;;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"}
|