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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-account-switch",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "description": "A cross-platform CLI tool to manage and switch Claude/Codex/Droids account configurations",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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: use "chat" for most APIs (OpenAI-compatible)
591
- profileConfig += `wire_api = "chat"\n`;
592
-
593
- // Add authentication header
594
- profileConfig += `http_headers = { "Authorization" = "Bearer ${account.apiKey}" }\n`;
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();
@@ -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