@pikecode/api-key-manager 1.0.38 → 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.
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Environment Switcher Command
3
+ * 供应商切换和管理的主命令
4
+ * @module commands/switch
5
+ */
6
+
1
7
  const path = require('path');
2
8
  const inquirer = require('inquirer');
3
9
  const chalk = require('chalk');
@@ -11,8 +17,15 @@ const { findSettingsConflict, backupSettingsFile, clearConflictKeys, saveSetting
11
17
  const { BaseCommand } = require('./BaseCommand');
12
18
  const { validator } = require('../utils/validator');
13
19
  const { ProviderStatusChecker } = require('../utils/provider-status-checker');
14
- const { maskToken } = require('../utils/secrets');
15
-
20
+ const { AUTH_MODE_DISPLAY, TOKEN_TYPE_DISPLAY, BASE_URL } = require('../constants');
21
+ const { LaunchArgsHelper } = require('./switch/launch-args-helper');
22
+ const { StatusHelper } = require('./switch/status-helper');
23
+
24
+ /**
25
+ * 环境切换器类
26
+ * 提供交互式界面用于选择、切换、管理和启动 API 供应商
27
+ * @extends BaseCommand
28
+ */
16
29
  class EnvSwitcher extends BaseCommand {
17
30
  constructor() {
18
31
  super();
@@ -27,10 +40,17 @@ class EnvSwitcher extends BaseCommand {
27
40
 
28
41
  async validateProvider(providerName) {
29
42
  await this.configManager.load();
30
- const provider = this.configManager.getProvider(providerName);
43
+
44
+ // 先尝试按名称查找,再尝试按别名查找
45
+ let provider = this.configManager.getProvider(providerName);
46
+ if (!provider) {
47
+ provider = this.configManager.getProviderByNameOrAlias(providerName);
48
+ }
49
+
31
50
  if (!provider) {
32
51
  throw new Error(`供应商 '${providerName}' 不存在\n使用 'akm list' 查看所有已配置的供应商`);
33
52
  }
53
+
34
54
  return provider;
35
55
  }
36
56
 
@@ -40,7 +60,12 @@ class EnvSwitcher extends BaseCommand {
40
60
  const provider = await this.validateProvider(providerName);
41
61
  const isCodex = provider.ideName === 'codex';
42
62
  const availableArgs = isCodex ? this.getCodexLaunchArgs() : this.getAvailableLaunchArgs();
43
- const defaultLaunchArgs = Array.isArray(provider.launchArgs) ? provider.launchArgs : [];
63
+
64
+ // 优先使用上次使用的参数,如果没有则使用默认的 launchArgs
65
+ const defaultLaunchArgs = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0
66
+ ? provider.lastUsedArgs
67
+ : (Array.isArray(provider.launchArgs) ? provider.launchArgs : []);
68
+
44
69
  const knownArgNames = new Set(availableArgs.map(arg => arg.name));
45
70
  const customLaunchArgs = defaultLaunchArgs
46
71
  .filter(arg => typeof arg === 'string' && !knownArgNames.has(arg));
@@ -57,7 +82,14 @@ class EnvSwitcher extends BaseCommand {
57
82
  }))
58
83
  ];
59
84
  const ideDisplayName = isCodex ? 'Codex CLI' : 'Claude Code';
60
-
85
+
86
+ // 显示提示:是否使用上次的参数
87
+ const isUsingLastUsed = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0;
88
+ if (isUsingLastUsed) {
89
+ console.log(UIHelper.colors.muted('💡 正在使用上次的启动参数'));
90
+ console.log();
91
+ }
92
+
61
93
  console.log(UIHelper.createTitle('启动配置', UIHelper.icons.launch));
62
94
  console.log();
63
95
  console.log(UIHelper.createCard('供应商', UIHelper.formatProvider(provider), UIHelper.icons.info));
@@ -70,13 +102,13 @@ class EnvSwitcher extends BaseCommand {
70
102
  ['ESC', '返回供应商选择']
71
103
  ]));
72
104
  console.log();
