Cadence
Reference

Command-line client

Sign in, hold login profiles, and drive a space's leases and task attachments from a terminal with cadence.

cadence is the command-line client for Cadence. It signs in and holds your login, lists and inspects the leases in your space, reads and writes task attachments, and carries the verbs a worker runs from inside a lease. Each command wraps one call to the same HTTP API the web interface and the HTTP API use, so anything you can do here you could also do with a raw HTTP request — the client is the shorter path, not a separate set of capabilities.

The examples below use https://cadence.example.com for the instance address and OPS for a space key — replace both with your own. Example task keys such as OPS-42 and lease ids such as 33521 are invented; use the real ones from your space.

This page assumes the cadence client is already installed and on your PATH. Every command below starts cadence; if your shell reports cadence: command not found, the client is not installed on this machine. If your instance offers the client for download, you can get it yourself from Download CLI in the web interface's user menu — see Downloading the command-line client. If that menu item is not there, your instance does not offer downloads, so get the client from whoever operates your instance.

The two authentication faces

cadence has two faces, and they authenticate differently. Which face a command belongs to is fixed — the client never guesses.

  • The operator face is the commands you run as yourself: login, whoami, profile, lease, and task-attachment. They authenticate with your personal smc_ login token, held in a profile (see Signing in).
  • The worker face is the worker commands. They run from inside a running lease and authenticate as that lease, reading the lease's credentials from the environment Cadence sets for the lease — you do not type a token or run these by hand (see Worker commands).

Keeping the faces apart is why a worker command can never act as you, and an operator command can never act as a lease.

Discovering commands

Run cadence with no arguments, or with -h / --help, to see the commands and their options. Every command and subcommand takes -h / --help for its own usage.

-v / --version prints the version and exits:

$ cadence --version
0.0.0-dev

A build you installed from a tagged release prints that release's version instead of 0.0.0-dev. The version command prints the same string, and adds --json:

$ cadence version --json
{"version":"0.0.0-dev"}

When you mistype a command, the client prints an error, suggests the closest command it knows, and points you at --help rather than running anything.

Signing in

Before you can run the operator commands you need a personal smc_ API token. Mint one in the web interface as described in API tokens — the full value is shown only once, so copy it then.

cadence login verifies a token against the server before it saves it, so a bad token fails loudly instead of being stored:

$ cadence login --url https://cadence.example.com
Paste your cadence login token (smc_…):
Logged in as you@example.com (id 4021) at https://cadence.example.com (profile default)

The prompt reads the token without echoing it. Instead of being prompted you can pass the token with --token <smc_…>, or pipe it on standard input — useful in a script:

$ echo "$MY_TOKEN" | cadence login --url https://cadence.example.com

If no token reaches the command by any of those routes, it refuses:

error: No token provided. Pass --token <smc_…>, pipe it on stdin, or run interactively.

--url sets the base URL recorded for the profile. Leave it off and the client keeps the profile's saved URL, or falls back to the built-in default when there is none. --json emits { user, baseUrl, profile } instead of the line above.

Where your login is stored

cadence login saves the token as a named profile in ~/.cadence/config.json. The file is created readable only by you (mode 0600), because every token carries your full authority. A config written by an older version of the client is migrated to the current format the first time a command reads it.

Never commit this file or share it — anyone who reads it holds your login.

Profiles

A profile is one saved login. Hold as many as you like — one per account or instance — and pick one per command. One profile is the active default, marked with *.

cadence profile ls lists your profiles. Tokens are never printed:

$ cadence profile ls
   NAME     TYPE      BASE URL
*  default  operator  https://cadence.example.com
   work     operator  https://cadence.example.com

With no profiles saved it prints nothing and says so:

No profiles saved. Run `cadence login` to create one.

cadence profile use <name> sets the active default. Naming a profile you have not saved is refused:

error: No such profile "work". Run `cadence profile ls` to see saved profiles.

cadence profile rm <name> deletes a saved profile. It refuses to remove the one that is currently the active default unless you pass --force, which also clears the active-default pointer:

error: Cannot remove "default": that's the active default — run `cadence profile use <other>` first, or pass `--force`.

