@infrarates/cli
v0.3.0
Published
CLI for the Oimo public API
Readme
Oimo CLI
Requires Node.js 22.11 or newer, with no runtime dependencies. The npm
package is @infrarates/cli and the command is oimo. MIT licence.
Install it from public npm (see Releases):
npm install -g @infrarates/cli # install
npm install -g @infrarates/cli@latest # update to the newest release
oimo --version
npm uninstall -g @infrarates/cli # removeFrom a checkout, npm install -g . in this directory or node oimo.mjs
runs the unreleased code. Run npm test for its tests.
Read data with an organization API key
Create a key on the Oimo API keys page. The server accepts keys for reviewed price history, customer price/terms/status reads, providers, catalog, activity and comparisons.
export OIMO_API_KEY='your-organization-api-key'
oimo providers list
oimo prices latest --provider openai --limit 100
oimo prices history --providers openai,anthropic --all > prices.json
oimo terms latest --provider openai
oimo terms diff --provider openai --doc terms-of-use
oimo status
oimo catalog --provider openai
oimo compare resources
oimo compare prices --resource 'resource-id-from-compare-resources'Keys always get the customer view, even if a staff session also exists. Pending rows are hidden and status responses omit internal details. Keys carry an organization identity and no user role; management and personal commands require a user session.
OIMO_API_KEY is not tied to an origin. A command that accepts a key
sends it, as a Bearer header, to the origin that command targets:
https://oimo.com by default, otherwise the one named by --base-url or
OIMO_BASE_URL. This differs from saved sessions, which are stored per
origin and never sent anywhere else. The origin must be HTTPS (HTTP only on
loopback) and redirects are refused, so the key reaches the origin you named
and no other. When a key is set, no session is sent with it; session commands
never send the key.
A key works only in the environment that issued it, so create one per environment and set it for the command instead of exporting a production key in a shell that also targets other origins:
OIMO_API_KEY="$OIMO_DEVELOP_KEY" oimo prices latest --base-url https://oimo.dev
OIMO_API_KEY="$OIMO_DEVELOP_KEY" OIMO_BASE_URL=https://oimo.dev oimo statusDo not point --base-url or OIMO_BASE_URL at an origin you do not
trust with the key in your environment.
Customer operations with a session
Run oimo login. It opens your default browser, signs you in through the
website's existing WorkOS flow, and asks you to approve CLI access for the shown
account. Approval starts a fresh WorkOS authorization-code exchange for the CLI;
AuthKit requires a fresh sign-in (max_age=0). The new session has its own
refresh token, so browser and CLI refreshes do not invalidate each other. The callback leaves
the website's session and staff organization selection in place. No cookie
copying is needed.
The CLI validates the returned session with /v1/me before saving it.
Use --base-url https://oimo.dev to sign in against the develop deployment.
For local development, start the service with [email protected]
and run oimo login --base-url http://localhost:8080.
If your browser cannot be launched automatically, the terminal prints a URL.
Open it on the same computer. login --no-browser prints the URL without trying
to launch a browser. Login waits up to five minutes; --timeout <seconds> sets
a deadline from 1 to 600 seconds. Ctrl-C or the browser's Cancel button ends it.
The handoff uses a temporary listener bound to 127.0.0.1, a random state value,
and an ephemeral X25519 key. The server requires same-origin browser consent,
binds the new code exchange to the approved account with a separate signed
OAuth state cookie, and encrypts the new CLI session with
X25519/HKDF-SHA256/AES-256-GCM. The encrypted result is POSTed to loopback,
with no credentials in URLs or the terminal. The CLI checks
the browser Origin, callback state, encryption tag and expiry, then closes the
listener. The handshake uses no persistent server records and works across
service instances.
login --session-stdin retains manual session import for scripts: it reads
OIMO_SESSION or a session value from stdin. Automatic browser login
always obtains a fresh session even if that environment variable is set.
Manually importing a browser session shares its refresh token; use browser
login to obtain independent credentials. Existing CLI sessions created before
this change must log in again to gain that independence.
oimo login
oimo me
oimo organizations get
oimo organizations create --data '{"name":"My workspace"}'
oimo organizations switch --data '{"organizationId":"org_..."}'
oimo webhooks list
oimo webhooks create --data @webhook.json
oimo webhooks deliveries webhook-id
oimo webhooks test webhook-id
oimo members list
oimo members update membership-id --data '{"role":"member"}'
oimo invitations create --data '{"email":"[email protected]","role":"member"}'
oimo api-keys create --data '{"name":"My integration"}'
oimo preferences get
oimo provider-requests list
oimo logoutThe CLI sends the session cookie and target Origin; existing role checks, current
membership checks, tenant boundaries and last-admin protection remain in force.
Admins manage people; admins and members manage integrations. Creation and
rotation commands print newly issued API keys or signing secrets in their JSON
responses, as the API does. JSON bodies may be inline, @file.json, or - for
stdin. See the service README for request-body schemas and oimo --help
for every command and its options.
Sessions are stored per origin at ~/.config/oimo/sessions.json with
permissions 0600. --config <file> overrides the location. Responses that rotate
or switch sessions update the saved value, and one that ends the session removes
it; when a response sets the cookie twice, the last one is kept, as in a
browser. Each update is applied to the file's current contents under a lock
file (sessions.json.lock), so commands running at the same time, against the
same or different origins, do not undo each other's rotations or logouts. The
organization a staff user is viewing is saved beside the session
and sent with later commands, until a switch into an organization they belong
to, a new login or logout clears it. An environment session overrides the
saved value and keeps nothing between commands, so a staff viewer selection
does not carry over; unset it after login to use the saved session and
rotations. logout removes only the selected origin's local session; it does
not revoke either remote WorkOS session or unset environment credentials.
Website logout ends the browser's WorkOS session and leaves an independently
created CLI session usable. Revoking the CLI's WorkOS session leaves the browser
session usable; revoking all sessions for the user ends both. Revocation is
enforced when the affected access token expires and its next refresh fails.
Output and paging
prices history shows a readable table in an interactive terminal: provider,
resource, dimension, exact price with currency, and billing unit. Each observation
also shows its tier, source and recorded timestamp, plus effective dates and
renames when present. Names wrap rather than being truncated; very narrow
terminals use stacked observations. The header shows the pinned snapshot and
providers, and a paginated result prints a command to read the next page with
the same --base-url, --limit and --config.
Piping or redirecting history output defaults to JSON with all original fields.
Use --format json to get JSON in the terminal, or --format table to redirect
the readable display. Rates retain their decimal strings and native currencies
and units, without conversions or rounding. Other commands print JSON; empty
responses print null, and terms content prints the upstream text.
Terms are vendor-written, so in an interactive terminal their control
characters (escape sequences, BEL, bidirectional overrides) are shown escaped
as \uXXXX instead of being interpreted; line breaks and tabs are kept. Piped
or redirected output is the text as served.
Errors go to stderr with exit code 1.
prices history reads /v1/prices/history, including older rates. By default,
one page is read. JSON includes asOf, providers, prices and nextCursor. --all combines
all pages in memory and prints only after every page succeeds. The cursor pins
the snapshot and providers; subsequent requests send only cursor and limit.
--alias takes an alias group's id or comparison name (compare aliases lists
them) and reads that resource at every provider that sells it; the answer adds
alias with the group's revision and members, and the cursor pins that revision.
Keep asOf to reproduce a reading. Default page size is 5000, maximum 10000.
Credit-denominated providers require explicit selection under the API defaults.
oimo prices history --as-of 2026-10-01T00:00:00Z --limit 100
oimo prices history --providers openai --limit 20
oimo prices history --providers openai --format json
oimo prices history --alias claude-opus-5.5 --all
oimo prices history --all > prices.json
oimo prices history --cursor 'cursor-from-previous-response'
oimo prices resource-history --provider openai --resource gpt-5
oimo prices latest --provider openai --offset 100 --limit 100 --sort price --dir asc
oimo prices history --base-url https://oimo.dev --allprices latest uses offset paging (maximum 1000 rows per page; without
--limit, an API key or a customer session gets the first 1000); it is a current
reading, not a pinned export, and has no automatic paging. resource-history
reads the existing per-resource /api/prices/history endpoint. Other list
commands follow their existing API bounds; use --after where shown in help.
Timestamps and provider names are validated by the API.
OIMO_BASE_URL sets a default origin; --base-url overrides it. Saved
sessions are never sent to another origin; an environment API key goes to the
selected origin (see above). HTTP is supported only on loopback
for local development. Redirects are refused and each request times out after
30 seconds. Requests are not automatically retried, including mutations.
Releases
The package is @infrarates/cli on public npm, first released as 0.1.0
(OIM-511). 0.2.0 removes compare proposals list and compare proposals
create: customers no longer propose alias changes, and the server no longer
answers them (OIM-677). 0.3.0 changes only the sign-in timeout message: sign-up
is open, so it points newcomers to Sign up instead of an invitation email
(OIM-867). The @infrarates scope is the one Oimo can already publish to: its packages
are maintained by Jorge's personal npm account (jtkm6), which is also the
account that links the package to the publish workflow. The owner of @oimo
is unconfirmed. The command stays oimo.
oimo --versionprints the installed package'spackage.jsonversion, so a release changes only that file. Versions follow semver: a removed or renamed command or option is a major bump while the version is 1.x or later (a minor one while it is 0.x).install-smoke.mjs(part ofnpm test) packs the package, checks the tarball holds exactlypackage.jsonand thefileslist (which includesLICENSE), installs it globally into an empty prefix with an empty npm cache and config, runs the installedoimofrom outside the repository, installs the next patch release's tarball over it (the upgrade path, e.g. 0.1.0 → 0.1.1), then uninstalls it. Release versions are plainMAJOR.MINOR.PATCH; the test refuses any otherpackage.jsonversion. It has passed onnode:22.11.0-slim(theenginesfloor) andnode:24-slimwith only this directory copied in:podman run --rm -v "$PWD:/src:ro,Z" node:22.11.0-slim \ sh -c 'cp -r /src /cli && cd /cli && npm test'.github/workflows/publish-cli.ymlpublishes. A pull request that touches this directory runs the tests andnpm publish --dry-run. To release:- merge a PR that bumps
versioninpackage.json; - tag that merged commit
cli-v<version>and push the tag (git tag cli-v0.1.1 <sha> && git push origin cli-v0.1.1); - a platform reviewer approves the run in the
npm-publishenvironment.
The workflow refuses a tag that does not match
package.jsonor a commit that is not onmain. It publishes with npm trusted publishing, where the job's GitHub OIDC token buys a one-time publish token, so no npm token is stored. It then installs the published version from npm on Node 22.11 and checksoimo --version. There is no provenance attestation because the repository is private.The package's Trusted Publisher settings on npmjs.com link it to
oimo-ai/oimo,publish-cli.ymland thenpm-publishenvironment, which is what lets that one job publish without a token. npm also requiresrepository.urlinpackage.jsonto nameoimo-ai/oimoexactly; without it the publish fails with onlyENEEDAUTH.- merge a PR that bumps
