diff --git a/CLAUDE.md b/CLAUDE.md index f901d485..908f3e61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. diff --git a/README.md b/README.md index 27868be8..4bfbe562 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ 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: @@ -52,8 +54,8 @@ The `config.json` file has several key sections: - **`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`. @@ -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" } @@ -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" } ``` @@ -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. @@ -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 { diff --git a/README_zh.md b/README_zh.md index b9589a24..614df61e 100644 --- a/README_zh.md +++ b/README_zh.md @@ -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`。 @@ -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" } @@ -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" } ``` @@ -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 /** * 一个自定义路由函数,用于根据请求确定使用哪个模型。 @@ -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 { diff --git a/docker-compose.yml b/docker-compose.yml index 9773de10..608db684 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 restart: unless-stopped diff --git a/src/cli.ts b/src/cli.ts index b857e9d5..eb589792 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -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"; @@ -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." diff --git a/src/constants.ts b/src/constants.ts index a978e22d..ad1515a7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -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"); diff --git a/src/index.ts b/src/index.ts index efb38f4e..2d0dc83d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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" ), }, diff --git a/src/server.ts b/src/server.ts index 6ca3e4d0..62573c69 100644 --- a/src/server.ts +++ b/src/server.ts @@ -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); @@ -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)) { @@ -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)) { @@ -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)) { diff --git a/src/utils/index.ts b/src/utils/index.ts index 5e47e842..fa6b58de 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -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." diff --git a/src/utils/modelSelector.ts b/src/utils/modelSelector.ts index 7c857423..78e49dfd 100644 --- a/src/utils/modelSelector.ts +++ b/src/utils/modelSelector.ts @@ -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"; @@ -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}`); diff --git a/ui/config.example.json b/ui/config.example.json index 2cabece9..a1e89aa2 100644 --- a/ui/config.example.json +++ b/ui/config.example.json @@ -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" }