From ddad9ef21d37f9928d8bbbb2cff04a9f320d0d3d Mon Sep 17 00:00:00 2001 From: LinYushen Date: Fri, 16 Jan 2026 12:36:25 +0800 Subject: [PATCH] Revert "Fix npm global install permission error by using user-level prefix" --- src/main/utils/agent-install.ts | 47 +++------- src/main/utils/path.ts | 12 --- tests/unit/main/utils/agent-install.test.ts | 95 --------------------- 3 files changed, 10 insertions(+), 144 deletions(-) delete mode 100644 tests/unit/main/utils/agent-install.test.ts diff --git a/src/main/utils/agent-install.ts b/src/main/utils/agent-install.ts index 19b148e33..cd39a6a34 100644 --- a/src/main/utils/agent-install.ts +++ b/src/main/utils/agent-install.ts @@ -4,12 +4,10 @@ */ import { spawn } from 'child_process' import { platform } from 'os' -import * as fs from 'fs' import type { BrowserWindow } from 'electron' import { IPC_CHANNELS } from '../../shared/ipc-channels' import type { InstallStep, InstallResult } from '../../shared/electron-api' import { commandExists } from './agent-check' -import { getEnhancedPath, getNpmUserPrefix } from './path' interface InstallOptions { window: BrowserWindow @@ -33,8 +31,8 @@ function spawnWithProgress( useShell: boolean = false ): Promise<{ success: boolean; error?: string }> { return new Promise((resolve) => { - // Use enhanced PATH that includes user-level npm global directory - const enhancedPath = getEnhancedPath() + // Ensure common paths are in PATH for npm, node, etc. + const enhancedPath = `/usr/local/bin:/opt/homebrew/bin:${process.env.PATH || ''}` console.log(`[agent-install] Spawning: ${command} ${args.join(' ')} (shell: ${useShell})`) @@ -111,19 +109,14 @@ async function installClaudeCLI( } /** - * Install claude-code-acp via npm to user-level directory + * Install claude-code-acp via npm */ async function installClaudeCodeACP( onProgress: (message: string) => void ): Promise<{ success: boolean; error?: string }> { - const prefix = getNpmUserPrefix() - - // Ensure the user-level npm global directory exists - await fs.promises.mkdir(prefix, { recursive: true }).catch(() => {}) - return spawnWithProgress( 'npm', - ['install', '-g', '--prefix', prefix, '@zed-industries/claude-code-acp'], + ['install', '-g', '@zed-industries/claude-code-acp'], onProgress, true ) @@ -224,41 +217,21 @@ async function installClaudeCode(options: InstallOptions): Promise void ): Promise<{ success: boolean; error?: string }> { - const prefix = getNpmUserPrefix() - - // Ensure the user-level npm global directory exists - await fs.promises.mkdir(prefix, { recursive: true }).catch(() => {}) - - return spawnWithProgress( - 'npm', - ['install', '-g', '--prefix', prefix, '@openai/codex'], - onProgress, - true - ) + return spawnWithProgress('npm', ['install', '-g', '@openai/codex'], onProgress, true) } /** - * Install codex-acp via npm to user-level directory + * Install codex-acp via npm */ async function installCodexACP( onProgress: (message: string) => void ): Promise<{ success: boolean; error?: string }> { - const prefix = getNpmUserPrefix() - - // Ensure the user-level npm global directory exists - await fs.promises.mkdir(prefix, { recursive: true }).catch(() => {}) - - return spawnWithProgress( - 'npm', - ['install', '-g', '--prefix', prefix, '@zed-industries/codex-acp'], - onProgress, - true - ) + return spawnWithProgress('npm', ['install', '-g', '@zed-industries/codex-acp'], onProgress, true) } /** @@ -387,9 +360,9 @@ export async function installAgent(options: InstallOptions): Promise ({ - spawn: vi.fn() -})) - -// Mock fs -vi.mock('fs', () => ({ - promises: { - mkdir: vi.fn().mockResolvedValue(undefined) - } -})) - -// Mock os module -vi.mock('os', () => ({ - platform: vi.fn().mockReturnValue('darwin') -})) - -// Mock path utilities -vi.mock('../../../../src/main/utils/path', () => ({ - getEnhancedPath: vi - .fn() - .mockReturnValue( - '/Users/test/.npm-global/bin:/Users/test/.opencode/bin:/usr/local/bin:/usr/bin:/bin' - ), - getNpmUserPrefix: vi.fn().mockReturnValue('/Users/test/.npm-global') -})) - -// Mock agent-check -vi.mock('../../../../src/main/utils/agent-check', () => ({ - commandExists: vi.fn().mockResolvedValue({ exists: false }) -})) - -import { formatInstallError } from '../../../../src/main/utils/agent-install' -import { getNpmUserPrefix, getEnhancedPath } from '../../../../src/main/utils/path' - -describe('agent-install', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - describe('getNpmUserPrefix', () => { - it('should return user-level npm global directory path', () => { - const prefix = getNpmUserPrefix() - expect(prefix).toBe('/Users/test/.npm-global') - }) - }) - - describe('getEnhancedPath', () => { - it('should include npm-global/bin in PATH', () => { - const path = getEnhancedPath() - expect(path).toContain('.npm-global/bin') - }) - }) - - describe('formatInstallError', () => { - it('should format EACCES permission errors', () => { - const error = 'npm ERR! code EACCES\nnpm ERR! syscall mkdir' - const result = formatInstallError(error) - expect(result).toBe('Permission denied. Please run: chmod -R u+w ~/.npm-global') - }) - - it('should format permission denied errors', () => { - const error = 'Error: EACCES: permission denied, mkdir /some/path' - const result = formatInstallError(error) - expect(result).toBe('Permission denied. Please run: chmod -R u+w ~/.npm-global') - }) - - it('should format network errors with ENOTFOUND', () => { - const error = 'npm ERR! code ENOTFOUND\nnpm ERR! network' - const result = formatInstallError(error) - expect(result).toBe('Network error. Please check your internet connection and try again.') - }) - - it('should format network errors with host resolution', () => { - const error = 'Could not resolve host: registry.npmjs.org' - const result = formatInstallError(error) - expect(result).toBe('Network error. Please check your internet connection and try again.') - }) - - it('should format command not found errors', () => { - const error = 'spawn npm ENOENT' - const result = formatInstallError(error) - expect(result).toBe('Command not found. Please ensure required tools are installed.') - }) - - it('should pass through unknown errors unchanged', () => { - const error = 'Some unknown error message' - const result = formatInstallError(error) - expect(result).toBe('Some unknown error message') - }) - }) -})