cckit 0.2.0 → 0.3.0

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 CHANGED
@@ -1,12 +1,12 @@
1
1
  # cckit (Node.js Edition)
2
2
 
3
- Code Kit for Claude Model Switching - Support 智谱LLM, MiniMax, Kimi, Kuaishou StreamLake, ZenMux.ai, and official Claude
3
+ Code Kit for Claude Model Switching - Support 智谱 Coding Plan, MiniMax Coding Plan, Kimi Coding Plan, 快手 StreamLake Coding Plan, 火山引擎 Coding Plan, ZenMux.ai, and official Claude
4
4
 
5
5
  A CLI tool for managing and switching between different Claude model providers and their configurations. Features support for model gateways like ZenMux.ai and Kuaishou StreamLake that provide unified access to multiple AI providers.
6
6
 
7
7
  ## Features
8
8
 
9
- - **Multiple Provider Support**: Configure and switch between 智谱LLM, MiniMax, Kimi, Kuaishou StreamLake, 火山方舟(Volcengine), 阿里云(Aliyun), ZenMux.ai, official Claude, and custom providers
9
+ - **Multiple Provider Support**: Configure and switch between 智谱 Coding Plan, MiniMax Coding Plan, Kimi Coding Plan, 快手 StreamLake Coding Plan, 火山引擎 Coding Plan, 阿里云 Coding Plan, 腾讯云 Coding Plan, 讯飞 Coding Plan, ZenMux.ai, official Claude, and custom providers
10
10
  - **Easy Configuration**: Simple command-line interface for managing API keys and settings
11
11
  - **Claude Integration**: Automatically updates Claude Code configuration files
12
12
  - **Export/Import**: Backup and restore your provider configurations
@@ -26,6 +26,129 @@ pnpm add -g cckit
26
26
  yarn global add cckit
