@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.
@@ -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
- async function main(providerName) {
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}`);
@@ -121,21 +121,23 @@ function ensureApiKeyAuthMethod(configToml) {
121
121
  return 'preferred_auth_method = "apikey"\n';
122
122
  }
123
123
 
124
- // 检查是否已经设置了 preferred_auth_method
125
- const authMethodRegex = /^preferred_auth_method\s*=\s*["']?([^"'\n]+)["']?\s*$/m;
126
- const match = configToml.match(authMethodRegex);
127
-
128
- if (match) {
129
- if (match[1].trim() === 'apikey') {
130
- // 已经是 apikey,无需修改
131
- return configToml;
132
- }
133
- // 替换为 apikey
134
- return configToml.replace(authMethodRegex, 'preferred_auth_method = "apikey"');
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
- // 没有找到 preferred_auth_method,在文件开头添加
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
- // 需要设置 base_url
157
- const newLine = `api_base_url = "${baseUrl}"\n`;
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
- return newLine;
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
- // 移除 base_url(使用官方 API)
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;
@@ -14,7 +14,7 @@ function sanitizeEnvValue(value) {
14
14
  }
15
15
 
16
16
  // 移除控制字符
17
- let cleaned = value.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
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
- 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;
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 };