-
Notifications
You must be signed in to change notification settings - Fork 0
apps cli
Active contributors: elwina
The capto CLI (crate capto-cli) is the control-plane client for the Capto desktop app. It is a thin, agent-friendly wrapper that sends HTTP requests to the running desktop over 127.0.0.1 and prints JSON. It never records by itself: it drives the single desktop RecordingSession, so one machine always has at most one live capture. The canonical reference is docs/CLI.md.
Agents and scripts need a stable, parseable way to control a screen recorder. The CLI provides one: it reads the desktop's connection details from the lockfile, authenticates with a Bearer token, and returns a JSON envelope on stdout with predictable exit codes. It also auto-launches the desktop when the control plane is not running, so a bare capto status mostly just works.
Commands are defined in crates/capto-cli/src/main.rs (a clap Command/Subcommand). Two global flags apply to most commands:
-
--human, print readable data instead of the JSON envelope -
--no-launch, fail if the desktop control plane is down (don't auto-start it)
| Command | Notes |
|---|---|
open |
Start the desktop only; does not wait for the control plane |
status |
Session snapshot (idle/starting/recording/paused/stopping) |
doctor |
Environment / FFmpeg / control-plane readiness |
record start|stop|pause|resume |
Recording controls |
shot |
Screenshot; returns data.path
|
config get|set|path |
Read / patch settings |
list displays|windows|audio|encoders |
Enumerate devices |
outputs recent|open |
Recent files / open in Explorer |
record start and shot share source-selection flags: --source display|window|region, plus --display, --window, and --x/--y/--width/--height for a region. record start also takes --format mp4|gif|audio, --fps, --quality, --encoder, --mic, --loopback, and --no-cursor. config set accepts either key=value pairs (camelCase keys, e.g. fps=60) or --json '{...}'.
On success the CLI prints { "ok": true, "data": ... }; on failure { "ok": false, "error": { "code", "message" } } (see emit_ok and the main error path in crates/capto-cli/src/main.rs). With --human it prints only the data or a plain message. data fields and settings keys are camelCase.
Exit codes are the stable part of the contract; branch on the code first, then error.code:
| Code | Name | When |
|---|---|---|
| 0 | ok | Success |
| 1 | usage | Bad args / unknown settings key |
| 2 | desktopUnavailable | No control plane / auth / launch failed |
| 3 | stateConflict | e.g. start while already recording |
| 4 | capture | Capture / device failure |
| 5 | encode | FFmpeg / encoder failure |
| 6 | configIo | Settings / outputs filesystem error |
The CLI finds the desktop before connecting (crates/capto-cli/src/client.rs::connect). It first tries an existing control plane; if the plane is down it spawns the desktop (unless --no-launch) and polls for readiness.
The lookup order for the desktop executable lives in crates/capto-cli/src/launch.rs::find_capto_exe:
-
CAPTO_APP_PATHenv var (must point atcapto-app.exe, not the CLI; a bare\\?\verbatim prefix is stripped) -
capto-app.exeorCapto.exenext to the CLI binary and one level up (covers the<install>\cli\layout) -
target/debug|release/capto-app.exerelative to the crate manifest, and the Tauri target directories -
%LOCALAPPDATA%\Capto\andProgram Files\Capto\
It deliberately skips any candidate that resolves to the CLI's own binary, so it never re-opens capto.exe. Launch uses ShellExecuteW on Windows so the desktop does not inherit the CLI's redirected stdout/stderr, which matters when agents capture JSON through pipes.
The CLI is defensive about the control-plane channel flapping while the desktop starts or quits. crates/capto-cli/src/resilience.rs implements a circuit breaker: it opens after the first 3 consecutive failures, fails fast while open, and half-opens after a 5-second cooldown to probe again. client.rs retries with exponential backoff (250ms, 500ms, ...) but only for idempotent GET requests, retrying a mutating POST that already reached the server could double-record.
crates/capto-cli/src/main.rs::map_http converts the error code from the server into the exit-code table above: unauthorized/desktopUnavailable → 2, stateConflict → 3, capture → 4, encode → 5, configIo → 6, badRequest/usage → 1, and anything else → 2. The lockfile, envelope, and request types it uses come from capto-ipc (crates/capto-ipc/src/types.rs).
Agents follow a fixed round trip:
1. doctor # exit 2 → open desktop / ask user; ffmpegOk must be true
2. list displays # optional
3. record start --source display
4. status # poll
5. record stop
6. outputs recent --limit 1
The screenshots workflow is status/open → shot --source display → use data.path. Agents should check status before starting, never record start twice, always record stop when done, and prefer --no-launch in headless CI.
The CLI is the backbone of both agent npm packages, see capto-agent-skill and capto-dsh-plugin. The skill ships skills/capto/SKILL.md plus references/cli.md (kept in sync with docs/CLI.md) and tells an agent to call capto <command>; the DeepSeek Harness plugin registers 14 typed capto_* tools that each invoke the CLI and normalize its output and exit codes. Both wrap the same binary rather than reimplementing anything.