# CLI

The RouterPlus CLI connects your coding tools to the gateway. You approve a key in the browser
once. The CLI stores the key and writes it into each tool's settings. The CLI also has a
coding agent of its own.

The CLI needs Node 24 or later. It has no other dependencies. Run it from a checkout of the
RouterPlus repository:

```bash
node tools/routerplus-cli/main.ts <command> [options]
```

## Commands

| Command | What it does |
|---|---|
| `login` | Opens the consent page, receives the approved key and stores it. `login --key-from-env` stores a key that you made in the console. |
| `setup <tool>` | Writes a tool's settings to use RouterPlus with the stored key. Tools: `claude-code`, `codex`, `cursor`, `opencode`, `cline`. |
| `status` | Shows the key's prefix, the gateway, its usage and limit (where the gateway reports them), and the tools that use RouterPlus. |
| `logout` | Deletes the stored key. With a management key, it also revokes the key. |
| `code [prompt]` | Starts a coding agent in the current directory. See [Coding agent](#coding-agent). |

## Log in

Make a key in the console, under Access > [API keys](https://app.routerplus.com/console/keys), and store it:

```bash
ROUTERPLUS_API_KEY=tm_vk_... node tools/routerplus-cli/main.ts login --key-from-env
```

The CLI reads the key from the environment, never from its command line, where other users of
the computer can see it in the process list.

### Log in from the browser

```bash
node tools/routerplus-cli/main.ts login
```

This needs the console's key approval page and `POST /v1/auth/keys`, which are not on every
gateway yet. When the gateway does not have them, use `--key-from-env`.

1. The CLI starts a server on `127.0.0.1`, on a random port, to receive the approval.
2. The CLI opens the consent page in your browser and prints its URL. If the browser does not
   open, copy the URL.
3. Sign in, select a workspace and click **Approve**. You can set a spending limit on the same
   page.
4. The browser returns to the CLI. The CLI exchanges the one-time code for a key with OAuth
   PKCE (S256).
5. The CLI stores the key in `~/.config/routerplus/credentials.json` with mode `0600`.

The approval must arrive within 5 minutes. The CLI never prints the key. It shows only the
first 12 characters.

| Option | Meaning |
|---|---|
| `--no-browser` | Print the URL and do not open a browser. Use this on a remote machine with port forwarding. |
| `--limit USD` | Ask for a spending limit, for example `25`. You can change it on the consent page. |
| `--key-from-env` | Store the key in `ROUTERPLUS_API_KEY` instead of opening the consent page. |

## Set up a tool

```bash
node tools/routerplus-cli/main.ts setup claude-code --dry-run
node tools/routerplus-cli/main.ts setup claude-code
```

`--dry-run` shows the lines that would change and writes nothing. Without `--dry-run`, the CLI
does these steps for each file it changes:

1. It copies the file to `<file>.bak`.
2. It writes the new file with mode `0600`.
3. It shows the lines it changed. The key shows as its prefix.

A second `setup` for the same tool changes nothing. The settings are the same as in
[Coding agents](/docs/install):

| Tool | What `setup` writes |
|---|---|
| `claude-code` | `~/.claude/settings.json`: `env.ANTHROPIC_BASE_URL` (`https://api.routerplus.com`, without `/v1`) and `env.ANTHROPIC_AUTH_TOKEN`. It removes `env.ANTHROPIC_API_KEY`, because that variable makes Claude Code open its login screen. Other settings stay. |
| `codex` | `~/.codex/config.toml`: `model_provider = "routerplus"` and a `[model_providers.routerplus]` table with `base_url` `https://api.routerplus.com/v1`, `wire_api = "responses"` and the key in `experimental_bearer_token`. It sets `model = "gpt-6-luna"` only when the file has no model. Other tables stay. |
| `opencode` | `~/.config/opencode/opencode.json`: `provider.routerplus` with `@ai-sdk/openai-compatible`, `baseURL` `https://api.routerplus.com/v1`, the key in `apiKey`, and `claude-sonnet-5-5`, `claude-opus-5-5` and `gpt-6-luna` with image input. It sets `model` only when the file has none. |
| `cline` | Writes no file and runs nothing. Cline keeps the key in its own settings, and its CLI takes the key only on its command line (`cline auth --apikey`), where other users of the computer can see it. So the CLI shows the steps for Cline's settings screen and the `cline auth` command without the key. `--copy-key` puts the key on the clipboard for you to paste. |
| `cursor` | Writes no file. Cursor keeps the key in its settings screen, so the CLI shows the steps. `--copy-key` puts the key on the clipboard for you to paste. |

After `setup`, the Codex and OpenCode files hold your key. Do not commit or share them.

## Status

```bash
node tools/routerplus-cli/main.ts status
```

```
Key: tm_vk_1a2b3c... (approved by you@example.com)
Stored in: /Users/you/.config/routerplus/credentials.json
Gateway: https://api.routerplus.com
Usage: $1.50 (limit $25.00, $23.50 left)
Rate limit: 300 requests per 1m
Tools:
  claude-code  configured
  codex        not configured
  cursor       set by hand
  opencode     configured with another key
  cline        not configured
```

- Usage and the limit come from `GET https://api.routerplus.com/v1/key`. A gateway without that endpoint
  shows "Usage: unavailable".
- "configured with another key" means that the tool uses RouterPlus with a different key. Run
  `setup` for that tool to use the stored key.

## Log out

```bash
node tools/routerplus-cli/main.ts logout
```

`logout` deletes the stored key. The key still works until you disable it in the console, under
Access > [API keys](https://app.routerplus.com/console/keys).

To revoke the key from the CLI, give a management key. Use `--management-key tm_mk_...` or the
`ROUTERPLUS_MANAGEMENT_KEY` variable. The CLI finds the key by its prefix in `GET /v1/keys` and
calls `DELETE /v1/keys/<id>`. This needs a gateway with the management API.

The tools that you set up keep the key in their own settings. Run `setup` again after the next
`login`, or remove the settings.

## Coding agent

`code` starts a coding agent in your terminal. The agent reads, searches and changes the files
in the current directory, and runs commands there. It uses the key in `ROUTERPLUS_API_KEY` when
that variable is set. Otherwise it uses the stored key.

```bash
cd my-project
node /path/to/routerplus/tools/routerplus-cli/main.ts code
node /path/to/routerplus/tools/routerplus-cli/main.ts code --model claude-sonnet-5-5 --max-cost 2
```

Type a task at the `>` prompt. The agent streams its answer and asks you before each change. It
keeps the conversation until you type `/clear` or `/exit`.

| Command | What it does |
|---|---|
| `/cost` | Shows the cost of the session in USD. |
| `/clear` | Starts a new conversation. |
| `/exit` | Quits and shows the session cost. |

### One-shot mode

Give the task as an argument and pipe stdin. The agent does the task, prints the cost and exits.

```bash
node tools/routerplus-cli/main.ts code --yes "Add a test for parseDate" < /dev/null
```

With no argument, the agent reads the task from stdin. In this mode the agent cannot ask you
for approval:

- Without `--yes`, it refuses writes and edits.
- Without `--yes-commands`, it refuses commands.
- Without `--auto-approve-reads` or `--yes`, it also refuses reads.
- It always refuses to send a file that may hold secrets, and a command that names one.

The exit code is `0` when the agent finishes and `1` when it stops early.

### Tools

| Tool | What it does | Asks you |
|---|---|---|
| `read_file` | Reads a text file, with line numbers. Takes `offset` and `limit` in lines. | Unless `--auto-approve-reads` or `--yes` |
| `list_dir` | Lists a directory. | Unless `--auto-approve-reads` or `--yes` |
| `search` | Searches file contents for a regular expression. Skips `.git`, `node_modules`, binary files and files over 1 MB. Returns at most 100 matches. | Unless `--auto-approve-reads` or `--yes` |
| `write_file` | Creates or replaces a file. Shows a diff first. | Unless `--yes` |
| `edit_file` | Replaces one string in a file. The string must occur once. Shows a diff first. | Unless `--yes` |
| `run` | Runs a shell command. Stops the command after 120 s and keeps the first 20,000 characters of output. | Unless `--yes-commands` |

### Safety

- Every path must be inside the current directory. The agent resolves every symbolic link and
  refuses a path that leads outside, and a link whose target does not exist. It checks the path
  again after you approve a write, and writes without following a link.
- Before each write or edit, the agent shows a unified diff.
- These files can hold secrets: `.env`, `.env.*`, `credentials.json`, `*.pem` and names that
  contain `key`. The agent checks both the name it is given and the file a link points to. It
  sends their contents only when you type `y`, also with `--yes`. `search` does not look in
  these files.
- `--yes` does not cover commands: a command can read any file, secrets included, and its output
  goes to the model. `--yes-commands` runs commands without asking, except a command that names a
  file that may hold secrets, which always asks. Use `--yes-commands` only in a directory you can
  restore, for example a clean git checkout.
- Commands run without the CLI's own keys in their environment (`ROUTERPLUS_API_KEY`,
  `ROUTERPLUS_MANAGEMENT_KEY`, `TM_API_KEY`, `TM_ADMIN_TOKEN`).
- Text from the model and from files is printed with control characters shown as `\u{...}`, so
  it cannot move the cursor, clear the screen or change an approval question.

### Options

| Option | Default | Meaning |
|---|---|---|
| `--model ID` | `claude-sonnet-5-5` | The model: any model id from [Models](/docs/models). |
| `--yes`, `-y` | off | Read, write and edit files without asking. |
| `--yes-commands` | off | Run commands without asking (not those that name a secrets file). |
| `--auto-approve-reads` | off | Read, list and search without asking. |
| `--max-cost USD` | none | Stop when the session cost reaches this amount. The cost is the sum of `usage.cost` in the answers. |

Each request sends these headers:

- `x-session-id`, with one id for the conversation. `/clear` starts a new id.
- `x-tm-labels: client=routerplus-code`, a label for the agent's requests.

## Options

| Option | Default | Meaning |
|---|---|---|
| `--api URL` | `https://api.routerplus.com` | The gateway. `login` stores it, and later commands use the stored value. `code` also reads `ROUTERPLUS_API_URL`. |
| `--console URL` | `https://app.routerplus.com` | The console with the consent page. |

Both URLs must use https. Plain http works only on `localhost`, `127.0.0.1` and `[::1]`.

The CLI has no dependencies beyond Node and no package to install yet: it runs from a checkout
of the repository.
