@pikecode/api-key-manager 1.0.45 → 1.1.0

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,253 @@
1
+ /**
2
+ * Claude Clean Command
3
+ * 分析并清理 ~/.claude.json 文件
4
+ * @module commands/claude-clean
5
+ */
6
+
7
+ const fs = require('fs-extra');
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const chalk = require('chalk');
11
+ const inquirer = require('inquirer');
12
+ const { Logger } = require('../utils/logger');
13
+ const { BaseCommand } = require('./BaseCommand');
14
+
15
+ const CLAUDE_JSON_PATH = path.join(os.homedir(), '.claude.json');
16
+
17
+ // 可清理的全局缓存字段(Claude Code 会自动重建)
18
+ const GLOBAL_CACHE_FIELDS = [
19
+ 'cachedStatsigGates',
20
+ 'cachedDynamicConfigs',
21
+ 'cachedGrowthBookFeatures',
22
+ 'clientDataCache'
23
+ ];
24
+
25
+ // 可清理的项目统计字段(历史数据,不影响功能)
26
+ const PROJECT_STAT_FIELDS = [
27
+ 'lastAPIDuration', 'lastCost', 'lastDuration',
28
+ 'lastLinesAdded', 'lastLinesRemoved', 'lastSessionId',
29
+ 'lastToolDuration', 'lastTotalCacheCreationInputTokens',
30
+ 'lastTotalCacheReadInputTokens', 'lastTotalInputTokens',
31
+ 'lastTotalOutputTokens', 'lastTotalWebSearchRequests',
32
+ 'lastAPIDurationWithoutRetries', 'lastModelUsage',
33
+ 'lastFpsAverage', 'lastFpsLow1Pct', 'lastSessionMetrics'
34
+ ];
35
+
36
+ // 可清理的项目缓存字段(会自动重建)
37
+ const PROJECT_CACHE_FIELDS = [
38
+ 'reactVulnerabilityCache',
39
+ 'exampleFiles',
40
+ 'exampleFilesGeneratedAt'
41
+ ];
42
+
43
+ class ClaudeCleaner extends BaseCommand {
44
+ constructor() {
45
+ super();
46
+ }
47
+
48
+ /**
49
+ * 分析 ~/.claude.json 并显示清理建议
50
+ */
51
+ async analyze() {
52
+ if (!await fs.pathExists(CLAUDE_JSON_PATH)) {
53
+ Logger.warning('~/.claude.json 不存在');
54
+ return;
55
+ }
56
+
57
+ const data = await fs.readJson(CLAUDE_JSON_PATH);
58
+ const totalSize = JSON.stringify(data).length;
59
+ const projects = data.projects || {};
60
+ const projectCount = Object.keys(projects).length;
61
+
62
+ console.log(chalk.bold('\n📊 ~/.claude.json 分析报告\n'));
63
+ console.log(chalk.gray(` 文件路径: ${CLAUDE_JSON_PATH}`));
64
+ console.log(chalk.gray(` 文件大小: ${this._formatSize(totalSize)}`));
65
+ console.log(chalk.gray(` 项目数量: ${projectCount} 个`));
66
+ console.log();
67
+
68
+ // 计算各方案可节省空间
69
+ const globalCacheSize = this._calcFieldsSize(data, GLOBAL_CACHE_FIELDS);
70
+ const statSize = this._calcProjectFieldsSize(projects, PROJECT_STAT_FIELDS);
71
+ const projCacheSize = this._calcProjectFieldsSize(projects, PROJECT_CACHE_FIELDS);
72
+ const staleProjects = this._findStaleProjects(projects);
73
+ const staleSize = staleProjects.reduce((sum, p) => sum + JSON.stringify(projects[p]).length, 0);
74
+
75
+ console.log(chalk.bold(' 清理方案预估:'));
76
+ console.log(chalk.green(` ✦ 保守清理 - 清理全局缓存 节省 ${this._formatSize(globalCacheSize)}`));
77
+ console.log(chalk.yellow(` ✦ 中等清理 - 清理缓存 + 项目统计 节省 ${this._formatSize(globalCacheSize + statSize)}`));
78
+ console.log(chalk.red(` ✦ 激进清理 - 清理所有 + 删除过期项目 节省 ${this._formatSize(globalCacheSize + statSize + projCacheSize + staleSize)}`));
79
+ console.log();
80
+
81
+ if (staleProjects.length > 0) {
82
+ console.log(chalk.gray(` 过期项目(路径不存在): ${staleProjects.length} 个`));
83
+ staleProjects.forEach(p => console.log(chalk.gray(` - ${p}`)));
84
+ console.log();
85
+ }
86
+
87
+ console.log(chalk.gray(' 运行 akm claude clean 执行清理'));
88
+ }
89
+
90
+ /**
91
+ * 交互式清理
92
+ */
93
+ async clean() {
94
+ if (!await fs.pathExists(CLAUDE_JSON_PATH)) {
95
+ Logger.warning('~/.claude.json 不存在');
96
+ return;
97
+ }
98
+
99
+ const data = await fs.readJson(CLAUDE_JSON_PATH);
100
+ const totalSize = JSON.stringify(data).length;
101
+ const projects = data.projects || {};
102
+
103
+ const globalCacheSize = this._calcFieldsSize(data, GLOBAL_CACHE_FIELDS);
104
+ const statSize = this._calcProjectFieldsSize(projects, PROJECT_STAT_FIELDS);
105
+ const projCacheSize = this._calcProjectFieldsSize(projects, PROJECT_CACHE_FIELDS);
106
+ const staleProjects = this._findStaleProjects(projects);
107
+ const staleSize = staleProjects.reduce((sum, p) => sum + JSON.stringify(projects[p]).length, 0);
108
+
109
+ console.log(chalk.bold('\n🧹 清理 ~/.claude.json\n'));
110
+ console.log(chalk.gray(` 当前大小: ${this._formatSize(totalSize)}`));
111
+ console.log();
112
+
113
+ let selection;
114
+ try {
115
+ selection = await this.prompt([
116
+ {
117
+ type: 'list',
118
+ name: 'mode',
119
+ message: '选择清理方案:',
120
+ choices: [
121
+ {
122
+ name: `保守清理 - 只清理全局缓存 ${chalk.green('(节省 ' + this._formatSize(globalCacheSize) + ')')}`,
123
+ value: 'conservative'
124
+ },
125
+ {
126
+ name: `中等清理 - 清理缓存 + 项目统计 ${chalk.yellow('(节省 ' + this._formatSize(globalCacheSize + statSize) + ')')}`,
127
+ value: 'moderate'
128
+ },
129
+ {
130
+ name: `激进清理 - 清理所有 + 删除过期项目 ${chalk.red('(节省 ' + this._formatSize(globalCacheSize + statSize + projCacheSize + staleSize) + ')')}`,
131
+ value: 'aggressive'
132
+ },
133
+ new inquirer.Separator(),
134
+ { name: '取消', value: null }
135
+ ]
136
+ }
137
+ ]);
138
+ } catch (error) {
139
+ if (this.isEscCancelled(error)) return;
140
+ throw error;
141
+ }
142
+
143
+ if (!selection.mode) {
144
+ Logger.info('操作已取消。');
145
+ return;
146
+ }
147
+
148
+ // 二次确认
149
+ let confirm;
150
+ try {
151
+ confirm = await this.prompt([
152
+ {
153
+ type: 'confirm',
154
+ name: 'ok',
155
+ message: `确认执行${selection.mode === 'conservative' ? '保守' : selection.mode === 'moderate' ? '中等' : '激进'}清理?(会自动备份)`,
156
+ default: false
157
+ }
158
+ ]);
159
+ } catch (error) {
160
+ if (this.isEscCancelled(error)) return;
161
+ throw error;
162
+ }
163
+
164
+ if (!confirm.ok) {
165
+ Logger.info('操作已取消。');
166
+ return;
167
+ }
168
+
169
+ // 备份
170
+ const backupPath = CLAUDE_JSON_PATH + `.backup-${Date.now()}`;
171
+ await fs.copy(CLAUDE_JSON_PATH, backupPath);
172
+ Logger.success(`已备份到: ${backupPath}`);
173
+
174
+ // 执行清理
175
+ const cleaned = JSON.parse(JSON.stringify(data)); // 深拷贝
176
+
177
+ // 所有方案都清理全局缓存
178
+ GLOBAL_CACHE_FIELDS.forEach(f => { delete cleaned[f]; });
179
+
180
+ if (selection.mode === 'moderate' || selection.mode === 'aggressive') {
181
+ // 清理项目统计
182
+ Object.keys(cleaned.projects || {}).forEach(p => {
183
+ PROJECT_STAT_FIELDS.forEach(f => { delete cleaned.projects[p][f]; });
184
+ });
185
+ }
186
+
187
+ if (selection.mode === 'aggressive') {
188
+ // 清理项目缓存
189
+ Object.keys(cleaned.projects || {}).forEach(p => {
190
+ PROJECT_CACHE_FIELDS.forEach(f => { delete cleaned.projects[p][f]; });
191
+ });
192
+ // 删除过期项目
193
+ staleProjects.forEach(p => { delete cleaned.projects[p]; });
194
+ }
195
+
196
+ await fs.writeJson(CLAUDE_JSON_PATH, cleaned, { spaces: 2 });
197
+
198
+ const newSize = JSON.stringify(cleaned).length;
199
+ const saved = totalSize - newSize;
200
+
201
+ Logger.success('清理完成!');
202
+ console.log(chalk.gray(` 清理前: ${this._formatSize(totalSize)}`));
203
+ console.log(chalk.gray(` 清理后: ${this._formatSize(newSize)}`));
204
+ console.log(chalk.green(` 节省了: ${this._formatSize(saved)} (${((saved / totalSize) * 100).toFixed(1)}%)`));
205
+ if (selection.mode === 'aggressive' && staleProjects.length > 0) {
206
+ console.log(chalk.gray(` 删除过期项目: ${staleProjects.length} 个`));
207
+ }
208
+ }
209
+
210
+ _calcFieldsSize(obj, fields) {
211
+ return fields.reduce((sum, f) => {
212
+ return sum + (obj[f] !== undefined ? JSON.stringify(obj[f]).length : 0);
213
+ }, 0);
214
+ }
215
+
216
+ _calcProjectFieldsSize(projects, fields) {
217
+ let total = 0;
218
+ Object.values(projects).forEach(proj => {
219
+ fields.forEach(f => {
220
+ if (proj[f] !== undefined) total += JSON.stringify(proj[f]).length;
221
+ });
222
+ });
223
+ return total;
224
+ }
225
+
226
+ _findStaleProjects(projects) {
227
+ return Object.keys(projects).filter(p => !fs.pathExistsSync('/' + p));
228
+ }
229
+
230
+ _formatSize(bytes) {
231
+ if (bytes < 1024) return bytes + ' B';
232
+ return (bytes / 1024).toFixed(1) + ' KB';
233
+ }
234
+ }
235
+
236
+ async function claudeCleanCommand(subcommand) {
237
+ const cleaner = new ClaudeCleaner();
238
+ try {
239
+ if (subcommand === 'analyze') {
240
+ await cleaner.analyze();
241
+ } else {
242
+ await cleaner.clean();
243
+ }
244
+ } catch (error) {
245
+ if (!cleaner.isEscCancelled(error)) {
246
+ Logger.error(`操作失败: ${error.message}`);
247
+ }
248
+ } finally {
249
+ cleaner.destroy();
250
+ }
251
+ }
252
+
253
+ module.exports = { claudeCleanCommand, ClaudeCleaner };
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Provider Clone Command
3
+ * 克隆现有供应商配置
4
+ * @module commands/clone
5
+ */
6
+
7
+ const inquirer = require('inquirer');
8
+ const chalk = require('chalk');
9
+ const { configManager } = require('../config');
10
+ const { validator } = require('../utils/validator');
11
+ const { Logger } = require('../utils/logger');
12
+ const { UIHelper } = require('../utils/ui-helper');
13
+ const { BaseCommand } = require('./BaseCommand');
14
+ const { getCodexLaunchArgs, checkExclusiveArgs } = require('../utils/launch-args');
15
+
16
+ /**
17
+ * 供应商克隆器类
18
+ * @extends BaseCommand
19
+ */
20
+ class ProviderCloner extends BaseCommand {
21
+ constructor() {
22
+ super();
23
+ this.configManager = configManager;
24
+ }
25
+
26
+ async interactive(sourceProviderName) {
27
+ await this.configManager.load();
28
+ const providers = this.configManager.listProviders();
29
+
30
+ if (providers.length === 0) {
31
+ Logger.warning('没有可克隆的供应商配置。请先添加一个。');
32
+ return;
33
+ }
34
+
35
+ // 选择源供应商
36
+ let source;
37
+ if (sourceProviderName) {
38
+ source = this.configManager.getProvider(sourceProviderName);
39
+ if (!source) {
40
+ Logger.error(`供应商 '${sourceProviderName}' 不存在。`);
41
+ return;
42
+ }
43
+ } else {
44
+ let selection;
45
+ try {
46
+ selection = await this.prompt([
47
+ {
48
+ type: 'list',
49
+ name: 'name',
50
+ message: '请选择要克隆的供应商:',
51
+ choices: [
52
+ ...providers.map(p => ({
53
+ name: `${p.displayName || p.name} ${chalk.gray('(' + p.name + ')')}`,
54
+ value: p.name
55
+ })),
56
+ new inquirer.Separator(),
57
+ { name: '取消', value: null }
58
+ ]
59
+ }
60
+ ]);
61
+ } catch (error) {
62
+ if (this.isEscCancelled(error)) return;
63
+ throw error;
64
+ }
65
+ if (!selection.name) {
66
+ Logger.info('操作已取消。');
67
+ return;
68
+ }
69
+ source = this.configManager.getProvider(selection.name);
70
+ }
71
+
72
+ console.log(UIHelper.createTitle(`克隆供应商: ${source.displayName || source.name}`, UIHelper.icons.copy || '📋'));
73
+ console.log();
74
+
75
+ const isCodex = source.ideName === 'codex';
76
+ const escListener = this.createESCListener(() => {
77
+ Logger.info('取消克隆供应商。');
78
+ }, '取消克隆');
79
+
80
+ try {
81
+ // 输入新名称和显示名称
82
+ let basicInfo;
83
+ try {
84
+ basicInfo = await this.prompt([
85
+ {
86
+ type: 'input',
87
+ name: 'name',
88
+ message: '新供应商名称:',
89
+ validate: (input) => {
90
+ if (!input || !input.trim()) return '名称不能为空';
91
+ if (input.trim() === source.name) return '新名称不能与源供应商相同';
92
+ const err = validator.validateName(input.trim());
93
+ if (err) return err;
94
+ if (this.configManager.getProvider(input.trim())) return `供应商 '${input.trim()}' 已存在,请使用其他名称`;
95
+ return true;
96
+ }
97
+ },
98
+ {
99
+ type: 'input',
100
+ name: 'displayName',
101
+ message: '新显示名称:',
102
+ default: `${source.displayName || source.name} (副本)`,
103
+ validate: (input) => validator.validateDisplayName(input) || true
104
+ }
105
+ ]);
106
+ } catch (error) {
107
+ this.removeESCListener(escListener);
108
+ if (this.isEscCancelled(error)) return;
109
+ throw error;
110
+ }
111
+
112
+ // 选择要修改的字段
113
+ let fieldsToModify;
114
+ try {
115
+ const fieldChoices = [
116
+ { name: `🔑 Token(当前: ${source.authToken ? source.authToken.slice(0, 8) + '...' : '未设置'})`, value: 'authToken', checked: true },
117
+ { name: `🌐 基础URL(当前: ${source.baseUrl || '默认'})`, value: 'baseUrl' }
118
+ ];
119
+ if (!isCodex) {
120
+ fieldChoices.push({ name: `🔐 认证模式(当前: ${source.authMode || 'api_key'})`, value: 'authMode' });
121
+ }
122
+ fieldChoices.push({ name: '🚀 启动参数', value: 'launchArgs' });
123
+
124
+ const fieldSelection = await this.prompt([
125
+ {
126
+ type: 'checkbox',
127
+ name: 'fields',
128
+ message: '选择要修改的字段(默认推荐修改 Token):',
129
+ choices: fieldChoices
130
+ }
131
+ ]);
132
+ fieldsToModify = fieldSelection.fields;
133
+ } catch (error) {
134
+ this.removeESCListener(escListener);
135
+ if (this.isEscCancelled(error)) return;
136
+ throw error;
137
+ }
138
+
139
+ // 收集修改值
140
+ const overrides = {};
141
+ if (fieldsToModify.length > 0) {
142
+ const questions = [];
143
+
144
+ if (fieldsToModify.includes('authToken')) {
145
+ questions.push({
146
+ type: 'input',
147
+ name: 'authToken',
148
+ message: isCodex ? 'OpenAI API Key:' : 'Token:',
149
+ default: source.authToken,
150
+ validate: (input) => validator.validateToken(input) || true
151
+ });
152
+ }
153
+
154
+ if (fieldsToModify.includes('baseUrl')) {
155
+ questions.push({
156
+ type: 'input',
157
+ name: 'baseUrl',
158
+ message: 'API 基础URL(留空使用默认):',
159
+ default: source.baseUrl || '',
160
+ validate: (input) => {
161
+ if (!input) return true;
162
+ return validator.validateUrl(input) || true;
163
+ }
164
+ });
165
+ }
166
+
167
+ if (!isCodex && fieldsToModify.includes('authMode')) {
168
+ questions.push({
169
+ type: 'list',
170
+ name: 'authMode',
171
+ message: '认证模式:',
172
+ choices: [
173
+ { name: '🔑 ANTHROPIC_API_KEY - 大多数第三方代理使用', value: 'api_key' },
174
+ { name: '🔐 ANTHROPIC_AUTH_TOKEN - 部分服务商使用', value: 'auth_token' }
175
+ ],
176
+ default: source.authMode || 'api_key'
177
+ });
178
+ }
179
+
180
+ if (fieldsToModify.includes('launchArgs')) {
181
+ if (isCodex) {
182
+ const codexArgs = getCodexLaunchArgs();
183
+ questions.push({
184
+ type: 'checkbox',
185
+ name: 'launchArgs',
186
+ message: 'Codex 启动参数:',
187
+ choices: codexArgs.map(arg => ({
188
+ name: `${arg.label} (${arg.name})${arg.description ? ' - ' + arg.description : ''}`,
189
+ value: arg.name,
190
+ checked: (source.launchArgs || []).includes(arg.name)
191
+ })),
192
+ validate: (selected) => checkExclusiveArgs(selected, codexArgs) || true
193
+ });
194
+ } else {
195
+ questions.push({
196
+ type: 'checkbox',
197
+ name: 'launchArgs',
198
+ message: '启动参数:',
199
+ choices: validator.getAvailableLaunchArgs().map(arg => ({
200
+ name: `${arg.label || arg.name} (${arg.name})${arg.description ? ' - ' + arg.description : ''}`,
201
+ value: arg.name,
202
+ checked: (source.launchArgs || []).includes(arg.name)
203
+ }))
204
+ });
205
+ }
206
+ }
207
+
208
+ if (questions.length > 0) {
209
+ let modifiedValues;
210
+ try {
211
+ modifiedValues = await this.prompt(questions);
212
+ } catch (error) {
213
+ this.removeESCListener(escListener);
214
+ if (this.isEscCancelled(error)) return;
215
+ throw error;
216
+ }
217
+ Object.assign(overrides, modifiedValues);
218
+ }
219
+ }
220
+
221
+ this.removeESCListener(escListener);
222
+
223
+ // 保存克隆的供应商
224
+ const newName = basicInfo.name.trim();
225
+ const authMode = overrides.authMode || source.authMode;
226
+ const baseUrl = overrides.baseUrl !== undefined ? overrides.baseUrl : source.baseUrl;
227
+
228
+ await this.configManager.addProvider(newName, {
229
+ displayName: basicInfo.displayName,
230
+ ideName: source.ideName,
231
+ authToken: overrides.authToken !== undefined ? overrides.authToken : source.authToken,
232
+ baseUrl,
233
+ authMode,
234
+ launchArgs: overrides.launchArgs !== undefined ? overrides.launchArgs : (source.launchArgs || []),
235
+ primaryModel: source.models?.primary || null,
236
+ smallFastModel: source.models?.smallFast || null,
237
+ setAsDefault: false
238
+ });
239
+
240
+ Logger.success(`供应商 '${basicInfo.displayName}' 克隆成功!`);
241
+ console.log(chalk.gray(` 源供应商: ${source.displayName || source.name} → 新供应商: ${basicInfo.displayName} (${newName})`));
242
+ console.log();
243
+
244
+ } catch (error) {
245
+ this.removeESCListener(escListener);
246
+ if (this.isEscCancelled(error)) return;
247
+ throw error;
248
+ }
249
+ }
250
+ }
251
+
252
+ async function cloneCommand(sourceProviderName) {
253
+ const cloner = new ProviderCloner();
254
+ try {
255
+ await cloner.interactive(sourceProviderName);
256
+ } catch (error) {
257
+ if (!cloner.isEscCancelled(error)) {
258
+ Logger.error(`克隆供应商失败: ${error.message}`);
259
+ }
260
+ } finally {
261
+ cloner.destroy();
262
+ }
263
+ }
264
+
265
+ module.exports = { cloneCommand, ProviderCloner };
@@ -8,7 +8,7 @@ const chalk = require('chalk');
8
8
  const { configManager } = require('../config');
