Manage agents, sessions, and kits on the Orca platform from the terminal.
TypeScript + commander for command routing, Ink (React) for TTY rendering, and
the official openai package for
Orca's OpenAI-compatible Agents API.
Running orca with no arguments shows a one-line brand banner and the command
list. In a terminal, list and detail views are borderless: hierarchy comes from
whitespace and weight, not boxes. Each view opens with a bold mint title and
·-separated metadata in subtle gray, content rows indent two spaces, and
list views end with a subtle next: hint teaching follow-up commands. Mint is
the brand accent (src/ui/theme.ts); primary actions use the terminal's inverse
foreground, while piped or --json output stays plain and
machine-clean. Unicode glyphs are restricted to a CP437/Latin-1 safe tier and
fall back to ASCII when the locale is not UTF-8 or ORCA_ASCII=1 is set.
NO_COLOR is honored, independently of the glyph tier.
End users install a standalone binary (no Node required) via the landing domain:
curl -fsSL https://orcapods.ai/install.sh | shThe script detects your OS/arch, downloads the matching binary, verifies its
SHA-256 checksum, and installs to ~/.local/bin/orca. Pin a version with
ORCA_VERSION=cli-v1.0.0 or change the target directory with ORCA_INSTALL_DIR.
Windows users download orca-windows-x64.tar.gz from the
releases page.
This repo is the public home of the CLI source and its release binaries; the Orca platform it talks to is a separate, private service.
orca update updates a standalone binary to the latest release in place: it
queries the GitHub Release, downloads the matching orca-<os>-<arch> binary,
verifies its SHA-256 against SHA256SUMS, and atomically swaps the running
executable. It needs no Orca credentials, so it also rescues a CLI too old for
the server (the server answers an old CLI's paths with "This CLI is too old for
Orca. Run orca update.").
orca update # update to the latest release
orca update --check # report whether a newer version exists, don't install
orca update --tag cli-v1.0.0 # install a specific version (also accepts 1.0.0)
orca update --force # reinstall the target version even if already currentorca -v prints the version and, in an interactive terminal, appends a hint
when a newer release is available. The check is cached for ~24h under the config
dir (update-check.json), is skipped in scripts (non-TTY stderr), and is
disabled entirely by ORCA_NO_UPDATE_CHECK. Both commands honor GITHUB_TOKEN
to lift the anonymous GitHub API rate limit.
Self-update is only available for the standalone binary. A source checkout
updates with git pull; Windows can't replace a running .exe, so re-download
orca-windows-x64.tar.gz from the releases page. In each of these cases
orca update prints the right instructions instead of attempting a swap. The
package is not published to npm; the installer and a source checkout are the
two supported paths.
Binaries are Bun-compiled (each embeds the Bun runtime, ~60-100 MB raw / ~23 MB
gzipped) and cross-compiled for all platforms from one CI runner. Cut a release
by pushing a tag; the release-cli workflow builds, packages, and publishes the
GitHub Release (default GITHUB_TOKEN, no extra secret needed):
git tag cli-v1.0.0 && git push origin cli-v1.0.0Build locally with npm run package:binaries (outputs dist-bin/*.tar.gz +
SHA256SUMS), or a single target with npm run build:binary -- bun-linux-x64.
The install.sh served from the landing domain points its REPO at this repo.
git clone https://github.com/okikorg/orca-cli && cd orca-cli
npm install
npm run dev -- --help # dev loop (tsx)
npm run build && npm link # global `orca`The CLI authenticates with an Orca API key (orca_sk_...). orca login (an
alias of orca auth login) signs in with a one-time code (RFC 8628 device
login), which works everywhere, including inside coding agents and over SSH:
- The CLI prints a code and a link to the dashboard's
/devicepage, and opens the link when a person is at the terminal. - Signed in to the dashboard, you check the code and approve (or deny) it.
- The CLI's next poll receives a new API key with your role, shown once and stored locally. No secret is stored on the server before that poll.
For CI, --with-token orca_sk_... stores a key minted elsewhere (orca keys create, or the dashboard) without any flow. Keys are stored per context in
~/.config/orca/config.json (chmod 600).
orca login # device login; defaults to the Orca production API
orca login --no-browser # print the code and link only
orca login --api-url http://localhost:8080 # a local or self-hosted server
orca whoami # tenant, actor, and role the stored key acts as
orca auth status
orca auth logout --revoke # revoke the key server-side, then clear itorca login defaults to the production API (https://api.orcapods.ai); pass
--api-url for a self-hosted or local server. The login is named after this
machine ("CLI on laptop", or "Claude Code on laptop" when a coding agent drives
it): the dashboard's approval page shows that name, and the minted key carries
it. The dashboard origin comes from the server's verification link and is
stored with the context; billing buy and billing manage send it so the
checkout returns there. orca kits show and orca kits copy take an Orca kit's
link (https://app.orcapods.ai/kits/kit-...) or its public id.
orca auth logout --revoke revokes the key on the server before forgetting it.
If the server refuses (a member's CLI key may not be theirs to revoke) or
cannot be reached, the key keeps working, so the CLI keeps it too and says so;
plain orca auth logout forgets it here only.
Contexts work like kubectl contexts, and are local only: orca context list,
orca context use prod, or per-invocation orca --context prod agents list.
orca doctor is a preflight that verifies everything the other commands need
and prints a concrete fix for every problem it finds. It runs read-only probes
only (it never creates a session, key, or any resource), timeboxes each network
probe to 3s so it never hangs, and completes in a few seconds.
orca doctor # Doctor header line, one row per check (status glyph + word), fix lines under failures
orca doctor --json # array of { name, status, message, fix? }
orca doctor --strict # promote warnings to failuresIt checks: Node version (>= 22), the color/TTY situation (informational),
whether the config file exists, parses, and is chmod 600, how the active
context resolves (which field came from a flag, env var, file, or baked default),
server reachability (GET /health, with latency), the API key's presence and
validity and role (GET /api/whoami), a credit preflight (GET /api/billing/wallet: an empty wallet warns, since turns on Orca credit are
refused while models on your own provider keys keep working), and the dashboard URL
that billing checkout returns to. Each check reports pass, warn, fail, or skip.
The exit code is 0 when nothing failed (warnings are allowed) and 1 when any
check failed; --strict also fails on warnings.
Environment overrides (all optional, win over the config file):
| Variable | Meaning |
|---|---|
ORCA_API_KEY |
Orca API key |
ORCA_API_URL |
server base URL (defaults to the Orca production API) |
ORCA_DASHBOARD_URL |
dashboard base URL, where billing checkout returns |
ORCA_CONTEXT |
context name |
ORCA_CONFIG_DIR |
config directory (default XDG) |
ORCA_ASCII |
set to 1 to force ASCII glyphs (no Unicode tier) |
CI needs no config file: ORCA_API_KEY=... ORCA_API_URL=... orca agents list --json.
[x] marks a positional that opens an interactive picker when omitted in a
terminal; in a script it is required (exit 2). An <agent> is an agent id or
the name of exactly one agent: the CLI asks the server for an agent with that
id first, then matches names. A name several agents share is refused with
their ids (exit 2); an unknown one is not found (exit 4).
orca login | orca auth login [--api-url u] [--label l] [--no-browser] [--with-token orca_sk_...]
orca whoami | orca auth whoami
orca auth status
orca auth logout [--revoke] [--yes]
orca context list|use [name]|show
orca chat [agent] [prompt...] [--session id] [--sandbox] [--template t] [--vault v]... [--file path[:dest]]...
orca agents list
orca agents get [agent]
orca agents create -f agent.yaml # YAML or JSON; - for stdin
orca agents update <agent> -f fields.yaml # only the fields in the file change
orca agents delete [agent] [--yes]
orca models [--payer orca|<provider>] # every model you can use, grouped by who pays, with the free model marked
orca sessions list [--agent agent]
orca sessions get <id> # its model, sandbox, why the sandbox last slept, usage
orca sessions create --agent <agent> [--sandbox] [--template t] [--vault v]... [--file path[:dest]]...
orca sessions items <id> [--limit n]
orca sessions artifacts <session> # files its turns saved under /workspace/outputs
orca sessions artifacts download <session> [artifact] [-o path|-] [--all]
orca sessions sleep <id> [--no-wait] # put its sandbox to sleep now; its next message wakes it
orca sessions delete <id> [--yes]
orca usage [--days n] [--group-by model|provider|credential|session|agent] [--session id] [--meter m]
orca usage events [--meter m] [--charged]
orca skills list|get <id>|delete <id> [--yes]
orca skills create <folder> # an Agent Skills folder with SKILL.md
orca vaults list|create <name>|delete <id> [--yes]
orca vaults credentials list <vault>
orca vaults credentials add <vault> --name n --server https://... [--token t]
orca vaults credentials add <vault> --name n --server https://... --basic --login l # password from stdin or a hidden prompt
orca vaults credentials delete <vault> <id> [--yes]
orca files list
orca files upload <path> [--name filename]
orca files download <id> [-o path|-]
orca files delete <id> [--yes]
orca kits show <link|public-id> # what the kit holds; no login needed
orca kits copy <link|public-id> [--name key=name]... [--skip key]... [--dry-run] [--yes]
orca keys list
orca keys create [name]
orca keys revoke <id> [--yes]
orca billing wallet
orca billing buy pro|max|pack:<cents> [--no-open] # admin; opens the checkout
orca billing manage [--no-open] # admin; opens the billing portal
orca mcp serve
orca doctor [--strict]
orca update [--check] [--tag t] [--force]
List commands share --limit N (1 to 100, default 10), --after <id> (the
cursor a previous page printed), and --all (follow the cursor through every
page, up to 10,000 rows).
Every list/get command supports --json (raw API payloads, stdout only).
When stdout is not a TTY, output degrades to uncolored tab-separated lines,
so orca agents list | cut -f1 works.
Key minting prints the secret exactly once. In a pipe, stdout carries only the
secret: ORCA_API_KEY=$(orca keys create ci </dev/null).
Agents, sessions, skills, vaults, and files live on the server's /v1 Agents
API, which the CLI calls through the openai package exactly as any OpenAI
client does. agents create -f takes the POST /v1/agents body; the model
says who pays (orca/openrouter/<OpenRouter id> for Orca credit,
<provider>/<id> with your own key for openai, anthropic, openrouter, vercel or
cheaperinference):
model: orca/openrouter/openai/gpt-5.6-luna # or anthropic/claude-sonnet-4-5 on your Anthropic key
name: support
instructions: |
You answer support questions.
tools:
- type: mcp
server_label: docs
transport: { type: http, server_url: https://mcp.example.com/docs }
credential_id: cred_123 # from: orca vaults credentials add
reasoning: { effort: medium }Only the shape is checked locally; the server validates every field, checks the
model against its provider's list, and the CLI prints its message and the field
it is about. In a terminal, a file with no model opens a picker of every model
you can use, grouped by who pays: Orca credit, at OpenRouter's price per million
tokens plus their fee, then each provider you saved a key for. With --json or
outside a terminal the file must name the model; orca models lists the same
names anywhere (--payer orca or --payer anthropic for one group, --json
for the server's list). The server marks each Orca credit model for your
plan: orca models shows "free model" for Orca's free models, on every plan
(each plan with its own daily share, on the pricing page). Credit pays for
every other Orca credit model on every plan, Free included. On Free the picker
offers the free model first.
orca chat <agent> [prompt] runs a turn on a /v1 session with your API key.
Without --session it creates a session of the agent first (no environment by
default; --sandbox for a hosted sandbox, --template to build it from an
environment template, --vault to let it use a vault's credentials). A template
or vault is an id or the name of exactly one; a name two of them share is
refused with their ids.
orca vaults credentials add stores a bearer token for an MCP server, from
--token, stdin or a hidden prompt. With --basic --login <login> it stores a
login and password instead, which Orca sends to that server as HTTP Basic (such
as DataForSEO's API login): the password comes from stdin or a hidden prompt,
never a flag, and is never printed. <vault> is an id or a name.
--file <path>[:<dest>] (repeatable, on chat and sessions create) stages a
file on this machine into the session's sandbox, at /workspace/<its name>
unless a destination follows the colon, and implies --sandbox. Each file is
uploaded to your files first. A file over the server's staging limit (16 MiB,
64 MiB for all of a sandbox's files together) is refused before anything is
uploaded. With --session, the files go into that session's sandbox before the
message; a session without one is refused.
Files the agent saves under /workspace/outputs are kept as artifacts when its
turn completes. orca sessions artifacts <session> lists them, and orca sessions artifacts download <session> <artifact> saves one (an id, or its path
as listed) under its own file name, or to -o path, or to stdout with -o -.
--all saves every artifact of the newest turn into the current folder, or the
folder -o names. A file name from the server never leaves that folder.
orca chat support # interactive REPL
orca chat support --file sales.csv "chart /workspace/sales.csv into /workspace/outputs/chart.png"
orca sessions artifacts download sess_123 --all -o results
orca chat support "summarize the open tickets" # one turn, streamed to stdout
echo "and the oldest?" | orca chat support --session sess_123
orca chat support "hi" --json # NDJSON, one session event per lineIn a terminal with no prompt, orca chat opens a REPL: a persistent transcript,
a mint prompt marker, assistant text streamed live, and the agent's tool
activity grouped by intent. The session is created with the first message and
every turn reuses it. Ctrl-C cancels an in-flight turn (on the server too), then
exits from idle, printing the command that resumes the session.
With a prompt argument or piped stdin it runs one turn: the answer streams to
stdout as plain text and the session id is printed to stderr (session sess_...), so scripts can resume with --session, also when the turn fails or
is refused. A failed turn exits 1 after printing what arrived; Ctrl-C cancels
the turn on the server and exits 130.
When a turn ends because a credit ran out (Orca credit, or your own provider
account's quota), orca chat in a terminal says which, shows the warning that
switching may re-send the conversation without the provider's cache and behave
differently, and asks whether to top up or switch this session. Switching picks
a model on the other side (the same OpenRouter model is preselected when it
exists there), applies from the next turn for this session only, and continues
with Continue. Outside a terminal or with --json it prints the reason and
the session id and exits 1.
On the Free plan, a refusal (a paid model with no credit, the free model's
calls used up for today, the included machine time used up, the free model or
Free sandboxes busy, the day's turns used) prints the server's message and its
ways forward as words: switch this session to the free model, add a provider
key, add credit, upgrade, or try again later, each with its command. Where the
free model is one of them, the switch in a terminal lists it first.
The free model's refusals reach every
plan: its calls used up for today, the free model busy, the free model
withdrawn (the switch then offers the next free model the server lists), the
free model off right now (Orca turned it off for every plan), and the free
model not part of your plan (no share on it), each with the server's message
and its ways forward. The server's notices during a turn (machine time nearly used up,
a turn ended at the free model's calls or at Free's time limit for a turn,
a sandbox with no network, a new sandbox after a workspace
too large to keep, a workspace near the size Free keeps) are printed on
stderr, and with --json arrive as their own events. orca sessions get
shows why the sandbox last slept, with the server's message (a workspace
discarded or near the size Free keeps, a suspension, the network turned on),
and last_sleep with --json. A suspended organization's refusal, and a
refusal at the most sessions (or other objects) the organization can keep,
is the server's message as it is, exit 1.
Every money figure is the server's: usage rows are priced when they are written,
in micro-USD, and the CLI formats them without any arithmetic of its own, by the
same rule as the dashboard: dollars and cents at a cent or more ($1,234.57),
every digit under a cent ($0.000047). Every usage figure comes from
/api/usage, a session's included: orca sessions get shows its cost, tokens
by model, web searches and machine time from there, never from the session
object. Whether paid work is paused is the server's verdict too: orca billing wallet shows paid_work_paused and the plan's minimum balance, and orca doctor warns on it, so a balance above zero but under the minimum reads as
paused, as the server treats it. A call whose cost was not reported when it
ended is charged later, on a late_charges row that orca usage events shows
as "Charged later" for the row it charges, so a balance can go below zero;
orca billing wallet then prints it as it is (-$0.60) and says paid work
resumes when credit brings it to the minimum. Credit Orca grants is spent
first and expires; credit you buy never does: the wallet prints one granted credit line per grant with what is left and its last day, then paid credit.
On every plan with a free-model share it adds the free model's calls today
(none while the free model is off). On Free it adds the included machine time
and when it resets, the network line while Free sandboxes have none (and, once
the organization has credit, while running sandboxes still lack it), and where
to ask for more credit; when Free's included machine time and free model are
withheld from the organization, it says so instead of the hours.
orca usage # last 30 days: cost, meters, daily chart
orca usage --days 7 --group-by agent # also each agent's tokens and cost
orca usage --session sess_123 # one session
orca usage events --charged # the rows with a cost: what the wallet's charges sum
orca billing wallet # balance, granted and paid credit, plan, period, packs on sale
orca billing buy pro # checkout for the Pro plan
orca billing buy pack:2000 # checkout for the $20 credit pack
orca billing manage # change or cancel the planbuy and manage print the Polar link and open it in a browser at a terminal
(--no-open to only print it); both are admin. In a pipe, stdout carries only
the link.
A kit is a set of agents, skills, and environment templates, ready to copy
into your organization. Kits are published by Orca, and only Orca publishes
them; each has a public id (kit-...) and a page on the dashboard. No secret
ever enters a kit, so a copy lists the credentials to add.
orca kits show prints what a kit holds, from the server's review of its newest
version: who publishes it, the version, three summaries the server writes (what
it runs, what it reaches, what it spends), then every agent with its
instructions and tools, every skill with its files, every template with its
network, packages, setup commands, files and plugins, and the credentials to
add. --json prints the review as the server sends it.
orca kits show https://app.orcapods.ai/kits/kit-xxxxxxxxxxxxxxxxx
orca kits copy kit-xxxxxxxxxxxxxxxxx --dry-run
orca kits copy kit-xxxxxxxxxxxxxxxxx --name agent-1=my-support --skip skill-1 --yes # after the person says yescopy prints the same review, asks y/N, and copies the version it printed. If
Orca published a newer version in between, it copies nothing, prints "This kit
changed while you were reading" with the new review, and exits 1, so the person
decides again. --yes skips the question: a coding agent passes it only after
the person has read the review and said yes (without --yes and without a
terminal, copy exits 2). --dry-run prints the review and the plan, and
stops.
copy copies every asset under its own name unless renamed with --name key=name or left out with --skip key (keys come from kits show). A skill
renamed this way has its SKILL.md renamed too; skill names use only letters,
digits, _ and -. A name already used by the same kind fails the whole copy
with "Name taken" and nothing is copied; rename and run it again.
Each credential a copy needs says where it goes: an MCP server's credential in
a vault (orca vaults credentials add), an environment variable on the copied
template, which the copy names.
The repo doubles as a Claude Code plugin marketplace. The orca plugin ships
the use-orca skill (install, login, golden-path commands) and auto-registers
the Orca MCP server:
claude plugin marketplace add okikorg/orca-cli
claude plugin install orca@orca
Or register just the MCP server against an existing install:
claude mcp add orca -- orca mcp serve
orca mcp serve speaks MCP over stdio with one tool per CLI action: identity
and keys, agents and models, sessions and chat (which sends a message and
waits for the reply; it and create_session take files to stage),
sleep_session (puts a session's sandbox to sleep now), a session's
artifacts, skills, vaults, files, usage and billing, and kits (show_kit and
copy_kit, which copies the version show_kit returned). The
commands that only touch this machine (login, logout, context, doctor, update)
have no tool. Other MCP clients (Cursor, Codex) use {"command": "orca", "args": ["mcp", "serve"]}. Login for agent contexts is orca login (device
login).
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | API/network error; a chat turn failed |
| 2 | usage or validation error |
| 3 | auth: missing/invalid key, insufficient role |
| 4 | named resource not found or gone |
| 130 | interrupted via Ctrl-C |
npm test- vitest (config permissions, command handlers against a fetch mock that also serves theopenaipackage, Ink components via ink-testing-library)npm run lint/npm run typecheck- Module layout:
lib/(no Ink imports) ->commands/->ui/(theme'd Ink components; design tokens insrc/ui/theme.ts) - Server contract: the Orca server's README documents the
/apiroutes;/v1is the OpenAI Agents API as typed by theopenaipackage