Each of ls, use, and rm takes --json.

Which login a command uses

An operator command resolves its credential in this order, taking the first that applies:

  1. --profile <name>, when the command is given one.
  2. The CADENCE_PROFILE environment variable, naming a saved profile.
  3. The active default profile.
  4. CADENCE_TOKEN (with CADENCE_BASE_URL for the address, if set) — an ephemeral login for scripting or continuous integration, needing no saved profile.

If steps 1 or 2 name a profile that does not exist, the command stops with a hard error rather than falling through — it never silently uses a different login. When none of the four applies, the command refuses:

error: Not logged in. Run `cadence login` first.

Confirm who you are

cadence whoami shows the identity the active login resolves to:

$ cadence whoami
you@example.com (id 4021)

--profile <name> runs it against a specific saved profile; --json emits the server's { user } body. This is the same identity as GET /api/v1/whoami.

Global controls

These apply to every command.

Request timeout

Every request is bounded by a per-request timeout, so a call to a wedged server fails instead of hanging. The default is 30 seconds. Raise or lower it with --timeout <seconds> on any command, or by setting the CADENCE_TIMEOUT environment variable; the flag wins over the variable, which wins over the default. The value must be a positive number of seconds.

When a request runs past the bound, the client aborts it and tells you which bound it hit and how to change it:

error: Request timed out after 30s: GET /whoami did not respond in time. If the server is healthy but slow, raise the bound with --timeout <seconds> or the CADENCE_TIMEOUT environment variable.

Colour

Help and error text are coloured when the output is a terminal. Set NO_COLOR to any non-empty value to turn colour off whatever else is set; set FORCE_COLOR to keep colour on even when the output is not a terminal — for example when piping to a pager.

Exit codes and errors

Every command that fails prints a single line to standard error, beginning error:, and exits non-zero. The client never prints a raw stack trace or a raw HTTP response body — a failure is always turned into a message that says what went wrong.

If you interrupt a command with Ctrl-C it exits 130; a termination signal exits 143.

When the client is too old

Cadence and the client agree on a protocol version. If your cadence is older than the version the server still accepts, the worker commands stop with an upgrade message rather than failing obscurely:

error: This cadence tool is too old to talk to the Cadence server. It speaks protocol version 1, but the server supports 2-3. Upgrade to the latest cadence and try again.

The supported range is whatever the server reports; install a newer cadence and run the command again. If your instance offers the client for download, get the newer version yourself from Download CLI in the web interface's user menu — see Downloading the command-line client § Updating the client. If that menu item is not there, get it from whoever operates your instance.

Watching leases

When a pool staffs a stage, it leases a task to a worker, and that lease is the unit of running work. These operator commands let you watch and reach the leases you can see. For what a lease is, its states, and what its logs and exec target mean, see Leases; the commands here show how the client reaches them.

Each command takes --profile <name> to run against a specific saved login.

List leases

cadence lease ls lists a space's leases, newest first. --space <key> is required:

$ cadence lease ls --space OPS --state running
ID     STATE    POOL      TASK    STARTED
33521  running  delivery  OPS-42  2026-05-20T11:03:00Z

Filter by lease state with --state, cap the page with --limit <n>, and page with --cursor <cursor>. When more results remain, the client prints the cursor to re-run with on standard error, so the table on standard output stays clean for piping:

More results — re-run with --cursor eyJ...

With no leases it prints No leases found. on standard error. --json emits the server's raw { leases, next_cursor } body.

Read one lease

cadence lease get <id> shows one lease's detail:

$ cadence lease get 33521
id:             33521
state:          running
attempt:        1
paused by:      —
pool:           7 — delivery
task:           OPS-42
workflow:       delivery
worker version: 42
image:          agent:1.4.0
created:        2026-05-20T11:00:00Z
dispatched:     2026-05-20T11:02:00Z
started:        2026-05-20T11:03:00Z
last heartbeat: 2026-05-20T11:15:00Z
finished:       —

