ai-account-switch 1.8.1 → 1.11.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.
@@ -102,6 +102,10 @@ async function addAccount(name, options) {
102
102
  {
103
103
  name: "responses - Use API key in auth.json (requires_openai_auth) (在 auth.json 中使用 API key)",
104
104
  value: WIRE_API_MODES.RESPONSES
105
+ },
106
+ {
107
+ name: "env - Use API key from environment variable (从环境变量获取 API key)",
108
+ value: WIRE_API_MODES.ENV
105
109
  }
106
110
  ],
107
111
  default: DEFAULT_WIRE_API
@@ -125,6 +129,34 @@ async function addAccount(name, options) {
125
129
  `\n✓ Selected wire_api mode (已选择模式): ${wireApiSelection}\n`
126
130
  )
127
131
  );
132
+
133
+ // If env mode is selected, prompt for environment variable name
134
+ let envKeyName = null;
135
+ if (wireApiSelection === WIRE_API_MODES.ENV) {
136
+ const envKeyAnswer = await inquirer.prompt([
137
+ {
138
+ type: "input",
139
+ name: "envKey",
140
+ message: "Enter environment variable name for API key (请输入 API key 的环境变量名称):",
141
+ default: "AIS_USER_API_KEY",
142
+ validate: (input) => {
143
+ if (!input.trim()) {
144
+ return "Environment variable name is required (环境变量名称不能为空)";
145
+ }
146
+ if (!/^[A-Z_][A-Z0-9_]*$/.test(input.trim())) {
147
+ return "Invalid variable name. Use uppercase letters, numbers, and underscores (e.g., MY_API_KEY) (变量名无效。请使用大写字母、数字和下划线,例如: MY_API_KEY)";
148
+ }
149
+ return true;
150
+ }
151
+ }
152
+ ]);
153
+ envKeyName = envKeyAnswer.envKey.trim();
154
+ console.log(
155
+ chalk.cyan(
156
+ `\n✓ Environment variable (环境变量): ${envKeyName}\n`
157
+ )
158
+ );
159
+ }
128
160
  } else if (typeAnswer.type === "Droids") {
129
161
  console.log(
130
162
  chalk.cyan("\n📝 Droids Configuration Tips (Droids 配置提示):")
@@ -210,6 +242,10 @@ async function addAccount(name, options) {
210
242
  // Add wire_api selection for Codex accounts
211
243
  if (typeAnswer.type === "Codex" && wireApiSelection) {
212
244
  accountData.wireApi = wireApiSelection;
245
+ // Add environment variable name for env mode
246
+ if (wireApiSelection === WIRE_API_MODES.ENV && envKeyName) {
247
+ accountData.envKey = envKeyName;
248
+ }
213
249
  }
214
250
 
215
251
  // Handle custom environment variables
@@ -514,6 +550,22 @@ async function addAccount(name, options) {
514
550
  " Your API key will be stored in ~/.codex/auth.json (API key 将存储在 ~/.codex/auth.json)\n"
515
551
  )
516
552
  );
553
+ } else if (accountData.wireApi === WIRE_API_MODES.ENV) {
554
+ console.log(
555
+ chalk.yellow(
556
+ " ⚠ Note: This account uses 'env' mode (此账号使用 'env' 模式)"
557
+ )
558
+ );
559
+ console.log(
560
+ chalk.white(
561
+ ` You need to export the environment variable before using Codex (使用 Codex 前需要导出环境变量):`
562
+ )
563
+ );
564
+ console.log(
565
+ chalk.cyan(
566
+ ` export ${accountData.envKey}="${accountData.apiKey}"\n`
567
+ )
568
+ );
517
569
  } else {
518
570
  console.log(
519
571
  chalk.cyan(
@@ -623,11 +675,14 @@ function listAccounts() {
623
675
  const account = accounts[name];
624
676
  const isActive = currentProject && currentProject.name === name;
625
677
  const marker = isActive ? chalk.green("● ") : " ";
678
+
679
+ // Display ID and name
680
+ const idDisplay = chalk.yellow(`[${account.id}]`);
626
681
  const nameDisplay = isActive
627
682
  ? chalk.green.bold(name)
628
683
  : chalk.cyan(name);
629
684
 
630
- console.log(`${marker}${nameDisplay}`);
685
+ console.log(`${marker}${idDisplay} ${nameDisplay}`);
631
686
  console.log(` Type: ${account.type}`);
632
687
  console.log(` API Key: ${maskApiKey(account.apiKey)}`);
633
688
  if (account.apiUrl) console.log(` API URL: ${account.apiUrl}`);
@@ -687,9 +742,9 @@ function listAccounts() {
687
742
  /**
688
743
  * Switch to a specific account for current project
689
744
  */
690
- async function useAccount(name) {
691
- if (!name) {
692
- // If no name provided, show interactive selection
745
+ async function useAccount(nameOrId) {
746
+ if (!nameOrId) {
747
+ // If no name/ID provided, show interactive selection
693
748
  const accounts = config.getAllAccounts();
694
749
  const accountNames = Object.keys(accounts);
695
750
 
@@ -702,21 +757,32 @@ async function useAccount(name) {
702
757
  return;
703
758
  }
704
759
 
760
+ // Create choices with ID and name
761
+ const choices = accountNames.map(name => {
762
+ const account = accounts[name];
763
+ return {
764
+ name: `[${account.id}] ${name}`,
765
+ value: name
766
+ };
767
+ });
768
+
705
769
  const answers = await inquirer.prompt([
706
770
  {
707
771
  type: "list",
708
772
  name: "accountName",
709
773
  message: "Select an account to use (请选择要使用的账号):",
710
- choices: accountNames,
774
+ choices: choices,
711
775
  },
712
776
  ]);
713
777
 
714
- name = answers.accountName;
778
+ nameOrId = answers.accountName;
715
779
  }
716
780
 
717
- if (!config.accountExists(name)) {
781
+ // Find account by ID or name
782
+ const accountInfo = config.getAccountByIdOrName(nameOrId);
783
+ if (!accountInfo) {
718
784
  console.log(
719
- chalk.red(`✗ Account '${name}' not found. (未找到账号 '${name}'。)`)
785
+ chalk.red(`✗ Account '${nameOrId}' not found. (未找到账号 '${nameOrId}'。)`)
720
786
  );
721
787
  console.log(
722
788
  chalk.yellow(
@@ -726,6 +792,7 @@ async function useAccount(name) {
726
792
  return;
727
793
  }
728
794
 
795
+ const name = accountInfo.name;
729
796
  const success = config.setProjectAccount(name);
730
797
  if (success) {
731
798
  const fs = require("fs");
@@ -775,6 +842,22 @@ async function useAccount(name) {
775
842
  `✓ API key stored in ~/.codex/auth.json (API key 已存储在 ~/.codex/auth.json)`
776
843
  )
777
844
  );
845
+ } else if (account.wireApi === WIRE_API_MODES.ENV) {
846
+ console.log(
847
+ chalk.yellow(
848
+ `✓ Wire API mode: ${WIRE_API_MODES.ENV} (使用 ${WIRE_API_MODES.ENV} 模式)`
849
+ )
850
+ );
851
+ console.log(
852
+ chalk.green(
853
+ `\n✓ Copy and run this command (复制并执行此命令):`
854
+ )
855
+ );
856
+ console.log(
857
+ chalk.cyan.bold(
858
+ ` export ${account.envKey || 'AIS_USER_API_KEY'}="${account.apiKey}" && codex --profile ${profileName}`
859
+ )
860
+ );
778
861
  } else {
779
862
  console.log(
780
863
  chalk.cyan(
@@ -920,6 +1003,7 @@ function showInfo() {
920
1003
  console.log(
921
1004
  chalk.bold("\n📌 Current Project Account Info (当前项目账号信息):\n")
922
1005
  );
1006
+ console.log(`${chalk.cyan("Account ID:")} ${chalk.yellow(`[${projectAccount.id}]`)}`);
923
1007
  console.log(
924
1008
  `${chalk.cyan("Account Name:")} ${chalk.green.bold(
925
1009
  projectAccount.name
@@ -989,8 +1073,8 @@ function showInfo() {
989
1073
  /**
990
1074
  * Remove an account
991
1075
  */
992
- async function removeAccount(name) {
993
- if (!name) {
1076
+ async function removeAccount(nameOrId) {
1077
+ if (!nameOrId) {
994
1078
  const accounts = config.getAllAccounts();
995
1079
  const accountNames = Object.keys(accounts);
996
1080
 
@@ -999,30 +1083,43 @@ async function removeAccount(name) {
999
1083
  return;
1000
1084
  }
1001
1085
 
1086
+ // Create choices with ID and name
1087
+ const choices = accountNames.map(name => {
1088
+ const account = accounts[name];
1089
+ return {
1090
+ name: `[${account.id}] ${name}`,
1091
+ value: name
1092
+ };
1093
+ });
1094
+
1002
1095
  const answers = await inquirer.prompt([
1003
1096
  {
1004
1097
  type: "list",
1005
1098
  name: "accountName",
1006
1099
  message: "Select an account to remove (请选择要删除的账号):",
1007
- choices: accountNames,
1100
+ choices: choices,
1008
1101
  },
1009
1102
  ]);
1010
1103
 
1011
- name = answers.accountName;
1104
+ nameOrId = answers.accountName;
1012
1105
  }
1013
1106
 
1014
- if (!config.accountExists(name)) {
1107
+ // Find account by ID or name
1108
+ const accountInfo = config.getAccountByIdOrName(nameOrId);
1109
+ if (!accountInfo) {
1015
1110
  console.log(
1016
- chalk.red(`✗ Account '${name}' not found. (未找到账号 '${name}'。)`)
1111
+ chalk.red(`✗ Account '${nameOrId}' not found. (未找到账号 '${nameOrId}'。)`)
1017
1112
  );
1018
1113
  return;
1019
1114
  }
1020
1115
 
1116
+ const name = accountInfo.name;
1117
+
1021
1118
  const { confirm } = await inquirer.prompt([
1022
1119
  {
1023
1120
  type: "confirm",
1024
1121
  name: "confirm",
1025
- message: `Are you sure you want to remove account '${name}'? (确定要删除账号 '${name}' 吗?)`,
1122
+ message: `Are you sure you want to remove account '${name}' (ID: ${accountInfo.id})? (确定要删除账号 '${name}' (ID: ${accountInfo.id}) 吗?)`,
1026
1123
  default: false,
1027
1124
  },
1028
1125
  ]);
@@ -1069,30 +1166,34 @@ function showCurrent() {
1069
1166
  /**
1070
1167
  * Export account configuration
1071
1168
  */
1072
- function exportAccount(name) {
1073
- if (!name) {
1169
+ function exportAccount(nameOrId) {
1170
+ if (!nameOrId) {
1074
1171
  console.log(
1075
- chalk.red("Please specify an account name. (请指定账号名称。)")
1172
+ chalk.red("Please specify an account name or ID. (请指定账号名称或 ID。)")
1076
1173
  );
1077
1174
  console.log(
1078
1175
  chalk.cyan(
1079
- "Usage: ais export <account-name> (用法: ais export <账号名>)"
1176
+ "Usage: ais export <account-name-or-id> (用法: ais export <账号名或ID>)"
1080
1177
  )
1081
1178
  );
1082
1179
  return;
1083
1180
  }
1084
1181
 
1085
- const account = config.getAccount(name);
1086
- if (!account) {
1182
+ // Find account by ID or name
1183
+ const accountInfo = config.getAccountByIdOrName(nameOrId);
1184
+ if (!accountInfo) {
1087
1185
  console.log(
1088
- chalk.red(`✗ Account '${name}' not found. (未找到账号 '${name}'。)`)
1186
+ chalk.red(`✗ Account '${nameOrId}' not found. (未找到账号 '${nameOrId}'。)`)
1089
1187
  );
1090
1188
  return;
1091
1189
  }
1092
1190
 
1191
+ const name = accountInfo.name;
1192
+ const { name: _, ...account } = accountInfo; // Remove the name property from accountInfo
1193
+
1093
1194
  console.log(
1094
1195
  chalk.bold(
1095
- `\n📤 Export for account '${name}' (账号 '${name}' 的导出数据):\n`
1196
+ `\n📤 Export for account '${name}' (ID: ${account.id}) (账号 '${name}' 的导出数据):\n`
1096
1197
  )
1097
1198
  );
1098
1199
  console.log(JSON.stringify({ [name]: account }, null, 2));