CLI workflow and reference

Copy concrete localhost commands and look up selection, lifecycle, operation, output, and exit behavior.

The commands below use the checked getting-started config, its slack service, seeded dev world, Ada user, and general channel.

Terminal one:

pnpm exec localhost dev

Terminal two:

pnpm exec localhost seed
pnpm exec localhost describe slack --instance dev --json
pnpm exec localhost exec slack --help
pnpm exec localhost exec slack create-user --instance dev --name Grace --json
pnpm exec localhost exec slack send-message --instance dev \
  --input-json '{"channel":"general","from":"Ada","text":"ready"}' --json
pnpm exec localhost logs slack --instance dev --tail 50 --json

Use pnpm exec localhost so the project's installed package supplies the binary. dev stays in the foreground, binds only loopback, acquires the storage lock, and creates an empty persistent dev world if none exists. The second terminal uses authenticated control commands; application traffic continues through plugin connection values and provider-shaped routes.

Select the project and world

pnpm exec localhost --config ./localhost.config.ts doctor --json
pnpm exec localhost instance list --config ./localhost.config.ts --json

LOCALHOST_INSTANCE=dev pnpm exec localhost describe slack --json
pnpm exec localhost describe slack --instance dev --json
SelectorRule
No --configWalk upward from the working directory; use the first supported config.
--config ./localhost.config.tsResolve that path from the working directory; skip discovery. It may appear before or after the command.
No instance selectorTarget dev.
LOCALHOST_INSTANCE=reviewChange the process default.
--instance devOverride both defaults for that command.

Help-only invocations do not load config. Commands that need a project stop when config loading fails; doctor records that issue and continues read-only inspection. Control commands compare the running fingerprint with the resolved config and stop before mutation on mismatch. Restart dev after editing config.

Create, reset, and destroy always require an explicit ID argument. LOCALHOST_INSTANCE does not make destructive targets implicit.

Discover and invoke operations

localhost describe [service] [--instance id] [--json]
localhost exec <service> --help
localhost exec <service> <operation> [generated flags | --input-json object] [--instance id] [--json]

Operation commands come from the selected running plugin's metadata. describe lists services or one service's operation schemas. exec slack --help converts keys such as createUser to create-user and displays the input accepted by the installed plugin version.

pnpm exec localhost exec slack create-user --name Lin --admin=false --json
pnpm exec localhost exec slack send-message \
  --channel general --from Ada --text ready --json
pnpm exec localhost exec slack send-message \
  --input-json '{"channel":"general","from":"Ada","text":"ready"}' --json
Input shapeCLI form
Flat strings, finite numbers, safe integers, booleans, or repeatable non-boolean scalar arraysGenerated long flags
Nested, union, referenced, dynamic, repeated boolean, or runtime-option collisionOne complete object through --input-json

Boolean flags accept an omitted value as true or an explicit --admin=false. Repeated arrays repeat the same flag. --input-json works as a complete object and cannot be combined with generated input flags. Invalid JSON, a non-object value, an unknown flag, or schema failure exits as input failure; the CLI does not guess another operation.

Operations are privileged local controls. They arrange or inspect a world; they do not replace the application's provider-shaped request boundary. See Operations and emulated APIs.

Manage instances and seed

pnpm exec localhost instance create review
pnpm exec localhost instance list --json
pnpm exec localhost seed --instance review
pnpm exec localhost instance reset review
pnpm exec localhost instance reset review --seed
pnpm exec localhost instance destroy review
GrammarTransition
instance create <id> [--seed]Create a persistent empty world; seed before readiness only when requested.
instance list [--json]Read lifecycle, persistence, seed, service, and clock summaries.
seed [--instance id]Seed an existing eligible world once.
instance reset <id> [--seed]Replace the named world; empty unless seed is requested.
instance destroy <id>Remove the named world.

Mutations print short acknowledgements and have no --json variant. They do not prompt. Resolve the ID before automation. Reset is replacement, not automatic reseeding; destroy is not an archive.

Render or inject application environment

pnpm exec localhost env --instance dev --format dotenv
pnpm exec localhost env --instance dev --format json
pnpm exec localhost run --instance dev -- pnpm test