9
9
  const { Logger } = require('../utils/logger');
10
10
  const { maybeMaskToken } = require('../utils/secrets');
11
- const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, BASE_URL, CURRENT_STATUS } = require('../constants');
11
+ const { AUTH_MODE_DISPLAY, BASE_URL, CURRENT_STATUS } = require('../constants');
12
12
 
13
13
  /**
14
14
  * 当前配置显示类
@@ -73,12 +73,6 @@ class CurrentConfig {
73
73
  // 显示认证模式
74
74
  console.log(chalk.gray(`认证模式: ${AUTH_MODE_DISPLAY[currentProvider.authMode] || currentProvider.authMode}`));
75
75
 
76
- // 如果是 api_key 模式,显示 tokenType
77
- if (currentProvider.authMode === 'api_key' && currentProvider.tokenType) {
78
- const tokenTypeDisplay = TOKEN_TYPE_DISPLAY[currentProvider.tokenType];
79
- console.log(chalk.gray(`Token类型: ${tokenTypeDisplay}`));
80
- }
81
-
82
76
  if (currentProvider.baseUrl) {
83
77
  console.log(chalk.gray(`基础URL: ${currentProvider.baseUrl}`));
84
78
  }
@@ -99,18 +93,10 @@ class CurrentConfig {
99
93
  if (currentProvider.baseUrl) {
100
94
  console.log(chalk.gray(`set ANTHROPIC_BASE_URL=${currentProvider.baseUrl}`));
101
95
  }
102
- if (currentProvider.authMode === 'oauth_token') {
103
- console.log(chalk.gray(`set CLAUDE_CODE_OAUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
104
- } else if (currentProvider.authMode === 'api_key') {
105
- // 根据 tokenType 显示对应的环境变量
106
- if (currentProvider.tokenType === 'auth_token') {
107
- console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
108
- } else {
109
- console.log(chalk.gray(`set ANTHROPIC_API_KEY=${maybeMaskToken(currentProvider.authToken, showToken)}`));
110
- }
111
- } else {
112
- // auth_token 模式
96
+ if (currentProvider.authMode === 'auth_token') {
113
97
  console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
98
+ } else {
99
+ console.log(chalk.gray(`set ANTHROPIC_API_KEY=${maybeMaskToken(currentProvider.authToken, showToken)}`));
114
100
  }
115
101
  if (currentProvider.models?.primary) {
116
102
  console.log(chalk.gray(`set ANTHROPIC_MODEL=${currentProvider.models.primary}`));
@@ -11,7 +11,7 @@ const { validator } = require('../utils/validator');
11
11
  const { Logger } = require('../utils/logger');
12
12
  const { UIHelper } = require('../utils/ui-helper');
13
13
  const { BaseCommand } = require('./BaseCommand');
14
- const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, IDE_NAMES } = require('../constants');
14
+ const { AUTH_MODE_DISPLAY, IDE_NAMES } = require('../constants');
15
15
 
16
16
  /**
17
17
  * 供应商编辑器类
@@ -167,53 +167,27 @@ class ProviderEditor extends BaseCommand {
167
167
  name: 'authMode',
168
168
  message: '认证模式:',
169
169
  choices: [
170
- { name: '🔑 通用API密钥模式 - 支持 ANTHROPIC_API_KEY 和 ANTHROPIC_AUTH_TOKEN', value: 'api_key' },
171
- { name: '🔐 认证令牌模式 (仅 ANTHROPIC_AUTH_TOKEN) - 适用于某些服务商', value: 'auth_token' },
172
- { name: '🌐 OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN) - 适用于官方Claude Code', value: 'oauth_token' }
170
+ { name: '🔑 ANTHROPIC_API_KEY - 大多数第三方代理使用', value: 'api_key' },
171
+ { name: '🔐 ANTHROPIC_AUTH_TOKEN - 部分服务商使用', value: 'auth_token' }
173
172
  ],
174
173
  default: providerToEdit.authMode
175
174
  },
176
- {
177
- type: 'list',
178
- name: 'tokenType',
179
- message: 'Token类型:',
180
- choices: [
181
- { name: '🔑 ANTHROPIC_API_KEY - 通用API密钥', value: 'api_key' },
182
- { name: '🔐 ANTHROPIC_AUTH_TOKEN - 认证令牌', value: 'auth_token' }
183
- ],
184
- default: providerToEdit.tokenType || 'api_key',
185
- when: (answers) => answers.authMode === 'api_key'
186
- },
187
175
  {
188
176
  type: 'input',
189
177
  name: 'baseUrl',
190
- message: (answers) => {
191
- if (answers.authMode === 'auth_token') {
192
- return 'API基础URL (留空使用官方API):';
193
- }
194
- return 'API基础URL:';
195
- },
178
+ message: 'API 基础URL (ANTHROPIC_BASE_URL):',
196
179
  default: providerToEdit.baseUrl || '',
197
- validate: (input, answers) => {
198
- if (answers.authMode === 'auth_token' && !input) {
199
- return true;
200
- }
180
+ validate: (input) => {
181
+ if (!input) return 'API 基础URL不能为空';
201
182
  return validator.validateUrl(input) || true;
202
- },
203
- when: (answers) => answers.authMode === 'api_key' || answers.authMode === 'auth_token'
183
+ }
204
184
  },
205
185
  {
206
186
  type: 'input',
207
187
  name: 'authToken',
208
188
  message: (answers) => {
209
- switch (answers.authMode) {
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 '认证令牌:';
216
- }
189
+ const envVar = answers.authMode === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
190
+ return `Token (${envVar}):`;
217
191
  },
218
192
  default: providerToEdit.authToken,
219
193
  validate: (input) => validator.validateToken(input) || true
@@ -269,15 +243,12 @@ class ProviderEditor extends BaseCommand {
269
243
  });
270
244
  } else {
271
245
  // Re-use addProvider which can overwrite existing providers
272
- // oauth_token 模式不需要 baseUrl,显式设为 null
273
- const baseUrl = answers.authMode === 'oauth_token' ? null : answers.baseUrl;
274
246
  await this.configManager.addProvider(name, {
275
247
  displayName: answers.displayName,
276
248
  ideName,
277
- baseUrl,
249
+ baseUrl: answers.baseUrl,
278
250
  authToken: answers.authToken,
279
251
  authMode: answers.authMode,
280
- tokenType: answers.tokenType, // 仅在 authMode 为 'api_key' 时使用
281
252
  launchArgs: answers.launchArgs,
282
253
  // Retain original model settings unless we add editing for them
283
254
  primaryModel: existingProvider.models?.primary || null,