CLI reference

September 18, 2026 · View on GitHub

The CLI is designed for both agents and humans: stable non-interactive commands, formatted JSON for resource operations, and ordinary readable logging for a long-running Worker.

Commands

labtasker --version
labtasker [--labtasker-root PATH] [--auto-start-local-server] COMMAND
labtasker config show

labtasker queue create NAME
labtasker queue list
labtasker queue delete NAME [--cascade]

labtasker task submit [OPTIONS]
labtasker task get TASK_ID
labtasker task list [OPTIONS]
labtasker task count [OPTIONS]
labtasker task update [TASK_ID | --filter FILTER] --changes JSON
labtasker task cancel TASK_ID
labtasker task requeue TASK_ID
labtasker task delete TASK_ID

labtasker worker list [OPTIONS]
labtasker worker count [OPTIONS]

labtasker loop [OPTIONS] -- COMMAND [ARG...]
labtasker-server --version
labtasker-server serve --connection http [OPTIONS]
labtasker-server serve --connection socket [OPTIONS]
labtasker-server status [--labtasker-root PATH]
labtasker-server stop [--labtasker-root PATH] [--force]
labtasker-server logs [--labtasker-root PATH]

Run --help on any command for its exact options and accepted values.

--labtasker-root and --auto-start-local-server are global Client options and must appear before the subcommand. The root defaults to exact CWD/.labtasker; it selects config, journals, and the managed-local endpoint without searching parents. Auto-start authority is false by default and applies only when no URL or external socket is configured.

Both executables expose their runtime distribution version without reading configuration, contacting a Server, or starting the local daemon. labtasker --version prints labtasker-client VERSION, because the Client distribution owns that executable. labtasker-server --version prints labtasker-server VERSION. Each result is one line on stdout with exit status 0. Root --help lists the option but does not print the current version.

Client options

The root options apply before the selected subcommand:

OptionDefaultContract
--versionfalsePrint labtasker-client VERSION and exit without resolving configuration.
--labtasker-root PATHexact CWD/.labtaskerSelect the exact config, journal, and managed-local root. No parent or VCS search occurs.
--auto-start-local-serverfalseGive only this invocation authority to start or recover the standard managed-local daemon. Invalid for an HTTP or explicit-socket endpoint.

Task command arguments and options are:

CommandArguments and options
task submit--args JSON {}, --name TEXT null, --metadata JSON {}, --priority INT 0, --max-attempts INT 3, repeatable --route TEXT (omission means default), optional --id TASK_ID, optional --queue QUEUE
task getrequired TASK_ID, optional --queue QUEUE
task listoptional --status, --name, --name-fuzzy, --filter, --order-by (created_at), --descending / --ascending (descending), --limit (100, range 1–1000), --cursor, and --queue
task countoptional --status, --name, --name-fuzzy, --filter, one comma-separated --group-by, --limit, --cursor, and --queue; limit/cursor require grouping
task updateexactly one of positional TASK_ID or --filter FILTER, required --changes JSON, optional --queue QUEUE
task cancel, task requeue, task deleterequired TASK_ID, optional --queue QUEUE

Queue, Worker, and execution options are:

CommandArguments and options
queue createrequired NAME
queue listno command options
queue deleterequired NAME; --cascade defaults to false
worker listoptional --filter, --limit (100, range 1–1000), --cursor, and --queue
worker countoptional --filter, one comma-separated --group-by, --limit, --cursor, and --queue; limit/cursor require grouping
loop--route (default), --queue, --max-consecutive-failures (5), --idle-timeout (300 seconds), nullable --force-stop-timeout, --metadata JSON ({}), then required direct child argv after --

Task progress, Worker telemetry, and early completion are Python execution-context helpers. The CLI does not expose progress, worker telemetry, or finish commands.

--status accepts exactly pending, running, succeeded, failed, or cancelled. --order-by accepts id, name, status, priority, attempt, max_attempts, last_route, created_at, updated_at, started_at, or finished_at. Page limits accept 1–1000. Positive counters reject zero; --force-stop-timeout accepts a finite non-negative number.

Command contracts

Finite resource commands are CLI forms of the same Python and HTTP operations. They do not add hidden prompts, implicit pagination, or alternate lifecycle rules.

Business responses that report an older Server package version produce a [labtasker] warning: on stderr recommending a Server upgrade. This adds no HTTP request and changes neither stdout nor exit status. A Client instance warns once per distinct older Server version, so a long-running Worker does not repeat the warning on every heartbeat. Separate CLI invocations may each warn. Servers that do not advertise a usable version do not trigger this warning.

