101 lines
3.9 KiB
Markdown
101 lines
3.9 KiB
Markdown
# CLI — `cli.ts`
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
Owns the five command surfaces — `createCommand`, `devCommand`,
|
|
`buildCommand`, `packageCommand`, `signCommand` — plus the
|
|
`runCli(argv, deps?)` dispatcher that the published binary delegates to.
|
|
Every command is a pure function: it validates its option struct against
|
|
the matching Zod schema, runs the orchestrated work, and returns a typed
|
|
report. `runCli` never calls `process.exit`; the only exit-call lives in
|
|
`src/bin/suaveplan-electrobun.ts`, so the entire CLI is unit-testable
|
|
without a child process.
|
|
|
|
`createCommand` is the scaffolder entry: it loads the layered config,
|
|
applies CLI overrides, optionally runs the prompt flow, then hands off
|
|
to `generate`. The other four commands are runner-only — they probe the
|
|
upstream `electrobun` binary, raise `ELECTROBUN.BINARY_UNAVAILABLE` when
|
|
missing, spawn the binary with the canonical argv, and return a
|
|
`CommandReport` carrying exit code, stdout, stderr, and duration.
|
|
|
|
## Features
|
|
|
|
- Five command functions, each with Zod-validated options and structured
|
|
reports.
|
|
- Pure `runCli(argv)` dispatcher with `--help` / `-h` short-circuit and
|
|
closed-set flag parsing.
|
|
- Long-flag `--key=value` and `--key value` forms supported uniformly.
|
|
- Unknown flags / subcommands raise `ELECTROBUN.UNKNOWN_FLAG`.
|
|
- Missing required values raise `ELECTROBUN.INVALID_OPTIONS`.
|
|
- `timeRunner` wrapper increments invocation + duration histograms per
|
|
call and increments failure counters on `ElectrobunError` paths.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { runCli } from "@suaveplan/electrobun/cli";
|
|
|
|
const { code } = await runCli(process.argv.slice(2));
|
|
process.exit(code);
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
import { buildCommand } from "@suaveplan/electrobun/cli";
|
|
import type { CommandRunner } from "@suaveplan/electrobun/types";
|
|
|
|
const customRunner: CommandRunner = async (input) => {
|
|
// ship the spawn to a remote CI worker
|
|
return await postToWorker(input);
|
|
};
|
|
const report = await buildCommand(
|
|
{ cwd: "/proj", target: "linux", update: true },
|
|
{ runner: customRunner }
|
|
);
|
|
```
|
|
|
|
## API Reference
|
|
|
|
- `createCommand(opts: CreateCommandOptions, deps: CommandDeps & {
|
|
prompter: Prompter }): Promise<GenerateReport>`.
|
|
- `devCommand(opts: DevCommandOptions, deps?: { runner? }):
|
|
Promise<CommandReport>`.
|
|
- `buildCommand(opts: BuildCommandOptions, deps?: { runner? }):
|
|
Promise<CommandReport>`.
|
|
- `packageCommand(opts: PackageCommandOptions, deps?: { runner? }):
|
|
Promise<CommandReport>`.
|
|
- `signCommand(opts: SignCommandOptions, deps?: { runner? }):
|
|
Promise<CommandReport>`.
|
|
- `runCli(argv: readonly string[], deps?: RunCliOptions): Promise<{
|
|
code: number }>`.
|
|
- `HELP_TEXT: string` — the full help banner.
|
|
|
|
## Implementation Notes
|
|
|
|
The arg parser uses a closed `ALL_KNOWN_FLAGS` set. New flags must be
|
|
added to the set before the parser will accept them; an unknown flag
|
|
fails the run with `ELECTROBUN.UNKNOWN_FLAG`, which keeps subtle typos
|
|
from being silently swallowed. The `--help` and `-h` short forms are
|
|
intercepted before subcommand dispatch so they work uniformly across
|
|
every subcommand.
|
|
|
|
`runCli` catches every `ElectrobunError` and folds it into `code: 1`
|
|
with a one-line stderr message. Any other thrown value re-propagates,
|
|
on the principle that the runtime should not eat unexpected exceptions
|
|
— if something other than a typed error escapes the call, the bin
|
|
wrapper surfaces it with `code: 2`.
|
|
|
|
The `timeRunner` helper increments `electrobun_runner_invocations_total`
|
|
on entry, records the duration histogram on every exit (success or
|
|
failure), and records `electrobun_runner_failures_total` when the thrown
|
|
`ElectrobunError` carries a numeric `details.exitCode`. The failure
|
|
counter labels the exit code with `-1` when no numeric exit-code is
|
|
available — for instance when the probe stage raises
|
|
`BINARY_UNAVAILABLE` — so dashboards can still attribute the failure
|
|
to the right command without inventing fake exit codes.
|