|
|
@@ -19,7 +19,8 @@ import {
|
|
|
installGit,
|
|
|
installAll,
|
|
|
uninstallSoftware,
|
|
|
- getNpmPath
|
|
|
+ getNpmPath,
|
|
|
+ getPnpmPath
|
|
|
} from './installer'
|
|
|
import { addInstallHistory, getInstallHistory } from './config'
|
|
|
import logger from './logger'
|
|
|
@@ -288,9 +289,57 @@ export function registerHandlers(): void {
|
|
|
|
|
|
logger.info(`npm 全局路径: ${npmGlobalBin} -> ${npmGlobalBinUnix}`)
|
|
|
|
|
|
+ // 获取 Node.js 路径
|
|
|
+ // 常见的 Node.js 安装路径
|
|
|
+ const fs = await import('fs')
|
|
|
+ const possibleNodePaths = [
|
|
|
+ 'C:\\Program Files\\nodejs',
|
|
|
+ 'C:\\Program Files (x86)\\nodejs',
|
|
|
+ path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'nodejs'),
|
|
|
+ // fnm 安装的 Node.js
|
|
|
+ path.join(os.homedir(), 'AppData', 'Local', 'fnm_multishells'),
|
|
|
+ // nvm-windows 安装的 Node.js
|
|
|
+ path.join(os.homedir(), 'AppData', 'Roaming', 'nvm')
|
|
|
+ ]
|
|
|
+
|
|
|
+ let nodePath = ''
|
|
|
+ for (const p of possibleNodePaths) {
|
|
|
+ if (fs.existsSync(p)) {
|
|
|
+ // 检查是否有 node.exe
|
|
|
+ const nodeExe = path.join(p, 'node.exe')
|
|
|
+ if (fs.existsSync(nodeExe)) {
|
|
|
+ nodePath = p
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没找到,尝试从 PATH 环境变量中获取
|
|
|
+ if (!nodePath) {
|
|
|
+ const pathEnv = process.env.PATH || ''
|
|
|
+ const pathDirs = pathEnv.split(';')
|
|
|
+ for (const dir of pathDirs) {
|
|
|
+ const nodeExe = path.join(dir, 'node.exe')
|
|
|
+ if (fs.existsSync(nodeExe)) {
|
|
|
+ nodePath = dir
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建额外的 PATH
|
|
|
+ let extraPaths = npmGlobalBinUnix
|
|
|
+ if (nodePath) {
|
|
|
+ const nodePathUnix = nodePath.replace(/\\/g, '/').replace(/^([A-Za-z]):/, '/$1')
|
|
|
+ extraPaths = `${nodePathUnix}:${npmGlobalBinUnix}`
|
|
|
+ logger.info(`Node.js 路径: ${nodePath} -> ${nodePathUnix}`)
|
|
|
+ } else {
|
|
|
+ logger.warn('未找到 Node.js 路径,可能会导致 claude 命令无法运行')
|
|
|
+ }
|
|
|
+
|
|
|
// 使用 execa 启动 Git Bash 并执行 claude 命令
|
|
|
- // 先将 npm 全局路径添加到 PATH,然后执行 claude 命令
|
|
|
- const bashCommand = `export PATH="${npmGlobalBinUnix}:$PATH"; claude; exec bash`
|
|
|
+ // 先将 Node.js 和 npm 全局路径添加到 PATH,然后执行 claude 命令
|
|
|
+ const bashCommand = `export PATH="${extraPaths}:$PATH"; claude; exec bash`
|
|
|
execa(gitBashPath, ['-c', bashCommand], {
|
|
|
detached: true,
|
|
|
stdio: 'ignore'
|
|
|
@@ -299,7 +348,7 @@ export function registerHandlers(): void {
|
|
|
return { success: true }
|
|
|
})
|
|
|
|
|
|
- // 安装 Claude Code (通过 npm 全局安装)
|
|
|
+ // 安装 Claude Code (通过 pnpm 或 npm 全局安装)
|
|
|
registerHandler('install-claude-code', async () => {
|
|
|
try {
|
|
|
logger.info('开始安装 Claude Code...')
|
|
|
@@ -310,11 +359,15 @@ export function registerHandlers(): void {
|
|
|
i18nKey: 'log.installingClaudeCode'
|
|
|
})
|
|
|
|
|
|
- // 使用完整的 npm 路径,避免 PATH 未生效的问题
|
|
|
- const npmPath = getNpmPath()
|
|
|
+ // 检测是否已安装 pnpm,优先使用 pnpm
|
|
|
+ const { commandExistsWithRefresh } = await import('./utils')
|
|
|
+ const hasPnpm = await commandExistsWithRefresh('pnpm')
|
|
|
+ const pkgManagerPath = hasPnpm ? getPnpmPath() : getNpmPath()
|
|
|
+ const pkgManagerName = hasPnpm ? 'pnpm' : 'npm'
|
|
|
+
|
|
|
const installArgs = ['install', '-g', '@anthropic-ai/claude-code']
|
|
|
- const fullCommand = `${npmPath} ${installArgs.join(' ')}`
|
|
|
- logger.info(`执行命令: ${fullCommand}`)
|
|
|
+ const fullCommand = `${pkgManagerPath} ${installArgs.join(' ')}`
|
|
|
+ logger.info(`使用 ${pkgManagerName} 安装,执行命令: ${fullCommand}`)
|
|
|
|
|
|
// 发送执行命令的日志
|
|
|
sendToRenderer('install-status', {
|
|
|
@@ -325,8 +378,8 @@ export function registerHandlers(): void {
|
|
|
i18nParams: { command: fullCommand }
|
|
|
})
|
|
|
|
|
|
- // 使用 npm 全局安装 @anthropic-ai/claude-code
|
|
|
- const result = await execa(npmPath, installArgs, {
|
|
|
+ // 使用 pnpm 或 npm 全局安装 @anthropic-ai/claude-code
|
|
|
+ const result = await execa(pkgManagerPath, installArgs, {
|
|
|
encoding: 'utf8',
|
|
|
// 捕获输出以便记录日志
|
|
|
stdout: 'pipe',
|
|
|
@@ -335,20 +388,20 @@ export function registerHandlers(): void {
|
|
|
|
|
|
// 记录安装输出
|
|
|
if (result.stdout) {
|
|
|
- logger.info(`npm install stdout: ${result.stdout}`)
|
|
|
+ logger.info(`${pkgManagerName} install stdout: ${result.stdout}`)
|
|
|
sendToRenderer('install-status', {
|
|
|
software: 'claudeCode',
|
|
|
- message: `npm 输出: ${result.stdout}`,
|
|
|
+ message: `${pkgManagerName} 输出: ${result.stdout}`,
|
|
|
progress: 80,
|
|
|
i18nKey: 'log.npmInstallStdout',
|
|
|
i18nParams: { output: result.stdout }
|
|
|
})
|
|
|
}
|
|
|
if (result.stderr) {
|
|
|
- logger.warn(`npm install stderr: ${result.stderr}`)
|
|
|
+ logger.warn(`${pkgManagerName} install stderr: ${result.stderr}`)
|
|
|
sendToRenderer('install-status', {
|
|
|
software: 'claudeCode',
|
|
|
- message: `npm 警告: ${result.stderr}`,
|
|
|
+ message: `${pkgManagerName} 警告: ${result.stderr}`,
|
|
|
progress: 85,
|
|
|
i18nKey: 'log.npmInstallStderr',
|
|
|
i18nParams: { output: result.stderr }
|
|
|
@@ -362,8 +415,8 @@ export function registerHandlers(): void {
|
|
|
i18nKey: 'log.installComplete'
|
|
|
})
|
|
|
|
|
|
- // 验证安装 - 使用 commandExistsWithRefresh 来刷新 PATH
|
|
|
- const { commandExistsWithRefresh, getCommandVersionWithRefresh } = await import('./utils')
|
|
|
+ // 验证安装 - 使用 getCommandVersionWithRefresh 来刷新 PATH
|
|
|
+ const { getCommandVersionWithRefresh } = await import('./utils')
|
|
|
const claudeExists = await commandExistsWithRefresh('claude')
|
|
|
|
|
|
if (claudeExists) {
|
|
|
@@ -412,7 +465,11 @@ export function registerHandlers(): void {
|
|
|
// 检查 VS Code 插件是否已安装
|
|
|
registerHandler('check-vscode-extension', async (_event, extensionId: string) => {
|
|
|
try {
|
|
|
- const result = await execa('code', ['--list-extensions'])
|
|
|
+ const result = await execa('code', ['--list-extensions'], {
|
|
|
+ encoding: 'utf8',
|
|
|
+ stdout: 'pipe',
|
|
|
+ stderr: 'pipe'
|
|
|
+ })
|
|
|
const extensions = result.stdout.split('\n').map(ext => ext.trim().toLowerCase())
|
|
|
const installed = extensions.includes(extensionId.toLowerCase())
|
|
|
logger.info(`检查 VS Code 插件 ${extensionId}: ${installed ? '已安装' : '未安装'}`)
|
|
|
@@ -427,11 +484,25 @@ export function registerHandlers(): void {
|
|
|
registerHandler('install-vscode-extension', async (_event, extensionId: string) => {
|
|
|
try {
|
|
|
logger.info(`开始安装 VS Code 插件: ${extensionId}`)
|
|
|
- await execa('code', ['--install-extension', extensionId])
|
|
|
+ await execa('code', ['--install-extension', extensionId], {
|
|
|
+ encoding: 'utf8',
|
|
|
+ stdout: 'pipe',
|
|
|
+ stderr: 'pipe'
|
|
|
+ })
|
|
|
logger.info(`VS Code 插件安装成功: ${extensionId}`)
|
|
|
return { success: true }
|
|
|
} catch (error) {
|
|
|
- const errorMessage = (error as Error).message
|
|
|
+ const execaError = error as { message?: string; stderr?: string; shortMessage?: string }
|
|
|
+ // 过滤乱码字符,只保留可读字符(ASCII 可打印字符和中文)
|
|
|
+ let errorMessage = execaError.shortMessage || execaError.message || '未知错误'
|
|
|
+ if (execaError.stderr) {
|
|
|
+ const stderrClean = execaError.stderr.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r]/g, '').trim()
|
|
|
+ if (stderrClean) {
|
|
|
+ errorMessage = stderrClean
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 从错误消息中也过滤乱码
|
|
|
+ errorMessage = errorMessage.replace(/[^\x20-\x7E\u4e00-\u9fa5\n\r:.\-_/\\]/g, '').trim()
|
|
|
logger.error(`VS Code 插件安装失败: ${extensionId}`, error)
|
|
|
return { success: false, error: errorMessage }
|
|
|
}
|