ai-account-switch 1.5.5 → 1.5.7
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 +129 -2
- package/README_ZH.md +129 -2
- package/package.json +5 -18
- package/src/commands.js +140 -32
- package/src/config.js +44 -7
- package/src/index.js +1 -1
- package/src/ui-server.js +188 -46
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README_ZH.md)
|
|
4
4
|
|
|
5
|
-
A cross-platform CLI tool to manage and switch between Claude/Codex account configurations for different projects.
|
|
5
|
+
A cross-platform CLI tool to manage and switch between Claude/Codex/Droids account configurations for different projects.
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ A cross-platform CLI tool to manage and switch between Claude/Codex account conf
|
|
|
13
13
|
- **Claude Code Integration**: Automatically generates `.claude/settings.local.json` for Claude Code CLI
|
|
14
14
|
- **Secure Storage**: Account credentials stored locally in your home directory
|
|
15
15
|
- **Interactive CLI**: Easy-to-use interactive prompts for all operations
|
|
16
|
-
- **Account Types**: Support for Claude, Codex, and other AI services
|
|
16
|
+
- **Account Types**: Support for Claude, Codex, Droids, and other AI services
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -329,6 +329,108 @@ grep -A 10 "$(cat .codex-profile)" ~/.codex/config.toml
|
|
|
329
329
|
ais doctor
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
+
### Droids Integration
|
|
333
|
+
|
|
334
|
+
When you add a **Droids** type account and run `ais use`, the tool automatically creates a configuration file at `.droids/config.json` in your project directory.
|
|
335
|
+
|
|
336
|
+
#### Adding a Droids Account
|
|
337
|
+
|
|
338
|
+
When adding a Droids account, you'll see helpful configuration tips:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
ais add my-droids-account
|
|
342
|
+
|
|
343
|
+
? Select account type: Droids
|
|
344
|
+
|
|
345
|
+
📝 Droids Configuration Tips:
|
|
346
|
+
• Droids configuration will be stored in .droids/config.json
|
|
347
|
+
• API URL is optional (defaults to Droids default endpoint)
|
|
348
|
+
• You can configure custom models and settings
|
|
349
|
+
|
|
350
|
+
? Enter API Key: sk-xxx...
|
|
351
|
+
? Enter API URL (optional): https://api.example.com
|
|
352
|
+
? Do you want to specify a model? (Optional) Yes
|
|
353
|
+
? Enter model name: droids-model-v1
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### Using Droids with Your Project
|
|
357
|
+
|
|
358
|
+
After running `ais use` with a Droids account:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
cd ~/my-project
|
|
362
|
+
ais use my-droids-account
|
|
363
|
+
|
|
364
|
+
# Output:
|
|
365
|
+
# ✓ Switched to account 'my-droids-account' for current project.
|
|
366
|
+
# ✓ Droids configuration generated at: .droids/config.json
|
|
367
|
+
#
|
|
368
|
+
# 📖 Next Steps:
|
|
369
|
+
# Start interactive session: droid
|
|
370
|
+
# This will enter project-level interactive mode
|
|
371
|
+
# Droids will automatically use the configuration from .droids/config.json
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
The tool creates:
|
|
375
|
+
- **Project Configuration**: `.droids/config.json` with your account settings
|
|
376
|
+
|
|
377
|
+
#### Running Droids
|
|
378
|
+
|
|
379
|
+
Start Droids interactive session:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
# In your project directory
|
|
383
|
+
droid
|
|
384
|
+
|
|
385
|
+
# Droids will automatically load configuration from .droids/config.json
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Droids Configuration Structure
|
|
389
|
+
|
|
390
|
+
The generated configuration in `.droids/config.json`:
|
|
391
|
+
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"apiKey": "your-api-key",
|
|
395
|
+
"baseUrl": "https://api.example.com",
|
|
396
|
+
"model": "droids-model-v1",
|
|
397
|
+
"customSettings": {
|
|
398
|
+
"CUSTOM_VAR": "value"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### Switching Between Projects
|
|
404
|
+
|
|
405
|
+
Each project can use a different Droids account:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# Project A
|
|
409
|
+
cd ~/project-a
|
|
410
|
+
ais use droids-account-1
|
|
411
|
+
droid
|
|
412
|
+
|
|
413
|
+
# Project B
|
|
414
|
+
cd ~/project-b
|
|
415
|
+
ais use droids-account-2
|
|
416
|
+
droid
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### Troubleshooting Droids
|
|
420
|
+
|
|
421
|
+
**Check Droids Configuration**
|
|
422
|
+
```bash
|
|
423
|
+
# View your Droids configuration
|
|
424
|
+
cat .droids/config.json
|
|
425
|
+
|
|
426
|
+
# Or use the doctor command
|
|
427
|
+
ais doctor
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**Droids CLI not found**
|
|
431
|
+
- Make sure Droids CLI is installed and available in your PATH
|
|
432
|
+
- Run `droid --version` to verify installation
|
|
433
|
+
|
|
332
434
|
#### Custom Environment Variables
|
|
333
435
|
|
|
334
436
|
You can add custom environment variables when creating an account. When prompted, enter them in `KEY=VALUE` format:
|
|
@@ -370,6 +472,9 @@ ais add work-claude
|
|
|
370
472
|
# Add a Codex account
|
|
371
473
|
ais add codex-dev
|
|
372
474
|
|
|
475
|
+
# Add a Droids account
|
|
476
|
+
ais add droids-dev
|
|
477
|
+
|
|
373
478
|
# List all accounts
|
|
374
479
|
ais list
|
|
375
480
|
```
|
|
@@ -517,6 +622,28 @@ MIT License - feel free to use this tool in your projects!
|
|
|
517
622
|
|
|
518
623
|
## Changelog
|
|
519
624
|
|
|
625
|
+
### v1.5.7
|
|
626
|
+
- **Droids Integration**:
|
|
627
|
+
- Added full support for Droids AI assistant
|
|
628
|
+
- Automatic generation of `.droids/config.json` configuration
|
|
629
|
+
- Simple model configuration for Droids accounts
|
|
630
|
+
- Interactive session command: `droid`
|
|
631
|
+
- Enhanced `ais doctor` command with Droids configuration detection
|
|
632
|
+
- **UI Enhancements**:
|
|
633
|
+
- Added type filter dropdown for quick account filtering
|
|
634
|
+
- Color-coded account cards by type (Claude: blue, Codex: purple, Droids: green, Other: orange)
|
|
635
|
+
- Left border color indicators on account cards
|
|
636
|
+
- Improved visual hierarchy and user experience
|
|
637
|
+
- **Model Configuration Improvements**:
|
|
638
|
+
- Separated model configuration for different account types
|
|
639
|
+
- Claude: Complex model groups with multiple model settings
|
|
640
|
+
- Codex/Droids: Simple model field for straightforward configuration
|
|
641
|
+
- All model settings moved to collapsible "Advanced Configuration" section
|
|
642
|
+
- **Better User Guidance**:
|
|
643
|
+
- Enhanced `ais use` command with clear next-step instructions
|
|
644
|
+
- Type-specific usage examples for each AI assistant
|
|
645
|
+
- Interactive mode prompts instead of one-time command examples
|
|
646
|
+
|
|
520
647
|
### v1.5.1
|
|
521
648
|
- **Codex Integration Enhancements**:
|
|
522
649
|
- Added full support for Codex CLI with profile-based configuration
|
package/README_ZH.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
一个跨平台的命令行工具,用于管理和切换不同项目的 Claude/Codex 账户配置。
|
|
5
|
+
一个跨平台的命令行工具,用于管理和切换不同项目的 Claude/Codex/Droids 账户配置。
|
|
6
6
|
|
|
7
7
|
## 特性
|
|
8
8
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- 🎯 主题自动跟随系统设置
|
|
22
22
|
- **安全存储**:账户凭证仅存储在本地
|
|
23
23
|
- **交互式命令行**:所有操作都有易用的交互式提示
|
|
24
|
-
- **多种账户类型**:支持 Claude、Codex 和其他 AI 服务
|
|
24
|
+
- **多种账户类型**:支持 Claude、Codex、Droids 和其他 AI 服务
|
|
25
25
|
|
|
26
26
|
## 安装
|
|
27
27
|
|
|
@@ -384,6 +384,108 @@ grep -A 10 "$(cat .codex-profile)" ~/.codex/config.toml
|
|
|
384
384
|
ais doctor
|
|
385
385
|
```
|
|
386
386
|
|
|
387
|
+
### Droids 集成
|
|
388
|
+
|
|
389
|
+
当你添加 **Droids** 类型账户并运行 `ais use` 时,工具会自动在项目目录中创建 `.droids/config.json` 配置文件。
|
|
390
|
+
|
|
391
|
+
#### 添加 Droids 账户
|
|
392
|
+
|
|
393
|
+
添加 Droids 账户时,你会看到有用的配置提示:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
ais add my-droids-account
|
|
397
|
+
|
|
398
|
+
? Select account type: Droids
|
|
399
|
+
|
|
400
|
+
📝 Droids Configuration Tips:
|
|
401
|
+
• Droids configuration will be stored in .droids/config.json
|
|
402
|
+
• API URL is optional (defaults to Droids default endpoint)
|
|
403
|
+
• You can configure custom models and settings
|
|
404
|
+
|
|
405
|
+
? Enter API Key: sk-xxx...
|
|
406
|
+
? Enter API URL (optional): https://api.example.com
|
|
407
|
+
? Do you want to specify a model? (Optional) Yes
|
|
408
|
+
? Enter model name: droids-model-v1
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### 在项目中使用 Droids
|
|
412
|
+
|
|
413
|
+
使用 Droids 账户运行 `ais use` 后:
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
cd ~/my-project
|
|
417
|
+
ais use my-droids-account
|
|
418
|
+
|
|
419
|
+
# 输出:
|
|
420
|
+
# ✓ Switched to account 'my-droids-account' for current project.
|
|
421
|
+
# ✓ Droids configuration generated at: .droids/config.json
|
|
422
|
+
#
|
|
423
|
+
# 📖 Next Steps:
|
|
424
|
+
# Start interactive session: droid
|
|
425
|
+
# This will enter project-level interactive mode
|
|
426
|
+
# Droids will automatically use the configuration from .droids/config.json
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
工具会创建:
|
|
430
|
+
- **项目配置**:`.droids/config.json` 包含你的账户设置
|
|
431
|
+
|
|
432
|
+
#### 运行 Droids
|
|
433
|
+
|
|
434
|
+
启动 Droids 交互会话:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
# 在项目目录中
|
|
438
|
+
droid
|
|
439
|
+
|
|
440
|
+
# Droids 会自动从 .droids/config.json 加载配置
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
#### Droids 配置结构
|
|
444
|
+
|
|
445
|
+
在 `.droids/config.json` 中生成的配置:
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
"apiKey": "your-api-key",
|
|
450
|
+
"baseUrl": "https://api.example.com",
|
|
451
|
+
"model": "droids-model-v1",
|
|
452
|
+
"customSettings": {
|
|
453
|
+
"CUSTOM_VAR": "value"
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
#### 在不同项目间切换
|
|
459
|
+
|
|
460
|
+
每个项目可以使用不同的 Droids 账户:
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
# 项目 A
|
|
464
|
+
cd ~/project-a
|
|
465
|
+
ais use droids-account-1
|
|
466
|
+
droid
|
|
467
|
+
|
|
468
|
+
# 项目 B
|
|
469
|
+
cd ~/project-b
|
|
470
|
+
ais use droids-account-2
|
|
471
|
+
droid
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
#### Droids 故障排除
|
|
475
|
+
|
|
476
|
+
**检查 Droids 配置**
|
|
477
|
+
```bash
|
|
478
|
+
# 查看你的 Droids 配置
|
|
479
|
+
cat .droids/config.json
|
|
480
|
+
|
|
481
|
+
# 或使用 doctor 命令
|
|
482
|
+
ais doctor
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Droids CLI 未找到**
|
|
486
|
+
- 确保 Droids CLI 已安装并在 PATH 中可用
|
|
487
|
+
- 运行 `droid --version` 验证安装
|
|
488
|
+
|
|
387
489
|
#### 自定义环境变量
|
|
388
490
|
|
|
389
491
|
在创建账户时可以添加自定义环境变量。在提示时,使用 `KEY=VALUE` 格式输入:
|
|
@@ -425,6 +527,9 @@ ais add work-claude
|
|
|
425
527
|
# 添加 Codex 账户
|
|
426
528
|
ais add codex-dev
|
|
427
529
|
|
|
530
|
+
# 添加 Droids 账户
|
|
531
|
+
ais add droids-dev
|
|
532
|
+
|
|
428
533
|
# 列出所有账户
|
|
429
534
|
ais list
|
|
430
535
|
```
|
|
@@ -594,6 +699,28 @@ MIT License - 欢迎在你的项目中使用此工具!
|
|
|
594
699
|
|
|
595
700
|
## 更新日志
|
|
596
701
|
|
|
702
|
+
### v1.5.7
|
|
703
|
+
- **Droids 集成**:
|
|
704
|
+
- 完整支持 Droids AI 助手
|
|
705
|
+
- 自动生成 `.droids/config.json` 配置文件
|
|
706
|
+
- Droids 账户的简单模型配置
|
|
707
|
+
- 交互会话命令:`droid`
|
|
708
|
+
- 增强 `ais doctor` 命令,支持 Droids 配置检测
|
|
709
|
+
- **UI 增强**:
|
|
710
|
+
- 添加类型筛选下拉框,快速过滤账户
|
|
711
|
+
- 按类型为账户卡片着色(Claude: 蓝色,Codex: 紫色,Droids: 绿色,Other: 橙色)
|
|
712
|
+
- 账户卡片左侧边框颜色指示器
|
|
713
|
+
- 改进视觉层次和用户体验
|
|
714
|
+
- **模型配置改进**:
|
|
715
|
+
- 为不同账户类型分离模型配置
|
|
716
|
+
- Claude: 复杂的模型组,支持多个模型设置
|
|
717
|
+
- Codex/Droids: 简单的模型字段,配置更直观
|
|
718
|
+
- 所有模型设置移至可折叠的"高级配置"区域
|
|
719
|
+
- **更好的用户指引**:
|
|
720
|
+
- 增强 `ais use` 命令,提供清晰的下一步操作说明
|
|
721
|
+
- 为每个 AI 助手提供特定类型的使用示例
|
|
722
|
+
- 交互模式提示而非一次性命令示例
|
|
723
|
+
|
|
597
724
|
### v1.5.1
|
|
598
725
|
- **Codex 集成增强**:
|
|
599
726
|
- 完整支持 Codex CLI 的 profile 配置
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-account-switch",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "A cross-platform CLI tool to manage and switch Claude/Codex account configurations",
|
|
3
|
+
"version": "1.5.7",
|
|
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": {
|
|
7
7
|
"ais": "./bin/ais.js"
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
"cli",
|
|
15
15
|
"claude",
|
|
16
16
|
"codex",
|
|
17
|
+
"droids",
|
|
17
18
|
"account",
|
|
18
19
|
"switch",
|
|
19
20
|
"cross-platform",
|
|
20
21
|
"claude-code",
|
|
21
|
-
"ai"
|
|
22
|
+
"ai",
|
|
23
|
+
"ai-assistant"
|
|
22
24
|
],
|
|
23
25
|
"author": "deanwanghewei@gmail.comnpm view ai-account-switch",
|
|
24
26
|
"license": "MIT",
|
|
@@ -30,24 +32,9 @@
|
|
|
30
32
|
"chalk": "^4.1.2",
|
|
31
33
|
"inquirer": "^8.2.5"
|
|
32
34
|
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"pkg": "^5.8.1"
|
|
35
|
-
},
|
|
36
35
|
"engines": {
|
|
37
36
|
"node": ">=14.0.0"
|
|
38
37
|
},
|
|
39
|
-
"pkg": {
|
|
40
|
-
"scripts": [
|
|
41
|
-
"src/**/*.js"
|
|
42
|
-
],
|
|
43
|
-
"assets": [],
|
|
44
|
-
"targets": [
|
|
45
|
-
"node18-linux-x64",
|
|
46
|
-
"node18-macos-x64",
|
|
47
|
-
"node18-win-x64"
|
|
48
|
-
],
|
|
49
|
-
"outputPath": "dist"
|
|
50
|
-
},
|
|
51
38
|
"repository": {
|
|
52
39
|
"type": "git",
|
|
53
40
|
"url": "https://github.com/yourusername/ai-agent-user-swith.git"
|
package/src/commands.js
CHANGED
|
@@ -44,7 +44,7 @@ async function addAccount(name, options) {
|
|
|
44
44
|
type: 'list',
|
|
45
45
|
name: 'type',
|
|
46
46
|
message: 'Select account type:',
|
|
47
|
-
choices: ['Claude', 'Codex', 'Other'],
|
|
47
|
+
choices: ['Claude', 'Codex', 'Droids', 'Other'],
|
|
48
48
|
default: 'Claude'
|
|
49
49
|
}
|
|
50
50
|
]);
|
|
@@ -55,6 +55,11 @@ async function addAccount(name, options) {
|
|
|
55
55
|
console.log(chalk.gray(' • API URL should include the full path (e.g., https://api.example.com/v1)'));
|
|
56
56
|
console.log(chalk.gray(' • AIS will automatically add /v1 if missing'));
|
|
57
57
|
console.log(chalk.gray(' • Codex uses OpenAI-compatible API format\n'));
|
|
58
|
+
} else if (typeAnswer.type === 'Droids') {
|
|
59
|
+
console.log(chalk.cyan('\n📝 Droids Configuration Tips:'));
|
|
60
|
+
console.log(chalk.gray(' • Droids configuration will be stored in .droids/config.json'));
|
|
61
|
+
console.log(chalk.gray(' • API URL is optional (defaults to Droids default endpoint)'));
|
|
62
|
+
console.log(chalk.gray(' • You can configure custom models and settings\n'));
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Prompt for remaining account details
|
|
@@ -183,28 +188,57 @@ async function addAccount(name, options) {
|
|
|
183
188
|
// Remove the addCustomEnv flag before saving
|
|
184
189
|
delete accountData.addCustomEnv;
|
|
185
190
|
|
|
186
|
-
//
|
|
187
|
-
accountData.
|
|
188
|
-
|
|
191
|
+
// Handle model configuration based on account type
|
|
192
|
+
if (accountData.type === 'Claude') {
|
|
193
|
+
// Claude uses complex model groups
|
|
194
|
+
accountData.modelGroups = {};
|
|
195
|
+
accountData.activeModelGroup = null;
|
|
189
196
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
// Prompt for model group configuration
|
|
198
|
+
const { createModelGroup } = await inquirer.prompt([
|
|
199
|
+
{
|
|
200
|
+
type: 'confirm',
|
|
201
|
+
name: 'createModelGroup',
|
|
202
|
+
message: 'Do you want to create a model group? (Recommended)',
|
|
203
|
+
default: true
|
|
204
|
+
}
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
if (createModelGroup) {
|
|
208
|
+
const groupName = 'default';
|
|
209
|
+
const modelGroupConfig = await promptForModelGroup();
|
|
210
|
+
|
|
211
|
+
if (Object.keys(modelGroupConfig).length > 0) {
|
|
212
|
+
accountData.modelGroups[groupName] = modelGroupConfig;
|
|
213
|
+
accountData.activeModelGroup = groupName;
|
|
214
|
+
console.log(chalk.green(`\n✓ Created model group '${groupName}'`));
|
|
215
|
+
}
|
|
197
216
|
}
|
|
198
|
-
|
|
217
|
+
} else if (accountData.type === 'Codex' || accountData.type === 'Droids') {
|
|
218
|
+
// Codex and Droids use simple model field
|
|
219
|
+
const { addModel } = await inquirer.prompt([
|
|
220
|
+
{
|
|
221
|
+
type: 'confirm',
|
|
222
|
+
name: 'addModel',
|
|
223
|
+
message: 'Do you want to specify a model? (Optional)',
|
|
224
|
+
default: false
|
|
225
|
+
}
|
|
226
|
+
]);
|
|
199
227
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
228
|
+
if (addModel) {
|
|
229
|
+
const { model } = await inquirer.prompt([
|
|
230
|
+
{
|
|
231
|
+
type: 'input',
|
|
232
|
+
name: 'model',
|
|
233
|
+
message: 'Enter model name:',
|
|
234
|
+
default: ''
|
|
235
|
+
}
|
|
236
|
+
]);
|
|
203
237
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
238
|
+
if (model.trim()) {
|
|
239
|
+
accountData.model = model.trim();
|
|
240
|
+
console.log(chalk.green(`\n✓ Model set to: ${accountData.model}`));
|
|
241
|
+
}
|
|
208
242
|
}
|
|
209
243
|
}
|
|
210
244
|
|
|
@@ -212,10 +246,13 @@ async function addAccount(name, options) {
|
|
|
212
246
|
config.addAccount(name, accountData);
|
|
213
247
|
console.log(chalk.green(`✓ Account '${name}' added successfully!`));
|
|
214
248
|
|
|
215
|
-
|
|
249
|
+
// Show model configuration tips based on account type
|
|
250
|
+
if (accountData.type === 'Claude' && accountData.activeModelGroup) {
|
|
216
251
|
console.log(chalk.cyan(`✓ Active model group: ${accountData.activeModelGroup}\n`));
|
|
217
252
|
console.log(chalk.gray('💡 Tip: Use "ais model add" to create more model groups'));
|
|
218
253
|
console.log(chalk.gray('💡 Tip: Use "ais model list" to view all model groups\n'));
|
|
254
|
+
} else if ((accountData.type === 'Codex' || accountData.type === 'Droids') && accountData.model) {
|
|
255
|
+
console.log(chalk.cyan(`✓ Model: ${accountData.model}\n`));
|
|
219
256
|
}
|
|
220
257
|
|
|
221
258
|
// Show usage instructions based on account type
|
|
@@ -232,6 +269,12 @@ async function addAccount(name, options) {
|
|
|
232
269
|
console.log(chalk.cyan(` ais use ${name}\n`));
|
|
233
270
|
console.log(chalk.gray('2. Start Claude Code in your project directory'));
|
|
234
271
|
console.log(chalk.gray('3. Claude Code will automatically use the project configuration\n'));
|
|
272
|
+
} else if (accountData.type === 'Droids') {
|
|
273
|
+
console.log(chalk.bold.cyan('\n📖 Droids Usage Instructions:\n'));
|
|
274
|
+
console.log(chalk.gray('1. Switch to this account in your project:'));
|
|
275
|
+
console.log(chalk.cyan(` ais use ${name}\n`));
|
|
276
|
+
console.log(chalk.gray('2. Start Droids in your project directory'));
|
|
277
|
+
console.log(chalk.gray('3. Droids will automatically use the configuration from .droids/config.json\n'));
|
|
235
278
|
}
|
|
236
279
|
}
|
|
237
280
|
|
|
@@ -344,11 +387,13 @@ function listAccounts() {
|
|
|
344
387
|
if (account.customEnv && Object.keys(account.customEnv).length > 0) {
|
|
345
388
|
console.log(` Custom Env: ${Object.keys(account.customEnv).join(', ')}`);
|
|
346
389
|
}
|
|
347
|
-
// Display model
|
|
348
|
-
if (account.modelGroups && Object.keys(account.modelGroups).length > 0) {
|
|
390
|
+
// Display model configuration based on account type
|
|
391
|
+
if (account.type === 'Claude' && account.modelGroups && Object.keys(account.modelGroups).length > 0) {
|
|
349
392
|
const groupNames = Object.keys(account.modelGroups);
|
|
350
393
|
const activeMarker = account.activeModelGroup ? ` (active: ${account.activeModelGroup})` : '';
|
|
351
394
|
console.log(` Model Groups: ${groupNames.join(', ')}${activeMarker}`);
|
|
395
|
+
} else if ((account.type === 'Codex' || account.type === 'Droids') && account.model) {
|
|
396
|
+
console.log(` Model: ${account.model}`);
|
|
352
397
|
}
|
|
353
398
|
console.log(` Created: ${new Date(account.createdAt).toLocaleString()}`);
|
|
354
399
|
console.log('');
|
|
@@ -408,16 +453,32 @@ async function useAccount(name) {
|
|
|
408
453
|
if (fs.existsSync(profileFile)) {
|
|
409
454
|
const profileName = fs.readFileSync(profileFile, 'utf8').trim();
|
|
410
455
|
console.log(chalk.cyan(`✓ Codex profile created: ${profileName}`));
|
|
411
|
-
console.log(
|
|
456
|
+
console.log('');
|
|
457
|
+
console.log(chalk.bold.cyan('📖 Next Steps:'));
|
|
458
|
+
console.log(chalk.yellow(` Start interactive session: ${chalk.bold(`codex --profile ${profileName}`)}`));
|
|
459
|
+
console.log(chalk.gray(' This will enter project-level interactive mode'));
|
|
412
460
|
}
|
|
461
|
+
} else if (account && account.type === 'Droids') {
|
|
462
|
+
console.log(chalk.cyan(`✓ Droids configuration generated at: .droids/config.json`));
|
|
463
|
+
console.log('');
|
|
464
|
+
console.log(chalk.bold.cyan('📖 Next Steps:'));
|
|
465
|
+
console.log(chalk.yellow(` Start interactive session: ${chalk.bold('droid')}`));
|
|
466
|
+
console.log(chalk.gray(' This will enter project-level interactive mode'));
|
|
467
|
+
console.log(chalk.gray(' Droids will automatically use the configuration from .droids/config.json'));
|
|
413
468
|
} else {
|
|
414
469
|
console.log(chalk.cyan(`✓ Claude configuration generated at: .claude/settings.local.json`));
|
|
470
|
+
console.log('');
|
|
471
|
+
console.log(chalk.bold.cyan('📖 Next Steps:'));
|
|
472
|
+
console.log(chalk.yellow(` Start interactive session: ${chalk.bold('claude')}`));
|
|
473
|
+
console.log(chalk.gray(' This will enter project-level interactive mode'));
|
|
474
|
+
console.log(chalk.gray(' Claude Code will automatically use the project configuration'));
|
|
415
475
|
}
|
|
416
476
|
|
|
417
477
|
// Check if .gitignore was updated
|
|
418
478
|
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
419
479
|
const gitDir = path.join(process.cwd(), '.git');
|
|
420
480
|
if (fs.existsSync(gitDir) && fs.existsSync(gitignorePath)) {
|
|
481
|
+
console.log('');
|
|
421
482
|
console.log(chalk.cyan(`✓ Updated .gitignore to exclude AIS configuration files`));
|
|
422
483
|
}
|
|
423
484
|
} else {
|
|
@@ -451,8 +512,8 @@ function showInfo() {
|
|
|
451
512
|
console.log(` ${chalk.gray('•')} ${key}: ${value}`);
|
|
452
513
|
});
|
|
453
514
|
}
|
|
454
|
-
// Display model
|
|
455
|
-
if (projectAccount.modelGroups && Object.keys(projectAccount.modelGroups).length > 0) {
|
|
515
|
+
// Display model configuration based on account type
|
|
516
|
+
if (projectAccount.type === 'Claude' && projectAccount.modelGroups && Object.keys(projectAccount.modelGroups).length > 0) {
|
|
456
517
|
console.log(`${chalk.cyan('Model Groups:')}`);
|
|
457
518
|
Object.keys(projectAccount.modelGroups).forEach(groupName => {
|
|
458
519
|
const isActive = projectAccount.activeModelGroup === groupName;
|
|
@@ -462,6 +523,8 @@ function showInfo() {
|
|
|
462
523
|
if (projectAccount.activeModelGroup) {
|
|
463
524
|
console.log(`${chalk.cyan('Active Model Group:')} ${chalk.green.bold(projectAccount.activeModelGroup)}`);
|
|
464
525
|
}
|
|
526
|
+
} else if ((projectAccount.type === 'Codex' || projectAccount.type === 'Droids') && projectAccount.model) {
|
|
527
|
+
console.log(`${chalk.cyan('Model:')} ${projectAccount.model}`);
|
|
465
528
|
}
|
|
466
529
|
console.log(`${chalk.cyan('Set At:')} ${new Date(projectAccount.setAt).toLocaleString()}`);
|
|
467
530
|
console.log(`${chalk.cyan('Project Root:')} ${projectAccount.projectRoot}`);
|
|
@@ -697,11 +760,47 @@ async function doctor() {
|
|
|
697
760
|
console.log(chalk.yellow(' Run "ais use <account>" to generate it'));
|
|
698
761
|
}
|
|
699
762
|
|
|
763
|
+
// Check Droids config
|
|
764
|
+
const droidsDir = path.join(projectRoot, '.droids');
|
|
765
|
+
const droidsConfigPath = path.join(droidsDir, 'config.json');
|
|
766
|
+
|
|
767
|
+
console.log(chalk.bold('\n5. Droids Configuration:'));
|
|
768
|
+
console.log(` Expected location: ${droidsConfigPath}`);
|
|
769
|
+
|
|
770
|
+
if (fs.existsSync(droidsConfigPath)) {
|
|
771
|
+
console.log(chalk.green(' ✓ Droids config exists'));
|
|
772
|
+
try {
|
|
773
|
+
const droidsConfig = JSON.parse(fs.readFileSync(droidsConfigPath, 'utf8'));
|
|
774
|
+
|
|
775
|
+
if (droidsConfig.apiKey) {
|
|
776
|
+
const masked = droidsConfig.apiKey.substring(0, 6) + '****' + droidsConfig.apiKey.substring(droidsConfig.apiKey.length - 4);
|
|
777
|
+
console.log(` API Key: ${masked}`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (droidsConfig.baseUrl) {
|
|
781
|
+
console.log(` Base URL: ${droidsConfig.baseUrl}`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (droidsConfig.model) {
|
|
785
|
+
console.log(` Model: ${droidsConfig.model}`);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (droidsConfig.customSettings) {
|
|
789
|
+
console.log(` Custom Settings: ${Object.keys(droidsConfig.customSettings).join(', ')}`);
|
|
790
|
+
}
|
|
791
|
+
} catch (e) {
|
|
792
|
+
console.log(chalk.red(` ✗ Error reading Droids config: ${e.message}`));
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
console.log(chalk.yellow(' ⚠ Droids config not found'));
|
|
796
|
+
console.log(chalk.yellow(' Run "ais use <droids-account>" to generate it'));
|
|
797
|
+
}
|
|
798
|
+
|
|
700
799
|
// Check Codex profile
|
|
701
800
|
const codexProfilePath = path.join(projectRoot, '.codex-profile');
|
|
702
801
|
const globalCodexConfig = path.join(os.homedir(), '.codex', 'config.toml');
|
|
703
802
|
|
|
704
|
-
console.log(chalk.bold('\
|
|
803
|
+
console.log(chalk.bold('\n6. Codex Configuration:'));
|
|
705
804
|
console.log(` Profile file: ${codexProfilePath}`);
|
|
706
805
|
|
|
707
806
|
if (fs.existsSync(codexProfilePath)) {
|
|
@@ -750,7 +849,7 @@ async function doctor() {
|
|
|
750
849
|
|
|
751
850
|
// Check global Claude config
|
|
752
851
|
const globalClaudeConfig = path.join(os.homedir(), '.claude', 'settings.json');
|
|
753
|
-
console.log(chalk.bold('\
|
|
852
|
+
console.log(chalk.bold('\n7. Global Claude Configuration:'));
|
|
754
853
|
console.log(` Location: ${globalClaudeConfig}`);
|
|
755
854
|
|
|
756
855
|
if (fs.existsSync(globalClaudeConfig)) {
|
|
@@ -773,7 +872,7 @@ async function doctor() {
|
|
|
773
872
|
}
|
|
774
873
|
|
|
775
874
|
// Check current account availability
|
|
776
|
-
console.log(chalk.bold('\
|
|
875
|
+
console.log(chalk.bold('\n8. Current Account Availability:'));
|
|
777
876
|
const projectAccount = config.getProjectAccount();
|
|
778
877
|
|
|
779
878
|
if (projectAccount && projectAccount.apiKey) {
|
|
@@ -812,6 +911,15 @@ async function doctor() {
|
|
|
812
911
|
} catch (e) {
|
|
813
912
|
console.log(chalk.yellow(' ⚠ Codex CLI not found'));
|
|
814
913
|
}
|
|
914
|
+
} else if (projectAccount.type === 'Droids') {
|
|
915
|
+
console.log(' Testing with Droids CLI...');
|
|
916
|
+
const { execSync } = require('child_process');
|
|
917
|
+
try {
|
|
918
|
+
execSync('droid --version', { stdio: 'pipe', timeout: 5000 });
|
|
919
|
+
console.log(chalk.green(' ✓ Droids CLI is available'));
|
|
920
|
+
} catch (e) {
|
|
921
|
+
console.log(chalk.yellow(' ⚠ Droids CLI not found'));
|
|
922
|
+
}
|
|
815
923
|
}
|
|
816
924
|
|
|
817
925
|
console.log(` API URL: ${projectAccount.apiUrl || 'https://api.anthropic.com'}`);
|
|
@@ -840,7 +948,7 @@ async function doctor() {
|
|
|
840
948
|
}
|
|
841
949
|
|
|
842
950
|
// Recommendations
|
|
843
|
-
console.log(chalk.bold('\
|
|
951
|
+
console.log(chalk.bold('\n9. Recommendations:'));
|
|
844
952
|
|
|
845
953
|
if (projectRoot && process.cwd() !== projectRoot) {
|
|
846
954
|
console.log(chalk.yellow(` ⚠ You are in a subdirectory (${path.relative(projectRoot, process.cwd())})`));
|
|
@@ -855,9 +963,9 @@ async function doctor() {
|
|
|
855
963
|
console.log(chalk.gray(` • File: ${globalClaudeConfig}`));
|
|
856
964
|
}
|
|
857
965
|
|
|
858
|
-
console.log(chalk.bold('\
|
|
859
|
-
console.log(chalk.cyan(' • Start Claude Code from your project directory or subdirectory'));
|
|
860
|
-
console.log(chalk.cyan(' • Check which account
|
|
966
|
+
console.log(chalk.bold('\n10. Next Steps:'));
|
|
967
|
+
console.log(chalk.cyan(' • Start Claude Code/Codex/Droids from your project directory or subdirectory'));
|
|
968
|
+
console.log(chalk.cyan(' • Check which account is being used'));
|
|
861
969
|
console.log(chalk.cyan(' • If wrong account is used, run: ais use <correct-account>'));
|
|
862
970
|
console.log('');
|
|
863
971
|
}
|
package/src/config.js
CHANGED
|
@@ -146,6 +146,9 @@ class ConfigManager {
|
|
|
146
146
|
if (account.type === 'Codex') {
|
|
147
147
|
// Codex type accounts only need Codex configuration
|
|
148
148
|
this.generateCodexConfig(account, projectRoot);
|
|
149
|
+
} else if (account.type === 'Droids') {
|
|
150
|
+
// Droids type accounts only need Droids configuration
|
|
151
|
+
this.generateDroidsConfig(account, projectRoot);
|
|
149
152
|
} else {
|
|
150
153
|
// Claude and other types need Claude Code configuration
|
|
151
154
|
this.generateClaudeConfig(account, projectRoot);
|
|
@@ -173,7 +176,8 @@ class ConfigManager {
|
|
|
173
176
|
const filesToIgnore = [
|
|
174
177
|
this.projectConfigFilename,
|
|
175
178
|
'.claude/settings.local.json',
|
|
176
|
-
'.codex-profile'
|
|
179
|
+
'.codex-profile',
|
|
180
|
+
'.droids/config.json'
|
|
177
181
|
];
|
|
178
182
|
|
|
179
183
|
let gitignoreContent = '';
|
|
@@ -346,6 +350,42 @@ class ConfigManager {
|
|
|
346
350
|
fs.writeFileSync(claudeConfigFile, JSON.stringify(claudeConfig, null, 2), 'utf8');
|
|
347
351
|
}
|
|
348
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Generate Droids configuration in .droids/config.json
|
|
355
|
+
*/
|
|
356
|
+
generateDroidsConfig(account, projectRoot = process.cwd()) {
|
|
357
|
+
const droidsDir = path.join(projectRoot, '.droids');
|
|
358
|
+
const droidsConfigFile = path.join(droidsDir, 'config.json');
|
|
359
|
+
|
|
360
|
+
// Create .droids directory if it doesn't exist
|
|
361
|
+
if (!fs.existsSync(droidsDir)) {
|
|
362
|
+
fs.mkdirSync(droidsDir, { recursive: true });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Build Droids configuration
|
|
366
|
+
const droidsConfig = {
|
|
367
|
+
apiKey: account.apiKey
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// Add API URL if specified
|
|
371
|
+
if (account.apiUrl) {
|
|
372
|
+
droidsConfig.baseUrl = account.apiUrl;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Add model configuration - Droids uses simple model field
|
|
376
|
+
if (account.model) {
|
|
377
|
+
droidsConfig.model = account.model;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Add custom environment variables as customSettings
|
|
381
|
+
if (account.customEnv && typeof account.customEnv === 'object') {
|
|
382
|
+
droidsConfig.customSettings = account.customEnv;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Write Droids configuration
|
|
386
|
+
fs.writeFileSync(droidsConfigFile, JSON.stringify(droidsConfig, null, 2), 'utf8');
|
|
387
|
+
}
|
|
388
|
+
|
|
349
389
|
/**
|
|
350
390
|
* Generate Codex profile in global ~/.codex/config.toml
|
|
351
391
|
*/
|
|
@@ -385,12 +425,9 @@ class ConfigManager {
|
|
|
385
425
|
|
|
386
426
|
profileConfig += `model_provider = "${providerName}"\n`;
|
|
387
427
|
|
|
388
|
-
// Add model configuration
|
|
389
|
-
if (account.
|
|
390
|
-
|
|
391
|
-
if (activeGroup && activeGroup.DEFAULT_MODEL) {
|
|
392
|
-
profileConfig += `model = "${activeGroup.DEFAULT_MODEL}"\n`;
|
|
393
|
-
}
|
|
428
|
+
// Add model configuration - Codex uses simple model field
|
|
429
|
+
if (account.model) {
|
|
430
|
+
profileConfig += `model = "${account.model}"\n`;
|
|
394
431
|
}
|
|
395
432
|
|
|
396
433
|
// Ensure API URL has proper path
|
package/src/index.js
CHANGED
|
@@ -25,7 +25,7 @@ const packageJson = require('../package.json');
|
|
|
25
25
|
|
|
26
26
|
program
|
|
27
27
|
.name('ais')
|
|
28
|
-
.description('AI Account Switch - Manage and switch Claude/Codex account configurations')
|
|
28
|
+
.description('AI Account Switch - Manage and switch Claude/Codex/Droids account configurations')
|
|
29
29
|
.version(packageJson.version);
|
|
30
30
|
|
|
31
31
|
// Add account command
|
package/src/ui-server.js
CHANGED
|
@@ -482,6 +482,21 @@ class UIServer {
|
|
|
482
482
|
color: var(--text-primary);
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
.filter-box {
|
|
486
|
+
min-width: 150px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.filter-box select {
|
|
490
|
+
width: 100%;
|
|
491
|
+
padding: 10px;
|
|
492
|
+
border: 1px solid var(--input-border);
|
|
493
|
+
border-radius: 5px;
|
|
494
|
+
font-size: 14px;
|
|
495
|
+
background: var(--input-bg);
|
|
496
|
+
color: var(--text-primary);
|
|
497
|
+
cursor: pointer;
|
|
498
|
+
}
|
|
499
|
+
|
|
485
500
|
.accounts-grid {
|
|
486
501
|
display: grid;
|
|
487
502
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
|
@@ -497,6 +512,7 @@ class UIServer {
|
|
|
497
512
|
display: flex;
|
|
498
513
|
flex-direction: column;
|
|
499
514
|
min-height: 200px;
|
|
515
|
+
border-left: 4px solid transparent;
|
|
500
516
|
}
|
|
501
517
|
|
|
502
518
|
.account-card:hover {
|
|
@@ -504,6 +520,22 @@ class UIServer {
|
|
|
504
520
|
box-shadow: 0 6px 12px var(--card-shadow-hover);
|
|
505
521
|
}
|
|
506
522
|
|
|
523
|
+
.account-card.type-claude {
|
|
524
|
+
border-left-color: #1976d2;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.account-card.type-codex {
|
|
528
|
+
border-left-color: #7b1fa2;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.account-card.type-droids {
|
|
532
|
+
border-left-color: #388e3c;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.account-card.type-other {
|
|
536
|
+
border-left-color: #f57c00;
|
|
537
|
+
}
|
|
538
|
+
|
|
507
539
|
.account-content {
|
|
508
540
|
flex: 1;
|
|
509
541
|
}
|
|
@@ -527,10 +559,28 @@ class UIServer {
|
|
|
527
559
|
border-radius: 4px;
|
|
528
560
|
font-size: 12px;
|
|
529
561
|
font-weight: 500;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.account-type.type-claude {
|
|
530
565
|
background: #e3f2fd;
|
|
531
566
|
color: #1976d2;
|
|
532
567
|
}
|
|
533
568
|
|
|
569
|
+
.account-type.type-codex {
|
|
570
|
+
background: #f3e5f5;
|
|
571
|
+
color: #7b1fa2;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.account-type.type-droids {
|
|
575
|
+
background: #e8f5e9;
|
|
576
|
+
color: #388e3c;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.account-type.type-other {
|
|
580
|
+
background: #fff3e0;
|
|
581
|
+
color: #f57c00;
|
|
582
|
+
}
|
|
583
|
+
|
|
534
584
|
.account-info {
|
|
535
585
|
margin-bottom: 10px;
|
|
536
586
|
}
|
|
@@ -807,6 +857,15 @@ class UIServer {
|
|
|
807
857
|
<div class="search-box">
|
|
808
858
|
<input type="text" id="searchInput" data-i18n-placeholder="searchPlaceholder" placeholder="搜索账号...">
|
|
809
859
|
</div>
|
|
860
|
+
<div class="filter-box">
|
|
861
|
+
<select id="typeFilter" onchange="renderAccounts()">
|
|
862
|
+
<option value="" data-i18n="allTypes">所有类型</option>
|
|
863
|
+
<option value="Claude">Claude</option>
|
|
864
|
+
<option value="Codex">Codex</option>
|
|
865
|
+
<option value="Droids">Droids</option>
|
|
866
|
+
<option value="Other" data-i18n="other">其他</option>
|
|
867
|
+
</select>
|
|
868
|
+
</div>
|
|
810
869
|
<button class="btn btn-primary" onclick="showAddModal()" data-i18n="addAccount">+ 添加账号</button>
|
|
811
870
|
<button class="btn btn-secondary" onclick="exportAccounts()" data-i18n="exportAll">导出全部</button>
|
|
812
871
|
<button class="btn btn-secondary" onclick="document.getElementById('importFile').click()" data-i18n="import">导入</button>
|
|
@@ -832,9 +891,10 @@ class UIServer {
|
|
|
832
891
|
</div>
|
|
833
892
|
<div class="form-group">
|
|
834
893
|
<label for="accountType" data-i18n="type">类型 *</label>
|
|
835
|
-
<select id="accountType" required>
|
|
894
|
+
<select id="accountType" required onchange="toggleModelFields()">
|
|
836
895
|
<option value="Claude">Claude</option>
|
|
837
896
|
<option value="Codex">Codex</option>
|
|
897
|
+
<option value="Droids">Droids</option>
|
|
838
898
|
<option value="Other" data-i18n="other">其他</option>
|
|
839
899
|
</select>
|
|
840
900
|
</div>
|
|
@@ -854,19 +914,31 @@ class UIServer {
|
|
|
854
914
|
<label for="description" data-i18n="description">描述 (可选)</label>
|
|
855
915
|
<textarea id="description" data-i18n-placeholder="descriptionPlaceholder" placeholder="用于生产环境的主账号"></textarea>
|
|
856
916
|
</div>
|
|
857
|
-
<div class="form-group">
|
|
858
|
-
<label data-i18n="customEnv">自定义环境变量 (可选)</label>
|
|
859
|
-
<div id="envVarsList"></div>
|
|
860
|
-
<button type="button" class="btn btn-secondary btn-small" onclick="addEnvVar()" data-i18n="addVariable">+ 添加变量</button>
|
|
861
|
-
</div>
|
|
862
917
|
<div class="form-group">
|
|
863
918
|
<div class="advanced-toggle" onclick="toggleAdvancedSettings()">
|
|
864
919
|
<span class="advanced-toggle-icon" id="advancedToggleIcon">▶</span>
|
|
865
|
-
<span data-i18n="advancedSettings"
|
|
920
|
+
<span data-i18n="advancedSettings">高级配置</span>
|
|
866
921
|
</div>
|
|
867
922
|
<div class="advanced-content" id="advancedContent">
|
|
868
|
-
|
|
869
|
-
<
|
|
923
|
+
<!-- Custom Environment Variables -->
|
|
924
|
+
<div class="form-group">
|
|
925
|
+
<label data-i18n="customEnv">自定义环境变量</label>
|
|
926
|
+
<div id="envVarsList"></div>
|
|
927
|
+
<button type="button" class="btn btn-secondary btn-small" onclick="addEnvVar()" data-i18n="addVariable">+ 添加变量</button>
|
|
928
|
+
</div>
|
|
929
|
+
<!-- Model Configuration -->
|
|
930
|
+
<div class="form-group">
|
|
931
|
+
<label data-i18n="modelConfig">模型配置</label>
|
|
932
|
+
<!-- Simple model field for Codex/Droids -->
|
|
933
|
+
<div id="simpleModelGroup" style="display: none;">
|
|
934
|
+
<input type="text" id="simpleModel" data-i18n-placeholder="simpleModelPlaceholder" placeholder="例如: gpt-4, droids-model-v1">
|
|
935
|
+
</div>
|
|
936
|
+
<!-- Model groups for Claude -->
|
|
937
|
+
<div id="claudeModelGroup" style="display: none;">
|
|
938
|
+
<div id="modelGroupsList" style="margin-bottom: 10px;"></div>
|
|
939
|
+
<button type="button" class="btn btn-secondary btn-small" onclick="addModelGroupUI()" data-i18n="addModelGroup">+ 添加模型组</button>
|
|
940
|
+
</div>
|
|
941
|
+
</div>
|
|
870
942
|
</div>
|
|
871
943
|
</div>
|
|
872
944
|
<div class="form-actions">
|
|
@@ -885,6 +957,7 @@ class UIServer {
|
|
|
885
957
|
subtitle: '管理你的 AI 账号配置',
|
|
886
958
|
themeLabel: '主题',
|
|
887
959
|
searchPlaceholder: '搜索账号...',
|
|
960
|
+
allTypes: '所有类型',
|
|
888
961
|
addAccount: '+ 添加账号',
|
|
889
962
|
exportAll: '导出全部',
|
|
890
963
|
import: '导入',
|
|
@@ -904,9 +977,12 @@ class UIServer {
|
|
|
904
977
|
emailPlaceholder: 'user@example.com',
|
|
905
978
|
description: '描述 (可选)',
|
|
906
979
|
descriptionPlaceholder: '用于生产环境的主账号',
|
|
907
|
-
|
|
980
|
+
advancedSettings: '高级配置',
|
|
981
|
+
customEnv: '自定义环境变量',
|
|
908
982
|
addVariable: '+ 添加变量',
|
|
909
|
-
|
|
983
|
+
modelConfig: '模型配置',
|
|
984
|
+
simpleModel: '模型',
|
|
985
|
+
simpleModelPlaceholder: '例如: gpt-4, droids-model-v1',
|
|
910
986
|
addModelGroup: '+ 添加模型组',
|
|
911
987
|
modelGroupName: '模型组名称',
|
|
912
988
|
setActive: '设为激活',
|
|
@@ -937,6 +1013,7 @@ class UIServer {
|
|
|
937
1013
|
subtitle: 'Manage your AI account configurations',
|
|
938
1014
|
themeLabel: 'Theme',
|
|
939
1015
|
searchPlaceholder: 'Search accounts...',
|
|
1016
|
+
allTypes: 'All Types',
|
|
940
1017
|
addAccount: '+ Add Account',
|
|
941
1018
|
exportAll: 'Export All',
|
|
942
1019
|
import: 'Import',
|
|
@@ -956,9 +1033,12 @@ class UIServer {
|
|
|
956
1033
|
emailPlaceholder: 'user@example.com',
|
|
957
1034
|
description: 'Description (optional)',
|
|
958
1035
|
descriptionPlaceholder: 'Main account for production environment',
|
|
959
|
-
|
|
1036
|
+
advancedSettings: 'Advanced Configuration',
|
|
1037
|
+
customEnv: 'Custom Environment Variables',
|
|
960
1038
|
addVariable: '+ Add Variable',
|
|
961
|
-
|
|
1039
|
+
modelConfig: 'Model Configuration',
|
|
1040
|
+
simpleModel: 'Model',
|
|
1041
|
+
simpleModelPlaceholder: 'e.g., gpt-4, droids-model-v1',
|
|
962
1042
|
addModelGroup: '+ Add Model Group',
|
|
963
1043
|
modelGroupName: 'Model Group Name',
|
|
964
1044
|
setActive: 'Set Active',
|
|
@@ -1068,11 +1148,18 @@ class UIServer {
|
|
|
1068
1148
|
const container = document.getElementById('accountsContainer');
|
|
1069
1149
|
const emptyState = document.getElementById('emptyState');
|
|
1070
1150
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
1151
|
+
const typeFilter = document.getElementById('typeFilter').value;
|
|
1071
1152
|
|
|
1072
1153
|
const filteredAccounts = Object.entries(accounts).filter(([name, data]) => {
|
|
1073
|
-
|
|
1154
|
+
// Search filter
|
|
1155
|
+
const matchesSearch = name.toLowerCase().includes(searchTerm) ||
|
|
1074
1156
|
(data.email && data.email.toLowerCase().includes(searchTerm)) ||
|
|
1075
1157
|
(data.type && data.type.toLowerCase().includes(searchTerm));
|
|
1158
|
+
|
|
1159
|
+
// Type filter
|
|
1160
|
+
const matchesType = !typeFilter || data.type === typeFilter;
|
|
1161
|
+
|
|
1162
|
+
return matchesSearch && matchesType;
|
|
1076
1163
|
});
|
|
1077
1164
|
|
|
1078
1165
|
if (filteredAccounts.length === 0) {
|
|
@@ -1082,12 +1169,14 @@ class UIServer {
|
|
|
1082
1169
|
}
|
|
1083
1170
|
|
|
1084
1171
|
emptyState.classList.add('hidden');
|
|
1085
|
-
container.innerHTML = filteredAccounts.map(([name, data]) =>
|
|
1086
|
-
|
|
1172
|
+
container.innerHTML = filteredAccounts.map(([name, data]) => {
|
|
1173
|
+
const typeClass = data.type ? \`type-\${data.type.toLowerCase()}\` : 'type-other';
|
|
1174
|
+
return \`
|
|
1175
|
+
<div class="account-card \${typeClass}">
|
|
1087
1176
|
<div class="account-content">
|
|
1088
1177
|
<div class="account-header">
|
|
1089
1178
|
<div class="account-name">\${name}</div>
|
|
1090
|
-
<div class="account-type">\${data.type || 'N/A'}</div>
|
|
1179
|
+
<div class="account-type \${typeClass}">\${data.type || 'N/A'}</div>
|
|
1091
1180
|
</div>
|
|
1092
1181
|
<div class="account-info">
|
|
1093
1182
|
<div class="info-label">\${t('apiKeyLabel')}</div>
|
|
@@ -1117,19 +1206,26 @@ class UIServer {
|
|
|
1117
1206
|
<div class="info-value">\${Object.keys(data.customEnv).join(', ')}</div>
|
|
1118
1207
|
</div>
|
|
1119
1208
|
\` : ''}
|
|
1120
|
-
\${data.modelGroups && Object.keys(data.modelGroups).length > 0 ? \`
|
|
1209
|
+
\${data.type === 'Claude' && data.modelGroups && Object.keys(data.modelGroups).length > 0 ? \`
|
|
1121
1210
|
<div class="account-info">
|
|
1122
1211
|
<div class="info-label">Model Groups</div>
|
|
1123
1212
|
<div class="info-value">\${Object.keys(data.modelGroups).join(', ')} \${data.activeModelGroup ? '(active: ' + data.activeModelGroup + ')' : ''}</div>
|
|
1124
1213
|
</div>
|
|
1125
1214
|
\` : ''}
|
|
1215
|
+
\${(data.type === 'Codex' || data.type === 'Droids') && data.model ? \`
|
|
1216
|
+
<div class="account-info">
|
|
1217
|
+
<div class="info-label">Model</div>
|
|
1218
|
+
<div class="info-value">\${data.model}</div>
|
|
1219
|
+
</div>
|
|
1220
|
+
\` : ''}
|
|
1126
1221
|
</div>
|
|
1127
1222
|
<div class="account-actions">
|
|
1128
1223
|
<button class="btn btn-secondary btn-small" onclick="editAccount('\${name}')">\${t('edit')}</button>
|
|
1129
1224
|
<button class="btn btn-danger btn-small" onclick="deleteAccount('\${name}')">\${t('delete')}</button>
|
|
1130
1225
|
</div>
|
|
1131
1226
|
</div>
|
|
1132
|
-
|
|
1227
|
+
\`;
|
|
1228
|
+
}).join('');
|
|
1133
1229
|
}
|
|
1134
1230
|
|
|
1135
1231
|
function maskApiKey(key) {
|
|
@@ -1137,6 +1233,22 @@ class UIServer {
|
|
|
1137
1233
|
return key.substring(0, 4) + '****' + key.substring(key.length - 4);
|
|
1138
1234
|
}
|
|
1139
1235
|
|
|
1236
|
+
function toggleModelFields() {
|
|
1237
|
+
const accountType = document.getElementById('accountType').value;
|
|
1238
|
+
const simpleModelGroup = document.getElementById('simpleModelGroup');
|
|
1239
|
+
const claudeModelGroup = document.getElementById('claudeModelGroup');
|
|
1240
|
+
|
|
1241
|
+
if (accountType === 'Codex' || accountType === 'Droids') {
|
|
1242
|
+
// Show simple model field, hide Claude model groups
|
|
1243
|
+
simpleModelGroup.style.display = 'block';
|
|
1244
|
+
claudeModelGroup.style.display = 'none';
|
|
1245
|
+
} else {
|
|
1246
|
+
// Show Claude model groups, hide simple model field
|
|
1247
|
+
simpleModelGroup.style.display = 'none';
|
|
1248
|
+
claudeModelGroup.style.display = 'block';
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1140
1252
|
function showAddModal() {
|
|
1141
1253
|
editingAccount = null;
|
|
1142
1254
|
document.getElementById('modalTitle').textContent = t('addAccountTitle');
|
|
@@ -1148,9 +1260,13 @@ class UIServer {
|
|
|
1148
1260
|
document.getElementById('modelGroupsList').innerHTML = '';
|
|
1149
1261
|
modelGroupCount = 0;
|
|
1150
1262
|
activeModelGroup = null;
|
|
1263
|
+
// Clear simple model
|
|
1264
|
+
document.getElementById('simpleModel').value = '';
|
|
1151
1265
|
// Collapse advanced settings
|
|
1152
1266
|
document.getElementById('advancedContent').classList.remove('expanded');
|
|
1153
1267
|
document.getElementById('advancedToggleIcon').classList.remove('expanded');
|
|
1268
|
+
// Toggle model fields based on default type (Claude)
|
|
1269
|
+
toggleModelFields();
|
|
1154
1270
|
document.getElementById('accountModal').classList.add('active');
|
|
1155
1271
|
}
|
|
1156
1272
|
|
|
@@ -1176,13 +1292,27 @@ class UIServer {
|
|
|
1176
1292
|
});
|
|
1177
1293
|
}
|
|
1178
1294
|
|
|
1179
|
-
//
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1295
|
+
// Toggle model fields based on account type
|
|
1296
|
+
toggleModelFields();
|
|
1297
|
+
|
|
1298
|
+
// Load model configuration based on account type
|
|
1299
|
+
if (account.type === 'Codex' || account.type === 'Droids') {
|
|
1300
|
+
// Load simple model field
|
|
1301
|
+
document.getElementById('simpleModel').value = account.model || '';
|
|
1302
|
+
// Clear model groups
|
|
1303
|
+
document.getElementById('modelGroupsList').innerHTML = '';
|
|
1304
|
+
modelGroupCount = 0;
|
|
1305
|
+
activeModelGroup = null;
|
|
1306
|
+
} else {
|
|
1307
|
+
// Load model groups for Claude
|
|
1308
|
+
document.getElementById('modelGroupsList').innerHTML = '';
|
|
1309
|
+
modelGroupCount = 0;
|
|
1310
|
+
activeModelGroup = null;
|
|
1311
|
+
// Clear simple model
|
|
1312
|
+
document.getElementById('simpleModel').value = '';
|
|
1313
|
+
|
|
1314
|
+
const hasModelGroups = account.modelGroups && Object.keys(account.modelGroups).length > 0;
|
|
1315
|
+
if (hasModelGroups) {
|
|
1186
1316
|
Object.entries(account.modelGroups).forEach(([groupName, groupConfig]) => {
|
|
1187
1317
|
const groupId = modelGroupCount++;
|
|
1188
1318
|
const isActive = account.activeModelGroup === groupName;
|
|
@@ -1237,13 +1367,14 @@ class UIServer {
|
|
|
1237
1367
|
container.appendChild(div);
|
|
1238
1368
|
});
|
|
1239
1369
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1370
|
+
// Expand advanced settings if model groups exist
|
|
1371
|
+
document.getElementById('advancedContent').classList.add('expanded');
|
|
1372
|
+
document.getElementById('advancedToggleIcon').classList.add('expanded');
|
|
1373
|
+
} else {
|
|
1374
|
+
// Collapse advanced settings if no model groups
|
|
1375
|
+
document.getElementById('advancedContent').classList.remove('expanded');
|
|
1376
|
+
document.getElementById('advancedToggleIcon').classList.remove('expanded');
|
|
1377
|
+
}
|
|
1247
1378
|
}
|
|
1248
1379
|
|
|
1249
1380
|
document.getElementById('accountModal').classList.add('active');
|
|
@@ -1404,12 +1535,22 @@ class UIServer {
|
|
|
1404
1535
|
delete accountData.customEnv;
|
|
1405
1536
|
}
|
|
1406
1537
|
|
|
1407
|
-
// Collect model
|
|
1408
|
-
|
|
1409
|
-
accountData.activeModelGroup = null;
|
|
1538
|
+
// Collect model configuration based on account type
|
|
1539
|
+
const accountType = document.getElementById('accountType').value;
|
|
1410
1540
|
|
|
1411
|
-
|
|
1412
|
-
|
|
1541
|
+
if (accountType === 'Codex' || accountType === 'Droids') {
|
|
1542
|
+
// Use simple model field
|
|
1543
|
+
const simpleModel = document.getElementById('simpleModel').value.trim();
|
|
1544
|
+
if (simpleModel) {
|
|
1545
|
+
accountData.model = simpleModel;
|
|
1546
|
+
}
|
|
1547
|
+
} else {
|
|
1548
|
+
// Collect model groups for Claude
|
|
1549
|
+
accountData.modelGroups = {};
|
|
1550
|
+
accountData.activeModelGroup = null;
|
|
1551
|
+
|
|
1552
|
+
const modelGroupsList = document.getElementById('modelGroupsList');
|
|
1553
|
+
modelGroupsList.querySelectorAll('.model-group-item').forEach(item => {
|
|
1413
1554
|
const groupId = parseInt(item.id.replace('modelGroup', ''));
|
|
1414
1555
|
const groupName = document.getElementById(\`groupName\${groupId}\`).value;
|
|
1415
1556
|
|
|
@@ -1430,15 +1571,16 @@ class UIServer {
|
|
|
1430
1571
|
|
|
1431
1572
|
accountData.modelGroups[groupName] = groupConfig;
|
|
1432
1573
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1574
|
+
// Check if this is the active group
|
|
1575
|
+
if (activeModelGroup === groupId) {
|
|
1576
|
+
accountData.activeModelGroup = groupName;
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1438
1579
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1580
|
+
if (Object.keys(accountData.modelGroups).length === 0) {
|
|
1581
|
+
delete accountData.modelGroups;
|
|
1582
|
+
delete accountData.activeModelGroup;
|
|
1583
|
+
}
|
|
1442
1584
|
}
|
|
1443
1585
|
|
|
1444
1586
|
try {
|