UNPKG

cc-model-switcher

Version:

A simple CLI tool to switch Claude Code AI models easily

218 lines (186 loc) 6.1 kB
#!/usr/bin/env node const { Command } = require('commander'); const chalk = require('chalk'); const inquirer = require('inquirer'); const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); const program = new Command(); // 获取用户主目录 const homeDir = require('os').homedir(); const configPath = path.join(homeDir, '.models.json'); // 默认配置 - 使用占位符 const defaultConfig = { models: { kimi: { description: "Kimi AI (Moonshot)", env: { "ANTHROPIC_BASE_URL": "https://api.moonshot.cn/anthropic/", "ANTHROPIC_AUTH_TOKEN": "YOUR_KIMI_API_KEY_HERE", "API_TIMEOUT_MS": "600000", "ANTHROPIC_MODEL": "kimi-k2-0711-preview", "ANTHROPIC_SMALL_FAST_MODEL": "kimi-k2-0711-preview" } }, deepseek: { description: "DeepSeek V3.1", env: { "ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic", "ANTHROPIC_AUTH_TOKEN": "YOUR_DEEPSEEK_API_KEY_HERE", "API_TIMEOUT_MS": "600000", "ANTHROPIC_MODEL": "deepseek-chat", "ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat" } } } }; // 初始化配置文件 function initConfig() { if (!fs.existsSync(configPath)) { fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2)); console.log(chalk.green(`✅ 已创建配置文件: ${configPath}`)); } } // 读取配置 function readConfig() { try { const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); return config; } catch (error) { console.error(chalk.red(`❌ 读取配置文件失败: ${error.message}`)); process.exit(1); } } // 保存配置 function saveConfig(config) { try { fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); } catch (error) { console.error(chalk.red(`❌ 保存配置文件失败: ${error.message}`)); process.exit(1); } } // 列出所有模型 function listModels() { const config = readConfig(); console.log(chalk.cyan('\n📋 可用模型:')); Object.entries(config.models).forEach(([name, model]) => { console.log(` ${chalk.green(name)} - ${model.description}`); }); console.log(); } // 检查并提示API密钥 function checkApiKeys(config) { let hasPlaceholder = false; Object.entries(config.models).forEach(([name, model]) => { if (model.env.ANTHROPIC_AUTH_TOKEN.includes('YOUR_') || model.env.ANTHROPIC_AUTH_TOKEN === 'placeholder') { console.log(chalk.yellow(`⚠️ 模型 ${name} 需要配置API密钥`)); hasPlaceholder = true; } }); if (hasPlaceholder) { console.log(chalk.cyan(`\n💡 请编辑配置文件添加API密钥: ${configPath}`)); console.log(chalk.gray('将 ANTHROPIC_AUTH_TOKEN 的值替换为你的实际API密钥')); } return hasPlaceholder; } // 切换模型 function switchModel(modelName) { const config = readConfig(); if (!config.models[modelName]) { console.log(chalk.red(`❌ 模型 "${modelName}" 不存在`)); listModels(); process.exit(1); } const model = config.models[modelName]; // 检查API密钥 if (checkApiKeys(config)) { process.exit(1); } console.log(chalk.cyan(`🔄 正在切换到模型: ${chalk.green(modelName)}`)); // 设置环境变量并启动Claude Code const env = { ...process.env }; Object.entries(model.env).forEach(([key, value]) => { env[key] = value; }); console.log(chalk.green(`✅ 已切换到 ${modelName},正在启动 Claude Code...`)); // 启动Claude Code - 正确处理Windows环境 const workDir = process.cwd(); console.log(chalk.cyan(`📁 工作目录: ${workDir}`)); const platform = process.platform; let claude; if (platform === 'win32') { // Windows: 直接使用cwd参数设置工作目录 const normalizedPath = path.resolve(workDir); console.log(chalk.gray(`规范化路径: ${normalizedPath}`)); // 修复日期: 2025-01-14 // 修复内容: 使用spawn的cwd参数直接设置工作目录,避免PowerShell路径处理问题 console.log(chalk.blue(`🚀 正在启动 Claude Code (工作目录: ${normalizedPath})`)); claude = spawn('npx', ['claude'], { stdio: 'inherit', env: env, shell: true, cwd: normalizedPath }); } else { // macOS/Linux: 正常启动 claude = spawn('claude', [], { stdio: 'inherit', env: env, shell: true, cwd: workDir }); } claude.on('error', (error) => { if (error.code === 'ENOENT') { console.error(chalk.red('❌ Claude Code 未安装或未添加到PATH')); console.log(chalk.yellow('Windows用户请确保:')); console.log(chalk.yellow('1. 已安装Claude Code')); console.log(chalk.yellow('2. 已安装Git Bash')); console.log(chalk.yellow('3. 已将claude命令添加到系统PATH')); console.log(chalk.yellow('4. 或设置环境变量 CLAUDE_CODE_GIT_BASH_PATH')); } else { console.error(chalk.red(`❌ 启动Claude Code失败: ${error.message}`)); } process.exit(1); }); } // 交互式选择模型 async function interactiveSelect() { const config = readConfig(); const models = Object.entries(config.models).map(([name, model]) => ({ name: `${name} - ${model.description}`, value: name })); const answer = await inquirer.prompt([ { type: 'list', name: 'model', message: '选择要使用的模型:', choices: models } ]); switchModel(answer.model); } // 主程序 program .name('cc_switch') .description('Claude Code 模型切换器') .version('1.0.0'); program .argument('[model]', '模型名称') .option('-l, --list', '列出所有可用模型') .option('-i, --interactive', '交互式选择模型') .action((model, options) => { initConfig(); if (options.list) { listModels(); } else if (options.interactive) { interactiveSelect(); } else if (model) { switchModel(model); } else { switchModel('kimi'); // 默认使用kimi } }); program.parse();