mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-11 16:39:15 +08:00
feat: support openai codex (#9332)
* support openai codex * lint * refactor: remove unused codeTools enum from constant.ts * fix build * fix lin * fix: add support for qwenCode CLI tool and improve error handling in CodeToolsService
This commit is contained in:
parent
1da1721ec2
commit
332ba5d678
@ -211,3 +211,10 @@ export const MIN_WINDOW_WIDTH = 1080
|
|||||||
export const SECOND_MIN_WINDOW_WIDTH = 520
|
export const SECOND_MIN_WINDOW_WIDTH = 520
|
||||||
export const MIN_WINDOW_HEIGHT = 600
|
export const MIN_WINDOW_HEIGHT = 600
|
||||||
export const defaultByPassRules = 'localhost,127.0.0.1,::1'
|
export const defaultByPassRules = 'localhost,127.0.0.1,::1'
|
||||||
|
|
||||||
|
export enum codeTools {
|
||||||
|
qwenCode = 'qwen-code',
|
||||||
|
claudeCode = 'claude-code',
|
||||||
|
geminiCli = 'gemini-cli',
|
||||||
|
openaiCodex = 'openai-codex'
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { isWin } from '@main/constant'
|
|||||||
import { removeEnvProxy } from '@main/utils'
|
import { removeEnvProxy } from '@main/utils'
|
||||||
import { isUserInChina } from '@main/utils/ipService'
|
import { isUserInChina } from '@main/utils/ipService'
|
||||||
import { getBinaryName } from '@main/utils/process'
|
import { getBinaryName } from '@main/utils/process'
|
||||||
|
import { codeTools } from '@shared/config/constant'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
import { promisify } from 'util'
|
import { promisify } from 'util'
|
||||||
|
|
||||||
@ -41,23 +42,33 @@ class CodeToolsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getPackageName(cliTool: string) {
|
public async getPackageName(cliTool: string) {
|
||||||
if (cliTool === 'claude-code') {
|
switch (cliTool) {
|
||||||
return '@anthropic-ai/claude-code'
|
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) {
|
public async getCliExecutableName(cliTool: string) {
|
||||||
if (cliTool === 'claude-code') {
|
switch (cliTool) {
|
||||||
return 'claude'
|
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<boolean> {
|
private async isPackageInstalled(cliTool: string): Promise<boolean> {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
setSelectedModel
|
setSelectedModel
|
||||||
} from '@renderer/store/codeTools'
|
} from '@renderer/store/codeTools'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
|
import { codeTools } from '@shared/config/constant'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
export const useCodeTools = () => {
|
export const useCodeTools = () => {
|
||||||
@ -20,7 +21,7 @@ export const useCodeTools = () => {
|
|||||||
|
|
||||||
// 设置选择的 CLI 工具
|
// 设置选择的 CLI 工具
|
||||||
const setCliTool = useCallback(
|
const setCliTool = useCallback(
|
||||||
(tool: string) => {
|
(tool: codeTools) => {
|
||||||
dispatch(setSelectedCliTool(tool))
|
dispatch(setSelectedCliTool(tool))
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { getModelUniqId } from '@renderer/services/ModelService'
|
|||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { setIsBunInstalled } from '@renderer/store/mcp'
|
import { setIsBunInstalled } from '@renderer/store/mcp'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
|
import { codeTools } from '@shared/config/constant'
|
||||||
import { Alert, Button, Checkbox, Input, Select, Space } from 'antd'
|
import { Alert, Button, Checkbox, Input, Select, Space } from 'antd'
|
||||||
import { Download, Terminal, X } from 'lucide-react'
|
import { Download, Terminal, X } from 'lucide-react'
|
||||||
import { FC, useCallback, useEffect, useState } from 'react'
|
import { FC, useCallback, useEffect, useState } from 'react'
|
||||||
@ -18,9 +19,10 @@ import styled from 'styled-components'
|
|||||||
|
|
||||||
// CLI 工具选项
|
// CLI 工具选项
|
||||||
const CLI_TOOLS = [
|
const CLI_TOOLS = [
|
||||||
{ value: 'qwen-code', label: 'Qwen Code' },
|
{ value: codeTools.qwenCode, label: 'Qwen Code' },
|
||||||
{ value: 'claude-code', label: 'Claude Code' },
|
{ value: codeTools.claudeCode, label: 'Claude Code' },
|
||||||
{ value: 'gemini-cli', label: 'Gemini CLI' }
|
{ value: codeTools.geminiCli, label: 'Gemini CLI' },
|
||||||
|
{ value: codeTools.openaiCodex, label: 'OpenAI Codex' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api']
|
const SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api']
|
||||||
@ -53,7 +55,7 @@ const CodeToolsPage: FC = () => {
|
|||||||
const [autoUpdateToLatest, setAutoUpdateToLatest] = useState(false)
|
const [autoUpdateToLatest, setAutoUpdateToLatest] = useState(false)
|
||||||
|
|
||||||
// 处理 CLI 工具选择
|
// 处理 CLI 工具选择
|
||||||
const handleCliToolChange = (value: string) => {
|
const handleCliToolChange = (value: codeTools) => {
|
||||||
setCliTool(value)
|
setCliTool(value)
|
||||||
// 不再清空模型选择,因为每个工具都会记住自己的模型
|
// 不再清空模型选择,因为每个工具都会记住自己的模型
|
||||||
}
|
}
|
||||||
@ -79,9 +81,9 @@ const CodeToolsPage: FC = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const availableProviders =
|
const availableProviders =
|
||||||
selectedCliTool === 'claude-code'
|
selectedCliTool === codeTools.claudeCode
|
||||||
? claudeProviders
|
? claudeProviders
|
||||||
: selectedCliTool === 'gemini-cli'
|
: selectedCliTool === codeTools.geminiCli
|
||||||
? geminiProviders
|
? geminiProviders
|
||||||
: openAiProviders
|
: openAiProviders
|
||||||
|
|
||||||
@ -194,7 +196,7 @@ const CodeToolsPage: FC = () => {
|
|||||||
const apiKey = await aiProvider.getApiKey()
|
const apiKey = await aiProvider.getApiKey()
|
||||||
|
|
||||||
let env: Record<string, string> = {}
|
let env: Record<string, string> = {}
|
||||||
if (selectedCliTool === 'claude-code') {
|
if (selectedCliTool === codeTools.claudeCode) {
|
||||||
env = {
|
env = {
|
||||||
ANTHROPIC_API_KEY: apiKey,
|
ANTHROPIC_API_KEY: apiKey,
|
||||||
ANTHROPIC_BASE_URL: modelProvider.apiHost,
|
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 apiSuffix = modelProvider.id === 'aihubmix' ? '/gemini' : ''
|
||||||
const apiBaseUrl = modelProvider.apiHost + apiSuffix
|
const apiBaseUrl = modelProvider.apiHost + apiSuffix
|
||||||
env = {
|
env = {
|
||||||
@ -213,7 +215,7 @@ const CodeToolsPage: FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedCliTool === 'qwen-code') {
|
if (selectedCliTool === codeTools.qwenCode || selectedCliTool === codeTools.openaiCodex) {
|
||||||
env = {
|
env = {
|
||||||
OPENAI_API_KEY: apiKey,
|
OPENAI_API_KEY: apiKey,
|
||||||
OPENAI_BASE_URL: baseUrl,
|
OPENAI_BASE_URL: baseUrl,
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
|
import { codeTools } from '@shared/config/constant'
|
||||||
|
|
||||||
// 常量定义
|
// 常量定义
|
||||||
const MAX_DIRECTORIES = 10 // 最多保存10个目录
|
const MAX_DIRECTORIES = 10 // 最多保存10个目录
|
||||||
|
|
||||||
export interface CodeToolsState {
|
export interface CodeToolsState {
|
||||||
// 当前选择的 CLI 工具,默认使用 qwen-code
|
// 当前选择的 CLI 工具,默认使用 qwen-code
|
||||||
selectedCliTool: string
|
selectedCliTool: codeTools
|
||||||
// 为每个 CLI 工具单独保存选择的模型
|
// 为每个 CLI 工具单独保存选择的模型
|
||||||
selectedModels: Record<string, Model | null>
|
selectedModels: Record<string, Model | null>
|
||||||
// 为每个 CLI 工具单独保存环境变量
|
// 为每个 CLI 工具单独保存环境变量
|
||||||
@ -18,11 +19,12 @@ export interface CodeToolsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: CodeToolsState = {
|
export const initialState: CodeToolsState = {
|
||||||
selectedCliTool: 'qwen-code',
|
selectedCliTool: codeTools.qwenCode,
|
||||||
selectedModels: {
|
selectedModels: {
|
||||||
'qwen-code': null,
|
[codeTools.qwenCode]: null,
|
||||||
'claude-code': null,
|
[codeTools.claudeCode]: null,
|
||||||
'gemini-cli': null
|
[codeTools.geminiCli]: null,
|
||||||
|
[codeTools.openaiCodex]: null
|
||||||
},
|
},
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
'qwen-code': '',
|
'qwen-code': '',
|
||||||
@ -38,7 +40,7 @@ const codeToolsSlice = createSlice({
|
|||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
// 设置选择的 CLI 工具
|
// 设置选择的 CLI 工具
|
||||||
setSelectedCliTool: (state, action: PayloadAction<string>) => {
|
setSelectedCliTool: (state, action: PayloadAction<codeTools>) => {
|
||||||
state.selectedCliTool = action.payload
|
state.selectedCliTool = action.payload
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user