feat(vscode-ide-companion): improve CLI path detection and error handling

- Move determineNodePathForCli function to dedicated cliPathDetector.ts file
- Enhance error handling with specific guidance for permission issues
- Add detailed error messages for different failure scenarios
- Improve logging for debugging CLI path detection issues

This change improves the reliability of CLI path detection by providing better error messages and handling edge cases more gracefully.
This commit is contained in:
yiliang114
2025-12-03 00:27:20 +08:00
parent 2e449f4d45
commit c6299bf135
14 changed files with 788 additions and 198 deletions

View File

@@ -124,9 +124,26 @@ export class CliDetector {
} catch (detectionError) {
console.log('[CliDetector] CLI not found, error:', detectionError);
// CLI not found
let error = `Qwen Code CLI not found in PATH. Please install it using: npm install -g @qwen-code/qwen-code@latest`;
// Provide specific guidance for permission errors
if (detectionError instanceof Error) {
const errorMessage = detectionError.message;
if (
errorMessage.includes('EACCES') ||
errorMessage.includes('Permission denied')
) {
error += `\n\nThis may be due to permission issues. Possible solutions:
\n1. Reinstall the CLI without sudo: npm install -g @qwen-code/qwen-code@latest
\n2. If previously installed with sudo, fix ownership: sudo chown -R $(whoami) $(npm config get prefix)/lib/node_modules/@qwen-code/qwen-code
\n3. Use nvm for Node.js version management to avoid permission issues
\n4. Check your PATH environment variable includes npm's global bin directory`;
}
}
this.cachedResult = {
isInstalled: false,
error: `Qwen Code CLI not found in PATH. Please install it using: npm install -g @qwen-code/qwen-code@latest`,
error,
};
this.lastCheckTime = now;
return this.cachedResult;
@@ -135,9 +152,24 @@ export class CliDetector {
console.log('[CliDetector] General detection error:', error);
const errorMessage =
error instanceof Error ? error.message : String(error);
let userFriendlyError = `Failed to detect Qwen Code CLI: ${errorMessage}`;
// Provide specific guidance for permission errors
if (
errorMessage.includes('EACCES') ||
errorMessage.includes('Permission denied')
) {
userFriendlyError += `\n\nThis may be due to permission issues. Possible solutions:
\n1. Reinstall the CLI without sudo: npm install -g @qwen-code/qwen-code@latest
\n2. If previously installed with sudo, fix ownership: sudo chown -R $(whoami) $(npm config get prefix)/lib/node_modules/@qwen-code/qwen-code
\n3. Use nvm for Node.js version management to avoid permission issues
\n4. Check your PATH environment variable includes npm's global bin directory`;
}
this.cachedResult = {
isInstalled: false,
error: `Failed to detect Qwen Code CLI: ${errorMessage}`,
error: userFriendlyError,
};
this.lastCheckTime = now;
return this.cachedResult;

View File

@@ -161,9 +161,23 @@ export class CliInstaller {
console.error('[CliInstaller] Installation failed:', errorMessage);
console.error('[CliInstaller] Error stack:', error);
// Provide specific guidance for permission errors
let userFriendlyMessage = `Failed to install Qwen Code CLI: ${errorMessage}`;
if (
errorMessage.includes('EACCES') ||
errorMessage.includes('Permission denied')
) {
userFriendlyMessage += `\n\nThis is likely due to permission issues. Possible solutions:
\n1. Reinstall without sudo: npm install -g @qwen-code/qwen-code@latest
\n2. Fix npm permissions: sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
\n3. Use nvm for Node.js version management to avoid permission issues
\n4. Configure npm to use a different directory: npm config set prefix ~/.npm-global`;
}
vscode.window
.showErrorMessage(
`Failed to install Qwen Code CLI: ${errorMessage}`,
userFriendlyMessage,
'Try Manual Installation',
'View Documentation',
)
@@ -173,9 +187,26 @@ export class CliInstaller {
'Qwen Code Installation',
);
terminal.show();
terminal.sendText(
'npm install -g @qwen-code/qwen-code@latest',
);
// Provide different installation commands based on error type
if (
errorMessage.includes('EACCES') ||
errorMessage.includes('Permission denied')
) {
terminal.sendText('# Try installing without sudo:');
terminal.sendText(
'npm install -g @qwen-code/qwen-code@latest',
);
terminal.sendText('');
terminal.sendText('# Or fix npm permissions:');
terminal.sendText(
'sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}',
);
} else {
terminal.sendText(
'npm install -g @qwen-code/qwen-code@latest',
);
}
} else if (selection === 'View Documentation') {
vscode.env.openExternal(
vscode.Uri.parse(

View File

@@ -0,0 +1,128 @@
/**
* @license
* Copyright 2025 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { statSync } from 'fs';
export interface CliPathDetectionResult {
path: string | null;
error?: string;
}
/**
* Determine the correct Node.js executable path for a given CLI installation
* 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
*/
export function determineNodePathForCli(
cliPath: string,
): CliPathDetectionResult {
// 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 {
const stats = statSync(nodePath);
if (stats.isFile()) {
// Verify it's executable
if (stats.mode & 0o111) {
console.log(`[CLI] Found Node.js executable for CLI at: ${nodePath}`);
return { path: nodePath };
} else {
console.log(`[CLI] Node.js found at ${nodePath} but not executable`);
return {
path: null,
error: `Node.js found at ${nodePath} but not executable. You may need to fix file permissions or reinstall the CLI.`,
};
}
}
} catch (error) {
// Differentiate between error types
if (error instanceof Error) {
if ('code' in error && error.code === 'EACCES') {
console.log(`[CLI] Permission denied accessing ${nodePath}`);
return {
path: null,
error: `Permission denied accessing ${nodePath}. The CLI may have been installed with sudo privileges. Try reinstalling without sudo or adjusting file permissions.`,
};
} else if ('code' in error && error.code === 'ENOENT') {
// File not found, continue to next pattern
continue;
} else {
console.log(`[CLI] Error accessing ${nodePath}: ${error.message}`);
return {
path: null,
error: `Error accessing Node.js at ${nodePath}: ${error.message}`,
};
}
}
}
}
// 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 {
const stats = statSync(nodePath);
if (stats.isFile()) {
if (stats.mode & 0o111) {
console.log(
`[CLI] Found Node.js executable in CLI directory at: ${nodePath}`,
);
return { path: nodePath };
} else {
console.log(`[CLI] Node.js found at ${nodePath} but not executable`);
return {
path: null,
error: `Node.js found at ${nodePath} but not executable. You may need to fix file permissions or reinstall the CLI.`,
};
}
}
} catch (error) {
// Differentiate between error types
if (error instanceof Error) {
if ('code' in error && error.code === 'EACCES') {
console.log(`[CLI] Permission denied accessing ${nodePath}`);
return {
path: null,
error: `Permission denied accessing ${nodePath}. The CLI may have been installed with sudo privileges. Try reinstalling without sudo or adjusting file permissions.`,
};
} else if ('code' in error && error.code === 'ENOENT') {
// File not found, continue
continue;
} else {
console.log(`[CLI] Error accessing ${nodePath}: ${error.message}`);
return {
path: null,
error: `Error accessing Node.js at ${nodePath}: ${error.message}`,
};
}
}
}
}
console.log(`[CLI] Could not determine Node.js path for CLI: ${cliPath}`);
return {
path: null,
error: `Could not find Node.js executable for CLI at ${cliPath}. Please verify the CLI installation.`,
};
}