27
27
  ```
28
28
 
29
+ ## Quick Start (Interactive Mode)
30
+
31
+ ```bash
32
+ # Start interactive configuration wizard (recommended for first-time users)
33
+ cckit interactive
34
+ ```
35
+
36
+ ## Interactive Mode Details
37
+
38
+ The interactive wizard (`cckit interactive`) provides a user-friendly, step-by-step configuration experience with arrow key selection:
39
+
40
+ ### Step-by-Step Guide
41
+
42
+ ```
43
+ ▸ Step 1: 选择要配置的 AI 提供商
44
+ ```
45
+ Use ↑/↓ arrow keys to select from:
46
+ - **Preset Providers**: 智谱 Coding Plan, MiniMax Coding Plan, Kimi Coding Plan, ZenMux, 快手 StreamLake Coding Plan, 火山引擎 Coding Plan, 阿里云 Coding Plan, 腾讯云 Coding Plan, 讯飞 Coding Plan, Claude
47
+ - **Custom Provider**: For local LLMs (Ollama, LocalAI, LM Studio) or any OpenAI-compatible API
48
+
49
+ ```
50
+ ▸ Step 2: 配置 [Provider Name]
51
+ ```
52
+ - Enter your API key (required)
53
+ - For custom providers: Enter the base URL (e.g., `http://localhost:11434/v1`)
54
+
55
+ ```
56
+ ▸ Step 3: 设置模型
57
+ ```
58
+ - Enter the model name (default value provided based on provider)
59
+
60
+ ```
61
+ ▸ Step 4: 保存配置
62
+ ```
63
+ - If existing configurations found, choose to update or create new
64
+ - Configuration saved to `~/.cckit/config.json`
65
+
66
+ ```
67
+ ▸ Step 5: 测试连接
68
+ ```
69
+ - Optional: Test the connection to verify API key and endpoint
70
+
71
+ ```
72
+ ▸ Step 6: 切换 Provider
73
+ ```
74
+ - Optional: Switch to the newly configured provider immediately
75
+
76
+ ### Features
77
+
78
+ - **Arrow Key Navigation**: Easy selection with keyboard
79
+ - **Smart Defaults**: Pre-filled model names based on provider
80
+ - **Existing Config Detection**: Detects and offers to update existing configurations
81
+ - **Connection Testing**: Verify setup before switching
82
+ - **Immediate Switch**: Option to activate the provider right away
83
+
84
+ ### Example Session
85
+
86
+ ```bash
87
+ $ cckit interactive
88
+
89
+ ___ ___ / ___ ( ) __ ___
90
+ // ) ) // ) ) //\ \ / / / /
91
+ // // // \ \ / / / /
92
+ ((____ ((____ // \ \ / / / /
93
+ 交互式配置向导
94
+
95
+ ▸ Step 1: 选择要配置的 AI 提供商
96
+
97
+ ? 请选择提供商: (Use arrow keys)
98
+ ❯ 智谱 Coding Plan (GLM)
99
+ MiniMax Coding Plan
100
+ Kimi Coding Plan
101
+ ZenMux.ai
102
+ 快手 StreamLake Coding Plan
103
+ 火山引擎 Coding Plan
104
+ 阿里云 Coding Plan
105
+ 腾讯云 Coding Plan
106
+ 讯飞 Coding Plan
107
+ Claude (Official)
108
+ 自定义 Provider (Local LLM / OpenAI Compatible)
109
+
110
+ ▸ Step 2: 配置 智谱 Coding Plan
111
+
112
+ ? 请输入 API Key: [your-api-key]
113
+
114
+ ▸ Step 3: 设置模型
115
+
116
+ ? 请输入模型名称: (GLM-4.7)
117
+
118
+ ▸ Step 4: 保存配置
119
+
120
+ ✓ 配置已保存 (id: zhipu_xxx)
121
+ Provider: 智谱 Coding Plan
122
+ Model: GLM-4.7
123
+
124
+ ▸ Step 5: 测试连接
125
+
126
+ ? 是否测试连接? (Y/n)
127
+
128
+ ✓ 连接成功!
129
+ 响应时间: 234ms
130
+
131
+ ▸ Step 6: 切换 Provider
132
+
133
+ ? 是否立即切换到该 Provider? (Y/n)
134
+
135
+ ✓ 已切换到 智谱 Coding Plan
136
+ 请重启终端或新开会话以使配置生效
137
+
138
+ ___ ___ / ___ ( ) __ ___
139
+ // ) ) // ) ) //\ \ / / / /
140
+ // // // \ \ / / / /
141
+ ((____ ((____ // \ \ / / / /
142
+ 配置完成! ✓
143
+ ```
144
+
145
+ The interactive wizard will guide you through:
146
+ 1. Selecting an AI provider (智谱 Coding Plan, MiniMax Coding Plan, Kimi Coding Plan, Claude, ZenMux, 快手 StreamLake Coding Plan, 火山引擎 Coding Plan, 阿里云 Coding Plan, 腾讯云 Coding Plan, 讯飞 Coding Plan, or Custom)
147
+ 2. Entering your API key
148
+ 3. Setting up the model name
149
+ 4. Testing the connection
150
+ 5. Switching to the configured provider
151
+
29
152
  ## Usage
30
153
 
31
154
  ### Basic Commands
@@ -67,9 +190,9 @@ cckit remove-model <provider> <model>
67
190
 
68
191
  ### Supported Providers
69
192
 
70
- #### 智谱LLM (Zhipu) - [https://www.bigmodel.cn/claude-code?ic=AFDPNDPWIF](https://www.bigmodel.cn/claude-code?ic=AFDPNDPWIF)
193
+ #### 智谱 Coding Plan - [https://www.bigmodel.cn/claude-code?ic=AFDPNDPWIF](https://www.bigmodel.cn/claude-code?ic=AFDPNDPWIF)
71
194
  ```bash
72
- # Configure 智谱LLM with a single model
195
+ # Configure 智谱 Coding Plan with a single model
73
196
  cckit configure zhipu --api-key "your-api-key" --model "GLM-4.7"
74
197
 
75
198
  # Configure with multiple models
@@ -84,13 +207,13 @@ cckit set-model zhipu "GLM-4-Plus"
84
207
  # Remove a model
85
208
  cckit remove-model zhipu "GLM-4.7"
86
209
 
87
- # Switch to 智谱LLM
210
+ # Switch to 智谱 Coding Plan
88
211
  cckit switch zhipu
89
212
  ```
