@invokta/cli
@invokta/cli exposes an engine through list, describe, and run. A run
always calls engine.invoke; the adapter does not import handlers or reproduce
domain validation, authorization, events, or error normalization.
Install
Section titled “Install”yarn add @invokta/cliThe package is native ESM and requires Node.js 22.20.0 or later. It exports only
runCli, RunCliOptions, and CliIo from the package root.
Commands
Section titled “Commands”engine listengine describe <capability-id>engine run <capability-id> --input '<json>'engine run <capability-id> --stdinWrites the frozen summaries returned by engine.list().
node dist/cli.js listdescribe
Section titled “describe”Writes the selected capability contract returned by engine.describe().
node dist/cli.js describe support.classify-ticketExactly one input source is required. Pass a JSON object as an argument:
node dist/cli.js run support.classify-ticket \ --input '{"ticketId":"T-123"}'Or read strict UTF-8 JSON from standard input:
printf '%s' '{"ticketId":"T-123"}' | \ node dist/cli.js run support.classify-ticket --stdinMissing, duplicate, combined, trailing, or unknown arguments are invalid usage. The CLI has no actor, role, login, or principal-selection flag. The composition root supplies trusted local identity.
import { runCli, type CliIo, type RunCliOptions,} from "@invokta/cli";interface CliIo { readonly readStdin: () => Promise<string>; readonly writeStdout: ( text: string, ) => void | Promise<void>; readonly writeStderr: ( text: string, ) => void | Promise<void>;}
interface RunCliOptions { readonly argv?: readonly string[]; readonly principal: Principal | null; readonly signal?: AbortSignal; readonly format?: "json" | "human"; readonly io?: Partial<CliIo>;}
function runCli<Capabilities extends CapabilityMap>( engine: Engine<Capabilities>, options: RunCliOptions,): Promise<number>;argv defaults to process.argv.slice(2). The principal property must exist,
including when its value is null. An absent property is invalid usage.
signal is passed unchanged to engine.invoke for run. format defaults to
compact json; human uses deterministic two-space JSON formatting for
successful results. Error output remains compact in either mode.
io can replace any subset of standard input, output, and diagnostics. Both
synchronous and asynchronous writers are supported and awaited; unspecified
methods use the process defaults.
const exitCode = await runCli(engine, { argv: [ "run", "support.classify-ticket", "--input", '{"ticketId":"T-123"}', ], principal: { id: "local:operator" }, signal: controller.signal, io: { async writeStdout(text) { await resultFile.write(text); }, },});
process.exitCode = exitCode;runCli returns the numeric code. It never calls process.exit or assigns
process.exitCode.
Process contract
Section titled “Process contract”| Exit code | Meaning |
|---|---|
0 |
Command and output write completed successfully |
1 |
Invocation, cancellation, validation, authorization, or output write failed |
2 |
Invalid command, arguments, JSON, principal option, or UTF-8 input |
Standard output contains only the requested JSON result. Diagnostics and compact errors are written to standard error.
An engine error contains code, message, and optional publicDetails. Cause,
stack, rejected input, and principal are not serialized. Unknown failures and
standard-output write failures are sanitized as EXECUTION_FAILED. A failed
diagnostic write is contained and does not replace the chosen exit code.
The default stdin reader performs fatal incremental UTF-8 decoding, including
multibyte sequences split across chunks. Invokta does not impose a default stdin
size limit; a host receiving untrusted local input must provide a bounded
readStdin implementation.