diff --git a/packages/shared/config/constant.ts b/packages/shared/config/constant.ts index 17304f357f..82b78459a5 100644 --- a/packages/shared/config/constant.ts +++ b/packages/shared/config/constant.ts @@ -211,3 +211,10 @@ export const MIN_WINDOW_WIDTH = 1080 export const SECOND_MIN_WINDOW_WIDTH = 520 export const MIN_WINDOW_HEIGHT = 600 export const defaultByPassRules = 'localhost,127.0.0.1,::1' + +export enum codeTools { + qwenCode = 'qwen-code', + claudeCode = 'claude-code', + geminiCli = 'gemini-cli', + openaiCodex = 'openai-codex' +} diff --git a/src/main/services/CodeToolsService.ts b/src/main/services/CodeToolsService.ts index 5fa2ce87c2..d408ad78a7 100644 --- a/src/main/services/CodeToolsService.ts +++ b/src/main/services/CodeToolsService.ts @@ -7,6 +7,7 @@ import { isWin } from '@main/constant' import { removeEnvProxy } from '@main/utils' import { isUserInChina } from '@main/utils/ipService' import { getBinaryName } from '@main/utils/process' +import { codeTools } from '@shared/config/constant' import { spawn } from 'child_process' import { promisify } from 'util' @@ -41,23 +42,33 @@ class CodeToolsService { } public async getPackageName(cliTool: string) { - if (cliTool === 'claude-code') { - return '@anthropic-ai/claude-code' + switch (cliTool) { + case codeTools.claudeCode: + return '@anthropic-ai/claude-code' + case codeTools.geminiCli: + return '@google/gemini-cli' + case codeTools.openaiCodex: + return '@openai/codex' + case codeTools.qwenCode: + return '@qwen-code/qwen-code' + default: + throw new Error(`Unsupported CLI tool: ${cliTool}`) } - if (cliTool === 'gemini-cli') { - return '@google/gemini-cli' - } - return '@qwen-code/qwen-code' } public async getCliExecutableName(cliTool: string) { - if (cliTool === 'claude-code') { - return 'claude' + switch (cliTool) { + case codeTools.claudeCode: + return 'claude' + case codeTools.geminiCli: + return 'gemini' + case codeTools.openaiCodex: + return 'codex' + case codeTools.qwenCode: + return 'qwen' + default: + throw new Error(`Unsupported CLI tool: ${cliTool}`) } - if (cliTool === 'gemini-cli') { - return 'gemini' - } - return 'qwen' } private async isPackageInstalled(cliTool: string): Promise { diff --git a/src/renderer/src/hooks/useCodeTools.ts b/src/renderer/src/hooks/useCodeTools.ts index ded65f75b9..db8b21bbeb 100644 --- a/src/renderer/src/hooks/useCodeTools.ts +++ b/src/renderer/src/hooks/useCodeTools.ts @@ -11,6 +11,7 @@ import { setSelectedModel } from '@renderer/store/codeTools' import { Model } from '@renderer/types' +import { codeTools } from '@shared/config/constant' import { useCallback } from 'react' export const useCodeTools = () => { @@ -20,7 +21,7 @@ export const useCodeTools = () => { // 设置选择的 CLI 工具 const setCliTool = useCallback( - (tool: string) => { + (tool: codeTools) => { dispatch(setSelectedCliTool(tool)) }, [dispatch] diff --git a/src/renderer/src/pages/code/CodeToolsPage.tsx b/src/renderer/src/pages/code/CodeToolsPage.tsx index c462a0becd..bb4457438d 100644 --- a/src/renderer/src/pages/code/CodeToolsPage.tsx +++ b/src/renderer/src/pages/code/CodeToolsPage.tsx @@ -10,6 +10,7 @@ import { getModelUniqId } from '@renderer/services/ModelService' import { useAppDispatch, useAppSelector } from '@renderer/store' import { setIsBunInstalled } from '@renderer/store/mcp' import { Model } from '@renderer/types' +import { codeTools } from '@shared/config/constant' import { Alert, Button, Checkbox, Input, Select, Space } from 'antd' import { Download, Terminal, X } from 'lucide-react' import { FC, useCallback, useEffect, useState } from 'react' @@ -18,9 +19,10 @@ import styled from 'styled-components' // CLI 工具选项 const CLI_TOOLS = [ - { value: 'qwen-code', label: 'Qwen Code' }, - { value: 'claude-code', label: 'Claude Code' }, - { value: 'gemini-cli', label: 'Gemini CLI' } + { value: codeTools.qwenCode, label: 'Qwen Code' }, + { value: codeTools.claudeCode, label: 'Claude Code' }, + { value: codeTools.geminiCli, label: 'Gemini CLI' }, + { value: codeTools.openaiCodex, label: 'OpenAI Codex' } ] const SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api'] @@ -53,7 +55,7 @@ const CodeToolsPage: FC = () => { const [autoUpdateToLatest, setAutoUpdateToLatest] = useState(false) // 处理 CLI 工具选择 - const handleCliToolChange = (value: string) => { + const handleCliToolChange = (value: codeTools) => { setCliTool(value) // 不再清空模型选择,因为每个工具都会记住自己的模型 } @@ -79,9 +81,9 @@ const CodeToolsPage: FC = () => { ) const availableProviders = - selectedCliTool === 'claude-code' + selectedCliTool === codeTools.claudeCode ? claudeProviders - : selectedCliTool === 'gemini-cli' + : selectedCliTool === codeTools.geminiCli ? geminiProviders : openAiProviders @@ -194,7 +196,7 @@ const CodeToolsPage: FC = () => { const apiKey = await aiProvider.getApiKey() let env: Record = {} - if (selectedCliTool === 'claude-code') { + if (selectedCliTool === codeTools.claudeCode) { env = { ANTHROPIC_API_KEY: apiKey, ANTHROPIC_BASE_URL: modelProvider.apiHost, @@ -202,7 +204,7 @@ const CodeToolsPage: FC = () => { } } - if (selectedCliTool === 'gemini-cli') { + if (selectedCliTool === codeTools.geminiCli) { const apiSuffix = modelProvider.id === 'aihubmix' ? '/gemini' : '' const apiBaseUrl = modelProvider.apiHost + apiSuffix env = { @@ -213,7 +215,7 @@ const CodeToolsPage: FC = () => { } } - if (selectedCliTool === 'qwen-code') { + if (selectedCliTool === codeTools.qwenCode || selectedCliTool === codeTools.openaiCodex) { env = { OPENAI_API_KEY: apiKey, OPENAI_BASE_URL: baseUrl, diff --git a/src/renderer/src/store/codeTools.ts b/src/renderer/src/store/codeTools.ts index 267671bed7..689335b43f 100644 --- a/src/renderer/src/store/codeTools.ts +++ b/src/renderer/src/store/codeTools.ts @@ -1,12 +1,13 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { Model } from '@renderer/types' +import { codeTools } from '@shared/config/constant' // 常量定义 const MAX_DIRECTORIES = 10 // 最多保存10个目录 export interface CodeToolsState { // 当前选择的 CLI 工具,默认使用 qwen-code - selectedCliTool: string + selectedCliTool: codeTools // 为每个 CLI 工具单独保存选择的模型 selectedModels: Record // 为每个 CLI 工具单独保存环境变量 @@ -18,11 +19,12 @@ export interface CodeToolsState { } export const initialState: CodeToolsState = { - selectedCliTool: 'qwen-code', + selectedCliTool: codeTools.qwenCode, selectedModels: { - 'qwen-code': null, - 'claude-code': null, - 'gemini-cli': null + [codeTools.qwenCode]: null, + [codeTools.claudeCode]: null, + [codeTools.geminiCli]: null, + [codeTools.openaiCodex]: null }, environmentVariables: { 'qwen-code': '', @@ -38,7 +40,7 @@ const codeToolsSlice = createSlice({ initialState, reducers: { // 设置选择的 CLI 工具 - setSelectedCliTool: (state, action: PayloadAction) => { + setSelectedCliTool: (state, action: PayloadAction) => { state.selectedCliTool = action.payload },