Revert "fix(vscode-ide-companion): 解决 mac 环境多个 node 版本的安装问题"

This reverts commit 530039c517.
This commit is contained in:
yiliang114
2025-11-28 00:57:33 +08:00
parent 530039c517
commit d5ede56e62
6 changed files with 20 additions and 201 deletions

View File

@@ -20,7 +20,6 @@ import type {
} from './connectionTypes.js'; } from './connectionTypes.js';
import { AcpMessageHandler } from './acpMessageHandler.js'; import { AcpMessageHandler } from './acpMessageHandler.js';
import { AcpSessionManager } from './acpSessionManager.js'; import { AcpSessionManager } from './acpSessionManager.js';
import { statSync } from 'fs';
/** /**
* ACP Connection Handler for VSCode Extension * ACP Connection Handler for VSCode Extension
@@ -70,75 +69,7 @@ export class AcpConnection {
} }
/** /**
* Determine the correct Node.js executable path for a given CLI installation * Connect to ACP backend
* Handles various Node.js version managers (nvm, n, manual installations)
*
* @param cliPath - Path to the CLI executable
* @returns Path to the Node.js executable, or null if not found
*/
private determineNodePathForCli(cliPath: string): string | null {
// Common patterns for Node.js installations
const nodePathPatterns = [
// NVM pattern: /Users/user/.nvm/versions/node/vXX.XX.X/bin/qwen -> /Users/user/.nvm/versions/node/vXX.XX.X/bin/node
cliPath.replace(/\/bin\/qwen$/, '/bin/node'),
// N pattern: /Users/user/n/bin/qwen -> /Users/user/n/bin/node
cliPath.replace(/\/bin\/qwen$/, '/bin/node'),
// Manual installation pattern: /usr/local/bin/qwen -> /usr/local/bin/node
cliPath.replace(/\/qwen$/, '/node'),
// Alternative pattern: /opt/nodejs/bin/qwen -> /opt/nodejs/bin/node
cliPath.replace(/\/bin\/qwen$/, '/bin/node'),
];
// Check each pattern
for (const nodePath of nodePathPatterns) {
try {
if (statSync(nodePath).isFile()) {
// Verify it's executable
const stats = statSync(nodePath);
if (stats.mode & 0o111) {
// Check if executable
console.log(
`[ACP] Found Node.js executable for CLI at: ${nodePath}`,
);
return nodePath;
}
}
} catch (_error) {
// File doesn't exist or other error, continue to next pattern
continue;
}
}
// Try to find node in the same directory as the CLI
const cliDir = cliPath.substring(0, cliPath.lastIndexOf('/'));
const potentialNodePaths = [`${cliDir}/node`, `${cliDir}/bin/node`];
for (const nodePath of potentialNodePaths) {
try {
if (statSync(nodePath).isFile()) {
const stats = statSync(nodePath);
if (stats.mode & 0o111) {
console.log(
`[ACP] Found Node.js executable in CLI directory at: ${nodePath}`,
);
return nodePath;
}
}
} catch (_error) {
// File doesn't exist, continue
continue;
}
}
console.log(`[ACP] Could not determine Node.js path for CLI: ${cliPath}`);
return null;
}
/**
* 连接到ACP后端
* *
* @param backend - Backend type * @param backend - Backend type
* @param cliPath - CLI path * @param cliPath - CLI path
@@ -184,24 +115,9 @@ export class AcpConnection {
spawnCommand = isWindows ? 'npx.cmd' : 'npx'; spawnCommand = isWindows ? 'npx.cmd' : 'npx';
spawnArgs = [...parts.slice(1), '--experimental-acp', ...extraArgs]; spawnArgs = [...parts.slice(1), '--experimental-acp', ...extraArgs];
} else { } else {
// For qwen CLI, ensure we use the correct Node.js version
// Handle various Node.js version managers (nvm, n, manual installations)
if (cliPath.includes('/qwen') && !isWindows) {
// Try to determine the correct node executable for this qwen installation
const nodePath = this.determineNodePathForCli(cliPath);
if (nodePath) {
spawnCommand = nodePath;
spawnArgs = [cliPath, '--experimental-acp', ...extraArgs];
} else {
// Fallback to direct execution
spawnCommand = cliPath; spawnCommand = cliPath;
spawnArgs = ['--experimental-acp', ...extraArgs]; spawnArgs = ['--experimental-acp', ...extraArgs];
} }
} else {
spawnCommand = cliPath;
spawnArgs = ['--experimental-acp', ...extraArgs];
}
}
console.log('[ACP] Spawning command:', spawnCommand, spawnArgs.join(' ')); console.log('[ACP] Spawning command:', spawnCommand, spawnArgs.join(' '));

View File

@@ -72,14 +72,12 @@ export class QwenAgentManager {
/** /**
* Connect to Qwen service * Connect to Qwen service
* *
* @param workingDir - 工作目录 * @param workingDir - Working directory
* @param authStateManager - 认证状态管理器(可选) * @param authStateManager - Auth state manager (optional)
* @param cliPath - CLI路径可选如果提供将覆盖配置中的路径
*/ */
async connect( async connect(
workingDir: string, workingDir: string,
authStateManager?: AuthStateManager, authStateManager?: AuthStateManager,
cliPath?: string,
): Promise<void> { ): Promise<void> {
this.currentWorkingDir = workingDir; this.currentWorkingDir = workingDir;
await this.connectionHandler.connect( await this.connectionHandler.connect(
@@ -87,7 +85,6 @@ export class QwenAgentManager {
this.sessionReader, this.sessionReader,
workingDir, workingDir,
authStateManager, authStateManager,
cliPath,
); );
} }

View File

@@ -23,18 +23,16 @@ export class QwenConnectionHandler {
/** /**
* Connect to Qwen service and establish session * Connect to Qwen service and establish session
* *
* @param connection - ACP连接实例 * @param connection - ACP connection instance
* @param sessionReader - 会话读取器实例 * @param sessionReader - Session reader instance
* @param workingDir - 工作目录 * @param workingDir - Working directory
* @param authStateManager - 认证状态管理器(可选) * @param authStateManager - Auth state manager (optional)
* @param cliPath - CLI路径可选如果提供将覆盖配置中的路径
*/ */
async connect( async connect(
connection: AcpConnection, connection: AcpConnection,
sessionReader: QwenSessionReader, sessionReader: QwenSessionReader,
workingDir: string, workingDir: string,
authStateManager?: AuthStateManager, authStateManager?: AuthStateManager,
cliPath?: string,
): Promise<void> { ): Promise<void> {
const connectId = Date.now(); const connectId = Date.now();
console.log(`\n========================================`); console.log(`\n========================================`);
@@ -43,9 +41,7 @@ export class QwenConnectionHandler {
console.log(`========================================\n`); console.log(`========================================\n`);
const config = vscode.workspace.getConfiguration('qwenCode'); const config = vscode.workspace.getConfiguration('qwenCode');
// Use the provided CLI path if available, otherwise use the configured path const cliPath = config.get<string>('qwen.cliPath', 'qwen');
const effectiveCliPath =
cliPath || config.get<string>('qwen.cliPath', 'qwen');
const openaiApiKey = config.get<string>('qwen.openaiApiKey', ''); const openaiApiKey = config.get<string>('qwen.openaiApiKey', '');
const openaiBaseUrl = config.get<string>('qwen.openaiBaseUrl', ''); const openaiBaseUrl = config.get<string>('qwen.openaiBaseUrl', '');
const model = config.get<string>('qwen.model', ''); const model = config.get<string>('qwen.model', '');
@@ -67,7 +63,7 @@ export class QwenConnectionHandler {
console.log('[QwenAgentManager] Using proxy:', proxy); console.log('[QwenAgentManager] Using proxy:', proxy);
} }
await connection.connect('qwen', effectiveCliPath, workingDir, extraArgs); await connection.connect('qwen', cliPath, workingDir, extraArgs);
// Determine authentication method // Determine authentication method
const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth';

View File

@@ -93,39 +93,9 @@ export class CliInstaller {
const execAsync = promisify(exec); const execAsync = promisify(exec);
try { try {
// Use NVM environment to ensure we get the same Node.js version
// as when they run 'node -v' in terminal
// Fallback chain: default alias -> node alias -> current version
const installCommand =
process.platform === 'win32'
? 'npm install -g @qwen-code/qwen-code@latest'
: 'source ~/.nvm/nvm.sh 2>/dev/null && (nvm use default 2>/dev/null || nvm use node 2>/dev/null || nvm use 2>/dev/null); npm install -g @qwen-code/qwen-code@latest';
console.log(
'[CliInstaller] Installing with command:',
installCommand,
);
console.log(
'[CliInstaller] Current process PATH:',
process.env.PATH,
);
// Also log Node.js version being used by VS Code
console.log(
'[CliInstaller] VS Code Node.js version:',
process.version,
);
console.log(
'[CliInstaller] VS Code Node.js execPath:',
process.execPath,
);
const { stdout, stderr } = await execAsync( const { stdout, stderr } = await execAsync(
installCommand, 'npm install -g @qwen-code/qwen-code@latest',
{ { timeout: 120000 }, // 2 minutes timeout
timeout: 120000,
shell: '/bin/bash',
}, // 2 minutes timeout
); );
console.log('[CliInstaller] Installation output:', stdout); console.log('[CliInstaller] Installation output:', stdout);
@@ -159,7 +129,6 @@ export class CliInstaller {
const errorMessage = const errorMessage =
error instanceof Error ? error.message : String(error); error instanceof Error ? error.message : String(error);
console.error('[CliInstaller] Installation failed:', errorMessage); console.error('[CliInstaller] Installation failed:', errorMessage);
console.error('[CliInstaller] Error stack:', error);
vscode.window vscode.window
.showErrorMessage( .showErrorMessage(

View File

@@ -40,77 +40,28 @@ export class CliDetector {
this.cachedResult && this.cachedResult &&
now - this.lastCheckTime < this.CACHE_DURATION_MS now - this.lastCheckTime < this.CACHE_DURATION_MS
) { ) {
console.log('[CliDetector] Returning cached result');
return this.cachedResult; return this.cachedResult;
} }
console.log(
'[CliDetector] Starting CLI detection, current PATH:',
process.env.PATH,
);
try { try {
const isWindows = process.platform === 'win32'; const isWindows = process.platform === 'win32';
const whichCommand = isWindows ? 'where' : 'which'; const whichCommand = isWindows ? 'where' : 'which';
// Check if qwen command exists // Check if qwen command exists
try { try {
// Use NVM environment for consistent detection const { stdout } = await execAsync(`${whichCommand} qwen`, {
// Fallback chain: default alias -> node alias -> current version
const detectionCommand =
process.platform === 'win32'
? `${whichCommand} qwen`
: 'source ~/.nvm/nvm.sh 2>/dev/null && (nvm use default 2>/dev/null || nvm use node 2>/dev/null || nvm use 2>/dev/null); which qwen';
console.log(
'[CliDetector] Detecting CLI with command:',
detectionCommand,
);
const { stdout } = await execAsync(detectionCommand, {
timeout: 5000, timeout: 5000,
shell: '/bin/bash',
}); });
// The output may contain multiple lines, with NVM activation messages const cliPath = stdout.trim().split('\n')[0];
// We want the last line which should be the actual path
const lines = stdout
.trim()
.split('\n')
.filter((line) => line.trim());
const cliPath = lines[lines.length - 1];
console.log('[CliDetector] Found CLI at:', cliPath);
// Try to get version // Try to get version
let version: string | undefined; let version: string | undefined;
try { try {
// Use NVM environment for version check const { stdout: versionOutput } = await execAsync('qwen --version', {
// Fallback chain: default alias -> node alias -> current version
// Also ensure we use the correct Node.js version that matches the CLI installation
const versionCommand =
process.platform === 'win32'
? 'qwen --version'
: 'source ~/.nvm/nvm.sh 2>/dev/null && (nvm use default 2>/dev/null || nvm use node 2>/dev/null || nvm use 2>/dev/null); qwen --version';
console.log(
'[CliDetector] Getting version with command:',
versionCommand,
);
const { stdout: versionOutput } = await execAsync(versionCommand, {
timeout: 5000, timeout: 5000,
shell: '/bin/bash',
}); });
// The output may contain multiple lines, with NVM activation messages version = versionOutput.trim();
// We want the last line which should be the actual version } catch {
const versionLines = versionOutput
.trim()
.split('\n')
.filter((line) => line.trim());
version = versionLines[versionLines.length - 1];
console.log('[CliDetector] CLI version:', version);
} catch (versionError) {
console.log('[CliDetector] Failed to get CLI version:', versionError);
// Version check failed, but CLI is installed // Version check failed, but CLI is installed
} }
@@ -121,8 +72,7 @@ export class CliDetector {
}; };
this.lastCheckTime = now; this.lastCheckTime = now;
return this.cachedResult; return this.cachedResult;
} catch (detectionError) { } catch (_error) {
console.log('[CliDetector] CLI not found, error:', detectionError);
// CLI not found // CLI not found
this.cachedResult = { this.cachedResult = {
isInstalled: false, isInstalled: false,
@@ -132,7 +82,6 @@ export class CliDetector {
return this.cachedResult; return this.cachedResult;
} }
} catch (error) { } catch (error) {
console.log('[CliDetector] General detection error:', error);
const errorMessage = const errorMessage =
error instanceof Error ? error.message : String(error); error instanceof Error ? error.message : String(error);
this.cachedResult = { this.cachedResult = {
@@ -166,9 +115,6 @@ export class CliDetector {
'Install via npm:', 'Install via npm:',
' npm install -g @qwen-code/qwen-code@latest', ' npm install -g @qwen-code/qwen-code@latest',
'', '',
'If you are using nvm (automatically handled by the plugin):',
' The plugin will automatically use your default nvm version',
'',
'Or install from source:', 'Or install from source:',
' git clone https://github.com/QwenLM/qwen-code.git', ' git clone https://github.com/QwenLM/qwen-code.git',
' cd qwen-code', ' cd qwen-code',

View File

@@ -399,12 +399,7 @@ export class WebViewProvider {
const authInfo = await this.authStateManager.getAuthInfo(); const authInfo = await this.authStateManager.getAuthInfo();
console.log('[WebViewProvider] Auth cache status:', authInfo); console.log('[WebViewProvider] Auth cache status:', authInfo);
// Pass the detected CLI path to ensure we use the correct installation await this.agentManager.connect(workingDir, this.authStateManager);
await this.agentManager.connect(
workingDir,
this.authStateManager,
cliDetection.cliPath,
);
console.log('[WebViewProvider] Agent connected successfully'); console.log('[WebViewProvider] Agent connected successfully');
this.agentInitialized = true; this.agentInitialized = true;