Skip to content

@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.

Terminal window
yarn add @invokta/cli

The package is native ESM and requires Node.js 22.20.0 or later. It exports only runCli, RunCliOptions, and CliIo from the package root.

engine list
engine describe <capability-id>
engine run <capability-id> --input '<json>'
engine run <capability-id> --stdin

Writes the frozen summaries returned by engine.list().

Terminal window
node dist/cli.js list

Writes the selected capability contract returned by engine.describe().

Terminal window
node dist/cli.js describe support.classify-ticket

Exactly one input source is required. Pass a JSON object as an argument:

Terminal window
node dist/cli.js run support.classify-ticket \
--input '{"ticketId":"T-123"}'

Or read strict UTF-8 JSON from standard input:

Terminal window
printf '%s' '{"ticketId":"T-123"}' | \
node dist/cli.js run support.classify-ticket --stdin

Missing, 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.

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.