90
213
 
91
- #### MiniMax - [https://platform.minimaxi.com/subscribe/coding-plan](https://platform.minimaxi.com/subscribe/coding-plan?code=3FiunKqPhD&source=link)
214
+ #### MiniMax Coding Plan - [https://platform.minimaxi.com/subscribe/coding-plan](https://platform.minimaxi.com/subscribe/coding-plan?code=3FiunKqPhD&source=link)
92
215
  ```bash
93
- # Configure MiniMax with multiple models
216
+ # Configure MiniMax Coding Plan with multiple models
94
217
  cckit configure minimax --api-key "your-api-key" --model "MiniMax-M2" --model "MiniMax-M4"
95
218
 
96
219
  # List models
@@ -102,13 +225,13 @@ cckit set-model minimax "MiniMax-M4"
102
225
  # Remove a model
103
226
  cckit remove-model minimax "MiniMax-M2"
104
227
 
105
- # Switch to MiniMax
228
+ # Switch to MiniMax Coding Plan
106
229
  cckit switch minimax
107
230
  ```
108
231
 
109
- #### Kimi (Moonshot) - [https://www.kimi.com/coding/docs/](https://www.kimi.com/coding/docs/)
232
+ #### Kimi Coding Plan - [https://www.kimi.com/coding/docs/](https://www.kimi.com/coding/docs/)
110
233
  ```bash
111
- # Configure Kimi with multiple models
234
+ # Configure Kimi Coding Plan with multiple models
112
235
  cckit configure kimi --api-key "your-api-key" --model "kimi-for-coding" --model "kimi-plus"
113
236
 
114
237
  # List models
@@ -117,7 +240,7 @@ cckit models kimi
117
240
  # Set active model
118
241
  cckit set-model kimi "kimi-plus"
119
242
 
120
- # Switch to Kimi
243
+ # Switch to Kimi Coding Plan
121
244
  cckit switch kimi
122
245
  ```
123
246
 
@@ -151,9 +274,9 @@ cckit set-model claude "claude-3-opus-20250219"
151
274
  cckit switch claude
152
275
  ```
153
276
 
154
- #### Kuaishou StreamLake - [快手万擎引擎](https://streamlake.com/marketing/coding-plan)
277
+ #### 快手 StreamLake Coding Plan - [快手万擎引擎](https://streamlake.com/marketing/coding-plan)
155
278
  ```bash
156
- # Configure Kuaishou StreamLake with multiple models
279
+ # Configure 快手 StreamLake Coding Plan with multiple models
157
280
  cckit configure streamlake --api-key "your-streamlake-api-key" --model "kat-coder-pro-v1" --model "claude-3-opus-20250219"
158
281
 
159
282
  # List models
@@ -162,13 +285,13 @@ cckit models streamlake
162
285
  # Set active model
163
286
  cckit set-model streamlake "claude-3-opus-20250219"
164
287
 
165
- # Switch to Kuaishou StreamLake
288
+ # Switch to 快手 StreamLake Coding Plan
166
289
  cckit switch streamlake
167
290
  ```
168
291
 
169
- #### Volcengine [火山方舟](https://www.volcengine.com/activity/codingplan)
292
+ #### 火山引擎 Coding Plan [火山方舟](https://www.volcengine.com/activity/codingplan)
170
293
  ```bash
171
- # Configure Volcengine with multiple models
294
+ # Configure 火山引擎 Coding Plan with multiple models
172
295
  cckit configure volcengine --api-key "your-ark-api-key" --model "ark-code-latest" --model "ark-code-pro"
173
296
 
174
297
  # List models
@@ -177,7 +300,7 @@ cckit models volcengine
177
300
  # Set active model
