Skip to content
Runner · Reference

CLI commands

dry-run, inspect, trigger, start, config.

Derived from runner/src/engine/bin

The runner ships six CLI verbs. Together they form a ramp from safe to live: introspect with no chain access, simulate with no gas, fire one real tick, then run the daemon.

Rule: dry-run never signs and never spends. trigger is exactly one real tick. start is the long-lived daemon.

Plugins are registered by editing src/plugins/index.ts — every bin imports the registry from there. The bins themselves are marked ENGINE. DO NOT EDIT.

The commands

CommandInvocationChain accessWhat it does
pluginspnpm pluginsnoneLists every plugin in the registry with its vaultId and triggers.
show-configpnpm show-confignonePrints resolved config (no secrets).
inspectpnpm inspect <plugin-name>read-onlyLive snapshot of what the plugin would see right now.
dry-runpnpm dry-run <plugin-name>simulateRuns decide() once and simulates the tx. No signature, no gas.
triggerpnpm trigger <plugin-name>one txFires the plugin once — a single real decide()/execute(), then exits.
startpnpm startliveStarts the daemon: registers every plugin, runs the scheduler.

plugins

Pure introspection: no RPC, no signer, no state read. It does not need RUNNER_PLUGINS — it lists everything in the registry regardless of what the daemon would load. For each plugin it calls the factory and prints vaultId (truncated) plus triggers, formatted as cron(<everySeconds>s), event(<topic>), or the raw kind. A factory that throws shows as factory-error; the footer counts the failures and points you at the matching PLUGIN_<NAME>_VAULT_ID env vars.

show-config

Prints the resolved runtime config and exits. Echoes Network, Sui RPC URL, the Predict package/object refs, the Plugins to load list, and the State backend (custom if you export stateFactory, else in-memory). OPERATOR_PRIVATE_KEY is shown only as ✅ set or ❌ MISSING — never the value, even partial. HEALTH_PORT and LOG_LEVEL are echoed with their defaults. On a config error it prints the message and exits 1. See Configuration reference.

inspect

Read-only snapshot: no tx, no signing, no state mutation. Loads the plugin, reads the wallet balance (no signature), fetches the vault via Vault.fetch, and prints the highlights — nav, navPerShare, quoteBalance, aggregateExposure, maxSinglePosition, exposureHeadroom, paused, strategyPaused, isFrozen, open-position counts, and the granted strategy permissions. With a custom stateFactory it also previews the first 10 plugin state keys. This is the command for *"why isn't my plugin acting?"* — check isFrozen, headroom, and granted perms.

dry-run

Runs the plugin's decide() once, simulates the resulting tx via devInspect, and prints the decision it took (actions, noop reasons, gas estimate) regardless of LOG_LEVEL. Read-only: no signature, no gas, no on-chain change. The simulated result has no digest, but effects, events, and a gas estimate are populated. The signer is still loaded — for its address, not to sign. The plugin must be a member of RUNNER_PLUGINS, or the command throws dry-run: plugin "<name>" not in RUNNER_PLUGINS list. Only that one plugin is registered. Plugins that persist via onExecuted should check result.dryRun and skip writes on simulations. A missing <plugin-name> arg exits 2.

trigger

Fires the plugin once: a single real decide()/execute() with a real signature and real gas, then exits. Use it to debug without the daemon, as a CI smoke test, or to force a manual trigger in an emergency. Like dry-run, the target must be in RUNNER_PLUGINS (else exec: plugin "<name>" not in RUNNER_PLUGINS list), and only the requested plugin is registered — others in the CSV are ignored in this mode. Missing arg exits 2.

start

The daemon entrypoint. Loads config, builds the client and signer, registers every plugin in RUNNER_PLUGINS, and starts the scheduler. As a pre-flight it logs the operator address and SUI balance so you can spot an empty wallet before the first tick — there is no threshold assertion; monitor externally. It starts the healthcheck HTTP server when HEALTH_PORT > 0 (default 3030, bound to HEALTH_HOST, default 127.0.0.1; set HEALTH_PORT=0 to disable) and shuts down gracefully on SIGINT/SIGTERM, waiting (up to 30s) for an in-flight tick to finish so lifecycle hooks aren't cut off. A second signal skips the wait. A fatal startup error logs JSON to stderr and exits 1.

Next: the full env-var list → Configuration reference.