Skip to content

Latest commit

 

History

History
340 lines (267 loc) · 8.76 KB

GEMINI_ANALYSIS.md

File metadata and controls

340 lines (267 loc) · 8.76 KB

Gemini 实现对比分析:cc-switch vs cc-r

目的:分析参考项目 (cc-switch/Rust) 与当前项目 (cc-r/Go) 在 Gemini 配置管理的实现差异,识别配置问题。


概览表

方面 cc-switch (参考) cc-r (当前) 状态
语言框架 Rust/Tauri Go/Wails3 -
Gemini 目录 ~/.gemini/ ~/.gemini/ ✅ 一致
供应商配置 DB 或配置文件 ~/.code-switch/gemini-providers.json ✅ 合理
代理前缀 /gemini /gemini ✅ 一致
备份文件名 未在代码中找到 .cc-studio.backup ❌ 命名错误
配置验证 有(严格模式) 缺失 ⚠️ 缺失验证

关键差异

1. 备份文件命名 (Critical)

cc-r 代码 (services/geminiservice.go:656, 689):

backupPath := envPath + ".cc-studio.backup"  // ❌ 错误!

问题:

  • 命名为 .cc-studio.backup,但项目是 "code-switch"
  • 来自旧项目迁移的遗留代码
  • 当禁用代理时,恢复备份会使用错误的文件名

修复:

backupPath := envPath + ".cc-switch.backup"  // 或 ".code-switch.backup"

2. 配置验证缺失 (High Priority)

cc-switch (src-tauri/src/gemini_config.rs:226-278):

// 两级验证
validate_gemini_settings()       // 基础格式检查
validate_gemini_settings_strict() // 要求必需字段

cc-r:

// 仅有检测,无验证
detectGeminiAuthType(provider)  // 只是识别类型

SwitchProvider() 的问题:

func (s *GeminiService) SwitchProvider(id string) error {
    // ... 找到 provider ...
    
    // ❌ 直接写入,无验证
    authType := detectGeminiAuthType(provider)
    switch authType {
    case GeminiAuthAPIKey:
        if provider.APIKey == "" {
            // 应该在这里拒绝!
        }
    }
}

影响:

  • 用户可以切换到配置不完整的供应商(缺 API Key)
  • Gemini 读取配置时会失败,但用户不知道原因

修复:

func (s *GeminiService) SwitchProvider(id string) error {
    // ... 找到 provider ...
    
    authType := detectGeminiAuthType(provider)
    
    // 新增:验证配置完整性
    if authType != GeminiAuthOAuth && provider.APIKey == "" {
        return fmt.Errorf("无法切换:API Key 未设置")
    }
    
    // ... 继续写入配置 ...
}

3. 认证类型 selectedType 值不准确

cc-r 代码 (services/geminiservice.go:232-262):

case GeminiAuthPackycode, GeminiAuthAPIKey, GeminiAuthGeneric:
    // ... 所有 API Key 类型都写同样的值
    if err := writeGeminiSettings(map[string]any{
        "security": map[string]any{
            "auth": map[string]any{
                "selectedType": string(GeminiAuthAPIKey),  // ❌ 统一为 "gemini-api-key"
            },
        },
    }); err != nil {
        // ...
    }

问题:

  • PackyCode 是特殊的第三方供应商,应该有自己的认证标记
  • 所有 API Key 类型都写 "gemini-api-key",无法区分
  • 会导致 Gemini CLI 无法识别 PackyCode 的特殊配置需求

修复:按认证类型写不同的 selectedType

switch authType {
case GeminiAuthOAuth:
    // ... oauth-personal ...
case GeminiAuthPackycode:
    if err := writeGeminiSettings(map[string]any{
        "security": map[string]any{
            "auth": map[string]any{
                "selectedType": "packycode",  // ✅ 区分类型
            },
        },
    }); err != nil {
        // ...
    }
case GeminiAuthAPIKey:
    if err := writeGeminiSettings(map[string]any{
        "security": map[string]any{
            "auth": map[string]any{
                "selectedType": "gemini-api-key",  // ✅ 保留原有
            },
        },
    }); err != nil {
        // ...
    }
}

4. envConfig 处理不当

代码 (services/geminiservice.go:234-247):

envConfig := provider.EnvConfig
if envConfig == nil {
    envConfig = make(map[string]string)  // ❌ 创建空 map
}
// 然后尝试从 provider 字段补充配置
if provider.BaseURL != "" && envConfig["GOOGLE_GEMINI_BASE_URL"] == "" {
    envConfig["GOOGLE_GEMINI_BASE_URL"] = provider.BaseURL
}

问题:

  • 如果 EnvConfig 为 nil,会丢失预设中的配置
  • 应该先复制预设配置,再覆盖

修复:

envConfig := make(map[string]string)

