@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.
@@ -57,17 +57,38 @@ if (InputPrompt && !InputPrompt.prototype.__allowEmptyPatched) {
57
57
 
58
58
  const ESC_CANCELLED_ERROR_CODE = 'ESC_CANCELLED';
59
59
 
60
+ /**
61
+ * 命令基类
62
+ * 提供所有命令的通用功能,包括 ESC 键处理、提示交互等
63
+ */
60
64
  class BaseCommand {
65
+ /**
66
+ * 创建命令实例
67
+ * @param {Object} [options={}] - 配置选项
68
+ * @param {NodeJS.ReadStream} [options.input] - 输入流
69
+ */
61
70
  constructor(options = {}) {
62
71
  const input = options.input || process.stdin;
72
+ /** @type {EscNavigationManager} ESC 键管理器 */
63
73
  this.escManager = new EscNavigationManager(input);
74
+ /** @type {Object|null} 当前活动的提示 */
64
75
  this.activePrompt = null;
65
76
  }
66
77
 
78
+ /**
79
+ * 检查错误是否为 ESC 取消错误
80
+ * @param {Error} error - 错误对象
81
+ * @returns {boolean} 是否为 ESC 取消
82
+ */
67
83
  isEscCancelled(error) {
68
84
  return Boolean(error && error.code === ESC_CANCELLED_ERROR_CODE);
69
85
  }
70
86
 
87
+ /**
88
+ * 显示交互式提示
89
+ * @param {Array|Object} questions - Inquirer 问题配置
90
+ * @returns {Promise<Object>} 用户答案
91
+ */
71
92
  async prompt(questions) {
72
93
  const promptPromise = inquirer.prompt(questions);
73
94
  let settled = false;
@@ -204,6 +225,52 @@ class BaseCommand {
204
225
  }
205
226
  }
206
227
 
228
+ /**
229
+ * 带 ESC 监听的 prompt 执行
230
+ * 自动处理 ESC 监听器的创建、移除和错误处理
231
+ * @param {Array} questions - inquirer 问卷配置
232
+ * @param {string} escMessage - ESC 返回信息
233
+ * @param {Function} escCallback - ESC 按下时的回调函数
234
+ * @returns {Promise<Object>} - prompt 答案
235
+ */
236
+ async promptWithESC(questions, escMessage, escCallback) {
237
+ const escListener = this.createESCListener(escCallback, escMessage);
238
+ try {
239
+ return await this.prompt(questions);
240
+ } catch (error) {
241
+ if (this.isEscCancelled(error)) {
242
+ this.removeESCListener(escListener);
243
+ throw error;
244
+ }
245
+ throw error;
246
+ } finally {
247
+ this.removeESCListener(escListener);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * 带 ESC 监听的 prompt 执行,支持返回默认值
253
+ * @param {Array} questions - inquirer 问卷配置
254
+ * @param {string} escMessage - ESC 返回信息
255
+ * @param {Function} escCallback - ESC 按下时的回调函数
256
+ * @param {*} escReturnValue - ESC 按下时返回的值
257
+ * @returns {Promise<Object|*>} - prompt 答案或 escReturnValue
258
+ */
259
+ async promptWithESCAndDefault(questions, escMessage, escCallback, escReturnValue) {
260
+ const escListener = this.createESCListener(escCallback, escMessage);
261
+ try {
262
+ return await this.prompt(questions);
263
+ } catch (error) {
264
+ if (this.isEscCancelled(error)) {
265
+ this.removeESCListener(escListener);
266
+ return escReturnValue;
267
+ }
268
+ throw error;
269
+ } finally {
270
+ this.removeESCListener(escListener);
271
+ }
272
+ }
273
+
207
274
  destroy() {
208
275
  this.cleanupAllListeners();
209
276
  }
@@ -1,19 +1,43 @@
1
+ /**
2
+ * Provider Adder Command
3
+ * 添加新供应商的交互式命令
4
+ * @module commands/add
5
+ */
6
+
1
7
  const inquirer = require('inquirer');
2
8
  const chalk = require('chalk');
3
9
  const { configManager } = require('../config');
4
10
  const { validator } = require('../utils/validator');
5
11
  const { Logger } = require('../utils/logger');
6
12
  const { UIHelper } = require('../utils/ui-helper');
7
- const { maskToken } = require('../utils/secrets');
8
13
  const { BaseCommand } = require('./BaseCommand');
9
-
14
+ const {
15
+ AUTH_MODE_DISPLAY_DETAILED,
16
+ TOKEN_TYPE_DISPLAY,
17
+ IDE_NAMES
18
+ } = require('../constants');
19
+
20
+ /**
21
+ * 供应商添加器类
22
+ * 用于交互式添加新的 API 供应商配置
23
+ * @extends BaseCommand
24
+ */
10
25
  class ProviderAdder extends BaseCommand {
26
+ /**
27
+ * 创建供应商添加器实例
28
+ * @param {Object} options - 配置选项
29
+ * @param {string} [options.ideName] - 预设的 IDE 名称(claude-code 或 codex)
30
+ */
11
31
  constructor(options = {}) {
12
32
  super();
13
33
  this.configManager = configManager;
14
34
  this.presetIdeName = options.ideName || null;
15
35
  }
16
36
 
37
+ /**
38
+ * 执行交互式添加供应商流程
39
+ * @returns {Promise<void>}
40
+ */
17
41
  async interactive() {
18
42
  console.log(UIHelper.createTitle('添加新供应商', UIHelper.icons.add));
19
43
  console.log();
@@ -26,18 +50,10 @@ class ProviderAdder extends BaseCommand {
26
50
  ['ESC', '取消添加']
27
51
  ]));
28
52
  console.log();
29
-
30
- // 设置 ESC 键监听
31
- const escListener = this.createESCListener(() => {
32
- Logger.info('取消添加供应商');
33
- // 使用CommandRegistry避免循环引用
34
- const { registry } = require('../CommandRegistry');
35
- registry.executeCommand('switch');
36
- }, '取消添加');
37
53
 
38
54
  try {
39
55
  // 首先选择是否使用预设配置
40
- const typeAnswer = await this.prompt([
56
+ const typeAnswer = await this.promptWithESC([
41
57
  {
42
58
  type: 'list',
43
59
  name: 'providerType',
@@ -48,10 +64,12 @@ class ProviderAdder extends BaseCommand {
48
64
  ],
49
65
  default: 'custom'
50
66
  }
51
- ]);
52
-
53
- // 移除 ESC 键监听
54
- this.removeESCListener(escListener);
67
+ ], '取消添加', () => {
68
+ Logger.info('取消添加供应商');
69
+ // 使用CommandRegistry避免循环引用
70
+ const { registry } = require('../CommandRegistry');
71
+ registry.executeCommand('switch');
72
+ });
55
73
 
56
74
  if (typeAnswer.providerType === 'official_oauth') {
57
75
  return await this.addOfficialOAuthProvider();
@@ -59,8 +77,6 @@ class ProviderAdder extends BaseCommand {
59
77
  return await this.addCustomProvider();
60
78
  }
61
79
  } catch (error) {
62
- // 移除 ESC 键监听
63
- this.removeESCListener(escListener);
64
80
  if (this.isEscCancelled(error)) {
65
81
  return;
66
82
  }
@@ -80,17 +96,9 @@ class ProviderAdder extends BaseCommand {
80
96
  ['ESC', '取消添加']
81
97
  ]));
82
98
  console.log();
83
-
84
- // 设置 ESC 键监听
85
- const escListener = this.createESCListener(() => {
86
- Logger.info('取消添加供应商');
87
- // 使用CommandRegistry避免循环引用
88
- const { registry } = require('../CommandRegistry');
89
- registry.executeCommand('switch');
90
- }, '取消添加');
91
99
 
92
100
  try {
93
- const answers = await this.prompt([
101
+ const answers = await this.promptWithESC([
94
102
  {
95
103
  type: 'input',
96
104
  name: 'name',
@@ -132,11 +140,13 @@ class ProviderAdder extends BaseCommand {
132
140
  message: '是否设置为当前供应商?',
133
141
  default: true
134
142
  }
135
- ]);
143
+ ], '取消添加', () => {
144
+ Logger.info('取消添加供应商');
145
+ // 使用CommandRegistry避免循环引用
146
+ const { registry } = require('../CommandRegistry');
147
+ registry.executeCommand('switch');
148
+ });
136
149
 
137
- // 移除 ESC 键监听
138
- this.removeESCListener(escListener);
139
-
140
150
  // 使用官方 OAuth 配置
141
151
  await this.saveProvider({
142
152
  ...answers,
@@ -144,8 +154,6 @@ class ProviderAdder extends BaseCommand {
144
154
  baseUrl: null // OAuth 模式不需要 baseUrl
145
155
  });
146
156
  } catch (error) {
147
- // 移除 ESC 键监听
148
- this.removeESCListener(escListener);
149
157
  if (this.isEscCancelled(error)) {
150
158
  return;
151
159
  }
@@ -166,16 +174,8 @@ class ProviderAdder extends BaseCommand {
166
174
  ]));
167
175
  console.log();
168
176
 
169
- // 设置 ESC 键监听
170
- const escListener = this.createESCListener(() => {
171
- Logger.info('取消添加供应商');
172
- // 使用CommandRegistry避免循环引用
173
- const { registry } = require('../CommandRegistry');
174
- registry.executeCommand('switch');
175
- }, '取消添加');
176
-
177
177
  try {
178
- const answers = await this.prompt([
178
+ const answers = await this.promptWithESC([
179
179
  {
180
180
  type: 'list',
181
181
  name: 'ideName',
@@ -218,6 +218,17 @@ class ProviderAdder extends BaseCommand {
218
218
  return true;
219
219
  }
220
220
  },
221
+ {
222
+ type: 'input',
223
+ name: 'alias',
224
+ message: '请输入供应商别名 (可选,用于快速切换):',
225
+ validate: (input) => {
226
+ if (!input) return true; // 别名是可选的
227
+ const error = validator.validateName(input);
228
+ if (error) return error;
229
+ return true;
230
+ }
231
+ },
221
232
  {
222
233
  type: 'list',
223
234
  name: 'authMode',
@@ -270,15 +281,15 @@ class ProviderAdder extends BaseCommand {
270
281
  name: 'authToken',
271
282
  message: (answers) => {
272
283
  switch (answers.authMode) {
273
- case 'api_key':
274
- const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
275
- return `请输入Token (${tokenTypeLabel}):`;
276
- case 'auth_token':
277
- return '请输入认证令牌 (ANTHROPIC_AUTH_TOKEN):';
278
- case 'oauth_token':
279
- return '请输入OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
280
- default:
281
- return '请输入认证令牌:';
284
+ case 'api_key':
285
+ const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
286
+ return `请输入Token (${tokenTypeLabel}):`;
287
+ case 'auth_token':
288
+ return '请输入认证令牌 (ANTHROPIC_AUTH_TOKEN):';
289
+ case 'oauth_token':
290
+ return '请输入OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
291
+ default:
292
+ return '请输入认证令牌:';
282
293
  }
283
294
  },
284
295
  validate: (input) => {
@@ -340,10 +351,12 @@ class ProviderAdder extends BaseCommand {
340
351
  default: false,
341
352
  when: (answers) => (answers.ideName || this.presetIdeName) !== 'codex'
342
353
  }
343
- ]);
344
-
345
- // 移除 ESC 键监听
346
- this.removeESCListener(escListener);
354
+ ], '取消添加', () => {
355
+ Logger.info('取消添加供应商');
356
+ // 使用CommandRegistry避免循环引用
357
+ const { registry } = require('../CommandRegistry');
358
+ registry.executeCommand('switch');
359
+ });
347
360
 
348
361
  // 如果是预设的 ideName,设置到 answers 中
349
362
  if (!answers.ideName && this.presetIdeName) {
@@ -390,8 +403,6 @@ class ProviderAdder extends BaseCommand {
390
403
 
391
404
  await this.saveProvider(answers);
392
405
  } catch (error) {
393
- // 移除 ESC 键监听
394
- this.removeESCListener(escListener);
395
406
  if (this.isEscCancelled(error)) {
396
407
  return;
397
408
  }
@@ -448,26 +459,22 @@ class ProviderAdder extends BaseCommand {
448
459
  }
449
460
 
450
461
  async confirmOverwrite(name) {
451
- const escListener = this.createESCListener(() => {
452
- Logger.info('取消覆盖供应商');
453
- const { switchCommand } = require('./switch');
454
- switchCommand();
455
- }, '取消覆盖');
456
-
457
462
  try {
458
- const { overwrite } = await this.prompt([
463
+ const { overwrite } = await this.promptWithESC([
459
464
  {
460
465
  type: 'confirm',
461
466
  name: 'overwrite',
462
467
  message: `供应商 '${name}' 已存在,是否覆盖?`,
463
468
  default: false
464
469
  }
465
- ]);
470
+ ], '取消覆盖', () => {
471
+ Logger.info('取消覆盖供应商');
472
+ const { switchCommand } = require('./switch');
473
+ switchCommand();
474
+ });
466
475
 
467
- this.removeESCListener(escListener);
468
476
  return overwrite;
469
477
  } catch (error) {
470
- this.removeESCListener(escListener);
471
478
  throw error;
472
479
  }
473
480
  }
@@ -487,33 +494,22 @@ class ProviderAdder extends BaseCommand {
487
494
  ]));
488
495
  console.log();
489
496
 
490
- const escListener = this.createESCListener(() => {
497
+ const result = await this.promptWithESCAndDefault([
498
+ {
499
+ type: 'checkbox',
500
+ name: 'launchArgs',
501
+ message: '请选择启动参数:',
502
+ choices: validator.getAvailableLaunchArgs().map(arg => ({
503
+ name: `${arg.name} - ${arg.description}`,
504
+ value: arg.name,
505
+ checked: false
506
+ }))
507
+ }
508
+ ], '跳过配置', () => {
491
509
  Logger.info('跳过启动参数配置');
492
- }, '跳过配置');
510
+ }, { launchArgs: [] });
493
511
 
494
- try {
495
- const { launchArgs } = await this.prompt([
496
- {
497
- type: 'checkbox',
498
- name: 'launchArgs',
499
- message: '请选择启动参数:',
500
- choices: validator.getAvailableLaunchArgs().map(arg => ({
501
- name: `${arg.name} - ${arg.description}`,
502
- value: arg.name,
503
- checked: false
504
- }))
505
- }
506
- ]);
507
-
508
- this.removeESCListener(escListener);
509
- return launchArgs;
510
- } catch (error) {
511
- this.removeESCListener(escListener);
512
- if (this.isEscCancelled(error)) {
513
- return [];
514
- }
515
- throw error;
516
- }
512
+ return result.launchArgs;
517
513
  }
518
514
 
519
515
  async promptModelConfiguration() {
@@ -528,48 +524,37 @@ class ProviderAdder extends BaseCommand {
528
524
  ]));
529
525
  console.log();
530
526
 
531
- const escListener = this.createESCListener(() => {
532
- Logger.info('跳过模型参数配置');
533
- }, '跳过配置');
534
-
535
- try {
536
- const responses = await this.prompt([
537
- {
538
- type: 'input',
539
- name: 'primaryModel',
540
- message: '主模型 (ANTHROPIC_MODEL):',
541
- default: '',
542
- validate: (input) => {
543
- const error = validator.validateModel(input);
544
- if (error) return error;
545
- return true;
546
- }
547
- },
548
- {
549
- type: 'input',
550
- name: 'smallFastModel',
551
- message: '快速模型 (ANTHROPIC_SMALL_FAST_MODEL):',
552
- default: '',
553
- validate: (input) => {
554
- const error = validator.validateModel(input);
555
- if (error) return error;
556
- return true;
557
- }
527
+ const responses = await this.promptWithESCAndDefault([
528
+ {
529
+ type: 'input',
530
+ name: 'primaryModel',
531
+ message: '主模型 (ANTHROPIC_MODEL):',
532
+ default: '',
533
+ validate: (input) => {
534
+ const error = validator.validateModel(input);
535
+ if (error) return error;
536
+ return true;
537
+ }
538
+ },
539
+ {
540
+ type: 'input',
541
+ name: 'smallFastModel',
542
+ message: '快速模型 (ANTHROPIC_SMALL_FAST_MODEL):',
543
+ default: '',
544
+ validate: (input) => {
545
+ const error = validator.validateModel(input);
546
+ if (error) return error;
547
+ return true;
558
548
  }
559
- ]);
560
-
561
- this.removeESCListener(escListener);
562
- return {
563
- primaryModel: responses.primaryModel,
564
- smallFastModel: responses.smallFastModel
565
- };
566
- } catch (error) {
567
- this.removeESCListener(escListener);
568
- if (this.isEscCancelled(error)) {
569
- return { primaryModel: null, smallFastModel: null };
570
549
  }
571
- throw error;
572
- }
550
+ ], '跳过配置', () => {
551
+ Logger.info('跳过模型参数配置');
552
+ }, { primaryModel: null, smallFastModel: null });
553
+
554
+ return {
555
+ primaryModel: responses.primaryModel,
556
+ smallFastModel: responses.smallFastModel
557
+ };
573
558
  }
574
559
 
575
560
  async importCodexConfig() {
@@ -627,15 +612,11 @@ class ProviderAdder extends BaseCommand {
627
612
  ]));
628
613
  console.log();
629
614
 
630
- const escListener = this.createESCListener(() => {
631
- Logger.info('跳过 Codex 启动参数配置');
632
- }, '跳过配置');
633
-
634
615
  try {
635
616
  const { getCodexLaunchArgs, checkExclusiveArgs } = require('../utils/launch-args');
636
617
  const codexArgs = getCodexLaunchArgs();
637
618
 
638
- const { launchArgs } = await this.prompt([
619
+ const result = await this.promptWithESCAndDefault([
639
620
  {
640
621
  type: 'checkbox',
641
622
  name: 'launchArgs',
@@ -646,9 +627,11 @@ class ProviderAdder extends BaseCommand {
646
627
  checked: arg.checked
647
628
  }))
648
629
  }
649
- ]);
630
+ ], '跳过配置', () => {
631
+ Logger.info('跳过 Codex 启动参数配置');
632
+ }, { launchArgs: [] });
650
633
 
651
- this.removeESCListener(escListener);
634
+ const { launchArgs } = result;
652
635
 
653
636
  const conflictError = checkExclusiveArgs(launchArgs, codexArgs);
654
637
  if (conflictError) {
@@ -657,10 +640,6 @@ class ProviderAdder extends BaseCommand {
657
640
  }
658
641
  return launchArgs;
659
642
  } catch (error) {
660
- this.removeESCListener(escListener);
661
- if (this.isEscCancelled(error)) {
662
- return [];
663
- }
664
643
  throw error;
665
644
  }
666
645
  }
@@ -679,7 +658,7 @@ class ProviderAdder extends BaseCommand {
679
658
  console.log(chalk.gray(` OPENAI_BASE_URL: ${answers.baseUrl}`));
680
659
  }
681
660
  if (answers.authToken) {
682
- console.log(chalk.gray(` OPENAI_API_KEY: ${maskToken(answers.authToken)}`));
661
+ console.log(chalk.gray(` OPENAI_API_KEY: ${answers.authToken}`));
683
662
  }
684
663
  if (answers.launchArgs && answers.launchArgs.length > 0) {
685
664
  console.log(chalk.gray(` 启动参数: ${answers.launchArgs.join(' ')}`));
@@ -688,24 +667,18 @@ class ProviderAdder extends BaseCommand {
688
667
  return;
689
668
  }
690
669
 
691
- const authModeDisplay = {
692
- api_key: '通用API密钥模式',
693
- auth_token: '认证令牌模式 (仅 ANTHROPIC_AUTH_TOKEN)',
694
- oauth_token: 'OAuth令牌模式 (CLAUDE_CODE_OAUTH_TOKEN)'
695
- };
696
-
697
- console.log(chalk.gray(` 认证模式: ${authModeDisplay[answers.authMode] || answers.authMode}`));
670
+ console.log(chalk.gray(` 认证模式: ${AUTH_MODE_DISPLAY_DETAILED[answers.authMode] || answers.authMode}`));
698
671
 
699
672
  // 如果是 api_key 模式,显示 tokenType
700
673
  if (answers.authMode === 'api_key' && answers.tokenType) {
701
- const tokenTypeDisplay = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
674
+ const tokenTypeDisplay = TOKEN_TYPE_DISPLAY[answers.tokenType];
702
675
  console.log(chalk.gray(` Token类型: ${tokenTypeDisplay}`));
703
676
  }
704
677
 
705
678
  if (answers.baseUrl) {
706
679
  console.log(chalk.gray(` 基础URL: ${answers.baseUrl}`));
707
680
  }
708
- console.log(chalk.gray(` Token: ${maskToken(answers.authToken)}`));
681
+ console.log(chalk.gray(` Token: ${answers.authToken}`));
709
682
 
710
683
  if (launchArgs.length > 0) {
711
684
  console.log(chalk.gray(` 启动参数: ${launchArgs.join(' ')}`));
@@ -1,10 +1,23 @@
1
+ /**
2
+ * Backup Manager Command
3
+ * 配置备份和恢复管理
4
+ * @module commands/backup
5
+ */
6
+
1
7
  const fs = require('fs-extra');
2
8
  const path = require('path');
3
9
  const chalk = require('chalk');
4
10
  const { configManager } = require('../config');
5
11
  const { Logger } = require('../utils/logger');
6
12
 
13
+ /**
14
+ * 备份管理器类
15
+ * 用于导出、导入和管理供应商配置备份
16
+ */
7
17
  class BackupManager {
18
+ /**
19
+ * 创建备份管理器实例
20
+ */
8
21
  constructor() {
9
22
  this.configManager = configManager;
10
23
  }
@@ -116,7 +129,7 @@ class BackupManager {
116
129
 
117
130
  await this.configManager.save();
118
131
 
119
- Logger.success(`配置导入完成`);
132
+ Logger.success('配置导入完成');
120
133
  console.log(chalk.gray(` 导入文件: ${absolutePath}`));
121
134
  console.log(chalk.gray(` 成功导入: ${addedCount} 个供应商`));
122
135
  if (skippedCount > 0) {