@pikecode/api-key-manager 1.0.32 → 1.0.34

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 CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  - 🎯 **双 IDE 支持** - 同时管理 Claude Code 和 Codex CLI 配置
11
11
  - 🔄 **快速切换** - 一键切换不同的 API 提供商
12
- - 🔐 **安全存储** - 本地加密存储 API 密钥
12
+ - 🔐 **安全存储** - 本地文件存储(Unix 自动设置为 0600 权限)
13
13
  - 🎨 **多认证模式** - 支持 OAuth、API Key、Auth Token
14
14
  - 🚀 **启动参数** - 为每个供应商配置专属启动参数
15
15
  - 💾 **备份恢复** - 配置导出、导入、备份功能
@@ -95,13 +95,16 @@ akm list --codex
95
95
 
96
96
  # 仅列出 Claude Code 供应商
97
97
  akm list --claude
98
+
99
+ # 显示完整 Token(默认脱敏,慎用)
100
+ akm list --show-token
98
101
  ```
99
102
 
100
103
  **显示内容:**
101
104
  - ✅ 当前激活的供应商
102
105
  - 🟢/🟡/🔴 API 可用性状态
103
106
  - [Codex]/[Claude] IDE 类型标签
104
- - 认证模式、环境变量、启动参数
107
+ - 认证模式、环境变量、启动参数(Token 默认脱敏,可用 `--show-token` 显示完整)
105
108
  - 创建时间、最后使用时间
106
109
 
107
110
  #### `akm current`
@@ -109,6 +112,9 @@ akm list --claude
109
112
 
110
113
  ```bash
111
114
  akm current