// 1. 复制预设配置
if provider.EnvConfig != nil {
    for k, v := range provider.EnvConfig {
        envConfig[k] = v
    }
}

// 2. 补充来自 provider 字段的配置
if provider.BaseURL != "" && envConfig["GOOGLE_GEMINI_BASE_URL"] == "" {
    envConfig["GOOGLE_GEMINI_BASE_URL"] = provider.BaseURL
}
// ...

5. 代理地址格式歧义

代码 (services/geminiservice.go:708-725):

func buildProxyURL(relayAddr string) string {
    addr := strings.TrimSpace(relayAddr)
    if addr == "" {
        addr = ":18100"  // ❌ 歧义!
    }
    // ...
    if strings.HasPrefix(host, ":") {
        host = "127.0.0.1" + host
    }
    // ...
}

问题:

  • :18100 在 Go 中表示监听所有网卡(0.0.0.0:18100),但代码假设它是端口
  • main.go:88 传入的 ":18100" 实际上应该指向 127.0.0.1:18100
  • 这与安全架构冲突(代理应只监听本地回环)

修复:在调用点明确使用本地地址

// main.go:88
geminiService := services.NewGeminiService("127.0.0.1:18100")  // ✅ 明确本地

或改进 buildProxyURL() 的处理逻辑。


6. 测试参数缺失 (Blocker)

代码 (services/geminiservice_test.go:8, 233):

func TestGeminiService_GetPresets(t *testing.T) {
    svc := NewGeminiService()  // ❌ 缺参数
    // ...
}

问题:

  • NewGeminiService(relayAddr string) 要求传入地址参数
  • 测试中缺少参数,导致编译失败
  • 影响 go test ./... 整体执行

修复:

func TestGeminiService_GetPresets(t *testing.T) {
    svc := NewGeminiService(":18100")  // ✅ 添加参数
    presets := svc.GetPresets()
    // ...
}

func TestGeminiPreset_Fields(t *testing.T) {
    svc := NewGeminiService(":18100")  // ✅ 添加参数
    presets := svc.GetPresets()
    // ...
}

次要差异

7. .env 文件行尾处理

代码 (services/geminiservice.go:391):

lines := strings.Split(content, "\n")

问题:

  • Windows 上 .env 文件可能有 \r\n 行尾
  • strings.Split() 会在每个 \n 处分割,保留 \r
  • strings.TrimSpace() 会去除 \r,但逻辑不够清晰

改进(非阻断):

// 使用 bufio.Scanner 或
lines := strings.Split(strings.ReplaceAll(content, "\r\n", "\n"), "\n")

8. 缺少自定义目录支持

cc-switch (src-tauri/src/gemini_config.rs:8-17):

pub fn get_gemini_dir() -> PathBuf {
    if let Some(custom) = crate::settings::get_gemini_override_dir() {
        return custom;  // 支持覆盖
    }
    // ...
}

cc-r:无此机制,硬编码 ~/.gemini/

影响:低(大多数用户不需要,高级用户可修改)


9. 测试覆盖不完整

缺失的测试:

  • ❌ buildProxyURL() 的各种输入格式
  • ❌ SwitchProvider() 的切换逻辑
  • ❌ EnableProxy() / DisableProxy() 的备份恢复
  • ❌ 配置验证的失败场景

建议:添加这些测试,确保重构后功能完整。


修复优先级

优先级 问题 文件:行 修复工作量
🔴 P0 测试参数缺失 geminiservice_test.go:8,233 5 分钟
🔴 P0 配置验证缺失 geminiservice.go:SwitchProvider 15 分钟
🟠 P1 备份文件命名 geminiservice.go:656,689 5 分钟
🟠 P1 selectedType 值 geminiservice.go:232-262 10 分钟
🟡 P2 envConfig 处理 geminiservice.go:234-247 10 分钟
🟡 P2 代理地址歧义 geminiservice.go:708-725, main.go:88 10 分钟
🟢 P3 行尾处理 geminiservice.go:391 5 分钟
🟢 P3 测试覆盖 geminiservice_test.go 30 分钟

总计:约 90 分钟可修复所有问题


对标参考资源

  • cc-switch 验证逻辑:src-tauri/src/gemini_config.rs:226-278
  • cc-switch 文件写入:src-tauri/src/gemini_config.rs:287-337
  • cc-switch 单元测试:src-tauri/src/gemini_config.rs:375-656

验证清单

修复后请确保:

  • go test ./services -v -run TestGemini 通过
  • EnableProxy() 后,.cc-switch.backup 或 .code-switch.backup 文件存在
  • 切换到无 API Key 的供应商时返回错误
  • PackyCode 供应商的 selectedType 为 "packycode"
  • buildProxyURL() 返回 http://127.0.0.1:18100/gemini
  • 前端能正确切换 Gemini 供应商

分析日期:2025-11-29
分析工具:Claude Code (Haiku 4.5)
版本:1.0