73
-
105
+
74
106
  // 设置 ESC 键监听
75
107
  const escListener = this.createESCListener(() => {
76
108
  Logger.info('返回供应商选择');
77
109
  this.showProviderSelection();
78
110
  }, '返回供应商选择');
79
-
111
+
80
112
  // 显示启动参数选择界面
81
113
  const choices = [
82
114
  {
@@ -108,7 +140,7 @@ class EnvSwitcher extends BaseCommand {
108
140
  }
109
141
  throw error;
110
142
  }
111
-
143
+
112
144
  this.removeESCListener(escListener);
113
145
 
114
146
  // 检查互斥参数
@@ -118,9 +150,12 @@ class EnvSwitcher extends BaseCommand {
118
150
  return await this.showLaunchArgsSelection(providerName);
119
151
  }
120
152
 
153
+ // 保存上次使用的启动参数
154
+ await this.configManager.updateLastUsedArgs(providerName, answers.selectedArgs);
155
+
121
156
  // 选择参数后直接启动
122
157
  await this.launchProvider(provider, answers.selectedArgs);
123
-
158
+
124
159
  } catch (error) {
125
160
  await this.handleError(error, '选择启动参数');
126
161
  }
@@ -282,6 +317,64 @@ class EnvSwitcher extends BaseCommand {
282
317
  }
283
318
  }
284
319
 
