@pikecode/api-key-manager 1.0.39 → 1.0.42
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 +120 -3
- 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/current.js
CHANGED
|
@@ -1,19 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Current Config Command
|
|
3
|
+
* 显示当前激活的供应商配置
|
|
4
|
+
* @module commands/current
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const chalk = require('chalk');
|
|
2
8
|
const { configManager } = require('../config');
|
|
3
9
|
const { Logger } = require('../utils/logger');
|
|
4
10
|
const { maybeMaskToken } = require('../utils/secrets');
|
|
11
|
+
const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, BASE_URL, CURRENT_STATUS } = require('../constants');
|
|
5
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 当前配置显示类
|
|
15
|
+
* 用于显示当前激活的 API 供应商配置详情
|
|
16
|
+
*/
|
|
6
17
|
class CurrentConfig {
|
|
18
|
+
/**
|
|
19
|
+
* 创建当前配置显示器实例
|
|
20
|
+
*/
|
|
7
21
|
constructor() {
|
|
8
22
|
this.configManager = configManager;
|
|
9
23
|
}
|
|
10
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 显示当前供应商配置
|
|
27
|
+
* @param {Object} options - 显示选项
|
|
28
|
+
* @param {boolean} [options.showToken=true] - 是否显示完整 token
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
11
31
|
async show(options = {}) {
|
|
12
32
|
try {
|
|
13
|
-
|
|
33
|
+
// 默认显示完整 token,不再加密
|
|
34
|
+
const showToken = options.showToken !== false;
|
|
14
35
|
await this.configManager.ensureLoaded();
|
|
15
36
|
const currentProvider = this.configManager.getCurrentProvider();
|
|
16
|
-
|
|
37
|
+
|
|
17
38
|
if (!currentProvider) {
|
|
18
39
|
Logger.warning('未设置当前供应商');
|
|
19
40
|
Logger.info('请使用 "akm <供应商名>" 切换供应商');
|
|
@@ -22,7 +43,7 @@ class CurrentConfig {
|
|
|
22
43
|
|
|
23
44
|
console.log(chalk.blue('\n📍 当前配置:'));
|
|
24
45
|
console.log(chalk.gray('═'.repeat(60)));
|
|
25
|
-
|
|
46
|
+
|
|
26
47
|
console.log(chalk.green(`供应商: ${currentProvider.displayName}`));
|
|
27
48
|
console.log(chalk.gray(`内部名称: ${currentProvider.name}`));
|
|
28
49
|
|
|
@@ -50,16 +71,11 @@ class CurrentConfig {
|
|
|
50
71
|
}
|
|
51
72
|
|
|
52
73
|
// 显示认证模式
|
|
53
|
-
|
|
54
|
-
api_key: '通用API密钥模式',
|
|
55
|
-
auth_token: '认证令牌模式',
|
|
56
|
-
oauth_token: 'OAuth令牌模式'
|
|
57
|
-
};
|
|
58
|
-
console.log(chalk.gray(`认证模式: ${authModeDisplay[currentProvider.authMode] || currentProvider.authMode}`));
|
|
74
|
+
console.log(chalk.gray(`认证模式: ${AUTH_MODE_DISPLAY[currentProvider.authMode] || currentProvider.authMode}`));
|
|
59
75
|
|
|
60
76
|
// 如果是 api_key 模式,显示 tokenType
|
|
61
77
|
if (currentProvider.authMode === 'api_key' && currentProvider.tokenType) {
|
|
62
|
-
const tokenTypeDisplay = currentProvider.tokenType
|
|
78
|
+
const tokenTypeDisplay = TOKEN_TYPE_DISPLAY[currentProvider.tokenType];
|
|
63
79
|
console.log(chalk.gray(`Token类型: ${tokenTypeDisplay}`));
|
|
64
80
|
}
|
|
65
81
|
|
|
@@ -69,15 +85,15 @@ class CurrentConfig {
|
|
|
69
85
|
console.log(chalk.gray(`认证Token: ${maybeMaskToken(currentProvider.authToken, showToken)}`));
|
|
70
86
|
console.log(chalk.gray(`创建时间: ${new Date(currentProvider.createdAt).toLocaleString()}`));
|
|
71
87
|
console.log(chalk.gray(`最后使用: ${new Date(currentProvider.lastUsed).toLocaleString()}`));
|
|
72
|
-
|
|
88
|
+
|
|
73
89
|
// 显示模型配置
|
|
74
90
|
if (currentProvider.models && (currentProvider.models.primary || currentProvider.models.smallFast)) {
|
|
75
91
|
console.log(chalk.gray(`主模型: ${currentProvider.models.primary || '未设置'}`));
|
|
76
92
|
console.log(chalk.gray(`快速模型: ${currentProvider.models.smallFast || '未设置'}`));
|
|
77
93
|
}
|
|
78
|
-
|
|
94
|
+
|
|
79
95
|
console.log(chalk.gray('═'.repeat(60)));
|
|
80
|
-
|
|
96
|
+
|
|
81
97
|
// 显示环境变量设置方式
|
|
82
98
|
console.log(chalk.blue('\n🔧 环境变量设置:'));
|
|
83
99
|
if (currentProvider.baseUrl) {
|
|
@@ -103,7 +119,7 @@ class CurrentConfig {
|
|
|
103
119
|
console.log(chalk.gray(`set ANTHROPIC_SMALL_FAST_MODEL=${currentProvider.models.smallFast}`));
|
|
104
120
|
}
|
|
105
121
|
console.log(chalk.gray('claude'));
|
|
106
|
-
|
|
122
|
+
|
|
107
123
|
} catch (error) {
|
|
108
124
|
Logger.error(`获取当前配置失败: ${error.message}`);
|
|
109
125
|
throw error;
|
package/src/commands/edit.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Editor Command
|
|
3
|
+
* 编辑现有供应商配置
|
|
4
|
+
* @module commands/edit
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const inquirer = require('inquirer');
|
|
2
8
|
const chalk = require('chalk');
|
|
3
9
|
const { configManager } = require('../config');
|
|
@@ -5,13 +11,27 @@ const { validator } = require('../utils/validator');
|
|
|
5
11
|
const { Logger } = require('../utils/logger');
|
|
6
12
|
const { UIHelper } = require('../utils/ui-helper');
|
|
7
13
|
const { BaseCommand } = require('./BaseCommand');
|
|
14
|
+
const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, IDE_NAMES } = require('../constants');
|
|
8
15
|
|
|
16
|
+
/**
|
|
17
|
+
* 供应商编辑器类
|
|
18
|
+
* 用于交互式编辑现有的 API 供应商配置
|
|
19
|
+
* @extends BaseCommand
|
|
20
|
+
*/
|
|
9
21
|
class ProviderEditor extends BaseCommand {
|
|
22
|
+
/**
|
|
23
|
+
* 创建供应商编辑器实例
|
|
24
|
+
*/
|
|
10
25
|
constructor() {
|
|
11
26
|
super();
|
|
12
27
|
this.configManager = configManager;
|
|
13
28
|
}
|
|
14
29
|
|
|
30
|
+
/**
|
|
31
|
+
* 执行交互式编辑供应商流程
|
|
32
|
+
* @param {string} [providerName] - 要编辑的供应商名称,如果不提供则让用户选择
|
|
33
|
+
* @returns {Promise<void>}
|
|
34
|
+
*/
|
|
15
35
|
async interactive(providerName) {
|
|
16
36
|
await this.configManager.load();
|
|
17
37
|
const providers = this.configManager.listProviders();
|
|
@@ -23,40 +43,40 @@ class ProviderEditor extends BaseCommand {
|
|
|
23
43
|
|
|
24
44
|
let providerToEdit;
|
|
25
45
|
if (providerName) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
providerToEdit = this.configManager.getProvider(providerName);
|
|
47
|
+
if (!providerToEdit) {
|
|
48
|
+
Logger.error(`供应商 '${providerName}' 不存在。`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
31
51
|
} else {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
]);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
if (this.isEscCancelled(error)) {
|
|
48
|
-
return;
|
|
52
|
+
let selection;
|
|
53
|
+
try {
|
|
54
|
+
selection = await this.prompt([
|
|
55
|
+
{
|
|
56
|
+
type: 'list',
|
|
57
|
+
name: 'selectedProviderName',
|
|
58
|
+
message: '请选择要编辑的供应商:',
|
|
59
|
+
choices: [
|
|
60
|
+
...providers.map(p => ({ name: p.displayName || p.name, value: p.name })),
|
|
61
|
+
new inquirer.Separator(),
|
|
62
|
+
{ name: '取消', value: null }
|
|
63
|
+
]
|
|
49
64
|
}
|
|
50
|
-
|
|
65
|
+
]);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (this.isEscCancelled(error)) {
|
|
68
|
+
return;
|
|
51
69
|
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
const { selectedProviderName } = selection;
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
if (!selectedProviderName) {
|
|
76
|
+
Logger.info('操作已取消。');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
providerToEdit = this.configManager.getProvider(selectedProviderName);
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
console.log(UIHelper.createTitle(`编辑供应商: ${providerToEdit.displayName}`, UIHelper.icons.edit));
|
|
@@ -81,7 +101,7 @@ class ProviderEditor extends BaseCommand {
|
|
|
81
101
|
name: 'displayName',
|
|
82
102
|
message: '供应商显示名称:',
|
|
83
103
|
default: providerToEdit.displayName,
|
|
84
|
-
validate: (input) => validator.validateDisplayName(input) || true
|
|
104
|
+
validate: (input) => validator.validateDisplayName(input) || true
|
|
85
105
|
}
|
|
86
106
|
];
|
|
87
107
|
|
|
@@ -123,13 +143,13 @@ class ProviderEditor extends BaseCommand {
|
|
|
123
143
|
...codexArgs.map(arg => ({
|
|
124
144
|
name: `${arg.label} (${arg.name})${arg.description ? ' - ' + arg.description : ''}`,
|
|
125
145
|
value: arg.name,
|
|
126
|
-
checked: existingLaunchArgs.includes(arg.name)
|
|
146
|
+
checked: existingLaunchArgs.includes(arg.name)
|
|
127
147
|
})),
|
|
128
148
|
...customCodexArgs.map(arg => ({
|
|
129
149
|
name: `${arg} ${chalk.gray('(自定义参数)')}`,
|
|
130
150
|
value: arg,
|
|
131
|
-
checked: true
|
|
132
|
-
}))
|
|
151
|
+
checked: true
|
|
152
|
+
}))
|
|
133
153
|
],
|
|
134
154
|
validate: (selected) => {
|
|
135
155
|
const conflictError = checkExclusiveArgs(selected, codexArgs);
|
|
@@ -149,9 +169,9 @@ class ProviderEditor extends BaseCommand {
|
|
|
149
169
|
choices: [
|
|
150
170
|
{ name: '🔑 通用API密钥模式 - 支持 ANTHROPIC_API_KEY 和 ANTHROPIC_AUTH_TOKEN', value: 'api_key' },
|
|
151
171
|
{ name: '🔐 认证令牌模式 (仅 ANTHROPIC_AUTH_TOKEN) - 适用于某些服务商', value: 'auth_token' },
|
|
152
|
-
{ name: '🌐 OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN) - 适用于官方Claude Code', value: 'oauth_token' }
|
|
172
|
+
{ name: '🌐 OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN) - 适用于官方Claude Code', value: 'oauth_token' }
|
|
153
173
|
],
|
|
154
|
-
default: providerToEdit.authMode
|
|
174
|
+
default: providerToEdit.authMode
|
|
155
175
|
},
|
|
156
176
|
{
|
|
157
177
|
type: 'list',
|
|
@@ -180,23 +200,23 @@ class ProviderEditor extends BaseCommand {
|
|
|
180
200
|
}
|
|
181
201
|
return validator.validateUrl(input) || true;
|
|
182
202
|
},
|
|
183
|
-
when: (answers) => answers.authMode === 'api_key' || answers.authMode === 'auth_token'
|
|
203
|
+
when: (answers) => answers.authMode === 'api_key' || answers.authMode === 'auth_token'
|
|
184
204
|
},
|
|
185
205
|
{
|
|
186
206
|
type: 'input',
|
|
187
207
|
name: 'authToken',
|
|
188
208
|
message: (answers) => {
|
|
189
209
|
switch (answers.authMode) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
210
|
+
case 'api_key':
|
|
211
|
+
const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
|
|
212
|
+
return `Token (${tokenTypeLabel}):`;
|
|
213
|
+
case 'auth_token': return '认证令牌 (ANTHROPIC_AUTH_TOKEN):';
|
|
214
|
+
case 'oauth_token': return 'OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
|
|
215
|
+
default: return '认证令牌:';
|
|
196
216
|
}
|
|
197
217
|
},
|
|
198
218
|
default: providerToEdit.authToken,
|
|
199
|
-
validate: (input) => validator.validateToken(input) || true
|
|
219
|
+
validate: (input) => validator.validateToken(input) || true
|
|
200
220
|
},
|
|
201
221
|
{
|
|
202
222
|
type: 'checkbox',
|
|
@@ -205,8 +225,8 @@ class ProviderEditor extends BaseCommand {
|
|
|
205
225
|
choices: validator.getAvailableLaunchArgs().map(arg => ({
|
|
206
226
|
name: `${arg.label || arg.name} (${arg.name})${arg.description ? ' - ' + arg.description : ''}`,
|
|
207
227
|
value: arg.name,
|
|
208
|
-
checked: providerToEdit.launchArgs && providerToEdit.launchArgs.includes(arg.name)
|
|
209
|
-
}))
|
|
228
|
+
checked: providerToEdit.launchArgs && providerToEdit.launchArgs.includes(arg.name)
|
|
229
|
+
}))
|
|
210
230
|
}
|
|
211
231
|
);
|
|
212
232
|
}
|
|
@@ -262,7 +282,7 @@ class ProviderEditor extends BaseCommand {
|
|
|
262
282
|
// Retain original model settings unless we add editing for them
|
|
263
283
|
primaryModel: existingProvider.models?.primary || null,
|
|
264
284
|
smallFastModel: existingProvider.models?.smallFast || null,
|
|
265
|
-
setAsDefault: false
|
|
285
|
+
setAsDefault: false // Don't change default status on edit
|
|
266
286
|
});
|
|
267
287
|
}
|
|
268
288
|
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check Command
|
|
3
|
+
* 配置健康检查和告警
|
|
4
|
+
* @module commands/health
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const { configManager } = require('../config');
|
|
9
|
+
const { Logger } = require('../utils/logger');
|
|
10
|
+
const { HealthChecker } = require('../utils/health-checker');
|
|
11
|
+
const { UIHelper } = require('../utils/ui-helper');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 显示单个供应商的健康检查
|
|
15
|
+
* @param {string} providerName - 供应商名称或别名
|
|
16
|
+
* @param {Object} options - 选项
|
|
17
|
+
*/
|
|
18
|
+
async function showProviderHealth(providerName, options = {}) {
|
|
19
|
+
try {
|
|
20
|
+
await configManager.ensureLoaded();
|
|
21
|
+
|
|
22
|
+
// 支持别名查找
|
|
23
|
+
let provider = configManager.getProvider(providerName);
|
|
24
|
+
if (!provider) {
|
|
25
|
+
provider = configManager.getProviderByNameOrAlias(providerName);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!provider) {
|
|
29
|
+
Logger.error(`供应商 '${providerName}' 不存在`);
|
|
30
|
+
Logger.info('使用 "akm list" 查看所有已配置的供应商');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(chalk.blue(`\n🏥 健康检查: ${provider.displayName} (${provider.name})`));
|
|
35
|
+
console.log(chalk.gray('═'.repeat(60)));
|
|
36
|
+
console.log();
|
|
37
|
+
|
|
38
|
+
const checker = new HealthChecker();
|
|
39
|
+
const loadingInterval = UIHelper.createLoadingAnimation('检查中...');
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const results = await checker.performHealthCheck(provider, options);
|
|
43
|
+
UIHelper.clearLoadingAnimation(loadingInterval);
|
|
44
|
+
|
|
45
|
+
console.log(checker.formatHealthReport(results));
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
// 如果有问题,显示建议
|
|
49
|
+
if (results.overallStatus === 'error' || results.overallStatus === 'warning') {
|
|
50
|
+
console.log(chalk.yellow('💡 建议操作:'));
|
|
51
|
+
|
|
52
|
+
if (results.checks.tokenExpiry?.status === 'expired' || results.checks.tokenExpiry?.status === 'critical') {
|
|
53
|
+
console.log(chalk.yellow(' • 使用 "akm edit" 更新 Token'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (results.checks.quota?.status === 'exceeded' || results.checks.quota?.status === 'critical') {
|
|
57
|
+
console.log(chalk.yellow(' • 检查 API 配额使用情况'));
|
|
58
|
+
console.log(chalk.yellow(' • 考虑升级套餐或添加新的供应商'));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (results.checks.connectivity?.status === 'offline') {
|
|
62
|
+
console.log(chalk.yellow(' • 检查网络连接'));
|
|
63
|
+
console.log(chalk.yellow(' • 验证 Token 是否正确'));
|
|
64
|
+
console.log(chalk.yellow(' • 使用 "akm validate" 进行详细诊断'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (results.checks.lastUsed?.status === 'stale') {
|
|
68
|
+
console.log(chalk.yellow(' • 考虑删除长期未使用的配置'));
|
|
69
|
+
console.log(chalk.yellow(' • 或运行一次验证确保仍然可用'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
} catch (error) {
|
|
76
|
+
UIHelper.clearLoadingAnimation(loadingInterval);
|
|
77
|
+
Logger.error(`健康检查失败: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
Logger.error(`健康检查执行失败: ${error.message}`);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 显示所有供应商的健康检查
|
|
88
|
+
* @param {Object} options - 选项
|
|
89
|
+
*/
|
|
90
|
+
async function showAllHealth(options = {}) {
|
|
91
|
+
try {
|
|
92
|
+
await configManager.ensureLoaded();
|
|
93
|
+
let providers = configManager.listProviders();
|
|
94
|
+
|
|
95
|
+
// 应用过滤器
|
|
96
|
+
if (options.filter === 'codex') {
|
|
97
|
+
providers = providers.filter(p => p.ideName === 'codex');
|
|
98
|
+
} else if (options.filter === 'claude') {
|
|
99
|
+
providers = providers.filter(p => p.ideName !== 'codex');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (providers.length === 0) {
|
|
103
|
+
if (options.filter) {
|
|
104
|
+
const filterName = options.filter === 'codex' ? 'Codex CLI' : 'Claude Code';
|
|
105
|
+
Logger.warning(`暂无 ${filterName} 供应商配置`);
|
|
106
|
+
} else {
|
|
107
|
+
Logger.warning('暂无配置的供应商');
|
|
108
|
+
}
|
|
109
|
+
Logger.info('请使用 "akm add" 添加供应商配置');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const titleSuffix = options.filter === 'codex' ? ' (Codex CLI)' : (options.filter === 'claude' ? ' (Claude Code)' : '');
|
|
114
|
+
console.log(chalk.blue(`\n🏥 健康检查${titleSuffix}`));
|
|
115
|
+
console.log(chalk.gray('═'.repeat(60)));
|
|
116
|
+
console.log();
|
|
117
|
+
|
|
118
|
+
const checker = new HealthChecker();
|
|
119
|
+
const allResults = [];
|
|
120
|
+
let completedCount = 0;
|
|
121
|
+
const total = providers.length;
|
|
122
|
+
|
|
123
|
+
// 创建进度显示
|
|
124
|
+
const progressInterval = setInterval(() => {
|
|
125
|
+
process.stdout.write(`\r检查中... ${completedCount}/${total}`);
|
|
126
|
+
}, 100);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// 并行检查所有供应商
|
|
130
|
+
const checkPromises = providers.map(async (provider) => {
|
|
131
|
+
const results = await checker.performHealthCheck(provider, {
|
|
132
|
+
checkConnectivity: options.connectivity !== false
|
|
133
|
+
});
|
|
134
|
+
completedCount++;
|
|
135
|
+
return results;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const results = await Promise.all(checkPromises);
|
|
139
|
+
clearInterval(progressInterval);
|
|
140
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // 清除进度显示
|
|
141
|
+
|
|
142
|
+
allResults.push(...results);
|
|
143
|
+
|
|
144
|
+
// 显示结果
|
|
145
|
+
const currentProvider = configManager.getCurrentProvider();
|
|
146
|
+
|
|
147
|
+
results.forEach((result, index) => {
|
|
148
|
+
const provider = providers[index];
|
|
149
|
+
const isCurrent = provider.name === currentProvider?.name;
|
|
150
|
+
const statusIcon = isCurrent ? '✅' : '🔹';
|
|
151
|
+
|
|
152
|
+
console.log(checker.formatHealthReport(result));
|
|
153
|
+
if (isCurrent) {
|
|
154
|
+
console.log(chalk.green(' (当前活跃)'));
|
|
155
|
+
}
|
|
156
|
+
console.log();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// 显示统计
|
|
160
|
+
const errorCount = allResults.filter(r => r.overallStatus === 'error').length;
|
|
161
|
+
const warningCount = allResults.filter(r => r.overallStatus === 'warning').length;
|
|
162
|
+
const okCount = allResults.filter(r => r.overallStatus === 'ok' || r.overallStatus === 'info').length;
|
|
163
|
+
|
|
164
|
+
console.log(chalk.gray('═'.repeat(60)));
|
|
165
|
+
console.log(chalk.blue('📊 健康统计:'));
|
|
166
|
+
console.log(` 总计: ${total} 个供应商`);
|
|
167
|
+
console.log(` ${chalk.green('✓')} 健康: ${okCount}`);
|
|
168
|
+
console.log(` ${chalk.yellow('⚠')} 警告: ${warningCount}`);
|
|
169
|
+
console.log(` ${chalk.red('✗')} 错误: ${errorCount}`);
|
|
170
|
+
|
|
171
|
+
// 显示需要关注的问题
|
|
172
|
+
if (errorCount > 0 || warningCount > 0) {
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(chalk.yellow('⚠️ 发现需要关注的问题:'));
|
|
175
|
+
|
|
176
|
+
allResults.forEach((result) => {
|
|
177
|
+
const hasIssue = result.overallStatus === 'error' || result.overallStatus === 'warning';
|
|
178
|
+
if (hasIssue) {
|
|
179
|
+
const issues = Object.entries(result.checks)
|
|
180
|
+
.filter(([_, check]) => check.level === 'error' || check.level === 'warning')
|
|
181
|
+
.map(([name, check]) => `${checker._getCheckLabel(name)}: ${check.message}`);
|
|
182
|
+
|
|
183
|
+
if (issues.length > 0) {
|
|
184
|
+
console.log(chalk.yellow(` • ${result.displayName}:`));
|
|
185
|
+
issues.forEach(issue => {
|
|
186
|
+
console.log(chalk.yellow(` - ${issue}`));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
} catch (error) {
|
|
194
|
+
clearInterval(progressInterval);
|
|
195
|
+
Logger.error(`批量健康检查失败: ${error.message}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
} catch (error) {
|
|
199
|
+
Logger.error(`健康检查执行失败: ${error.message}`);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 健康检查命令
|
|
206
|
+
* @param {string|null} providerName - 供应商名称
|
|
207
|
+
* @param {Object} options - 选项
|
|
208
|
+
*/
|
|
209
|
+
async function healthCommand(providerName, options = {}) {
|
|
210
|
+
try {
|
|
211
|
+
if (providerName) {
|
|
212
|
+
await showProviderHealth(providerName, options);
|
|
213
|
+
} else {
|
|
214
|
+
await showAllHealth(options);
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
Logger.error(`健康检查失败: ${error.message}`);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { healthCommand };
|
package/src/commands/list.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Lister Command
|
|
3
|
+
* 列出和显示所有配置的供应商
|
|
4
|
+
* @module commands/list
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const chalk = require('chalk');
|
|
2
8
|
const { configManager } = require('../config');
|
|
3
9
|
const { Logger } = require('../utils/logger');
|
|
4
10
|
const { ProviderStatusChecker } = require('../utils/provider-status-checker');
|
|
5
11
|
const { maybeMaskToken } = require('../utils/secrets');
|
|
12
|
+
const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, BASE_URL } = require('../constants');
|
|
6
13
|
|
|
14
|
+
/**
|
|
15
|
+
* 供应商列表显示类
|
|
16
|
+
* 用于列出、过滤和显示所有配置的 API 供应商
|
|
17
|
+
*/
|
|
7
18
|
class ProviderLister {
|
|
19
|
+
/**
|
|
20
|
+
* 创建供应商列表显示器实例
|
|
21
|
+
*/
|
|
8
22
|
constructor() {
|
|
9
23
|
this.configManager = configManager;
|
|
10
24
|
this.statusChecker = new ProviderStatusChecker();
|
|
11
25
|
}
|
|
12
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 列出供应商配置
|
|
29
|
+
* @param {string|null} filter - 过滤器 ('codex', 'claude', 或 null 表示全部)
|
|
30
|
+
* @param {Object} options - 显示选项
|
|
31
|
+
* @param {boolean} [options.showToken=true] - 是否显示完整 token
|
|
32
|
+
* @returns {Promise<void>}
|
|
33
|
+
*/
|
|
13
34
|
async list(filter = null, options = {}) {
|
|
14
35
|
try {
|
|
15
|
-
|
|
36
|
+
// 默认显示完整 token,不再加密
|
|
37
|
+
const showToken = options.showToken !== false;
|
|
16
38
|
await this.configManager.ensureLoaded();
|
|
17
39
|
let providers = this.configManager.listProviders();
|
|
18
40
|
const currentProvider = this.configManager.getCurrentProvider();
|
|
@@ -54,7 +76,9 @@ class ProviderLister {
|
|
|
54
76
|
? chalk.cyan('[Codex]')
|
|
55
77
|
: chalk.magenta('[Claude]');
|
|
56
78
|
|
|
57
|
-
|
|
79
|
+
// 如果有别名,显示别名
|
|
80
|
+
const aliasText = provider.alias ? chalk.yellow(` [别名: ${provider.alias}]`) : '';
|
|
81
|
+
console.log(`${status} ${availabilityIcon} ${ideTag} ${nameColor(provider.name)} (${provider.displayName})${aliasText} - ${availabilityText}`);
|
|
58
82
|
|
|
59
83
|
if (provider.ideName === 'codex') {
|
|
60
84
|
console.log(chalk.gray(' IDE: Codex CLI'));
|
|
@@ -66,12 +90,7 @@ class ProviderLister {
|
|
|
66
90
|
}
|
|
67
91
|
} else {
|
|
68
92
|
// 显示认证模式
|
|
69
|
-
|
|
70
|
-
api_key: '通用API密钥模式',
|
|
71
|
-
auth_token: '认证令牌模式',
|
|
72
|
-
oauth_token: 'OAuth令牌模式'
|
|
73
|
-
};
|
|
74
|
-
console.log(chalk.gray(` 认证模式: ${authModeDisplay[provider.authMode] || provider.authMode}`));
|
|
93
|
+
console.log(chalk.gray(` 认证模式: ${AUTH_MODE_DISPLAY[provider.authMode] || provider.authMode}`));
|
|
75
94
|
|
|
76
95
|
// 根据不同模式显示对应的环境变量名称
|
|
77
96
|
if (provider.authMode === 'oauth_token') {
|
|
@@ -88,7 +107,7 @@ class ProviderLister {
|
|
|
88
107
|
console.log(chalk.gray(` ANTHROPIC_BASE_URL: ${provider.baseUrl}`));
|
|
89
108
|
}
|
|
90
109
|
if (provider.authToken) {
|
|
91
|
-
const tokenEnvName = provider.tokenType
|
|
110
|
+
const tokenEnvName = TOKEN_TYPE_DISPLAY[provider.tokenType] || 'ANTHROPIC_API_KEY';
|
|
92
111
|
console.log(chalk.gray(` ${tokenEnvName}: ${maybeMaskToken(provider.authToken, showToken)}`));
|
|
93
112
|
}
|
|
94
113
|
} else {
|
|
@@ -118,7 +137,7 @@ class ProviderLister {
|
|
|
118
137
|
});
|
|
119
138
|
|
|
120
139
|
console.log(chalk.gray('═'.repeat(60)));
|
|
121
|
-
|
|
140
|
+
|
|
122
141
|
if (currentProvider) {
|
|
123
142
|
console.log(chalk.green(`\n当前供应商: ${currentProvider.displayName}`));
|
|
124
143
|
} else {
|
|
@@ -126,7 +145,7 @@ class ProviderLister {
|
|
|
126
145
|
}
|
|
127
146
|
|
|
128
147
|
console.log(chalk.blue(`\n总计: ${providers.length} 个供应商`));
|
|
129
|
-
|
|
148
|
+
|
|
130
149
|
} catch (error) {
|
|
131
150
|
Logger.error(`获取供应商列表失败: ${error.message}`);
|
|
132
151
|
throw error;
|
package/src/commands/remove.js
CHANGED
|
@@ -1,19 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Remover Command
|
|
3
|
+
* 删除供应商配置
|
|
4
|
+
* @module commands/remove
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const inquirer = require('inquirer');
|
|
2
8
|
const { configManager } = require('../config');
|
|
3
9
|
const { Logger } = require('../utils/logger');
|
|
4
10
|
const { UIHelper } = require('../utils/ui-helper');
|
|
5
11
|
const { BaseCommand } = require('./BaseCommand');
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 供应商删除器类
|
|
15
|
+
* 用于删除已配置的 API 供应商
|
|
16
|
+
* @extends BaseCommand
|
|
17
|
+
*/
|
|
7
18
|
class ProviderRemover extends BaseCommand {
|
|
19
|
+
/**
|
|
20
|
+
* 创建供应商删除器实例
|
|
21
|
+
*/
|
|
8
22
|
constructor() {
|
|
9
23
|
super();
|
|
10
24
|
this.configManager = configManager;
|
|
11
25
|
}
|
|
12
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 删除供应商配置
|
|
29
|
+
* @param {string} [providerName] - 要删除的供应商名称,如果不提供则进入交互式选择
|
|
30
|
+
* @returns {Promise<void>}
|
|
31
|
+
*/
|
|
13
32
|
async remove(providerName) {
|
|
14
33
|
try {
|
|
15
34
|
await this.configManager.ensureLoaded();
|
|
16
|
-
|
|
35
|
+
|
|
17
36
|
// 如果没有指定供应商名称,显示选择列表
|
|
18
37
|
if (!providerName) {
|
|
19
38
|
return await this.interactiveRemove();
|
|
@@ -50,7 +69,7 @@ class ProviderRemover extends BaseCommand {
|
|
|
50
69
|
|
|
51
70
|
await this.configManager.removeProvider(providerName);
|
|
52
71
|
Logger.success(`供应商 '${provider.displayName}' 已删除`);
|
|
53
|
-
|
|
72
|
+
|
|
54
73
|
} catch (error) {
|
|
55
74
|
if (this.isEscCancelled(error)) {
|
|
56
75
|
return;
|
|
@@ -63,7 +82,7 @@ class ProviderRemover extends BaseCommand {
|
|
|
63
82
|
async interactiveRemove() {
|
|
64
83
|
await this.configManager.ensureLoaded();
|
|
65
84
|
const providers = this.configManager.listProviders();
|
|
66
|
-
|
|
85
|
+
|
|
67
86
|
if (providers.length === 0) {
|
|
68
87
|
Logger.warning('暂无配置的供应商');
|
|
69
88
|
return;
|