mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-01-30 06:12:06 +00:00
fix doc
This commit is contained in:
@@ -185,7 +185,7 @@ Located in `packages/shared/src/preset/`:
|
|||||||
|
|
||||||
- **sensitiveFields.ts**: Identify and sanitize sensitive fields
|
- **sensitiveFields.ts**: Identify and sanitize sensitive fields
|
||||||
- Detects api_key, password, secret fields automatically
|
- Detects api_key, password, secret fields automatically
|
||||||
- Creates `requiredInputs` array for installation prompts
|
- Replaces sensitive values with environment variable placeholders
|
||||||
|
|
||||||
### Preset File Format
|
### Preset File Format
|
||||||
|
|
||||||
@@ -206,12 +206,6 @@ Located in `packages/shared/src/preset/`:
|
|||||||
"label": "OpenAI API Key",
|
"label": "OpenAI API Key",
|
||||||
"prompt": "Enter your OpenAI API key"
|
"prompt": "Enter your OpenAI API key"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"field": "Providers[0].api_key",
|
|
||||||
"placeholder": "Enter your API key"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -141,14 +141,14 @@ A preset is a directory containing a `manifest.json` file:
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "{{apiKey}}",
|
"api_key": "{{apiKey}}",
|
||||||
"models": ["gpt-4", "gpt-3.5-turbo"]
|
"models": ["gpt-4", "gpt-3.5-turbo"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai:gpt-4"
|
"default": "openai,gpt-4"
|
||||||
},
|
},
|
||||||
|
|
||||||
"schema": [
|
"schema": [
|
||||||
@@ -251,4 +251,4 @@ ccr preset export my-preset --include-sensitive
|
|||||||
|
|
||||||
- [Configuration Guide](/docs/cli/config/basic) - Basic configuration
|
- [Configuration Guide](/docs/cli/config/basic) - Basic configuration
|
||||||
- [Project-Level Configuration](/docs/cli/config/project-level) - Project-specific settings
|
- [Project-Level Configuration](/docs/cli/config/project-level) - Project-specific settings
|
||||||
- [Server: Presets](/docs/server/advanced/presets) - Advanced preset topics
|
- [Presets](/docs/presets/intro) - Advanced preset topics
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ Output: ` feature/auth 📝 +150/-25 ⏱️ 2m34s`
|
|||||||
|
|
||||||
Statusline themes can be included in presets. When you install a preset with statusline configuration, it will automatically apply when you activate that preset.
|
Statusline themes can be included in presets. When you install a preset with statusline configuration, it will automatically apply when you activate that preset.
|
||||||
|
|
||||||
See [Presets](/docs/server/advanced/presets) for more information.
|
See [Presets](/docs/presets/intro) for more information.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ CLI uses the same configuration file as Server: `~/.claude-code-router/config.js
|
|||||||
|
|
||||||
## Configuration Methods
|
## Configuration Methods
|
||||||
|
|
||||||
You can configure Claude Code Router in three ways:
|
You can configure Claude Code Router in two ways:
|
||||||
|
|
||||||
### Option 1: Edit Configuration File Directly
|
### Option 1: Edit Configuration File Directly
|
||||||
|
|
||||||
@@ -26,20 +26,6 @@ Open the web interface and configure visually:
|
|||||||
ccr ui
|
ccr ui
|
||||||
```
|
```
|
||||||
|
|
||||||
### Option 3: Interactive Configuration
|
|
||||||
|
|
||||||
Use the interactive command-line configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ccr model
|
|
||||||
```
|
|
||||||
|
|
||||||
This will guide you through:
|
|
||||||
1. Select LLM provider
|
|
||||||
2. Configure API Key
|
|
||||||
3. Select model
|
|
||||||
4. Set routing rules
|
|
||||||
|
|
||||||
## Restart After Configuration Changes
|
## Restart After Configuration Changes
|
||||||
|
|
||||||
After modifying the configuration file or making changes through the Web UI, you must restart the service:
|
After modifying the configuration file or making changes through the Web UI, you must restart the service:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Edit `~/.claude-code-router/config.json`:
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "your-api-key-here",
|
"api_key": "your-api-key-here",
|
||||||
"models": ["gpt-4", "gpt-3.5-turbo"]
|
"models": ["gpt-4", "gpt-3.5-turbo"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,6 @@ ccr preset install /path/to/preset-directory
|
|||||||
ccr preset install my-preset
|
ccr preset install my-preset
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note Note
|
|
||||||
CLI mode **does not support** installing presets directly from URLs. To install from GitHub, clone to local first or use Web UI.
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Using Presets
|
#### Using Presets
|
||||||
|
|
||||||
After installing a preset, you can use the preset name to start Claude Code:
|
After installing a preset, you can use the preset name to start Claude Code:
|
||||||
@@ -41,9 +37,6 @@ After installing a preset, you can use the preset name to start Claude Code:
|
|||||||
```bash
|
```bash
|
||||||
# Start with a specific preset
|
# Start with a specific preset
|
||||||
ccr my-preset "your prompt"
|
ccr my-preset "your prompt"
|
||||||
|
|
||||||
# Background task with preset
|
|
||||||
ccr my-preset --background "your prompt"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The preset will:
|
The preset will:
|
||||||
@@ -89,13 +82,6 @@ Then open `http://localhost:3000` in your browser.
|
|||||||
2. Select the preset you want to install from the list
|
2. Select the preset you want to install from the list
|
||||||
3. Click the "Install" button
|
3. Click the "Install" button
|
||||||
|
|
||||||
Or manually enter a GitHub repository URL:
|
|
||||||
|
|
||||||
```
|
|
||||||
Format: https://github.com/username/repo
|
|
||||||
Example: https://github.com/example/ccr-presets
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Reconfigure Preset
|
#### Reconfigure Preset
|
||||||
|
|
||||||
1. Click the "View Details" icon next to the preset
|
1. Click the "View Details" icon next to the preset
|
||||||
@@ -309,13 +295,13 @@ For complex configuration needs, use `configMappings` to precisely control value
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "{{primaryProvider}}",
|
"name": "{{primaryProvider}}",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "{{apiKey}}",
|
"api_key": "{{apiKey}}",
|
||||||
"models": ["{{defaultModel}}"]
|
"models": ["{{defaultModel}}"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "{{primaryProvider}}/{{defaultModel}}"
|
"default": "{{primaryProvider}},{{defaultModel}}"
|
||||||
},
|
},
|
||||||
"PROXY_URL": "{{proxyUrl}}"
|
"PROXY_URL": "{{proxyUrl}}"
|
||||||
},
|
},
|
||||||
@@ -352,9 +338,6 @@ These fields describe basic information about the preset:
|
|||||||
| `license` | string | - | License type |
|
| `license` | string | - | License type |
|
||||||
| `keywords` | string[] | - | Keyword tags |
|
| `keywords` | string[] | - | Keyword tags |
|
||||||
| `ccrVersion` | string | - | Compatible CCR version |
|
| `ccrVersion` | string | - | Compatible CCR version |
|
||||||
| `source` | string | - | Preset source URL |
|
|
||||||
| `sourceType` | string | - | Source type (`local`/`gist`/`registry`) |
|
|
||||||
| `checksum` | string | - | Content checksum (SHA256) |
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -382,6 +365,14 @@ These fields are directly merged into CCR's configuration. All fields supported
|
|||||||
| `Router` | object | Routing configuration |
|
| `Router` | object | Routing configuration |
|
||||||
| `transformers` | array | Transformer configuration |
|
| `transformers` | array | Transformer configuration |
|
||||||
| `StatusLine` | object | Status bar configuration |
|
| `StatusLine` | object | Status bar configuration |
|
||||||
|
| `NON_INTERACTIVE_MODE` | boolean | Enable non-interactive mode (for CI/CD) |
|
||||||
|
|
||||||
|
**CLI-Only Fields** (these fields only work in CLI mode and are not used by the server):
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `noServer` | boolean | Skip local server startup and use provider's API directly |
|
||||||
|
| `claudeCodeSettings` | object | Claude Code specific settings (env, statusLine, etc.) |
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -390,14 +381,14 @@ Example:
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai/gpt-4o",
|
"default": "openai,gpt-4o",
|
||||||
"background": "openai/gpt-4o-mini"
|
"background": "openai,gpt-4o-mini"
|
||||||
},
|
},
|
||||||
"PORT": 8080
|
"PORT": 8080
|
||||||
}
|
}
|
||||||
@@ -413,7 +404,6 @@ These fields are used to create interactive configuration templates:
|
|||||||
| `template` | object | Configuration template (with variable references) |
|
| `template` | object | Configuration template (with variable references) |
|
||||||
| `configMappings` | array | Configuration mapping rules |
|
| `configMappings` | array | Configuration mapping rules |
|
||||||
| `userValues` | object | User-filled values (used at runtime) |
|
| `userValues` | object | User-filled values (used at runtime) |
|
||||||
| `requiredInputs` | array | Required input list (auto-generated) |
|
|
||||||
|
|
||||||
**Schema Field Types:**
|
**Schema Field Types:**
|
||||||
|
|
||||||
@@ -483,24 +473,16 @@ cat > ~/.claude-code-router/presets/simple-openai/manifest.json << 'EOF'
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai/gpt-4o",
|
"default": "openai,gpt-4o",
|
||||||
"background": "openai/gpt-4o-mini"
|
"background": "openai,gpt-4o-mini"
|
||||||
},
|
}
|
||||||
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"id": "Providers[0].api_key",
|
|
||||||
"prompt": "Enter OpenAI API Key",
|
|
||||||
"placeholder": "OPENAI_API_KEY"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -584,14 +566,14 @@ cat > ~/.claude-code-router/presets/advanced-config/manifest.json << 'EOF'
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "#{provider}",
|
"name": "#{provider}",
|
||||||
"api_base_url": "#{provider === 'openai' ? 'https://api.openai.com/v1' : 'https://api.deepseek.com'}",
|
"api_base_url": "#{provider === 'openai' ? 'https://api.openai.com/v1/chat/completions' : 'https://api.deepseek.com/v1/chat/completions'}",
|
||||||
"api_key": "#{apiKey}",
|
"api_key": "#{apiKey}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "#{provider}/gpt-4o",
|
"default": "#{provider},gpt-4o",
|
||||||
"background": "#{provider}/gpt-4o-mini"
|
"background": "#{provider},gpt-4o-mini"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -639,12 +621,6 @@ ccr preset export my-exported-preset \
|
|||||||
--tags "production,openai"
|
--tags "production,openai"
|
||||||
```
|
```
|
||||||
|
|
||||||
:::tip Share Presets
|
|
||||||
Exported preset directories can be directly shared with others. Recipients can:
|
|
||||||
- **CLI Mode**: Place directory in `~/.claude-code-router/presets/`, then run `ccr preset install preset-name`
|
|
||||||
- **Web UI Mode**: Upload directory to GitHub, then install via repository URL
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Preset File Location
|
## Preset File Location
|
||||||
|
|
||||||
Presets are stored in:
|
Presets are stored in:
|
||||||
@@ -141,14 +141,14 @@ ccr preset delete my-config
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "{{apiKey}}",
|
"api_key": "{{apiKey}}",
|
||||||
"models": ["gpt-4", "gpt-3.5-turbo"]
|
"models": ["gpt-4", "gpt-3.5-turbo"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai:gpt-4"
|
"default": "openai,gpt-4"
|
||||||
},
|
},
|
||||||
|
|
||||||
"schema": [
|
"schema": [
|
||||||
@@ -251,4 +251,4 @@ ccr preset export my-preset --include-sensitive
|
|||||||
|
|
||||||
- [配置指南](/zh/docs/cli/config/basic) - 基础配置
|
- [配置指南](/zh/docs/cli/config/basic) - 基础配置
|
||||||
- [项目级配置](/zh/docs/cli/config/project-level) - 项目特定设置
|
- [项目级配置](/zh/docs/cli/config/project-level) - 项目特定设置
|
||||||
- [服务器:预设](/zh/docs/server/advanced/presets) - 高级预设主题
|
- [预设](/zh/docs/presets/intro) - 高级预设主题
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ module.exports = async function(variables, options) {
|
|||||||
|
|
||||||
Statusline 主题可以包含在 presets 中。当您安装带有 statusline 配置的 preset 时,激活该 preset 时会自动应用。
|
Statusline 主题可以包含在 presets 中。当您安装带有 statusline 配置的 preset 时,激活该 preset 时会自动应用。
|
||||||
|
|
||||||
查看 [Presets](/docs/server/advanced/presets) 了解更多信息。
|
查看 [Presets](/zh/docs/presets/intro) 了解更多信息。
|
||||||
|
|
||||||
## 故障排除
|
## 故障排除
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,6 @@ ccr preset install /path/to/preset-directory
|
|||||||
ccr preset install my-preset
|
ccr preset install my-preset
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note 注意
|
|
||||||
CLI 方式**不支持**从 URL 直接安装预设。如需从 GitHub 安装,请先克隆到本地或使用 Web UI。
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### 使用预设
|
#### 使用预设
|
||||||
|
|
||||||
安装预设后,可以使用预设名称启动 Claude Code:
|
安装预设后,可以使用预设名称启动 Claude Code:
|
||||||
@@ -42,9 +38,6 @@ CLI 方式**不支持**从 URL 直接安装预设。如需从 GitHub 安装,
|
|||||||
```bash
|
```bash
|
||||||
# 使用指定预设启动
|
# 使用指定预设启动
|
||||||
ccr my-preset "your prompt"
|
ccr my-preset "your prompt"
|
||||||
|
|
||||||
# 后台任务使用预设
|
|
||||||
ccr my-preset --background "your prompt"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
预设会:
|
预设会:
|
||||||
@@ -90,13 +83,6 @@ ccr ui
|
|||||||
2. 在预设列表中选择要安装的预设
|
2. 在预设列表中选择要安装的预设
|
||||||
3. 点击"安装"按钮
|
3. 点击"安装"按钮
|
||||||
|
|
||||||
或手动输入 GitHub 仓库地址:
|
|
||||||
|
|
||||||
```
|
|
||||||
格式:https://github.com/username/repo
|
|
||||||
示例:https://github.com/example/ccr-presets
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 重新配置预设
|
#### 重新配置预设
|
||||||
|
|
||||||
1. 在预设列表中点击"查看详情"按钮
|
1. 在预设列表中点击"查看详情"按钮
|
||||||
@@ -310,13 +296,13 @@ CCR 引入了强大的动态配置系统,支持:
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "{{primaryProvider}}",
|
"name": "{{primaryProvider}}",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "{{apiKey}}",
|
"api_key": "{{apiKey}}",
|
||||||
"models": ["{{defaultModel}}"]
|
"models": ["{{defaultModel}}"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "{{primaryProvider}}/{{defaultModel}}"
|
"default": "{{primaryProvider}},{{defaultModel}}"
|
||||||
},
|
},
|
||||||
"PROXY_URL": "{{proxyUrl}}"
|
"PROXY_URL": "{{proxyUrl}}"
|
||||||
},
|
},
|
||||||
@@ -353,9 +339,6 @@ CCR 引入了强大的动态配置系统,支持:
|
|||||||
| `license` | string | - | 许可证类型 |
|
| `license` | string | - | 许可证类型 |
|
||||||
| `keywords` | string[] | - | 关键词标签 |
|
| `keywords` | string[] | - | 关键词标签 |
|
||||||
| `ccrVersion` | string | - | 兼容的 CCR 版本 |
|
| `ccrVersion` | string | - | 兼容的 CCR 版本 |
|
||||||
| `source` | string | - | 预设来源 URL |
|
|
||||||
| `sourceType` | string | - | 来源类型(`local`/`gist`/`registry`) |
|
|
||||||
| `checksum` | string | - | 内容校验和(SHA256) |
|
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -383,6 +366,14 @@ CCR 引入了强大的动态配置系统,支持:
|
|||||||
| `Router` | object | 路由配置 |
|
| `Router` | object | 路由配置 |
|
||||||
| `transformers` | array | 转换器配置 |
|
| `transformers` | array | 转换器配置 |
|
||||||
| `StatusLine` | object | 状态栏配置 |
|
| `StatusLine` | object | 状态栏配置 |
|
||||||
|
| `NON_INTERACTIVE_MODE` | boolean | 启用非交互模式(用于 CI/CD) |
|
||||||
|
|
||||||
|
**CLI 专用字段**(这些字段仅在 CLI 模式下有效,服务器不使用):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `noServer` | boolean | 跳过本地服务器启动,直接使用 Provider 的 API |
|
||||||
|
| `claudeCodeSettings` | object | Claude Code 特定设置(环境变量、状态栏等) |
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -391,14 +382,14 @@ CCR 引入了强大的动态配置系统,支持:
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai/gpt-4o",
|
"default": "openai,gpt-4o",
|
||||||
"background": "openai/gpt-4o-mini"
|
"background": "openai,gpt-4o-mini"
|
||||||
},
|
},
|
||||||
"PORT": 8080
|
"PORT": 8080
|
||||||
}
|
}
|
||||||
@@ -414,7 +405,6 @@ CCR 引入了强大的动态配置系统,支持:
|
|||||||
| `template` | object | 配置模板(使用变量引用) |
|
| `template` | object | 配置模板(使用变量引用) |
|
||||||
| `configMappings` | array | 配置映射规则 |
|
| `configMappings` | array | 配置映射规则 |
|
||||||
| `userValues` | object | 用户填写的值(运行时使用) |
|
| `userValues` | object | 用户填写的值(运行时使用) |
|
||||||
| `requiredInputs` | array | 必填输入项列表(自动生成) |
|
|
||||||
|
|
||||||
**schema 字段类型:**
|
**schema 字段类型:**
|
||||||
|
|
||||||
@@ -484,24 +474,16 @@ cat > ~/.claude-code-router/presets/simple-openai/manifest.json << 'EOF'
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai/gpt-4o",
|
"default": "openai,gpt-4o",
|
||||||
"background": "openai/gpt-4o-mini"
|
"background": "openai,gpt-4o-mini"
|
||||||
},
|
}
|
||||||
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"id": "Providers[0].api_key",
|
|
||||||
"prompt": "Enter OpenAI API Key",
|
|
||||||
"placeholder": "OPENAI_API_KEY"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -585,14 +567,14 @@ cat > ~/.claude-code-router/presets/advanced-config/manifest.json << 'EOF'
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "#{provider}",
|
"name": "#{provider}",
|
||||||
"api_base_url": "#{provider === 'openai' ? 'https://api.openai.com/v1' : 'https://api.deepseek.com'}",
|
"api_base_url": "#{provider === 'openai' ? 'https://api.openai.com/v1/chat/completions' : 'https://api.deepseek.com/v1/chat/completions'}",
|
||||||
"api_key": "#{apiKey}",
|
"api_key": "#{apiKey}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Router": {
|
"Router": {
|
||||||
"default": "#{provider}/gpt-4o",
|
"default": "#{provider},gpt-4o",
|
||||||
"background": "#{provider}/gpt-4o-mini"
|
"background": "#{provider},gpt-4o-mini"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -640,12 +622,6 @@ ccr preset export my-exported-preset \
|
|||||||
--tags "production,openai"
|
--tags "production,openai"
|
||||||
```
|
```
|
||||||
|
|
||||||
:::tip 分享预设
|
|
||||||
导出的预设目录可以直接分享给他人。接收者可以:
|
|
||||||
- **CLI 方式**:将目录放到 `~/.claude-code-router/presets/`,然后运行 `ccr preset install 预设名`
|
|
||||||
- **Web UI 方式**:将目录上传到 GitHub,然后通过仓库 URL 安装
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 预设文件位置
|
## 预设文件位置
|
||||||
|
|
||||||
预设保存在:
|
预设保存在:
|
||||||
@@ -55,7 +55,6 @@ Preset 是一个预定义的配置包,用于快速配置 Claude Code Router。
|
|||||||
"schema": [...],
|
"schema": [...],
|
||||||
"template": {...},
|
"template": {...},
|
||||||
"configMappings": [...],
|
"configMappings": [...],
|
||||||
"requiredInputs": [...],
|
|
||||||
"userValues": {...}
|
"userValues": {...}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -113,7 +112,7 @@ Provider 配置数组,定义 LLM 服务提供商。
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"],
|
"models": ["gpt-4o", "gpt-4o-mini"],
|
||||||
"transformer": "anthropic",
|
"transformer": "anthropic",
|
||||||
@@ -159,7 +158,7 @@ Provider 配置数组,定义 LLM 服务提供商。
|
|||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `default` | string | 默认路由(格式:`provider/model`) |
|
| `default` | string | 默认路由(格式:`provider,model`) |
|
||||||
| `background` | string | 后台任务路由 |
|
| `background` | string | 后台任务路由 |
|
||||||
| `think` | string | 思考模式路由 |
|
| `think` | string | 思考模式路由 |
|
||||||
| `longContext` | string | 长上下文路由 |
|
| `longContext` | string | 长上下文路由 |
|
||||||
@@ -550,27 +549,6 @@ userValues 存储用户在安装时填写的值,运行时自动应用。
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### requiredInputs(必填输入)
|
|
||||||
|
|
||||||
requiredInputs 是导出预设时自动生成的字段列表,用于提示用户需要填写哪些信息。
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"id": "Providers[0].api_key",
|
|
||||||
"prompt": "Enter api_key",
|
|
||||||
"placeholder": "OPENAI_API_KEY"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "PROXY_URL",
|
|
||||||
"prompt": "Enter proxy URL",
|
|
||||||
"placeholder": "PROXY_URL"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 敏感字段处理
|
## 敏感字段处理
|
||||||
|
|
||||||
CCR 会自动识别敏感字段(如 `api_key`、`secret`、`password` 等),并将其替换为环境变量占位符。
|
CCR 会自动识别敏感字段(如 `api_key`、`secret`、`password` 等),并将其替换为环境变量占位符。
|
||||||
@@ -617,13 +595,6 @@ $VARIABLE_NAME
|
|||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_key": "${OPENAI_API_KEY}"
|
"api_key": "${OPENAI_API_KEY}"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"id": "Providers[0].api_key",
|
|
||||||
"prompt": "Enter api_key",
|
|
||||||
"placeholder": "OPENAI_API_KEY"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -642,7 +613,7 @@ $VARIABLE_NAME
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "openai",
|
"name": "openai",
|
||||||
"api_base_url": "https://api.openai.com/v1",
|
"api_base_url": "https://api.openai.com/v1/chat/completions",
|
||||||
"api_key": "${OPENAI_API_KEY}",
|
"api_key": "${OPENAI_API_KEY}",
|
||||||
"models": ["gpt-4o", "gpt-4o-mini"]
|
"models": ["gpt-4o", "gpt-4o-mini"]
|
||||||
}
|
}
|
||||||
@@ -651,15 +622,7 @@ $VARIABLE_NAME
|
|||||||
"Router": {
|
"Router": {
|
||||||
"default": "openai/gpt-4o",
|
"default": "openai/gpt-4o",
|
||||||
"background": "openai/gpt-4o-mini"
|
"background": "openai/gpt-4o-mini"
|
||||||
},
|
}
|
||||||
|
|
||||||
"requiredInputs": [
|
|
||||||
{
|
|
||||||
"id": "Providers[0].api_key",
|
|
||||||
"prompt": "Enter OpenAI API Key",
|
|
||||||
"placeholder": "OPENAI_API_KEY"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -806,7 +769,7 @@ $VARIABLE_NAME
|
|||||||
"Providers": [
|
"Providers": [
|
||||||
{
|
{
|
||||||
"name": "#{primaryProvider}",
|
"name": "#{primaryProvider}",
|
||||||
"api_base_url": "#{primaryProvider === 'openai' ? 'https://api.openai.com/v1' : 'https://api.deepseek.com'}",
|
"api_base_url": "#{primaryProvider === 'openai' ? 'https://api.openai.com/v1/chat/completions' : 'https://api.deepseek.com/v1/chat/completions'}",
|
||||||
"api_key": "#{apiKey}",
|
"api_key": "#{apiKey}",
|
||||||
"models": [
|
"models": [
|
||||||
"#{defaultModel}",
|
"#{defaultModel}",
|
||||||
|
|||||||
@@ -106,11 +106,21 @@ const sidebars: SidebarsConfig = {
|
|||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
'server/advanced/custom-router',
|
'server/advanced/custom-router',
|
||||||
'server/advanced/presets',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Presets',
|
||||||
|
link: {
|
||||||
|
type: 'generated-index',
|
||||||
|
title: 'CCR Presets',
|
||||||
|
description: 'Predefined configurations for quick setup',
|
||||||
|
slug: 'category/presets',
|
||||||
|
},
|
||||||
|
items: ['presets/intro'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async function listPresets(): Promise<void> {
|
|||||||
const manifest = JSON5.parse(content);
|
const manifest = JSON5.parse(content);
|
||||||
|
|
||||||
// Extract metadata fields from manifest
|
// Extract metadata fields from manifest
|
||||||
const { Providers, Router, PORT, HOST, API_TIMEOUT_MS, PROXY_URL, LOG, LOG_LEVEL, StatusLine, NON_INTERACTIVE_MODE, requiredInputs, ...metadata } = manifest;
|
const { Providers, Router, PORT, HOST, API_TIMEOUT_MS, PROXY_URL, LOG, LOG_LEVEL, StatusLine, NON_INTERACTIVE_MODE, ...metadata } = manifest;
|
||||||
|
|
||||||
const name = metadata.name || dirName;
|
const name = metadata.name || dirName;
|
||||||
const description = metadata.description || '';
|
const description = metadata.description || '';
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ export const createServer = async (config: any): Promise<any> => {
|
|||||||
const manifest = JSON.parse(content);
|
const manifest = JSON.parse(content);
|
||||||
|
|
||||||
// Extract metadata fields
|
// Extract metadata fields
|
||||||
const { Providers, Router, PORT, HOST, API_TIMEOUT_MS, PROXY_URL, LOG, LOG_LEVEL, StatusLine, NON_INTERACTIVE_MODE, requiredInputs, ...metadata } = manifest;
|
const { Providers, Router, PORT, HOST, API_TIMEOUT_MS, PROXY_URL, LOG, LOG_LEVEL, StatusLine, NON_INTERACTIVE_MODE, ...metadata } = manifest;
|
||||||
|
|
||||||
presets.push({
|
presets.push({
|
||||||
id: dirName, // Use directory name as unique identifier
|
id: dirName, // Use directory name as unique identifier
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export interface ExportResult {
|
|||||||
presetDir: string;
|
presetDir: string;
|
||||||
sanitizedConfig: any;
|
sanitizedConfig: any;
|
||||||
metadata: PresetMetadata;
|
metadata: PresetMetadata;
|
||||||
requiredInputs: any[];
|
|
||||||
sanitizedCount: number;
|
sanitizedCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,8 +40,7 @@ export function createManifest(
|
|||||||
presetName: string,
|
presetName: string,
|
||||||
config: any,
|
config: any,
|
||||||
sanitizedConfig: any,
|
sanitizedConfig: any,
|
||||||
options: ExportOptions,
|
options: ExportOptions
|
||||||
requiredInputs: any[] = []
|
|
||||||
): ManifestFile {
|
): ManifestFile {
|
||||||
const metadata: PresetMetadata = {
|
const metadata: PresetMetadata = {
|
||||||
name: presetName,
|
name: presetName,
|
||||||
@@ -55,7 +53,6 @@ export function createManifest(
|
|||||||
return {
|
return {
|
||||||
...metadata,
|
...metadata,
|
||||||
...sanitizedConfig,
|
...sanitizedConfig,
|
||||||
requiredInputs: options.includeSensitive ? undefined : requiredInputs,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,13 +78,12 @@ export async function exportPreset(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 2. Sanitize configuration
|
// 2. Sanitize configuration
|
||||||
const { sanitizedConfig, requiredInputs, sanitizedCount } = await sanitizeConfig(config);
|
const { sanitizedConfig, sanitizedCount } = await sanitizeConfig(config);
|
||||||
|
|
||||||
// 3. Generate manifest.json (flattened structure)
|
// 3. Generate manifest.json (flattened structure)
|
||||||
const manifest: ManifestFile = {
|
const manifest: ManifestFile = {
|
||||||
...metadata,
|
...metadata,
|
||||||
...sanitizedConfig,
|
...sanitizedConfig,
|
||||||
requiredInputs: options.includeSensitive ? undefined : requiredInputs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 4. Create preset directory
|
// 4. Create preset directory
|
||||||
@@ -114,7 +110,6 @@ export async function exportPreset(
|
|||||||
presetDir,
|
presetDir,
|
||||||
sanitizedConfig,
|
sanitizedConfig,
|
||||||
metadata,
|
metadata,
|
||||||
requiredInputs,
|
|
||||||
sanitizedCount,
|
sanitizedCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Sensitive field identification and sanitization functionality
|
* Sensitive field identification and sanitization functionality
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RequiredInput, SanitizeResult } from './types';
|
import { SanitizeResult } from './types';
|
||||||
|
|
||||||
// Sensitive field pattern list
|
// Sensitive field pattern list
|
||||||
const SENSITIVE_PATTERNS = [
|
const SENSITIVE_PATTERNS = [
|
||||||
@@ -88,17 +88,15 @@ function extractEnvVarName(value: string): string | null {
|
|||||||
* Recursively traverse object to identify and sanitize sensitive fields
|
* Recursively traverse object to identify and sanitize sensitive fields
|
||||||
* @param config Configuration object
|
* @param config Configuration object
|
||||||
* @param path Current field path
|
* @param path Current field path
|
||||||
* @param requiredInputs Required inputs array (cumulative)
|
|
||||||
* @param sanitizedCount Sanitized field count
|
* @param sanitizedCount Sanitized field count
|
||||||
*/
|
*/
|
||||||
function sanitizeObject(
|
function sanitizeObject(
|
||||||
config: any,
|
config: any,
|
||||||
path: string = '',
|
path: string = '',
|
||||||
requiredInputs: RequiredInput[] = [],
|
|
||||||
sanitizedCount: number = 0
|
sanitizedCount: number = 0
|
||||||
): { sanitized: any; requiredInputs: RequiredInput[]; count: number } {
|
): { sanitized: any; count: number } {
|
||||||
if (!config || typeof config !== 'object') {
|
if (!config || typeof config !== 'object') {
|
||||||
return { sanitized: config, requiredInputs, count: sanitizedCount };
|
return { sanitized: config, count: sanitizedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(config)) {
|
if (Array.isArray(config)) {
|
||||||
@@ -107,14 +105,12 @@ function sanitizeObject(
|
|||||||
const result = sanitizeObject(
|
const result = sanitizeObject(
|
||||||
config[i],
|
config[i],
|
||||||
path ? `${path}[${i}]` : `[${i}]`,
|
path ? `${path}[${i}]` : `[${i}]`,
|
||||||
requiredInputs,
|
|
||||||
sanitizedCount
|
sanitizedCount
|
||||||
);
|
);
|
||||||
sanitizedArray.push(result.sanitized);
|
sanitizedArray.push(result.sanitized);
|
||||||
requiredInputs = result.requiredInputs;
|
|
||||||
sanitizedCount = result.count;
|
sanitizedCount = result.count;
|
||||||
}
|
}
|
||||||
return { sanitized: sanitizedArray, requiredInputs, count: sanitizedCount };
|
return { sanitized: sanitizedArray, count: sanitizedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizedObj: any = {};
|
const sanitizedObj: any = {};
|
||||||
@@ -126,15 +122,6 @@ function sanitizeObject(
|
|||||||
// If value is already an environment variable, keep unchanged
|
// If value is already an environment variable, keep unchanged
|
||||||
if (isEnvPlaceholder(value)) {
|
if (isEnvPlaceholder(value)) {
|
||||||
sanitizedObj[key] = value;
|
sanitizedObj[key] = value;
|
||||||
// Still need to record as required input, but use existing environment variable
|
|
||||||
const envVarName = extractEnvVarName(value);
|
|
||||||
if (envVarName && !requiredInputs.some(input => input.id === currentPath)) {
|
|
||||||
requiredInputs.push({
|
|
||||||
id: currentPath,
|
|
||||||
prompt: `Enter ${key}`,
|
|
||||||
placeholder: envVarName,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Sanitize: replace with environment variable placeholder
|
// Sanitize: replace with environment variable placeholder
|
||||||
// Try to infer entity name from path
|
// Try to infer entity name from path
|
||||||
@@ -161,20 +148,12 @@ function sanitizeObject(
|
|||||||
const envVarName = generateEnvVarName('global', entityName, key);
|
const envVarName = generateEnvVarName('global', entityName, key);
|
||||||
sanitizedObj[key] = `\${${envVarName}}`;
|
sanitizedObj[key] = `\${${envVarName}}`;
|
||||||
|
|
||||||
// Record as required input
|
|
||||||
requiredInputs.push({
|
|
||||||
id: currentPath,
|
|
||||||
prompt: `Enter ${key}`,
|
|
||||||
placeholder: envVarName,
|
|
||||||
});
|
|
||||||
|
|
||||||
sanitizedCount++;
|
sanitizedCount++;
|
||||||
}
|
}
|
||||||
} else if (typeof value === 'object' && value !== null) {
|
} else if (typeof value === 'object' && value !== null) {
|
||||||
// Recursively process nested objects
|
// Recursively process nested objects
|
||||||
const result = sanitizeObject(value, currentPath, requiredInputs, sanitizedCount);
|
const result = sanitizeObject(value, currentPath, sanitizedCount);
|
||||||
sanitizedObj[key] = result.sanitized;
|
sanitizedObj[key] = result.sanitized;
|
||||||
requiredInputs = result.requiredInputs;
|
|
||||||
sanitizedCount = result.count;
|
sanitizedCount = result.count;
|
||||||
} else {
|
} else {
|
||||||
// Keep original value
|
// Keep original value
|
||||||
@@ -182,7 +161,7 @@ function sanitizeObject(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { sanitized: sanitizedObj, requiredInputs, count: sanitizedCount };
|
return { sanitized: sanitizedObj, count: sanitizedCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,7 +177,6 @@ export async function sanitizeConfig(config: any): Promise<SanitizeResult> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
sanitizedConfig: result.sanitized,
|
sanitizedConfig: result.sanitized,
|
||||||
requiredInputs: result.requiredInputs,
|
|
||||||
sanitizedCount: result.count,
|
sanitizedCount: result.count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,15 @@ export interface PresetConfigSection {
|
|||||||
transformers?: TransformerConfig[];
|
transformers?: TransformerConfig[];
|
||||||
StatusLine?: any;
|
StatusLine?: any;
|
||||||
NON_INTERACTIVE_MODE?: boolean;
|
NON_INTERACTIVE_MODE?: boolean;
|
||||||
|
|
||||||
|
// CLI-only fields (not used by server)
|
||||||
|
noServer?: boolean; // CLI: Whether to skip local server startup and use provider's API directly
|
||||||
|
claudeCodeSettings?: { // CLI: Claude Code specific settings
|
||||||
|
env?: Record<string, any>; // CLI: Environment variables to pass to Claude Code
|
||||||
|
statusLine?: any; // CLI: Status line configuration
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +253,6 @@ export enum MergeStrategy {
|
|||||||
// Sanitization result
|
// Sanitization result
|
||||||
export interface SanitizeResult {
|
export interface SanitizeResult {
|
||||||
sanitizedConfig: any;
|
sanitizedConfig: any;
|
||||||
requiredInputs: RequiredInput[];
|
|
||||||
sanitizedCount: number;
|
sanitizedCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user