178
301
  cckit set-model volcengine "ark-code-pro"
179
302
 
180
- # Switch to Volcengine
303
+ # Switch to 火山引擎 Coding Plan
181
304
  cckit switch volcengine
182
305
  ```
183
306
 
@@ -196,6 +319,36 @@ cckit set-model aliyun "glm-5"
196
319
  cckit switch aliyun
197
320
  ```
198
321
 
322
+ #### Tencent [腾讯云 Coding Plan]
323
+ ```bash
324
+ # Configure Tencent Coding Plan with multiple models
325
+ cckit configure tencent --api-key "your-api-key" --model "GLM-5" --model "MiniMax-M2.5" --model "Kimi-K2.5"
326
+
327
+ # List models
328
+ cckit models tencent
329
+
330
+ # Set active model
331
+ cckit set-model tencent "MiniMax-M2.5"
332
+
333
+ # Switch to Tencent
334
+ cckit switch tencent
335
+ ```
336
+
337
+ #### XFYun [讯飞 Coding Plan]
338
+ ```bash
339
+ # Configure XFYun Coding Plan with multiple models
340
+ cckit configure xfyun --api-key "your-api-key" --model "GLM-5" --model "MiniMax-M2.5" --model "Kimi-K2.5"
341
+
342
+ # List models
343
+ cckit models xfyun
344
+
345
+ # Set active model
346
+ cckit set-model xfyun "MiniMax-M2.5"
347
+
348
+ # Switch to XFYun
349
+ cckit switch xfyun
350
+ ```
351
+
199
352
  #### Custom Provider - Local/OpaqueAI/OpenAI Compatible APIs
200
353
 
201
354
  Configure custom providers for local LLMs (Ollama, LocalAI, LM Studio), opaque API endpoints, or any Anthropic-compatible API:
@@ -239,6 +392,9 @@ cckit import backup.json
239
392
 
240
393
  # Reset to default Claude configuration
241
394
  cckit reset