CommandSuccessful stdoutContract
config showOne resolved configuration objectResolves current sources without network access, file creation, or local Server startup; never prints a token.
task submitOne Task object--args/--metadata default to {}, --priority to 0, --max-attempts to 3, and omitted routes to default. Repeat --route for several exact routes; use --id for a caller-chosen idempotent Task ID.
task getOne Task objectID-addressed; an unknown Task is an error, not null.
task list{"items":[...],"next_cursor":...}Returns one page. --status, exact --name, --name-fuzzy, and --filter combine with AND.
task count{"count":N} or a grouped pageCounts the complete selection; --group-by opts into grouped counts.
task update TASK_IDThe resulting TaskReplaces supplied fields on one non-running Task.
task update --filter ...{"matched":N,"updated":M}Requires an explicit filter and atomically updates all matching non-running Tasks.
task cancelThe resulting TaskAccepts pending/running; repeating on cancelled is idempotent.
task requeueThe resulting TaskAccepts pending/failed/cancelled; resets attempt and last error. Succeeded Tasks require a new submission.
task deleteNothingPermanently deletes one non-running Task; absent is idempotent.
worker list{"items":[...],"next_cursor":...}Lists unexpired observations by ID ascending; accepts --filter, --limit, --cursor, and --queue.
worker count{"count":N} or a grouped pageCounts unexpired observations; accepts --filter, --group-by, --limit, --cursor, and --queue.
queue createOne Queue objectIdempotent create-by-name.
queue listComplete Queue arrayNot paginated.
queue deleteNothingNon-empty requires --cascade; running Tasks still block deletion.

task update --changes accepts only name, args, metadata, priority, max_attempts, routes, and result. Supplied objects and lists are complete replacements, not merges. Status changes use cancel and requeue; status is not writable.

labtasker loop is a continuing Command Worker, not a finite resource command. It claims through one exact route and executes at most one child at a time. The required -- separates Labtasker options from one direct argv template; see Command Workers. --max-consecutive-failures INTEGER defaults to 5 and must be positive. --metadata JSON supplies one strict JSON object describing that Worker invocation. Labtasker does not automatically populate hostname, scheduler, GPU, or other resource fields. The Worker exits 1 after reporting that many consecutive execution failures; see failure protection.

--idle-timeout measures confirmed empty-Queue time. Claim communication failures pause it and use an independent fixed five-minute recovery window. Empty and temporarily unavailable claim paths share internal jittered backoff, with no CLI tuning option. A recovered claim is lease-confirmed before the child process starts.

Server commands have a separate ownership boundary:

The Server requires POSIX advisory file locking in every transport and lifecycle mode. On Windows, help and version inspection remain available, while every operational Server command exits 1 before creating state, opening SQLite, binding a listener, or starting a process.

CommandContract
serve --connection http|socketRuns one foreground Server, or a detached one with --daemon. Transport selection is required. --database-filesystem defaults to auto; one process owns one SQLite file.
status [--labtasker-root PATH]Read-only JSON describing the daemon selected by exact root; it creates and cleans nothing.
stop [--labtasker-root PATH] [--force]Stops only the reverified daemon for that root; normal stop never sends SIGKILL.
logs [--labtasker-root PATH]Prints that daemon's complete log; it does not follow.

serve has this exact public option surface:

OptionRequirement or default
--connection http|socketRequired; there is no transport default.
--labtasker-root PATHExact CWD/.labtasker.
--database PATH<labtasker-root>/server.db; it may be elsewhere.
--database-filesystem auto|local|sharedauto.
--host HOSTHTTP-only; 127.0.0.1.
--port PORTHTTP-only; 8000, range 1–65535.
--socket PATHSocket-only; otherwise derive the owner-only runtime socket from the canonical root.
--daemonfalse; lifecycle only.

status, stop, and logs each accept only --labtasker-root, defaulting to exact CWD/.labtasker; stop additionally accepts --force, default false. Private _ensure-daemon and _daemon coordinator commands are hidden implementation details, not public CLI contracts.

serve defaults the root to exact CWD/.labtasker, its database to <root>/server.db, and detached mode to false. HTTP defaults to 127.0.0.1:8000; socket mode derives an owner-only socket from the root. --host/--port and --socket are mutually transport-specific. --daemon changes lifecycle only. A matching detached launch is idempotent; a conflicting launch fails and asks the operator to stop the existing daemon first. There is no public start command.

