@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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 全局常量定义
|
|
3
|
+
* 统一管理项目中重复出现的常量,提高可维护性
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============ 认证模式常量 ============
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 认证模式的显示名称映射
|
|
10
|
+
*/
|
|
11
|
+
const AUTH_MODE_DISPLAY = {
|
|
12
|
+
api_key: '通用API密钥模式',
|
|
13
|
+
auth_token: '认证令牌模式',
|
|
14
|
+
oauth_token: 'OAuth令牌模式'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 认证模式详细说明(用于添加供应商时)
|
|
19
|
+
*/
|
|
20
|
+
const AUTH_MODE_DISPLAY_DETAILED = {
|
|
21
|
+
api_key: '通用API密钥模式',
|
|
22
|
+
auth_token: '认证令牌模式 (仅 ANTHROPIC_AUTH_TOKEN)',
|
|
23
|
+
oauth_token: 'OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN)'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 所有支持的认证模式列表
|
|
28
|
+
*/
|
|
29
|
+
const AUTH_MODES = ['api_key', 'auth_token', 'oauth_token'];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Token 类型显示映射
|
|
33
|
+
*/
|
|
34
|
+
const TOKEN_TYPE_DISPLAY = {
|
|
35
|
+
auth_token: 'ANTHROPIC_AUTH_TOKEN',
|
|
36
|
+
api_key: 'ANTHROPIC_API_KEY'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ============ ESC 返回提示常量 ============
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ESC 导航提示消息
|
|
43
|
+
*/
|
|
44
|
+
const ESC_HINTS = {
|
|
45
|
+
BACK_TO_PROVIDER_SELECTION: '返回供应商选择',
|
|
46
|
+
BACK_TO_MANAGE_LIST: '返回管理列表',
|
|
47
|
+
BACK_TO_QUICK_SETTINGS: '返回快速设置',
|
|
48
|
+
BACK_TO_MAIN_MENU: '返回主菜单',
|
|
49
|
+
EXIT_PROGRAM: '退出程序',
|
|
50
|
+
CANCEL_ADD: '取消添加'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* ESC 日志消息
|
|
55
|
+
*/
|
|
56
|
+
const ESC_LOGS = {
|
|
57
|
+
BACK_TO_PROVIDER_SELECTION: '返回供应商选择',
|
|
58
|
+
BACK_TO_MANAGE_LIST: '返回管理列表',
|
|
59
|
+
BACK_TO_QUICK_SETTINGS: '返回快速设置',
|
|
60
|
+
BACK_TO_MAIN_MENU: '返回主菜单',
|
|
61
|
+
EXIT_PROGRAM: '退出程序',
|
|
62
|
+
CANCEL_ADD: '取消添加供应商',
|
|
63
|
+
CANCEL_LAUNCH: '已取消启动'
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ============ IDE 类型常量 ============
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* IDE 名称常量
|
|
70
|
+
*/
|
|
71
|
+
const IDE_NAMES = {
|
|
72
|
+
CLAUDE: 'claude',
|
|
73
|
+
CODEX: 'codex'
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* IDE 显示名称
|
|
78
|
+
*/
|
|
79
|
+
const IDE_DISPLAY_NAMES = {
|
|
80
|
+
claude: 'Claude Code',
|
|
81
|
+
codex: 'Codex CLI'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// ============ 状态检查常量 ============
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 供应商状态
|
|
88
|
+
*/
|
|
89
|
+
const PROVIDER_STATUS = {
|
|
90
|
+
ONLINE: 'online',
|
|
91
|
+
DEGRADED: 'degraded',
|
|
92
|
+
OFFLINE: 'offline',
|
|
93
|
+
PENDING: 'pending',
|
|
94
|
+
UNKNOWN: 'unknown'
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 状态图标映射
|
|
99
|
+
*/
|
|
100
|
+
const STATUS_ICONS = {
|
|
101
|
+
online: '🟢',
|
|
102
|
+
degraded: '🟡',
|
|
103
|
+
offline: '🔴',
|
|
104
|
+
pending: '⏳',
|
|
105
|
+
unknown: '⚪'
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 状态颜色配置 (chalk 颜色名称)
|
|
110
|
+
*/
|
|
111
|
+
const STATUS_COLORS = {
|
|
112
|
+
online: 'green',
|
|
113
|
+
degraded: 'yellow',
|
|
114
|
+
offline: 'red',
|
|
115
|
+
pending: 'gray',
|
|
116
|
+
unknown: 'gray'
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// ============ 操作类型常量 ============
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 特殊选择值
|
|
123
|
+
*/
|
|
124
|
+
const SPECIAL_SELECTION = {
|
|
125
|
+
ADD_PROVIDER: '__ADD__',
|
|
126
|
+
MANAGE_PROVIDER: '__MANAGE__',
|
|
127
|
+
OPEN_CONFIG: '__OPEN_CONFIG__',
|
|
128
|
+
EXIT: '__EXIT__',
|
|
129
|
+
BACK: 'back'
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// ============ 配置相关常量 ============
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 基础 URL 相关的常量
|
|
136
|
+
*/
|
|
137
|
+
const BASE_URL = {
|
|
138
|
+
OFFICIAL_DEFAULT: '✨ 官方默认服务器',
|
|
139
|
+
NOT_SET: '⚠️ 未设置'
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 当前状态显示
|
|
144
|
+
*/
|
|
145
|
+
const CURRENT_STATUS = {
|
|
146
|
+
IN_USE: '✅ 使用中',
|
|
147
|
+
NOT_IN_USE: '⚫ 未使用'
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// ============ 模型相关常量 ============
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 默认模型值
|
|
154
|
+
*/
|
|
155
|
+
const DEFAULT_MODELS = {
|
|
156
|
+
NOT_SET: '未设置'
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 模型类型
|
|
161
|
+
*/
|
|
162
|
+
const MODEL_TYPES = {
|
|
163
|
+
PRIMARY: 'primary',
|
|
164
|
+
SMALL_FAST: 'smallFast'
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// ============ 消息常量 ============
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 系统消息
|
|
171
|
+
*/
|
|
172
|
+
const MESSAGES = {
|
|
173
|
+
CONFIGURATION_COMPLETE: '🎉 供应商添加完成!正在返回主界面...',
|
|
174
|
+
NO_PROVIDER_CONFIGURED: '暂无配置的供应商',
|
|
175
|
+
NO_CODEX_PROVIDER: '暂无 Codex CLI 供应商配置',
|
|
176
|
+
NO_CLAUDE_PROVIDER: '暂无 Claude Code 供应商配置',
|
|
177
|
+
ADD_FIRST_PROVIDER: '请先运行 "akm add" 添加供应商配置'
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// ============ API 相关常量 ============
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* API 请求配置
|
|
184
|
+
*/
|
|
185
|
+
const API_CONFIG = {
|
|
186
|
+
// 请求超时时间(毫秒)
|
|
187
|
+
DEFAULT_TIMEOUT: 5000,
|
|
188
|
+
// 状态缓存时间(毫秒)
|
|
189
|
+
CACHE_TTL: 30000,
|
|
190
|
+
// 默认测试消息
|
|
191
|
+
TEST_MESSAGE: '你好',
|
|
192
|
+
// 默认最大 Token 数
|
|
193
|
+
MAX_TOKENS: 32,
|
|
194
|
+
// 默认模型
|
|
195
|
+
DEFAULT_MODEL: 'claude-haiku-4-5-20251001'
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 环境变量名称
|
|
200
|
+
*/
|
|
201
|
+
const ENV_VARS = {
|
|
202
|
+
ANTHROPIC_AUTH_TOKEN: 'ANTHROPIC_AUTH_TOKEN',
|
|
203
|
+
ANTHROPIC_API_KEY: 'ANTHROPIC_API_KEY',
|
|
204
|
+
OPENAI_API_KEY: 'OPENAI_API_KEY',
|
|
205
|
+
OPENAI_BASE_URL: 'OPENAI_BASE_URL',
|
|
206
|
+
CLAUDE_CODE_OAUTH_TOKEN: 'CLAUDE_CODE_OAUTH_TOKEN'
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
module.exports = {
|
|
210
|
+
// 认证模式
|
|
211
|
+
AUTH_MODE_DISPLAY,
|
|
212
|
+
AUTH_MODE_DISPLAY_DETAILED,
|
|
213
|
+
AUTH_MODES,
|
|
214
|
+
TOKEN_TYPE_DISPLAY,
|
|
215
|
+
|
|
216
|
+
// ESC 提示
|
|
217
|
+
ESC_HINTS,
|
|
218
|
+
ESC_LOGS,
|
|
219
|
+
|
|
220
|
+
// IDE
|
|
221
|
+
IDE_NAMES,
|
|
222
|
+
IDE_DISPLAY_NAMES,
|
|
223
|
+
|
|
224
|
+
// 状态
|
|
225
|
+
PROVIDER_STATUS,
|
|
226
|
+
STATUS_ICONS,
|
|
227
|
+
STATUS_COLORS,
|
|
228
|
+
|
|
229
|
+
// 操作
|
|
230
|
+
SPECIAL_SELECTION,
|
|
231
|
+
|
|
232
|
+
// 配置
|
|
233
|
+
BASE_URL,
|
|
234
|
+
CURRENT_STATUS,
|
|
235
|
+
|
|
236
|
+
// 模型
|
|
237
|
+
DEFAULT_MODELS,
|
|
238
|
+
MODEL_TYPES,
|
|
239
|
+
|
|
240
|
+
// 消息
|
|
241
|
+
MESSAGES,
|
|
242
|
+
|
|
243
|
+
// API
|
|
244
|
+
API_CONFIG,
|
|
245
|
+
ENV_VARS
|
|
246
|
+
};
|
package/src/index.js
CHANGED
|
@@ -2,14 +2,21 @@ require('./utils/inquirer-setup');
|
|
|
2
2
|
const { switchCommand } = require('./commands/switch');
|
|
3
3
|
const { Logger } = require('./utils/logger');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 主入口函数
|
|
7
|
+
* @param {string} providerName - 供应商名称
|
|
8
|
+
* @param {Object} options - 选项
|
|
9
|
+
* @param {boolean} options.quick - 快速启动
|
|
10
|
+
* @param {boolean} options.noArgs - 不使用任何启动参数
|
|
11
|
+
*/
|
|
12
|
+
async function main(providerName, options = {}) {
|
|
6
13
|
try {
|
|
7
14
|
if (providerName) {
|
|
8
15
|
// 直接切换到指定供应商
|
|
9
|
-
await switchCommand(providerName);
|
|
16
|
+
await switchCommand(providerName, options);
|
|
10
17
|
} else {
|
|
11
18
|
// 显示供应商选择界面
|
|
12
|
-
await switchCommand();
|
|
19
|
+
await switchCommand(null, options);
|
|
13
20
|
}
|
|
14
21
|
} catch (error) {
|
|
15
22
|
Logger.fatal(`程序执行失败: ${error.message}`);
|
package/src/utils/codex-files.js
CHANGED
|
@@ -121,21 +121,23 @@ function ensureApiKeyAuthMethod(configToml) {
|
|
|
121
121
|
return 'preferred_auth_method = "apikey"\n';
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
//
|
|
125
|
-
const authMethodRegex = /^preferred_auth_method\s*=\s*["']?
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
124
|
+
// 移除所有已存在的 preferred_auth_method 行(防止重复)
|
|
125
|
+
const authMethodRegex = /^preferred_auth_method\s*=\s*["']?[^"'\n]*["']?\s*\n?/gm;
|
|
126
|
+
let cleanedConfig = configToml.replace(authMethodRegex, '');
|
|
127
|
+
|
|
128
|
+
// 清理可能存在的损坏字符(如孤立的 { 或空行堆积)
|
|
129
|
+
cleanedConfig = cleanedConfig.replace(/^[{}]+\s*\n?/gm, '');
|
|
130
|
+
cleanedConfig = cleanedConfig.replace(/^\n{3,}/gm, '\n\n');
|
|
131
|
+
|
|
132
|
+
// 在文件开头添加 preferred_auth_method
|
|
133
|
+
const newLine = 'preferred_auth_method = "apikey"\n';
|
|
134
|
+
|
|
135
|
+
// 如果清理后为空,直接返回新配置
|
|
136
|
+
if (!cleanedConfig.trim()) {
|
|
137
|
+
return newLine;
|
|
135
138
|
}
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
return 'preferred_auth_method = "apikey"\n' + configToml;
|
|
140
|
+
return newLine + cleanedConfig;
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
/**
|
|
@@ -153,23 +155,32 @@ function updateApiBaseUrl(configToml, baseUrl) {
|
|
|
153
155
|
const baseUrlRegex = /^api_base_url\s*=\s*["']?[^"'\n]*["']?\s*\n?/m;
|
|
154
156
|
|
|
155
157
|
if (baseUrl) {
|
|
156
|
-
// 需要设置
|
|
157
|
-
const newLine = `api_base_url = "${baseUrl}"
|
|
158
|
+
// 需要设置 api_base_url
|
|
159
|
+
const newLine = `api_base_url = "${baseUrl}"`;
|
|
158
160
|
|
|
159
161
|
if (configToml.match(baseUrlRegex)) {
|
|
160
162
|
// 替换现有的
|
|
161
|
-
return configToml.replace(baseUrlRegex, newLine);
|
|
163
|
+
return configToml.replace(baseUrlRegex, newLine + '\n');
|
|
162
164
|
}
|
|
163
|
-
//
|
|
165
|
+
// 空配置,直接返回新行
|
|
164
166
|
if (configToml.length === 0) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
return newLine + '\n';
|
|
168
|
+
}
|
|
169
|
+
// 找到第一个 section [xxx],在它之前插入
|
|
170
|
+
const sectionMatch = configToml.match(/^\[/m);
|
|
171
|
+
if (sectionMatch) {
|
|
172
|
+
const insertPos = configToml.indexOf(sectionMatch[0]);
|
|
173
|
+
const before = configToml.slice(0, insertPos);
|
|
174
|
+
const after = configToml.slice(insertPos);
|
|
175
|
+
// 确保前面有换行分隔
|
|
176
|
+
const separator = before.endsWith('\n') ? '' : '\n';
|
|
177
|
+
return before + separator + newLine + '\n\n' + after;
|
|
167
178
|
}
|
|
168
|
-
//
|
|
179
|
+
// 没有 section,在文件末尾添加
|
|
169
180
|
const separator = configToml.endsWith('\n') ? '' : '\n';
|
|
170
|
-
return configToml + separator + newLine;
|
|
181
|
+
return configToml + separator + newLine + '\n';
|
|
171
182
|
} else {
|
|
172
|
-
// 移除
|
|
183
|
+
// 移除 api_base_url(使用官方 API)
|
|
173
184
|
return configToml.replace(baseUrlRegex, '');
|
|
174
185
|
}
|
|
175
186
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Opener Utility
|
|
3
|
+
* 使用默认应用打开配置文件
|
|
4
|
+
* @module utils/config-opener
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const fs = require('fs-extra');
|
|
2
8
|
const path = require('path');
|
|
3
9
|
const os = require('os');
|
|
@@ -5,10 +11,19 @@ const { spawn } = require('child_process');
|
|
|
5
11
|
|
|
6
12
|
const AKM_CONFIG_FILE = path.join(os.homedir(), '.akm-config.json');
|
|
7
13
|
|
|
14
|
+
/**
|
|
15
|
+
* 检查配置文件是否存在
|
|
16
|
+
* @returns {Promise<boolean>} 文件是否存在
|
|
17
|
+
*/
|
|
8
18
|
function ensureConfigExists() {
|
|
9
19
|
return fs.pathExists(AKM_CONFIG_FILE);
|
|
10
20
|
}
|
|
11
21
|
|
|
22
|
+
/**
|
|
23
|
+
* 使用系统默认应用打开文件
|
|
24
|
+
* @param {string} filePath - 文件路径
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
12
27
|
function openFileWithDefaultApp(filePath) {
|
|
13
28
|
return new Promise((resolve, reject) => {
|
|
14
29
|
let command;
|
package/src/utils/env-utils.js
CHANGED
|
@@ -14,7 +14,7 @@ function sanitizeEnvValue(value) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// 移除控制字符
|
|
17
|
-
|
|
17
|
+
const cleaned = value.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
|
|
18
18
|
|
|
19
19
|
// 检测可能的 shell 命令注入(允许 $ 因为 token 可能包含)
|
|
20
20
|
// 只禁止明确的命令分隔符和反引号执行
|
|
@@ -4,32 +4,32 @@ const { Logger } = require('./logger');
|
|
|
4
4
|
class ErrorHandler {
|
|
5
5
|
static handle(error, context = '') {
|
|
6
6
|
const contextStr = context ? `[${context}] ` : '';
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
switch (error.type) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
case 'CONFIG_NOT_FOUND':
|
|
10
|
+
Logger.error(`${contextStr}配置文件不存在,请先添加供应商`);
|
|
11
|
+
break;
|
|
12
|
+
case 'PROVIDER_NOT_FOUND':
|
|
13
|
+
Logger.error(`${contextStr}指定的供应商不存在`);
|
|
14
|
+
break;
|
|
15
|
+
case 'INVALID_TOKEN':
|
|
16
|
+
Logger.error(`${contextStr}Token格式无效`);
|
|
17
|
+
break;
|
|
18
|
+
case 'INVALID_URL':
|
|
19
|
+
Logger.error(`${contextStr}URL格式无效`);
|
|
20
|
+
break;
|
|
21
|
+
case 'INVALID_NAME':
|
|
22
|
+
Logger.error(`${contextStr}供应商名称格式无效`);
|
|
23
|
+
break;
|
|
24
|
+
case 'FILE_PERMISSION':
|
|
25
|
+
Logger.error(`${contextStr}文件权限不足`);
|
|
26
|
+
break;
|
|
27
|
+
case 'NETWORK_ERROR':
|
|
28
|
+
Logger.error(`${contextStr}网络连接错误`);
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
Logger.error(`${contextStr}${error.message || '未知错误'}`);
|
|
32
|
+
break;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -50,4 +50,4 @@ class ErrorHandler {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
module.exports = { ErrorHandler };
|
|
53
|
+
module.exports = { ErrorHandler };
|