395
+
396
+ # Interactive configuration wizard (new!)
397
+ cckit interactive
242
398
  ```
243
399
 
244
400
  ## Configuration
@@ -259,13 +415,15 @@ The active model is used when Claude Code switches to that provider. If no expli
259
415
 
260
416
  | Provider | Default Model | Auth Method | Capabilities | Default Base URL |
261
417
  |----------|---------------|-------------|--------------|------------------|
262
- | 智谱LLM | GLM-4.7 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support | https://open.bigmodel.cn/api/anthropic |
263
- | MiniMax | MiniMax-M2 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Multi-language | https://api.minimaxi.com/anthropic |
264
- | Kimi | kimi-for-coding | ANTHROPIC_API_KEY | Chat, Long Context, Code Generation | https://api.kimi.com/coding/ |
418
+ | 智谱 Coding Plan | GLM-4.7 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support | https://open.bigmodel.cn/api/anthropic |
419
+ | MiniMax Coding Plan | MiniMax-M2 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Multi-language | https://api.minimaxi.com/anthropic |
420
+ | Kimi Coding Plan | kimi-for-coding | ANTHROPIC_API_KEY | Chat, Long Context, Code Generation | https://api.kimi.com/coding/ |
265
421
  | ZenMux.ai | claude-3-5-sonnet-20241022 | ANTHROPIC_API_KEY | Model Gateway, Multi-provider, Claude Compatible | https://zenmux.ai/api/anthropic |
266
- | Kuaishou StreamLake | kat-coder-pro-v1 | ANTHROPIC_API_KEY | Chat, Code Generation, Chinese Support, Video Understanding | https://wanqing.streamlakeapi.com/api/gateway/v1/endpoints/kat-coder-pro-v1/claude-code-proxy |
267
- | Volcengine | ark-code-latest | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support | https://ark.cn-beijing.volces.com/api/coding |
268
- | Aliyun | claude-sonnet-4-20250514 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support, Multimodal | https://coding.dashscope.aliyuncs.com/apps/anthropic |
422
+ | 快手 StreamLake Coding Plan | kat-coder-pro-v1 | ANTHROPIC_API_KEY | Chat, Code Generation, Chinese Support, Video Understanding | https://wanqing.streamlakeapi.com/api/gateway/v1/endpoints/kat-coder-pro-v1/claude-code-proxy |
423
+ | 火山引擎 Coding Plan | ark-code-latest | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support | https://ark.cn-beijing.volces.com/api/coding |
424
+ | 阿里云 Coding Plan | claude-sonnet-4-20250514 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support, Multimodal | https://coding.dashscope.aliyuncs.com/apps/anthropic |
425
+ | 腾讯云 Coding Plan | GLM-5 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support, Multimodal | https://api.lkeap.cloud.tencent.com/coding/anthropic |
426
+ | 讯飞 Coding Plan | GLM-5 | ANTHROPIC_AUTH_TOKEN | Chat, Code Generation, Chinese Support, Multimodal | https://maas-coding-api.cn-huabei-1.xf-yun.com/anthropic |
269
427
  | Claude | claude-3-5-sonnet-20241022 | ANTHROPIC_API_KEY | Chat, Code Generation, Analysis, Multimodal | Official |
270
428
  | Custom | - | ANTHROPIC_API_KEY | Custom, User-defined, Local LLM, Anthropic Compatible API | User-defined |
271
429
 
@@ -299,7 +457,7 @@ For more build options and details, see [BUILD.md](BUILD.md).
299
457
 
300
458
  ## Requirements
301
459
 
302
- - Node.js 18 or higher
460
+ - Node.js 20 or higher
303
461
  - pnpm (or npm/yarn)
304
462
  - Claude Code installed
305
463
  - Valid API keys for the providers you want to use
@@ -2,6 +2,10 @@ import { ConfigManager } from './config.js';
2
2
  export declare class Commands {
3
3
  private configManager;
4
4
  constructor(configManager: ConfigManager);
5
+ /**
6
+ * Interactive configuration wizard with arrow key selection
7
+ */
8
+ interactive(): Promise<void>;
5
9
  list(): void;
6
10
  switch(providerId: string): void;
7
11
  current(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAsD,MAAM,aAAa,CAAC;AAKhG,qBAAa,QAAQ;IACP,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEhD,IAAI,IAAI,IAAI;IAgCZ,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAmBhC,OAAO,IAAI,IAAI;IA8Bf,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,IAAI;IAoEP,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAuCxB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IA6B1B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuEpE,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IA2CxD,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAkB7B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa1B,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,sBAAsB;YAgChB,YAAY;CAgB3B"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAsD,MAAM,aAAa,CAAC;AAahG,qBAAa,QAAQ;IACP,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,aAAa;IAEhD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAmOlC,IAAI,IAAI,IAAI;IAsCZ,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAmBhC,OAAO,IAAI,IAAI;IAkCf,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,IAAI;IAuEP,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IA6CxB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IA6B1B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuFpE,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IA2CxD,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAkB7B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa1B,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,sBAAsB;YAoChB,YAAY;CAM3B"}
package/dist/commands.js CHANGED
@@ -1,13 +1,215 @@
1
1
  import chalk from 'chalk';
2
2
  import fs from 'fs';
3
- import readline from 'readline';
3
+ import { select, input, confirm } from '@inquirer/prompts';
4
4
  import { createProvider } from './config.js';
5
- import { testProvider } from './providers.js';
5
+ import { testProvider, PROVIDER_CONFIGS } from './providers.js';
6
6
  import { i18n } from './i18n.js';
7
7
  export class Commands {
8
8
  constructor(configManager) {
9
9
  this.configManager = configManager;
10
10
  }
11
+ /**
12
+ * Interactive configuration wizard with arrow key selection
13
+ */
14
+ async interactive() {
15
+ console.log();
16
+ console.log(chalk.blue.bold(' ___ ___ / ___ ( ) __ ___ '));
17
+ console.log(chalk.blue.bold(' // ) ) // ) ) //\\ \\ / / / / '));
18
+ console.log(chalk.blue.bold(' // // // \\ \\ / / / / '));
19
+ console.log(chalk.blue.bold(' ((____ ((____ // \\ \\ / / / / '));
20
+ console.log(chalk.cyan.bold(' 交互式配置向导'));
21
+ console.log();
22
+ // Step 1: Select provider type using arrow keys
23
+ console.log();
24
+ console.log(chalk.cyan.bold('▸ Step 1: 选择要配置的 AI 提供商'));
25
+ console.log();
26
+ const providerTypes = Object.entries(PROVIDER_CONFIGS)
27
+ .filter(([key]) => key !== 'custom')
28
+ .map(([key, config]) => ({
29
+ value: key,
30
+ name: config.display_name,
31
+ defaultModel: config.default_model,
32
+ }));
33
+ const providerTypeChoices = [
34
+ ...providerTypes.map((p) => ({
35
+ value: { type: 'preset', id: p.value, name: p.name, defaultModel: p.defaultModel },
36
+ name: p.name,
37
+ })),
38
+ {
39
+ value: { type: 'custom' },
40
+ name: chalk.green('自定义 Provider (Local LLM / OpenAI Compatible)'),
41
+ },
42
+ ];
43
+ const providerChoice = await select({
44
+ message: '请选择提供商',
45
+ choices: providerTypeChoices,
46
+ pageSize: 12,
47
+ });
48
+ let providerType;
49
+ let providerName;
50
+ let defaultModel;
51
+ if (providerChoice.type === 'custom') {
52
+ providerType = 'custom';
53
+ providerName = await input({
54
+ message: '请输入自定义 Provider 名称 (例如: ollama, my-api)',
55
+ default: 'ollama',
56
+ });
57
+ defaultModel = 'llama3';
58
+ }
59
+ else {
60
+ providerType = providerChoice.id;
61
+ providerName = providerChoice.name;
62
+ defaultModel = providerChoice.defaultModel;
63
+ }
64
+ console.log();
65
+ console.log(chalk.cyan.bold(`▸ Step 2: 配置 ${providerName}`));
66
+ console.log();
67
+ // Step 2: Enter API key
68
+ const apiKey = await input({
69
+ message: '请输入 API Key',
70
+ validate: (value) => {
71
+ if (!value || value.trim() === '') {
72
+ return 'API Key 不能为空';
73
+ }
74
+ return true;
75
+ },
76
+ });
77
+ // Step 3: Enter base URL (for custom providers)
78
+ let baseUrl;
79
+ if (providerType === 'custom') {
80
+ baseUrl = await input({
81
+ message: '请输入 Base URL',
82
+ default: 'http://localhost:11434/v1',
83
+ });
84
+ }
85
+ // Step 4: Enter model name
86
+ console.log();
87
+ console.log(chalk.cyan.bold('▸ Step 3: 设置模型'));
88
+ console.log();
89
+ const modelInput = await input({
90
+ message: '请输入模型名称 (多个模型用逗号分隔,如: claude-3-opus, claude-3-sonnet)',
91
+ default: defaultModel,
92
+ });
93
+ // Parse model names: split by comma and trim whitespace
94
+ const models = modelInput
95
+ .split(',')
96
+ .map((m) => m.trim())
97
+ .filter((m) => m.length > 0);
98
+ const model = models[0] || defaultModel;
99
+ // Step 5: Save configuration
100
+ console.log();
101
+ console.log(chalk.cyan.bold('▸ Step 4: 保存配置'));
102
+ console.log();
103
+ const providers = this.configManager.listProviders();
104
+ const sameTypeProviders = providers.filter((p) => p.provider_type === providerType);
105
+ let provider;
106
+ // If there are existing providers of the same type, ask user what to do
107
+ if (sameTypeProviders.length > 0 && providerType !== 'custom') {
108
+ const updateChoices = [
109
+ ...sameTypeProviders.map((p, i) => ({
110
+ value: { action: 'update', index: i },
111
+ name: `${p.name} (模型: ${p.current_model || p.models[0] || 'none'})`,
112
+ })),
113
+ {
114
+ value: { action: 'create' },
115
+ name: chalk.green(`创建新的 ${providerName} 配置`),
116
+ },
117
+ ];
118
+ const updateChoice = await select({
119
+ message: `发现 ${sameTypeProviders.length} 个已存在的配置,请选择`,
120
+ choices: updateChoices,
121
+ });
122
+ if (updateChoice.action === 'update') {
123
+ const existingProvider = sameTypeProviders[updateChoice.index];
124
+ existingProvider.api_key = apiKey;
125
+ if (baseUrl)
126
+ existingProvider.base_url = baseUrl;
127
+ // Add all new models that don't already exist
128
+ for (const m of models) {
129
+ if (!existingProvider.models.includes(m)) {
130
+ existingProvider.models.push(m);
131
+ }
132
+ }
133
+ existingProvider.current_model = model;
134
+ existingProvider.updated_at = new Date().toISOString();
135
+ provider = existingProvider;
136
+ }
137
+ }
138
+ else if (providerType === 'custom') {
139
+ // For custom providers, check if a provider with the same name exists
140
+ const existingCustomProvider = providers.find((p) => p.provider_type === 'custom' && p.name === providerName);
141
+ if (existingCustomProvider) {
142
+ const shouldUpdate = await confirm({
143
+ message: `发现已存在的 "${providerName}" 配置,是否更新?`,
144
+ default: true,
145
+ });
146
+ if (shouldUpdate) {
147
+ existingCustomProvider.api_key = apiKey;
148
+ if (baseUrl)
149
+ existingCustomProvider.base_url = baseUrl;
150
+ // Add all new models that don't already exist
151
+ for (const m of models) {
152
+ if (!existingCustomProvider.models.includes(m)) {
153
+ existingCustomProvider.models.push(m);
154
+ }
155
+ }
156
+ existingCustomProvider.current_model = model;
157
+ existingCustomProvider.updated_at = new Date().toISOString();
158
+ provider = existingCustomProvider;
159
+ }
160
+ }
161
+ }
162
+ // If provider is not set, create a new one
163
+ if (!provider) {
164
+ provider = createProvider(providerType, providerName, apiKey, baseUrl, models);
165
+ provider.current_model = model;
166
+ }
167
+ this.configManager.saveProvider(provider);
168
+ console.log(chalk.green(`✓ 配置已保存 (id: ${provider.id})`));
169
+ console.log(` ${i18n.general.provider_label()}: ${providerName}`);
170
+ console.log(` ${i18n.general.model_label()}: ${models.join(', ')}`);
171
+ // Step 6: Ask to test connection
172
+ console.log();
173
+ console.log(chalk.cyan.bold('▸ Step 5: 测试连接'));
174
+ console.log();
175
+ const shouldTest = await confirm({
176
+ message: '是否测试连接?',
177
+ default: true,
178
+ });
179
+ if (shouldTest) {
180
+ console.log(chalk.yellow(`正在测试 ${providerName} 连接...`));
181
+ const result = await testProvider(provider);
182
+ if (result.success) {
183
+ console.log(chalk.green(`✓ 连接成功!`));
184
+ if (result.response_time_ms) {
185
+ console.log(chalk.dim(` 响应时间: ${result.response_time_ms}ms`));
186
+ }
187
+ }
188
+ else {
189
+ console.log(chalk.red(`✗ 连接失败: ${result.message}`));
190
+ }
191
+ }
192
+ // Step 7: Ask to switch
193
+ console.log();
194
+ console.log(chalk.cyan.bold('▸ Step 6: 切换 Provider'));
195
+ console.log();
196
+ const shouldSwitch = await confirm({
197
+ message: '是否立即切换到该 Provider?',
198
+ default: true,
199
+ });
200
+ if (shouldSwitch) {
201
+ this.configManager.switchProvider(provider.id);
202
+ console.log(chalk.green.bold(`✓ 已切换到 ${providerName}`));
203
+ console.log(chalk.dim(i18n.cli.restart_note()));
204
+ }
205
+ console.log();
206
+ console.log(chalk.green.bold(' ___ ___ / ___ ( ) __ ___ '));
207
+ console.log(chalk.green.bold(' // ) ) // ) ) //\\ \\ / / / / '));
208
+ console.log(chalk.green.bold(' // // // \\ \\ / / / / '));
209
+ console.log(chalk.green.bold(' ((____ ((____ // \\ \\ / / / / '));
210
+ console.log(chalk.cyan.bold(' 配置完成! ✓'));
211
+ console.log();
212
+ }
11
213
  list() {
12
214
  console.log(chalk.blue.bold(i18n.cli.list_title()));
13
215
  console.log();
@@ -15,7 +217,9 @@ export class Commands {
15
217
  const currentProvider = this.configManager.getCurrentProvider();
16
218
  for (const provider of providers) {
17
219
  const isCurrent = currentProvider?.id === provider.id;
18
- const status = isCurrent ? chalk.green.bold(i18n.status.active()) : chalk.dim(i18n.status.inactive());
220
+ const status = isCurrent
221
+ ? chalk.green.bold(i18n.status.active())
222
+ : chalk.dim(i18n.status.inactive());
19
223
  const providerName = this.getProviderDisplayName(provider.provider_type, provider.name, true);
20
224
  console.log(` ${status} ${chalk.bold(providerName)}`);
21
225
  console.log(` ${i18n.general.id_label()}: ${chalk.dim(provider.id)}`);
@@ -71,17 +275,20 @@ export class Commands {
71
275
  if (!provider) {
72
276
  const providerTypeMap = {
73
277
  zhipu: ['zhipu', i18n.providers.get_display_name('zhipu')],
74
- '智谱': ['zhipu', i18n.providers.get_display_name('zhipu')],
278
+ 智谱: ['zhipu', i18n.providers.get_display_name('zhipu')],
75
279
  minimax: ['minimax', i18n.providers.get_display_name('minimax')],
76
280
  kimi: ['kimi', i18n.providers.get_display_name('kimi')],
77
281
  claude: ['claude', i18n.providers.get_display_name('claude')],
78
282
  zenmux: ['zenmux', i18n.providers.get_display_name('zenmux')],
79
283
  streamlake: ['streamlake', i18n.providers.get_display_name('streamlake')],
80
284
  kuaishou: ['streamlake', i18n.providers.get_display_name('streamlake')],
81
- '快手': ['streamlake', i18n.providers.get_display_name('streamlake')],
285
+ 快手: ['streamlake', i18n.providers.get_display_name('streamlake')],
82
286
  volcengine: ['volcengine', i18n.providers.get_display_name('volcengine')],
83
287
  aliyun: ['aliyun', i18n.providers.get_display_name('aliyun')],
84
- '阿里云': ['aliyun', i18n.providers.get_display_name('aliyun')],
288
+ 阿里云: ['aliyun', i18n.providers.get_display_name('aliyun')],
289
+ tencent: ['tencent', i18n.providers.get_display_name('tencent')],
290
+ 腾讯: ['tencent', i18n.providers.get_display_name('tencent')],
291
+ 腾讯云: ['tencent', i18n.providers.get_display_name('tencent')],
85
292
  };
86
293
  // Check if this is a custom provider (format: custom:<name> or just a new name)
87
294
  if (providerId.startsWith('custom:')) {
@@ -348,15 +555,9 @@ export class Commands {
348
555
  return colorFn(displayName);
349
556
  }
350
557
  async askForSwitch(providerName) {
351
- const rl = readline.createInterface({
352
- input: process.stdin,
353
- output: process.stdout,
354
- });
355
- return new Promise((resolve) => {
356
- rl.question(`Do you want to switch to ${providerName} now? [y/N]: `, (answer) => {
357
- rl.close();
358
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
359
- });
558
+ return confirm({
559
+ message: `Do you want to switch to ${providerName} now?`,
560
+ default: false,
360
561
  });
361
562
  }
362
563
  }