feat(CodeTools): add environment variable support for CLI tools; update UI to manage environment variables and enhance localization for related strings

This commit is contained in:
kangfenmao 2025-08-19 16:39:50 +08:00
parent 29d4e37f6b
commit 2265ecab21
10 changed files with 94 additions and 12 deletions

View File

@ -283,12 +283,11 @@ class CodeToolsService {
} }
// Build command to execute // Build command to execute
let baseCommand: string let baseCommand = `${bunPath} "${executablePath}"`
const bunInstallPath = path.join(os.homedir(), '.cherrystudio') const bunInstallPath = path.join(os.homedir(), '.cherrystudio')
if (isInstalled) { if (isInstalled) {
// If already installed, run executable directly (with optional update message) // If already installed, run executable directly (with optional update message)
baseCommand = `"${executablePath}"`
if (updateMessage) { if (updateMessage) {
baseCommand = `echo "Checking ${cliTool} version..."${updateMessage} && ${baseCommand}` baseCommand = `echo "Checking ${cliTool} version..."${updateMessage} && ${baseCommand}`
} }
@ -301,7 +300,7 @@ class CodeToolsService {
: `export BUN_INSTALL="${bunInstallPath}" && export NPM_CONFIG_REGISTRY="${registryUrl}" &&` : `export BUN_INSTALL="${bunInstallPath}" && export NPM_CONFIG_REGISTRY="${registryUrl}" &&`
const installCommand = `${installEnvPrefix} "${bunPath}" install -g ${packageName}` const installCommand = `${installEnvPrefix} "${bunPath}" install -g ${packageName}`
baseCommand = `echo "Installing ${packageName}..." && ${installCommand} && echo "Installation complete, starting ${cliTool}..." && "${executablePath}"` baseCommand = `echo "Installing ${packageName}..." && ${installCommand} && echo "Installation complete, starting ${cliTool}..." && "${baseCommand}"`
} }
switch (platform) { switch (platform) {

View File

@ -6,6 +6,7 @@ import {
removeDirectory, removeDirectory,
resetCodeTools, resetCodeTools,
setCurrentDirectory, setCurrentDirectory,
setEnvironmentVariables,
setSelectedCliTool, setSelectedCliTool,
setSelectedModel setSelectedModel
} from '@renderer/store/codeTools' } from '@renderer/store/codeTools'
@ -33,6 +34,14 @@ export const useCodeTools = () => {
[dispatch] [dispatch]
) )
// 设置环境变量
const setEnvVars = useCallback(
(envVars: string) => {
dispatch(setEnvironmentVariables(envVars))
},
[dispatch]
)
// 添加目录 // 添加目录
const addDir = useCallback( const addDir = useCallback(
(directory: string) => { (directory: string) => {
@ -85,6 +94,9 @@ export const useCodeTools = () => {
// 获取当前CLI工具选择的模型 // 获取当前CLI工具选择的模型
const selectedModel = codeToolsState.selectedModels[codeToolsState.selectedCliTool] || null const selectedModel = codeToolsState.selectedModels[codeToolsState.selectedCliTool] || null
// 获取当前CLI工具的环境变量
const environmentVariables = codeToolsState?.environmentVariables?.[codeToolsState.selectedCliTool] || ''
// 检查是否可以启动(所有必需字段都已填写) // 检查是否可以启动(所有必需字段都已填写)
const canLaunch = Boolean(codeToolsState.selectedCliTool && selectedModel && codeToolsState.currentDirectory) const canLaunch = Boolean(codeToolsState.selectedCliTool && selectedModel && codeToolsState.currentDirectory)
@ -92,6 +104,7 @@ export const useCodeTools = () => {
// 状态 // 状态
selectedCliTool: codeToolsState.selectedCliTool, selectedCliTool: codeToolsState.selectedCliTool,
selectedModel: selectedModel, selectedModel: selectedModel,
environmentVariables: environmentVariables,
directories: codeToolsState.directories, directories: codeToolsState.directories,
currentDirectory: codeToolsState.currentDirectory, currentDirectory: codeToolsState.currentDirectory,
canLaunch, canLaunch,
@ -99,6 +112,7 @@ export const useCodeTools = () => {
// 操作函数 // 操作函数
setCliTool, setCliTool,
setModel, setModel,
setEnvVars,
addDir, addDir,
removeDir, removeDir,
setCurrentDir, setCurrentDir,

View File

@ -654,6 +654,8 @@
"cli_tool": "CLI Tool", "cli_tool": "CLI Tool",
"cli_tool_placeholder": "Select the CLI tool to use", "cli_tool_placeholder": "Select the CLI tool to use",
"description": "Quickly launch multiple code CLI tools to improve development efficiency", "description": "Quickly launch multiple code CLI tools to improve development efficiency",
"env_vars_help": "Enter custom environment variables (one per line, format: KEY=value)",
"environment_variables": "Environment Variables",
"folder_placeholder": "Select working directory", "folder_placeholder": "Select working directory",
"install_bun": "Install Bun", "install_bun": "Install Bun",
"installing_bun": "Installing...", "installing_bun": "Installing...",

View File

@ -654,6 +654,8 @@
"cli_tool": "CLI ツール", "cli_tool": "CLI ツール",
"cli_tool_placeholder": "使用する CLI ツールを選択してください", "cli_tool_placeholder": "使用する CLI ツールを選択してください",
"description": "開発効率を向上させるために、複数のコード CLI ツールを迅速に起動します", "description": "開発効率を向上させるために、複数のコード CLI ツールを迅速に起動します",
"env_vars_help": "環境変数を設定して、CLI ツールの実行時に使用します。各変数は 1 行ごとに設定してください。",
"environment_variables": "環境変数",
"folder_placeholder": "作業ディレクトリを選択してください", "folder_placeholder": "作業ディレクトリを選択してください",
"install_bun": "Bun をインストール", "install_bun": "Bun をインストール",
"installing_bun": "インストール中...", "installing_bun": "インストール中...",

View File

@ -654,6 +654,8 @@
"cli_tool": "Инструмент", "cli_tool": "Инструмент",
"cli_tool_placeholder": "Выберите CLI-инструмент для использования", "cli_tool_placeholder": "Выберите CLI-инструмент для использования",
"description": "Быстро запускает несколько CLI-инструментов для кода, повышая эффективность разработки", "description": "Быстро запускает несколько CLI-инструментов для кода, повышая эффективность разработки",
"env_vars_help": "Установите переменные окружения для использования при запуске CLI-инструментов. Каждая переменная должна быть на отдельной строке в формате KEY=value",
"environment_variables": "Переменные окружения",
"folder_placeholder": "Выберите рабочую директорию", "folder_placeholder": "Выберите рабочую директорию",
"install_bun": "Установить Bun", "install_bun": "Установить Bun",
"installing_bun": "Установка...", "installing_bun": "Установка...",

View File

@ -654,6 +654,8 @@
"cli_tool": "CLI 工具", "cli_tool": "CLI 工具",
"cli_tool_placeholder": "选择要使用的 CLI 工具", "cli_tool_placeholder": "选择要使用的 CLI 工具",
"description": "快速启动多个代码 CLI 工具,提高开发效率", "description": "快速启动多个代码 CLI 工具,提高开发效率",
"env_vars_help": "输入自定义环境变量每行一个格式KEY=value",
"environment_variables": "环境变量",
"folder_placeholder": "选择工作目录", "folder_placeholder": "选择工作目录",
"install_bun": "安装 Bun", "install_bun": "安装 Bun",
"installing_bun": "安装中...", "installing_bun": "安装中...",

View File

@ -654,6 +654,8 @@
"cli_tool": "CLI 工具", "cli_tool": "CLI 工具",
"cli_tool_placeholder": "選擇要使用的 CLI 工具", "cli_tool_placeholder": "選擇要使用的 CLI 工具",
"description": "快速啟動多個程式碼 CLI 工具,提高開發效率", "description": "快速啟動多個程式碼 CLI 工具,提高開發效率",
"env_vars_help": "輸入自定義環境變數每行一個格式KEY=value",
"environment_variables": "環境變數",
"folder_placeholder": "選擇工作目錄", "folder_placeholder": "選擇工作目錄",
"install_bun": "安裝 Bun", "install_bun": "安裝 Bun",
"installing_bun": "安裝中...", "installing_bun": "安裝中...",

View File

@ -10,7 +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 { Alert, Button, Checkbox, 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'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -35,11 +35,13 @@ const CodeToolsPage: FC = () => {
const { const {
selectedCliTool, selectedCliTool,
selectedModel, selectedModel,
environmentVariables,
directories, directories,
currentDirectory, currentDirectory,
canLaunch, canLaunch,
setCliTool, setCliTool,
setModel, setModel,
setEnvVars,
setCurrentDir, setCurrentDir,
removeDir, removeDir,
selectFolder selectFolder
@ -100,6 +102,11 @@ const CodeToolsPage: FC = () => {
} }
} }
// 处理环境变量更改
const handleEnvVarsChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setEnvVars(e.target.value)
}
// 处理文件夹选择 // 处理文件夹选择
const handleFolderSelect = async () => { const handleFolderSelect = async () => {
try { try {
@ -214,6 +221,22 @@ const CodeToolsPage: FC = () => {
} }
} }
// 解析用户自定义的环境变量
if (environmentVariables) {
const lines = environmentVariables.split('\n')
for (const line of lines) {
const trimmedLine = line.trim()
if (trimmedLine && trimmedLine.includes('=')) {
const [key, ...valueParts] = trimmedLine.split('=')
const trimmedKey = key.trim()
const value = valueParts.join('=').trim()
if (trimmedKey) {
env[trimmedKey] = value
}
}
}
}
try { try {
// 这里可以添加实际的启动逻辑 // 这里可以添加实际的启动逻辑
logger.info('启动配置:', { logger.info('启动配置:', {
@ -340,6 +363,18 @@ const CodeToolsPage: FC = () => {
</Space.Compact> </Space.Compact>
</SettingsItem> </SettingsItem>
<SettingsItem>
<div className="settings-label">{t('code.environment_variables')}</div>
<Input.TextArea
placeholder={`KEY1=value1\nKEY2=value2`}
value={environmentVariables}
onChange={handleEnvVarsChange}
rows={2}
style={{ fontFamily: 'monospace' }}
/>
<div style={{ fontSize: 12, color: 'var(--color-text-3)', marginTop: 4 }}>{t('code.env_vars_help')}</div>
</SettingsItem>
<SettingsItem> <SettingsItem>
<div className="settings-label">{t('code.update_options')}</div> <div className="settings-label">{t('code.update_options')}</div>
<Checkbox checked={autoUpdateToLatest} onChange={(e) => setAutoUpdateToLatest(e.target.checked)}> <Checkbox checked={autoUpdateToLatest} onChange={(e) => setAutoUpdateToLatest(e.target.checked)}>

View File

@ -9,6 +9,8 @@ export interface CodeToolsState {
selectedCliTool: string selectedCliTool: string
// 为每个 CLI 工具单独保存选择的模型 // 为每个 CLI 工具单独保存选择的模型
selectedModels: Record<string, Model | null> selectedModels: Record<string, Model | null>
// 为每个 CLI 工具单独保存环境变量
environmentVariables: Record<string, string>
// 记录用户选择过的所有目录,支持增删 // 记录用户选择过的所有目录,支持增删
directories: string[] directories: string[]
// 当前选择的目录 // 当前选择的目录
@ -22,6 +24,11 @@ export const initialState: CodeToolsState = {
'claude-code': null, 'claude-code': null,
'gemini-cli': null 'gemini-cli': null
}, },
environmentVariables: {
'qwen-code': '',
'claude-code': '',
'gemini-cli': ''
},
directories: [], directories: [],
currentDirectory: '' currentDirectory: ''
} }
@ -40,6 +47,18 @@ const codeToolsSlice = createSlice({
state.selectedModels[state.selectedCliTool] = action.payload state.selectedModels[state.selectedCliTool] = action.payload
}, },
// 设置环境变量(为当前 CLI 工具设置)
setEnvironmentVariables: (state, action: PayloadAction<string>) => {
if (!state.environmentVariables) {
state.environmentVariables = {
'qwen-code': '',
'claude-code': '',
'gemini-cli': ''
}
}
state.environmentVariables[state.selectedCliTool] = action.payload
},
// 添加目录到列表中 // 添加目录到列表中
addDirectory: (state, action: PayloadAction<string>) => { addDirectory: (state, action: PayloadAction<string>) => {
const directory = action.payload const directory = action.payload
@ -87,14 +106,11 @@ const codeToolsSlice = createSlice({
// 重置所有设置 // 重置所有设置
resetCodeTools: (state) => { resetCodeTools: (state) => {
state.selectedCliTool = 'qwen-code' state.selectedCliTool = initialState.selectedCliTool
state.selectedModels = { state.selectedModels = initialState.selectedModels
'qwen-code': null, state.environmentVariables = initialState.environmentVariables
'claude-code': null, state.directories = initialState.directories
'gemini-cli': null state.currentDirectory = initialState.currentDirectory
}
state.directories = []
state.currentDirectory = ''
} }
} }
}) })
@ -102,6 +118,7 @@ const codeToolsSlice = createSlice({
export const { export const {
setSelectedCliTool, setSelectedCliTool,
setSelectedModel, setSelectedModel,
setEnvironmentVariables,
addDirectory, addDirectory,
removeDirectory, removeDirectory,
setCurrentDirectory, setCurrentDirectory,

View File

@ -2123,6 +2123,13 @@ const migrateConfig = {
'133': (state: RootState) => { '133': (state: RootState) => {
try { try {
state.settings.sidebarIcons.visible.push('code_tools') state.settings.sidebarIcons.visible.push('code_tools')
if (state.codeTools) {
state.codeTools.environmentVariables = {
'qwen-code': '',
'claude-code': '',
'gemini-cli': ''
}
}
return state return state
} catch (error) { } catch (error) {
logger.error('migrate 133 error', error as Error) logger.error('migrate 133 error', error as Error)