320
+ /**
321
+ * 快速启动供应商(跳过参数选择)
322
+ * @param {string} providerName - 供应商名称
323
+ * @param {Object} options - 启动选项
324
+ * @param {boolean} options.quick - 使用上次的启动参数
325
+ * @param {boolean} options.noArgs - 不使用任何启动参数
326
+ */
327
+ /**
328
+ * 快速启动供应商(跳过参数选择)
329
+ * @param {string} providerName - 供应商名称或别名
330
+ * @param {Object} options - 启动选项
331
+ * @param {boolean} options.quick - 使用上次的启动参数
332
+ * @param {boolean} options.noArgs - 不使用任何启动参数
333
+ */
334
+ async quickLaunchProvider(providerName, options) {
335
+ try {
336
+ await this.configManager.ensureLoaded();
337
+
338
+ // 支持别名查找
339
+ let provider = this.configManager.getProvider(providerName);
340
+ if (!provider) {
341
+ provider = this.configManager.getProviderByNameOrAlias(providerName);
342
+ }
343
+
344
+ if (!provider) {
345
+ throw new Error(`供应商 '${providerName}' 不存在\n使用 'akm list' 查看所有已配置的供应商`);
346
+ }
347
+
348
+ // 确定使用的启动参数
349
+ let selectedArgs;
350
+ if (options.noArgs) {
351
+ // 使用空参数
352
+ selectedArgs = [];
353
+ console.log(UIHelper.colors.muted('💡 使用空参数启动'));
354
+ } else if (options.quick) {
355
+ // 使用上次的启动参数或默认参数
356
+ selectedArgs = Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0
357
+ ? provider.lastUsedArgs
358
+ : (Array.isArray(provider.launchArgs) ? provider.launchArgs : []);
359
+
360
+ if (Array.isArray(provider.lastUsedArgs) && provider.lastUsedArgs.length > 0) {
361
+ console.log(UIHelper.colors.muted('💡 使用上次的启动参数: ' + selectedArgs.join(' ')));
362
+ } else {
363
+ console.log(UIHelper.colors.muted('💡 使用默认启动参数: ' + (selectedArgs.length > 0 ? selectedArgs.join(' ') : '(无)')));
364
+ }
365
+ }
366
+
367
+ // 更新上次使用的参数(使用真实的 provider.name 而不是别名)
368
+ await this.configManager.updateLastUsedArgs(provider.name, selectedArgs);
369
+
370
+ // 直接启动供应商
371
+ await this.launchProvider(provider, selectedArgs);
372
+
373
+ } catch (error) {
374
+ await this.handleError(error, '快速启动供应商');
375
+ }
376
+ }
377
+
285
378
  getAvailableLaunchArgs() {
286
379
  const { getClaudeLaunchArgs } = require('../utils/launch-args');
287
380
  return getClaudeLaunchArgs();
@@ -315,7 +408,7 @@ class EnvSwitcher extends BaseCommand {
315
408
  const initialStatusMap = this._buildInitialStatusMap(providers);
316
409
  // 显示欢迎界面(立即渲染)
317
410
  this.showWelcomeScreen(providers, initialStatusMap, null);
318
-
411
+
319
412
  if (providers.length === 0) {
320
413
  if (this.filter) {
321
414
  const filterName = this.filter === 'codex' ? 'Codex CLI' : 'Claude Code';
@@ -335,7 +428,7 @@ class EnvSwitcher extends BaseCommand {
335
428
  if (providers.length > 0) {
336
429
  this._startStatusRefresh(providers);
337
430
  }
338
-
431
+
339
432
  // 添加特殊选项
340
433
  choices.push(
341
434
  new inquirer.Separator(),
@@ -371,7 +464,7 @@ class EnvSwitcher extends BaseCommand {
371
464
  pageSize: 12
372
465
  }
373
466
  ]);
374
-
467
+
375
468
  // 移除 ESC 键监听
376
469
  this.removeESCListener(escListener);
377
470
 
@@ -385,7 +478,7 @@ class EnvSwitcher extends BaseCommand {
385
478
  const result = await this.handleSelection(answer.provider);
386
479
  this.currentPromptContext = null;
387
480
  return result;
388
-
481
+
389
482
  } catch (error) {
390
483
  await this.handleError(error, '显示供应商选择');
391
484
  } finally {
@@ -408,11 +501,11 @@ class EnvSwitcher extends BaseCommand {
408
501
 
409
502
  showWelcomeScreen(providers, statusMap = {}, statusError = null) {
410
503
  this.clearScreen();
411
-
504
+
412
505
  if (providers.length > 0) {
413
506
  console.log(UIHelper.colors.info(`总共 ${providers.length} 个供应商配置`));
414
507
  }
415
-
508
+
416
509
  if (statusError) {
417
510
  console.log();
418
511
  console.log(UIHelper.createCard('状态检测', `检测失败: ${statusError.message}`, UIHelper.icons.warning));
@@ -431,18 +524,18 @@ class EnvSwitcher extends BaseCommand {
431
524
 
432
525
  async handleSelection(selection) {
433
526
  switch (selection) {
434
- case '__ADD__':
435
- // 使用CommandRegistry避免循环引用
436
- const { registry } = require('../CommandRegistry');
437
- return await registry.executeCommand('add');
438
- case '__MANAGE__':
439
- return await this.showManageMenu();
440
- case '__EXIT__':
441
- this.showExitScreen();
442
- this.destroy();
443
- process.exit(0);
444
- default:
445
- return await this.showLaunchArgsSelection(selection);
527
+ case '__ADD__':
528
+ // 使用CommandRegistry避免循环引用
529
+ const { registry } = require('../CommandRegistry');
530
+ return await registry.executeCommand('add');
531
+ case '__MANAGE__':
532
+ return await this.showManageMenu();
533
+ case '__EXIT__':
534
+ this.showExitScreen();
535
+ this.destroy();
536
+ process.exit(0);
537
+ default:
538
+ return await this.showLaunchArgsSelection(selection);
446
539
  }
447
540
  }
448
541
 
@@ -486,20 +579,20 @@ class EnvSwitcher extends BaseCommand {
486
579
  }
487
580
  throw error;
488
581
  }
489
-
582
+
490
583
  this.removeESCListener(escListener);
491
584
 
492
585
  switch (answer.setting) {
493
- case 'search':
494
- return await this.showSearchProvider();
495
- case 'batch':
496
- return await this.showBatchEdit();
497
- case 'global':
498
- return await this.showGlobalSettings();
499
- case 'stats':
500
- return await this.showStatistics();
501
- case 'back':
502
- return await this.showProviderSelection();
586
+ case 'search':
587
+ return await this.showSearchProvider();
588
+ case 'batch':
589
+ return await this.showBatchEdit();
590
+ case 'global':
591
+ return await this.showGlobalSettings();
592
+ case 'stats':
593
+ return await this.showStatistics();
594
+ case 'back':
595
+ return await this.showProviderSelection();
503
596
  }
504
597
  }
505
598
 
@@ -514,13 +607,13 @@ class EnvSwitcher extends BaseCommand {
514
607
  ['ESC', '返回快速设置']
515
608
  ]));
516
609
  console.log();
517
-
610
+
518
611
  // 设置 ESC 键监听
519
612
  const escListener = this.createESCListener(() => {
520
613
  Logger.info('返回快速设置');
521
614
  this.showQuickSettings();
522
615
  }, '返回快速设置');
523
-
616
+
524
617
  try {
525
618
  await this.prompt([
526
619
  {
@@ -536,7 +629,7 @@ class EnvSwitcher extends BaseCommand {
536
629
  }
537
630
  throw error;
538
631
  }
539
-
632
+
540
633
  this.removeESCListener(escListener);
541
634
 
542
635
  return await this.showQuickSettings();
@@ -553,13 +646,13 @@ class EnvSwitcher extends BaseCommand {
553
646
  ['ESC', '返回快速设置']
554
647
  ]));
555
648
  console.log();
556
-
649
+
557
650
  // 设置 ESC 键监听
558
651
  const escListener = this.createESCListener(() => {
559
652
  Logger.info('返回快速设置');
560
653
  this.showQuickSettings();
561
654
  }, '返回快速设置');
562
-
655
+
563
656
  try {
564
657
  await this.prompt([
565
658
  {
@@ -575,7 +668,7 @@ class EnvSwitcher extends BaseCommand {
575
668
  }
576
669
  throw error;
577
670
  }
578
-
671
+
579
672
  this.removeESCListener(escListener);
580
673
 
581
674
  return await this.showQuickSettings();
@@ -612,12 +705,12 @@ class EnvSwitcher extends BaseCommand {
612
705
  }
613
706
  throw error;
614
707
  }
615
-
708
+
616
709
  this.removeESCListener(escListener);
617
710
 
618
711
  await this.configManager.load();
619
712
  const providers = this.configManager.listProviders();
620
- const searchResults = providers.filter(p =>
713
+ const searchResults = providers.filter(p =>
621
714
  p.name.toLowerCase().includes(answer.search.toLowerCase()) ||
622
715
  p.displayName.toLowerCase().includes(answer.search.toLowerCase())
623
716
  );
@@ -673,7 +766,7 @@ class EnvSwitcher extends BaseCommand {
673
766
  }
674
767
  throw error;
675
768
  }
676
-
769
+
677
770
  this.removeESCListener(escListener2);
678
771
 
679
772
  if (result.provider === 'back') {
@@ -687,7 +780,7 @@ class EnvSwitcher extends BaseCommand {
687
780
  await this.configManager.load();
688
781
  const providers = this.configManager.listProviders();
689
782
  this.clearScreen();
690
-
783
+
691
784
  const totalProviders = providers.length;
692
785
  const currentProvider = providers.find(p => p.current);
693
786
  const totalUsage = providers.reduce((sum, p) => sum + (p.usageCount || 0), 0);
@@ -695,7 +788,7 @@ class EnvSwitcher extends BaseCommand {
695
788
 
696
789
  console.log(UIHelper.createTitle('使用统计', UIHelper.icons.info));
697
790
  console.log();
698
-
791
+
699
792
  const stats = [
700
793
  ['总供应商数', totalProviders],
701
794
  ['当前供应商', currentProvider ? currentProvider.displayName : '无'],
@@ -703,7 +796,7 @@ class EnvSwitcher extends BaseCommand {
703
796
  ['最常用供应商', mostUsed ? mostUsed.displayName : '无'],
704
797
  ['创建时间', providers.length > 0 ? UIHelper.formatTime(providers[0].createdAt) : '无']
705
798
  ];
706
-
799
+
707
800
  console.log(UIHelper.createTable(['项目', '数据'], stats));
708
801
  console.log();
709
802
  console.log(UIHelper.createHintLine([
@@ -717,7 +810,7 @@ class EnvSwitcher extends BaseCommand {
717
810
  Logger.info('返回快速设置');
718
811
  this.showQuickSettings();
719
812
  }, '返回快速设置');
720
-
813
+
721
814
  try {
722
815
  await this.prompt([
723
816
  {
@@ -733,7 +826,7 @@ class EnvSwitcher extends BaseCommand {
733
826
  }
734
827
  throw error;
735
828
  }
736
-
829
+
737
830
  this.removeESCListener(escListener);
738
831
 
739
832
  return await this.showQuickSettings();
@@ -827,10 +920,10 @@ class EnvSwitcher extends BaseCommand {
827
920
  ['ESC', '返回主菜单']
828
921
  ]));
829
922
  console.log();
830
-
923
+
831
924
  console.log(UIHelper.createTitle('供应商管理', UIHelper.icons.list));
832
925
  console.log();
833
-
926
+
834
927
  if (providers.length === 0) {
835
928
  console.log(UIHelper.createCard('提示', '暂无配置的供应商\n请先运行 "akm add" 添加供应商配置', UIHelper.icons.warning));
836
929
  return await this.showProviderSelection();
@@ -871,7 +964,7 @@ class EnvSwitcher extends BaseCommand {
871
964
  }
872
965
  throw error;
873
966
  }
874
-
967
+
875
968
  this.removeESCListener(escListener);
876
969
 
877
970
  this._cancelStatusRefresh();
@@ -879,7 +972,7 @@ class EnvSwitcher extends BaseCommand {
879
972
  const result = await this.handleManageAction(answer.action);
880
973
  this.currentPromptContext = null;
881
974
  return result;
882
-
975
+
883
976
  } catch (error) {
884
977
  await this.handleError(error, '显示供应商管理');
885
978
  } finally {
@@ -932,64 +1025,19 @@ class EnvSwitcher extends BaseCommand {
932
1025
  }
933
1026
 
934
1027
  _iconForState(state) {
935
- if (state === 'online') {
936
- return '🟢';
937
- }
938
- if (state === 'degraded') {
939
- return '🟡';
940
- }
941
- if (state === 'offline') {
942
- return '🔴';
943
- }
944
- if (state === 'pending') {
945
- return '⏳';
946
- }
947
- return '⚪';
1028
+ return StatusHelper.getIconForState(state);
948
1029
  }
949
1030
 
950
1031
  _formatAvailability(availability) {
951
- if (!availability) {
952
- return chalk.gray('测试中...');
953
- }
954
- if (availability.state === 'online') {
955
- return chalk.green(availability.label || '可用');
956
- }
957
- if (availability.state === 'degraded') {
958
- return chalk.yellow(availability.label || '有限可用');
959
- }
960
- if (availability.state === 'offline') {
961
- return chalk.red(availability.label || '不可用');
962
- }
963
- if (availability.state === 'pending') {
964
- return chalk.gray(availability.label || '测试中...');
965
- }
966
- return chalk.gray(availability.label || '未知');
1032
+ return StatusHelper.formatAvailability(availability);
967
1033
  }
968
1034
 
969
1035
  _buildInitialStatusMap(providers) {
970
- const cached = this.latestStatusMap || {};
971
- const map = {};
972
- providers.forEach(provider => {
973
- map[provider.name] = cached[provider.name] || {
974
- state: 'pending',
975
- label: '测试中...',
976
- latency: null
977
- };
978
- });
979
- return map;
1036
+ return StatusHelper.buildInitialStatusMap(providers, this.latestStatusMap || {});
980
1037
  }
981
1038
 
982
1039
  _buildErrorStatusMap(providers, error) {
983
- const message = error ? `检测失败: ${error.message}` : '检测失败';
984
- const map = {};
985
- providers.forEach(provider => {
986
- map[provider.name] = {
987
- state: 'offline',
988
- label: message,
989
- latency: null
990
- };
991
- });
992
- return map;
1040
+ return StatusHelper.buildErrorStatusMap(providers, error);
993
1041
  }
994
1042
 
995
1043
  _startStatusRefresh(providers) {
@@ -1157,15 +1205,15 @@ class EnvSwitcher extends BaseCommand {
1157
1205
 
1158
1206
  async handleManageAction(action) {
1159
1207
  switch (action) {
1160
- case 'back':
1161
- return await this.showProviderSelection();
1162
- case 'exit':
1163
- Logger.info('👋 再见!');
1164
- this.destroy();
1165
- process.exit(0);
1166
- default:
1167
- // 如果选择的是供应商名称,显示该供应商的详细信息
1168
- return await this.showProviderDetails(action);
1208
+ case 'back':
1209
+ return await this.showProviderSelection();
1210
+ case 'exit':
1211
+ Logger.info('👋 再见!');
1212
+ this.destroy();
1213
+ process.exit(0);
1214
+ default:
1215
+ // 如果选择的是供应商名称,显示该供应商的详细信息
1216
+ return await this.showProviderDetails(action);
1169
1217
  }
1170
1218
  }
1171
1219
 
@@ -1174,7 +1222,7 @@ class EnvSwitcher extends BaseCommand {
1174
1222
  try {
1175
1223
  const provider = await this.validateProvider(providerName);
1176
1224
  this.clearScreen();
1177
-
1225
+
1178
1226
  console.log(UIHelper.createTitle('供应商详情', UIHelper.icons.info));
1179
1227
  console.log();
1180
1228
  console.log(UIHelper.createHintLine([
@@ -1183,33 +1231,27 @@ class EnvSwitcher extends BaseCommand {
1183
1231
  ['ESC', '返回管理列表']
1184
1232
  ]));
1185
1233
  console.log();
1186
-
1187
- const authModeDisplay = {
1188
- api_key: '通用API密钥模式',
1189
- auth_token: '认证令牌模式',
1190
- oauth_token: 'OAuth令牌模式'
1191
- };
1192
1234
 
1193
1235
  const details = [
1194
1236
  ['供应商名称', provider.name],
1195
1237
  ['显示名称', provider.displayName],
1196
- ['认证模式', authModeDisplay[provider.authMode] || provider.authMode],
1238
+ ['认证模式', AUTH_MODE_DISPLAY[provider.authMode] || provider.authMode]
1197
1239
  ];
1198
1240
 
1199
1241
  // 如果是 api_key 模式,添加 tokenType 信息
1200
1242
  if (provider.authMode === 'api_key' && provider.tokenType) {
1201
- const tokenTypeDisplay = provider.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
1243
+ const tokenTypeDisplay = TOKEN_TYPE_DISPLAY[provider.tokenType];
1202
1244
  details.push(['Token类型', tokenTypeDisplay]);
1203
1245
  }
1204
1246
 
1205
1247
  // 继续添加其他信息
1206
1248
  const baseUrlDisplay = provider.baseUrl
1207
1249
  || ((provider.authMode === 'oauth_token' || provider.authMode === 'auth_token')
1208
- ? '✨ 官方默认服务器'
1250
+ ? BASE_URL.OFFICIAL_DEFAULT
1209
1251
  : '⚠️ 未设置');
1210
1252
  details.push(
1211
1253
  ['基础URL', baseUrlDisplay],
1212
- ['认证令牌', provider.authToken ? maskToken(provider.authToken) : '未设置'],
1254
+ ['认证令牌', provider.authToken || '未设置'],
1213
1255
  ['主模型', provider.models?.primary || '未设置'],
1214
1256
  ['快速模型', provider.models?.smallFast || '未设置'],
1215
1257
  ['创建时间', UIHelper.formatTime(provider.createdAt)],
@@ -1217,10 +1259,10 @@ class EnvSwitcher extends BaseCommand {
1217
1259
  ['当前状态', provider.current ? '✅ 使用中' : '⚫ 未使用'],
1218
1260
  ['使用次数', provider.usageCount || 0]
1219
1261
  );
1220
-
1262
+
1221
1263
  console.log(UIHelper.createTable(['项目', '信息'], details));
1222
1264
  console.log();
1223
-
1265
+
1224
1266
  if (provider.launchArgs && provider.launchArgs.length > 0) {
1225
1267
  console.log(UIHelper.createCard('默认启动参数', provider.launchArgs.join(', '), UIHelper.icons.settings));
1226
1268
  console.log();
@@ -1256,24 +1298,24 @@ class EnvSwitcher extends BaseCommand {
1256
1298
  }
1257
1299
 
1258
1300
  switch (answer.action) {
1259
- case 'back':
1260
- // 移除 ESC 键监听
1261
- this.removeESCListener(escListener);
1262
- return await this.showManageMenu();
1263
- case 'edit':
1264
- // 移除 ESC 键监听
1265
- this.removeESCListener(escListener);
1266
- return await this.editProvider(providerName);
1267
- case 'remove':
1268
- // 移除 ESC 键监听
1269
- this.removeESCListener(escListener);
1270
- return await this.removeProvider(providerName);
1271
- case 'launch':
1272
- // 移除 ESC 键监听
1273
- this.removeESCListener(escListener);
1274
- return await this.showLaunchArgsSelection(providerName);
1301
+ case 'back':
1302
+ // 移除 ESC 键监听
1303
+ this.removeESCListener(escListener);
1304
+ return await this.showManageMenu();
1305
+ case 'edit':
1306
+ // 移除 ESC 键监听
1307
+ this.removeESCListener(escListener);
1308
+ return await this.editProvider(providerName);
1309
+ case 'remove':
1310
+ // 移除 ESC 键监听
1311
+ this.removeESCListener(escListener);
1312
+ return await this.removeProvider(providerName);
1313
+ case 'launch':
1314
+ // 移除 ESC 键监听
1315
+ this.removeESCListener(escListener);
1316
+ return await this.showLaunchArgsSelection(providerName);
1275
1317
  }
1276
-
1318
+
1277
1319
  } catch (error) {
1278
1320
  // 移除 ESC 键监听
1279
1321
  this.removeESCListener(escListener);
@@ -1285,9 +1327,9 @@ class EnvSwitcher extends BaseCommand {
1285
1327
  let escListener;
1286
1328
  try {
1287
1329
  await this.configManager.load();
1288
- const provider = this.configManager.getProvider(providerName);
1330
+ let provider = this.configManager.getProvider(providerName);
1289
1331
  this.clearScreen();
1290
-
1332
+
1291
1333
  if (!provider) {
1292
1334
  Logger.error(`供应商 '${providerName}' 不存在`);
1293
1335
  return await this.showManageMenu();
@@ -1319,6 +1361,19 @@ class EnvSwitcher extends BaseCommand {
1319
1361
  message: '显示名称:',
1320
1362
  default: provider.displayName,
1321
1363
  prefillDefault: true
1364
+ },
1365
+ {
1366
+ type: 'input',
1367
+ name: 'alias',
1368
+ message: '别名 (用于快速切换):',
1369
+ default: provider.alias,
1370
+ prefillDefault: true,
1371
+ validate: (input) => {
1372
+ if (!input) return true; // 别名是可选的
1373
+ const error = validator.validateName(input);
1374
+ if (error) return error;
1375
+ return true;
1376
+ }
1322
1377
  }
1323
1378
  ];
1324
1379
 
@@ -1393,15 +1448,15 @@ class EnvSwitcher extends BaseCommand {
1393
1448
  return 'API Key (OPENAI_API_KEY):';
1394
1449
  }
1395
1450
  switch (answers.authMode) {
1396
- case 'api_key':
1397
- const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
1398
- return `Token (${tokenTypeLabel}):`;
1399
- case 'auth_token':
1400
- return '认证令牌 (ANTHROPIC_AUTH_TOKEN):';
1401
- case 'oauth_token':
1402
- return 'OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
1403
- default:
1404
- return '认证令牌:';
1451
+ case 'api_key':
1452
+ const tokenTypeLabel = answers.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
1453
+ return `Token (${tokenTypeLabel}):`;
1454
+ case 'auth_token':
1455
+ return '认证令牌 (ANTHROPIC_AUTH_TOKEN):';
1456
+ case 'oauth_token':
1457
+ return 'OAuth令牌 (CLAUDE_CODE_OAUTH_TOKEN):';
1458
+ default:
1459
+ return '认证令牌:';
1405
1460
  }
1406
1461
  },
1407
1462
  default: provider.authToken,
@@ -1450,16 +1505,18 @@ class EnvSwitcher extends BaseCommand {
1450
1505
 
1451
1506
  // 更新供应商配置
1452
1507
  provider.displayName = answers.displayName || newName;
1453
- provider.baseUrl = answers.baseUrl;
1508
+ provider.alias = answers.alias || null;
1509
+ // oauth_token 模式不需要 baseUrl,显式设为 null
1510
+ provider.baseUrl = answers.authMode === 'oauth_token' ? null : answers.baseUrl;
1454
1511
  provider.authToken = answers.authToken;
1455
-
1512
+
1456
1513
  // Claude Code 特定的更新
1457
1514
  if (!isCodex) {
1458
1515
  provider.authMode = answers.authMode;
1459
1516
  if (answers.tokenType) {
1460
1517
  provider.tokenType = answers.tokenType; // 仅在 authMode 为 'api_key' 时使用
1461
1518
  }
1462
-
1519
+
1463
1520
  // 更新模型配置
1464
1521
  if (!provider.models) {
1465
1522
  provider.models = {};
@@ -1478,11 +1535,11 @@ class EnvSwitcher extends BaseCommand {
1478
1535
 
1479
1536
  await this.configManager.save();
1480
1537
  Logger.success(`供应商 '${newName}' 已更新`);
1481
-
1538
+
1482
1539
  // 移除 ESC 键监听
1483
1540
  this.removeESCListener(escListener);
1484
1541
  return await this.showManageMenu();
1485
-
1542
+
1486
1543
  } catch (error) {
1487
1544
  // 移除 ESC 键监听
1488
1545
  this.removeESCListener(escListener);
@@ -1497,7 +1554,7 @@ class EnvSwitcher extends BaseCommand {
1497
1554
  await this.configManager.load();
1498
1555
  const provider = this.configManager.getProvider(providerName);
1499
1556
  this.clearScreen();
1500
-
1557
+
1501
1558
  if (!provider) {
1502
1559
  Logger.error(`供应商 '${providerName}' 不存在`);
1503
1560
  return await this.showManageMenu();
@@ -1537,7 +1594,7 @@ class EnvSwitcher extends BaseCommand {
1537
1594
  // 移除 ESC 键监听
1538
1595
  this.removeESCListener(escListener);
1539
1596
  return await this.showManageMenu();
1540
-
1597
+
1541
1598
  } catch (error) {
1542
1599
  // 移除 ESC 键监听
1543
1600
  this.removeESCListener(escListener);
@@ -1553,7 +1610,12 @@ async function switchCommand(providerName, options = {}) {
1553
1610
 
1554
1611
  try {
1555
1612
  if (providerName) {
1556
- await switcher.showLaunchArgsSelection(providerName);
1613
+ // 如果指定了 quick 或 noArgs 选项,直接启动
1614
+ if (options.quick || options.noArgs) {
1615
+ await switcher.quickLaunchProvider(providerName, options);
1616
+ } else {
1617
+ await switcher.showLaunchArgsSelection(providerName);
1618
+ }
1557
1619
  } else {
1558
1620
  await switcher.showProviderSelection();
1559
1621
  }
@@ -1565,7 +1627,7 @@ async function switchCommand(providerName, options = {}) {
1565
1627
 
1566
1628
  async function editCommand(providerName) {
1567
1629
  const switcher = new EnvSwitcher();
1568
-
1630
+
1569
1631
  try {
1570
1632
  await switcher.editProvider(providerName);
1571
1633
  } finally {