@pikecode/api-key-manager 1.0.21 → 1.0.24
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/bin/akm.js +22 -2
- package/package.json +6 -1
- package/src/commands/add.js +187 -24
- package/src/commands/list.js +6 -1
- package/src/commands/switch.js +61 -12
package/bin/akm.js
CHANGED
|
@@ -34,15 +34,35 @@ program
|
|
|
34
34
|
program
|
|
35
35
|
.command('add')
|
|
36
36
|
.description('添加新的API密钥配置')
|
|
37
|
-
.
|
|
37
|
+
.option('--codex', '直接添加 Codex CLI 供应商')
|
|
38
|
+
.option('--claude', '直接添加 Claude Code 供应商')
|
|
39
|
+
.action(async (options) => {
|
|
38
40
|
try {
|
|
39
|
-
|
|
41
|
+
const ideName = options.codex ? 'codex' : (options.claude ? 'claude' : null);
|
|
42
|
+
await registry.executeCommand('add', { ideName });
|
|
40
43
|
} catch (error) {
|
|
41
44
|
console.error(chalk.red('❌ 添加失败:'), error.message);
|
|
42
45
|
process.exit(1);
|
|
43
46
|
}
|
|
44
47
|
});
|
|
45
48
|
|
|
49
|
+
// Switch command
|
|
50
|
+
program
|
|
51
|
+
.command('switch')
|
|
52
|
+
.description('切换到指定供应商')
|
|
53
|
+
.argument('[provider]', '直接切换到指定供应商')
|
|
54
|
+
.option('--codex', '仅显示 Codex CLI 供应商')
|
|
55
|
+
.option('--claude', '仅显示 Claude Code 供应商')
|
|
56
|
+
.action(async (provider, options) => {
|
|
57
|
+
try {
|
|
58
|
+
const filter = options.codex ? 'codex' : (options.claude ? 'claude' : null);
|
|
59
|
+
await registry.executeCommand('switch', provider, { filter });
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(chalk.red('❌ 切换失败:'), error.message);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
46
66
|
// Remove command
|
|
47
67
|
program
|
|
48
68
|
.command('remove')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikecode/api-key-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"description": "A CLI tool for managing and switching multiple API provider configurations",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"anthropic",
|
|
30
30
|
"anthropic-api-key",
|
|
31
31
|
"anthropic-auth-token",
|
|
32
|
+
"openai",
|
|
33
|
+
"openai-api-key",
|
|
34
|
+
"codex",
|
|
35
|
+
"codex-cli",
|
|
36
|
+
"claude-code",
|
|
32
37
|
"environment",
|
|
33
38
|
"config",
|
|
34
39
|
"provider",
|
package/src/commands/add.js
CHANGED
|
@@ -7,9 +7,10 @@ const { UIHelper } = require('../utils/ui-helper');
|
|
|
7
7
|
const { BaseCommand } = require('./BaseCommand');
|
|
8
8
|
|
|
9
9
|
class ProviderAdder extends BaseCommand {
|
|
10
|
-
constructor() {
|
|
10
|
+
constructor(options = {}) {
|
|
11
11
|
super();
|
|
12
12
|
this.configManager = new ConfigManager();
|
|
13
|
+
this.presetIdeName = options.ideName || null;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
async interactive() {
|
|
@@ -174,6 +175,28 @@ class ProviderAdder extends BaseCommand {
|
|
|
174
175
|
|
|
175
176
|
try {
|
|
176
177
|
const answers = await this.prompt([
|
|
178
|
+
{
|
|
179
|
+
type: 'list',
|
|
180
|
+
name: 'ideName',
|
|
181
|
+
message: '选择要管理的 IDE:',
|
|
182
|
+
choices: [
|
|
183
|
+
{ name: 'Claude Code (Anthropic)', value: 'claude' },
|
|
184
|
+
{ name: 'Codex CLI (OpenAI)', value: 'codex' }
|
|
185
|
+
],
|
|
186
|
+
default: this.presetIdeName || 'claude',
|
|
187
|
+
when: () => !this.presetIdeName
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'list',
|
|
191
|
+
name: 'importFromExisting',
|
|
192
|
+
message: '是否从现有 Codex 配置导入?',
|
|
193
|
+
choices: [
|
|
194
|
+
{ name: '从 ~/.codex 导入现有配置', value: 'import' },
|
|
195
|
+
{ name: '手动输入配置', value: 'manual' }
|
|
196
|
+
],
|
|
197
|
+
default: 'import',
|
|
198
|
+
when: (answers) => (answers.ideName || this.presetIdeName) === 'codex'
|
|
199
|
+
},
|
|
177
200
|
{
|
|
178
201
|
type: 'input',
|
|
179
202
|
name: 'name',
|
|
@@ -194,16 +217,6 @@ class ProviderAdder extends BaseCommand {
|
|
|
194
217
|
return true;
|
|
195
218
|
}
|
|
196
219
|
},
|
|
197
|
-
{
|
|
198
|
-
type: 'list',
|
|
199
|
-
name: 'ideName',
|
|
200
|
-
message: '选择要管理的 IDE:',
|
|
201
|
-
choices: [
|
|
202
|
-
{ name: 'Claude Code (Anthropic)', value: 'claude' },
|
|
203
|
-
{ name: 'Codex CLI (OpenAI)', value: 'codex' }
|
|
204
|
-
],
|
|
205
|
-
default: 'claude'
|
|
206
|
-
},
|
|
207
220
|
{
|
|
208
221
|
type: 'list',
|
|
209
222
|
name: 'authMode',
|
|
@@ -214,7 +227,7 @@ class ProviderAdder extends BaseCommand {
|
|
|
214
227
|
{ name: '🌐 OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN) - 适用于官方Claude Code', value: 'oauth_token' }
|
|
215
228
|
],
|
|
216
229
|
default: 'api_key',
|
|
217
|
-
when: (answers) => answers.ideName !== 'codex'
|
|
230
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex'
|
|
218
231
|
},
|
|
219
232
|
{
|
|
220
233
|
type: 'list',
|
|
@@ -225,7 +238,7 @@ class ProviderAdder extends BaseCommand {
|
|
|
225
238
|
{ name: '🔐 ANTHROPIC_AUTH_TOKEN - 认证令牌', value: 'auth_token' }
|
|
226
239
|
],
|
|
227
240
|
default: 'api_key',
|
|
228
|
-
when: (answers) => answers.ideName !== 'codex' && answers.authMode === 'api_key'
|
|
241
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex' && answers.authMode === 'api_key'
|
|
229
242
|
},
|
|
230
243
|
{
|
|
231
244
|
type: 'input',
|
|
@@ -249,7 +262,7 @@ class ProviderAdder extends BaseCommand {
|
|
|
249
262
|
if (error) return error;
|
|
250
263
|
return true;
|
|
251
264
|
},
|
|
252
|
-
when: (answers) => answers.ideName !== 'codex' && (answers.authMode === 'api_key' || answers.authMode === 'auth_token')
|
|
265
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex' && (answers.authMode === 'api_key' || answers.authMode === 'auth_token')
|
|
253
266
|
},
|
|
254
267
|
{
|
|
255
268
|
type: 'input',
|
|
@@ -271,9 +284,8 @@ class ProviderAdder extends BaseCommand {
|
|
|
271
284
|
const error = validator.validateToken(input);
|
|
272
285
|
if (error) return error;
|
|
273
286
|
return true;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
when: (answers) => answers.ideName !== 'codex'
|
|
287
|
+
},
|
|
288
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex'
|
|
277
289
|
},
|
|
278
290
|
{
|
|
279
291
|
type: 'input',
|
|
@@ -286,7 +298,7 @@ class ProviderAdder extends BaseCommand {
|
|
|
286
298
|
if (error) return error;
|
|
287
299
|
return true;
|
|
288
300
|
},
|
|
289
|
-
when: (answers) => answers.ideName === 'codex'
|
|
301
|
+
when: (answers) => (answers.ideName || this.presetIdeName) === 'codex' && answers.importFromExisting === 'manual'
|
|
290
302
|
},
|
|
291
303
|
{
|
|
292
304
|
type: 'input',
|
|
@@ -298,7 +310,7 @@ class ProviderAdder extends BaseCommand {
|
|
|
298
310
|
if (error) return error;
|
|
299
311
|
return true;
|
|
300
312
|
},
|
|
301
|
-
when: (answers) => answers.ideName === 'codex'
|
|
313
|
+
when: (answers) => (answers.ideName || this.presetIdeName) === 'codex' && answers.importFromExisting === 'manual'
|
|
302
314
|
},
|
|
303
315
|
{
|
|
304
316
|
type: 'confirm',
|
|
@@ -311,24 +323,68 @@ class ProviderAdder extends BaseCommand {
|
|
|
311
323
|
name: 'configureLaunchArgs',
|
|
312
324
|
message: '是否配置启动参数?',
|
|
313
325
|
default: false,
|
|
314
|
-
when: (answers) => answers.ideName !== 'codex'
|
|
326
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex'
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
type: 'confirm',
|
|
330
|
+
name: 'configureCodexLaunchArgs',
|
|
331
|
+
message: '是否配置 Codex 启动参数?',
|
|
332
|
+
default: false,
|
|
333
|
+
when: (answers) => (answers.ideName || this.presetIdeName) === 'codex'
|
|
315
334
|
},
|
|
316
335
|
{
|
|
317
336
|
type: 'confirm',
|
|
318
337
|
name: 'configureModels',
|
|
319
338
|
message: '是否配置模型参数?',
|
|
320
339
|
default: false,
|
|
321
|
-
when: (answers) => answers.ideName !== 'codex'
|
|
340
|
+
when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex'
|
|
322
341
|
}
|
|
323
342
|
]);
|
|
324
343
|
|
|
325
344
|
// 移除 ESC 键监听
|
|
326
345
|
this.removeESCListener(escListener);
|
|
327
|
-
|
|
346
|
+
|
|
347
|
+
// 如果是预设的 ideName,设置到 answers 中
|
|
348
|
+
if (!answers.ideName && this.presetIdeName) {
|
|
349
|
+
answers.ideName = this.presetIdeName;
|
|
350
|
+
}
|
|
351
|
+
|
|
328
352
|
if (answers.ideName === 'codex') {
|
|
329
353
|
answers.authMode = 'openai_api_key';
|
|
330
354
|
answers.tokenType = null;
|
|
331
355
|
answers.codexFiles = null;
|
|
356
|
+
|
|
357
|
+
// 从现有配置导入
|
|
358
|
+
if (answers.importFromExisting === 'import') {
|
|
359
|
+
const importedConfig = await this.importCodexConfig();
|
|
360
|
+
if (importedConfig) {
|
|
361
|
+
answers.authToken = importedConfig.apiKey;
|
|
362
|
+
answers.baseUrl = importedConfig.baseUrl;
|
|
363
|
+
} else {
|
|
364
|
+
Logger.warning('未能导入现有配置,请手动输入');
|
|
365
|
+
const manualAnswers = await this.prompt([
|
|
366
|
+
{
|
|
367
|
+
type: 'input',
|
|
368
|
+
name: 'baseUrl',
|
|
369
|
+
message: '请输入 OpenAI API 基础URL (如使用官方API可留空):',
|
|
370
|
+
default: ''
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
type: 'input',
|
|
374
|
+
name: 'authToken',
|
|
375
|
+
message: '请输入 OpenAI API Key (OPENAI_API_KEY):',
|
|
376
|
+
validate: (input) => input ? true : 'API Key 不能为空'
|
|
377
|
+
}
|
|
378
|
+
]);
|
|
379
|
+
answers.authToken = manualAnswers.authToken;
|
|
380
|
+
answers.baseUrl = manualAnswers.baseUrl;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Codex 启动参数配置
|
|
385
|
+
if (answers.configureCodexLaunchArgs) {
|
|
386
|
+
answers.launchArgs = await this.promptCodexLaunchArgsSelection();
|
|
387
|
+
}
|
|
332
388
|
}
|
|
333
389
|
|
|
334
390
|
await this.saveProvider(answers);
|
|
@@ -512,6 +568,110 @@ class ProviderAdder extends BaseCommand {
|
|
|
512
568
|
}
|
|
513
569
|
}
|
|
514
570
|
|
|
571
|
+
async importCodexConfig() {
|
|
572
|
+
try {
|
|
573
|
+
const { readCodexFiles } = require('../utils/codex-files');
|
|
574
|
+
const codexFiles = await readCodexFiles();
|
|
575
|
+
|
|
576
|
+
if (!codexFiles.authJson) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// 解析 auth.json 获取 API Key
|
|
581
|
+
const authData = JSON.parse(codexFiles.authJson);
|
|
582
|
+
const apiKey = authData.api_key || authData.openai_api_key || authData.OPENAI_API_KEY;
|
|
583
|
+
|
|
584
|
+
if (!apiKey) {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// 尝试从 config.toml 获取 base URL
|
|
589
|
+
let baseUrl = null;
|
|
590
|
+
if (codexFiles.configToml) {
|
|
591
|
+
const baseUrlMatch = codexFiles.configToml.match(/api_base\s*=\s*["']([^"']+)["']/);
|
|
592
|
+
if (baseUrlMatch) {
|
|
593
|
+
baseUrl = baseUrlMatch[1];
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
Logger.success(`成功从 ${codexFiles.codexHome} 导入配置`);
|
|
598
|
+
return { apiKey, baseUrl };
|
|
599
|
+
} catch (error) {
|
|
600
|
+
Logger.warning(`导入配置失败: ${error.message}`);
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async promptCodexLaunchArgsSelection() {
|
|
606
|
+
console.log(UIHelper.createTitle('配置 Codex 启动参数', UIHelper.icons.settings));
|
|
607
|
+
console.log();
|
|
608
|
+
console.log(UIHelper.createTooltip('选择要使用的 Codex 启动参数'));
|
|
609
|
+
console.log();
|
|
610
|
+
console.log(UIHelper.createHintLine([
|
|
611
|
+
['空格', '切换选中'],
|
|
612
|
+
['A', '全选'],
|
|
613
|
+
['I', '反选'],
|
|
614
|
+
['Enter', '确认选择'],
|
|
615
|
+
['ESC', '跳过配置']
|
|
616
|
+
]));
|
|
617
|
+
console.log();
|
|
618
|
+
|
|
619
|
+
const escListener = this.createESCListener(() => {
|
|
620
|
+
Logger.info('跳过 Codex 启动参数配置');
|
|
621
|
+
}, '跳过配置');
|
|
622
|
+
|
|
623
|
+
try {
|
|
624
|
+
const codexArgs = [
|
|
625
|
+
{
|
|
626
|
+
name: '--full-auto',
|
|
627
|
+
label: '全自动模式',
|
|
628
|
+
description: '自动批准所有操作',
|
|
629
|
+
checked: false
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
name: '--dangerously-bypass-approvals-and-sandbox',
|
|
633
|
+
label: '跳过审批和沙盒',
|
|
634
|
+
description: '危险:跳过所有安全检查',
|
|
635
|
+
checked: false
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: '--model',
|
|
639
|
+
label: '指定模型',
|
|
640
|
+
description: '使用特定模型 (需手动指定)',
|
|
641
|
+
checked: false
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
name: '--quiet',
|
|
645
|
+
label: '静默模式',
|
|
646
|
+
description: '减少输出信息',
|
|
647
|
+
checked: false
|
|
648
|
+
}
|
|
649
|
+
];
|
|
650
|
+
|
|
651
|
+
const { launchArgs } = await this.prompt([
|
|
652
|
+
{
|
|
653
|
+
type: 'checkbox',
|
|
654
|
+
name: 'launchArgs',
|
|
655
|
+
message: '请选择 Codex 启动参数:',
|
|
656
|
+
choices: codexArgs.map(arg => ({
|
|
657
|
+
name: `${arg.label} (${arg.name}) - ${arg.description}`,
|
|
658
|
+
value: arg.name,
|
|
659
|
+
checked: arg.checked
|
|
660
|
+
}))
|
|
661
|
+
}
|
|
662
|
+
]);
|
|
663
|
+
|
|
664
|
+
this.removeESCListener(escListener);
|
|
665
|
+
return launchArgs;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
this.removeESCListener(escListener);
|
|
668
|
+
if (this.isEscCancelled(error)) {
|
|
669
|
+
return [];
|
|
670
|
+
}
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
515
675
|
printProviderSummary(answers, launchArgs, modelConfig) {
|
|
516
676
|
const finalDisplayName = answers.displayName || answers.name;
|
|
517
677
|
Logger.success(`供应商 '${finalDisplayName}' 添加成功!`);
|
|
@@ -528,6 +688,9 @@ class ProviderAdder extends BaseCommand {
|
|
|
528
688
|
if (answers.authToken) {
|
|
529
689
|
console.log(chalk.gray(` OPENAI_API_KEY: ${answers.authToken}`));
|
|
530
690
|
}
|
|
691
|
+
if (answers.launchArgs && answers.launchArgs.length > 0) {
|
|
692
|
+
console.log(chalk.gray(` 启动参数: ${answers.launchArgs.join(' ')}`));
|
|
693
|
+
}
|
|
531
694
|
console.log(chalk.green('\n🎉 供应商添加完成!正在返回主界面...'));
|
|
532
695
|
return;
|
|
533
696
|
}
|
|
@@ -571,8 +734,8 @@ class ProviderAdder extends BaseCommand {
|
|
|
571
734
|
}
|
|
572
735
|
}
|
|
573
736
|
|
|
574
|
-
async function addCommand() {
|
|
575
|
-
const adder = new ProviderAdder();
|
|
737
|
+
async function addCommand(options = {}) {
|
|
738
|
+
const adder = new ProviderAdder(options);
|
|
576
739
|
try {
|
|
577
740
|
await adder.interactive();
|
|
578
741
|
} catch (error) {
|
package/src/commands/list.js
CHANGED
|
@@ -47,7 +47,12 @@ class ProviderLister {
|
|
|
47
47
|
const availabilityText = this._formatAvailability(availability);
|
|
48
48
|
const nameColor = isCurrent ? chalk.green : chalk.white;
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
// IDE 类型标签
|
|
51
|
+
const ideTag = provider.ideName === 'codex'
|
|
52
|
+
? chalk.cyan('[Codex]')
|
|
53
|
+
: chalk.magenta('[Claude]');
|
|
54
|
+
|
|
55
|
+
console.log(`${status} ${availabilityIcon} ${ideTag} ${nameColor(provider.name)} (${provider.displayName}) - ${availabilityText}`);
|
|
51
56
|
|
|
52
57
|
if (provider.ideName === 'codex') {
|
|
53
58
|
console.log(chalk.gray(' IDE: Codex CLI'));
|
package/src/commands/switch.js
CHANGED
|
@@ -20,6 +20,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
20
20
|
this.latestStatusMap = {};
|
|
21
21
|
this.currentPromptContext = null;
|
|
22
22
|
this.activeStatusRefresh = null;
|
|
23
|
+
this.filter = null;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async validateProvider(providerName) {
|
|
@@ -35,11 +36,9 @@ class EnvSwitcher extends BaseCommand {
|
|
|
35
36
|
try {
|
|
36
37
|
this.clearScreen();
|
|
37
38
|
const provider = await this.validateProvider(providerName);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
const availableArgs = this.getAvailableLaunchArgs();
|
|
39
|
+
const isCodex = provider.ideName === 'codex';
|
|
40
|
+
const availableArgs = isCodex ? this.getCodexLaunchArgs() : this.getAvailableLaunchArgs();
|
|
41
|
+
const ideDisplayName = isCodex ? 'Codex CLI' : 'Claude Code';
|
|
43
42
|
|
|
44
43
|
console.log(UIHelper.createTitle('启动配置', UIHelper.icons.launch));
|
|
45
44
|
console.log();
|
|
@@ -49,7 +48,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
49
48
|
['空格', '切换选中'],
|
|
50
49
|
['A', '全选'],
|
|
51
50
|
['I', '反选'],
|
|
52
|
-
['Enter',
|
|
51
|
+
['Enter', `启动 ${ideDisplayName}`],
|
|
53
52
|
['ESC', '返回供应商选择']
|
|
54
53
|
]));
|
|
55
54
|
console.log();
|
|
@@ -275,19 +274,60 @@ class EnvSwitcher extends BaseCommand {
|
|
|
275
274
|
];
|
|
276
275
|
}
|
|
277
276
|
|
|
277
|
+
getCodexLaunchArgs() {
|
|
278
|
+
return [
|
|
279
|
+
{
|
|
280
|
+
name: '--continue',
|
|
281
|
+
label: '继续上次对话',
|
|
282
|
+
description: '恢复上次的对话记录',
|
|
283
|
+
checked: false
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: '--full-auto',
|
|
287
|
+
label: '全自动模式',
|
|
288
|
+
description: '自动批准所有操作',
|
|
289
|
+
checked: false
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: '--dangerously-bypass-approvals-and-sandbox',
|
|
293
|
+
label: '跳过审批和沙盒',
|
|
294
|
+
description: '危险:跳过所有安全检查',
|
|
295
|
+
checked: false
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: '--quiet',
|
|
299
|
+
label: '静默模式',
|
|
300
|
+
description: '减少输出信息',
|
|
301
|
+
checked: false
|
|
302
|
+
}
|
|
303
|
+
];
|
|
304
|
+
}
|
|
305
|
+
|
|
278
306
|
// getArgDescription 方法已被移除,直接使用 arg.description
|
|
279
307
|
|
|
280
308
|
async showProviderSelection() {
|
|
281
309
|
try {
|
|
282
310
|
// 并行加载配置和准备界面
|
|
283
|
-
|
|
311
|
+
let providers = await this.configManager.ensureLoaded().then(() => this.configManager.listProviders());
|
|
312
|
+
|
|
313
|
+
// 应用过滤器
|
|
314
|
+
if (this.filter === 'codex') {
|
|
315
|
+
providers = providers.filter(p => p.ideName === 'codex');
|
|
316
|
+
} else if (this.filter === 'claude') {
|
|
317
|
+
providers = providers.filter(p => p.ideName !== 'codex');
|
|
318
|
+
}
|
|
284
319
|
|
|
285
320
|
const initialStatusMap = this._buildInitialStatusMap(providers);
|
|
286
321
|
// 显示欢迎界面(立即渲染)
|
|
287
322
|
this.showWelcomeScreen(providers, initialStatusMap, null);
|
|
288
323
|
|
|
289
324
|
if (providers.length === 0) {
|
|
290
|
-
|
|
325
|
+
if (this.filter) {
|
|
326
|
+
const filterName = this.filter === 'codex' ? 'Codex CLI' : 'Claude Code';
|
|
327
|
+
Logger.warning(`暂无 ${filterName} 供应商配置`);
|
|
328
|
+
} else {
|
|
329
|
+
Logger.warning('暂无配置的供应商');
|
|
330
|
+
}
|
|
291
331
|
Logger.info('请先运行 "akm add" 添加供应商配置');
|
|
292
332
|
return;
|
|
293
333
|
}
|
|
@@ -314,6 +354,10 @@ class EnvSwitcher extends BaseCommand {
|
|
|
314
354
|
const currentProvider = providers.find(p => p.current);
|
|
315
355
|
const defaultChoice = currentProvider ? currentProvider.name : providers[0]?.name;
|
|
316
356
|
|
|
357
|
+
// 构建提示信息
|
|
358
|
+
const filterSuffix = this.filter === 'codex' ? ' (Codex CLI)' : (this.filter === 'claude' ? ' (Claude Code)' : '');
|
|
359
|
+
const promptMessage = `请选择要切换的供应商${filterSuffix} (总计 ${providers.length} 个):`;
|
|
360
|
+
|
|
317
361
|
// 设置 ESC 键监听
|
|
318
362
|
const escListener = this.createESCListener(() => {
|
|
319
363
|
Logger.info('退出程序');
|
|
@@ -325,7 +369,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
325
369
|
{
|
|
326
370
|
type: 'list',
|
|
327
371
|
name: 'provider',
|
|
328
|
-
message:
|
|
372
|
+
message: promptMessage,
|
|
329
373
|
choices,
|
|
330
374
|
default: defaultChoice,
|
|
331
375
|
pageSize: 12
|
|
@@ -863,7 +907,11 @@ class EnvSwitcher extends BaseCommand {
|
|
|
863
907
|
const icon = this._iconForState(availability?.state);
|
|
864
908
|
const statusText = this._formatAvailability(availability);
|
|
865
909
|
const statusLabel = chalk.gray('-') + ' ' + statusText;
|
|
866
|
-
|
|
910
|
+
// IDE 类型标签
|
|
911
|
+
const ideTag = provider.ideName === 'codex'
|
|
912
|
+
? chalk.cyan('[Codex]')
|
|
913
|
+
: chalk.magenta('[Claude]');
|
|
914
|
+
const label = `${icon} ${ideTag} ${UIHelper.formatProvider(provider)}${isLastUsed ? UIHelper.colors.muted(' --- 上次使用') : ''} ${statusLabel}`;
|
|
867
915
|
|
|
868
916
|
return {
|
|
869
917
|
name: label,
|
|
@@ -1465,9 +1513,10 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1465
1513
|
}
|
|
1466
1514
|
}
|
|
1467
1515
|
|
|
1468
|
-
async function switchCommand(providerName) {
|
|
1516
|
+
async function switchCommand(providerName, options = {}) {
|
|
1469
1517
|
const switcher = new EnvSwitcher();
|
|
1470
|
-
|
|
1518
|
+
switcher.filter = options.filter || null;
|
|
1519
|
+
|
|
1471
1520
|
try {
|
|
1472
1521
|
if (providerName) {
|
|
1473
1522
|
await switcher.showLaunchArgsSelection(providerName);
|