status writes this stable shape; fields that require verified live metadata are null when it is unavailable:

{
  "state": "running",
  "labtasker_root": "/absolute/root",
  "database": "/absolute/server.db",
  "database_filesystem": "shared",
  "connection": "socket",
  "host": null,
  "port": null,
  "socket": "/absolute/server.sock",
  "log": "/absolute/root/server.log",
  "pid": 1234,
  "version": "2.5.0"
}

state is exactly running, starting, unhealthy, or stopped. database, database_filesystem, connection, host, port, socket, pid, and version are null when not applicable or when live metadata cannot be verified. Runtime metadata is diagnostic state, not configuration or authority.

Inspect route demand and Worker activity

labtasker task count --status pending --group-by routes,status
labtasker worker count --group-by route,status
labtasker worker list --filter 'route == "sdxl" and status == "busy"'
labtasker worker list --filter 'metadata.hostname == "node-7"'
labtasker worker count --filter 'telemetry.gpu_utilization < 0.1'

--group-by is one comma-separated argument with no spaces. Task fields are routes and status; Worker fields are route and status. Either order is valid. Empty fields, duplicates, unsupported fields and repeated --group-by options are usage errors. Without grouping, count output remains {"count":N}.

Grouped output contains group_by, the complete matching count, one page of items with key/count, and next_cursor. --limit (default 100, maximum 1000) and --cursor require grouping on count commands. Fetch later pages explicitly with the same filters and grouping. Multi-route Tasks contribute to every compatible route group; group counts may overlap. Pages reflect current data rather than a fixed snapshot.

Worker results are supplementary observations: idle means waiting, and busy includes reporting and post-finish cleanup. Delays and temporary missing Workers are possible. No observed Worker for a route does not prove no process exists. See HTTP observation semantics.

JSON input

--args, --metadata, and --changes accept one strict JSON object. The CLI does not offer repeated --arg key=value parsing because that would introduce a second type system and ambiguous coercion.

labtasker task submit \
  --args '{"seed":1,"enabled":true,"tags":["a","b"]}'

Shell quoting protects the JSON from the shell; it is not part of the JSON.

Output and exit behavior

Successful finite resource commands print one two-space-indented JSON document with no ANSI styling. Delete commands complete quietly. Handled configuration, transport, and API errors print the stable Labtasker error envelope to stdout and exit 1 without an application traceback. stdout is therefore the single machine-readable response channel for finite commands: callers distinguish a successful value from an error envelope with the exit status and the top-level error key. CLI argument or usage errors remain natural-language stderr and exit 2; an interrupted Worker retains exit 130.

Every finite Client operation identifies its selected managed-local, external socket, or HTTP Server on stderr after connecting. The single [labtasker] connected line explicitly names the endpoint and its Unix, HTTP, or HTTPS transport; managed-local connections also identify the root, database and socket. Authorized startup transitions are likewise visible. Requested data or a handled error envelope remains alone on stdout. Finite Client diagnostics use [labtasker], while Server CLI diagnostics use [labtasker-server]. Detached serve and stop report actions on stderr, status prints stable JSON on stdout, and logs writes log content to stdout.

labtasker loop is different: it is a supervised long-running process, so it uses ordinary logs whose default format includes a millisecond UTC timestamp, level and [labtasker] prefix, and tees child output in real time. It does not emit JSON Lines or hide the child behind a pager. This Command Worker requires POSIX process-group support; on Windows it writes the unsupported-platform message to stderr and exits with status 1 before connecting to the Server or claiming a Task.

Pagination

task list intentionally returns one page. Agents can read next_cursor and make the next explicit call:

labtasker task list --limit 100 --cursor OPAQUE_CURSOR

There is no automatic pager or interactive confirmation. Destructive scope is made explicit with identifiers, filters, or --cascade instead.

Search Task names

labtasker task list --name-fuzzy "tr ev"
labtasker task count --name-fuzzy "tr ev"
labtasker task list --name "train_model_eval"

--name-fuzzy ignores case and surrounding whitespace. Every whitespace-separated word must appear as a subsequence in the Task name; words can appear in any order. tr ev and ev tr both match train_model_eval. Empty searches add no restriction. Punctuation is literal; this is not the full fzf query language. The Server searches the Queue before pagination and preserves the chosen ordering. All supplied selectors combine with AND. Keep the same search input when reusing a cursor. --name and --filter 'name == "..."' retain strict equality.