Ingram Cloud

Documentation

CLI

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:

  1. An idsmt_1CeiMLuPbyEaUASpW5BbxU, or its bare body with the prefix omitted.
  2. A natural key — the value you already know: a smith's external_id, an agent's slug, a project's name. Matched exactly, server-side.
  3. A prefix — git-style abbreviation of the id, six characters is unambiguous in practice; two matches is an error naming both, never a guess.
  4. last (or last~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.