黄中银 3 долоо хоног өмнө
parent
commit
01e1637b6d

+ 51 - 9
ApqInstaller/electron/modules/installer.ts

@@ -546,11 +546,26 @@ export async function installNodejs(
 
       checkCancelled()
 
-      // pnpm 安装后也需要使用完整路径
-      const pnpmCmd = getPnpmPath()
+      // 获取刷新后的 PATH,确保能找到刚安装的 pnpm
+      const { getRefreshedPath } = await import('./utils')
+      const refreshedPath = await getRefreshedPath()
+      const execEnv = { ...process.env, PATH: refreshedPath }
+
+      // 运行 pnpm setup 配置全局 bin 目录
+      onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 全局目录...`, 88)
+      try {
+        await execa('pnpm', ['setup'], { env: execEnv, shell: platform === 'win32' })
+        logger.installInfo('pnpm setup 完成')
+      } catch (error) {
+        logger.installWarn('pnpm setup 失败', error)
+      }
+
+      checkCancelled()
+
+      // 配置 pnpm 镜像
       onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 镜像...`, 95)
       try {
-        await executeCommand(pnpmCmd, ['config', 'set', 'registry', NPM_REGISTRY], false)
+        await execa('pnpm', ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv, shell: platform === 'win32' })
       } catch (error) {
         logger.installWarn('配置 pnpm 镜像失败', error)
       }
