zcf 3.2.3 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -14
- package/dist/chunks/api-providers.mjs +76 -0
- package/dist/chunks/claude-code-config-manager.mjs +21 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +210 -14
- package/dist/chunks/codex-config-switch.mjs +197 -12
- package/dist/chunks/codex-provider-manager.mjs +20 -9
- package/dist/chunks/codex-uninstaller.mjs +19 -24
- package/dist/chunks/commands.mjs +1 -1
- package/dist/chunks/features.mjs +637 -0
- package/dist/chunks/simple-config.mjs +182 -29
- package/dist/cli.mjs +13 -613
- package/dist/i18n/locales/en/api.json +6 -1
- package/dist/i18n/locales/en/codex.json +7 -0
- package/dist/i18n/locales/en/errors.json +1 -0
- package/dist/i18n/locales/en/multi-config.json +8 -1
- package/dist/i18n/locales/zh-CN/api.json +6 -1
- package/dist/i18n/locales/zh-CN/codex.json +7 -0
- package/dist/i18n/locales/zh-CN/errors.json +1 -0
- package/dist/i18n/locales/zh-CN/multi-config.json +8 -1
- package/dist/index.d.mts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +191 -0
- package/templates/claude-code/CLAUDE.md +1 -0
- package/templates/claude-code/en/output-styles/engineer-professional.md +2 -1
- package/templates/claude-code/en/output-styles/laowang-engineer.md +1 -0
- package/templates/claude-code/en/output-styles/nekomata-engineer.md +1 -0
- package/templates/claude-code/en/output-styles/ojousama-engineer.md +1 -0
- package/templates/claude-code/zh-CN/output-styles/engineer-professional.md +2 -1
- package/templates/claude-code/zh-CN/output-styles/laowang-engineer.md +1 -0
- package/templates/claude-code/zh-CN/output-styles/nekomata-engineer.md +1 -0
- package/templates/claude-code/zh-CN/output-styles/ojousama-engineer.md +1 -0
- package/templates/codex/en/system-prompt/engineer-professional.md +2 -1
- package/templates/codex/en/system-prompt/laowang-engineer.md +1 -0
- package/templates/codex/en/system-prompt/nekomata-engineer.md +1 -0
- package/templates/codex/en/system-prompt/ojousama-engineer.md +1 -0
- package/templates/codex/zh-CN/system-prompt/engineer-professional.md +2 -1
- package/templates/codex/zh-CN/system-prompt/laowang-engineer.md +1 -0
- package/templates/codex/zh-CN/system-prompt/nekomata-engineer.md +1 -0
- package/templates/codex/zh-CN/system-prompt/ojousama-engineer.md +1 -0
package/README.md
CHANGED
|
@@ -111,14 +111,17 @@ npx zcf ccr --all-lang zh-CN # Configure CCR in Chinese
|
|
|
111
111
|
For CI/CD and automated setups, use `--skip-prompt` with parameters:
|
|
112
112
|
|
|
113
113
|
```bash
|
|
114
|
-
#
|
|
114
|
+
# Using API provider preset (v3.3.0+ New - Simplified)
|
|
115
|
+
npx zcf i -s -p 302ai -k "sk-xxx"
|
|
116
|
+
|
|
117
|
+
# Shorthand version (traditional)
|
|
115
118
|
npx zcf i -s -g zh-CN -t api_key -k "sk-xxx" -u "https://xxx.xxx"
|
|
116
119
|
|
|
117
|
-
# Complete version
|
|
120
|
+
# Complete version (traditional)
|
|
118
121
|
npx zcf i --skip-prompt --all-lang zh-CN --api-type api_key --api-key "sk-xxx" --api-url "https://xxx.xxx"
|
|
119
122
|
|
|
120
|
-
#
|
|
121
|
-
npx zcf i -s -
|
|
123
|
+
# Using provider preset with custom models
|
|
124
|
+
npx zcf i -s -p 302ai -k "sk-xxx" -M "claude-sonnet-4-5" -F "claude-haiku-4-5"
|
|
122
125
|
|
|
123
126
|
# Complete version (with custom models)
|
|
124
127
|
npx zcf i --skip-prompt \
|
|
@@ -127,8 +130,66 @@ npx zcf i --skip-prompt \
|
|
|
127
130
|
--api-url "https://xxx.xxx" \
|
|
128
131
|
--api-model "claude-sonnet-4-5" \
|
|
129
132
|
--api-fast-model "claude-haiku-4-5"
|
|
133
|
+
|
|
134
|
+
# Multiple API configurations (JSON string)
|
|
135
|
+
npx zcf i -s --api-configs '[
|
|
136
|
+
{"provider":"302ai","key":"sk-xxx"},
|
|
137
|
+
{"provider":"glm","key":"sk-yyy"},
|
|
138
|
+
{"name":"custom","type":"api_key","key":"sk-zzz","url":"https://custom.api.com","primaryModel":"claude-sonnet-4-5","fastModel":"claude-haiku-4-5","default":true}
|
|
139
|
+
]'
|
|
140
|
+
|
|
141
|
+
# Multiple API configurations (JSON file)
|
|
142
|
+
npx zcf i -s --api-configs-file ./api-configs.json
|
|
143
|
+
|
|
144
|
+
# For Codex with multiple providers
|
|
145
|
+
npx zcf i -s -T cx --api-configs '[
|
|
146
|
+
{"provider":"302ai","key":"sk-xxx"},
|
|
147
|
+
{"name":"custom","type":"api_key","key":"sk-yyy","url":"https://custom.api.com","primaryModel":"gpt-5","default":true}
|
|
148
|
+
]'
|
|
130
149
|
```
|
|
131
150
|
|
|
151
|
+
#### 🎯 API Provider Presets (v3.3.0+ New)
|
|
152
|
+
|
|
153
|
+
ZCF now supports API provider presets that automatically configure baseUrl and models, simplifying configuration from 5+ parameters to just 2:
|
|
154
|
+
|
|
155
|
+
**Supported Providers:**
|
|
156
|
+
- `302ai` - [302.AI](https://share.302.ai/gAT9VG) API Service
|
|
157
|
+
- `glm` - GLM (z.ai)
|
|
158
|
+
- `minimax` - MiniMax API Service
|
|
159
|
+
- `kimi` - Kimi (Moonshot AI)
|
|
160
|
+
- `custom` - Custom API endpoint (requires manual URL configuration)
|
|
161
|
+
|
|
162
|
+
**Usage Examples:**
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Using 302.AI provider
|
|
166
|
+
npx zcf i --skip-prompt --provider 302ai --api-key "sk-xxx"
|
|
167
|
+
# or shorthand
|
|
168
|
+
npx zcf i -s -p 302ai -k "sk-xxx"
|
|
169
|
+
|
|
170
|
+
# Using GLM provider
|
|
171
|
+
npx zcf i -s -p glm -k "sk-xxx"
|
|
172
|
+
|
|
173
|
+
# Using MiniMax provider
|
|
174
|
+
npx zcf i -s -p minimax -k "sk-xxx"
|
|
175
|
+
|
|
176
|
+
# Using Kimi provider
|
|
177
|
+
npx zcf i -s -p kimi -k "sk-xxx"
|
|
178
|
+
|
|
179
|
+
# Using custom provider (requires URL)
|
|
180
|
+
npx zcf i -s -p custom -k "sk-xxx" -u "https://api.example.com"
|
|
181
|
+
|
|
182
|
+
# For Codex
|
|
183
|
+
npx zcf i -s -T cx -p 302ai -k "sk-xxx"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Benefits:**
|
|
187
|
+
- ✅ Automatic baseUrl configuration
|
|
188
|
+
- ✅ Automatic authType selection
|
|
189
|
+
- ✅ Automatic model configuration (if available)
|
|
190
|
+
- ✅ Reduces configuration from 5+ parameters to 2
|
|
191
|
+
- ✅ Supports both Claude Code and Codex
|
|
192
|
+
|
|
132
193
|
#### Non-interactive Mode Parameters
|
|
133
194
|
|
|
134
195
|
When using `--skip-prompt`, the following parameters are available:
|
|
@@ -136,21 +197,25 @@ When using `--skip-prompt`, the following parameters are available:
|
|
|
136
197
|
| Parameter | Description | Values | Required | Default |
|
|
137
198
|
| ---------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
138
199
|
| `--skip-prompt, -s` | Skip all interactive prompts | - | Yes (for non-interactive mode) | - |
|
|
200
|
+
| `--provider, -p` | API provider preset (v3.3.0+ New) | `302ai`, `glm`, `minimax`, `kimi`, `custom` | No | - (Simplifies configuration by auto-filling baseUrl and models) |
|
|
139
201
|
| `--lang, -l` | ZCF display language (applies to all commands) | `zh-CN`, `en` | No | `en` or user's saved preference |
|
|
140
202
|
| `--config-lang, -c` | Configuration language (template files language) | `zh-CN`, `en` | No | `en` |
|
|
141
203
|
| `--ai-output-lang, -a` | AI output language | `zh-CN`, `en`, custom string | No | `en` |
|
|
142
204
|
| `--all-lang, -g` | Set all language parameters (applies to all commands) | `zh-CN`, `en`, custom string | No | - (Priority: `--all-lang` > `--lang` > saved user preference > interactive prompt. Custom string sets AI output language to custom while interaction and config languages remain 'en') |
|
|
143
205
|
| `--config-action, -r` | Config handling | `new`, `backup`, `merge`, `docs-only`, `skip` | No | `backup` |
|
|
144
|
-
| `--api-type, -t` | API configuration type | `auth_token`, `api_key`, `ccr_proxy`, `skip` | No | `skip`
|
|
206
|
+
| `--api-type, -t` | API configuration type | `auth_token`, `api_key`, `ccr_proxy`, `skip` | No | `skip` (auto-set to `api_key` when `--provider` is specified) |
|
|
145
207
|
| `--api-key, -k` | API key (for both API key and auth token types) | string | Required when `api-type` is not `skip` | - |
|
|
146
|
-
| `--api-url, -u` | Custom API URL | URL string | No | official API
|
|
147
|
-
| `--api-model, -M` | Primary API model | string (e.g., `claude-sonnet-4-5`) | No | -
|
|
148
|
-
| `--api-fast-model, -F` | Fast API model (Claude Code only) | string (e.g., `claude-haiku-4-5`) | No | -
|
|
208
|
+
| `--api-url, -u` | Custom API URL | URL string | No | official API (auto-filled when using `--provider`) |
|
|
209
|
+
| `--api-model, -M` | Primary API model | string (e.g., `claude-sonnet-4-5`) | No | - (auto-filled when using `--provider` if available) |
|
|
210
|
+
| `--api-fast-model, -F` | Fast API model (Claude Code only) | string (e.g., `claude-haiku-4-5`) | No | - (auto-filled when using `--provider` if available) |
|
|
149
211
|
| `--mcp-services, -m` | MCP services to install (multi-select, comma-separated) | `context7`, `open-websearch`, `spec-workflow`, `mcp-deepwiki`, `Playwright`, `exa`, `serena`, or `skip` for none | No | `all` |
|
|
150
212
|
| `--workflows, -w` | Workflows to install (multi-select, comma-separated) | `commonTools`, `sixStepsWorkflow`, `featPlanUx`, `gitWorkflow`, `bmadWorkflow`, or `skip` for none | No | `all` |
|
|
151
213
|
| `--output-styles, -o` | Output styles to install (multi-select, comma-separated) | `engineer-professional`, `nekomata-engineer`, `laowang-engineer`, `ojousama-engineer`, or `skip` for none | No | `all` |
|
|
152
214
|
| `--default-output-style, -d` | Default output style | Same as output styles plus built-in: `default`, `explanatory`, `learning` | No | `engineer-professional` |
|
|
153
215
|
| `--install-cometix-line, -x` | Install CCometixLine statusline tool | `true`, `false` | No | `true` |
|
|
216
|
+
| `--code-type, -T` | Target code tool type | `claude-code`, `codex`, `cc`, `cx` | No | Current active tool type from ZCF config |
|
|
217
|
+
| `--api-configs` | Multiple API configurations (JSON string) | JSON array string of API configuration objects | No | - (Mutually exclusive with `--api-configs-file`) |
|
|
218
|
+
| `--api-configs-file` | Multiple API configurations (JSON file path) | Path to JSON file containing API configuration array | No | - (Mutually exclusive with `--api-configs`) |
|
|
154
219
|
|
|
155
220
|
#### 🤖 Codex Support (v3.0.0+ New)
|
|
156
221
|
|
|
@@ -402,12 +467,35 @@ After configuration:
|
|
|
402
467
|
|
|
403
468
|
### 🔐 API Configuration
|
|
404
469
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
-
|
|
470
|
+
ZCF provides flexible API configuration options for both Claude Code and Codex:
|
|
471
|
+
|
|
472
|
+
**Quick Setup with API Provider Presets (v3.3.0+ New):**
|
|
473
|
+
|
|
474
|
+
Choose from popular API providers with pre-configured settings:
|
|
475
|
+
- **302.AI** - Pay-as-you-go AI service with comprehensive model support
|
|
476
|
+
- **GLM (智谱AI)** - Zhipu AI's GLM models
|
|
477
|
+
- **MiniMax** - MiniMax AI service
|
|
478
|
+
- **Kimi (Moonshot AI)** - Moonshot's Kimi models
|
|
479
|
+
- **Custom Configuration** - Full manual configuration for any provider
|
|
480
|
+
|
|
481
|
+
When using a preset provider, you only need to:
|
|
482
|
+
1. Select the provider from the list
|
|
483
|
+
2. Enter your API key
|
|
484
|
+
|
|
485
|
+
The system automatically configures:
|
|
486
|
+
- API base URL
|
|
487
|
+
- Authentication type (API Key or Auth Token)
|
|
488
|
+
- Default models (if applicable)
|
|
489
|
+
- Wire API protocol (for Codex)
|
|
490
|
+
|
|
491
|
+
**Traditional Configuration Methods:**
|
|
492
|
+
|
|
493
|
+
- **Official Login**: Use official authentication system (no API configuration needed)
|
|
494
|
+
- **Auth Token**: For tokens obtained via OAuth or browser login
|
|
495
|
+
- **API Key**: For API keys from Anthropic Console or custom providers
|
|
496
|
+
- **CCR Proxy**: Configure Claude Code Router proxy
|
|
497
|
+
- **Custom API URL**: Support for any compatible API endpoint
|
|
498
|
+
- **Partial Modification**: Update only needed configuration items (v2.0+)
|
|
411
499
|
|
|
412
500
|
### 💾 Configuration Management
|
|
413
501
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const API_PROVIDER_PRESETS = [
|
|
2
|
+
{
|
|
3
|
+
id: "302ai",
|
|
4
|
+
name: "302.AI",
|
|
5
|
+
supportedCodeTools: ["claude-code", "codex"],
|
|
6
|
+
claudeCode: {
|
|
7
|
+
baseUrl: "https://api.302.ai/cc",
|
|
8
|
+
authType: "api_key"
|
|
9
|
+
},
|
|
10
|
+
codex: {
|
|
11
|
+
baseUrl: "https://api.302.ai/v1",
|
|
12
|
+
wireApi: "responses"
|
|
13
|
+
},
|
|
14
|
+
description: "302.AI API Service"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "glm",
|
|
18
|
+
name: "GLM",
|
|
19
|
+
supportedCodeTools: ["claude-code", "codex"],
|
|
20
|
+
claudeCode: {
|
|
21
|
+
baseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
22
|
+
authType: "auth_token"
|
|
23
|
+
},
|
|
24
|
+
codex: {
|
|
25
|
+
baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
26
|
+
wireApi: "chat",
|
|
27
|
+
defaultModel: "GLM-4.6"
|
|
28
|
+
},
|
|
29
|
+
description: "GLM (\u667A\u8C31AI)"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "minimax",
|
|
33
|
+
name: "MiniMax",
|
|
34
|
+
supportedCodeTools: ["claude-code", "codex"],
|
|
35
|
+
claudeCode: {
|
|
36
|
+
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
37
|
+
authType: "auth_token",
|
|
38
|
+
defaultModels: ["MiniMax-M2", "MiniMax-M2"]
|
|
39
|
+
},
|
|
40
|
+
codex: {
|
|
41
|
+
baseUrl: "https://api.minimaxi.com/v1",
|
|
42
|
+
wireApi: "chat",
|
|
43
|
+
defaultModel: "MiniMax-M2"
|
|
44
|
+
},
|
|
45
|
+
description: "MiniMax API Service"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "kimi",
|
|
49
|
+
name: "Kimi",
|
|
50
|
+
supportedCodeTools: ["claude-code", "codex"],
|
|
51
|
+
claudeCode: {
|
|
52
|
+
baseUrl: "https://api.moonshot.cn/anthropic",
|
|
53
|
+
authType: "auth_token",
|
|
54
|
+
defaultModels: ["kimi-k2-0905-preview", "kimi-k2-turbo-preview"]
|
|
55
|
+
},
|
|
56
|
+
codex: {
|
|
57
|
+
baseUrl: "https://api.moonshot.cn/v1",
|
|
58
|
+
wireApi: "chat",
|
|
59
|
+
defaultModel: "kimi-k2-0905-preview"
|
|
60
|
+
},
|
|
61
|
+
description: "Kimi (Moonshot AI)"
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
function getApiProviders(codeToolType) {
|
|
65
|
+
return API_PROVIDER_PRESETS.filter(
|
|
66
|
+
(provider) => provider.supportedCodeTools.includes(codeToolType)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function getProviderPreset(providerId) {
|
|
70
|
+
return API_PROVIDER_PRESETS.find((provider) => provider.id === providerId);
|
|
71
|
+
}
|
|
72
|
+
function getValidProviderIds() {
|
|
73
|
+
return API_PROVIDER_PRESETS.map((provider) => provider.id);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { API_PROVIDER_PRESETS, getApiProviders, getProviderPreset, getValidProviderIds };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import { join } from 'pathe';
|
|
3
|
-
import {
|
|
3
|
+
import { q as ZCF_CONFIG_FILE, Z as ZCF_CONFIG_DIR, ae as ensureDir, af as readDefaultTomlConfig, ag as createDefaultTomlConfig, ah as exists, ai as readJsonConfig, aj as writeTomlConfig, S as SETTINGS_FILE, ak as copyFile } from './simple-config.mjs';
|
|
4
4
|
import 'node:fs';
|
|
5
5
|
import 'node:process';
|
|
6
6
|
import 'ansis';
|
|
@@ -182,15 +182,15 @@ class ClaudeCodeConfigManager {
|
|
|
182
182
|
* Apply profile settings to Claude Code runtime
|
|
183
183
|
*/
|
|
184
184
|
static async applyProfileSettings(profile) {
|
|
185
|
-
const { ensureI18nInitialized, i18n } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
185
|
+
const { ensureI18nInitialized, i18n } = await import('./simple-config.mjs').then(function (n) { return n.b4; });
|
|
186
186
|
ensureI18nInitialized();
|
|
187
187
|
try {
|
|
188
188
|
if (!profile) {
|
|
189
|
-
const { switchToOfficialLogin } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
189
|
+
const { switchToOfficialLogin } = await import('./simple-config.mjs').then(function (n) { return n.b8; });
|
|
190
190
|
switchToOfficialLogin();
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
|
-
const { readJsonConfig: readJsonConfig2, writeJsonConfig } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
193
|
+
const { readJsonConfig: readJsonConfig2, writeJsonConfig } = await import('./simple-config.mjs').then(function (n) { return n.b6; });
|
|
194
194
|
const settings = readJsonConfig2(SETTINGS_FILE) || {};
|
|
195
195
|
if (!settings.env)
|
|
196
196
|
settings.env = {};
|
|
@@ -202,7 +202,7 @@ class ClaudeCodeConfigManager {
|
|
|
202
202
|
settings.env.ANTHROPIC_AUTH_TOKEN = profile.apiKey;
|
|
203
203
|
delete settings.env.ANTHROPIC_API_KEY;
|
|
204
204
|
} else if (profile.authType === "ccr_proxy") {
|
|
205
|
-
const { readCcrConfig } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
205
|
+
const { readCcrConfig } = await import('./simple-config.mjs').then(function (n) { return n.b9; });
|
|
206
206
|
const ccrConfig = readCcrConfig();
|
|
207
207
|
if (!ccrConfig) {
|
|
208
208
|
throw new Error(i18n.t("ccr:ccrNotConfigured") || "CCR proxy configuration not found");
|
|
@@ -221,8 +221,18 @@ class ClaudeCodeConfigManager {
|
|
|
221
221
|
else
|
|
222
222
|
delete settings.env.ANTHROPIC_BASE_URL;
|
|
223
223
|
}
|
|
224
|
+
if (profile.primaryModel) {
|
|
225
|
+
settings.env.ANTHROPIC_MODEL = profile.primaryModel;
|
|
226
|
+
} else {
|
|
227
|
+
delete settings.env.ANTHROPIC_MODEL;
|
|
228
|
+
}
|
|
229
|
+
if (profile.fastModel) {
|
|
230
|
+
settings.env.ANTHROPIC_SMALL_FAST_MODEL = profile.fastModel;
|
|
231
|
+
} else {
|
|
232
|
+
delete settings.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
233
|
+
}
|
|
224
234
|
writeJsonConfig(SETTINGS_FILE, settings);
|
|
225
|
-
const { setPrimaryApiKey, addCompletedOnboarding } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
235
|
+
const { setPrimaryApiKey, addCompletedOnboarding } = await import('./simple-config.mjs').then(function (n) { return n.b7; });
|
|
226
236
|
setPrimaryApiKey();
|
|
227
237
|
addCompletedOnboarding();
|
|
228
238
|
if (shouldRestartCcr) {
|
|
@@ -250,6 +260,10 @@ class ClaudeCodeConfigManager {
|
|
|
250
260
|
sanitized.apiKey = profile.apiKey;
|
|
251
261
|
if (profile.baseUrl)
|
|
252
262
|
sanitized.baseUrl = profile.baseUrl;
|
|
263
|
+
if (profile.primaryModel)
|
|
264
|
+
sanitized.primaryModel = profile.primaryModel;
|
|
265
|
+
if (profile.fastModel)
|
|
266
|
+
sanitized.fastModel = profile.fastModel;
|
|
253
267
|
return sanitized;
|
|
254
268
|
}
|
|
255
269
|
/**
|
|
@@ -575,7 +589,7 @@ class ClaudeCodeConfigManager {
|
|
|
575
589
|
*/
|
|
576
590
|
static async syncCcrProfile() {
|
|
577
591
|
try {
|
|
578
|
-
const { readCcrConfig } = await import('./simple-config.mjs').then(function (n) { return n.
|
|
592
|
+
const { readCcrConfig } = await import('./simple-config.mjs').then(function (n) { return n.b9; });
|
|
579
593
|
const ccrConfig = readCcrConfig();
|
|
580
594
|
if (!ccrConfig) {
|
|
581
595
|
await this.ensureCcrProfileExists(ccrConfig);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ansis from 'ansis';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import {
|
|
3
|
+
import { aa as ensureI18nInitialized, ab as i18n, ac as addNumbersToChoices, ad as validateApiKey } from './simple-config.mjs';
|
|
4
4
|
import { ClaudeCodeConfigManager } from './claude-code-config-manager.mjs';
|
|
5
5
|
import 'node:fs';
|
|
6
6
|
import 'node:process';
|
|
@@ -48,6 +48,7 @@ async function configureIncrementalManagement() {
|
|
|
48
48
|
const choices = [
|
|
49
49
|
{ name: i18n.t("multi-config:addProfile"), value: "add" },
|
|
50
50
|
{ name: i18n.t("multi-config:editProfile"), value: "edit" },
|
|
51
|
+
{ name: i18n.t("multi-config:copyProfile"), value: "copy" },
|
|
51
52
|
{ name: i18n.t("multi-config:deleteProfile"), value: "delete" },
|
|
52
53
|
{ name: i18n.t("common:skip"), value: "skip" }
|
|
53
54
|
];
|
|
@@ -68,6 +69,9 @@ async function configureIncrementalManagement() {
|
|
|
68
69
|
case "edit":
|
|
69
70
|
await handleEditProfile(profiles);
|
|
70
71
|
break;
|
|
72
|
+
case "copy":
|
|
73
|
+
await handleCopyProfile(profiles);
|
|
74
|
+
break;
|
|
71
75
|
case "delete":
|
|
72
76
|
await handleDeleteProfile(profiles);
|
|
73
77
|
break;
|
|
@@ -85,17 +89,40 @@ async function promptContinueAdding() {
|
|
|
85
89
|
async function handleAddProfile() {
|
|
86
90
|
console.log(ansis.cyan(`
|
|
87
91
|
${i18n.t("multi-config:addingNewProfile")}`));
|
|
92
|
+
const { getApiProviders } = await import('./api-providers.mjs');
|
|
93
|
+
const providers = getApiProviders("claude-code");
|
|
94
|
+
const providerChoices = [
|
|
95
|
+
{ name: i18n.t("api:customProvider"), value: "custom" },
|
|
96
|
+
...providers.map((p) => ({ name: p.name, value: p.id }))
|
|
97
|
+
];
|
|
98
|
+
const { selectedProvider } = await inquirer.prompt([{
|
|
99
|
+
type: "list",
|
|
100
|
+
name: "selectedProvider",
|
|
101
|
+
message: i18n.t("api:selectApiProvider"),
|
|
102
|
+
choices: addNumbersToChoices(providerChoices)
|
|
103
|
+
}]);
|
|
104
|
+
let prefilledBaseUrl;
|
|
105
|
+
let prefilledAuthType;
|
|
106
|
+
if (selectedProvider !== "custom") {
|
|
107
|
+
const provider = providers.find((p) => p.id === selectedProvider);
|
|
108
|
+
if (provider?.claudeCode) {
|
|
109
|
+
prefilledBaseUrl = provider.claudeCode.baseUrl;
|
|
110
|
+
prefilledAuthType = provider.claudeCode.authType;
|
|
111
|
+
console.log(ansis.gray(i18n.t("api:providerSelected", { name: provider.name })));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
88
114
|
const answers = await inquirer.prompt([
|
|
89
115
|
{
|
|
90
116
|
type: "input",
|
|
91
117
|
name: "profileName",
|
|
92
118
|
message: i18n.t("multi-config:profileNamePrompt"),
|
|
119
|
+
default: selectedProvider !== "custom" ? providers.find((p) => p.id === selectedProvider)?.name : void 0,
|
|
93
120
|
validate: (input) => {
|
|
94
121
|
const trimmed = input.trim();
|
|
95
122
|
if (!trimmed) {
|
|
96
123
|
return i18n.t("multi-config:profileNameRequired");
|
|
97
124
|
}
|
|
98
|
-
if (!/^[\w\-\s
|
|
125
|
+
if (!/^[\w\-\s.\u4E00-\u9FA5]+$/.test(trimmed)) {
|
|
99
126
|
return i18n.t("multi-config:profileNameInvalid");
|
|
100
127
|
}
|
|
101
128
|
return true;
|
|
@@ -109,14 +136,16 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
109
136
|
{ name: i18n.t("multi-config:authType.api_key"), value: "api_key" },
|
|
110
137
|
{ name: i18n.t("multi-config:authType.auth_token"), value: "auth_token" }
|
|
111
138
|
],
|
|
112
|
-
default: "api_key"
|
|
139
|
+
default: prefilledAuthType || "api_key",
|
|
140
|
+
when: () => selectedProvider === "custom"
|
|
141
|
+
// Only ask if custom
|
|
113
142
|
},
|
|
114
143
|
{
|
|
115
144
|
type: "input",
|
|
116
145
|
name: "baseUrl",
|
|
117
146
|
message: i18n.t("multi-config:baseUrlPrompt"),
|
|
118
|
-
default: "https://api.anthropic.com",
|
|
119
|
-
when: (answers2) => answers2.authType !== "ccr_proxy",
|
|
147
|
+
default: prefilledBaseUrl || "https://api.anthropic.com",
|
|
148
|
+
when: (answers2) => selectedProvider === "custom" && answers2.authType !== "ccr_proxy",
|
|
120
149
|
validate: (input) => {
|
|
121
150
|
const trimmed = input.trim();
|
|
122
151
|
if (!trimmed) {
|
|
@@ -133,8 +162,8 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
133
162
|
{
|
|
134
163
|
type: "input",
|
|
135
164
|
name: "apiKey",
|
|
136
|
-
message: i18n.t("multi-config:apiKeyPrompt"),
|
|
137
|
-
when: (answers2) => answers2.authType !== "ccr_proxy",
|
|
165
|
+
message: selectedProvider !== "custom" ? i18n.t("api:enterProviderApiKey", { provider: providers.find((p) => p.id === selectedProvider)?.name || selectedProvider }) : i18n.t("multi-config:apiKeyPrompt"),
|
|
166
|
+
when: (answers2) => selectedProvider === "custom" ? answers2.authType !== "ccr_proxy" : true,
|
|
138
167
|
validate: (input) => {
|
|
139
168
|
const trimmed = input.trim();
|
|
140
169
|
if (!trimmed) {
|
|
@@ -146,7 +175,14 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
146
175
|
}
|
|
147
176
|
return true;
|
|
148
177
|
}
|
|
149
|
-
}
|
|
178
|
+
}
|
|
179
|
+
]);
|
|
180
|
+
let modelConfig = null;
|
|
181
|
+
if (selectedProvider === "custom") {
|
|
182
|
+
const { promptCustomModels } = await import('./features.mjs');
|
|
183
|
+
modelConfig = await promptCustomModels();
|
|
184
|
+
}
|
|
185
|
+
const { setAsDefault } = await inquirer.prompt([
|
|
150
186
|
{
|
|
151
187
|
type: "confirm",
|
|
152
188
|
name: "setAsDefault",
|
|
@@ -159,11 +195,19 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
159
195
|
const profile = {
|
|
160
196
|
id: profileId,
|
|
161
197
|
name: profileName,
|
|
162
|
-
authType: answers.authType
|
|
198
|
+
authType: selectedProvider === "custom" ? answers.authType : prefilledAuthType
|
|
163
199
|
};
|
|
164
|
-
if (
|
|
200
|
+
if (profile.authType !== "ccr_proxy") {
|
|
165
201
|
profile.apiKey = answers.apiKey.trim();
|
|
166
|
-
profile.baseUrl = answers.baseUrl.trim();
|
|
202
|
+
profile.baseUrl = selectedProvider === "custom" ? answers.baseUrl.trim() : prefilledBaseUrl;
|
|
203
|
+
}
|
|
204
|
+
if (modelConfig) {
|
|
205
|
+
if (modelConfig.primaryModel.trim()) {
|
|
206
|
+
profile.primaryModel = modelConfig.primaryModel.trim();
|
|
207
|
+
}
|
|
208
|
+
if (modelConfig.fastModel.trim()) {
|
|
209
|
+
profile.fastModel = modelConfig.fastModel.trim();
|
|
210
|
+
}
|
|
167
211
|
}
|
|
168
212
|
const existingProfile = ClaudeCodeConfigManager.getProfileByName(profile.name);
|
|
169
213
|
if (existingProfile) {
|
|
@@ -188,14 +232,16 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
188
232
|
name: profile.name,
|
|
189
233
|
authType: profile.authType,
|
|
190
234
|
apiKey: profile.apiKey,
|
|
191
|
-
baseUrl: profile.baseUrl
|
|
235
|
+
baseUrl: profile.baseUrl,
|
|
236
|
+
primaryModel: profile.primaryModel,
|
|
237
|
+
fastModel: profile.fastModel
|
|
192
238
|
});
|
|
193
239
|
if (updateResult.success) {
|
|
194
240
|
console.log(ansis.green(i18n.t("multi-config:profileUpdated", { name: profile.name })));
|
|
195
241
|
if (updateResult.backupPath) {
|
|
196
242
|
console.log(ansis.gray(i18n.t("common:backupCreated", { path: updateResult.backupPath })));
|
|
197
243
|
}
|
|
198
|
-
if (
|
|
244
|
+
if (setAsDefault) {
|
|
199
245
|
const switchResult = await ClaudeCodeConfigManager.switchProfile(existingProfile.id);
|
|
200
246
|
if (switchResult.success) {
|
|
201
247
|
console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: profile.name })));
|
|
@@ -213,7 +259,7 @@ ${i18n.t("multi-config:addingNewProfile")}`));
|
|
|
213
259
|
if (result.backupPath) {
|
|
214
260
|
console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
|
|
215
261
|
}
|
|
216
|
-
if (
|
|
262
|
+
if (setAsDefault) {
|
|
217
263
|
const switchResult = await ClaudeCodeConfigManager.switchProfile(runtimeProfile.id);
|
|
218
264
|
if (switchResult.success) {
|
|
219
265
|
console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: runtimeProfile.name })));
|
|
@@ -306,12 +352,28 @@ ${i18n.t("multi-config:editingProfile", { name: selectedProfile.name })}`));
|
|
|
306
352
|
}
|
|
307
353
|
}
|
|
308
354
|
]);
|
|
355
|
+
let modelConfig = null;
|
|
356
|
+
if (selectedProfile.authType !== "ccr_proxy") {
|
|
357
|
+
const { promptCustomModels } = await import('./features.mjs');
|
|
358
|
+
modelConfig = await promptCustomModels(
|
|
359
|
+
selectedProfile.primaryModel,
|
|
360
|
+
selectedProfile.fastModel
|
|
361
|
+
);
|
|
362
|
+
}
|
|
309
363
|
const updateData = {
|
|
310
364
|
name: answers.profileName.trim()
|
|
311
365
|
};
|
|
312
366
|
if (selectedProfile.authType !== "ccr_proxy") {
|
|
313
367
|
updateData.apiKey = answers.apiKey.trim();
|
|
314
368
|
updateData.baseUrl = answers.baseUrl.trim();
|
|
369
|
+
if (modelConfig) {
|
|
370
|
+
if (modelConfig.primaryModel.trim()) {
|
|
371
|
+
updateData.primaryModel = modelConfig.primaryModel.trim();
|
|
372
|
+
}
|
|
373
|
+
if (modelConfig.fastModel.trim()) {
|
|
374
|
+
updateData.fastModel = modelConfig.fastModel.trim();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
315
377
|
}
|
|
316
378
|
const result = await ClaudeCodeConfigManager.updateProfile(selectedProfile.id, updateData);
|
|
317
379
|
if (result.success) {
|
|
@@ -331,6 +393,140 @@ ${i18n.t("multi-config:editingProfile", { name: selectedProfile.name })}`));
|
|
|
331
393
|
console.log(ansis.red(i18n.t("multi-config:profileUpdateFailed", { error: result.error })));
|
|
332
394
|
}
|
|
333
395
|
}
|
|
396
|
+
async function handleCopyProfile(profiles) {
|
|
397
|
+
const choices = profiles.map((profile) => ({
|
|
398
|
+
name: `${profile.name} (${getAuthTypeLabel(profile.authType)})`,
|
|
399
|
+
value: profile.id
|
|
400
|
+
}));
|
|
401
|
+
const { selectedProfileId } = await inquirer.prompt([{
|
|
402
|
+
type: "list",
|
|
403
|
+
name: "selectedProfileId",
|
|
404
|
+
message: i18n.t("multi-config:selectProfileToCopy"),
|
|
405
|
+
choices: addNumbersToChoices(choices)
|
|
406
|
+
}]);
|
|
407
|
+
if (!selectedProfileId) {
|
|
408
|
+
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const selectedProfile = profiles.find((p) => p.id === selectedProfileId);
|
|
412
|
+
if (!selectedProfile) {
|
|
413
|
+
console.log(ansis.red(i18n.t("multi-config:profileNotFound")));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
console.log(ansis.cyan(`
|
|
417
|
+
${i18n.t("multi-config:copyingProfile", { name: selectedProfile.name })}`));
|
|
418
|
+
const copiedName = `${selectedProfile.name}-copy`;
|
|
419
|
+
const questions = [
|
|
420
|
+
{
|
|
421
|
+
type: "input",
|
|
422
|
+
name: "profileName",
|
|
423
|
+
message: i18n.t("multi-config:profileNamePrompt"),
|
|
424
|
+
default: copiedName,
|
|
425
|
+
validate: (input) => {
|
|
426
|
+
const trimmed = input.trim();
|
|
427
|
+
if (!trimmed) {
|
|
428
|
+
return i18n.t("multi-config:profileNameRequired");
|
|
429
|
+
}
|
|
430
|
+
if (!/^[\w\-\s.\u4E00-\u9FA5]+$/.test(trimmed)) {
|
|
431
|
+
return i18n.t("multi-config:profileNameInvalid");
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
];
|
|
437
|
+
if (selectedProfile.authType !== "ccr_proxy") {
|
|
438
|
+
questions.push(
|
|
439
|
+
{
|
|
440
|
+
type: "input",
|
|
441
|
+
name: "baseUrl",
|
|
442
|
+
message: i18n.t("multi-config:baseUrlPrompt"),
|
|
443
|
+
default: selectedProfile.baseUrl || "https://api.anthropic.com",
|
|
444
|
+
validate: (input) => {
|
|
445
|
+
const trimmed = input.trim();
|
|
446
|
+
if (!trimmed) {
|
|
447
|
+
return i18n.t("multi-config:baseUrlRequired");
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
new URL(trimmed);
|
|
451
|
+
return true;
|
|
452
|
+
} catch {
|
|
453
|
+
return i18n.t("multi-config:baseUrlInvalid");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
type: "input",
|
|
459
|
+
name: "apiKey",
|
|
460
|
+
message: i18n.t("multi-config:apiKeyPrompt"),
|
|
461
|
+
default: selectedProfile.apiKey,
|
|
462
|
+
validate: (input) => {
|
|
463
|
+
const trimmed = input.trim();
|
|
464
|
+
if (!trimmed) {
|
|
465
|
+
return i18n.t("multi-config:apiKeyRequired");
|
|
466
|
+
}
|
|
467
|
+
const validation = validateApiKey(trimmed);
|
|
468
|
+
if (!validation.isValid) {
|
|
469
|
+
return validation.error || "Invalid API key format";
|
|
470
|
+
}
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
const answers = await inquirer.prompt(questions);
|
|
477
|
+
let modelConfig = null;
|
|
478
|
+
if (selectedProfile.authType !== "ccr_proxy") {
|
|
479
|
+
const { promptCustomModels } = await import('./features.mjs');
|
|
480
|
+
modelConfig = await promptCustomModels(
|
|
481
|
+
selectedProfile.primaryModel,
|
|
482
|
+
selectedProfile.fastModel
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
const { setAsDefault } = await inquirer.prompt([
|
|
486
|
+
{
|
|
487
|
+
type: "confirm",
|
|
488
|
+
name: "setAsDefault",
|
|
489
|
+
message: i18n.t("multi-config:setAsDefaultPrompt"),
|
|
490
|
+
default: false
|
|
491
|
+
}
|
|
492
|
+
]);
|
|
493
|
+
const profileName = answers.profileName.trim();
|
|
494
|
+
const profileId = ClaudeCodeConfigManager.generateProfileId(profileName);
|
|
495
|
+
const copiedProfile = {
|
|
496
|
+
id: profileId,
|
|
497
|
+
name: profileName,
|
|
498
|
+
authType: selectedProfile.authType
|
|
499
|
+
};
|
|
500
|
+
if (selectedProfile.authType !== "ccr_proxy") {
|
|
501
|
+
copiedProfile.apiKey = answers.apiKey.trim();
|
|
502
|
+
copiedProfile.baseUrl = answers.baseUrl.trim();
|
|
503
|
+
if (modelConfig) {
|
|
504
|
+
if (modelConfig.primaryModel.trim()) {
|
|
505
|
+
copiedProfile.primaryModel = modelConfig.primaryModel.trim();
|
|
506
|
+
}
|
|
507
|
+
if (modelConfig.fastModel.trim()) {
|
|
508
|
+
copiedProfile.fastModel = modelConfig.fastModel.trim();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const result = await ClaudeCodeConfigManager.addProfile(copiedProfile);
|
|
513
|
+
if (result.success) {
|
|
514
|
+
const runtimeProfile = result.addedProfile || { ...copiedProfile, id: profileId };
|
|
515
|
+
console.log(ansis.green(i18n.t("multi-config:profileCopied", { name: runtimeProfile.name })));
|
|
516
|
+
if (result.backupPath) {
|
|
517
|
+
console.log(ansis.gray(i18n.t("common:backupCreated", { path: result.backupPath })));
|
|
518
|
+
}
|
|
519
|
+
if (setAsDefault) {
|
|
520
|
+
const switchResult = await ClaudeCodeConfigManager.switchProfile(runtimeProfile.id);
|
|
521
|
+
if (switchResult.success) {
|
|
522
|
+
console.log(ansis.green(i18n.t("multi-config:profileSetAsDefault", { name: runtimeProfile.name })));
|
|
523
|
+
await ClaudeCodeConfigManager.applyProfileSettings(runtimeProfile);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
console.log(ansis.red(i18n.t("multi-config:profileCopyFailed", { error: result.error })));
|
|
528
|
+
}
|
|
529
|
+
}
|
|
334
530
|
async function handleDeleteProfile(profiles) {
|
|
335
531
|
if (profiles.length <= 1) {
|
|
336
532
|
console.log(ansis.yellow(i18n.t("multi-config:cannotDeleteLast")));
|