@pikecode/api-key-manager 1.1.3 → 1.2.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 +35 -5
- package/package.json +1 -1
- package/src/commands/add/codexImporter.js +48 -0
- package/src/commands/add/prompts.js +262 -0
- package/src/commands/add/providerSaver.js +75 -0
- package/src/commands/add/summaryPrinter.js +29 -0
- package/src/commands/add.js +10 -390
- package/src/commands/edit.js +1 -1
- package/src/commands/remove.js +63 -18
- package/src/config.js +117 -48
- package/src/constants/ui.js +108 -0
- package/src/utils/validator.js +2 -15
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- 🔄 **快速切换** — 一键切换不同的 API 供应商
|
|
12
12
|
- ⚡ **快速启动** — `-q` 跳过参数选择,秒级启动
|
|
13
13
|
- 🧠 **智能记忆** — 自动记住上次使用的启动参数
|
|
14
|
-
-
|
|
14
|
+
- 🗑️ **批量删除** — 支持一次选择多个供应商批量删除
|
|
15
15
|
- 🔌 **MCP 管理** — 管理 Claude Code 的 MCP 服务器配置
|
|
16
16
|
- 🧹 **配置清理** — 清理 Claude Code 配置文件中的冗余数据
|
|
17
17
|
- 📋 **克隆配置** — 快速克隆现有供应商配置
|
|
@@ -53,7 +53,7 @@ akm current
|
|
|
53
53
|
|------|------|
|
|
54
54
|
| `akm` / `akm switch` | 交互式选择和切换供应商 |
|
|
55
55
|
| `akm add` | 添加新的供应商配置 |
|
|
56
|
-
| `akm remove [provider]` |
|
|
56
|
+
| `akm remove [provider]` | 删除供应商配置(支持批量删除) |
|
|
57
57
|
| `akm list` | 列出所有供应商 |
|
|
58
58
|
| `akm current` | 显示当前激活的配置 |
|
|
59
59
|
| `akm edit [provider]` | 编辑供应商配置 |
|
|
@@ -128,9 +128,6 @@ akm
|
|
|
128
128
|
# 直接切换到指定供应商
|
|
129
129
|
akm my-provider
|
|
130
130
|
|
|
131
|
-
# 通过别名切换
|
|
132
|
-
akm prod
|
|
133
|
-
|
|
134
131
|
# 仅显示 Claude Code 供应商
|
|
135
132
|
akm switch --claude
|
|
136
133
|
|
|
@@ -185,6 +182,39 @@ akm clone my-provider
|
|
|
185
182
|
|
|
186
183
|
克隆时可以修改名称、认证模式、Token、基础 URL 等。
|
|
187
184
|
|
|
185
|
+
### 删除供应商
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# 交互式删除(支持批量选择)
|
|
189
|
+
akm remove
|
|
190
|
+
|
|
191
|
+
# 直接删除指定供应商
|
|
192
|
+
akm remove my-provider
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**批量删除流程:**
|
|
196
|
+
1. 使用空格键选择多个要删除的供应商
|
|
197
|
+
2. 按 Enter 确认选择
|
|
198
|
+
3. 预览将要删除的供应商列表
|
|
199
|
+
4. 最终确认后批量删除
|
|
200
|
+
|
|
201
|
+
### 编辑供应商
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 交互式选择要编辑的供应商
|
|
205
|
+
akm edit
|
|
206
|
+
|
|
207
|
+
# 直接编辑指定供应商
|
|
208
|
+
akm edit my-provider
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**可编辑内容:**
|
|
212
|
+
- 供应商名称
|
|
213
|
+
- 认证模式
|
|
214
|
+
- API 基础 URL
|
|
215
|
+
- Token
|
|
216
|
+
- 启动参数
|
|
217
|
+
|
|
188
218
|
### MCP 服务器管理
|
|
189
219
|
|
|
190
220
|
管理 `~/.claude.json` 中的 MCP 服务器配置:
|
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex configuration importer.
|
|
3
|
+
* Reads existing Codex config from ~/.codex and extracts API key and base URL.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Logger } = require('../../utils/logger');
|
|
7
|
+
const { readCodexFiles, extractBaseUrlFromConfigToml } = require('../../utils/codex-files');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Import Codex configuration from ~/.codex directory.
|
|
11
|
+
* @returns {Promise<{apiKey: string, baseUrl: string|null}|null>}
|
|
12
|
+
*/
|
|
13
|
+
async function importCodexConfig() {
|
|
14
|
+
try {
|
|
15
|
+
const codexFiles = await readCodexFiles();
|
|
16
|
+
|
|
17
|
+
if (!codexFiles.authJson) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 解析 auth.json 获取 API Key
|
|
22
|
+
const authData = JSON.parse(codexFiles.authJson);
|
|
23
|
+
const apiKey = authData.api_key || authData.openai_api_key || authData.OPENAI_API_KEY;
|
|
24
|
+
|
|
25
|
+
if (!apiKey) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 从 config.toml 中读取当前激活 provider 的 base_url
|
|
30
|
+
let baseUrl = null;
|
|
31
|
+
if (codexFiles.configToml) {
|
|
32
|
+
baseUrl = extractBaseUrlFromConfigToml(codexFiles.configToml);
|
|
33
|
+
if (!baseUrl) {
|
|
34
|
+
// 兼容旧格式(akm 之前错误写入的顶层字段)
|
|
35
|
+
const legacyMatch = codexFiles.configToml.match(/^api_base_url\s*=\s*["']([^"']+)["']/m);
|
|
36
|
+
if (legacyMatch) baseUrl = legacyMatch[1];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Logger.success(`成功从 ${codexFiles.codexHome} 导入配置`);
|
|
41
|
+
return { apiKey, baseUrl };
|
|
42
|
+
} catch (error) {
|
|
43
|
+
Logger.warning(`导入配置失败: ${error.message}`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { importCodexConfig };
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for ProviderAdder prompts.
|
|
3
|
+
* Each function returns a promise that resolves to the collected answers.
|
|
4
|
+
* The implementation mirrors the original large prompt array but is split
|
|
5
|
+
* into focused pieces to keep each function under 50 lines.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const inquirer = require('inquirer');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const { validator } = require('../../utils/validator');
|
|
11
|
+
const { UIHelper } = require('../../utils/ui-helper');
|
|
12
|
+
const { UI_MESSAGES } = require('../../constants/ui');
|
|
13
|
+
const { Logger } = require('../../utils/logger');
|
|
14
|
+
const { registry } = require('../../CommandRegistry');
|
|
15
|
+
const { getCodexLaunchArgs } = require('../../utils/launch-args');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Prompt the initial provider information (name, IDE, auth mode, URLs, tokens, etc.).
|
|
19
|
+
* @param {BaseCommand} adder - the ProviderAdder instance (used for ESC handling)
|
|
20
|
+
* @returns {Promise<Object>} answers object
|
|
21
|
+
*/
|
|
22
|
+
async function promptProviderInfo(adder) {
|
|
23
|
+
return await adder.promptWithESC([
|
|
24
|
+
{
|
|
25
|
+
type: 'list',
|
|
26
|
+
name: 'ideName',
|
|
27
|
+
message: UI_MESSAGES.SELECT_IDE,
|
|
28
|
+
choices: [
|
|
29
|
+
{ name: UI_MESSAGES.IDE_CLAUDE_CODE, value: 'claude' },
|
|
30
|
+
{ name: UI_MESSAGES.IDE_CODEX, value: 'codex' }
|
|
31
|
+
],
|
|
32
|
+
default: adder.presetIdeName || 'claude',
|
|
33
|
+
when: () => !adder.presetIdeName
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'list',
|
|
37
|
+
name: 'importFromExisting',
|
|
38
|
+
message: UI_MESSAGES.IMPORT_FROM_CODEX,
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: UI_MESSAGES.IMPORT_FROM_CODEX_EXISTING, value: 'import' },
|
|
41
|
+
{ name: UI_MESSAGES.IMPORT_FROM_CODEX_MANUAL, value: 'manual' }
|
|
42
|
+
],
|
|
43
|
+
default: 'import',
|
|
44
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) === 'codex'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: 'input',
|
|
48
|
+
name: 'name',
|
|
49
|
+
message: UI_MESSAGES.INPUT_PROVIDER_NAME,
|
|
50
|
+
validate: (input) => {
|
|
51
|
+
const err = validator.validateName(input);
|
|
52
|
+
return err || true;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
type: 'list',
|
|
57
|
+
name: 'authMode',
|
|
58
|
+
message: UI_MESSAGES.SELECT_AUTH_MODE,
|
|
59
|
+
choices: [
|
|
60
|
+
{ name: UI_MESSAGES.AUTH_MODE_API_KEY, value: 'api_key' },
|
|
61
|
+
{ name: UI_MESSAGES.AUTH_MODE_AUTH_TOKEN, value: 'auth_token' }
|
|
62
|
+
],
|
|
63
|
+
default: 'api_key',
|
|
64
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) !== 'codex'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'input',
|
|
68
|
+
name: 'baseUrl',
|
|
69
|
+
message: UI_MESSAGES.INPUT_BASE_URL,
|
|
70
|
+
validate: (input) => {
|
|
71
|
+
if (!input) return 'API 基础URL不能为空';
|
|
72
|
+
const err = validator.validateUrl(input);
|
|
73
|
+
return err || true;
|
|
74
|
+
},
|
|
75
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) !== 'codex'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'input',
|
|
79
|
+
name: 'authToken',
|
|
80
|
+
message: (answers) => {
|
|
81
|
+
const envVar = answers.authMode === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
|
|
82
|
+
return `${UI_MESSAGES.INPUT_TOKEN} (${envVar}):`;
|
|
83
|
+
},
|
|
84
|
+
validate: (input) => {
|
|
85
|
+
const err = validator.validateToken(input);
|
|
86
|
+
return err || true;
|
|
87
|
+
},
|
|
88
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) !== 'codex'
|
|
89
|
+
},
|
|
90
|
+
// Codex‑specific prompts (manual entry)
|
|
91
|
+
{
|
|
92
|
+
type: 'input',
|
|
93
|
+
name: 'baseUrl',
|
|
94
|
+
message: UI_MESSAGES.INPUT_OPENAI_BASE_URL,
|
|
95
|
+
default: '',
|
|
96
|
+
validate: (input) => {
|
|
97
|
+
if (!input) return true;
|
|
98
|
+
const err = validator.validateUrl(input);
|
|
99
|
+
return err || true;
|
|
100
|
+
},
|
|
101
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) === 'codex' && answers.importFromExisting === 'manual'
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: 'input',
|
|
105
|
+
name: 'authToken',
|
|
106
|
+
message: UI_MESSAGES.INPUT_OPENAI_API_KEY,
|
|
107
|
+
validate: (input) => {
|
|
108
|
+
if (!input) return 'API Key 不能为空';
|
|
109
|
+
const err = validator.validateToken(input);
|
|
110
|
+
return err || true;
|
|
111
|
+
},
|
|
112
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) === 'codex' && answers.importFromExisting === 'manual'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'confirm',
|
|
116
|
+
name: 'setAsDefault',
|
|
117
|
+
message: UI_MESSAGES.SET_AS_DEFAULT,
|
|
118
|
+
default: true
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'confirm',
|
|
122
|
+
name: 'configureLaunchArgs',
|
|
123
|
+
message: UI_MESSAGES.CONFIGURE_LAUNCH_ARGS,
|
|
124
|
+
default: false,
|
|
125
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) !== 'codex'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'confirm',
|
|
129
|
+
name: 'configureCodexLaunchArgs',
|
|
130
|
+
message: UI_MESSAGES.CONFIGURE_CODEX_LAUNCH_ARGS,
|
|
131
|
+
default: false,
|
|
132
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) === 'codex'
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
type: 'confirm',
|
|
136
|
+
name: 'configureModels',
|
|
137
|
+
message: UI_MESSAGES.CONFIGURE_MODELS,
|
|
138
|
+
default: false,
|
|
139
|
+
when: (answers) => (answers.ideName || adder.presetIdeName) !== 'codex'
|
|
140
|
+
}
|
|
141
|
+
], UI_MESSAGES.ESC_CANCEL_ADD, () => {
|
|
142
|
+
Logger.info(UI_MESSAGES.ADD_PROVIDER_CANCELLED);
|
|
143
|
+
registry.executeCommand('switch');
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Prompt for launch arguments (non‑Codex).
|
|
149
|
+
*/
|
|
150
|
+
async function promptLaunchArgs(adder) {
|
|
151
|
+
console.log(UIHelper.createTitle(UI_MESSAGES.CONFIG_LAUNCH_ARGS_TITLE, UIHelper.icons.settings));
|
|
152
|
+
console.log();
|
|
153
|
+
console.log(UIHelper.createTooltip(UI_MESSAGES.CONFIG_LAUNCH_ARGS_TOOLTIP));
|
|
154
|
+
console.log();
|
|
155
|
+
console.log(UIHelper.createStepIndicator(2, 2, UI_MESSAGES.ADD_PROVIDER_STEP_2_LAUNCH_ARGS));
|
|
156
|
+
console.log(UIHelper.createHintLine([
|
|
157
|
+
[UI_MESSAGES.HINT_SPACE, UI_MESSAGES.HINT_SPACE_DESC],
|
|
158
|
+
[UI_MESSAGES.HINT_A, UI_MESSAGES.HINT_A_DESC],
|
|
159
|
+
[UI_MESSAGES.HINT_I, UI_MESSAGES.HINT_I_DESC],
|
|
160
|
+
[UI_MESSAGES.HINT_ENTER, UI_MESSAGES.HINT_ENTER_CONFIRM],
|
|
161
|
+
[UI_MESSAGES.HINT_ESC, UI_MESSAGES.HINT_ESC_SKIP]
|
|
162
|
+
]));
|
|
163
|
+
console.log();
|
|
164
|
+
|
|
165
|
+
const result = await adder.promptWithESCAndDefault([
|
|
166
|
+
{
|
|
167
|
+
type: 'checkbox',
|
|
168
|
+
name: 'launchArgs',
|
|
169
|
+
message: UI_MESSAGES.SELECT_LAUNCH_ARGS,
|
|
170
|
+
choices: validator.getAvailableLaunchArgs().map(arg => ({
|
|
171
|
+
name: `${arg.name} - ${arg.description}`,
|
|
172
|
+
value: arg.name,
|
|
173
|
+
checked: false
|
|
174
|
+
}))
|
|
175
|
+
}
|
|
176
|
+
], UI_MESSAGES.ESC_SKIP_CONFIG, () => {
|
|
177
|
+
Logger.info(UI_MESSAGES.CONFIG_LAUNCH_ARGS_SKIP);
|
|
178
|
+
}, { launchArgs: [] });
|
|
179
|
+
|
|
180
|
+
return result.launchArgs;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Prompt for model configuration (non‑Codex).
|
|
185
|
+
*/
|
|
186
|
+
async function promptModelConfig(adder) {
|
|
187
|
+
console.log(UIHelper.createTitle(UI_MESSAGES.CONFIG_MODELS_TITLE, UIHelper.icons.settings));
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(UIHelper.createTooltip(UI_MESSAGES.CONFIG_MODELS_TOOLTIP));
|
|
190
|
+
console.log();
|
|
191
|
+
console.log(UIHelper.createStepIndicator(2, 2, UI_MESSAGES.ADD_PROVIDER_STEP_2_MODELS));
|
|
192
|
+
console.log(UIHelper.createHintLine([
|
|
193
|
+
[UI_MESSAGES.HINT_ENTER, UI_MESSAGES.HINT_ENTER_DESC],
|
|
194
|
+
[UI_MESSAGES.HINT_ESC, UI_MESSAGES.HINT_ESC_SKIP]
|
|
195
|
+
]));
|
|
196
|
+
console.log();
|
|
197
|
+
|
|
198
|
+
const responses = await adder.promptWithESCAndDefault([
|
|
199
|
+
{
|
|
200
|
+
type: 'input',
|
|
201
|
+
name: 'primaryModel',
|
|
202
|
+
message: UI_MESSAGES.INPUT_PRIMARY_MODEL,
|
|
203
|
+
default: '',
|
|
204
|
+
validate: (input) => validator.validateModel(input) || true
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: 'input',
|
|
208
|
+
name: 'smallFastModel',
|
|
209
|
+
message: UI_MESSAGES.INPUT_SMALL_FAST_MODEL,
|
|
210
|
+
default: '',
|
|
211
|
+
validate: (input) => validator.validateModel(input) || true
|
|
212
|
+
}
|
|
213
|
+
], UI_MESSAGES.ESC_SKIP_CONFIG, () => {
|
|
214
|
+
Logger.info(UI_MESSAGES.CONFIG_MODELS_SKIP);
|
|
215
|
+
}, { primaryModel: null, smallFastModel: null });
|
|
216
|
+
|
|
217
|
+
return { primaryModel: responses.primaryModel, smallFastModel: responses.smallFastModel };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Prompt for Codex‑specific launch arguments.
|
|
222
|
+
*/
|
|
223
|
+
async function promptCodexLaunchArgs(adder) {
|
|
224
|
+
// Reuse the same UI as the generic launch args but fetch Codex args.
|
|
225
|
+
console.log(UIHelper.createTitle(UI_MESSAGES.CONFIG_CODEX_LAUNCH_ARGS_TITLE, UIHelper.icons.settings));
|
|
226
|
+
console.log();
|
|
227
|
+
console.log(UIHelper.createTooltip(UI_MESSAGES.CONFIG_CODEX_LAUNCH_ARGS_TOOLTIP));
|
|
228
|
+
console.log();
|
|
229
|
+
console.log(UIHelper.createStepIndicator(2, 2, UI_MESSAGES.ADD_PROVIDER_STEP_2_LAUNCH_ARGS));
|
|
230
|
+
console.log(UIHelper.createHintLine([
|
|
231
|
+
[UI_MESSAGES.HINT_SPACE, UI_MESSAGES.HINT_SPACE_DESC],
|
|
232
|
+
[UI_MESSAGES.HINT_A, UI_MESSAGES.HINT_A_DESC],
|
|
233
|
+
[UI_MESSAGES.HINT_I, UI_MESSAGES.HINT_I_DESC],
|
|
234
|
+
[UI_MESSAGES.HINT_ENTER, UI_MESSAGES.HINT_ENTER_CONFIRM],
|
|
235
|
+
[UI_MESSAGES.HINT_ESC, UI_MESSAGES.HINT_ESC_SKIP]
|
|
236
|
+
]));
|
|
237
|
+
console.log();
|
|
238
|
+
|
|
239
|
+
const result = await adder.promptWithESCAndDefault([
|
|
240
|
+
{
|
|
241
|
+
type: 'checkbox',
|
|
242
|
+
name: 'launchArgs',
|
|
243
|
+
message: UI_MESSAGES.SELECT_LAUNCH_ARGS,
|
|
244
|
+
choices: getCodexLaunchArgs().map(arg => ({
|
|
245
|
+
name: `${arg.name} - ${arg.description}`,
|
|
246
|
+
value: arg.name,
|
|
247
|
+
checked: false
|
|
248
|
+
}))
|
|
249
|
+
}
|
|
250
|
+
], UI_MESSAGES.ESC_SKIP_CONFIG, () => {
|
|
251
|
+
Logger.info(UI_MESSAGES.CONFIG_CODEX_LAUNCH_ARGS_SKIP);
|
|
252
|
+
}, { launchArgs: [] });
|
|
253
|
+
|
|
254
|
+
return result.launchArgs;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
module.exports = {
|
|
258
|
+
promptProviderInfo,
|
|
259
|
+
promptLaunchArgs,
|
|
260
|
+
promptModelConfig,
|
|
261
|
+
promptCodexLaunchArgs
|
|
262
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper for saving a new provider and printing a summary.
|
|
3
|
+
* This mirrors the original `saveProvider` implementation but is split
|
|
4
|
+
* into a dedicated module to keep each file under 50 lines.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger } = require('../../utils/logger');
|
|
8
|
+
const { UIHelper } = require('../../utils/ui-helper');
|
|
9
|
+
const { promptCodexLaunchArgs, promptLaunchArgs, promptModelConfig } = require('./prompts');
|
|
10
|
+
const { printProviderSummary } = require('./summaryPrinter');
|
|
11
|
+
const { registry } = require('../../CommandRegistry');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Save the provider configuration.
|
|
15
|
+
* @param {BaseCommand} adder - ProviderAdder instance (provides configManager, prompts, etc.)
|
|
16
|
+
* @param {Object} answers - Collected answers from the prompts.
|
|
17
|
+
*/
|
|
18
|
+
async function saveProvider(adder, answers) {
|
|
19
|
+
try {
|
|
20
|
+
await adder.configManager.load();
|
|
21
|
+
|
|
22
|
+
// 如果已经存在同名供应商,询问是否覆盖
|
|
23
|
+
if (adder.configManager.getProvider(answers.name)) {
|
|
24
|
+
const shouldOverwrite = await adder.confirmOverwrite(answers.name);
|
|
25
|
+
if (!shouldOverwrite) {
|
|
26
|
+
Logger.warning('操作已取消');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 处理启动参数
|
|
32
|
+
let launchArgs = [];
|
|
33
|
+
if (answers.ideName === 'codex') {
|
|
34
|
+
launchArgs = answers.configureCodexLaunchArgs
|
|
35
|
+
? await promptCodexLaunchArgs(adder)
|
|
36
|
+
: [];
|
|
37
|
+
} else {
|
|
38
|
+
launchArgs = answers.configureLaunchArgs
|
|
39
|
+
? await promptLaunchArgs(adder)
|
|
40
|
+
: [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 处理模型配置(仅非 Codex)
|
|
44
|
+
let modelConfig = { primaryModel: null, smallFastModel: null };
|
|
45
|
+
if (answers.configureModels) {
|
|
46
|
+
modelConfig = await promptModelConfig(adder);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 保存到配置管理器(保持不可变写法在 configManager 中实现)
|
|
50
|
+
await adder.configManager.addProvider(answers.name, {
|
|
51
|
+
displayName: answers.displayName || answers.name,
|
|
52
|
+
ideName: answers.ideName || 'claude',
|
|
53
|
+
baseUrl: answers.baseUrl,
|
|
54
|
+
authToken: answers.authToken,
|
|
55
|
+
authMode: answers.authMode,
|
|
56
|
+
codexFiles: answers.codexFiles || null,
|
|
57
|
+
launchArgs,
|
|
58
|
+
primaryModel: modelConfig.primaryModel,
|
|
59
|
+
smallFastModel: modelConfig.smallFastModel,
|
|
60
|
+
setAsDefault: answers.setAsDefault
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 打印摘要并返回主界面
|
|
64
|
+
await printProviderSummary(adder, answers, launchArgs, modelConfig);
|
|
65
|
+
await adder.pauseBeforeReturn();
|
|
66
|
+
|
|
67
|
+
return await registry.executeCommand('switch');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (adder.isEscCancelled(error)) return;
|
|
70
|
+
Logger.error(`添加供应商失败: ${error.message}`);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { saveProvider };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper for printing provider summary after creation.
|
|
3
|
+
* Mirrors the original `printProviderSummary` logic but isolated.
|
|
4
|
+
*/
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const { Logger } = require('../../utils/logger');
|
|
7
|
+
const { UIHelper } = require('../../utils/ui-helper');
|
|
8
|
+
const { UI_MESSAGES } = require('../../constants/ui');
|
|
9
|
+
|
|
10
|
+
async function printProviderSummary(adder, answers, launchArgs, modelConfig) {
|
|
11
|
+
console.log(UIHelper.createTitle('供应商已创建', UIHelper.icons.success));
|
|
12
|
+
console.log();
|
|
13
|
+
console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_NAME}: ${answers.name}`));
|
|
14
|
+
console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_IDE}: ${answers.ideName || 'claude'}`));
|
|
15
|
+
if (answers.baseUrl) console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_BASE_URL}: ${answers.baseUrl}`));
|
|
16
|
+
if (answers.authToken) console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_TOKEN}: ${answers.authToken}`));
|
|
17
|
+
if (launchArgs && launchArgs.length) {
|
|
18
|
+
console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_LAUNCH_ARGS}:`));
|
|
19
|
+
launchArgs.forEach(arg => console.log(chalk.gray(` - ${arg}`)));
|
|
20
|
+
}
|
|
21
|
+
if (modelConfig && (modelConfig.primaryModel || modelConfig.smallFastModel)) {
|
|
22
|
+
console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_MODEL_CONFIG}:`));
|
|
23
|
+
if (modelConfig.primaryModel) console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_PRIMARY_MODEL}: ${modelConfig.primaryModel}`));
|
|
24
|
+
if (modelConfig.smallFastModel) console.log(chalk.gray(` ${UI_MESSAGES.PROVIDER_SMALL_FAST_MODEL}: ${modelConfig.smallFastModel}`));
|
|
25
|
+
}
|
|
26
|
+
Logger.success(UI_MESSAGES.ADD_PROVIDER_SUCCESS);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { printProviderSummary };
|