env prints the plugin-owned application projection. Dotenv is the default; JSON uses --format json, not --json. run overlays that projection on one directly spawned child, inherits stdio, forwards SIGHUP/SIGINT/SIGTERM while it owns the child, and returns the child's exit code. Everything after --, including child flags named --config or --instance, belongs to the child.

Connection names override inherited values. Runtime discovery metadata and the control token are never injected.

Inspect clocks, logs, and health

pnpm exec localhost clock status --instance dev --json
pnpm exec localhost clock advance 2h --instance dev --json
pnpm exec localhost logs slack --instance dev --tail 50 --json
pnpm exec localhost doctor --json
CommandResult
clock statusCurrent mode and RFC 3339 instance time.
clock advance <duration>One durable time window after configured services reconcile it.
logs [service]Bounded request, operation, delivery, and plugin logs.
doctorRead-only config, runtime-discovery, stored-manifest, service-version, and pending-trash report.

logs defaults to 50 entries; --tail accepts 0 through 1000. Its droppedEntries field tells you whether older or oversized evidence is absent. Correlation IDs identify one boundary, not a whole scenario.

doctor does not create directories, start a daemon, repair manifests, or remove trash. A report with status: "issues" still exits successfully; automation must inspect status and issues. An absent runtime is expected before dev starts.

Command inventory

The first two entries are owner-approved documentation-first contracts awaiting implementation review; the remaining entries are source-verified in the current CLI.

GrammarEffect
localhost initCreate a minimal config and ignore local runtime state.
localhost demo clone <name> [directory]Copy and install a named standalone demo without overwriting files.
localhost dev [--host host] [--port port]Start the foreground loopback runtime.
localhost describe [service] [--instance id] [--json]Read service and operation metadata.
localhost exec <service> [operation] ...Discover or invoke one plugin operation.
localhost instance create <id> [--seed]Create a persistent isolated world.
localhost instance list [--json]List known worlds.
localhost instance reset <id> [--seed]Replace a world, empty by default.
localhost instance destroy <id>Destroy the named world.
localhost seed [--instance id]Apply configured seed once.
localhost env [--instance id] [--format dotenv|json]Render application connection values.
localhost run [--instance id] -- <command...>Run one child with connection values.
localhost logs [service] [--instance id] [--tail n] [--json]Read bounded logs.
localhost clock status [--instance id] [--json]Read instance time.
localhost clock advance <duration> [--instance id] [--json]Advance and reconcile instance time.
localhost doctor [--json]Inspect project and stored runtime state without repair.

Stable output and exit status

With --json, success writes exactly one valid JSON value plus a newline to stdout. Diagnostics and dev readiness use stderr. A failed JSON command leaves stdout empty. Without --json, strings are written directly and other values as indented JSON. Operation output is the validated control response, not parsed plugin process output.

ExitMeaning
2CLI usage or validated input failure
3Config, discovery, transport, protocol, or control-authentication failure
4Missing instance, service, or operation
5Lifecycle conflict, committed mutation failure, or idle timeout
10Other plugin, runtime, lifecycle, or unexpected failure
130CLI interruption

Direct control failures render error: <message> on stderr and append a boundary correlation ID when supplied. A missing instance instead gets target-specific existing IDs and an exact localhost instance create <id> hint; that CLI-owned guidance omits the original correlation ID. The CLI does not expose structured error details. Use ControlApiError from the programmatic client when recovery needs fields such as a committed clock window.

After a run child starts, its exit code is passed through, including signal-derived 129, 130, or 143.

Plain HTTP control

The control API lives below /_/v1. Only /_/v1/health is unauthenticated; every other endpoint requires the current daemon token. This Bash example keeps it in one shell variable and clears it:

localhost_control_token="$(< .localhost2137/control-token)"
curl --silent http://127.0.0.1:2137/_/v1/instances \
  --header "authorization: Bearer $localhost_control_token"
unset localhost_control_token

Success uses { "data": ... }. Failure uses an error object with code, message, correlationId, and sometimes details. JSON mutations also require a JSON content type and exact shape. Prefer connectRuntime from localhost2137/client outside shell automation.

Read the token again after daemon restart. Never expose it to browser code, application processes, logs, traces, or build artifacts. Local security model defines the trust boundary.