The workflow: row shows the slug of the workflow the lease's work sits on. A field with no value shows an em dash, and so does workflow: for a lease with no workflow, such as a space-subject lease. The client no longer prints a board: row, so a script that reads board: from this output stops matching. For scripts, use --json: it emits the server's raw { lease } body, which the client passes through without reshaping.

Read a worker's logs

cadence lease logs <id> prints a lease's worker log lines, whether the lease is still running or has finished:

$ cadence lease logs 33521
Starting task OPS-42
Fetched payload
Done

Narrow the output with --tail <n> for the last N lines, --since <iso8601> for a lower time bound, and --container <name> to pick a container; page with --cursor <cursor>. When more logs remain it prints the cursor to re-run with on standard error; with no events it prints No log events. there.

-f / --follow keeps polling and printing new events until you interrupt it with Ctrl-C. --json emits the page body; under --follow it emits one JSON event per line as each arrives.

Open a shell in a lease

cadence lease exec <id> opens a shell inside a running lease's container. It asks the server where the lease runs and then shells in locally, so the matching tool must be installed on your PATH — aws for a lease running under Amazon ECS, or docker for one running under Docker. With no command it opens an interactive sh; put -- before a one-shot command so the client passes its flags through untouched:

$ cadence lease exec 33521 -- ls -la /tmp

The command exits with the child command's own exit code.

A lease you can reach still refuses a shell in these cases, each with its own message:

  • The lease was not started in a mode that allows a shell:

    error: Cannot exec into lease 33521: it was not started in debug mode, so no exec session is enabled. Re-dispatch the lease in debug mode and try again.

    A lease runs in debug mode only when its pool has debug mode turned on. Set the pool's debug setting (see Fleet), then let the pool lease the task again, so the new lease is one you can shell into.

  • The lease's runtime does not support shelling in at all — the message says so.

  • The needed tool is not installed:

    error: Cannot exec into lease 33521: the `docker` command was not found on your PATH. Install docker and try again.

Lease attachments

A lease keeps the files a worker attached to it — a run log, a transcript — even after it finishes. cadence lease-attachment lists and downloads them. For what a lease attachment is, see Leases. Each command takes --profile <name>.

cadence lease-attachment ls <lease-id> lists a lease's retained attachments, newest first:

$ cadence lease-attachment ls 33521
ID     KIND      CONTENT-TYPE  SIZE   CREATED
9001   run_logs  text/plain    14822  2026-05-20T11:16:00Z

With no attachments it prints No attachments. on standard error. --json emits the server's { attachments } body.

cadence lease-attachment get <lease-id> [attachment-id] downloads one attachment's bytes. Give exactly one selector — an attachment id, or --kind <kind> to fetch the newest attachment of that kind. Giving neither, or both, is refused before any request:

error: Give an attachment id or --kind to say which attachment to fetch.

By default the bytes go to standard output, byte-identical to what the server stored, so you can pipe or redirect them; --out <path> writes them to a file instead. Fetching by --kind when no attachment of that kind exists is an error naming the kind. When several attachments match the kind, the client fetches the newest and notes the choice on standard error:

2 attachments matched kind "run_logs"; fetched the newest (id 9001).

Task attachments

cadence task-attachment reads and writes the file attachments on any task you can reach. For what a task attachment is, its size limit, and the media types allowed, see Task data. Each command takes --profile <name>.

cadence task-attachment ls <task-key> lists a task's attachments, in the order they were added:

$ cadence task-attachment ls OPS-42
ID    FILENAME    CONTENT-TYPE  SIZE   CREATED
512   spec.pdf    application/pdf  20481  2026-05-20T09:00:00Z

With no attachments it prints No attachments. on standard error. --json emits the server's response body unchanged.

cadence task-attachment get <task-key> <attachment-id> downloads one attachment by id. A task's attachments are identified by filename and several may share one, so list first to find the id. The bytes go to standard output, or to --out <path> when given.

cadence task-attachment attach <task-key> <file> uploads a local file as an attachment. --content-type <type> is required and is never inferred from the file name — you state the media type the attachment is stored as:

$ cadence task-attachment attach OPS-42 ./spec.pdf --content-type application/pdf
Attached spec.pdf (20481 bytes) to task OPS-42.

