This commit is contained in:
musistudio
2026-01-02 22:51:22 +08:00
parent 9309cc8655
commit 1579413f3d
16 changed files with 86 additions and 200 deletions

View File

@@ -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"
}
] ]
} }
``` ```

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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"]
} }

View File

@@ -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:

View File

@@ -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) - 高级预设主题

View File

@@ -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) 了解更多信息。
## 故障排除 ## 故障排除

View File

@@ -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 安装
:::
## 预设文件位置 ## 预设文件位置
预设保存在: 预设保存在:

View File

@@ -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}",

View File

@@ -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'],
},
], ],
}; };

View File

@@ -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 || '';

View File

@@ -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

View File

@@ -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,
}; };
} }

View File

@@ -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,
}; };
} }

View File

@@ -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;
} }