ai-account-switch 1.7.1 → 1.8.1
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/package.json +1 -1
- package/src/commands/account.js +99 -0
- package/src/config.js +179 -5
- package/src/ui-server.js +46 -0
- package/CHANGELOG-v1.7.0.md +0 -176
package/package.json
CHANGED
package/src/commands/account.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const chalk = require("chalk");
|
|
2
2
|
const inquirer = require("inquirer");
|
|
3
3
|
const ConfigManager = require("../config");
|
|
4
|
+
const { WIRE_API_MODES, DEFAULT_WIRE_API, ACCOUNT_TYPES } = require("../config");
|
|
4
5
|
const { maskApiKey } = require("./helpers");
|
|
5
6
|
const { promptForModelGroup } = require("./model");
|
|
6
7
|
|
|
@@ -53,6 +54,9 @@ async function addAccount(name, options) {
|
|
|
53
54
|
},
|
|
54
55
|
]);
|
|
55
56
|
|
|
57
|
+
// Store wire_api selection for Codex accounts
|
|
58
|
+
let wireApiSelection = null;
|
|
59
|
+
|
|
56
60
|
// Show configuration tips based on account type
|
|
57
61
|
if (typeAnswer.type === "Codex") {
|
|
58
62
|
console.log(
|
|
@@ -83,6 +87,44 @@ async function addAccount(name, options) {
|
|
|
83
87
|
" • Codex uses OpenAI-compatible API format (Codex 使用 OpenAI 兼容的 API 格式)\n"
|
|
84
88
|
)
|
|
85
89
|
);
|
|
90
|
+
|
|
91
|
+
// Prompt for wire_api mode selection
|
|
92
|
+
const wireApiAnswer = await inquirer.prompt([
|
|
93
|
+
{
|
|
94
|
+
type: "list",
|
|
95
|
+
name: "wireApi",
|
|
96
|
+
message: "Select wire_api mode (请选择 wire_api 模式):",
|
|
97
|
+
choices: [
|
|
98
|
+
{
|
|
99
|
+
name: "chat - Use API key in HTTP headers (OpenAI-compatible) (在 HTTP 头中使用 API key,OpenAI 兼容)",
|
|
100
|
+
value: WIRE_API_MODES.CHAT
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "responses - Use API key in auth.json (requires_openai_auth) (在 auth.json 中使用 API key)",
|
|
104
|
+
value: WIRE_API_MODES.RESPONSES
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
default: DEFAULT_WIRE_API
|
|
108
|
+
}
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
wireApiSelection = wireApiAnswer.wireApi;
|
|
112
|
+
|
|
113
|
+
// Validate input
|
|
114
|
+
if (!Object.values(WIRE_API_MODES).includes(wireApiSelection)) {
|
|
115
|
+
console.log(
|
|
116
|
+
chalk.yellow(
|
|
117
|
+
`⚠ Invalid wire_api mode, using default: ${DEFAULT_WIRE_API} (无效的模式,使用默认值)`
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
wireApiSelection = DEFAULT_WIRE_API;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log(
|
|
124
|
+
chalk.cyan(
|
|
125
|
+
`\n✓ Selected wire_api mode (已选择模式): ${wireApiSelection}\n`
|
|
126
|
+
)
|
|
127
|
+
);
|
|
86
128
|
} else if (typeAnswer.type === "Droids") {
|
|
87
129
|
console.log(
|
|
88
130
|
chalk.cyan("\n📝 Droids Configuration Tips (Droids 配置提示):")
|
|
@@ -165,6 +207,11 @@ async function addAccount(name, options) {
|
|
|
165
207
|
// Merge type into accountData
|
|
166
208
|
accountData.type = typeAnswer.type;
|
|
167
209
|
|
|
210
|
+
// Add wire_api selection for Codex accounts
|
|
211
|
+
if (typeAnswer.type === "Codex" && wireApiSelection) {
|
|
212
|
+
accountData.wireApi = wireApiSelection;
|
|
213
|
+
}
|
|
214
|
+
|
|
168
215
|
// Handle custom environment variables
|
|
169
216
|
if (accountData.addCustomEnv) {
|
|
170
217
|
accountData.customEnv = {};
|
|
@@ -454,6 +501,27 @@ async function addAccount(name, options) {
|
|
|
454
501
|
)
|
|
455
502
|
);
|
|
456
503
|
console.log(chalk.cyan(` ais use ${name}\n`));
|
|
504
|
+
|
|
505
|
+
// Show wire_api mode specific information
|
|
506
|
+
if (accountData.wireApi === WIRE_API_MODES.RESPONSES) {
|
|
507
|
+
console.log(
|
|
508
|
+
chalk.yellow(
|
|
509
|
+
" ⚠ Note: This account uses 'responses' mode (此账号使用 'responses' 模式)"
|
|
510
|
+
)
|
|
511
|
+
);
|
|
512
|
+
console.log(
|
|
513
|
+
chalk.white(
|
|
514
|
+
" Your API key will be stored in ~/.codex/auth.json (API key 将存储在 ~/.codex/auth.json)\n"
|
|
515
|
+
)
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
console.log(
|
|
519
|
+
chalk.cyan(
|
|
520
|
+
" ✓ This account uses 'chat' mode (此账号使用 'chat' 模式)\n"
|
|
521
|
+
)
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
457
525
|
console.log(
|
|
458
526
|
chalk.white(
|
|
459
527
|
"2. Use Codex with the generated profile (使用生成的配置文件运行 Codex):"
|
|
@@ -562,6 +630,7 @@ function listAccounts() {
|
|
|
562
630
|
console.log(`${marker}${nameDisplay}`);
|
|
563
631
|
console.log(` Type: ${account.type}`);
|
|
564
632
|
console.log(` API Key: ${maskApiKey(account.apiKey)}`);
|
|
633
|
+
if (account.apiUrl) console.log(` API URL: ${account.apiUrl}`);
|
|
565
634
|
if (account.email) console.log(` Email: ${account.email}`);
|
|
566
635
|
if (account.description)
|
|
567
636
|
console.log(` Description: ${account.description}`);
|
|
@@ -589,6 +658,11 @@ function listAccounts() {
|
|
|
589
658
|
) {
|
|
590
659
|
console.log(` Model: ${account.model}`);
|
|
591
660
|
}
|
|
661
|
+
// Display wire_api configuration for Codex accounts
|
|
662
|
+
if (account.type === ACCOUNT_TYPES.CODEX) {
|
|
663
|
+
const wireApi = account.wireApi || `${DEFAULT_WIRE_API} (default)`;
|
|
664
|
+
console.log(` Wire API: ${wireApi}`);
|
|
665
|
+
}
|
|
592
666
|
console.log(
|
|
593
667
|
` Created: ${new Date(account.createdAt).toLocaleString()}`
|
|
594
668
|
);
|
|
@@ -688,6 +762,27 @@ async function useAccount(name) {
|
|
|
688
762
|
`✓ Codex profile created (Codex 配置文件已创建): ${profileName}`
|
|
689
763
|
)
|
|
690
764
|
);
|
|
765
|
+
|
|
766
|
+
// Display wire_api mode information
|
|
767
|
+
if (account.wireApi === WIRE_API_MODES.RESPONSES) {
|
|
768
|
+
console.log(
|
|
769
|
+
chalk.yellow(
|
|
770
|
+
`✓ Wire API mode: ${WIRE_API_MODES.RESPONSES} (使用 ${WIRE_API_MODES.RESPONSES} 模式)`
|
|
771
|
+
)
|
|
772
|
+
);
|
|
773
|
+
console.log(
|
|
774
|
+
chalk.yellow(
|
|
775
|
+
`✓ API key stored in ~/.codex/auth.json (API key 已存储在 ~/.codex/auth.json)`
|
|
776
|
+
)
|
|
777
|
+
);
|
|
778
|
+
} else {
|
|
779
|
+
console.log(
|
|
780
|
+
chalk.cyan(
|
|
781
|
+
`✓ Wire API mode: ${WIRE_API_MODES.CHAT} (使用 ${WIRE_API_MODES.CHAT} 模式)`
|
|
782
|
+
)
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
|
|
691
786
|
console.log("");
|
|
692
787
|
console.log(chalk.bold.cyan("📖 Next Steps (下一步):"));
|
|
693
788
|
console.log(
|
|
@@ -878,6 +973,10 @@ function showInfo() {
|
|
|
878
973
|
) {
|
|
879
974
|
console.log(`${chalk.cyan("Model:")} ${projectAccount.model}`);
|
|
880
975
|
}
|
|
976
|
+
// Display wire_api configuration for Codex accounts
|
|
977
|
+
if (projectAccount.type === ACCOUNT_TYPES.CODEX && projectAccount.wireApi) {
|
|
978
|
+
console.log(`${chalk.cyan("Wire API:")} ${projectAccount.wireApi}`);
|
|
979
|
+
}
|
|
881
980
|
console.log(
|
|
882
981
|
`${chalk.cyan("Set At:")} ${new Date(
|
|
883
982
|
projectAccount.setAt
|
package/src/config.js
CHANGED
|
@@ -2,6 +2,22 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
|
|
5
|
+
// Constants for wire API modes
|
|
6
|
+
const WIRE_API_MODES = {
|
|
7
|
+
CHAT: 'chat',
|
|
8
|
+
RESPONSES: 'responses'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_WIRE_API = WIRE_API_MODES.CHAT;
|
|
12
|
+
|
|
13
|
+
// Constants for account types
|
|
14
|
+
const ACCOUNT_TYPES = {
|
|
15
|
+
CLAUDE: 'Claude',
|
|
16
|
+
CODEX: 'Codex',
|
|
17
|
+
CCR: 'CCR',
|
|
18
|
+
DROIDS: 'Droids'
|
|
19
|
+
};
|
|
20
|
+
|
|
5
21
|
/**
|
|
6
22
|
* Cross-platform configuration manager
|
|
7
23
|
* Stores global accounts in user home directory
|
|
@@ -587,11 +603,26 @@ class ConfigManager {
|
|
|
587
603
|
profileConfig += `base_url = "${baseUrl}"\n`;
|
|
588
604
|
}
|
|
589
605
|
|
|
590
|
-
// Determine wire_api
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
606
|
+
// Determine wire_api based on account configuration (default to chat for backward compatibility)
|
|
607
|
+
const wireApi = account.wireApi || DEFAULT_WIRE_API;
|
|
608
|
+
|
|
609
|
+
if (wireApi === WIRE_API_MODES.CHAT) {
|
|
610
|
+
// Chat mode: use HTTP headers for authentication
|
|
611
|
+
profileConfig += `wire_api = "${WIRE_API_MODES.CHAT}"\n`;
|
|
612
|
+
profileConfig += `http_headers = { "Authorization" = "Bearer ${account.apiKey}" }\n`;
|
|
613
|
+
|
|
614
|
+
// Note: We do NOT clear auth.json here because:
|
|
615
|
+
// 1. auth.json is a global file shared by all projects
|
|
616
|
+
// 2. Other projects may be using responses mode and need the API key
|
|
617
|
+
// 3. Chat mode doesn't use auth.json anyway, so no conflict exists
|
|
618
|
+
} else if (wireApi === WIRE_API_MODES.RESPONSES) {
|
|
619
|
+
// Responses mode: use auth.json for authentication
|
|
620
|
+
profileConfig += `wire_api = "${WIRE_API_MODES.RESPONSES}"\n`;
|
|
621
|
+
profileConfig += `requires_openai_auth = true\n`;
|
|
622
|
+
|
|
623
|
+
// Update auth.json with API key
|
|
624
|
+
this.updateCodexAuthJson(account.apiKey);
|
|
625
|
+
}
|
|
595
626
|
}
|
|
596
627
|
|
|
597
628
|
// Remove all old profiles with the same name (including duplicates)
|
|
@@ -620,6 +651,146 @@ class ConfigManager {
|
|
|
620
651
|
fs.writeFileSync(helperScript, profileName, 'utf8');
|
|
621
652
|
}
|
|
622
653
|
|
|
654
|
+
/**
|
|
655
|
+
* Read auth.json file
|
|
656
|
+
* @private
|
|
657
|
+
* @returns {Object} Parsed auth data or empty object
|
|
658
|
+
*/
|
|
659
|
+
_readAuthJson(authJsonFile) {
|
|
660
|
+
if (!fs.existsSync(authJsonFile)) {
|
|
661
|
+
return {};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
try {
|
|
665
|
+
const content = fs.readFileSync(authJsonFile, 'utf8');
|
|
666
|
+
return JSON.parse(content);
|
|
667
|
+
} catch (parseError) {
|
|
668
|
+
const chalk = require('chalk');
|
|
669
|
+
console.warn(
|
|
670
|
+
chalk.yellow(
|
|
671
|
+
`⚠ Warning: Could not parse existing auth.json, will create new file (警告: 无法解析现有 auth.json,将创建新文件)`
|
|
672
|
+
)
|
|
673
|
+
);
|
|
674
|
+
return {};
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Write auth.json file atomically with proper permissions
|
|
680
|
+
* Uses atomic write (temp file + rename) to prevent corruption from concurrent access
|
|
681
|
+
* @private
|
|
682
|
+
* @param {string} authJsonFile - Path to auth.json
|
|
683
|
+
* @param {Object} authData - Auth data to write
|
|
684
|
+
*/
|
|
685
|
+
_writeAuthJson(authJsonFile, authData) {
|
|
686
|
+
const chalk = require('chalk');
|
|
687
|
+
const tempFile = `${authJsonFile}.tmp.${process.pid}`;
|
|
688
|
+
|
|
689
|
+
try {
|
|
690
|
+
// Write to temporary file first (atomic operation)
|
|
691
|
+
fs.writeFileSync(tempFile, JSON.stringify(authData, null, 2), 'utf8');
|
|
692
|
+
|
|
693
|
+
// Set file permissions to 600 (owner read/write only) for security
|
|
694
|
+
if (process.platform !== 'win32') {
|
|
695
|
+
fs.chmodSync(tempFile, 0o600);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Atomically rename temp file to actual file
|
|
699
|
+
// This is atomic on POSIX systems and prevents corruption
|
|
700
|
+
fs.renameSync(tempFile, authJsonFile);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
// Clean up temp file if it exists
|
|
703
|
+
if (fs.existsSync(tempFile)) {
|
|
704
|
+
try {
|
|
705
|
+
fs.unlinkSync(tempFile);
|
|
706
|
+
} catch (cleanupError) {
|
|
707
|
+
// Ignore cleanup errors
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Clear OPENAI_API_KEY in ~/.codex/auth.json for chat mode
|
|
716
|
+
* @deprecated This method is no longer called automatically.
|
|
717
|
+
* Chat mode doesn't require clearing auth.json since it doesn't use it.
|
|
718
|
+
*/
|
|
719
|
+
clearCodexAuthJson() {
|
|
720
|
+
const chalk = require('chalk');
|
|
721
|
+
const codexDir = path.join(os.homedir(), '.codex');
|
|
722
|
+
const authJsonFile = path.join(codexDir, 'auth.json');
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
// Ensure .codex directory exists
|
|
726
|
+
if (!fs.existsSync(codexDir)) {
|
|
727
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Read existing auth data
|
|
731
|
+
const authData = this._readAuthJson(authJsonFile);
|
|
732
|
+
|
|
733
|
+
// Clear OPENAI_API_KEY (set to empty string)
|
|
734
|
+
authData.OPENAI_API_KEY = "";
|
|
735
|
+
|
|
736
|
+
// Write atomically with proper permissions
|
|
737
|
+
this._writeAuthJson(authJsonFile, authData);
|
|
738
|
+
|
|
739
|
+
console.log(
|
|
740
|
+
chalk.cyan(
|
|
741
|
+
`✓ Cleared OPENAI_API_KEY in auth.json (chat mode) (已清空 auth.json 中的 OPENAI_API_KEY)`
|
|
742
|
+
)
|
|
743
|
+
);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error(
|
|
746
|
+
chalk.yellow(
|
|
747
|
+
`⚠ Warning: Failed to clear auth.json: ${error.message} (警告: 清空 auth.json 失败)`
|
|
748
|
+
)
|
|
749
|
+
);
|
|
750
|
+
// Don't throw error, just warn - this is not critical for chat mode
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Update ~/.codex/auth.json with API key for responses mode
|
|
756
|
+
* @param {string} apiKey - API key to store in auth.json
|
|
757
|
+
* @throws {Error} If file operations fail
|
|
758
|
+
*/
|
|
759
|
+
updateCodexAuthJson(apiKey) {
|
|
760
|
+
const chalk = require('chalk');
|
|
761
|
+
const codexDir = path.join(os.homedir(), '.codex');
|
|
762
|
+
const authJsonFile = path.join(codexDir, 'auth.json');
|
|
763
|
+
|
|
764
|
+
try {
|
|
765
|
+
// Ensure .codex directory exists
|
|
766
|
+
if (!fs.existsSync(codexDir)) {
|
|
767
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Read existing auth data
|
|
771
|
+
const authData = this._readAuthJson(authJsonFile);
|
|
772
|
+
|
|
773
|
+
// Update OPENAI_API_KEY
|
|
774
|
+
authData.OPENAI_API_KEY = apiKey;
|
|
775
|
+
|
|
776
|
+
// Write atomically with proper permissions
|
|
777
|
+
this._writeAuthJson(authJsonFile, authData);
|
|
778
|
+
|
|
779
|
+
console.log(
|
|
780
|
+
chalk.green(
|
|
781
|
+
`✓ Updated auth.json at: ${authJsonFile} (已更新 auth.json)`
|
|
782
|
+
)
|
|
783
|
+
);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
console.error(
|
|
786
|
+
chalk.red(
|
|
787
|
+
`✗ Failed to update auth.json: ${error.message} (更新 auth.json 失败)`
|
|
788
|
+
)
|
|
789
|
+
);
|
|
790
|
+
throw error;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
623
794
|
/**
|
|
624
795
|
* Get current project's active account
|
|
625
796
|
* Searches upwards from current directory to find project root
|
|
@@ -1160,3 +1331,6 @@ class ConfigManager {
|
|
|
1160
1331
|
}
|
|
1161
1332
|
|
|
1162
1333
|
module.exports = ConfigManager;
|
|
1334
|
+
module.exports.WIRE_API_MODES = WIRE_API_MODES;
|
|
1335
|
+
module.exports.DEFAULT_WIRE_API = DEFAULT_WIRE_API;
|
|
1336
|
+
module.exports.ACCOUNT_TYPES = ACCOUNT_TYPES;
|
package/src/ui-server.js
CHANGED
|
@@ -2,6 +2,7 @@ const http = require('http');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const ConfigManager = require('./config');
|
|
5
|
+
const { WIRE_API_MODES, DEFAULT_WIRE_API } = require('./config');
|
|
5
6
|
|
|
6
7
|
class UIServer {
|
|
7
8
|
constructor(port = null) {
|
|
@@ -1491,6 +1492,18 @@ class UIServer {
|
|
|
1491
1492
|
<label for="apiUrl" data-i18n="apiUrl">API URL (可选)</label>
|
|
1492
1493
|
<input type="text" id="apiUrl" data-i18n-placeholder="apiUrlPlaceholder" placeholder="https://api.anthropic.com">
|
|
1493
1494
|
</div>
|
|
1495
|
+
<!-- Wire API selection for Codex accounts -->
|
|
1496
|
+
<div class="form-group" id="wireApiGroup" style="display: none;">
|
|
1497
|
+
<label for="wireApi">Wire API 模式</label>
|
|
1498
|
+
<select id="wireApi">
|
|
1499
|
+
<option value="chat">chat - HTTP Headers 认证 (OpenAI 兼容)</option>
|
|
1500
|
+
<option value="responses">responses - auth.json 认证 (requires_openai_auth)</option>
|
|
1501
|
+
</select>
|
|
1502
|
+
<small style="color: #666; display: block; margin-top: 5px;">
|
|
1503
|
+
chat: API key 存储在 HTTP headers 中<br>
|
|
1504
|
+
responses: API key 存储在 ~/.codex/auth.json 中
|
|
1505
|
+
</small>
|
|
1506
|
+
</div>
|
|
1494
1507
|
<div class="form-group">
|
|
1495
1508
|
<label for="email" data-i18n="email">邮箱 (可选)</label>
|
|
1496
1509
|
<input type="email" id="email" data-i18n-placeholder="emailPlaceholder" placeholder="user@example.com">
|
|
@@ -1615,6 +1628,13 @@ class UIServer {
|
|
|
1615
1628
|
</div>
|
|
1616
1629
|
|
|
1617
1630
|
<script>
|
|
1631
|
+
// Constants for wire API modes (injected from backend)
|
|
1632
|
+
const WIRE_API_MODES = {
|
|
1633
|
+
CHAT: '${WIRE_API_MODES.CHAT}',
|
|
1634
|
+
RESPONSES: '${WIRE_API_MODES.RESPONSES}'
|
|
1635
|
+
};
|
|
1636
|
+
const DEFAULT_WIRE_API = '${DEFAULT_WIRE_API}';
|
|
1637
|
+
|
|
1618
1638
|
// i18n translations
|
|
1619
1639
|
const translations = {
|
|
1620
1640
|
zh: {
|
|
@@ -2021,6 +2041,12 @@ class UIServer {
|
|
|
2021
2041
|
<div class="info-value">\${data.model}</div>
|
|
2022
2042
|
</div>
|
|
2023
2043
|
\` : ''}
|
|
2044
|
+
\${data.type === 'Codex' ? \`
|
|
2045
|
+
<div class="account-info">
|
|
2046
|
+
<div class="info-label">Wire API</div>
|
|
2047
|
+
<div class="info-value">\${data.wireApi || (DEFAULT_WIRE_API + ' (default)')}</div>
|
|
2048
|
+
</div>
|
|
2049
|
+
\` : ''}
|
|
2024
2050
|
\${data.type === 'CCR' && data.ccrConfig ? \`
|
|
2025
2051
|
<div class="account-info">
|
|
2026
2052
|
<div class="info-label">CCR Provider</div>
|
|
@@ -2051,6 +2077,12 @@ class UIServer {
|
|
|
2051
2077
|
const simpleModelGroup = document.getElementById('simpleModelGroup');
|
|
2052
2078
|
const claudeModelGroup = document.getElementById('claudeModelGroup');
|
|
2053
2079
|
const ccrModelGroup = document.getElementById('ccrModelGroup');
|
|
2080
|
+
const wireApiGroup = document.getElementById('wireApiGroup');
|
|
2081
|
+
|
|
2082
|
+
// Show/hide wire_api field for Codex accounts
|
|
2083
|
+
if (wireApiGroup) {
|
|
2084
|
+
wireApiGroup.style.display = accountType === 'Codex' ? 'block' : 'none';
|
|
2085
|
+
}
|
|
2054
2086
|
|
|
2055
2087
|
if (accountType === 'Codex' || accountType === 'Droids') {
|
|
2056
2088
|
simpleModelGroup.style.display = 'block';
|
|
@@ -2117,6 +2149,12 @@ class UIServer {
|
|
|
2117
2149
|
if (account.type === 'Codex' || account.type === 'Droids') {
|
|
2118
2150
|
// Load simple model field
|
|
2119
2151
|
document.getElementById('simpleModel').value = account.model || '';
|
|
2152
|
+
|
|
2153
|
+
// Load wire_api for Codex accounts
|
|
2154
|
+
if (account.type === 'Codex') {
|
|
2155
|
+
document.getElementById('wireApi').value = account.wireApi || DEFAULT_WIRE_API;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2120
2158
|
// Clear model groups and CCR config
|
|
2121
2159
|
document.getElementById('modelGroupsList').innerHTML = '';
|
|
2122
2160
|
modelGroupCount = 0;
|
|
@@ -2379,6 +2417,14 @@ class UIServer {
|
|
|
2379
2417
|
if (simpleModel) {
|
|
2380
2418
|
accountData.model = simpleModel;
|
|
2381
2419
|
}
|
|
2420
|
+
|
|
2421
|
+
// Add wire_api for Codex accounts
|
|
2422
|
+
if (accountType === 'Codex') {
|
|
2423
|
+
const wireApi = document.getElementById('wireApi').value;
|
|
2424
|
+
if (wireApi) {
|
|
2425
|
+
accountData.wireApi = wireApi;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2382
2428
|
} else if (accountType === 'CCR') {
|
|
2383
2429
|
// Collect CCR config
|
|
2384
2430
|
const providerName = document.getElementById('ccrProviderName').value.trim();
|
package/CHANGELOG-v1.7.0.md
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# Changelog v1.7.0
|
|
2
|
-
|
|
3
|
-
## 🎉 Major Features
|
|
4
|
-
|
|
5
|
-
### MCP (Model Context Protocol) Web UI Management
|
|
6
|
-
- **Full MCP server management through Web UI**
|
|
7
|
-
- Add, edit, delete MCP servers
|
|
8
|
-
- Support for stdio, sse, and http server types
|
|
9
|
-
- Test MCP server connections
|
|
10
|
-
- Enable/disable servers per project
|
|
11
|
-
- Sync configuration to Claude Code
|
|
12
|
-
- Search and filter functionality
|
|
13
|
-
- Complete internationalization (Chinese/English)
|
|
14
|
-
|
|
15
|
-
### Backend API (9 new endpoints)
|
|
16
|
-
- `GET /api/mcp-servers` - Get all MCP servers
|
|
17
|
-
- `POST /api/mcp-servers` - Add new MCP server
|
|
18
|
-
- `PUT /api/mcp-servers/:name` - Update MCP server
|
|
19
|
-
- `DELETE /api/mcp-servers/:name` - Delete MCP server
|
|
20
|
-
- `POST /api/mcp-servers/:name/test` - Test connection
|
|
21
|
-
- `POST /api/mcp-servers/:name/enable` - Enable for project
|
|
22
|
-
- `POST /api/mcp-servers/:name/disable` - Disable for project
|
|
23
|
-
- `GET /api/mcp-servers/enabled` - Get enabled servers
|
|
24
|
-
- `POST /api/mcp-servers/sync` - Sync configuration
|
|
25
|
-
|
|
26
|
-
### Frontend UI Enhancements
|
|
27
|
-
- **Tab Navigation System**
|
|
28
|
-
- Accounts management tab
|
|
29
|
-
- MCP servers management tab
|
|
30
|
-
- Smooth tab switching with event listeners
|
|
31
|
-
|
|
32
|
-
- **MCP Server Management Interface**
|
|
33
|
-
- Card-based layout matching existing design
|
|
34
|
-
- Dynamic forms based on server type
|
|
35
|
-
- Environment variables and headers support
|
|
36
|
-
- Status indicators (enabled/disabled)
|
|
37
|
-
- Real-time search and filtering
|
|
38
|
-
|
|
39
|
-
## 🐛 Bug Fixes
|
|
40
|
-
|
|
41
|
-
### Fixed: Account Data Not Showing
|
|
42
|
-
- **Issue**: After adding tabs, account data disappeared
|
|
43
|
-
- **Cause**: Incorrect HTML structure and indentation
|
|
44
|
-
- **Fix**: Corrected tab div structure and proper indentation
|
|
45
|
-
|
|
46
|
-
### Fixed: switchTab is not defined
|
|
47
|
-
- **Issue**: JavaScript error when clicking tabs
|
|
48
|
-
- **Cause**: Function defined after being used in onclick
|
|
49
|
-
- **Fix**: Replaced onclick attributes with event listeners using DOMContentLoaded
|
|
50
|
-
|
|
51
|
-
### Fixed: Incorrect Search Result Messages
|
|
52
|
-
- **Issue**: When search has no results, showed "Add your first account" message
|
|
53
|
-
- **Cause**: Did not distinguish between "no data" and "no search results"
|
|
54
|
-
- **Fix**: Smart detection of filtered state vs empty state
|
|
55
|
-
- No data: Shows "Add your first..." message
|
|
56
|
-
- No search results: Shows "No matching results found"
|
|
57
|
-
|
|
58
|
-
## ✨ Improvements
|
|
59
|
-
|
|
60
|
-
### User Experience
|
|
61
|
-
- Better error messages for search results
|
|
62
|
-
- Clearer distinction between empty state and filtered state
|
|
63
|
-
- Improved tab navigation with proper event handling
|
|
64
|
-
- Consistent UI design across all features
|
|
65
|
-
|
|
66
|
-
### Code Quality
|
|
67
|
-
- Modern JavaScript practices (event listeners vs onclick)
|
|
68
|
-
- Better separation of concerns
|
|
69
|
-
- Improved maintainability
|
|
70
|
-
- Comprehensive error handling
|
|
71
|
-
|
|
72
|
-
### Internationalization
|
|
73
|
-
- Complete Chinese translations for MCP features
|
|
74
|
-
- Complete English translations for MCP features
|
|
75
|
-
- Consistent translation keys
|
|
76
|
-
|
|
77
|
-
## 📊 Testing
|
|
78
|
-
|
|
79
|
-
### Automated Tests
|
|
80
|
-
- 32/32 tests passing
|
|
81
|
-
- Comprehensive coverage of all features
|
|
82
|
-
- Verification scripts included
|
|
83
|
-
|
|
84
|
-
### Manual Testing
|
|
85
|
-
- Test guides provided
|
|
86
|
-
- Step-by-step instructions
|
|
87
|
-
- Expected results documented
|
|
88
|
-
|
|
89
|
-
## 📁 New Files
|
|
90
|
-
|
|
91
|
-
### Documentation
|
|
92
|
-
- `MCP-UI-IMPLEMENTATION.md` - Complete implementation guide
|
|
93
|
-
- `FIXED-ISSUES.md` - Detailed problem fixes
|
|
94
|
-
- `SEARCH-FIX.md` - Search result fix documentation
|
|
95
|
-
- `QUICK-TEST.md` - Quick testing guide
|
|
96
|
-
- `TEST-NOW.md` - Immediate testing instructions
|
|
97
|
-
- `CHANGELOG-v1.7.0.md` - This file
|
|
98
|
-
|
|
99
|
-
### Testing
|
|
100
|
-
- `verify-implementation.js` - Automated verification script
|
|
101
|
-
- `test-ui.html` - Manual testing guide
|
|
102
|
-
|
|
103
|
-
## 🔧 Technical Details
|
|
104
|
-
|
|
105
|
-
### Modified Files
|
|
106
|
-
- `src/ui-server.js` - Main implementation file
|
|
107
|
-
- Added 9 API handler methods
|
|
108
|
-
- Added MCP tab HTML structure
|
|
109
|
-
- Added MCP modal HTML
|
|
110
|
-
- Added MCP JavaScript functions
|
|
111
|
-
- Added MCP translations (zh/en)
|
|
112
|
-
- Added tab navigation CSS
|
|
113
|
-
- Fixed switchTab function placement
|
|
114
|
-
- Fixed search result messages
|
|
115
|
-
|
|
116
|
-
### Code Statistics
|
|
117
|
-
- Lines added: ~1500
|
|
118
|
-
- API endpoints: +9
|
|
119
|
-
- JavaScript functions: +15
|
|
120
|
-
- Translations: +40 keys
|
|
121
|
-
- Tests: 32 automated tests
|
|
122
|
-
|
|
123
|
-
## 🎯 Migration Guide
|
|
124
|
-
|
|
125
|
-
### For Users
|
|
126
|
-
No migration needed. All existing functionality remains unchanged.
|
|
127
|
-
|
|
128
|
-
### For Developers
|
|
129
|
-
If you've customized the UI:
|
|
130
|
-
1. Tab navigation now uses event listeners instead of onclick
|
|
131
|
-
2. Search results have improved empty state handling
|
|
132
|
-
3. New MCP management APIs available
|
|
133
|
-
|
|
134
|
-
## 🚀 Upgrade Instructions
|
|
135
|
-
|
|
136
|
-
### From v1.6.x to v1.7.0
|
|
137
|
-
|
|
138
|
-
1. **Update package**:
|
|
139
|
-
```bash
|
|
140
|
-
npm install -g ai-account-switch@1.7.0
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
2. **Verify installation**:
|
|
144
|
-
```bash
|
|
145
|
-
ais --version
|
|
146
|
-
# Should show: 1.7.0
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
3. **Test new features**:
|
|
150
|
-
```bash
|
|
151
|
-
ais ui
|
|
152
|
-
# Navigate to MCP Servers tab
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
## 📝 Breaking Changes
|
|
156
|
-
|
|
157
|
-
None. This is a backward-compatible release.
|
|
158
|
-
|
|
159
|
-
## 🙏 Acknowledgments
|
|
160
|
-
|
|
161
|
-
- Comprehensive testing and verification
|
|
162
|
-
- User feedback incorporated
|
|
163
|
-
- Production-ready code quality
|
|
164
|
-
|
|
165
|
-
## 📚 Resources
|
|
166
|
-
|
|
167
|
-
- [README.md](README.md) - Main documentation
|
|
168
|
-
- [MCP-UI-IMPLEMENTATION.md](MCP-UI-IMPLEMENTATION.md) - Implementation details
|
|
169
|
-
- [QUICK-TEST.md](QUICK-TEST.md) - Testing guide
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
**Release Date**: 2025-11-12
|
|
174
|
-
**Version**: 1.7.0
|
|
175
|
-
**Status**: Stable
|
|
176
|
-
**Tests**: 32/32 passing
|