@carrieros/cli
v0.3.0
Published
Carrier OS command-line interface — an interactive shell plus commands for the Carrier OS developer platform (chat, sessions, files, usage) over the public /v1 API.
Maintainers
Readme
Carrier OS CLI
A command-line client for the Carrier OS public /v1 API, covering the
workflow end-to-end:
Install CLI → cos login (browser) → Chat → Sessions → Files → Memory → UsageSign-in is a browser round trip, not a pasted key: cos login opens your
Carrier OS account, you approve the terminal once, and the CLI keeps its own
short-lived credentials. It is the same account you use on carrieros.ai — there
is no separate developer-platform login to create.
It consumes the public API exactly like any third-party developer would — no backend logic is duplicated. Every command is a thin, typed wrapper over an endpoint that already exists.
Installation
The CLI installs globally via npm — no repository cloning required for end users — and exposes two identical launch commands:
cos # primary — opens the interactive Carrier OS shell
carrieros # full-name alias (identical)npm install -g @carrieros/cli
cos # or: carrierosFrom source
Working on the CLI itself, or testing an unreleased change:
npm run build
npm link # `cos` now runs your working copynpm link is preferable to npm pack + npm install -g <tarball> during
development: the tarball installs a frozen copy, so a rebuild does not
reach the installed command and you end up debugging stale code.
Update
Upgrade to the latest published version at any time:
npm install -g @carrieros/cli@latestCheck what you're running with cos version — it prints the CLI version
alongside the API version it's talking to.
Uninstall
Removes both carrieros and cos from PATH:
npm uninstall -g @carrieros/cliFor developers — run from source
Iterate without reinstalling on every change:
git clone <repo-url> carrieros-cli
cd carrieros-cli
npm install
npm run build
npm link # links `cos` + `carrieros` to your working copy
# or run directly, no global install:
npm run dev -- <command> # tsx, no build step
node dist/main.js <command>
npm linkcreates a live junction — rebuilds reflect immediately. Avoidnpm install -g <tarball>during development: it installs a frozen copy that shadows the link. The branded logo is pure Unicode/ANSI (no image assets); Windows.exeinstallers are intentionally not part of this POC.
How to use Carrier OS CLI
Prerequisites: Node.js 18+ (node -v) and a
Carrier OS account — the same one you use on carrieros.ai. Nothing to generate
beforehand.
1. Sign in (one-time setup)
cos loginYour browser opens on your Carrier OS account, you approve this terminal, and
the CLI stores the session it is issued
(%APPDATA%/CarrierOS/config.json, or ~/.carrieros/config.json, written
0600). The browser never sees those credentials and the CLI never sees your
password.
Approving is a spending decision — a CLI turn draws on the same credits the website does — so the page names the account and lists what the terminal will be able to do before you confirm.
The session refreshes itself. You will not be asked again unless you run
cos logout or the session is revoked.
Pointing at a local / self-hosted API instead of production
(https://api.carrieros.ai/v1)? Pass --url once:
cos login --url http://localhost:5000/v1Confirm the setup:
cos doctor # config, connectivity, key checks
cos whoami # workspace / project / account2. Open the interactive shell
Run cos with no command to drop into the interactive shell — the default
experience. Type a message to chat, or type / to open the command palette:
cos❯ how do I add auth to my Next.js app?
● …answer streams in word-by-word…
☼ Computed in 3s
❯ /model # switch the AI model (arrow-key picker)
❯ /mode # switch the Carrier mode (arrow-key picker)
❯ /websearch # search the web on your next message
❯ /help # browse every command (arrow-navigable menu)Web search is a capability of chat, not a separate mode. Three ways in:
❯ /websearch # arm the NEXT message, then it lapses
❯ /websearch latest OpenAI news # search right now, changing nothing
❯ /websearch on # standing preference until /websearch offA searching turn shows its phases, then cites what it read:
❯ /websearch
✓ Web search enabled
Scope your next message
Mode live web
❯ what shipped in the latest node lts?
● Understanding your request
● Searching the web
● Reading sources
⠹ Synthesizing answer…
● Node.js 24 became Active LTS in October…
Sources · 6
[1] BBC
Trump gives Iran 10 days to respond
bbc.com
[2] Reuters
Latest developments in Iran talks
reuters.com
+ 2 more · /sources
✓ Web search · 6 sources · 3.2s/sources prints the full list with URLs; /open 2 opens one in your browser.
Conversations are numbered, not UUIDs. /sessions prints an ordered list and
every session command takes that number — ids still work everywhere for scripting:
❯ /sessions
1 Debugging the auth flow 12 msgs · 2h ago
2 ► AWS outage research 5 msgs · 1d ago
❯ /session 1 # continue #1
❯ /history 2 # transcript of #2
❯ /session delete 2 # asks before deleting
❯ /context # what your next message is sent withTurn shaping — /fast trades refinement for latency; /hipwr runs High
Power's 4-pass pipeline (expansion → hardening → architecture → finalization).
High Power only applies to the auto, summary, reasoning and research
modes, and your workspace admin can disable it entirely.
Every active capability shows in the footer, so you always know what you're about to send:
‖ Standard chat · Reasoning · GPT-5.2 · Fast · HIPWR · 🌐 WebsearchSlash commands (type / to filter them live):
| Command | Does |
|---|---|
| /model · /models | switch / list provider models (GPT, Grok, Claude, …) |
| /mode · /modes | switch / list Carrier modes (general, code, research, …) |
| /websearch [query\|on\|off\|auto\|status] | arm the next message · search now · set the standing preference |
| /sources · /open <n> | sources from the last search · open one in the browser |
| /fast [on\|off] · /hipwr [on\|off] | fast mode (quicker) · High Power (4-pass refinement) |
| /new [title] · /sessions | start a conversation · list them, numbered |
| /session <n> · /session delete <n> | continue #n · delete #n (asks first) |
| /history [n] · /context | transcript · what your next message is sent with |
| /upload <file> · /files | add + list context files |
| /whoami · /usage · /doctor | account · usage · diagnostics |
| /login · /logout · /version · /help · /exit | account · info · quit |
Keys: ↑ ↓ history · Tab complete · Enter run · Ctrl+C twice to exit.
3. Or run one-off commands (scripting / CI)
Every slash command is also a plain subcommand, so you can script the CLI without the shell:
cos chat --mode code # focused chat REPL
cos sessions # list conversations
cos history # transcript of the active session
cos upload ./report.pdf # upload + index a file
cos usage # requests, tokens, estimated cost
cos logout # remove the key from this machineCommands
| Command | Endpoint(s) reused | Purpose |
|---|---|---|
| login [--url] [--platform] | POST /platform/auth/device/{code,token} | Browser device authorization; stores the issued session |
| logout | POST /platform/auth/cli/logout | Revoke the session server-side and clear it locally |
| whoami | POST /v1/auth/verify | Show workspace / project / account |
| version | GET /v1/health | CLI + API versions |
| doctor | GET /v1/health, POST /v1/auth/verify | Config, connectivity, key checks |
| (no command) | POST /v1/chat | Default — the interactive Carrier OS shell |
| chat | POST /v1/chat | Focused chat REPL with a persistent session |
| models | GET /v1/models/chat | List provider models (GPT, Grok, Claude, …) |
| model [id] | — | Set the provider model (or auto); picker if no id |
| modes | GET /v1/models | List Carrier modes (general, code, research, …) |
| mode [id] | — | Set the Carrier mode; picker if no id |
| websearch [on\|off\|auto] | — | Live web search for chat turns; shows state if no arg |
| fast [on\|off] | — | Fast mode — shorter pipeline, quicker replies |
| hipwr [on\|off] | — | High Power — 4-pass refinement (admin-gated) |
| new [title] | POST /v1/sessions (only with a title) | Start a conversation |
| sessions | GET /v1/conversations | Numbered list of conversations |
| session <n\|id> | GET /v1/sessions/:id | Continue that conversation |
| session use [n\|id] | GET /v1/sessions/:id | Same — kept for compatibility |
| session delete <n\|id> | DELETE /v1/sessions/:id | Delete (asks first) |
| history [n\|id] | GET /v1/sessions/:id/messages | Print a transcript |
| context | GET /v1/sessions/:id | What the next message is sent with |
| clear | — | Start a fresh session (drops the local pointer) |
| upload <file> | POST /v1/files + …/:id/process | Upload and index a file |
| files | — (local) | List files uploaded from this machine |
| usage | GET /v1/usage | Requests, tokens, estimated cost |
| guide | — | Styled workflow overview |
In the interactive shell every command above is also a /slash command
(/sessions, /upload, /usage, …), plus /model · /mode · /clear ·
/help · /exit. See How to use.
Architecture
Layered and dependency-inverted — commands depend on services, services depend on the public API contract, nothing depends on the other direction.
src/
main.ts # Commander wiring + top-level error handling
types/index.ts # The /v1 contract, typed (no logic)
services/
config.ts # ConfigStore — the only thing that touches disk
api.ts # ApiClient — the only thing that speaks HTTP
context.ts # CommandContext — DI passed to every command
output.ts # chalk formatting + Markdown→ANSI renderer
spinner.ts # ora spinner helper
meta.ts # CLI version lookup
commands/ # one file per command; each `register(program, ctx)`
login · logout · whoami · version · doctor
chat · sessions · history (clear lives with sessions)
upload · files · usage · models · helpKey design decisions
- Single HTTP boundary.
ApiClientis the only module importingaxios. Each method maps 1:1 to a/v1endpoint and returns the already-unwrappeddata, or throws a typedApiErrorcarrying the server's{code, message}. Adding an endpoint = adding one method. - Single disk boundary.
ConfigStoreowns%APPDATA%/CarrierOS/config.json(falls back to~/.carrieros/config.json, written0600). It stores the CLI's own access/refresh pair, the active session id, a cached scope, and recent uploads — never a main-site cookie, and never your password. - Dependency injection. Every command receives a
CommandContext(config,client(),requireAuth()), so commands are thin and don't reach for globals.requireAuth()centralizes the "not logged in" guard. - No duplicated business logic. Modes, cost, extraction, rate limits, semantic token stripping — all already handled server-side. The CLI just renders JSON.
- Strong typing.
strictTypeScript withnoUnusedLocals/Parameters; the entire/v1contract is typed intypes/index.ts. - Windows-first, clean UX.
chalkcolors,oraspinners,@inquirer/promptsfor the masked key prompt and session picker, and a small built-in Markdown→ANSI renderer (no markdown dependency).
Streaming is simulated: the API returns the full response, which the CLI then reveals word-by-word so it reads like live streaming. Real SSE token streaming would require a backend change.
Testing steps
Prerequisites: the API running locally (npm run dev in api.carrieros.ai,
default http://localhost:5000) and the main site running (default
http://localhost:3000) so the browser step has a page to land on. Point the
API at it with CLI_AUTHORIZE_URL, or set FRONTEND_URL and let the API append
/cli/authorize.
cd carrieros-cli
npm install && npm run build
# Connectivity (no session needed)
node dist/main.js doctor # ✓ API reachable, ⚠ not signed in
node dist/main.js version # CLI + API versions
# Auth — opens the browser, approve there, the CLI finishes on its own
node dist/main.js login --url http://localhost:5000/v1
node dist/main.js whoami # workspace / project / account
node dist/main.js doctor # now: ✓ signed in
# Chat + sessions
node dist/main.js models # list Carrier modes
node dist/main.js chat # ask something; /new, /exit
node dist/main.js sessions # the session appears, marked ►
node dist/main.js history # transcript of the active session
node dist/main.js clear # next message starts fresh
# Files + usage
node dist/main.js upload ./somefile.pdf
node dist/main.js files
node dist/main.js usage
node dist/main.js logout # key removed from this machineNegative checks
node dist/main.js usage # (logged out) → "Run `carrieros login` first."
node dist/main.js login --url http://localhost:1/v1 # → clean NETWORK_ERROR
node dist/main.js login --url http://localhost:1/v1 && node dist/main.js doctor # → clean NETWORK_ERRORNot in scope yet
No billing · no marketplace · no plugins · no agents · no SDK · no IDE
integration · no repo awareness · no multi-agent · no .exe installers.
Browser sign-in and self-update both landed — cos login and cos update.
