Descanto Docs
CLI

CLI (descanto command)

descanto: the command-line interface for Descanto cloud desktops.

@descanto/cli ships the descanto command -- a thin wrapper over @descanto/sdk (bundled in) for driving desktops from a shell or a CI pipeline.

Install

npm install -g @descanto/cli
descanto --help

# or run it without installing:
npx -y @descanto/cli --help

Auth

Highest wins:

  1. --api-key <canto_sk_...> (exists for CI; beware shell history)
  2. CANTO_API_KEY env var
  3. ~/.config/canto/config.json ($XDG_CONFIG_HOME/canto/config.json when set), written 0600 by descanto login
descanto login    # prompts (hidden) for the key, validates via /v1/me, saves config
descanto whoami

Base URL resolves the same way: --base-url -> CANTO_BASE_URL -> the config file -> the SDK default. descanto login follows that same chain when validating the key, so it always talks to the API every later command will. Logging in again merges into the existing config file rather than replacing it -- an existing baseUrl survives a re-login, and only --base-url (not CANTO_BASE_URL, which stays env-scoped for that one invocation) gets persisted.

Commands

descanto list [--state awake]
descanto create [--tier default] [--billing-mode hourly] [--idle-timeout 600] \
             [--env K=V ...] [--setup-script file.sh] [--ephemeral]
descanto wake <id> [--no-wait]
descanto hibernate <id> [--no-wait]
descanto destroy <id> [--yes] [--no-wait]          # prompts unless --yes or --json
descanto fork <id> --count N --acknowledge-shared-state [--ephemeral] [--no-wait]

descanto exec <id> <command...> [--timeout SECS]   # exits with the guest exit code
descanto exec <id> -d <command...>                 # detached: prints the processId
descanto exec-status <id> <process-id> [--wait]

descanto files get <id> <remote-path> [local-path] # omitted/- writes raw bytes to stdout
descanto files put <id> <local-path> <remote-path> # - reads stdin

descanto stream <id> [--claim view|control] [--takeover] [--open]
descanto usage [--days 30] [--start MS --end MS]

descanto keys create <name> | descanto keys list | descanto keys revoke <id>
descanto webhooks create --url U --event E [--event E ...]
descanto webhooks list | descanto webhooks delete <id>
descanto webhooks deliveries <id> [--limit N]

Each command maps directly onto the API reference: descanto create is POST /v1/desktops, descanto exec is POST .../exec, and so on -- see the linked reference pages for field-level detail on each (--tier, --billing-mode, --idle-timeout, --env, --setup-script map onto CreateDesktopBody; --acknowledge-shared-state is the same required acknowledgment forking requires everywhere else).

descanto exec -d mirrors the SDK's detached: true -- it prints the processId and returns immediately instead of blocking; poll it with descanto exec-status <id> <process-id> [--wait].

--json contract

Every command accepts --json: exactly the API's JSON (or the SDK's settled result) on stdout, nothing else on stdout; problem+json (or { "error": { "message": ... } } for non-API failures) on stderr. This makes descanto ... --json | jq safe to script against without worrying about interleaved human-readable output.

Exit codes: 0 success, 1 API/runtime error, 2 usage error -- except blocking descanto exec (and descanto exec-status --wait), which instead propagates the guest command's own exit code, so descanto exec $ID make test && echo ok works the way you'd expect from a local shell.

Development

bun install
bun run test        # vitest, SDK/fetch mocked
bun run typecheck
bun run build        # tsup, bundles @descanto/sdk (noExternal)

Releases go out in lockstep with @descanto/sdk / @descanto/mcp.

On this page