@caeliq/claude-code-router 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +82 -686
- package/dist/cli.js +1223 -1140
- package/dist/index.html +45 -53
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-

|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
[](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)
|
|
4
4
|
|
|
5
|
+
Claude Code Router is an adaptive LLM gateway for Claude Code. It routes each request to the most suitable model and provider while preserving tool calls, streaming, extended thinking, and multi-turn context across different APIs.
|
|
6
|
+
|
|
5
7
|
## ✨ Features
|
|
6
8
|
|
|
7
|
-
- **Model Routing**: Route requests
|
|
9
|
+
- **Adaptive Model Routing**: Route requests by scenario, including background tasks, thinking, long context, web search, and image workflows.
|
|
10
|
+
- **Tool Use & Thinking**: Preserve tool calls, tool results, and reasoning content across providers with different API formats.
|
|
8
11
|
- **Multi-Provider Support**: Supports various model providers like OpenRouter, DeepSeek, Ollama, Gemini, Antigravity, Volcengine, SiliconFlow, Codex, Claude subscription, Qwen, Chrome On-Device, and Cursor (SDK).
|
|
9
12
|
- **Request/Response Transformation**: Customize requests and responses for different providers using transformers.
|
|
13
|
+
- **Native Client Protocols**: Accept Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses requests through the same router and fallback pipeline.
|
|
10
14
|
- **Dynamic Model Switching**: Switch models on-the-fly within Claude Code using the `/model` command.
|
|
11
15
|
- **CLI Model Management**: Manage models and providers directly from the terminal with `ccr model`.
|
|
12
16
|
- **GitHub Actions Integration**: Trigger Claude Code tasks in your GitHub workflows.
|
|
@@ -41,20 +45,23 @@ This fork is based on [claude-code-router](https://github.com/musistudio/claude-
|
|
|
41
45
|
#### Prerequisites
|
|
42
46
|
|
|
43
47
|
Before you begin, ensure you have the following installed on your system:
|
|
44
|
-
- **Docker
|
|
45
|
-
- **Node.js** (Optional): Required to run from source, publish packages, or use the **Chrome On-Device** bridge. This fork requires **Node.js ≥ 22.
|
|
48
|
+
- **Docker** (Recommended): The primary way to run the router via the published image. See [Docker Install Guide](https://docs.docker.com/get-docker/).
|
|
49
|
+
- **Node.js** (Optional): Required to run from source, publish packages, or use the **Chrome On-Device** bridge. This fork requires **Node.js ≥ 22.19.0** (needed by `undici`). See [Node.js Download](https://nodejs.org/).
|
|
46
50
|
- **Claude Code**: See the [official quickstart guide](https://code.claude.com/docs/en/quickstart) for installation instructions.
|
|
47
51
|
|
|
48
52
|
#### Quick Start with Docker
|
|
49
53
|
|
|
50
|
-
The fastest way to launch Claude Code Router is
|
|
54
|
+
The fastest way to launch Claude Code Router is the published Docker image:
|
|
51
55
|
|
|
52
56
|
```shell
|
|
53
|
-
|
|
54
|
-
docker
|
|
57
|
+
mkdir -p ~/.claude-code-router
|
|
58
|
+
docker run -d --name ccr \
|
|
59
|
+
-p 3456:3456 \
|
|
60
|
+
-v ~/.claude-code-router:/root/.claude-code-router \
|
|
61
|
+
ghcr.io/oakimov/claude-code-router:latest
|
|
55
62
|
```
|
|
56
63
|
|
|
57
|
-
The
|
|
64
|
+
The image ships the server and UI, exposes the proxy on `http://localhost:3456`, and mounts `~/.claude-code-router` as the config directory (`/root/.claude-code-router` inside the container). Set `"HOST": "0.0.0.0"` in your `config.json` so the port mapping can reach the server. After changing the config, restart with `docker restart ccr`; view logs with `docker logs -f ccr`.
|
|
58
65
|
|
|
59
66
|
### 2. Configuration
|
|
60
67
|
|
|
@@ -68,8 +75,9 @@ The `config.json` file has several key sections:
|
|
|
68
75
|
- **Logging Systems**: The Claude Code Router uses two separate logging systems:
|
|
69
76
|
- **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `~/.claude-code-router/logs/` directory with filenames like `ccr-*.log`
|
|
70
77
|
- **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log`
|
|
71
|
-
- **`APIKEY`** (optional): You can set a secret key to authenticate requests.
|
|
78
|
+
- **`APIKEY`** (optional): You can set a secret key to authenticate requests. API clients can provide it in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. The web UI exchanges it for an opaque `HttpOnly`, same-site session cookie and never stores the key in browser storage. UI sessions are kept in memory and require login again after CCR restarts. Example: `"APIKEY": "your-secret-key"`.
|
|
72
79
|
- **`HOST`** (optional): You can set the host address for the server. If `APIKEY` is not set, the host will be forced to `127.0.0.1` for security reasons to prevent unauthorized access. Example: `"HOST": "0.0.0.0"`.
|
|
80
|
+
- **Rate limiting**: Every route has a default limit of 1000 requests per minute. The shared default is defined by `RATE_LIMIT_CONFIG` in `packages/shared/src/constants.ts`; change it there to update all route limits.
|
|
73
81
|
- **`NON_INTERACTIVE_MODE`** (optional): When set to `true`, enables compatibility with non-interactive environments like GitHub Actions, Docker containers, or other CI/CD systems. This sets appropriate environment variables (`CI=true`, `FORCE_COLOR=0`, etc.) and configures stdin handling to prevent the process from hanging in automated environments. Example: `"NON_INTERACTIVE_MODE": true`.
|
|
74
82
|
|
|
75
83
|
- **`Providers`**: Used to configure different model providers.
|
|
@@ -97,125 +105,34 @@ Claude Code Router supports environment variable interpolation for secure API ke
|
|
|
97
105
|
|
|
98
106
|
This allows you to keep sensitive API keys in environment variables instead of hardcoding them in configuration files. The interpolation works recursively through nested objects and arrays.
|
|
99
107
|
|
|
100
|
-
Here is a
|
|
108
|
+
Here is a minimal configuration:
|
|
101
109
|
|
|
102
110
|
```json
|
|
103
111
|
{
|
|
104
112
|
"APIKEY": "your-secret-key",
|
|
105
|
-
"PROXY_URL": "http://127.0.0.1:7890",
|
|
106
|
-
"LOG": true,
|
|
107
|
-
"API_TIMEOUT_MS": 600000,
|
|
108
|
-
"NON_INTERACTIVE_MODE": false,
|
|
109
113
|
"Providers": [
|
|
110
114
|
{
|
|
111
115
|
"name": "openrouter",
|
|
112
116
|
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
|
|
113
|
-
"api_key": "
|
|
114
|
-
"models": [
|
|
115
|
-
"google/gemini-2.5-pro-preview",
|
|
116
|
-
"anthropic/claude-sonnet-4",
|
|
117
|
-
"anthropic/claude-3.5-sonnet",
|
|
118
|
-
"anthropic/claude-3.7-sonnet:thinking"
|
|
119
|
-
],
|
|
117
|
+
"api_key": "$OPENROUTER_API_KEY",
|
|
118
|
+
"models": ["anthropic/claude-sonnet-4"],
|
|
120
119
|
"transformer": {
|
|
121
120
|
"use": ["openrouter"]
|
|
122
121
|
}
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"name": "deepseek",
|
|
126
|
-
"api_base_url": "https://api.deepseek.com/chat/completions",
|
|
127
|
-
"api_key": "sk-xxx",
|
|
128
|
-
"models": ["deepseek-chat", "deepseek-reasoner"],
|
|
129
|
-
"transformer": {
|
|
130
|
-
"use": ["deepseek"],
|
|
131
|
-
"deepseek-chat": {
|
|
132
|
-
"use": ["tooluse"]
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"name": "ollama",
|
|
138
|
-
"api_base_url": "http://localhost:11434/v1/chat/completions",
|
|
139
|
-
"api_key": "ollama",
|
|
140
|
-
"models": ["qwen2.5-coder:latest"]
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"name": "gemini",
|
|
144
|
-
"api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
|
|
145
|
-
"api_key": "sk-xxx",
|
|
146
|
-
"models": ["gemini-2.5-flash", "gemini-2.5-pro", "gemma-4-31b-it"],
|
|
147
|
-
"transformer": {
|
|
148
|
-
"use": ["gemini"]
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
"name": "volcengine",
|
|
153
|
-
"api_base_url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
|
|
154
|
-
"api_key": "sk-xxx",
|
|
155
|
-
"models": ["deepseek-v3-250324", "deepseek-r1-250528"],
|
|
156
|
-
"transformer": {
|
|
157
|
-
"use": ["deepseek"]
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
"name": "modelscope",
|
|
162
|
-
"api_base_url": "https://api-inference.modelscope.cn/v1/chat/completions",
|
|
163
|
-
"api_key": "",
|
|
164
|
-
"models": ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507"],
|
|
165
|
-
"transformer": {
|
|
166
|
-
"use": [
|
|
167
|
-
[
|
|
168
|
-
"maxtoken",
|
|
169
|
-
{
|
|
170
|
-
"max_tokens": 65536
|
|
171
|
-
}
|
|
172
|
-
],
|
|
173
|
-
"enhancetool"
|
|
174
|
-
],
|
|
175
|
-
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
|
|
176
|
-
"use": ["reasoning"]
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
"name": "dashscope",
|
|
182
|
-
"api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
|
|
183
|
-
"api_key": "",
|
|
184
|
-
"models": ["qwen3-coder-plus"],
|
|
185
|
-
"transformer": {
|
|
186
|
-
"use": [
|
|
187
|
-
[
|
|
188
|
-
"maxtoken",
|
|
189
|
-
{
|
|
190
|
-
"max_tokens": 65536
|
|
191
|
-
}
|
|
192
|
-
],
|
|
193
|
-
"enhancetool"
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
"name": "aihubmix",
|
|
199
|
-
"api_base_url": "https://aihubmix.com/v1/chat/completions",
|
|
200
|
-
"api_key": "sk-",
|
|
201
|
-
"models": [
|
|
202
|
-
"glm-4.5",
|
|
203
|
-
"claude-opus-4-20250514",
|
|
204
|
-
"gemini-2.5-pro"
|
|
205
|
-
]
|
|
206
122
|
}
|
|
207
123
|
],
|
|
208
124
|
"Router": {
|
|
209
|
-
"default": "
|
|
210
|
-
"background": "ollama,qwen2.5-coder:latest",
|
|
211
|
-
"think": "deepseek,deepseek-reasoner",
|
|
212
|
-
"longContext": "openrouter,google/gemini-2.5-pro-preview",
|
|
213
|
-
"longContextThreshold": 60000,
|
|
214
|
-
"webSearch": "gemini,gemini-2.5-flash"
|
|
125
|
+
"default": "openrouter,anthropic/claude-sonnet-4"
|
|
215
126
|
}
|
|
216
127
|
}
|
|
217
128
|
```
|
|
218
129
|
|
|
130
|
+
> **See also**: The complete config reference, per-provider examples, and routing
|
|
131
|
+
> options are in `docs/docs/server/config/basic.md`,
|
|
132
|
+
> `docs/docs/server/config/providers.md`,
|
|
133
|
+
> `docs/docs/server/config/transformers.md`, and
|
|
134
|
+
> `docs/docs/server/config/routing.md`.
|
|
135
|
+
|
|
219
136
|
#### Adding a New Provider
|
|
220
137
|
|
|
221
138
|
If you want to add a new provider and automatically discover its models, follow these steps:
|
|
@@ -278,6 +195,19 @@ You can also configure Claude Code to always use the router by editing its `sett
|
|
|
278
195
|
|
|
279
196
|
This approach lets you run `claude` directly without needing `ccr code`.
|
|
280
197
|
|
|
198
|
+
#### Via OpenAI-compatible clients
|
|
199
|
+
|
|
200
|
+
OpenAI SDK clients can use CCR without an Anthropic compatibility layer:
|
|
201
|
+
|
|
202
|
+
```shell
|
|
203
|
+
export OPENAI_BASE_URL=http://127.0.0.1:3456/v1
|
|
204
|
+
export OPENAI_API_KEY=your-router-api-key
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
CCR accepts Chat Completions at `/v1/chat/completions` (alias `/chat/completions`) and Responses at `/v1/responses` (alias `/responses`). Send a model as `provider,model` to select a destination explicitly, or send a bare model and configure `Router.default`. Both JSON and SSE responses are converted back to the protocol used by the client.
|
|
208
|
+
|
|
209
|
+
The compatibility layer supports ordinary text, images, function tools/results, reasoning effort, and usage reporting. Stateful Responses features such as `store: true`, `previous_response_id`, conversations, background mode, provider file IDs, and unsupported hosted tools return an explicit 400 error instead of being silently discarded.
|
|
210
|
+
|
|
281
211
|
> **Note**: After modifying the configuration file, you need to restart the service for the changes to take effect:
|
|
282
212
|
>
|
|
283
213
|
> ```shell
|
|
@@ -294,7 +224,7 @@ ccr ui
|
|
|
294
224
|
|
|
295
225
|
This will open a web-based interface where you can easily view and edit your `config.json` file.
|
|
296
226
|
|
|
297
|
-

|
|
298
228
|
|
|
299
229
|
### 5. CLI Model Management
|
|
300
230
|
|
|
@@ -305,24 +235,9 @@ ccr model
|
|
|
305
235
|
```
|
|
306
236
|

|
|
307
237
|
|
|
308
|
-
|
|
238
|
+
`ccr model` lets you view configured models, switch models per scenario, add models, and create providers with transformer configuration — all with validation and prompts.
|
|
309
239
|
|
|
310
|
-
-
|
|
311
|
-
- See all configured models (default, background, think, longContext, webSearch, image)
|
|
312
|
-
- Switch models: Quickly change which model is used for each router type
|
|
313
|
-
- Add new models: Add models to existing providers
|
|
314
|
-
- Create new providers: Set up complete provider configurations including:
|
|
315
|
-
- Provider name and API endpoint
|
|
316
|
-
- API key
|
|
317
|
-
- Available models
|
|
318
|
-
- Transformer configuration with support for:
|
|
319
|
-
- Multiple transformers (openrouter, deepseek, gemini, etc.)
|
|
320
|
-
- Transformer options (e.g., maxtoken with custom limits)
|
|
321
|
-
- Provider-specific routing (e.g., OpenRouter provider preferences)
|
|
322
|
-
|
|
323
|
-
The CLI tool validates all inputs and provides helpful prompts to guide you through the configuration process, making it easy to manage complex setups without editing JSON files manually.
|
|
324
|
-
|
|
325
|
-
For non-interactive model discovery, you can also test provider access and list remote models directly:
|
|
240
|
+
For non-interactive model discovery from any provider with a model-list endpoint:
|
|
326
241
|
|
|
327
242
|
```shell
|
|
328
243
|
ccr model get claude
|
|
@@ -330,44 +245,11 @@ ccr model get gemini
|
|
|
330
245
|
ccr model get openai
|
|
331
246
|
```
|
|
332
247
|
|
|
333
|
-
|
|
334
|
-
- Calls the provider's model-list endpoint using the configured API key
|
|
335
|
-
- Prints the remote models returned by the provider
|
|
336
|
-
- Prompts to append missing models and remove configured models that the API no longer returns
|
|
248
|
+
`ccr model get <provider>` fetches remote models, then prompts to append missing ones and remove configured ones the API no longer returns. Built-in endpoint support exists for `anthropic`/`claude`, `gemini`, `openai`, `codex`, and `cursor`. Other providers can use `models_api_url` plus a `models_response_format` (`listPath`, `idPath`, `stripPrefix`) to parse custom JSON responses.
|
|
337
249
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
The `models_response_format` object supports:
|
|
343
|
-
- `listPath`: JSON path to the array of models (e.g., `"data"`, `"models"`, or `""` for root array)
|
|
344
|
-
- `idPath`: Field name within each model object to use as ID (e.g., `"id"`, `"name"`, `"slug"`)
|
|
345
|
-
- `stripPrefix`: Optional prefix to remove from model IDs (e.g., `"models/"`)
|
|
346
|
-
|
|
347
|
-
Example:
|
|
348
|
-
|
|
349
|
-
```json
|
|
350
|
-
{
|
|
351
|
-
"name": "together.ai",
|
|
352
|
-
"api_base_url": "https://api.together.ai/v1/chat/completions",
|
|
353
|
-
"models_api_url": "https://api.together.ai/v1/models",
|
|
354
|
-
"api_key": "$TOGETHERAI_API_KEY",
|
|
355
|
-
"models": [],
|
|
356
|
-
"models_response_format": {
|
|
357
|
-
"listPath": "",
|
|
358
|
-
"idPath": "id"
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
You can also override these settings via CLI flags for testing:
|
|
364
|
-
```shell
|
|
365
|
-
ccr model get my-provider --list-path data --id-path id --strip-prefix "v1/"
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
If the provider returns model changes, `ccr model get <provider>` can append missing entries and remove unavailable configured entries, each behind a separate confirmation prompt.
|
|
369
|
-
|
|
370
|
-
> **Note**: After syncing models into `config.json`, restart the service with `ccr restart` so the updated provider list is picked up by the running server.
|
|
250
|
+
> **See also**: `docs/docs/server/guides/model-discovery.md` and `docs/docs/cli/commands/model-get.md`.
|
|
251
|
+
>
|
|
252
|
+
> **Note**: After syncing models into `config.json`, restart the service with `ccr restart`.
|
|
371
253
|
|
|
372
254
|
#### Antigravity Authentication
|
|
373
255
|
|
|
@@ -403,9 +285,7 @@ Example provider:
|
|
|
403
285
|
}
|
|
404
286
|
```
|
|
405
287
|
|
|
406
|
-
|
|
407
|
-
- **`cachedContent: false`** — Antigravity has no Google `cachedContents` resource. The Gemini default is `true`; leaving it on causes 404s.
|
|
408
|
-
- **`thoughtSignatureFallback: "skip"`** — explicit form of the default. When a tool call is replayed without a cached `thoughtSignature`, CCR stamps Google's `skip_thought_signature_validator` sentinel so the gateway does not 400. Only change this to `"none"` if your endpoint rejects that sentinel.
|
|
288
|
+
`cachedContent: false` is required — Antigravity has no Google `cachedContents` resource, and leaving the Gemini default (`true`) causes 404s. The `thoughtSignatureFallback` option is covered in `docs/docs/server/config/transformers.md`.
|
|
409
289
|
|
|
410
290
|
> **Note**: Keep the CCR server running during auth. Using Antigravity IDE OAuth client credentials from a non-IDE client may violate Google's terms.
|
|
411
291
|
|
|
@@ -413,248 +293,74 @@ Why those Gemini options:
|
|
|
413
293
|
|
|
414
294
|
The Codex provider supports two authentication modes:
|
|
415
295
|
|
|
416
|
-
- **OAuth** via `ccr codex-auth`
|
|
417
|
-
- **PAT** via `api_key: "at-..."`
|
|
418
|
-
|
|
419
|
-
##### OAuth mode
|
|
420
|
-
|
|
421
|
-
Before using Codex models with OAuth, authenticate with your OpenAI account:
|
|
422
|
-
|
|
423
|
-
```shell
|
|
424
|
-
ccr codex-auth
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
This command:
|
|
428
|
-
1. Opens your browser to the OpenAI OAuth authorization page
|
|
429
|
-
2. After you sign in, the OAuth callback is handled by the running CCR server
|
|
430
|
-
3. Tokens are stored in `~/.claude-code-router/codex_auth.json`
|
|
431
|
-
4. The CLI and server independently refresh tokens five minutes before expiry
|
|
432
|
-
|
|
433
|
-
CCR derives the selected ChatGPT workspace and FedRAMP routing state from the
|
|
434
|
-
OAuth ID token. Runtime requests and `ccr model get codex` both send the same
|
|
435
|
-
Codex bearer, account, and routing headers. Refreshes use an atomic credential
|
|
436
|
-
file and a cross-process lock so a separately running CLI and server cannot
|
|
437
|
-
reuse the same rotating refresh token. A runtime OAuth 401 performs one guarded
|
|
438
|
-
credential reload/refresh retry.
|
|
439
|
-
|
|
440
|
-
> **Note**: The server must be running for `ccr codex-auth` to work, as it hosts the OAuth callback endpoint.
|
|
441
|
-
|
|
442
|
-
**Running with Docker**:
|
|
296
|
+
- **OAuth** via `ccr codex-auth` — browser flow handled by the CCR server callback on port `1455` (Docker maps `1455:3456`); tokens stored in `~/.claude-code-router/codex_auth.json` and auto-refreshed.
|
|
297
|
+
- **PAT** via a literal `api_key: "at-..."` (or an env var containing an `at-` token) — skips `ccr codex-auth`.
|
|
443
298
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
```shell
|
|
447
|
-
docker exec -it claude-code-router ccr codex-auth
|
|
448
|
-
```
|
|
299
|
+
An `at-` value is always treated as a PAT and never silently falls back to OAuth; any other placeholder selects OAuth tokens.
|
|
449
300
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
##### PAT mode
|
|
453
|
-
|
|
454
|
-
If your provider `api_key` starts with `at-`, CCR treats it as a Codex Personal Access Token and uses it directly. In PAT mode, you do **not** run `ccr codex-auth`.
|
|
455
|
-
|
|
456
|
-
```json
|
|
457
|
-
{
|
|
458
|
-
"name": "codex",
|
|
459
|
-
"api_base_url": "https://chatgpt.com/backend-api/codex",
|
|
460
|
-
"api_key": "at-your-personal-access-token",
|
|
461
|
-
"models": ["gpt-5.4"],
|
|
462
|
-
"transformer": {
|
|
463
|
-
"use": ["codex"]
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
CCR resolves the PAT's account, user, plan, and FedRAMP metadata through
|
|
469
|
-
OpenAI's `/whoami` endpoint before calling the Codex backend. Both runtime
|
|
470
|
-
requests and `ccr model get codex` then send `Authorization`,
|
|
471
|
-
`ChatGPT-Account-ID`, and `X-OpenAI-Fedramp` when required. Metadata requests
|
|
472
|
-
are deduplicated and cached briefly by the server.
|
|
473
|
-
|
|
474
|
-
Auth mode is explicit: an `at-` value is always treated as a PAT. An invalid or
|
|
475
|
-
revoked PAT fails as PAT authentication and is never silently replaced with
|
|
476
|
-
OAuth. Any non-PAT placeholder selects OAuth tokens from
|
|
477
|
-
`~/.claude-code-router/codex_auth.json`.
|
|
478
|
-
|
|
479
|
-
> **See also**: Full Codex setup and troubleshooting are documented in `docs/docs/server/guides/codex.md`.
|
|
301
|
+
> **See also**: Full Codex setup, both auth modes, provider config, and troubleshooting are in `docs/docs/server/guides/codex.md`.
|
|
480
302
|
|
|
481
303
|
#### Cursor Provider Authentication
|
|
482
304
|
|
|
483
|
-
The Cursor provider
|
|
484
|
-
|
|
485
|
-
1. Provider `api_key` starting with `crsr_` (Cursor dashboard API key)
|
|
486
|
-
2. Otherwise `CURSOR_API_KEY` from the environment
|
|
487
|
-
|
|
488
|
-
Example provider:
|
|
489
|
-
|
|
490
|
-
```json
|
|
491
|
-
{
|
|
492
|
-
"name": "cursor",
|
|
493
|
-
"api_base_url": "https://cursor.com",
|
|
494
|
-
"api_key": "$CURSOR_API_KEY",
|
|
495
|
-
"models": ["composer-2"],
|
|
496
|
-
"transformer": {
|
|
497
|
-
"use": [
|
|
498
|
-
[
|
|
499
|
-
"cursor-sdk",
|
|
500
|
-
{
|
|
501
|
-
"cursorMode": "bridge"
|
|
502
|
-
}
|
|
503
|
-
]
|
|
504
|
-
]
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
- **bridge** (default): Claude Code hosts tools; Cursor built-ins are denied in an isolated workspace under `~/.claude-code-router/cursor-sdk-workspaces/`
|
|
510
|
-
- Cursor builds its harness prompt server-side from the SDK workspace root, so bridge mode grounds the model in the host environment instead: the project root and platform from the incoming `<env>` block are injected through the workspace `AGENTS.md`, the head and tail of the agent prompt, and the built-in denial message. Host tool calls whose arguments reference the isolated workspace are intercepted, answered with a correction rather than forwarded to Claude Code, and counted in the session metrics. Isolated workspaces are removed with their session and swept when orphaned.
|
|
511
|
-
- Discover models with `ccr model get cursor` (lists via `Cursor.models.list`, not REST `/models`)
|
|
512
|
-
- Docker Compose passes `CURSOR_API_KEY` into the container when set; local Cursor sandboxing is forced off in Docker
|
|
513
|
-
- Cursor prompt caching is native to the held-open SDK agent session. CCR reports per-request token estimates to Claude Code and maps SDK cache-read deltas back as bounded Anthropic cache-read usage.
|
|
514
|
-
- Cursor thinking is forwarded from both SDK stream `thinking` messages and token-level `Agent.send({ onDelta })` `thinking-delta` updates, then closed with a synthetic signature so Claude Code can render it as Anthropic extended thinking. Claude Code 2.1.89+ hides interactive thinking summaries by default; enable `"showThinkingSummaries": true` in the settings file passed to Claude Code to display them. This is a client rendering setting: CCR still transports the thinking block when it is disabled.
|
|
515
|
-
- Cursor turns are coordinated per Claude conversation. Identical overlapping retries share one bounded response producer (and recent completed result), so only one consumer reads the Cursor iterator and only one `agent.send` is submitted. Stopping the last subscriber awaits bounded run/iterator retirement before re-entry. Pure, exactly matched tool results resume a live parked run; a rejected result plus meaningful replacement text, an unmatched/dead run, or divergent transcript retires the old agent and replays the full conversation. Idle agents receive slim follow-ups only when the incoming transcript is exactly the committed host-visible transcript plus one supported user message; larger suffixes are fully replayed. CCR does not use `local.force` to bypass Cursor's active-run guard.
|
|
305
|
+
The Cursor provider runs models in-process via the official `@cursor/sdk` (no browser OAuth CLI). Auth resolves from the provider `api_key` starting with `crsr_`, then `CURSOR_API_KEY`. Default **bridge** mode keeps Claude Code as the tool host; Cursor built-ins are denied in an isolated workspace. Discover models with `ccr model get cursor`.
|
|
516
306
|
|
|
517
|
-
> **See also**: Full Cursor setup
|
|
307
|
+
> **See also**: Full Cursor setup, bridge/plan/agent modes, and configuration are in `docs/docs/server/guides/cursor.md`.
|
|
518
308
|
|
|
519
309
|
#### Claude Subscription Authentication
|
|
520
310
|
|
|
521
|
-
|
|
311
|
+
Route Claude Code through your Claude Pro or Max subscription via OAuth:
|
|
522
312
|
|
|
523
313
|
```shell
|
|
524
314
|
ccr claude-auth
|
|
525
315
|
```
|
|
526
316
|
|
|
527
|
-
|
|
528
|
-
1. Opens your browser to the Claude OAuth authorization page
|
|
529
|
-
2. After you sign in, the OAuth callback is handled by the running CCR server on port `1455`
|
|
530
|
-
3. Tokens are stored in `~/.claude-code-router/claude_auth.json`
|
|
531
|
-
4. The `claude-auth` transformer automatically refreshes tokens when they expire
|
|
532
|
-
|
|
533
|
-
> **Note**: The server must be running for `ccr claude-auth` to work, as it hosts the OAuth callback endpoint on port 1455.
|
|
534
|
-
|
|
535
|
-
**Running with Docker**:
|
|
536
|
-
|
|
537
|
-
The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
|
|
538
|
-
|
|
539
|
-
```shell
|
|
540
|
-
docker exec -it claude-code-router ccr claude-auth
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
The CLI prints a URL to open in your host browser. After signing in, the browser redirects to `http://localhost:1455/callback`, which Docker forwards to the container. Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
|
|
544
|
-
|
|
545
|
-
A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain:
|
|
546
|
-
|
|
547
|
-
```json
|
|
548
|
-
{
|
|
549
|
-
"name": "claude-subscription",
|
|
550
|
-
"api_base_url": "https://api.anthropic.com",
|
|
551
|
-
"api_key": "no-key",
|
|
552
|
-
"models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
|
|
553
|
-
"transformer": {
|
|
554
|
-
"use": ["claude-auth", "Anthropic"]
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
> **See also**: Full Claude subscription setup is documented in `docs/docs/server/guides/claude-auth.md`.
|
|
317
|
+
The OAuth flow is handled by the CCR server callback on port `1455` (Docker maps `1455:3456`); tokens are stored in `~/.claude-code-router/claude_auth.json` and auto-refreshed. A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain.
|
|
560
318
|
|
|
561
|
-
**
|
|
319
|
+
> **See also**: Full setup, the transformer chain, client classification, and billing/identity details are in `docs/docs/server/guides/claude-auth.md`.
|
|
562
320
|
|
|
563
321
|
#### Qwen Provider Authentication
|
|
564
322
|
|
|
565
|
-
|
|
323
|
+
Authenticate with Qwen Chat by saving a JWT from `chat.qwen.ai` localStorage:
|
|
566
324
|
|
|
567
325
|
```shell
|
|
568
326
|
ccr qwen-auth
|
|
569
327
|
```
|
|
570
328
|
|
|
571
|
-
|
|
572
|
-
1. Prints a URL (`http://127.0.0.1:<port>/qwen/auth`) for the in-browser auth page
|
|
573
|
-
2. The page offers two options:
|
|
574
|
-
- **Bookmarklet (recommended)**: drag the "Get Qwen Token" link to your bookmarks bar, then click it on the signed-in Qwen page. The token is sent back to CCR automatically.
|
|
575
|
-
- **Manual paste**: sign in at `chat.qwen.ai`, open dev tools (F12) → Console, run `copy(localStorage.getItem('token'))`, paste the JWT into the form and submit.
|
|
576
|
-
3. The token is validated against `qwen.aikit.club/v1/validate` and saved to `~/.claude-code-router/qwen_auth.json` (mode 0600)
|
|
577
|
-
4. The `qwen-auth` transformer automatically refreshes the token when it nears expiry (within 6 hours)
|
|
578
|
-
|
|
579
|
-
> **Note**: The server must be running for `ccr qwen-auth` to work, as it hosts the auth form at `/qwen/auth`. Unlike the Codex flow, no OAuth callback is required — the token is pasted directly into the form.
|
|
580
|
-
|
|
581
|
-
**Running with Docker**:
|
|
582
|
-
|
|
583
|
-
The Qwen auth page is served on the regular CCR port (no separate callback port). When running in Docker:
|
|
584
|
-
|
|
585
|
-
```shell
|
|
586
|
-
docker exec -it claude-code-router ccr qwen-auth
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
The CLI prints a URL to open in your host browser (`http://localhost:3456/qwen/auth`, which Docker forwards to the container). Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
|
|
329
|
+
The CCR server hosts an auth page at `/qwen/auth` offering a bookmarklet or manual paste. The token is validated, saved to `~/.claude-code-router/qwen_auth.json`, and auto-refreshed. The Qwen provider requires the `qwen-auth` + `reasoning` + `OpenAI` transformer chain.
|
|
590
330
|
|
|
591
|
-
**
|
|
331
|
+
> **See also**: Full Qwen setup and provider config are in `docs/docs/server/guides/qwen.md`.
|
|
592
332
|
|
|
593
333
|
#### Chrome On-Device Bridge
|
|
594
334
|
|
|
595
|
-
|
|
335
|
+
Use Chrome's built-in Gemini Nano (~4GB local model) with zero API cost via a host-side bridge:
|
|
596
336
|
|
|
597
337
|
```bash
|
|
598
|
-
|
|
599
|
-
ccr chrome-bridge
|
|
600
|
-
|
|
601
|
-
# Custom ports
|
|
338
|
+
ccr chrome-bridge # default: port 3457, CDP 9222
|
|
602
339
|
ccr chrome-bridge --port 3457 --cdp 9222
|
|
603
340
|
```
|
|
604
341
|
|
|
605
|
-
The bridge:
|
|
606
|
-
1. Checks if Chrome is running with remote debugging enabled (port 9222)
|
|
607
|
-
2. If not, launches Chrome with the required flags (`--remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug-profile`)
|
|
608
|
-
3. Connects to Chrome via Puppeteer/CDP with a 5-minute protocol timeout to handle slow model inference
|
|
609
|
-
4. Loads a page that accesses the Prompt API (`window.LanguageModel`) and maintains a persistent `LanguageModel` session across all requests — conversation history is carried forward naturally within the session, not rebuilt per request
|
|
610
|
-
5. Replaces Claude Code's system prompt with a minimal tool-focused one (5 core tools), using `responseConstraint` (JSON Schema) to force the model to emit structured JSON with `{text, tool_calls[]}` fields
|
|
611
|
-
6. Exposes an OpenAI-compatible HTTP API on `0.0.0.0:3457`:
|
|
612
|
-
- `GET /v1/models` — returns available models with live context usage
|
|
613
|
-
- `GET /v1/models/{model_name}` — returns individual model info (display_name, max_input_tokens, capabilities)
|
|
614
|
-
- `POST /v1/chat/completions` — chat completions with streaming and non-streaming support
|
|
615
|
-
- `GET /health` — health check
|
|
342
|
+
The bridge connects to Chrome's Prompt API over CDP, maintains persistent model sessions, and exposes an OpenAI-compatible API (`/v1/chat/completions`) on `127.0.0.1:3457`. It replaces Claude Code's system prompt with a minimal tool-focused one and uses `responseConstraint` (JSON Schema) to force structured `{text, tool_calls[]}` output.
|
|
616
343
|
|
|
617
|
-
**Prerequisites**:
|
|
344
|
+
**Prerequisites**: enable `chrome://flags/#optimization-guide-on-device-model` and `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input`, restart Chrome, and let the ~4GB model download.
|
|
618
345
|
|
|
619
|
-
> **Note for Docker**: The bridge runs on the Docker **host**, not inside the container
|
|
346
|
+
> **Note for Docker**: The bridge runs on the Docker **host**, not inside the container — set the provider host to `http://host.docker.internal:3457`. Full setup, provider config, features, and limitations are in `docs/docs/server/guides/chrome-on-device.md`.
|
|
620
347
|
|
|
621
348
|
### 6. Presets Management
|
|
622
349
|
|
|
623
|
-
|
|
350
|
+
Save, share, and reuse configurations:
|
|
624
351
|
|
|
625
352
|
```shell
|
|
626
|
-
#
|
|
627
|
-
ccr preset export my-preset
|
|
628
|
-
|
|
629
|
-
# Export with metadata
|
|
353
|
+
ccr preset export my-preset # export current config as a preset
|
|
630
354
|
ccr preset export my-preset --description "My OpenAI config" --author "Your Name" --tags "openai,production"
|
|
631
|
-
|
|
632
|
-
# Install a preset from local directory
|
|
633
|
-
ccr preset install /path/to/preset
|
|
634
|
-
|
|
635
|
-
# List all installed presets
|
|
355
|
+
ccr preset install /path/to/preset # install from a directory
|
|
636
356
|
ccr preset list
|
|
637
|
-
|
|
638
|
-
# Show preset information
|
|
639
357
|
ccr preset info my-preset
|
|
640
|
-
|
|
641
|
-
# Delete a preset
|
|
642
358
|
ccr preset delete my-preset
|
|
643
359
|
```
|
|
644
360
|
|
|
645
|
-
|
|
646
|
-
- **Export**: Save your current configuration as a preset directory (with manifest.json)
|
|
647
|
-
- **Install**: Install presets from local directories
|
|
648
|
-
- **Sensitive Data Handling**: API keys and other sensitive data are automatically sanitized during export (marked as `{{field}}` placeholders)
|
|
649
|
-
- **Dynamic Configuration**: Presets can include input schemas for collecting required information during installation
|
|
650
|
-
- **Version Control**: Each preset includes version metadata for tracking updates
|
|
361
|
+
Presets store your configuration (plus metadata) as a directory with `manifest.json`. Sensitive fields are sanitized to `{{field}}` placeholders on export, and presets can include input schemas to collect required values (e.g. API keys) at install time.
|
|
651
362
|
|
|
652
|
-
**
|
|
653
|
-
```
|
|
654
|
-
~/.claude-code-router/presets/
|
|
655
|
-
├── my-preset/
|
|
656
|
-
│ └── manifest.json # Contains configuration and metadata
|
|
657
|
-
```
|
|
363
|
+
> **See also**: `docs/docs/cli/commands/preset.md`.
|
|
658
364
|
|
|
659
365
|
### 7. Activate Command (Environment Variables Setup)
|
|
660
366
|
|
|
@@ -682,299 +388,24 @@ The `activate` command sets the following environment variables:
|
|
|
682
388
|
|
|
683
389
|
> **Note**: Make sure the Claude Code Router service is running (`ccr start`) before using the activated environment variables. The environment variables are only valid for the current shell session. To make them persistent, you can add `eval "$(ccr activate)"` to your shell configuration file (e.g., `~/.zshrc` or `~/.bashrc`).
|
|
684
390
|
|
|
685
|
-
#### Providers
|
|
686
|
-
|
|
687
|
-
The `Providers` array is where you define the different model providers you want to use. Each provider object requires:
|
|
391
|
+
#### Providers and Transformers
|
|
688
392
|
|
|
689
|
-
|
|
690
|
-
- `api_base_url`: The full API endpoint for chat completions.
|
|
691
|
-
- `api_key`: Your API key for the provider.
|
|
692
|
-
- `models`: A list of model names available from this provider.
|
|
693
|
-
- `transformer` (optional): Specifies transformers to process requests and responses.
|
|
393
|
+
The `Providers` array defines each provider: `name`, `api_base_url`, `api_key`, `models`, and an optional `transformer` object. The `transformer.use` list applies transformers globally (all models) or per model key, and some transformers accept options via a nested `[name, options]` array.
|
|
694
394
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
Transformers allow you to modify the request and response payloads to ensure compatibility with different provider APIs.
|
|
698
|
-
|
|
699
|
-
- **Global Transformer**: Apply a transformer to all models from a provider. In this example, the `openrouter` transformer is applied to all models under the `openrouter` provider.
|
|
700
|
-
```json
|
|
701
|
-
{
|
|
702
|
-
"name": "openrouter",
|
|
703
|
-
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
|
|
704
|
-
"api_key": "sk-xxx",
|
|
705
|
-
"models": [
|
|
706
|
-
"google/gemini-2.5-pro-preview",
|
|
707
|
-
"anthropic/claude-sonnet-4",
|
|
708
|
-
"anthropic/claude-3.5-sonnet"
|
|
709
|
-
],
|
|
710
|
-
"transformer": { "use": ["openrouter"] }
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
- **Model-Specific Transformer**: Apply a transformer to a specific model. In this example, the `deepseek` transformer is applied to all models, and an additional `tooluse` transformer is applied only to the `deepseek-chat` model.
|
|
714
|
-
|
|
715
|
-
```json
|
|
716
|
-
{
|
|
717
|
-
"name": "deepseek",
|
|
718
|
-
"api_base_url": "https://api.deepseek.com/chat/completions",
|
|
719
|
-
"api_key": "sk-xxx",
|
|
720
|
-
"models": ["deepseek-chat", "deepseek-reasoner"],
|
|
721
|
-
"transformer": {
|
|
722
|
-
"use": ["deepseek"],
|
|
723
|
-
"deepseek-chat": { "use": ["tooluse"] }
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
- **Passing Options to a Transformer**: Some transformers, like `maxtoken`, accept options. To pass options, use a nested array where the first element is the transformer name and the second is an options object.
|
|
729
|
-
```json
|
|
730
|
-
{
|
|
731
|
-
"name": "siliconflow",
|
|
732
|
-
"api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
|
|
733
|
-
"api_key": "sk-xxx",
|
|
734
|
-
"models": ["moonshotai/Kimi-K2-Instruct"],
|
|
735
|
-
"transformer": {
|
|
736
|
-
"use": [
|
|
737
|
-
[
|
|
738
|
-
"maxtoken",
|
|
739
|
-
{
|
|
740
|
-
"max_tokens": 16384
|
|
741
|
-
}
|
|
742
|
-
]
|
|
743
|
-
]
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
```
|
|
395
|
+
> **See also**: Provider schema and per-provider examples are in `docs/docs/server/config/providers.md`; the transformer reference and option passing are in `docs/docs/server/config/transformers.md`.
|
|
747
396
|
|
|
748
397
|
**Available Built-in Transformers:**
|
|
749
398
|
|
|
750
|
-
- `Anthropic
|
|
751
|
-
- `deepseek
|
|
752
|
-
- `
|
|
753
|
-
|
|
754
|
-
- **`thoughtSignatureFallback`** (`"skip"` \| `"none"`, default `"skip"`): what to do when a replayed tool call has no cached Gemini `thoughtSignature` (Claude Code's Anthropic `tool_use` cannot carry that field, so CCR caches signatures by tool-call id and restores them; a miss otherwise 400s). `"skip"` means stamp Google's documented sentinel `skip_thought_signature_validator` on the **first** `functionCall` of the step — the value name refers to that sentinel, **not** “disable the fallback.” Leave `"skip"` for Gemini/Antigravity; set `"none"` only if the endpoint rejects the sentinel (some Vertex). Real cached signatures are always preferred; the sentinel is a last resort.
|
|
755
|
-
- `mistral`: Adapts requests/responses for Mistral API.
|
|
756
|
-
- `openrouter`: Adapts requests/responses for OpenRouter API. It can also accept a `provider` routing parameter to specify which underlying providers OpenRouter should use. For more details, refer to the [OpenRouter documentation](https://openrouter.ai/docs/features/provider-routing). See an example below:
|
|
757
|
-
```json
|
|
758
|
-
"transformer": {
|
|
759
|
-
"use": ["openrouter"],
|
|
760
|
-
"moonshotai/kimi-k2": {
|
|
761
|
-
"use": [
|
|
762
|
-
[
|
|
763
|
-
"openrouter",
|
|
764
|
-
{
|
|
765
|
-
"provider": {
|
|
766
|
-
"only": ["moonshotai/fp8"]
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
]
|
|
770
|
-
]
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
```
|
|
774
|
-
- `groq`: Adapts requests/responses for groq API.
|
|
775
|
-
- `maxtoken`: Sets a specific `max_tokens` value.
|
|
776
|
-
- `tooluse`: Optimizes tool usage for certain models via `tool_choice`.
|
|
777
|
-
- `gemini-cli` (experimental): Unofficial support for Gemini via Gemini CLI [gemini-cli.js](https://gist.github.com/musistudio/1c13a65f35916a7ab690649d3df8d1cd).
|
|
778
|
-
- `reasoning`: Used to process the `reasoning_content` field.
|
|
779
|
-
- `sampling`: Used to process sampling information fields such as `temperature`, `top_p`, `top_k`, and `repetition_penalty`.
|
|
780
|
-
- `enhancetool`: Adds a layer of error tolerance to the tool call parameters returned by the LLM (this will cause the tool call information to no longer be streamed).
|
|
781
|
-
- `cleancache`: Clears the `cache_control` field from requests.
|
|
782
|
-
- `vertex-gemini`: Handles the Gemini API using Vertex authentication.
|
|
783
|
-
- `chutes-glm` Unofficial support for GLM 4.5 model via Chutes [chutes-glm-transformer.js](https://gist.github.com/vitobotta/2be3f33722e05e8d4f9d2b0138b8c863).
|
|
784
|
-
- `qwen-cli` (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b).
|
|
785
|
-
- `rovo-cli` (experimental): Unofficial support for gpt-5 via Atlassian Rovo Dev CLI [rovo-cli.js](https://gist.github.com/SaseQ/c2a20a38b11276537ec5332d1f7a5e53).
|
|
786
|
-
- `codex`: Adapts requests/responses for the Codex (ChatGPT) backend API. Supports OAuth via `ccr codex-auth` or PAT auth when `api_key` starts with `at-`.
|
|
787
|
-
- `claude-auth`: Authenticates requests to Anthropic's API using your Claude Pro or Max subscription OAuth token. Converts Unified format to Anthropic format and handles SSE response conversion. Use it together with `Anthropic` in the provider chain, and authenticate via `ccr claude-auth`.
|
|
788
|
-
- `antigravity-auth`: OAuth + envelope middleware for Google's Antigravity gateway (`cloudcode-pa`). Chain **after** `gemini`. For Antigravity you must set `cachedContent: false` on the Gemini stage (no `cachedContents` resource there); keep `thoughtSignatureFallback: "skip"` unless the endpoint rejects Google's thought-signature sentinel. Authenticate with `ccr antigravity-auth`.
|
|
789
|
-
- `chrome-on-device`: Routes requests to Chrome's on-device Gemini Nano model via the Prompt API. Uses `responseConstraint` for structured JSON output. Requires a bridge process running on the host (`ccr chrome-bridge`).
|
|
790
|
-
|
|
791
|
-
**Chrome On-Device Provider Configuration:**
|
|
792
|
-
|
|
793
|
-
The `chrome-on-device` transformer routes requests to Chrome's built-in Gemini Nano model. This is a ~4GB on-device model that runs locally with no API costs. The model is accessed through Chrome's Prompt API (`window.LanguageModel`) via a bridge process.
|
|
794
|
-
|
|
795
|
-
**Prerequisites:**
|
|
796
|
-
|
|
797
|
-
1. Google Chrome installed on your system (macOS, Windows, or Linux)
|
|
798
|
-
2. Enable Chrome flags (one-time):
|
|
799
|
-
- `chrome://flags/#optimization-guide-on-device-model` → **Enabled**
|
|
800
|
-
- `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input` → **Enabled**
|
|
801
|
-
3. Restart Chrome after enabling flags and wait for the model to download (~4GB)
|
|
802
|
-
4. Start the bridge process on the host: `ccr chrome-bridge`
|
|
803
|
-
|
|
804
|
-
**Provider Configuration:**
|
|
805
|
-
|
|
806
|
-
```json
|
|
807
|
-
{
|
|
808
|
-
"name": "chrome-nano",
|
|
809
|
-
"api_base_url": "http://127.0.0.1:3457",
|
|
810
|
-
"api_key": "placeholder",
|
|
811
|
-
"models": ["gemini-nano"],
|
|
812
|
-
"transformer": {
|
|
813
|
-
"use": ["chrome-on-device", "tooluse"]
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
```
|
|
817
|
-
|
|
818
|
-
> **Note**: The `tooluse` transformer is required alongside `chrome-on-device` to enable the a la carte tool-calling system (including the `ExitTool` for plain text responses) and to inject the necessary system reminders that help the model transition between thinking and acting.
|
|
819
|
-
|
|
820
|
-
**Starting the Bridge:**
|
|
821
|
-
|
|
822
|
-
The bridge is a standalone HTTP server that runs on the host and bridges HTTP requests to Chrome's Prompt API via CDP (Chrome DevTools Protocol):
|
|
823
|
-
|
|
824
|
-
```bash
|
|
825
|
-
# Start the bridge (default: port 3457, CDP port 9222)
|
|
826
|
-
ccr chrome-bridge
|
|
827
|
-
|
|
828
|
-
# Custom ports
|
|
829
|
-
ccr chrome-bridge --port 3457 --cdp 9222
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
The bridge automatically launches Chrome with the required flags if it's not already running (`--remote-debugging-port=9222 --user-data-dir=<temp_dir>`).
|
|
833
|
-
|
|
834
|
-
> **Note for Docker users**: The bridge must run on the Docker **host** (not inside the container), since it needs direct access to Chrome via CDP. When CCR runs in Docker, set the provider host to `http://host.docker.internal:3457`.
|
|
835
|
-
|
|
836
|
-
**How It Works:**
|
|
837
|
-
|
|
838
|
-
1. The transformer replaces Claude Code's system prompt with a minimal tool-focused one listing 5 core tools (Bash, Read, Write, Edit, ExitTool)
|
|
839
|
-
2. The bridge maintains persistent `LanguageModel` sessions — one per client fingerprint (`User-Agent + IP` hash). Conversation history is carried forward naturally within each session, not rebuilt per turn. It calls `session.promptStreaming()` with a `responseConstraint` (JSON Schema) that forces structured output: `{"tool_calls": [{"name": "...", "arguments": {...}}]}`. Text responses are handled by the model calling the `ExitTool`.
|
|
840
|
-
3. The bridge transforms Claude Code's internal context blocks in user messages to conserve the limited context budget: `<system-reminder>` blocks containing tool calls or results are converted into structured `<tool_result>` tags, while other `<system-reminder>` blocks and `<command-*>` / `<local-command-*>` blocks for unsupported tools are stripped
|
|
841
|
-
4. The bridge parses the structured JSON response into OpenAI-format SSE chunks (`chat.completion.chunk`) or a single non-streaming response (`chat.completion`)
|
|
842
|
-
5. Tool calls are detected from the parsed JSON and converted to `tool_calls` in the response; `finish_reason` is set to `"tool_calls"` or `"stop"` accordingly
|
|
843
|
-
6. Multi-turn tool use is supported — consecutive requests are processed within the same persistent session
|
|
844
|
-
7. **Multi-session support**: Requests are fingerprinted by `User-Agent + IP` hash into separate sessions, allowing multiple concurrent Claude Code instances without context contamination. A built-in web dashboard (served on the bridge port) shows real-time stats for all sessions, including turn count, idle time, and context usage
|
|
845
|
-
8. **Idle session eviction**: Sessions idle for more than 5 minutes are automatically destroyed to free resources. The `cli` session (dashboard default) is never evicted. Sessions can also be manually evicted via the dashboard's Evict button
|
|
846
|
-
9. Auto-compaction triggers at 85% context usage, resetting the session while preserving the system prompt
|
|
847
|
-
|
|
848
|
-
**Limitations:**
|
|
849
|
-
|
|
850
|
-
- **Tool calling**: Uses `responseConstraint` (JSON Schema) for structured output rather than native function calling — this works reliably but depends on the model following the schema
|
|
851
|
-
- **Multi-turn consistency**: The small on-device model may occasionally loop on the same tool call or respond with text instead of calling a needed tool. A retry mechanism with corrected prompts mitigates this
|
|
852
|
-
- **No thinking/reasoning blocks**: The Prompt API doesn't separate thinking from visible output
|
|
853
|
-
- **Context window**: Limited to 9216 tokens; auto-compaction engages at 85% usage. Old interactions are evicted on context overflow
|
|
854
|
-
- **Output limit**: The model may stall on whitespace-heavy content (e.g., Python indentation). The bridge uses write-then-edit incremental file creation (3 lines per Write call) and whitespace stall detection with abort
|
|
855
|
-
- **Cross-platform support**: Compatible with macOS, Windows, and Linux (requires Chrome installation and manual flag enablement)
|
|
856
|
-
|
|
857
|
-
**Codex Provider Configuration:**
|
|
858
|
-
|
|
859
|
-
The Codex transformer connects to the ChatGPT backend API, providing access to GPT-5.x models. It supports either OAuth authentication or a PAT in `api_key`.
|
|
860
|
-
|
|
861
|
-
```json
|
|
862
|
-
{
|
|
863
|
-
"name": "codex",
|
|
864
|
-
"api_base_url": "https://chatgpt.com/backend-api/codex",
|
|
865
|
-
"api_key": "oauth_dummy_key",
|
|
866
|
-
"models": ["gpt-5.4"],
|
|
867
|
-
"transformer": {
|
|
868
|
-
"use": ["codex"]
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
```
|
|
872
|
-
|
|
873
|
-
> **OAuth mode**: Keep `api_key` as a placeholder and run `ccr codex-auth`. OAuth tokens are stored in `~/.claude-code-router/codex_auth.json`.
|
|
874
|
-
|
|
875
|
-
```json
|
|
876
|
-
{
|
|
877
|
-
"name": "codex",
|
|
878
|
-
"api_base_url": "https://chatgpt.com/backend-api/codex",
|
|
879
|
-
"api_key": "at-your-personal-access-token",
|
|
880
|
-
"models": ["gpt-5.4"],
|
|
881
|
-
"transformer": {
|
|
882
|
-
"use": ["codex"]
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
> **PAT mode**: If `api_key` starts with `at-`, CCR uses it directly and skips `ccr codex-auth`.
|
|
888
|
-
|
|
889
|
-
> **Note**: If `api_key` is not a PAT, CCR falls back to OAuth tokens from `~/.claude-code-router/codex_auth.json`.
|
|
890
|
-
|
|
891
|
-
**Qwen Provider Configuration:**
|
|
399
|
+
- `Anthropic` — passes through to an Anthropic endpoint unchanged. `OpenAI` — registers the `/v1/chat/completions` route (the body is already in OpenAI shape).
|
|
400
|
+
- Provider adapters: `deepseek`, `groq`, `mistral`, `openrouter`, `gemini` / `vertex-gemini`, `codex`, `claude-auth`, `antigravity-auth`, `qwen-auth`, `cursor-sdk`, `chrome-on-device`.
|
|
401
|
+
- `maxtoken` — sets a specific `max_tokens`. `tooluse` — optimizes tool usage via `tool_choice`. `reasoning` — replays provider `reasoning_content` across turns. `sampling` — maps `temperature` / `top_p` / `top_k` / `repetition_penalty`. `enhancetool` — adds error tolerance to tool-call parameters (disables streaming of tool calls). `cleancache` — clears `cache_control`. `customparams` — injects custom request parameters.
|
|
402
|
+
- Experimental gist/CLI integrations: `gemini-cli`, `chutes-glm`, `qwen-cli`, `rovo-cli`.
|
|
892
403
|
|
|
893
|
-
The
|
|
894
|
-
|
|
895
|
-
```json
|
|
896
|
-
{
|
|
897
|
-
"name": "qwen",
|
|
898
|
-
"api_base_url": "https://qwen.aikit.club/v1/chat/completions",
|
|
899
|
-
"api_key": "qwen-placeholder",
|
|
900
|
-
"models": ["qwen3-max", "qwen3-coder-plus"],
|
|
901
|
-
"transformer": {
|
|
902
|
-
"use": ["qwen-auth", "reasoning", "OpenAI"]
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
Three transformers are required in the chain:
|
|
908
|
-
|
|
909
|
-
- `qwen-auth` — sets the `Authorization: Bearer <jwt>` header on every outbound request (loading/refreshing the JWT from `~/.claude-code-router/qwen_auth.json`) and strips the trailing `<details>...</details>` block Qwen injects into responses.
|
|
910
|
-
- `reasoning` — maps Claude Code's unified `reasoning` field onto the request so the Qwen endpoint's `enable_thinking` and `thinking_budget` parameters are populated.
|
|
911
|
-
- `OpenAI` — registers the `POST /v1/chat/completions` route. It is a thin endpoint stub with no body conversion, so it must remain last in the chain.
|
|
912
|
-
|
|
913
|
-
> **Note**: The `api_key` field is a placeholder — actual authentication is handled via the JWT stored in `~/.claude-code-router/qwen_auth.json`. Run `ccr qwen-auth` to authenticate before using the Qwen provider.
|
|
914
|
-
|
|
915
|
-
**Claude Subscription Provider Configuration:**
|
|
916
|
-
|
|
917
|
-
The `claude-auth` transformer routes requests to Anthropic's API using your Claude Pro or Max subscription OAuth token instead of a static API key.
|
|
918
|
-
|
|
919
|
-
```json
|
|
920
|
-
{
|
|
921
|
-
"name": "claude-subscription",
|
|
922
|
-
"api_base_url": "https://api.anthropic.com",
|
|
923
|
-
"api_key": "no-key",
|
|
924
|
-
"models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
|
|
925
|
-
"transformer": {
|
|
926
|
-
"use": ["claude-auth", "Anthropic"]
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
Two transformers are required in the chain:
|
|
932
|
-
|
|
933
|
-
- `claude-auth` — converts the request from Unified (OpenAI) format to Anthropic format, injects `Authorization: Bearer <token>` (loading/refreshing the token from `~/.claude-code-router/claude_auth.json`), and converts the Anthropic SSE response back to Unified format.
|
|
934
|
-
- `Anthropic` — registers the `POST /v1/messages` route. It has no body conversion in the provider chain, so it acts as a no-op endpoint stub.
|
|
935
|
-
|
|
936
|
-
> **Note**: The `api_key` field is a placeholder — actual authentication is handled via OAuth tokens stored in `~/.claude-code-router/claude_auth.json`. Run `ccr claude-auth` to authenticate before using this provider. The transformer automatically sends Anthropic's `oauth-2025-04-20` beta so subscription OAuth Bearer tokens are accepted.
|
|
937
|
-
|
|
938
|
-
**DeepSeek via OpenCode (Mandatory Reasoning Replay):**
|
|
939
|
-
|
|
940
|
-
DeepSeek models require previous assistant reasoning content to be replayed in subsequent requests. When using DeepSeek models through providers like OpenCode, apply the `reasoning` transformer at the model level to handle this automatically:
|
|
941
|
-
|
|
942
|
-
```json
|
|
943
|
-
{
|
|
944
|
-
"name": "opencode",
|
|
945
|
-
"api_base_url": "https://opencode.ai/zen/go/v1/chat/completions",
|
|
946
|
-
"api_key": "$OPENCODE_API_KEY",
|
|
947
|
-
"models": ["deepseek-v4-pro", "deepseek-v4-flash"],
|
|
948
|
-
"transformer": {
|
|
949
|
-
"use": ["OpenAI"],
|
|
950
|
-
"deepseek-v4-pro": {
|
|
951
|
-
"use": ["reasoning"]
|
|
952
|
-
},
|
|
953
|
-
"deepseek-v4-flash": {
|
|
954
|
-
"use": ["reasoning"]
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
```
|
|
959
|
-
|
|
960
|
-
> **Note**: The `reasoning` transformer must be applied specifically to DeepSeek models (not provider-wide). It replays the assistant's reasoning output from previous turns as required by the DeepSeek API.
|
|
404
|
+
> **See also**: The full transformer reference — including the `gemini` `cachedContent` / `thoughtSignatureFallback` options and the `openrouter` provider-routing parameter — is in `docs/docs/server/config/transformers.md`.
|
|
961
405
|
|
|
962
406
|
**Custom Transformers:**
|
|
963
407
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
```json
|
|
967
|
-
{
|
|
968
|
-
"transformers": [
|
|
969
|
-
{
|
|
970
|
-
"path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
|
|
971
|
-
"options": {
|
|
972
|
-
"project": "xxx"
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
]
|
|
976
|
-
}
|
|
977
|
-
```
|
|
408
|
+
Load your own transformers via the `transformers` field in `config.json`, e.g. `{ "transformers": [{ "path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js", "options": { "project": "xxx" } }] }`. See `docs/docs/server/config/transformers.md` for the full custom-transformer guide.
|
|
978
409
|
|
|
979
410
|
#### Router
|
|
980
411
|
|
|
@@ -994,42 +425,7 @@ Example: `/model openrouter,anthropic/claude-3.5-sonnet`
|
|
|
994
425
|
|
|
995
426
|
#### Custom Router
|
|
996
427
|
|
|
997
|
-
For
|
|
998
|
-
|
|
999
|
-
In your `config.json`:
|
|
1000
|
-
|
|
1001
|
-
```json
|
|
1002
|
-
{
|
|
1003
|
-
"CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
|
|
1004
|
-
}
|
|
1005
|
-
```
|
|
1006
|
-
|
|
1007
|
-
The custom router file must be a JavaScript module that exports an `async` function. This function receives the request object and the config object as arguments and should return the provider and model name as a string (e.g., `"provider_name,model_name"`), or `null` to fall back to the default router.
|
|
1008
|
-
|
|
1009
|
-
Here is an example of a `custom-router.js` based on `custom-router.example.js`:
|
|
1010
|
-
|
|
1011
|
-
```javascript
|
|
1012
|
-
// /User/xxx/.claude-code-router/custom-router.js
|
|
1013
|
-
|
|
1014
|
-
/**
|
|
1015
|
-
* A custom router function to determine which model to use based on the request.
|
|
1016
|
-
*
|
|
1017
|
-
* @param {object} req - The request object from Claude Code, containing the request body.
|
|
1018
|
-
* @param {object} config - The application's config object.
|
|
1019
|
-
* @returns {Promise<string|null>} - A promise that resolves to the "provider,model_name" string, or null to use the default router.
|
|
1020
|
-
*/
|
|
1021
|
-
module.exports = async function router(req, config) {
|
|
1022
|
-
const userMessage = req.body.messages.find((m) => m.role === "user")?.content;
|
|
1023
|
-
|
|
1024
|
-
if (userMessage && userMessage.includes("explain this code")) {
|
|
1025
|
-
// Use a powerful model for code explanation
|
|
1026
|
-
return "openrouter,anthropic/claude-3.5-sonnet";
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
// Fallback to the default router configuration
|
|
1030
|
-
return null;
|
|
1031
|
-
};
|
|
1032
|
-
```
|
|
428
|
+
For advanced routing logic, set `CUSTOM_ROUTER_PATH` in `config.json` to a JS module exporting an `async function(req, config)` that returns a `"provider,model"` string, or `null` to fall back to the default router. See `custom-router.example.js` and `docs/docs/server/advanced/custom-router.md`.
|
|
1033
429
|
|
|
1034
430
|
##### Subagent Routing
|
|
1035
431
|
|
|
@@ -1068,10 +464,10 @@ See [the implementation plan and review](tasks/caching-plan.md) for the provider
|
|
|
1068
464
|
|
|
1069
465
|
## Status Line (Beta)
|
|
1070
466
|
To better monitor the status of claude-code-router at runtime, version v1.0.40 includes a built-in statusline tool, which you can enable in the UI.
|
|
1071
|
-

|
|
1072
468
|
|
|
1073
469
|
The effect is as follows:
|
|
1074
|
-

|
|
1075
471
|
|
|
1076
472
|
## 🤖 GitHub Actions
|
|
1077
473
|
|