mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Revert "fix(vscode-ide-companion): 解决 mac 环境多个 node 版本的安装问题"
This reverts commit 530039c517.
This commit is contained in:
@@ -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(' '));
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user