115
+
116
+ # 显示完整 Token(默认脱敏,慎用)
117
+ akm current --show-token
112
118
  ```
113
119
 
114
120
  **显示内容:**
package/bin/akm.js CHANGED
@@ -83,10 +83,11 @@ program
83
83
  .description('列出所有API密钥配置')
84
84
  .option('--codex', '仅显示 Codex CLI 供应商')
85
85
  .option('--claude', '仅显示 Claude Code 供应商')
86
+ .option('--show-token', '显示完整 Token(默认脱敏)')
86
87
  .action(async (options) => {
87
88
  try {
88
89
  const filter = options.codex ? 'codex' : (options.claude ? 'claude' : null);
89
- await registry.executeCommand('list', filter);
90
+ await registry.executeCommand('list', filter, { showToken: options.showToken });
90
91
  } catch (error) {
91
92
  console.error(chalk.red('❌ 列表失败:'), error.message);
92
93
  process.exit(1);
@@ -97,9 +98,10 @@ program
97
98
  program
98
99
  .command('current')
99
100
  .description('显示当前活跃的配置')
100
- .action(async () => {
101
+ .option('--show-token', '显示完整 Token(默认脱敏)')
102
+ .action(async (options) => {
101
103
  try {
102
- await registry.executeCommand('current');
104
+ await registry.executeCommand('current', { showToken: options.showToken });
103
105
  } catch (error) {
104
106
  console.error(chalk.red('❌ 获取当前配置失败:'), error.message);
105
107
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikecode/api-key-manager",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "description": "A CLI tool for managing and switching multiple API provider configurations",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,15 +1,16 @@
1
1
  const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
- const { ConfigManager } = require('../config');
3
+ const { configManager } = require('../config');
4
4
  const { validator } = require('../utils/validator');
5
5
  const { Logger } = require('../utils/logger');
6
6
  const { UIHelper } = require('../utils/ui-helper');
7
+ const { maskToken } = require('../utils/secrets');
7
8
  const { BaseCommand } = require('./BaseCommand');
8
9
 
9
10
  class ProviderAdder extends BaseCommand {
10
11
  constructor(options = {}) {
11
12
  super();
12
- this.configManager = new ConfigManager();
13
+ this.configManager = configManager;
13
14
  this.presetIdeName = options.ideName || null;
14
15
  }
15
16
 
@@ -391,6 +392,9 @@ class ProviderAdder extends BaseCommand {
391
392
  } catch (error) {
392
393
  // 移除 ESC 键监听
393
394
  this.removeESCListener(escListener);
395
+ if (this.isEscCancelled(error)) {
396
+ return;
397
+ }
394
398
  throw error;
395
399
  }
396
400
  }
@@ -407,9 +411,9 @@ class ProviderAdder extends BaseCommand {
407
411
  }
408
412
  }
409
413
 
410
- const launchArgs = answers.configureLaunchArgs
411
- ? await this.promptLaunchArgsSelection()
412
- : [];
414
+ const launchArgs = answers.ideName === 'codex'
415
+ ? (Array.isArray(answers.launchArgs) ? answers.launchArgs : [])
416
+ : (answers.configureLaunchArgs ? await this.promptLaunchArgsSelection() : []);
413
417
 
414
418
  const modelConfig = answers.configureModels
415
419
  ? await this.promptModelConfiguration()
@@ -621,32 +625,8 @@ class ProviderAdder extends BaseCommand {
621
625
  }, '跳过配置');
622
626
 
623
627
  try {
624
- const codexArgs = [
625
- {
626
- name: 'resume',
627
- label: '继续上次对话',
628
- description: '恢复之前的会话',
629
- checked: false
630
- },
631
- {
632
- name: '--full-auto',
633
- label: '全自动模式',
634
- description: '自动批准 + 工作区写入沙盒 (与跳过沙盒互斥)',
635
- checked: false
636
- },
637
- {
638
- name: '--dangerously-bypass-approvals-and-sandbox',
639
- label: '跳过审批和沙盒',
640
- description: '危险:跳过所有安全检查 (与全自动互斥)',
641
- checked: false
642
- },
643
- {
644
- name: '--search',
645
- label: '启用网页搜索',
646
- description: '允许模型搜索网页',
647
- checked: false
648
- }
649
- ];
628
+ const { getCodexLaunchArgs, checkExclusiveArgs } = require('../utils/launch-args');
629
+ const codexArgs = getCodexLaunchArgs();
650
630
 
651
631
  const { launchArgs } = await this.prompt([
652
632
  {
@@ -662,6 +642,12 @@ class ProviderAdder extends BaseCommand {
662
642
  ]);
663
643
 
664
644
  this.removeESCListener(escListener);
645
+
646
+ const conflictError = checkExclusiveArgs(launchArgs, codexArgs);
647
+ if (conflictError) {
648
+ Logger.warning(conflictError);
649
+ return await this.promptCodexLaunchArgsSelection();
650
+ }
665
651
  return launchArgs;
666
652
  } catch (error) {
667
653
  this.removeESCListener(escListener);
@@ -686,7 +672,7 @@ class ProviderAdder extends BaseCommand {
686
672
  console.log(chalk.gray(` OPENAI_BASE_URL: ${answers.baseUrl}`));
687
673
  }
688
674
  if (answers.authToken) {
689
- console.log(chalk.gray(` OPENAI_API_KEY: ${answers.authToken}`));
675
+ console.log(chalk.gray(` OPENAI_API_KEY: ${maskToken(answers.authToken)}`));
690
676
  }
691
677
  if (answers.launchArgs && answers.launchArgs.length > 0) {
692
678
  console.log(chalk.gray(` 启动参数: ${answers.launchArgs.join(' ')}`));
@@ -712,7 +698,7 @@ class ProviderAdder extends BaseCommand {
712
698
  if (answers.baseUrl) {
713
699
  console.log(chalk.gray(` 基础URL: ${answers.baseUrl}`));
714
700
  }
715
- console.log(chalk.gray(` Token: ${answers.authToken}`));
701
+ console.log(chalk.gray(` Token: ${maskToken(answers.authToken)}`));
716
702
 
717
703
  if (launchArgs.length > 0) {
718
704
  console.log(chalk.gray(` 启动参数: ${launchArgs.join(' ')}`));
@@ -1,12 +1,12 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
- const { ConfigManager } = require('../config');
4
+ const { configManager } = require('../config');
5
5
  const { Logger } = require('../utils/logger');
6
6
 
7
7
  class BackupManager {
8
8
  constructor() {
9
- this.configManager = new ConfigManager();
9
+ this.configManager = configManager;
10
10
  }
11
11
 
12
12
  /**
@@ -1,14 +1,16 @@
1
1
  const chalk = require('chalk');
2
- const { ConfigManager } = require('../config');
2
+ const { configManager } = require('../config');
3
3
  const { Logger } = require('../utils/logger');
4
+ const { maybeMaskToken } = require('../utils/secrets');
4
5
 
5
6
  class CurrentConfig {
6
7
  constructor() {
7
- this.configManager = new ConfigManager();
8
+ this.configManager = configManager;
8
9
  }
9
10
 
10
- async show() {
11
+ async show(options = {}) {
11
12
  try {
13
+ const showToken = Boolean(options.showToken);
12
14
  await this.configManager.ensureLoaded();
13
15
  const currentProvider = this.configManager.getCurrentProvider();
14
16
 
@@ -30,7 +32,7 @@ class CurrentConfig {
30
32
  console.log(chalk.gray(`OPENAI_BASE_URL: ${currentProvider.baseUrl}`));
31
33
  }
32
34
  if (currentProvider.authToken) {
33
- console.log(chalk.gray(`OPENAI_API_KEY: ${currentProvider.authToken}`));
35
+ console.log(chalk.gray(`OPENAI_API_KEY: ${maybeMaskToken(currentProvider.authToken, showToken)}`));
34
36
  }
35
37
  console.log(chalk.gray(`创建时间: ${new Date(currentProvider.createdAt).toLocaleString()}`));
36
38
  console.log(chalk.gray(`最后使用: ${new Date(currentProvider.lastUsed).toLocaleString()}`));
@@ -41,7 +43,7 @@ class CurrentConfig {
41
43
  console.log(chalk.gray(`set OPENAI_BASE_URL=${currentProvider.baseUrl}`));
42
44
  }
43
45
  if (currentProvider.authToken) {
44
- console.log(chalk.gray(`set OPENAI_API_KEY=${currentProvider.authToken}`));
46
+ console.log(chalk.gray(`set OPENAI_API_KEY=${maybeMaskToken(currentProvider.authToken, showToken)}`));
45
47
  }
46
48
  console.log(chalk.gray('codex'));
47
49
  return;
@@ -64,7 +66,7 @@ class CurrentConfig {
64
66
  if (currentProvider.baseUrl) {
65
67
  console.log(chalk.gray(`基础URL: ${currentProvider.baseUrl}`));
66
68
  }
67
- console.log(chalk.gray(`认证Token: ${currentProvider.authToken}`));
69
+ console.log(chalk.gray(`认证Token: ${maybeMaskToken(currentProvider.authToken, showToken)}`));
68
70
  console.log(chalk.gray(`创建时间: ${new Date(currentProvider.createdAt).toLocaleString()}`));
69
71
  console.log(chalk.gray(`最后使用: ${new Date(currentProvider.lastUsed).toLocaleString()}`));
70
72
 
@@ -82,17 +84,17 @@ class CurrentConfig {
82
84
  console.log(chalk.gray(`set ANTHROPIC_BASE_URL=${currentProvider.baseUrl}`));
83
85
  }
84
86
  if (currentProvider.authMode === 'oauth_token') {
85
- console.log(chalk.gray(`set CLAUDE_CODE_OAUTH_TOKEN=${currentProvider.authToken}`));
87
+ console.log(chalk.gray(`set CLAUDE_CODE_OAUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
86
88
  } else if (currentProvider.authMode === 'api_key') {
87
89
  // 根据 tokenType 显示对应的环境变量
88
90
  if (currentProvider.tokenType === 'auth_token') {
89
- console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${currentProvider.authToken}`));
91
+ console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
90
92
  } else {
91
- console.log(chalk.gray(`set ANTHROPIC_API_KEY=${currentProvider.authToken}`));
93
+ console.log(chalk.gray(`set ANTHROPIC_API_KEY=${maybeMaskToken(currentProvider.authToken, showToken)}`));
92
94
  }
93
95
  } else {
94
96
  // auth_token 模式
95
- console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${currentProvider.authToken}`));
97
+ console.log(chalk.gray(`set ANTHROPIC_AUTH_TOKEN=${maybeMaskToken(currentProvider.authToken, showToken)}`));
96
98
  }
