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.
- package/.playwright-mcp/grid-view-before.png +0 -0
- package/.playwright-mcp/list-view.png +0 -0
- package/CLAUDE.md +338 -0
- package/README.md +42 -6
- package/README_EN.md +40 -6
- package/package.json +4 -4
- package/src/commands/account.js +124 -23
- package/src/commands/env.js +728 -0
- package/src/commands/helpers.js +32 -0
- package/src/commands/index.js +22 -1
- package/src/commands/mcp.js +71 -13
- package/src/config.js +290 -33
- package/src/index.js +90 -25
- package/src/ui-server.js +1093 -9
package/src/commands/account.js
CHANGED
|
@@ -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(
|
|
691
|
-
if (!
|
|
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:
|
|
774
|
+
choices: choices,
|
|
711
775
|
},
|
|
712
776
|
]);
|
|
713
777
|
|
|
714
|
-
|
|
778
|
+
nameOrId = answers.accountName;
|
|
715
779
|
}
|
|
716
780
|
|
|
717
|
-
|
|
781
|
+
// Find account by ID or name
|
|
782
|
+
const accountInfo = config.getAccountByIdOrName(nameOrId);
|
|
783
|
+
if (!accountInfo) {
|
|
718
784
|
console.log(
|
|
719
|
-
chalk.red(`✗ Account '${
|
|
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(
|
|
993
|
-
if (!
|
|
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:
|
|
1100
|
+
choices: choices,
|
|
1008
1101
|
},
|
|
1009
1102
|
]);
|
|
1010
1103
|
|
|
1011
|
-
|
|
1104
|
+
nameOrId = answers.accountName;
|
|
1012
1105
|
}
|
|
1013
1106
|
|
|
1014
|
-
|
|
1107
|
+
// Find account by ID or name
|
|
1108
|
+
const accountInfo = config.getAccountByIdOrName(nameOrId);
|
|
1109
|
+
if (!accountInfo) {
|
|
1015
1110
|
console.log(
|
|
1016
|
-
chalk.red(`✗ Account '${
|
|
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(
|
|
1073
|
-
if (!
|
|
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
|
-
|
|
1086
|
-
|
|
1182
|
+
// Find account by ID or name
|
|
1183
|
+
const accountInfo = config.getAccountByIdOrName(nameOrId);
|
|
1184
|
+
if (!accountInfo) {
|
|
1087
1185
|
console.log(
|
|
1088
|
-
chalk.red(`✗ Account '${
|
|
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));
|