Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ This project is a TypeScript-based router for Claude Code requests. It allows ro

- **Entry Point**: The main command-line interface logic is in `src/cli.ts`. It handles parsing commands like `start`, `stop`, and `code`.
- **Server**: The `ccr start` command launches a server that listens for requests from Claude Code. The server logic is initiated from `src/index.ts`.
- **Configuration**: The router is configured via a JSON file located at `~/.claude-code-router/config.json`. This file defines API providers, routing rules, and custom transformers. An example can be found in `config.example.json`.
- **Configuration**: The router is configured via a JSON file located at `~/.claude-code-router/config.json`. You can customize this directory by setting the `CLAUDE_CODE_ROUTER_DIR` environment variable. This file defines API providers, routing rules, and custom transformers. An example can be found in `config.example.json`.
- **Routing**: The core routing logic determines which LLM provider and model to use for a given request. It supports default routes for different scenarios (`default`, `background`, `think`, `longContext`, `webSearch`) and can be extended with a custom JavaScript router file. The router logic is likely in `src/utils/router.ts`.
- **Providers and Transformers**: The application supports multiple LLM providers. Transformers adapt the request and response formats for different provider APIs.
- **Claude Code Integration**: When a user runs `ccr code`, the command is forwarded to the running router service. The service then processes the request, applies routing rules, and sends it to the configured LLM. If the service isn't running, `ccr code` will attempt to start it automatically.
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,18 @@ npm install -g @musistudio/claude-code-router

### 2. Configuration

Create and configure your `~/.claude-code-router/config.json` file. For more details, you can refer to `config.example.json`.
> **Note**: You can customize the configuration directory by setting the `CLAUDE_CODE_ROUTER_DIR` environment variable. By default, it is located at `~/.claude-code-router`.

Create and configure your `config.json` file in the claude-code-router directory. For more details, you can refer to `config.example.json`.

The `config.json` file has several key sections:

- **`PROXY_URL`** (optional): You can set a proxy for API requests, for example: `"PROXY_URL": "http://127.0.0.1:7890"`.
- **`LOG`** (optional): You can enable logging by setting it to `true`. When set to `false`, no log files will be created. Default is `true`.
- **`LOG_LEVEL`** (optional): Set the logging level. Available options are: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`. Default is `"debug"`.
- **Logging Systems**: The Claude Code Router uses two separate logging systems:
- **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`
- **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log`
- **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `logs/` subdirectory with filenames like `ccr-*.log`
- **Application-level logs**: Routing decisions and business logic events are logged in `claude-code-router.log`
- **`APIKEY`** (optional): You can set a secret key to authenticate requests. When set, clients must provide this key in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. Example: `"APIKEY": "your-secret-key"`.
- **`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"`.
- **`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`.
Expand Down Expand Up @@ -361,7 +363,7 @@ You can also create your own transformers and load them via the `transformers` f
{
"transformers": [
{
"path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
"path": "/path/to/your/claude-code-router/plugins/gemini-cli.js",
"options": {
"project": "xxx"
}
Expand Down Expand Up @@ -394,7 +396,7 @@ In your `config.json`:

```json
{
"CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
"CUSTOM_ROUTER_PATH": "/path/to/your/claude-code-router/custom-router.js"
}
```

Expand All @@ -403,7 +405,7 @@ The custom router file must be a JavaScript module that exports an `async` funct
Here is an example of a `custom-router.js` based on `custom-router.example.js`:

```javascript
// /User/xxx/.claude-code-router/custom-router.js
// /path/to/your/claude-code-router/custom-router.js

/**
* A custom router function to determine which model to use based on the request.
Expand Down Expand Up @@ -475,6 +477,8 @@ jobs:
- name: Prepare Environment
run: |
curl -fsSL https://bun.sh/install | bash
# Set the CLAUDE_CODE_ROUTER_DIR environment variable for the workflow
echo "CLAUDE_CODE_ROUTER_DIR=$HOME/.claude-code-router" >> $GITHUB_ENV
mkdir -p $HOME/.claude-code-router
cat << 'EOF' > $HOME/.claude-code-router/config.json
{
Expand Down
16 changes: 10 additions & 6 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@ npm install -g @musistudio/claude-code-router

### 2. 配置

创建并配置您的 `~/.claude-code-router/config.json` 文件。有关更多详细信息,您可以参考 `config.example.json`。
> **注意**: 您可以通过设置 `CLAUDE_CODE_ROUTER_DIR` 环境变量自定义配置目录。默认情况下,它位于 `~/.claude-code-router`。

在 claude-code-router 目录中创建并配置您的 `config.json` 文件。有关更多详细信息,您可以参考 `config.example.json`。

`config.json` 文件有几个关键部分:
- **`PROXY_URL`** (可选): 您可以为 API 请求设置代理,例如:`"PROXY_URL": "http://127.0.0.1:7890"`。
- **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。当设置为 `false` 时,将不会创建日志文件。默认值为 `true`。
- **`LOG_LEVEL`** (可选): 设置日志级别。可用选项包括:`"fatal"`、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`。默认值为 `"debug"`。
- **日志系统**: Claude Code Router 使用两个独立的日志系统:
- **服务器级别日志**: HTTP 请求、API 调用和服务器事件使用 pino 记录在 `~/.claude-code-router/logs/` 目录中,文件名类似于 `ccr-*.log`
- **应用程序级别日志**: 路由决策和业务逻辑事件记录在 `~/.claude-code-router/claude-code-router.log` 文件中
- **服务器级别日志**: HTTP 请求、API 调用和服务器事件使用 pino 记录在 `logs/` 子目录中,文件名类似于 `ccr-*.log`
- **应用程序级别日志**: 路由决策和业务逻辑事件记录在 `claude-code-router.log` 文件中
- **`APIKEY`** (可选): 您可以设置一个密钥来进行身份验证。设置后,客户端请求必须在 `Authorization` 请求头 (例如, `Bearer your-secret-key`) 或 `x-api-key` 请求头中提供此密钥。例如:`"APIKEY": "your-secret-key"`。
- **`HOST`** (可选): 您可以设置服务的主机地址。如果未设置 `APIKEY`,出于安全考虑,主机地址将强制设置为 `127.0.0.1`,以防止未经授权的访问。例如:`"HOST": "0.0.0.0"`。
- **`NON_INTERACTIVE_MODE`** (可选): 当设置为 `true` 时,启用与非交互式环境(如 GitHub Actions、Docker 容器或其他 CI/CD 系统)的兼容性。这会设置适当的环境变量(`CI=true`、`FORCE_COLOR=0` 等)并配置 stdin 处理,以防止进程在自动化环境中挂起。例如:`"NON_INTERACTIVE_MODE": true`。
Expand Down Expand Up @@ -308,7 +310,7 @@ Transformers 允许您修改请求和响应负载,以确保与不同提供商
{
"transformers": [
{
"path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
"path": "/path/to/your/claude-code-router/plugins/gemini-cli.js",
"options": {
"project": "xxx"
}
Expand Down Expand Up @@ -341,7 +343,7 @@ Transformers 允许您修改请求和响应负载,以确保与不同提供商

```json
{
"CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
"CUSTOM_ROUTER_PATH": "/path/to/your/claude-code-router/custom-router.js"
}
```

Expand All @@ -350,7 +352,7 @@ Transformers 允许您修改请求和响应负载,以确保与不同提供商
这是一个基于 `custom-router.example.js` 的 `custom-router.js` 示例:

```javascript
// /User/xxx/.claude-code-router/custom-router.js
// /path/to/your/claude-code-router/custom-router.js

/**
* 一个自定义路由函数,用于根据请求确定使用哪个模型。
Expand Down Expand Up @@ -422,6 +424,8 @@ jobs:
- name: Prepare Environment
run: |
curl -fsSL https://bun.sh/install | bash
# 为工作流设置 CLAUDE_CODE_ROUTER_DIR 环境变量
echo "CLAUDE_CODE_ROUTER_DIR=$HOME/.claude-code-router" >> $GITHUB_ENV
mkdir -p $HOME/.claude-code-router
cat << 'EOF' > $HOME/.claude-code-router/config.json
{
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ services:
build: .
ports:
- "3456:3456"
# You can set the CLAUDE_CODE_ROUTER_DIR environment variable to customize the configuration directory.
# environment:
# - CLAUDE_CODE_ROUTER_DIR=/data
volumes:
- ~/.claude-code-router:/root/.claude-code-router
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example shows setting CLAUDE_CODE_ROUTER_DIR=/data but the volumes mapping is still hardcoded to ~/.claude-code-router:/root/.claude-code-router. If a user sets the environment variable as suggested, there will be a mismatch between where the application looks for config (/data inside the container) and where the volume is mounted (/root/.claude-code-router).

Consider either:

  1. Updating the volume mapping to match the example: - ~/.claude-code-router:/data
  2. Or changing the environment variable example to match the current volume: - CLAUDE_CODE_ROUTER_DIR=/root/.claude-code-router

Option 2 is simpler as it requires no volume changes, but option 1 provides a cleaner separation of data.

Suggested change
- ~/.claude-code-router:/root/.claude-code-router
- ~/.claude-code-router:/data

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLAUDE_CODE_ROUTER_DIR=/root/.claude-code-router

restart: unless-stopped
4 changes: 2 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { runModelSelector } from "./utils/modelSelector"; // ADD THIS LINE
import { version } from "../package.json";
import { spawn, exec } from "child_process";
import { PID_FILE, REFERENCE_COUNT_FILE } from "./constants";
import { PID_FILE, REFERENCE_COUNT_FILE, CONFIG_FILE } from "./constants";
import fs, { existsSync, readFileSync } from "fs";
import { join } from "path";

Expand Down Expand Up @@ -207,7 +207,7 @@ async function main() {
Router: {},
});
console.log(
"Created minimal default configuration file at ~/.claude-code-router/config.json"
`Created minimal default configuration file at ${CONFIG_FILE}`
);
console.log(
"Please edit this file with your actual configuration."
Expand Down
4 changes: 3 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from "node:path";
import os from "node:os";

export const HOME_DIR = path.join(os.homedir(), ".claude-code-router");
export const HOME_DIR = process.env.CLAUDE_CODE_ROUTER_DIR
? path.resolve(process.env.CLAUDE_CODE_ROUTER_DIR)
: path.join(os.homedir(), ".claude-code-router");

export const CONFIG_FILE = path.join(HOME_DIR, "config.json");

Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ async function run(options: RunOptions = {}) {
HOST: HOST,
PORT: servicePort,
LOG_FILE: join(
homedir(),
".claude-code-router",
HOME_DIR,
"claude-code-router.log"
),
},
Expand Down
8 changes: 4 additions & 4 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { checkForUpdates, performUpdate } from "./utils";
import { join } from "path";
import fastifyStatic from "@fastify/static";
import { readdirSync, statSync, readFileSync, writeFileSync, existsSync } from "fs";
import { homedir } from "os";
import {calculateTokenCount} from "./utils/router";
import { HOME_DIR } from "./constants";

export const createServer = (config: any): Server => {
const server = new Server(config);
Expand Down Expand Up @@ -114,7 +114,7 @@ export const createServer = (config: any): Server => {
// 获取日志文件列表端点
server.app.get("/api/logs/files", async (req, reply) => {
try {
const logDir = join(homedir(), ".claude-code-router", "logs");
const logDir = join(HOME_DIR, "logs");
const logFiles: Array<{ name: string; path: string; size: number; lastModified: string }> = [];

if (existsSync(logDir)) {
Expand Down Expand Up @@ -156,7 +156,7 @@ export const createServer = (config: any): Server => {
logFilePath = filePath;
} else {
// 如果没有指定文件路径,使用默认的日志文件路径
logFilePath = join(homedir(), ".claude-code-router", "logs", "app.log");
logFilePath = join(HOME_DIR, "logs", "app.log");
}

if (!existsSync(logFilePath)) {
Expand Down Expand Up @@ -184,7 +184,7 @@ export const createServer = (config: any): Server => {
logFilePath = filePath;
} else {
// 如果没有指定文件路径,使用默认的日志文件路径
logFilePath = join(homedir(), ".claude-code-router", "logs", "app.log");
logFilePath = join(HOME_DIR, "logs", "app.log");
}

if (existsSync(logFilePath)) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export const readConfigFile = async () => {
// Create a minimal default config file
await writeConfigFile(config);
console.log(
"Created minimal default configuration file at ~/.claude-code-router/config.json"
`Created minimal default configuration file at ${CONFIG_FILE}`
);
console.log(
"Please edit this file with your actual configuration."
Expand Down
4 changes: 2 additions & 2 deletions src/utils/modelSelector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { select, input, confirm } from '@inquirer/prompts';
import { HOME_DIR } from '../constants';

// ANSI color codes
const RESET = "\x1B[0m";
Expand Down Expand Up @@ -68,8 +69,7 @@ const AVAILABLE_TRANSFORMERS = [
];

function getConfigPath(): string {
const configDir = path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude-code-router');
const configPath = path.join(configDir, 'config.json');
const configPath = path.join(HOME_DIR, 'config.json');

if (!fs.existsSync(configPath)) {
throw new Error(`config.json not found at ${configPath}`);
Expand Down
2 changes: 1 addition & 1 deletion ui/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"API_TIMEOUT_MS": 600000,
"transformers": [
{
"path": "/Users/abc/.claude-code-router/plugins/gemini-cli.js",
"path": "/path/to/your/claude-code-router/plugins/gemini-cli.js",
"options": {
"project": "x"
}
Expand Down