97
99
  if (currentProvider.models?.primary) {
98
100
  console.log(chalk.gray(`set ANTHROPIC_MODEL=${currentProvider.models.primary}`));
@@ -109,9 +111,9 @@ class CurrentConfig {
109
111
  }
110
112
  }
111
113
 
112
- async function currentCommand() {
114
+ async function currentCommand(options = {}) {
113
115
  const current = new CurrentConfig();
114
- await current.show();
116
+ await current.show(options);
115
117
  }
116
118
 
117
119
  module.exports = { currentCommand, CurrentConfig };
@@ -1,6 +1,6 @@
1
1
  const inquirer = require('inquirer');
2
2
  const chalk = require('chalk');
3
- const { ConfigManager } = require('../config');
3
+ const { configManager } = require('../config');
4
4
  const { validator } = require('../utils/validator');
5
5
  const { Logger } = require('../utils/logger');
6
6
  const { UIHelper } = require('../utils/ui-helper');
@@ -9,7 +9,7 @@ const { BaseCommand } = require('./BaseCommand');
9
9
  class ProviderEditor extends BaseCommand {
10
10
  constructor() {
11
11
  super();
12
- this.configManager = new ConfigManager();
12
+ this.configManager = configManager;
13
13
  }
14
14
 
15
15
  async interactive(providerName) {
@@ -86,6 +86,14 @@ class ProviderEditor extends BaseCommand {
86
86
  ];
87
87
 
88
88
  if (isCodex) {
89
+ const existingLaunchArgs = Array.isArray(providerToEdit.launchArgs) ? providerToEdit.launchArgs : [];
90
+ const { getCodexLaunchArgs, checkExclusiveArgs } = require('../utils/launch-args');
91
+ const codexArgs = getCodexLaunchArgs();
92
+
93
+ const knownCodexArgNames = new Set(codexArgs.map(arg => arg.name));
94
+ const customCodexArgs = existingLaunchArgs
95
+ .filter(arg => typeof arg === 'string' && !knownCodexArgNames.has(arg));
96
+
89
97
  questions.push(
90
98
  {
91
99
  type: 'input',
@@ -106,6 +114,30 @@ class ProviderEditor extends BaseCommand {
106
114
  if (!input) return 'API Key 不能为空';
107
115
  return validator.validateToken(input) || true;
108
116
  }
117
+ },
118
+ {
119
+ type: 'checkbox',
120
+ name: 'launchArgs',
121
+ message: 'Codex 启动参数:',
122
+ choices: [
123
+ ...codexArgs.map(arg => ({
124
+ name: `${arg.label} (${arg.name})${arg.description ? ' - ' + arg.description : ''}`,
125
+ value: arg.name,
126
+ checked: existingLaunchArgs.includes(arg.name),
127
+ })),
128
+ ...customCodexArgs.map(arg => ({
129
+ name: `${arg} ${chalk.gray('(自定义参数)')}`,
130
+ value: arg,
131
+ checked: true,
132
+ })),
133
+ ],
134
+ validate: (selected) => {
135
+ const conflictError = checkExclusiveArgs(selected, codexArgs);
136
+ if (conflictError) {
137
+ return conflictError;
138
+ }
139
+ return true;
140
+ }
109
141
  }
110
142
  );
111
143
  } else {
@@ -135,9 +167,19 @@ class ProviderEditor extends BaseCommand {
135
167
  {
136
168
  type: 'input',
137
169
  name: 'baseUrl',
138
- message: 'API基础URL:',
139
- default: providerToEdit.baseUrl,
140
- validate: (input) => validator.validateUrl(input) || true,
170
+ message: (answers) => {
171
+ if (answers.authMode === 'auth_token') {
172
+ return 'API基础URL (留空使用官方API):';
173
+ }
174
+ return 'API基础URL:';
175
+ },
176
+ default: providerToEdit.baseUrl || '',
177
+ validate: (input, answers) => {
178
+ if (answers.authMode === 'auth_token' && !input) {
179
+ return true;
180
+ }
181
+ return validator.validateUrl(input) || true;
182
+ },
141
183
  when: (answers) => answers.authMode === 'api_key' || answers.authMode === 'auth_token',
142
184
  },
143
185
  {
@@ -202,15 +244,17 @@ class ProviderEditor extends BaseCommand {
202
244
  ideName: 'codex',
203
245
  baseUrl: answers.baseUrl || null,
204
246
  authToken: answers.authToken,
205
- launchArgs: existingProvider.launchArgs || [],
247
+ launchArgs: answers.launchArgs,
206
248
  setAsDefault: false
207
249
  });
208
250
  } else {
209
251
  // Re-use addProvider which can overwrite existing providers
252
+ // oauth_token 模式不需要 baseUrl,显式设为 null
253
+ const baseUrl = answers.authMode === 'oauth_token' ? null : answers.baseUrl;
210
254
  await this.configManager.addProvider(name, {
211
255
  displayName: answers.displayName,
212
256
  ideName,
213
- baseUrl: answers.baseUrl,
257
+ baseUrl,
214
258
  authToken: answers.authToken,
215
259
  authMode: answers.authMode,
216
260
  tokenType: answers.tokenType, // 仅在 authMode 为 'api_key' 时使用
@@ -1,16 +1,18 @@
1
1
  const chalk = require('chalk');
2
- const { ConfigManager } = require('../config');
2
+ const { configManager } = require('../config');
3
3
  const { Logger } = require('../utils/logger');
4
4
  const { ProviderStatusChecker } = require('../utils/provider-status-checker');
5
+ const { maybeMaskToken } = require('../utils/secrets');
5
6
 
6
7
  class ProviderLister {
7
8
  constructor() {
8
- this.configManager = new ConfigManager();
9
+ this.configManager = configManager;
9
10
  this.statusChecker = new ProviderStatusChecker();
10
11
  }
11
12
 
12
- async list(filter = null) {
13
+ async list(filter = null, options = {}) {
13
14
  try {
15
+ const showToken = Boolean(options.showToken);
14
16
  await this.configManager.ensureLoaded();
15
17
  let providers = this.configManager.listProviders();
16
18
  const currentProvider = this.configManager.getCurrentProvider();
@@ -60,7 +62,7 @@ class ProviderLister {
60
62
  console.log(chalk.gray(` OPENAI_BASE_URL: ${provider.baseUrl}`));
61
63
  }
62
64
  if (provider.authToken) {
63
- console.log(chalk.gray(` OPENAI_API_KEY: ${provider.authToken}`));
65
+ console.log(chalk.gray(` OPENAI_API_KEY: ${maybeMaskToken(provider.authToken, showToken)}`));
64
66
  }
65
67
  } else {
66
68
  // 显示认证模式
@@ -75,7 +77,7 @@ class ProviderLister {
75
77
  if (provider.authMode === 'oauth_token') {
76
78
  // OAuth 模式
77
79
  if (provider.authToken) {
78
- console.log(chalk.gray(` CLAUDE_CODE_OAUTH_TOKEN: ${provider.authToken}`));
80
+ console.log(chalk.gray(` CLAUDE_CODE_OAUTH_TOKEN: ${maybeMaskToken(provider.authToken, showToken)}`));
79
81
  }
80
82
  if (provider.baseUrl) {
81
83
  console.log(chalk.gray(` ANTHROPIC_BASE_URL: ${provider.baseUrl}`));
@@ -87,7 +89,7 @@ class ProviderLister {
87
89
  }
88
90
  if (provider.authToken) {
89
91
  const tokenEnvName = provider.tokenType === 'auth_token' ? 'ANTHROPIC_AUTH_TOKEN' : 'ANTHROPIC_API_KEY';
90
- console.log(chalk.gray(` ${tokenEnvName}: ${provider.authToken}`));
92
+ console.log(chalk.gray(` ${tokenEnvName}: ${maybeMaskToken(provider.authToken, showToken)}`));
91
93
  }
92
94
  } else {
93
95
  // auth_token 模式
@@ -95,7 +97,7 @@ class ProviderLister {
95
97
  console.log(chalk.gray(` ANTHROPIC_BASE_URL: ${provider.baseUrl}`));
96
98
  }
97
99
  if (provider.authToken) {
98
- console.log(chalk.gray(` ANTHROPIC_AUTH_TOKEN: ${provider.authToken}`));
100
+ console.log(chalk.gray(` ANTHROPIC_AUTH_TOKEN: ${maybeMaskToken(provider.authToken, showToken)}`));
99
101
  }
100
102
  }
101
103
  }
@@ -161,9 +163,9 @@ class ProviderLister {
161
163
  }
162
164
  }
163
165
 
164
- async function listCommand(filter = null) {
166
+ async function listCommand(filter = null, options = {}) {
165
167
  const lister = new ProviderLister();
166
- await lister.list(filter);
168
+ await lister.list(filter, options);
167
169
  }
168
170
 
169
171
  module.exports = { listCommand, ProviderLister };
@@ -1,5 +1,5 @@
1
1
  const inquirer = require('inquirer');
2
- const { ConfigManager } = require('../config');
2
+ const { configManager } = require('../config');
3
3
  const { Logger } = require('../utils/logger');
4
4
  const { UIHelper } = require('../utils/ui-helper');
5
5
  const { BaseCommand } = require('./BaseCommand');
@@ -7,7 +7,7 @@ const { BaseCommand } = require('./BaseCommand');
7
7
  class ProviderRemover extends BaseCommand {
8
8
  constructor() {
9
9
  super();
10
- this.configManager = new ConfigManager();
10
+ this.configManager = configManager;
11
11
  }
12
12
 
13
13
  async remove(providerName) {
@@ -2,7 +2,7 @@ const path = require('path');
2
2
  const inquirer = require('inquirer');
3
3
  const chalk = require('chalk');
4
4
  const Choices = require('inquirer/lib/objects/choices');
5
- const { ConfigManager } = require('../config');
5
+ const { configManager } = require('../config');
6
6
  const { executeWithEnv } = require('../utils/env-launcher');
7
7
  const { executeCodexWithEnv } = require('../utils/codex-launcher');
8
8
  const { Logger } = require('../utils/logger');
@@ -11,11 +11,12 @@ const { findSettingsConflict, backupSettingsFile, clearConflictKeys, saveSetting
11
11
  const { BaseCommand } = require('./BaseCommand');
12
12
  const { validator } = require('../utils/validator');
13
13
  const { ProviderStatusChecker } = require('../utils/provider-status-checker');
14
+ const { maskToken } = require('../utils/secrets');
14
15
 
15
16
  class EnvSwitcher extends BaseCommand {
16
17
  constructor() {
17
18
  super();
18
- this.configManager = new ConfigManager();
19
+ this.configManager = configManager;
19
20
  this.statusChecker = new ProviderStatusChecker();
20
21
  this.latestStatusMap = {};
21
22
  this.currentPromptContext = null;
@@ -39,6 +40,22 @@ class EnvSwitcher extends BaseCommand {
39
40
  const provider = await this.validateProvider(providerName);
40
41
  const isCodex = provider.ideName === 'codex';
41
42
  const availableArgs = isCodex ? this.getCodexLaunchArgs() : this.getAvailableLaunchArgs();
43
+ const defaultLaunchArgs = Array.isArray(provider.launchArgs) ? provider.launchArgs : [];
44
+ const knownArgNames = new Set(availableArgs.map(arg => arg.name));
45
+ const customLaunchArgs = defaultLaunchArgs
46
+ .filter(arg => typeof arg === 'string' && !knownArgNames.has(arg));
47
+ const mergedArgs = [
48
+ ...availableArgs.map(arg => ({
49
+ ...arg,
50
+ checked: defaultLaunchArgs.includes(arg.name) || Boolean(arg.checked)
51
+ })),
52
+ ...customLaunchArgs.map(name => ({
53
+ name,
54
+ label: name,
55
+ description: '自定义启动参数',
56
+ checked: true
57
+ }))
58
+ ];
42
59
  const ideDisplayName = isCodex ? 'Codex CLI' : 'Claude Code';
43
60
 
44
61
  console.log(UIHelper.createTitle('启动配置', UIHelper.icons.launch));
@@ -66,7 +83,7 @@ class EnvSwitcher extends BaseCommand {
66
83
  type: 'checkbox',
67
84
  name: 'selectedArgs',
68
85
  message: '选择启动参数:',
69
- choices: availableArgs.map(arg => {
86
+ choices: mergedArgs.map(arg => {
70
87
  const commandText = UIHelper.colors.muted(`(${arg.name})`);
71
88
  const descriptionText = arg.description
72
89
  ? ` ${UIHelper.colors.muted(arg.description)}`
@@ -75,7 +92,7 @@ class EnvSwitcher extends BaseCommand {
75
92
  return {
76
93
  name: `${UIHelper.colors.accent(arg.label || arg.name)} ${commandText}${descriptionText}`,
77
94
  value: arg.name,
78
- checked: arg.checked || false
95
+ checked: Boolean(arg.checked)
79
96
  };
80
97
  })
81
98
  }
@@ -266,78 +283,20 @@ class EnvSwitcher extends BaseCommand {
266
283
  }
267
284
 
268
285
  getAvailableLaunchArgs() {
269
- return [
270
- {
271
- name: '--continue',
272
- label: '继续上次对话',
273
- description: '恢复上次的对话记录',
274
- checked: false
275
- },
276
- {
277
- name: '--dangerously-skip-permissions',
278
- label: '最高权限',
279
- description: '仅在沙盒环境中使用',
280
- checked: false
281
- }
282
- ];
286
+ const { getClaudeLaunchArgs } = require('../utils/launch-args');
287
+ return getClaudeLaunchArgs();
283
288
  }
284
289
 
285
290
  checkExclusiveArgs(selectedArgs, availableArgs) {
286
- if (!selectedArgs || selectedArgs.length < 2) {
287
- return null;
288
- }
289
-
290
- for (const argDef of availableArgs) {
291
- if (!argDef.exclusive || !selectedArgs.includes(argDef.name)) {
292
- continue;
293
- }
294
-
295
- for (const exclusiveArg of argDef.exclusive) {
296
- if (selectedArgs.includes(exclusiveArg)) {
297
- const arg1 = availableArgs.find(a => a.name === argDef.name);
298
- const arg2 = availableArgs.find(a => a.name === exclusiveArg);
299
- return `"${arg1?.label || argDef.name}" 和 "${arg2?.label || exclusiveArg}" 不能同时选择`;
300
- }
301
- }
302
- }
303
-
304
- return null;
291
+ const { checkExclusiveArgs } = require('../utils/launch-args');
292
+ return checkExclusiveArgs(selectedArgs, availableArgs);
305
293
  }
306
294
 
307
295
  getCodexLaunchArgs() {
308
- return [
309
- {
310
- name: 'resume',
311
- label: '继续上次对话',
312
- description: '恢复之前的会话',
313
- checked: false,
314
- isSubcommand: true
315
- },
316
- {
317
- name: '--full-auto',
318
- label: '全自动模式',
319
- description: '自动批准 + 工作区写入沙盒',
320
- checked: false,
321
- exclusive: ['--dangerously-bypass-approvals-and-sandbox']
322
- },
323
- {
324
- name: '--dangerously-bypass-approvals-and-sandbox',
325
- label: '跳过审批和沙盒',
326
- description: '危险:跳过所有安全检查',
327
- checked: false,
328
- exclusive: ['--full-auto']
329
- },
330
- {
331
- name: '--search',
332
- label: '启用网页搜索',
333
- description: '允许模型搜索网页',
334
- checked: false
335
- }
336
- ];
296
+ const { getCodexLaunchArgs } = require('../utils/launch-args');
297
+ return getCodexLaunchArgs();
337
298
  }
338
299
 
339
- // getArgDescription 方法已被移除,直接使用 arg.description
340
-
341
300
  async showProviderSelection() {
342
301
  try {
343
302
  // 并行加载配置和准备界面
@@ -398,6 +357,7 @@ class EnvSwitcher extends BaseCommand {
398
357
  const escListener = this.createESCListener(() => {
399
358
  Logger.info('退出程序');
400
359
  this.showExitScreen();
360
+ this.destroy();
401
361
  process.exit(0);
402
362
  }, '退出程序');
403
363
 
@@ -479,6 +439,7 @@ class EnvSwitcher extends BaseCommand {
479
439
  return await this.showManageMenu();
480
440
  case '__EXIT__':
481
441
  this.showExitScreen();
442
+ this.destroy();
482
443
  process.exit(0);
483
444
  default:
484
445
  return await this.showLaunchArgsSelection(selection);
@@ -1198,6 +1159,7 @@ class EnvSwitcher extends BaseCommand {
1198
1159
  return await this.showProviderSelection();
1199
1160
  case 'exit':
1200
1161
  Logger.info('👋 再见!');
1162
+ this.destroy();
1201
1163
  process.exit(0);
1202
1164
  default:
1203
1165
  // 如果选择的是供应商名称,显示该供应商的详细信息
@@ -1239,9 +1201,13 @@ class EnvSwitcher extends BaseCommand {
1239
1201
  }
1240
1202
 
1241
1203
  // 继续添加其他信息
1204
+ const baseUrlDisplay = provider.baseUrl
1205
+ || ((provider.authMode === 'oauth_token' || provider.authMode === 'auth_token')
1206
+ ? '✨ 官方默认服务器'
1207
+ : '⚠️ 未设置');
1242
1208
  details.push(
1243
- ['基础URL', provider.baseUrl || (provider.authMode === 'oauth_token' ? '✨ 官方默认服务器' : '⚠️ 未设置')],
1244
- ['认证令牌', provider.authToken || '未设置'],
1209
+ ['基础URL', baseUrlDisplay],
1210
+ ['认证令牌', provider.authToken ? maskToken(provider.authToken) : '未设置'],
1245
1211
  ['主模型', provider.models?.primary || '未设置'],
1246
1212
  ['快速模型', provider.models?.smallFast || '未设置'],
1247
1213
  ['创建时间', UIHelper.formatTime(provider.createdAt)],
@@ -1459,7 +1425,8 @@ class EnvSwitcher extends BaseCommand {
1459
1425
  await this.configManager.ensureLoaded();
1460
1426
  const providersMap = this.configManager.config.providers;
1461
1427
 
1462
- if (providersMap[newName] && providersMap[newName] !== provider) {
1428
+ // 如果新名称已存在且不是当前供应商,则报错
1429
+ if (providersMap[newName]) {
1463
1430
  Logger.error(`供应商名称 '${newName}' 已存在,请使用其他名称`);
1464
1431
  return await this.showManageMenu();
1465
1432
  }