CLI
ic is the Ingram Cloud command line: every /v1 operation as a command,
generated from the same OpenAPI document the API itself emits, plus browser
sign-in and a terminal chat with a smith. It is
@ingram-cloud/cli on npm.
npm install -g @ingram-cloud/cli
ic --help
Sign in
ic login
Opens a browser at the console's /cli/login, where you approve the request
and pick an organization; ic never sees a password or a pasted secret — the
console redeems a one-time code on a loopback callback and hands ic an
organization key, the same credential Auth & tokens
covers for infrastructure-as-code. On a machine with no browser, sign in with
--noBrowser instead: it prints a URL and asks you to paste back the code
the page shows.
ic login --noBrowser
An organization key can list and create projects and mint their tokens, but
reads no smith, run or memory itself — that needs a project token, which is
what ic project use gets you next.
Pick a project
A project is a tenant: its own smiths, agents and tokens, isolated from every other project in your organization.
ic project list
ic project use acme
ic project use mints a project-scoped token (30 days) with your
organization key and stores it under your OS config directory
($XDG_CONFIG_HOME/ingram-cloud/config.json, %APPDATA%\ingram-cloud\config.json
on Windows) — 0600, never world-readable. Every command after this that isn't
under ic organization sends that token. Running ic project use again
revokes the previous token before minting the next, so switching projects
doesn't accumulate live tokens on the server. ic project current prints
which one is active; ic logout revokes what can be revoked and forgets this
machine's login.
Signing in on more than one machine, or keeping staging and production
tokens side by side? --profile on login, project use, and every other
command names which stored login to use — omit it and default is assumed.
The command tree
The tree mirrors the /v1 surface: ic <resource> <action> [args] [flags],
one command per operation. A path parameter is a positional, in path order; a
request body's top-level fields and every query parameter are flags.
ic smiths create --external-id user_42 --display-name "Ada Lovelace"
ic smiths list --external-id-prefix user_
ic smiths get user_42
ic smiths runs create user_42 --input '{"role":"user","content":"hi"}'
Resource groups nest the same way the API's paths do:
ic smiths connections list user_42
ic smiths memory recall user_42 --query "billing"
ic organization projects tokens create acme
ic organization billing balance
ic <resource> --help, and ic <resource> <action> --help, show what a
level takes — flags, their wire type, and which are required — without
leaving the shell. A flag value can also come from a file: --instructions @prompt.md reads the file.
Not every request body is worth flag-by-flag: --body overrides.json (or
--body - for stdin) reads a whole JSON body from a file, and any flag set
alongside it overrides that one field:
ic smiths update user_42 --body overrides.json --display-name "Ada"
Naming a resource
A positional never has to be the raw id. Four forms resolve, in this order:
- An id —
smt_1CeiMLuPbyEaUASpW5BbxU, or its bare body with the prefix omitted. - A natural key — the value you already know: a smith's
external_id, an agent'sslug, a project'sname. Matched exactly, server-side. - A prefix — git-style abbreviation of the id, six characters is unambiguous in practice; two matches is an error naming both, never a guess.
last(orlast~1,last~2, …) — the most recently seen resource of that kind, from a local cache of ids you've listed or acted on.
ic smiths get user_42 # natural key
ic smiths get smt_1CeiMLuPb # id prefix
ic smiths runs list last # the smith you just touched
Piping and --json
A terminal gets a table or a field list; anything else gets the response body
as JSON, unchanged — ic smiths list | jq '.data[].id' needs no flag, the
same bargain gh makes. --json forces the JSON form even on a terminal,
for a script that always wants it.
Chat
ic chat --smith user_42
An interactive REPL against one smith: each line you type starts a streamed
run, replies print as they arrive, and a run that pauses with approval.required
prompts you to approve or reject it (answering any elicitation fields
inline) before resuming via /submit. Ctrl-C cancels the live run and exits;
a second Ctrl-C exits immediately without waiting for the cancel to land.
Passing the message as an argument instead of leaving it to the prompt makes it a single scripted turn:
ic chat --smith user_42 "What's on my account?"
No smith yet? --external-id with --agent upserts one — the same
(external_id, agent_id) idempotency POST /v1/smiths gives you directly,
so it's always safe to call:
ic chat --external-id user_42 --agent support-bot "hi"
The escape hatch: ic api
Every /v1 path is reachable even before it has a command of its own:
ic api get smiths/user_42/revisions
ic api post smiths -f - <<< '{"external_id":"user_42","agent_id":"agt_…"}'
ic api prints the response body exactly as the server sent it — no table,
no reshaping — which is what makes it double as a debugging tool for a
command's own output: run the same call through ic api when you want to see
the raw wire shape a table is hiding.
Shell completion
ic shell completion
Wires bash's complete for ic, including tab-completion for resource ids
pulled from ones you've recently listed or acted on.
Scripting without a stored login
INGRAM_CLOUD_TOKEN overrides the stored profile for one process — set it in
CI and skip ic login entirely:
INGRAM_CLOUD_TOKEN=$IC_TOKEN ic smiths list
IC_BASE_URL overrides the API base URL the same way, for a self-hosted or
staging endpoint.