mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 06:30:10 +08:00
feat: Support custom git bash path (#11813)
* feat: allow custom Git Bash path for Claude Code Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * format code * format code * update i18n * fix: correct Git Bash invalid path translation key Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * test: cover null inputs for validateGitBashPath Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * refactor: rely on findGitBash for env override check Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: validate env override for Git Bash path Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * chore: align Git Bash path getter with platform guard Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * test: cover env override behavior in findGitBash Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * refactor: unify Git Bash path detection logic - Add customPath parameter to findGitBash() for config-based paths - Simplify checkGitBash IPC handler by delegating to findGitBash - Change validateGitBashPath success log level from info to debug - Only show success Alert when custom path is configured - Add tests for customPath parameter priority handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
8cd4b1b747
commit
ed695a8620
@ -241,6 +241,8 @@ export enum IpcChannel {
|
||||
System_GetHostname = 'system:getHostname',
|
||||
System_GetCpuName = 'system:getCpuName',
|
||||
System_CheckGitBash = 'system:checkGitBash',
|
||||
System_GetGitBashPath = 'system:getGitBashPath',
|
||||
System_SetGitBashPath = 'system:setGitBashPath',
|
||||
|
||||
// DevTools
|
||||
System_ToggleDevTools = 'system:toggleDevTools',
|
||||
|
||||
@ -6,7 +6,7 @@ import { loggerService } from '@logger'
|
||||
import { isLinux, isMac, isPortable, isWin } from '@main/constant'
|
||||
import { generateSignature } from '@main/integration/cherryai'
|
||||
import anthropicService from '@main/services/AnthropicService'
|
||||
import { findGitBash, getBinaryPath, isBinaryExists, runInstallScript } from '@main/utils/process'
|
||||
import { findGitBash, getBinaryPath, isBinaryExists, runInstallScript, validateGitBashPath } from '@main/utils/process'
|
||||
import { handleZoomFactor } from '@main/utils/zoom'
|
||||
import type { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
|
||||
import type { UpgradeChannel } from '@shared/config/constant'
|
||||
@ -35,7 +35,7 @@ import appService from './services/AppService'
|
||||
import AppUpdater from './services/AppUpdater'
|
||||
import BackupManager from './services/BackupManager'
|
||||
import { codeToolsService } from './services/CodeToolsService'
|
||||
import { configManager } from './services/ConfigManager'
|
||||
import { ConfigKeys, configManager } from './services/ConfigManager'
|
||||
import CopilotService from './services/CopilotService'
|
||||
import DxtService from './services/DxtService'
|
||||
import { ExportService } from './services/ExportService'
|
||||
@ -499,7 +499,8 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
}
|
||||
|
||||
try {
|
||||
const bashPath = findGitBash()
|
||||
const customPath = configManager.get(ConfigKeys.GitBashPath) as string | undefined
|
||||
const bashPath = findGitBash(customPath)
|
||||
|
||||
if (bashPath) {
|
||||
logger.info('Git Bash is available', { path: bashPath })
|
||||
@ -513,6 +514,35 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle(IpcChannel.System_GetGitBashPath, () => {
|
||||
if (!isWin) {
|
||||
return null
|
||||
}
|
||||
|
||||
const customPath = configManager.get(ConfigKeys.GitBashPath) as string | undefined
|
||||
return customPath ?? null
|
||||
})
|
||||
|
||||
ipcMain.handle(IpcChannel.System_SetGitBashPath, (_, newPath: string | null) => {
|
||||
if (!isWin) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!newPath) {
|
||||
configManager.set(ConfigKeys.GitBashPath, null)
|
||||
return true
|
||||
}
|
||||
|
||||
const validated = validateGitBashPath(newPath)
|
||||
if (!validated) {
|
||||
return false
|
||||
}
|
||||
|
||||
configManager.set(ConfigKeys.GitBashPath, validated)
|
||||
return true
|
||||
})
|
||||
|
||||
ipcMain.handle(IpcChannel.System_ToggleDevTools, (e) => {
|
||||
const win = BrowserWindow.fromWebContents(e.sender)
|
||||
win && win.webContents.toggleDevTools()
|
||||
|
||||
@ -31,7 +31,8 @@ export enum ConfigKeys {
|
||||
DisableHardwareAcceleration = 'disableHardwareAcceleration',
|
||||
Proxy = 'proxy',
|
||||
EnableDeveloperMode = 'enableDeveloperMode',
|
||||
ClientId = 'clientId'
|
||||
ClientId = 'clientId',
|
||||
GitBashPath = 'gitBashPath'
|
||||
}
|
||||
|
||||
export class ConfigManager {
|
||||
|
||||
@ -15,6 +15,8 @@ import { query } from '@anthropic-ai/claude-agent-sdk'
|
||||
import { loggerService } from '@logger'
|
||||
import { config as apiConfigService } from '@main/apiServer/config'
|
||||
import { validateModelId } from '@main/apiServer/utils'
|
||||
import { ConfigKeys, configManager } from '@main/services/ConfigManager'
|
||||
import { validateGitBashPath } from '@main/utils/process'
|
||||
import getLoginShellEnvironment from '@main/utils/shell-env'
|
||||
import { app } from 'electron'
|
||||
|
||||
@ -107,6 +109,8 @@ class ClaudeCodeService implements AgentServiceInterface {
|
||||
Object.entries(loginShellEnv).filter(([key]) => !key.toLowerCase().endsWith('_proxy'))
|
||||
) as Record<string, string>
|
||||
|
||||
const customGitBashPath = validateGitBashPath(configManager.get(ConfigKeys.GitBashPath) as string | undefined)
|
||||
|
||||
const env = {
|
||||
...loginShellEnvWithoutProxies,
|
||||
// TODO: fix the proxy api server
|
||||
@ -126,7 +130,8 @@ class ClaudeCodeService implements AgentServiceInterface {
|
||||
// Set CLAUDE_CONFIG_DIR to app's userData directory to avoid path encoding issues
|
||||
// on Windows when the username contains non-ASCII characters (e.g., Chinese characters)
|
||||
// This prevents the SDK from using the user's home directory which may have encoding problems
|
||||
CLAUDE_CONFIG_DIR: path.join(app.getPath('userData'), '.claude')
|
||||
CLAUDE_CONFIG_DIR: path.join(app.getPath('userData'), '.claude'),
|
||||
...(customGitBashPath ? { CLAUDE_CODE_GIT_BASH_PATH: customGitBashPath } : {})
|
||||
}
|
||||
|
||||
const errorChunks: string[] = []
|
||||
|
||||
@ -3,7 +3,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { findExecutable, findGitBash } from '../process'
|
||||
import { findExecutable, findGitBash, validateGitBashPath } from '../process'
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('child_process')
|
||||
@ -289,7 +289,133 @@ describe.skipIf(process.platform !== 'win32')('process utilities', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateGitBashPath', () => {
|
||||
it('returns null when path is null', () => {
|
||||
const result = validateGitBashPath(null)
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null when path is undefined', () => {
|
||||
const result = validateGitBashPath(undefined)
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it('returns normalized path when valid bash.exe exists', () => {
|
||||
const customPath = 'C:\\PortableGit\\bin\\bash.exe'
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => p === 'C:\\PortableGit\\bin\\bash.exe')
|
||||
|
||||
const result = validateGitBashPath(customPath)
|
||||
|
||||
expect(result).toBe('C:\\PortableGit\\bin\\bash.exe')
|
||||
})
|
||||
|
||||
it('returns null when file does not exist', () => {
|
||||
vi.mocked(fs.existsSync).mockReturnValue(false)
|
||||
|
||||
const result = validateGitBashPath('C:\\missing\\bash.exe')
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null when path is not bash.exe', () => {
|
||||
const customPath = 'C:\\PortableGit\\bin\\git.exe'
|
||||
vi.mocked(fs.existsSync).mockReturnValue(true)
|
||||
|
||||
const result = validateGitBashPath(customPath)
|
||||
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('findGitBash', () => {
|
||||
describe('customPath parameter', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.CLAUDE_CODE_GIT_BASH_PATH
|
||||
})
|
||||
|
||||
it('uses customPath when valid', () => {
|
||||
const customPath = 'C:\\CustomGit\\bin\\bash.exe'
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => p === customPath)
|
||||
|
||||
const result = findGitBash(customPath)
|
||||
|
||||
expect(result).toBe(customPath)
|
||||
expect(execFileSync).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('falls back when customPath is invalid', () => {
|
||||
const customPath = 'C:\\Invalid\\bash.exe'
|
||||
const gitPath = 'C:\\Program Files\\Git\\cmd\\git.exe'
|
||||
const bashPath = 'C:\\Program Files\\Git\\bin\\bash.exe'
|
||||
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => {
|
||||
if (p === customPath) return false
|
||||
if (p === gitPath) return true
|
||||
if (p === bashPath) return true
|
||||
return false
|
||||
})
|
||||
|
||||
vi.mocked(execFileSync).mockReturnValue(gitPath)
|
||||
|
||||
const result = findGitBash(customPath)
|
||||
|
||||
expect(result).toBe(bashPath)
|
||||
})
|
||||
|
||||
it('prioritizes customPath over env override', () => {
|
||||
const customPath = 'C:\\CustomGit\\bin\\bash.exe'
|
||||
const envPath = 'C:\\EnvGit\\bin\\bash.exe'
|
||||
process.env.CLAUDE_CODE_GIT_BASH_PATH = envPath
|
||||
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => p === customPath || p === envPath)
|
||||
|
||||
const result = findGitBash(customPath)
|
||||
|
||||
expect(result).toBe(customPath)
|
||||
})
|
||||
})
|
||||
|
||||
describe('env override', () => {
|
||||
beforeEach(() => {
|
||||
delete process.env.CLAUDE_CODE_GIT_BASH_PATH
|
||||
})
|
||||
|
||||
it('uses CLAUDE_CODE_GIT_BASH_PATH when valid', () => {
|
||||
const envPath = 'C:\\OverrideGit\\bin\\bash.exe'
|
||||
process.env.CLAUDE_CODE_GIT_BASH_PATH = envPath
|
||||
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => p === envPath)
|
||||
|
||||
const result = findGitBash()
|
||||
|
||||
expect(result).toBe(envPath)
|
||||
expect(execFileSync).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('falls back when CLAUDE_CODE_GIT_BASH_PATH is invalid', () => {
|
||||
const envPath = 'C:\\Invalid\\bash.exe'
|
||||
const gitPath = 'C:\\Program Files\\Git\\cmd\\git.exe'
|
||||
const bashPath = 'C:\\Program Files\\Git\\bin\\bash.exe'
|
||||
|
||||
process.env.CLAUDE_CODE_GIT_BASH_PATH = envPath
|
||||
|
||||
vi.mocked(fs.existsSync).mockImplementation((p) => {
|
||||
if (p === envPath) return false
|
||||
if (p === gitPath) return true
|
||||
if (p === bashPath) return true
|
||||
return false
|
||||
})
|
||||
|
||||
vi.mocked(execFileSync).mockReturnValue(gitPath)
|
||||
|
||||
const result = findGitBash()
|
||||
|
||||
expect(result).toBe(bashPath)
|
||||
})
|
||||
})
|
||||
|
||||
describe('git.exe path derivation', () => {
|
||||
it('should derive bash.exe from standard Git installation (Git/cmd/git.exe)', () => {
|
||||
const gitPath = 'C:\\Program Files\\Git\\cmd\\git.exe'
|
||||
|
||||
@ -131,15 +131,37 @@ export function findExecutable(name: string): string | null {
|
||||
|
||||
/**
|
||||
* Find Git Bash executable on Windows
|
||||
* @param customPath - Optional custom path from config
|
||||
* @returns Full path to bash.exe or null if not found
|
||||
*/
|
||||
export function findGitBash(): string | null {
|
||||
export function findGitBash(customPath?: string | null): string | null {
|
||||
// Git Bash is Windows-only
|
||||
if (!isWin) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 1. Find git.exe and derive bash.exe path
|
||||
// 1. Check custom path from config first
|
||||
if (customPath) {
|
||||
const validated = validateGitBashPath(customPath)
|
||||
if (validated) {
|
||||
logger.debug('Using custom Git Bash path from config', { path: validated })
|
||||
return validated
|
||||
}
|
||||
logger.warn('Custom Git Bash path provided but invalid', { path: customPath })
|
||||
}
|
||||
|
||||
// 2. Check environment variable override
|
||||
const envOverride = process.env.CLAUDE_CODE_GIT_BASH_PATH
|
||||
if (envOverride) {
|
||||
const validated = validateGitBashPath(envOverride)
|
||||
if (validated) {
|
||||
logger.debug('Using CLAUDE_CODE_GIT_BASH_PATH override for bash.exe', { path: validated })
|
||||
return validated
|
||||
}
|
||||
logger.warn('CLAUDE_CODE_GIT_BASH_PATH provided but path is invalid', { path: envOverride })
|
||||
}
|
||||
|
||||
// 3. Find git.exe and derive bash.exe path
|
||||
const gitPath = findExecutable('git')
|
||||
if (gitPath) {
|
||||
// Try multiple possible locations for bash.exe relative to git.exe
|
||||
@ -164,7 +186,7 @@ export function findGitBash(): string | null {
|
||||
})
|
||||
}
|
||||
|
||||
// 2. Fallback: check common Git Bash paths directly
|
||||
// 4. Fallback: check common Git Bash paths directly
|
||||
const commonBashPaths = [
|
||||
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'),
|
||||
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'),
|
||||
@ -181,3 +203,25 @@ export function findGitBash(): string | null {
|
||||
logger.debug('Git Bash not found - checked git derivation and common paths')
|
||||
return null
|
||||
}
|
||||
|
||||
export function validateGitBashPath(customPath?: string | null): string | null {
|
||||
if (!customPath) {
|
||||
return null
|
||||
}
|
||||
|
||||
const resolved = path.resolve(customPath)
|
||||
|
||||
if (!fs.existsSync(resolved)) {
|
||||
logger.warn('Custom Git Bash path does not exist', { path: resolved })
|
||||
return null
|
||||
}
|
||||
|
||||
const isExe = resolved.toLowerCase().endsWith('bash.exe')
|
||||
if (!isExe) {
|
||||
logger.warn('Custom Git Bash path is not bash.exe', { path: resolved })
|
||||
return null
|
||||
}
|
||||
|
||||
logger.debug('Validated custom Git Bash path', { path: resolved })
|
||||
return resolved
|
||||
}
|
||||
|
||||
@ -124,7 +124,10 @@ const api = {
|
||||
getDeviceType: () => ipcRenderer.invoke(IpcChannel.System_GetDeviceType),
|
||||
getHostname: () => ipcRenderer.invoke(IpcChannel.System_GetHostname),
|
||||
getCpuName: () => ipcRenderer.invoke(IpcChannel.System_GetCpuName),
|
||||
checkGitBash: (): Promise<boolean> => ipcRenderer.invoke(IpcChannel.System_CheckGitBash)
|
||||
checkGitBash: (): Promise<boolean> => ipcRenderer.invoke(IpcChannel.System_CheckGitBash),
|
||||
getGitBashPath: (): Promise<string | null> => ipcRenderer.invoke(IpcChannel.System_GetGitBashPath),
|
||||
setGitBashPath: (newPath: string | null): Promise<boolean> =>
|
||||
ipcRenderer.invoke(IpcChannel.System_SetGitBashPath, newPath)
|
||||
},
|
||||
devTools: {
|
||||
toggle: () => ipcRenderer.invoke(IpcChannel.System_ToggleDevTools)
|
||||
|
||||
@ -60,6 +60,7 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
||||
|
||||
const [form, setForm] = useState<BaseAgentForm>(() => buildAgentForm(agent))
|
||||
const [hasGitBash, setHasGitBash] = useState<boolean>(true)
|
||||
const [customGitBashPath, setCustomGitBashPath] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
@ -70,7 +71,11 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
||||
const checkGitBash = useCallback(
|
||||
async (showToast = false) => {
|
||||
try {
|
||||
const gitBashInstalled = await window.api.system.checkGitBash()
|
||||
const [gitBashInstalled, savedPath] = await Promise.all([
|
||||
window.api.system.checkGitBash(),
|
||||
window.api.system.getGitBashPath().catch(() => null)
|
||||
])
|
||||
setCustomGitBashPath(savedPath ?? '')
|
||||
setHasGitBash(gitBashInstalled)
|
||||
if (showToast) {
|
||||
if (gitBashInstalled) {
|
||||
@ -93,6 +98,46 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
||||
|
||||
const selectedPermissionMode = form.configuration?.permission_mode ?? 'default'
|
||||
|
||||
const handlePickGitBash = useCallback(async () => {
|
||||
try {
|
||||
const selected = await window.api.file.select({
|
||||
title: t('agent.gitBash.pick.title', 'Select Git Bash executable'),
|
||||
filters: [{ name: 'Executable', extensions: ['exe'] }],
|
||||
properties: ['openFile']
|
||||
})
|
||||
|
||||
if (!selected || selected.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const pickedPath = selected[0].path
|
||||
const ok = await window.api.system.setGitBashPath(pickedPath)
|
||||
if (!ok) {
|
||||
window.toast.error(
|
||||
t('agent.gitBash.pick.invalidPath', 'Selected file is not a valid Git Bash executable (bash.exe).')
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
setCustomGitBashPath(pickedPath)
|
||||
await checkGitBash(true)
|
||||
} catch (error) {
|
||||
logger.error('Failed to pick Git Bash path', error as Error)
|
||||
window.toast.error(t('agent.gitBash.pick.failed', 'Failed to set Git Bash path'))
|
||||
}
|
||||
}, [checkGitBash, t])
|
||||
|
||||
const handleClearGitBash = useCallback(async () => {
|
||||
try {
|
||||
await window.api.system.setGitBashPath(null)
|
||||
setCustomGitBashPath('')
|
||||
await checkGitBash(true)
|
||||
} catch (error) {
|
||||
logger.error('Failed to clear Git Bash path', error as Error)
|
||||
window.toast.error(t('agent.gitBash.pick.failed', 'Failed to set Git Bash path'))
|
||||
}
|
||||
}, [checkGitBash, t])
|
||||
|
||||
const onPermissionModeChange = useCallback((value: PermissionMode) => {
|
||||
setForm((prev) => {
|
||||
const parsedConfiguration = AgentConfigurationSchema.parse(prev.configuration ?? {})
|
||||
@ -324,6 +369,9 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
||||
<Button size="small" onClick={() => checkGitBash(true)}>
|
||||
{t('agent.gitBash.error.recheck', 'Recheck Git Bash Installation')}
|
||||
</Button>
|
||||
<Button size="small" style={{ marginLeft: 8 }} onClick={handlePickGitBash}>
|
||||
{t('agent.gitBash.pick.button', 'Select Git Bash Path')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
type="error"
|
||||
@ -331,6 +379,33 @@ const PopupContainer: React.FC<Props> = ({ agent, afterSubmit, resolve }) => {
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{hasGitBash && customGitBashPath && (
|
||||
<Alert
|
||||
message={t('agent.gitBash.found.title', 'Git Bash configured')}
|
||||
description={
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<div>
|
||||
{t('agent.gitBash.customPath', {
|
||||
defaultValue: 'Using custom path: {{path}}',
|
||||
path: customGitBashPath
|
||||
})}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<Button size="small" onClick={handlePickGitBash}>
|
||||
{t('agent.gitBash.pick.button', 'Select Git Bash Path')}
|
||||
</Button>
|
||||
<Button size="small" onClick={handleClearGitBash}>
|
||||
{t('agent.gitBash.clear.button', 'Clear custom path')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
type="success"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
)}
|
||||
<FormRow>
|
||||
<FormItem style={{ flex: 1 }}>
|
||||
<Label>
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Using auto-detected Git Bash",
|
||||
"clear": {
|
||||
"button": "Clear custom path"
|
||||
},
|
||||
"customPath": "Using custom path: {{path}}",
|
||||
"error": {
|
||||
"description": "Git Bash is required to run agents on Windows. The agent cannot function without it. Please install Git for Windows from",
|
||||
"recheck": "Recheck Git Bash Installation",
|
||||
"title": "Git Bash Required"
|
||||
},
|
||||
"found": {
|
||||
"title": "Git Bash configured"
|
||||
},
|
||||
"notFound": "Git Bash not found. Please install it first.",
|
||||
"pick": {
|
||||
"button": "Select Git Bash Path",
|
||||
"failed": "Failed to set Git Bash path",
|
||||
"invalidPath": "Selected file is not a valid Git Bash executable (bash.exe).",
|
||||
"title": "Select Git Bash executable"
|
||||
},
|
||||
"success": "Git Bash detected successfully!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "使用自动检测的 Git Bash",
|
||||
"clear": {
|
||||
"button": "清除自定义路径"
|
||||
},
|
||||
"customPath": "使用自定义路径:{{path}}",
|
||||
"error": {
|
||||
"description": "在 Windows 上运行智能体需要 Git Bash。没有它智能体无法运行。请从以下地址安装 Git for Windows",
|
||||
"recheck": "重新检测 Git Bash 安装",
|
||||
"title": "需要 Git Bash"
|
||||
},
|
||||
"found": {
|
||||
"title": "已配置 Git Bash"
|
||||
},
|
||||
"notFound": "未找到 Git Bash。请先安装。",
|
||||
"pick": {
|
||||
"button": "选择 Git Bash 路径",
|
||||
"failed": "设置 Git Bash 路径失败",
|
||||
"invalidPath": "选择的文件不是有效的 Git Bash 可执行文件(bash.exe)。",
|
||||
"title": "选择 Git Bash 可执行文件"
|
||||
},
|
||||
"success": "成功检测到 Git Bash!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "使用自動偵測的 Git Bash",
|
||||
"clear": {
|
||||
"button": "清除自訂路徑"
|
||||
},
|
||||
"customPath": "使用自訂路徑:{{path}}",
|
||||
"error": {
|
||||
"description": "在 Windows 上執行代理程式需要 Git Bash。沒有它代理程式無法運作。請從以下地址安裝 Git for Windows",
|
||||
"recheck": "重新檢測 Git Bash 安裝",
|
||||
"title": "需要 Git Bash"
|
||||
},
|
||||
"found": {
|
||||
"title": "已配置 Git Bash"
|
||||
},
|
||||
"notFound": "找不到 Git Bash。請先安裝。",
|
||||
"pick": {
|
||||
"button": "選擇 Git Bash 路徑",
|
||||
"failed": "設定 Git Bash 路徑失敗",
|
||||
"invalidPath": "選擇的檔案不是有效的 Git Bash 可執行檔(bash.exe)。",
|
||||
"title": "選擇 Git Bash 可執行檔"
|
||||
},
|
||||
"success": "成功偵測到 Git Bash!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Automatisch ermitteltes Git Bash wird verwendet",
|
||||
"clear": {
|
||||
"button": "Benutzerdefinierten Pfad löschen"
|
||||
},
|
||||
"customPath": "Benutzerdefinierter Pfad: {{path}}",
|
||||
"error": {
|
||||
"description": "Git Bash ist erforderlich, um Agents unter Windows auszuführen. Der Agent kann ohne es nicht funktionieren. Bitte installieren Sie Git für Windows von",
|
||||
"recheck": "Überprüfe die Git Bash-Installation erneut",
|
||||
"title": "Git Bash erforderlich"
|
||||
},
|
||||
"found": {
|
||||
"title": "Git Bash konfiguriert"
|
||||
},
|
||||
"notFound": "Git Bash nicht gefunden. Bitte installieren Sie es zuerst.",
|
||||
"pick": {
|
||||
"button": "Git Bash Pfad auswählen",
|
||||
"failed": "Git Bash Pfad konnte nicht gesetzt werden",
|
||||
"invalidPath": "Die ausgewählte Datei ist keine gültige Git Bash ausführbare Datei (bash.exe).",
|
||||
"title": "Git Bash ausführbare Datei auswählen"
|
||||
},
|
||||
"success": "Git Bash erfolgreich erkannt!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "[to be translated]:Using auto-detected Git Bash",
|
||||
"clear": {
|
||||
"button": "[to be translated]:Clear custom path"
|
||||
},
|
||||
"customPath": "[to be translated]:Using custom path: {{path}}",
|
||||
"error": {
|
||||
"description": "Το Git Bash απαιτείται για την εκτέλεση πρακτόρων στα Windows. Ο πράκτορας δεν μπορεί να λειτουργήσει χωρίς αυτό. Παρακαλούμε εγκαταστήστε το Git για Windows από",
|
||||
"recheck": "Επανέλεγχος Εγκατάστασης του Git Bash",
|
||||
"title": "Απαιτείται Git Bash"
|
||||
},
|
||||
"found": {
|
||||
"title": "[to be translated]:Git Bash configured"
|
||||
},
|
||||
"notFound": "Το Git Bash δεν βρέθηκε. Παρακαλώ εγκαταστήστε το πρώτα.",
|
||||
"pick": {
|
||||
"button": "[to be translated]:Select Git Bash Path",
|
||||
"failed": "[to be translated]:Failed to set Git Bash path",
|
||||
"invalidPath": "[to be translated]:Selected file is not a valid Git Bash executable (bash.exe).",
|
||||
"title": "[to be translated]:Select Git Bash executable"
|
||||
},
|
||||
"success": "Το Git Bash εντοπίστηκε με επιτυχία!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Usando Git Bash detectado automáticamente",
|
||||
"clear": {
|
||||
"button": "Borrar ruta personalizada"
|
||||
},
|
||||
"customPath": "Usando ruta personalizada: {{path}}",
|
||||
"error": {
|
||||
"description": "Se requiere Git Bash para ejecutar agentes en Windows. El agente no puede funcionar sin él. Instale Git para Windows desde",
|
||||
"recheck": "Volver a verificar la instalación de Git Bash",
|
||||
"title": "Git Bash Requerido"
|
||||
},
|
||||
"found": {
|
||||
"title": "Git Bash configurado"
|
||||
},
|
||||
"notFound": "Git Bash no encontrado. Por favor, instálalo primero.",
|
||||
"pick": {
|
||||
"button": "Seleccionar ruta de Git Bash",
|
||||
"failed": "No se pudo configurar la ruta de Git Bash",
|
||||
"invalidPath": "El archivo seleccionado no es un ejecutable válido de Git Bash (bash.exe).",
|
||||
"title": "Seleccionar ejecutable de Git Bash"
|
||||
},
|
||||
"success": "¡Git Bash detectado con éxito!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Utilisation de Git Bash détecté automatiquement",
|
||||
"clear": {
|
||||
"button": "Effacer le chemin personnalisé"
|
||||
},
|
||||
"customPath": "Utilisation du chemin personnalisé : {{path}}",
|
||||
"error": {
|
||||
"description": "Git Bash est requis pour exécuter des agents sur Windows. L'agent ne peut pas fonctionner sans. Veuillez installer Git pour Windows depuis",
|
||||
"recheck": "Revérifier l'installation de Git Bash",
|
||||
"title": "Git Bash requis"
|
||||
},
|
||||
"notFound": "Git Bash introuvable. Veuillez l’installer d’abord.",
|
||||
"found": {
|
||||
"title": "Git Bash configuré"
|
||||
},
|
||||
"notFound": "Git Bash non trouvé. Veuillez l'installer d'abord.",
|
||||
"pick": {
|
||||
"button": "Sélectionner le chemin Git Bash",
|
||||
"failed": "Échec de la configuration du chemin Git Bash",
|
||||
"invalidPath": "Le fichier sélectionné n'est pas un exécutable Git Bash valide (bash.exe).",
|
||||
"title": "Sélectionner l'exécutable Git Bash"
|
||||
},
|
||||
"success": "Git Bash détecté avec succès !"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "[to be translated]:Using auto-detected Git Bash",
|
||||
"clear": {
|
||||
"button": "[to be translated]:Clear custom path"
|
||||
},
|
||||
"customPath": "[to be translated]:Using custom path: {{path}}",
|
||||
"error": {
|
||||
"description": "Windowsでエージェントを実行するにはGit Bashが必要です。これがないとエージェントは動作しません。以下からGit for Windowsをインストールしてください。",
|
||||
"recheck": "Git Bashのインストールを再確認してください",
|
||||
"title": "Git Bashが必要です"
|
||||
},
|
||||
"found": {
|
||||
"title": "[to be translated]:Git Bash configured"
|
||||
},
|
||||
"notFound": "Git Bash が見つかりません。先にインストールしてください。",
|
||||
"pick": {
|
||||
"button": "[to be translated]:Select Git Bash Path",
|
||||
"failed": "[to be translated]:Failed to set Git Bash path",
|
||||
"invalidPath": "[to be translated]:Selected file is not a valid Git Bash executable (bash.exe).",
|
||||
"title": "[to be translated]:Select Git Bash executable"
|
||||
},
|
||||
"success": "Git Bashが正常に検出されました!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Usando Git Bash detectado automaticamente",
|
||||
"clear": {
|
||||
"button": "Limpar caminho personalizado"
|
||||
},
|
||||
"customPath": "Usando caminho personalizado: {{path}}",
|
||||
"error": {
|
||||
"description": "O Git Bash é necessário para executar agentes no Windows. O agente não pode funcionar sem ele. Por favor, instale o Git para Windows a partir de",
|
||||
"recheck": "Reverificar a Instalação do Git Bash",
|
||||
"title": "Git Bash Necessário"
|
||||
},
|
||||
"found": {
|
||||
"title": "Git Bash configurado"
|
||||
},
|
||||
"notFound": "Git Bash não encontrado. Por favor, instale-o primeiro.",
|
||||
"pick": {
|
||||
"button": "Selecionar caminho do Git Bash",
|
||||
"failed": "Falha ao configurar o caminho do Git Bash",
|
||||
"invalidPath": "O arquivo selecionado não é um executável válido do Git Bash (bash.exe).",
|
||||
"title": "Selecionar executável do Git Bash"
|
||||
},
|
||||
"success": "Git Bash detectado com sucesso!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
@ -31,12 +31,26 @@
|
||||
}
|
||||
},
|
||||
"gitBash": {
|
||||
"autoDetected": "Используется автоматически обнаруженный Git Bash",
|
||||
"clear": {
|
||||
"button": "Очистить пользовательский путь"
|
||||
},
|
||||
"customPath": "Используется пользовательский путь: {{path}}",
|
||||
"error": {
|
||||
"description": "Для запуска агентов в Windows требуется Git Bash. Без него агент не может работать. Пожалуйста, установите Git для Windows с",
|
||||
"recheck": "Повторная проверка установки Git Bash",
|
||||
"title": "Требуется Git Bash"
|
||||
},
|
||||
"found": {
|
||||
"title": "Git Bash настроен"
|
||||
},
|
||||
"notFound": "Git Bash не найден. Пожалуйста, сначала установите его.",
|
||||
"pick": {
|
||||
"button": "Выбрать путь Git Bash",
|
||||
"failed": "Не удалось настроить путь Git Bash",
|
||||
"invalidPath": "Выбранный файл не является допустимым исполняемым файлом Git Bash (bash.exe).",
|
||||
"title": "Выберите исполняемый файл Git Bash"
|
||||
},
|
||||
"success": "Git Bash успешно обнаружен!"
|
||||
},
|
||||
"input": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user