@@ -587,11 +602,26 @@ export async function installNodejs(
 
     checkCancelled()
 
-    // pnpm 安装后也需要使用完整路径
-    const pnpmCmd = getPnpmPath()
+    // 获取刷新后的 PATH,确保能找到刚安装的 pnpm
+    const { getRefreshedPath } = await import('./utils')
+    const refreshedPath = await getRefreshedPath()
+    const execEnv = { ...process.env, PATH: refreshedPath }
+
+    // 运行 pnpm setup 配置全局 bin 目录
+    onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 全局目录...`, 82)
+    try {
+      await execa('pnpm', ['setup'], { env: execEnv })
+      logger.installInfo('pnpm setup 完成')
+    } catch (error) {
+      logger.installWarn('pnpm setup 失败', error)
+    }
+
+    checkCancelled()
+
+    // 配置 pnpm 镜像
     onStatus('nodejs', `${STATUS_MESSAGES.CONFIGURING} pnpm 镜像...`, 90)
     try {
-      await executeCommand(pnpmCmd, ['config', 'set', 'registry', NPM_REGISTRY], false)
+      await execa('pnpm', ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv })
     } catch (error) {
       logger.installWarn('配置 pnpm 镜像失败', error)
     }
@@ -912,10 +942,22 @@ export async function installAll(options: InstallOptions, onStatus: StatusCallba
       onStatus('all', `${STATUS_MESSAGES.INSTALLING} pnpm...`, getProgress())
       await executeCommand(npmCmd, ['install', '-g', 'pnpm'], true)
 
-      // pnpm 安装后也需要使用完整路径
-      const pnpmCmd = getPnpmPath()
+      // 获取刷新后的 PATH,确保能找到刚安装的 pnpm
+      const { getRefreshedPath } = await import('./utils')
+      const refreshedPath = await getRefreshedPath()
+      const execEnv = { ...process.env, PATH: refreshedPath }
+
+      // 运行 pnpm setup 配置全局 bin 目录
+      try {
+        await execa('pnpm', ['setup'], { env: execEnv, shell: platform === 'win32' })
+        logger.installInfo('pnpm setup 完成')
+      } catch (error) {
+        logger.installWarn('pnpm setup 失败', error)
+      }
+
+      // 配置 pnpm 镜像
       try {
-        await executeCommand(pnpmCmd, ['config', 'set', 'registry', NPM_REGISTRY], false)
+        await execa('pnpm', ['config', 'set', 'registry', NPM_REGISTRY], { env: execEnv, shell: platform === 'win32' })
       } catch (error) {
         logger.installWarn('配置 pnpm 镜像失败', error)
       }

+ 25 - 38
ApqInstaller/electron/modules/ipc-handlers.ts

@@ -18,8 +18,7 @@ import {
   installVscode,
   installGit,
   installAll,
-  uninstallSoftware,
-  getNpmPath
+  uninstallSoftware
 } from './installer'
 import { addInstallHistory, getInstallHistory } from './config'
 import logger from './logger'
@@ -380,34 +379,31 @@ export function registerHandlers(): void {
         i18nKey: 'log.installingClaudeCode'
       })
 
+      const { commandExistsWithRefresh, getRefreshedPath } = await import('./utils')
+      const isWindows = process.platform === 'win32'
+
+      // 获取刷新后的 PATH,确保能找到新安装的命令
+      const refreshedPath = await getRefreshedPath()
+      const execEnv = { ...process.env, PATH: refreshedPath }
+
       // 检测是否已安装 pnpm,优先使用 pnpm
-      const { commandExistsWithRefresh, getCommandPath } = await import('./utils')
       const hasPnpm = await commandExistsWithRefresh('pnpm')
+      const pkgManager = hasPnpm ? 'pnpm' : 'npm'
 
-      // 使用动态获取的路径,而不是固定路径
-      // 因为 pnpm 可能通过不同方式安装(npm, corepack, scoop 等)
-      let pkgManagerPath: string
-      let pkgManagerName: string
-
+      // 如果使用 pnpm,确保全局目录已配置(pnpm setup 是幂等的)
       if (hasPnpm) {
-        // 尝试获取 pnpm 的实际路径
-        const pnpmPath = await getCommandPath('pnpm')
-        if (pnpmPath) {
-          pkgManagerPath = pnpmPath
-          pkgManagerName = 'pnpm'
-        } else {
-          // 如果无法获取路径,回退到 npm
-          pkgManagerPath = await getCommandPath('npm') || getNpmPath()
-          pkgManagerName = 'npm'
+        try {
+          await execa(pkgManager, ['setup'], { shell: isWindows, env: execEnv })
+          logger.installInfo('pnpm setup 完成')
+        } catch (setupError) {
+          // pnpm setup 失败不是致命错误,可能已经配置过了
+          logger.installWarn('pnpm setup 失败,尝试继续安装', setupError)
         }
-      } else {
-        pkgManagerPath = await getCommandPath('npm') || getNpmPath()
-        pkgManagerName = 'npm'
       }
 
       const installArgs = ['install', '-g', '@anthropic-ai/claude-code']
-      const fullCommand = `${pkgManagerPath} ${installArgs.join(' ')}`
-      logger.installInfo(`使用 ${pkgManagerName} 安装,执行命令: ${fullCommand}`)
+      const fullCommand = `${pkgManager} ${installArgs.join(' ')}`
+      logger.installInfo(`使用 ${pkgManager} 安装,执行命令: ${fullCommand}`)
 
       // 发送执行命令的日志
       sendToRenderer('install-status', {
@@ -419,40 +415,31 @@ export function registerHandlers(): void {
       })
 
       // 使用 pnpm 或 npm 全局安装 @anthropic-ai/claude-code
-      // Windows 上执行 .cmd 文件时,使用 shell: true 避免参数解析问题
-      const isWindows = process.platform === 'win32'
-
-      // 获取刷新后的 PATH,确保新安装的 Node.js 路径在 PATH 中
-      const { getRefreshedPath } = await import('./utils')
-      const refreshedPath = isWindows ? await getRefreshedPath() : process.env.PATH
-
-      const result = await execa(pkgManagerPath, installArgs, {
+      // 直接使用命令名,通过刷新后的 PATH 环境变量找到命令
+      const result = await execa(pkgManager, installArgs, {
         encoding: 'utf8',
-        // 捕获输出以便记录日志
         stdout: 'pipe',
         stderr: 'pipe',
-        // Windows 上使用 shell 模式执行 .cmd 文件,避免参数解析问题
         shell: isWindows,
-        // 使用刷新后的 PATH 环境变量
-        env: { ...process.env, PATH: refreshedPath }
+        env: execEnv
       })
 
       // 记录安装输出
       if (result.stdout) {
-        logger.installInfo(`${pkgManagerName} install stdout: ${result.stdout}`)
+        logger.installInfo(`${pkgManager} install stdout: ${result.stdout}`)
         sendToRenderer('install-status', {
           software: 'claudeCode',
-          message: `${pkgManagerName} 输出: ${result.stdout}`,
+          message: `${pkgManager} 输出: ${result.stdout}`,
           progress: 80,
           i18nKey: 'log.npmInstallStdout',
           i18nParams: { output: result.stdout }
         })
       }
       if (result.stderr) {
-        logger.installWarn(`${pkgManagerName} install stderr: ${result.stderr}`)
+        logger.installWarn(`${pkgManager} install stderr: ${result.stderr}`)
         sendToRenderer('install-status', {
           software: 'claudeCode',
-          message: `${pkgManagerName} 警告: ${result.stderr}`,
+          message: `${pkgManager} 警告: ${result.stderr}`,
           progress: 85,
           i18nKey: 'log.npmInstallStderr',
           i18nParams: { output: result.stderr }