Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 4 additions & 3 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 +115,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 +157,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 +185,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
Loading