--json emits the server's attachment record. An empty file, or a file that does not exist, is refused before any request:

error: Cannot attach: file "./spec.pdf" is empty; nothing would be uploaded.

Worker commands

The worker commands are the worker face. An agent runs them from inside a running lease, and they authenticate as that lease using the credentials Cadence puts in the lease's environment — the lease id, its token, and the instance address. You do not set these or run these commands by hand; they are what a worker image calls instead of raw HTTP requests. Run outside a lease, where that environment is not set, they refuse:

error: MC_BASE_URL is not set; worker commands must run inside a lease.

Each command acts on the lease it is running inside — there is no lease-id argument.

claim

cadence worker claim claims the lease and prints its payload. A lease that has already reached a terminal state cannot be claimed again:

error: Cannot claim lease: lease 33521 is already terminal (HTTP 410) and cannot be claimed again.

payload

cadence worker payload prints the worker configuration for the lease — its system prompt, model, skills, and the rest — as JSON. -o / --output <file> writes it to a file and leaves standard output empty, for a script that reads the file.

heartbeat

cadence worker heartbeat reports the worker is alive and prints the control word the server returns, one of none, cancel, expire, pause:pool, pause:operator, or resume:

$ cadence worker heartbeat
none

A worker loop reads that word to know whether to keep going, stop, pause, or wind down. expire means the lease is within 120 seconds of its pool's max lifetime, after which Cadence kills it. On expire, a worker should upload what it needs to keep, with cadence worker attach or cadence worker attach-task, before the cap.

When more than one word applies, cancel wins over expire, and expire wins over pause:pool, pause:operator and resume. So a paused lease still hears expire. A running lease in a paused pool hears expire too, and while it does, that heartbeat does not pause the lease for the pool.

--json emits { control, reported_outcome, reported_summary }, passing the two reported fields through exactly as the server sent them — null when nothing was reported.

complete

cadence worker complete ends the lease with a terminal outcome. --outcome is required:

$ cadence worker complete --outcome success --summary "Task done"
completed

--summary <text> adds a short human-readable summary, --details <json> a structured JSON object. --json emits { state }. This ends the lease; to record an intended outcome without ending it, use report-outcome.

report-outcome

cadence worker report-outcome records the lease's intended outcome without ending the lease — the non-terminal counterpart to complete. --outcome is required; --summary <text> is optional:

$ cadence worker report-outcome --outcome success --summary "Looking good"
success — Looking good

--json emits { reported_outcome, reported_summary }.

activities

cadence worker activities records activities on the lease's subject — the task for a task lease, or the workflow or space for a workflow- or space-subject lease. Pass them as a JSON array with --activities <json>, or pipe the array on standard input. The value must be a JSON array:

error: activities must be a JSON array of activity objects.

report-usage

cadence worker report-usage reports one reading of a usage counter. All four of --counter <name>, --amount <n>, --unit <unit>, and --idempotency-key <key> are required, and --amount must be a non-negative integer. The idempotency key makes the report safe to repeat: a repeat with the same key records nothing new, noted on standard error:

$ cadence worker report-usage --counter tokens --amount 1500 --unit token --idempotency-key run-1
usage recorded

Repeating it prints usage already recorded (no change).

Attachments from a worker

A worker writes and reads attachments the same way an operator does, but scoped to its own lease and task:

  • cadence worker attach <file> uploads a file as an attachment on the lease. --kind <kind> is required; --content-type <type> defaults to application/octet-stream and is never inferred from the file name. --json emits the server's record:

    $ cadence worker attach ./run.log --kind run_logs --content-type text/plain
    Attached run_logs (14822 bytes).
  • cadence worker attach-task <task-key> <file> attaches a file to the lease's own task. --content-type <type> is required. Attaching to any other task is refused as not allowed rather than as a bad token.

  • cadence worker list-task-attachments <task-key> and cadence worker get-task-attachment <task-key> <attachment-id> read the lease's own task's attachments — the list, and one attachment's bytes to standard output or to --out <path>.

On this page