@pikecode/api-key-manager 1.0.39 → 1.0.43
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 +396 -2
- package/bin/akm.js +121 -4
- package/package.json +12 -5
- package/src/CommandRegistry.js +25 -0
- package/src/commands/BaseCommand.js +67 -0
- package/src/commands/add.js +125 -152
- package/src/commands/backup.js +14 -1
- package/src/commands/batch.js +314 -0
- package/src/commands/benchmark.js +344 -0
- package/src/commands/current.js +30 -14
- package/src/commands/edit.js +66 -46
- package/src/commands/health.js +222 -0
- package/src/commands/list.js +30 -11
- package/src/commands/remove.js +22 -3
- package/src/commands/stats.js +282 -0
- package/src/commands/switch/launch-args-helper.js +84 -0
- package/src/commands/switch/status-helper.js +124 -0
- package/src/commands/switch.js +224 -163
- package/src/commands/validate.js +310 -0
- package/src/config.js +243 -2
- package/src/constants/index.js +246 -0
- package/src/index.js +10 -3
- package/src/utils/codex-files.js +33 -22
- package/src/utils/config-opener.js +15 -0
- package/src/utils/env-utils.js +1 -1
- package/src/utils/error-handler.js +26 -26
- package/src/utils/health-checker.js +350 -0
- package/src/utils/inquirer-setup.js +11 -0
- package/src/utils/provider-status-checker.js +29 -10
- package/src/utils/ui-helper.js +87 -85
- package/src/utils/update-checker.js +25 -7
- package/src/utils/validator.js +12 -12
package/src/commands/switch.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Switcher Command
|
|
3
|
+
* 供应商切换和管理的主命令
|
|
4
|
+
* @module commands/switch
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const path = require('path');
|
|
2
8
|
const inquirer = require('inquirer');
|
|
3
9
|
const chalk = require('chalk');
|
|
@@ -11,8 +17,15 @@ const { findSettingsConflict, backupSettingsFile, clearConflictKeys, saveSetting
|
|
|
11
17
|
const { BaseCommand } = require('./BaseCommand');
|
|
12
18
|
const { validator } = require('../utils/validator');
|
|
13
19
|
const { ProviderStatusChecker } = require('../utils/provider-status-checker');
|
|
14
|
-
const {
|
|
15
|
-
|
|
20
|
+
const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, BASE_URL } = require('../constants');
|
|
21
|
+
const { LaunchArgsHelper } = require('./switch/launch-args-helper');
|
|
22
|
+
const { StatusHelper } = require('./switch/status-helper');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 环境切换器类
|
|
26
|
+
* 提供交互式界面用于选择、切换、管理和启动 API 供应商
|
|
27
|
+
* @extends BaseCommand
|
|
28
|
+
*/
|
|
16
29
|
class EnvSwitcher extends BaseCommand {
|
|
17
30
|
constructor() {
|
|
18
31
|
super();
|
|
@@ -27,10 +40,17 @@ class EnvSwitcher extends BaseCommand {
|
|
|
27
40
|
|
|
28
41
|
async validateProvider(providerName) {
|
|
29
42
|
await this.configManager.load();
|
|
30
|
-
|
|
43
|
+
|
|
44
|
+
// 先尝试按名称查找,再尝试按别名查找
|
|
45
|
+
let provider = this.configManager.getProvider(providerName);
|
|
46
|
+
if (!provider) {
|
|
47
|
+
provider = this.configManager.getProviderByNameOrAlias(providerName);
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
if (!provider) {
|
|
32
51
|
throw new Error(`供应商 '${providerName}' 不存在\n使用 'akm list' 查看所有已配置的供应商`);
|
|
33
52
|
}
|
|
53
|
+
|
|
34
54
|
return provider;
|
|
35
55
|
}
|
|
36
56
|
|
|
@@ -40,7 +60,12 @@ class EnvSwitcher extends BaseCommand {
|
|
|
40
60
|
const provider = await this.validateProvider(providerName);
|
|
41
61
|
const isCodex = provider.ideName === 'codex';
|
|
42
62
|
const availableArgs = isCodex ? this.getCodexLaunchArgs() : this.getAvailableLaunchArgs();
|
|
43
|
-
|
|
63
|
+
|
|
64
|
+
// 优先使用上次使用的参数,如果没有则使用默认的 launchArgs
|
|
65
|
+
const defaultLaunchArgs = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0
|
|
66
|
+
? provider.lastUsedArgs
|
|
67
|
+
: (Array.isArray(provider.launchArgs) ? provider.launchArgs : []);
|
|
68
|
+
|
|
44
69
|
const knownArgNames = new Set(availableArgs.map(arg => arg.name));
|
|
45
70
|
const customLaunchArgs = defaultLaunchArgs
|
|
46
71
|
.filter(arg => typeof arg === 'string' && !knownArgNames.has(arg));
|
|
@@ -57,7 +82,14 @@ class EnvSwitcher extends BaseCommand {
|
|
|
57
82
|
}))
|
|
58
83
|
];
|
|
59
84
|
const ideDisplayName = isCodex ? 'Codex CLI' : 'Claude Code';
|
|
60
|
-
|
|
85
|
+
|
|
86
|
+
// 显示提示:是否使用上次的参数
|
|
87
|
+
const isUsingLastUsed = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0;
|
|
88
|
+
if (isUsingLastUsed) {
|
|
89
|
+
console.log(UIHelper.colors.muted('💡 正在使用上次的启动参数'));
|
|
90
|
+
console.log();
|
|
91
|
+
}
|
|
92
|
+
|
|
61
93
|
console.log(UIHelper.createTitle('启动配置', UIHelper.icons.launch));
|
|
62
94
|
console.log();
|
|
63
95
|
console.log(UIHelper.createCard('供应商', UIHelper.formatProvider(provider), UIHelper.icons.info));
|
|
@@ -70,13 +102,13 @@ class EnvSwitcher extends BaseCommand {
|
|
|
70
102
|
['ESC', '返回供应商选择']
|
|
71
103
|
]));
|
|
72
104
|
console.log();
|
|
73
|
-
|
|
105
|
+
|
|
74
106
|
// 设置 ESC 键监听
|
|
75
107
|
const escListener = this.createESCListener(() => {
|
|
76
108
|
Logger.info('返回供应商选择');
|
|
77
109
|
this.showProviderSelection();
|
|
78
110
|
}, '返回供应商选择');
|
|
79
|
-
|
|
111
|
+
|
|
80
112
|
// 显示启动参数选择界面
|
|
81
113
|
const choices = [
|
|
82
114
|
{
|
|
@@ -108,7 +140,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
108
140
|
}
|
|
109
141
|
throw error;
|
|
110
142
|
}
|
|
111
|
-
|
|
143
|
+
|
|
112
144
|
this.removeESCListener(escListener);
|
|
113
145
|
|
|
114
146
|
// 检查互斥参数
|
|
@@ -118,9 +150,12 @@ class EnvSwitcher extends BaseCommand {
|
|
|
118
150
|
return await this.showLaunchArgsSelection(providerName);
|
|
119
151
|
}
|
|
120
152
|
|
|
153
|
+
// 保存上次使用的启动参数
|
|
154
|
+
await this.configManager.updateLastUsedArgs(providerName, answers.selectedArgs);
|
|
155
|
+
|
|
121
156
|
// 选择参数后直接启动
|
|
122
157
|
await this.launchProvider(provider, answers.selectedArgs);
|
|
123
|
-
|
|
158
|
+
|
|
124
159
|
} catch (error) {
|
|
125
160
|
await this.handleError(error, '选择启动参数');
|
|
126
161
|
}
|
|
@@ -282,6 +317,64 @@ class EnvSwitcher extends BaseCommand {
|
|
|
282
317
|
}
|
|
283
318
|
}
|
|
284
319
|
|
|
320
|
+
/**
|
|
321
|
+
* 快速启动供应商(跳过参数选择)
|
|
322
|
+
* @param {string} providerName - 供应商名称
|
|
323
|
+
* @param {Object} options - 启动选项
|
|
324
|
+
* @param {boolean} options.quick - 使用上次的启动参数
|
|
325
|
+
* @param {boolean} options.noArgs - 不使用任何启动参数
|
|
326
|
+
*/
|
|
327
|
+
/**
|
|
328
|
+
* 快速启动供应商(跳过参数选择)
|
|
329
|
+
* @param {string} providerName - 供应商名称或别名
|
|
330
|
+
* @param {Object} options - 启动选项
|
|
331
|
+
* @param {boolean} options.quick - 使用上次的启动参数
|
|
332
|
+
* @param {boolean} options.noArgs - 不使用任何启动参数
|
|
333
|
+
*/
|
|
334
|
+
async quickLaunchProvider(providerName, options) {
|
|
335
|
+
try {
|
|
336
|
+
await this.configManager.ensureLoaded();
|
|
337
|
+
|
|
338
|
+
// 支持别名查找
|
|
339
|
+
let provider = this.configManager.getProvider(providerName);
|
|
340
|
+
if (!provider) {
|
|
341
|
+
provider = this.configManager.getProviderByNameOrAlias(providerName);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!provider) {
|
|
345
|
+
throw new Error(`供应商 '${providerName}' 不存在\n使用 'akm list' 查看所有已配置的供应商`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 确定使用的启动参数
|
|
349
|
+
let selectedArgs;
|
|
350
|
+
if (options.noArgs) {
|
|
351
|
+
// 使用空参数
|
|
352
|
+
selectedArgs = [];
|
|
353
|
+
console.log(UIHelper.colors.muted('💡 使用空参数启动'));
|
|
354
|
+
} else if (options.quick) {
|
|
355
|
+
// 使用上次的启动参数或默认参数
|
|
356
|
+
selectedArgs = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0
|
|
357
|
+
? provider.lastUsedArgs
|
|
358
|
+
: (Array.isArray(provider.launchArgs) ? provider.launchArgs : []);
|
|
359
|
+
|
|
360
|
+
if (Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0) {
|
|
361
|
+
console.log(UIHelper.colors.muted('💡 使用上次的启动参数: ' + selectedArgs.join(' ')));
|
|
362
|
+
} else {
|
|
363
|
+
console.log(UIHelper.colors.muted('💡 使用默认启动参数: ' + (selectedArgs.length > 0 ? selectedArgs.join(' ') : '(无)')));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 更新上次使用的参数(使用真实的 provider.name 而不是别名)
|
|
368
|
+
await this.configManager.updateLastUsedArgs(provider.name, selectedArgs);
|
|
369
|
+
|
|
370
|
+
// 直接启动供应商
|
|
371
|
+
await this.launchProvider(provider, selectedArgs);
|
|
372
|
+
|
|
373
|
+
} catch (error) {
|
|
374
|
+
await this.handleError(error, '快速启动供应商');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
285
378
|
getAvailableLaunchArgs() {
|
|
286
379
|
const { getClaudeLaunchArgs } = require('../utils/launch-args');
|
|
287
380
|
return getClaudeLaunchArgs();
|
|
@@ -315,7 +408,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
315
408
|
const initialStatusMap = this._buildInitialStatusMap(providers);
|
|
316
409
|
// 显示欢迎界面(立即渲染)
|
|
317
410
|
this.showWelcomeScreen(providers, initialStatusMap, null);
|
|
318
|
-
|
|
411
|
+
|
|
319
412
|
if (providers.length === 0) {
|
|
320
413
|
if (this.filter) {
|
|
321
414
|
const filterName = this.filter === 'codex' ? 'Codex CLI' : 'Claude Code';
|
|
@@ -335,7 +428,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
335
428
|
if (providers.length > 0) {
|
|
336
429
|
this._startStatusRefresh(providers);
|
|
337
430
|
}
|
|
338
|
-
|
|
431
|
+
|
|
339
432
|
// 添加特殊选项
|
|
340
433
|
choices.push(
|
|
341
434
|
new inquirer.Separator(),
|
|
@@ -371,7 +464,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
371
464
|
pageSize: 12
|
|
372
465
|
}
|
|
373
466
|
]);
|
|
374
|
-
|
|
467
|
+
|
|
375
468
|
// 移除 ESC 键监听
|
|
376
469
|
this.removeESCListener(escListener);
|
|
377
470
|
|
|
@@ -385,7 +478,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
385
478
|
const result = await this.handleSelection(answer.provider);
|
|
386
479
|
this.currentPromptContext = null;
|
|
387
480
|
return result;
|
|
388
|
-
|
|
481
|
+
|
|
389
482
|
} catch (error) {
|
|
390
483
|
await this.handleError(error, '显示供应商选择');
|
|
391
484
|
} finally {
|
|
@@ -408,11 +501,11 @@ class EnvSwitcher extends BaseCommand {
|
|
|
408
501
|
|
|
409
502
|
showWelcomeScreen(providers, statusMap = {}, statusError = null) {
|
|
410
503
|
this.clearScreen();
|
|
411
|
-
|
|
504
|
+
|
|
412
505
|
if (providers.length > 0) {
|
|
413
506
|
console.log(UIHelper.colors.info(`总共 ${providers.length} 个供应商配置`));
|
|
414
507
|
}
|
|
415
|
-
|
|
508
|
+
|
|
416
509
|
if (statusError) {
|
|
417
510
|
console.log();
|
|
418
511
|
console.log(UIHelper.createCard('状态检测', `检测失败: ${statusError.message}`, UIHelper.icons.warning));
|
|
@@ -431,18 +524,18 @@ class EnvSwitcher extends BaseCommand {
|
|
|
431
524
|
|
|
432
525
|
async handleSelection(selection) {
|
|
433
526
|
switch (selection) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
527
|
+
case '__ADD__':
|
|
528
|
+
// 使用CommandRegistry避免循环引用
|
|
529
|
+
const { registry } = require('../CommandRegistry');
|
|
530
|
+
return await registry.executeCommand('add');
|
|
531
|
+
case '__MANAGE__':
|
|
532
|
+
return await this.showManageMenu();
|
|
533
|
+
case '__EXIT__':
|
|
534
|
+
this.showExitScreen();
|
|
535
|
+
this.destroy();
|
|
536
|
+
process.exit(0);
|
|
537
|
+
default:
|
|
538
|
+
return await this.showLaunchArgsSelection(selection);
|
|
446
539
|
}
|
|
447
540
|
}
|
|
448
541
|
|
|
@@ -486,20 +579,20 @@ class EnvSwitcher extends BaseCommand {
|
|
|
486
579
|
}
|
|
487
580
|
throw error;
|
|
488
581
|
}
|
|
489
|
-
|
|
582
|
+
|
|
490
583
|
this.removeESCListener(escListener);
|
|
491
584
|
|
|
492
585
|
switch (answer.setting) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
586
|
+
case 'search':
|
|
587
|
+
return await this.showSearchProvider();
|
|
588
|
+
case 'batch':
|
|
589
|
+
return await this.showBatchEdit();
|
|
590
|
+
case 'global':
|
|
591
|
+
return await this.showGlobalSettings();
|
|
592
|
+
case 'stats':
|
|
593
|
+
return await this.showStatistics();
|
|
594
|
+
case 'back':
|
|
595
|
+
return await this.showProviderSelection();
|
|
503
596
|
}
|
|
504
597
|
}
|
|
505
598
|
|
|
@@ -514,13 +607,13 @@ class EnvSwitcher extends BaseCommand {
|
|
|
514
607
|
['ESC', '返回快速设置']
|
|
515
608
|
]));
|
|
516
609
|
console.log();
|
|
517
|
-
|
|
610
|
+
|
|
518
611
|
// 设置 ESC 键监听
|
|
519
612
|
const escListener = this.createESCListener(() => {
|
|
520
613
|
Logger.info('返回快速设置');
|
|
521
614
|
this.showQuickSettings();
|
|
522
615
|
}, '返回快速设置');
|
|
523
|
-
|
|
616
|
+
|
|
524
617
|
try {
|
|
525
618
|
await this.prompt([
|
|
526
619
|
{
|
|
@@ -536,7 +629,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
536
629
|
}
|
|
537
630
|
throw error;
|
|
538
631
|
}
|
|
539
|
-
|
|
632
|
+
|
|
540
633
|
this.removeESCListener(escListener);
|
|
541
634
|
|
|
542
635
|
return await this.showQuickSettings();
|
|
@@ -553,13 +646,13 @@ class EnvSwitcher extends BaseCommand {
|
|
|
553
646
|
['ESC', '返回快速设置']
|
|
554
647
|
]));
|
|
555
648
|
console.log();
|
|
556
|
-
|
|
649
|
+
|
|
557
650
|
// 设置 ESC 键监听
|
|
558
651
|
const escListener = this.createESCListener(() => {
|
|
559
652
|
Logger.info('返回快速设置');
|
|
560
653
|
this.showQuickSettings();
|
|
561
654
|
}, '返回快速设置');
|
|
562
|
-
|
|
655
|
+
|
|
563
656
|
try {
|
|
564
657
|
await this.prompt([
|
|
565
658
|
{
|
|
@@ -575,7 +668,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
575
668
|
}
|
|
576
669
|
throw error;
|
|
577
670
|
}
|
|
578
|
-
|
|
671
|
+
|
|
579
672
|
this.removeESCListener(escListener);
|
|
580
673
|
|
|
581
674
|
return await this.showQuickSettings();
|
|
@@ -612,12 +705,12 @@ class EnvSwitcher extends BaseCommand {
|
|
|
612
705
|
}
|
|
613
706
|
throw error;
|
|
614
707
|
}
|
|
615
|
-
|
|
708
|
+
|
|
616
709
|
this.removeESCListener(escListener);
|
|
617
710
|
|
|
618
711
|
await this.configManager.load();
|
|
619
712
|
const providers = this.configManager.listProviders();
|
|
620
|
-
const searchResults = providers.filter(p =>
|
|
713
|
+
const searchResults = providers.filter(p =>
|
|
621
714
|
p.name.toLowerCase().includes(answer.search.toLowerCase()) ||
|
|
622
715
|
p.displayName.toLowerCase().includes(answer.search.toLowerCase())
|
|
623
716
|
);
|
|
@@ -673,7 +766,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
673
766
|
}
|
|
674
767
|
throw error;
|
|
675
768
|
}
|
|
676
|
-
|
|
769
|
+
|
|
677
770
|
this.removeESCListener(escListener2);
|
|
678
771
|
|
|
679
772
|
if (result.provider === 'back') {
|
|
@@ -687,7 +780,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
687
780
|
await this.configManager.load();
|
|
688
781
|
const providers = this.configManager.listProviders();
|
|
689
782
|
this.clearScreen();
|
|
690
|
-
|
|
783
|
+
|
|
691
784
|
const totalProviders = providers.length;
|
|
692
785
|
const currentProvider = providers.find(p => p.current);
|
|
693
786
|
const totalUsage = providers.reduce((sum, p) => sum + (p.usageCount || 0), 0);
|
|
@@ -695,7 +788,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
695
788
|
|
|
696
789
|
console.log(UIHelper.createTitle('使用统计', UIHelper.icons.info));
|
|
697
790
|
console.log();
|
|
698
|
-
|
|
791
|
+
|
|
699
792
|
const stats = [
|
|
700
793
|
['总供应商数', totalProviders],
|
|
701
794
|
['当前供应商', currentProvider ? currentProvider.displayName : '无'],
|
|
@@ -703,7 +796,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
703
796
|
['最常用供应商', mostUsed ? mostUsed.displayName : '无'],
|
|
704
797
|
['创建时间', providers.length > 0 ? UIHelper.formatTime(providers[0].createdAt) : '无']
|
|
705
798
|
];
|
|
706
|
-
|
|
799
|
+
|
|
707
800
|
console.log(UIHelper.createTable(['项目', '数据'], stats));
|
|
708
801
|
console.log();
|
|
709
802
|
console.log(UIHelper.createHintLine([
|
|
@@ -717,7 +810,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
717
810
|
Logger.info('返回快速设置');
|
|
718
811
|
this.showQuickSettings();
|
|
719
812
|
}, '返回快速设置');
|
|
720
|
-
|
|
813
|
+
|
|
721
814
|
try {
|
|
722
815
|
await this.prompt([
|
|
723
816
|
{
|
|
@@ -733,7 +826,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
733
826
|
}
|
|
734
827
|
throw error;
|
|
735
828
|
}
|
|
736
|
-
|
|
829
|
+
|
|
737
830
|
this.removeESCListener(escListener);
|
|
738
831
|
|
|
739
832
|
return await this.showQuickSettings();
|
|
@@ -827,10 +920,10 @@ class EnvSwitcher extends BaseCommand {
|
|
|
827
920
|
['ESC', '返回主菜单']
|
|
828
921
|
]));
|
|
829
922
|
console.log();
|
|
830
|
-
|
|
923
|
+
|
|
831
924
|
console.log(UIHelper.createTitle('供应商管理', UIHelper.icons.list));
|
|
832
925
|
console.log();
|
|
833
|
-
|
|
926
|
+
|
|
834
927
|
if (providers.length === 0) {
|
|
835
928
|
console.log(UIHelper.createCard('提示', '暂无配置的供应商\n请先运行 "akm add" 添加供应商配置', UIHelper.icons.warning));
|
|
836
929
|
return await this.showProviderSelection();
|
|
@@ -871,7 +964,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
871
964
|
}
|
|
872
965
|
throw error;
|
|
873
966
|
}
|
|
874
|
-
|
|
967
|
+
|
|
875
968
|
this.removeESCListener(escListener);
|
|
876
969
|
|
|
877
970
|
this._cancelStatusRefresh();
|
|
@@ -879,7 +972,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
879
972
|
const result = await this.handleManageAction(answer.action);
|
|
880
973
|
this.currentPromptContext = null;
|
|
881
974
|
return result;
|
|
882
|
-
|
|
975
|
+
|
|
883
976
|
} catch (error) {
|
|
884
977
|
await this.handleError(error, '显示供应商管理');
|
|
885
978
|
} finally {
|
|
@@ -932,64 +1025,19 @@ class EnvSwitcher extends BaseCommand {
|
|
|
932
1025
|
}
|
|
933
1026
|
|
|
934
1027
|
_iconForState(state) {
|
|
935
|
-
|
|
936
|
-
return '🟢';
|
|
937
|
-
}
|
|
938
|
-
if (state === 'degraded') {
|
|
939
|
-
return '🟡';
|
|
940
|
-
}
|
|
941
|
-
if (state === 'offline') {
|
|
942
|
-
return '🔴';
|
|
943
|
-
}
|
|
944
|
-
if (state === 'pending') {
|
|
945
|
-
return '⏳';
|
|
946
|
-
}
|
|
947
|
-
return '⚪';
|
|
1028
|
+
return StatusHelper.getIconForState(state);
|
|
948
1029
|
}
|
|
949
1030
|
|
|
950
1031
|
_formatAvailability(availability) {
|
|
951
|
-
|
|
952
|
-
return chalk.gray('测试中...');
|
|
953
|
-
}
|
|
954
|
-
if (availability.state === 'online') {
|
|
955
|
-
return chalk.green(availability.label || '可用');
|
|
956
|
-
}
|
|
957
|
-
if (availability.state === 'degraded') {
|
|
958
|
-
return chalk.yellow(availability.label || '有限可用');
|
|
959
|
-
}
|
|
960
|
-
if (availability.state === 'offline') {
|
|
961
|
-
return chalk.red(availability.label || '不可用');
|
|
962
|
-
}
|
|
963
|
-
if (availability.state === 'pending') {
|
|
964
|
-
return chalk.gray(availability.label || '测试中...');
|
|
965
|
-
}
|
|
966
|
-
return chalk.gray(availability.label || '未知');
|
|
1032
|
+
return StatusHelper.formatAvailability(availability);
|
|
967
1033
|
}
|
|
968
1034
|
|
|
969
1035
|
_buildInitialStatusMap(providers) {
|
|
970
|
-
|
|
971
|
-
const map = {};
|
|
972
|
-
providers.forEach(provider => {
|
|
973
|
-
map[provider.name] = cached[provider.name] || {
|
|
974
|
-
state: 'pending',
|
|
975
|
-
label: '测试中...',
|
|
976
|
-
latency: null
|
|
977
|
-
};
|
|
978
|
-
});
|
|
979
|
-
return map;
|
|
1036
|
+
return StatusHelper.buildInitialStatusMap(providers, this.latestStatusMap || {});
|
|
980
1037
|
}
|
|
981
1038
|
|
|
982
1039
|
_buildErrorStatusMap(providers, error) {
|
|
983
|
-
|
|
984
|
-
const map = {};
|
|
985
|
-
providers.forEach(provider => {
|
|
986
|
-
map[provider.name] = {
|
|
987
|
-
state: 'offline',
|
|
988
|
-
label: message,
|
|
989
|
-
latency: null
|
|
990
|
-
};
|
|
991
|
-
});
|
|
992
|
-
return map;
|
|
1040
|
+
return StatusHelper.buildErrorStatusMap(providers, error);
|
|
993
1041
|
}
|
|
994
1042
|
|
|
995
1043
|
_startStatusRefresh(providers) {
|
|
@@ -1157,15 +1205,15 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1157
1205
|
|
|
1158
1206
|
async handleManageAction(action) {
|
|
1159
1207
|
switch (action) {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1208
|
+
case 'back':
|
|
1209
|
+
return await this.showProviderSelection();
|
|
1210
|
+
case 'exit':
|
|
1211
|
+
Logger.info('👋 再见!');
|
|
1212
|
+
this.destroy();
|
|
1213
|
+
process.exit(0);
|
|
1214
|
+
default:
|
|
1215
|
+
// 如果选择的是供应商名称,显示该供应商的详细信息
|
|
1216
|
+
return await this.showProviderDetails(action);
|
|
1169
1217
|
}
|
|
1170
1218
|
}
|
|
1171
1219
|
|
|
@@ -1174,7 +1222,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1174
1222
|
try {
|
|
1175
1223
|
const provider = await this.validateProvider(providerName);
|
|
1176
1224
|
this.clearScreen();
|
|
1177
|
-
|
|
1225
|
+
|
|
1178
1226
|
console.log(UIHelper.createTitle('供应商详情', UIHelper.icons.info));
|
|
1179
1227
|
console.log();
|
|
1180
1228
|
console.log(UIHelper.createHintLine([
|
|
@@ -1183,33 +1231,27 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1183
1231
|
['ESC', '返回管理列表']
|
|
1184
1232
|
]));
|
|
1185
1233
|
console.log();
|
|
1186
|
-
|
|
1187
|
-
const authModeDisplay = {
|
|
1188
|
-
api_key: '通用API密钥模式',
|
|
1189
|
-
auth_token: '认证令牌模式',
|
|
1190
|
-
oauth_token: 'OAuth令牌模式'
|
|
1191
|
-
};
|
|
1192
1234
|
|
|
1193
1235
|
const details = [
|
|
1194
1236
|
['供应商名称', provider.name],
|
|
1195
1237
|
['显示名称', provider.displayName],
|
|
1196
|
-
['认证模式',
|
|
1238
|
+
['认证模式', AUTH_MODE_DISPLAY[provider.authMode] || provider.authMode]
|
|
1197
1239
|
];
|
|
1198
1240
|
|
|
1199
1241
|
// 如果是 api_key 模式,添加 tokenType 信息
|
|
1200
1242
|
if (provider.authMode === 'api_key' && provider.tokenType) {
|
|
1201
|
-
const tokenTypeDisplay = provider.tokenType
|
|
1243
|
+
const tokenTypeDisplay = TOKEN_TYPE_DISPLAY[provider.tokenType];
|
|
1202
1244
|
details.push(['Token类型', tokenTypeDisplay]);
|
|
1203
1245
|
}
|
|
1204
1246
|
|
|
1205
1247
|
// 继续添加其他信息
|
|
1206
1248
|
const baseUrlDisplay = provider.baseUrl
|
|
1207
1249
|
|| ((provider.authMode === 'oauth_token' || provider.authMode === 'auth_token')
|
|
1208
|
-
?
|
|
1250
|
+
? BASE_URL.OFFICIAL_DEFAULT
|
|
1209
1251
|
: '⚠️ 未设置');
|
|
1210
1252
|
details.push(
|
|
1211
1253
|
['基础URL', baseUrlDisplay],
|
|
1212
|
-
['认证令牌', provider.authToken
|
|
1254
|
+
['认证令牌', provider.authToken || '未设置'],
|
|
1213
1255
|
['主模型', provider.models?.primary || '未设置'],
|
|
1214
1256
|
['快速模型', provider.models?.smallFast || '未设置'],
|
|
1215
1257
|
['创建时间', UIHelper.formatTime(provider.createdAt)],
|
|
@@ -1217,10 +1259,10 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1217
1259
|
['当前状态', provider.current ? '✅ 使用中' : '⚫ 未使用'],
|
|
1218
1260
|
['使用次数', provider.usageCount || 0]
|
|
1219
1261
|
);
|
|
1220
|
-
|
|
1262
|
+
|
|
1221
1263
|
console.log(UIHelper.createTable(['项目', '信息'], details));
|
|
1222
1264
|
console.log();
|
|
1223
|
-
|
|
1265
|
+
|
|
1224
1266
|
if (provider.launchArgs && provider.launchArgs.length > 0) {
|
|
1225
1267
|
console.log(UIHelper.createCard('默认启动参数', provider.launchArgs.join(', '), UIHelper.icons.settings));
|
|
1226
1268
|
console.log();
|
|
@@ -1256,24 +1298,24 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1256
1298
|
}
|
|
1257
1299
|
|
|
1258
1300
|
switch (answer.action) {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1301
|
+
case 'back':
|
|
1302
|
+
// 移除 ESC 键监听
|
|
1303
|
+
this.removeESCListener(escListener);
|
|
1304
|
+
return await this.showManageMenu();
|
|
1305
|
+
case 'edit':
|
|
1306
|
+
// 移除 ESC 键监听
|
|
1307
|
+
this.removeESCListener(escListener);
|
|
1308
|
+
return await this.editProvider(providerName);
|
|
1309
|
+
case 'remove':
|
|
1310
|
+
// 移除 ESC 键监听
|
|
1311
|
+
this.removeESCListener(escListener);
|
|
1312
|
+
return await this.removeProvider(providerName);
|
|
1313
|
+
case 'launch':
|
|
1314
|
+
// 移除 ESC 键监听
|
|
1315
|
+
this.removeESCListener(escListener);
|
|
1316
|
+
return await this.showLaunchArgsSelection(providerName);
|
|
1275
1317
|
}
|
|
1276
|
-
|
|
1318
|
+
|
|
1277
1319
|
} catch (error) {
|
|
1278
1320
|
// 移除 ESC 键监听
|
|
1279
1321
|
this.removeESCListener(escListener);
|
|
@@ -1285,9 +1327,9 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1285
1327
|
let escListener;
|
|
1286
1328
|
try {
|
|
1287
1329
|
await this.configManager.load();
|
|
1288
|
-
|
|
1330
|
+
let provider = this.configManager.getProvider(providerName);
|
|
1289
1331
|
this.clearScreen();
|
|
1290
|
-
|
|
1332
|
+
|
|
1291
1333
|
if (!provider) {
|
|
1292
1334
|
Logger.error(`供应商 '${providerName}' 不存在`);
|
|
1293
1335
|
return await this.showManageMenu();
|
|
@@ -1319,6 +1361,19 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1319
1361
|
message: '显示名称:',
|
|
1320
1362
|
default: provider.displayName,
|
|
1321
1363
|
prefillDefault: true
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
type: 'input',
|
|
1367
|
+
name: 'alias',
|
|
1368
|
+
message: '别名 (用于快速切换):',
|
|
1369
|
+
default: provider.alias,
|
|
1370
|
+
prefillDefault: true,
|
|
1371
|
+
validate: (input) => {
|
|
1372
|
+
if (!input) return true; // 别名是可选的
|
|
1373
|
+
const error = validator.validateName(input);
|
|
1374
|
+
if (error) return error;
|
|
1375
|
+
return true;
|
|
1376
|
+
}
|
|
1322
1377
|
}
|
|
1323
1378
|
];
|
|
1324
1379
|
|
|
@@ -1393,15 +1448,15 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1393
1448
|
return 'API Key (OPENAI_API_KEY):';
|
|
1394
1449
|
}
|
|
1395
1450
|
switch (answers.authMode) {
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1451
|
+
case 'api_key':
|
|
1452
|
+
const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
|
|
1453
|
+
return `Token (${tokenTypeLabel}):`;
|
|
1454
|
+
case 'auth_token':
|
|
1455
|
+
return '认证令牌 (ANTHROPIC_AUTH_TOKEN):';
|
|
1456
|
+
case 'oauth_token':
|
|
1457
|
+
return 'OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
|
|
1458
|
+
default:
|
|
1459
|
+
return '认证令牌:';
|
|
1405
1460
|
}
|
|
1406
1461
|
},
|
|
1407
1462
|
default: provider.authToken,
|
|
@@ -1450,17 +1505,18 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1450
1505
|
|
|
1451
1506
|
// 更新供应商配置
|
|
1452
1507
|
provider.displayName = answers.displayName || newName;
|
|
1508
|
+
provider.alias = answers.alias || null;
|
|
1453
1509
|
// oauth_token 模式不需要 baseUrl,显式设为 null
|
|
1454
1510
|
provider.baseUrl = answers.authMode === 'oauth_token' ? null : answers.baseUrl;
|
|
1455
1511
|
provider.authToken = answers.authToken;
|
|
1456
|
-
|
|
1512
|
+
|
|
1457
1513
|
// Claude Code 特定的更新
|
|
1458
1514
|
if (!isCodex) {
|
|
1459
1515
|
provider.authMode = answers.authMode;
|
|
1460
1516
|
if (answers.tokenType) {
|
|
1461
1517
|
provider.tokenType = answers.tokenType; // 仅在 authMode 为 'api_key' 时使用
|
|
1462
1518
|
}
|
|
1463
|
-
|
|
1519
|
+
|
|
1464
1520
|
// 更新模型配置
|
|
1465
1521
|
if (!provider.models) {
|
|
1466
1522
|
provider.models = {};
|
|
@@ -1479,11 +1535,11 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1479
1535
|
|
|
1480
1536
|
await this.configManager.save();
|
|
1481
1537
|
Logger.success(`供应商 '${newName}' 已更新`);
|
|
1482
|
-
|
|
1538
|
+
|
|
1483
1539
|
// 移除 ESC 键监听
|
|
1484
1540
|
this.removeESCListener(escListener);
|
|
1485
1541
|
return await this.showManageMenu();
|
|
1486
|
-
|
|
1542
|
+
|
|
1487
1543
|
} catch (error) {
|
|
1488
1544
|
// 移除 ESC 键监听
|
|
1489
1545
|
this.removeESCListener(escListener);
|
|
@@ -1498,7 +1554,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1498
1554
|
await this.configManager.load();
|
|
1499
1555
|
const provider = this.configManager.getProvider(providerName);
|
|
1500
1556
|
this.clearScreen();
|
|
1501
|
-
|
|
1557
|
+
|
|
1502
1558
|
if (!provider) {
|
|
1503
1559
|
Logger.error(`供应商 '${providerName}' 不存在`);
|
|
1504
1560
|
return await this.showManageMenu();
|
|
@@ -1538,7 +1594,7 @@ class EnvSwitcher extends BaseCommand {
|
|
|
1538
1594
|
// 移除 ESC 键监听
|
|
1539
1595
|
this.removeESCListener(escListener);
|
|
1540
1596
|
return await this.showManageMenu();
|
|
1541
|
-
|
|
1597
|
+
|
|
1542
1598
|
} catch (error) {
|
|
1543
1599
|
// 移除 ESC 键监听
|
|
1544
1600
|
this.removeESCListener(escListener);
|
|
@@ -1554,7 +1610,12 @@ async function switchCommand(providerName, options = {}) {
|
|
|
1554
1610
|
|
|
1555
1611
|
try {
|
|
1556
1612
|
if (providerName) {
|
|
1557
|
-
|
|
1613
|
+
// 如果指定了 quick 或 noArgs 选项,直接启动
|
|
1614
|
+
if (options.quick || options.noArgs) {
|
|
1615
|
+
await switcher.quickLaunchProvider(providerName, options);
|
|
1616
|
+
} else {
|
|
1617
|
+
await switcher.showLaunchArgsSelection(providerName);
|
|
1618
|
+
}
|
|
1558
1619
|
} else {
|
|
1559
1620
|
await switcher.showProviderSelection();
|
|
1560
1621
|
}
|
|
@@ -1566,7 +1627,7 @@ async function switchCommand(providerName, options = {}) {
|
|
|
1566
1627
|
|
|
1567
1628
|
async function editCommand(providerName) {
|
|
1568
1629
|
const switcher = new EnvSwitcher();
|
|
1569
|
-
|
|
1630
|
+
|
|
1570
1631
|
try {
|
|
1571
1632
|
await switcher.editProvider(providerName);
|
|
1572
1633
|
} finally {
|