fix: windows install fail

This commit is contained in:
hangerye
2025-06-11 00:33:15 +08:00
parent 0c4942a2da
commit be4ded5ab7
5 changed files with 149 additions and 13 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mcp-chrome-bridge",
"version": "1.0.10",
"version": "1.0.11",
"description": "Chrome Native-Messaging host (Node)",
"main": "dist/index.js",
"bin": {
+13 -1
View File
@@ -22,7 +22,19 @@ program
try {
// Detect if running with root/administrator privileges
const isRoot = process.getuid && process.getuid() === 0; // Unix/Linux/Mac
const isAdmin = process.platform === 'win32' && require('is-admin')(); // Windows requires additional package
let isAdmin = false;
if (process.platform === 'win32') {
try {
isAdmin = require('is-admin')(); // Windows requires additional package
} catch (error) {
console.warn(
colorText('Warning: Unable to detect administrator privileges on Windows', 'yellow'),
);
isAdmin = false;
}
}
const hasElevatedPermissions = isRoot || isAdmin;
// If --system option is specified or running with root/administrator privileges
+19 -1
View File
@@ -24,7 +24,11 @@ function detectGlobalInstall(): boolean {
// Method 2: Check if we're in a global pnpm directory structure
// pnpm global packages are typically installed in ~/.local/share/pnpm/global/5/node_modules
const globalPnpmPatterns = ['/pnpm/global/', '/.local/share/pnpm/', '/pnpm-global/'];
// Windows: %APPDATA%\pnpm\global\5\node_modules
const globalPnpmPatterns =
process.platform === 'win32'
? ['\\pnpm\\global\\', '\\pnpm-global\\', '\\AppData\\Roaming\\pnpm\\']
: ['/pnpm/global/', '/.local/share/pnpm/', '/pnpm-global/'];
if (globalPnpmPatterns.some((pattern) => __dirname.includes(pattern))) {
return true;
@@ -35,6 +39,20 @@ function detectGlobalInstall(): boolean {
return true;
}
// Method 4: Windows-specific global installation paths
if (process.platform === 'win32') {
const windowsGlobalPatterns = [
'\\npm\\node_modules\\',
'\\AppData\\Roaming\\npm\\node_modules\\',
'\\Program Files\\nodejs\\node_modules\\',
'\\nodejs\\node_modules\\',
];
if (windowsGlobalPatterns.some((pattern) => __dirname.includes(pattern))) {
return true;
}
}
return false;
}
+62 -1
View File
@@ -63,22 +63,83 @@ if not defined NODE_EXEC (
)
)
)
REM Check for Volta (another Node.js version manager)
if exist "%LOCALAPPDATA%\Volta\bin\node.exe" (
if not defined NODE_EXEC (
set "NODE_EXEC=%LOCALAPPDATA%\Volta\bin\node.exe"
echo Found Volta node at %LOCALAPPDATA%\Volta\bin\node.exe >> "%WRAPPER_LOG%"
)
)
REM Check for fnm (Fast Node Manager)
if exist "%LOCALAPPDATA%\fnm_multishells" (
for /f "delims=" %%v in ('dir /b /ad "%LOCALAPPDATA%\fnm_multishells" 2^>nul ^| sort /r') do (
if not defined NODE_EXEC if exist "%LOCALAPPDATA%\fnm_multishells\%%v\node.exe" (
set "NODE_EXEC=%LOCALAPPDATA%\fnm_multishells\%%v\node.exe"
echo Found fnm node at %LOCALAPPDATA%\fnm_multishells\%%v\node.exe >> "%WRAPPER_LOG%"
goto :node_found
)
)
)
REM Check for Scoop installation
if exist "%USERPROFILE%\scoop\apps\nodejs\current\node.exe" (
if not defined NODE_EXEC (
set "NODE_EXEC=%USERPROFILE%\scoop\apps\nodejs\current\node.exe"
echo Found Scoop node at %USERPROFILE%\scoop\apps\nodejs\current\node.exe >> "%WRAPPER_LOG%"
)
)
REM Check for Chocolatey installation
if exist "%ProgramData%\chocolatey\lib\nodejs\tools\node.exe" (
if not defined NODE_EXEC (
set "NODE_EXEC=%ProgramData%\chocolatey\lib\nodejs\tools\node.exe"
echo Found Chocolatey node at %ProgramData%\chocolatey\lib\nodejs\tools\node.exe >> "%WRAPPER_LOG%"
)
)
)
:node_found
if not defined NODE_EXEC (
echo ERROR: Node.js executable not found! >> "%WRAPPER_LOG%"
echo Searched 'where node.exe' and common installation paths. >> "%WRAPPER_LOG%"
echo Searched paths: >> "%WRAPPER_LOG%"
echo - %ProgramFiles%\nodejs\node.exe >> "%WRAPPER_LOG%"
echo - %ProgramFiles(x86)%\nodejs\node.exe >> "%WRAPPER_LOG%"
echo - %LOCALAPPDATA%\Programs\nodejs\node.exe >> "%WRAPPER_LOG%"
echo - %APPDATA%\nvm\* >> "%WRAPPER_LOG%"
echo - %LOCALAPPDATA%\Volta\bin\node.exe >> "%WRAPPER_LOG%"
echo - %LOCALAPPDATA%\fnm_multishells\* >> "%WRAPPER_LOG%"
echo - %USERPROFILE%\scoop\apps\nodejs\current\node.exe >> "%WRAPPER_LOG%"
echo - %ProgramData%\chocolatey\lib\nodejs\tools\node.exe >> "%WRAPPER_LOG%"
echo Please install Node.js or ensure it's in your PATH. >> "%WRAPPER_LOG%"
echo You can download Node.js from: https://nodejs.org/ >> "%WRAPPER_LOG%"
exit /B 1
)
echo Using Node executable: %NODE_EXEC% >> "%WRAPPER_LOG%"
echo Node version found by script: >> "%WRAPPER_LOG%"
call "%NODE_EXEC%" -v >> "%WRAPPER_LOG%" 2>>&1
REM Verify Node.js script exists
if not exist "%NODE_SCRIPT%" (
echo ERROR: Node.js script not found: %NODE_SCRIPT% >> "%WRAPPER_LOG%"
echo Please ensure the native host is properly installed. >> "%WRAPPER_LOG%"
exit /B 1
)
echo Node.js script exists: %NODE_SCRIPT% >> "%WRAPPER_LOG%"
echo Executing: "%NODE_EXEC%" "%NODE_SCRIPT%" >> "%WRAPPER_LOG%"
echo ==================== Starting Native Host ==================== >> "%WRAPPER_LOG%"
REM Execute the Node.js script. Stdout goes to Chrome. Stderr goes to the log file.
call "%NODE_EXEC%" "%NODE_SCRIPT%" 2>> "%STDERR_LOG%"
set "EXIT_CODE=%ERRORLEVEL%"
echo ==================== Native Host Exited ==================== >> "%WRAPPER_LOG%"
echo Exit code: %EXIT_CODE% >> "%WRAPPER_LOG%"
echo Execution completed at %DATE% %TIME% >> "%WRAPPER_LOG%"
endlocal
exit /B %ERRORLEVEL%
exit /B %EXIT_CODE%
+54 -9
View File
@@ -199,6 +199,28 @@ export async function createManifestContent(): Promise<any> {
};
}
/**
* 验证Windows注册表项是否存在
*/
function verifyWindowsRegistryEntry(registryKey: string, expectedPath: string): boolean {
if (os.platform() !== 'win32') {
return true; // 非Windows平台跳过验证
}
try {
const result = execSync(`reg query "${registryKey}" /ve`, { encoding: 'utf8', stdio: 'pipe' });
const lines = result.split('\n');
for (const line of lines) {
if (line.includes('REG_SZ') && line.includes(expectedPath.replace(/\\/g, '\\\\'))) {
return true;
}
}
return false;
} catch (error) {
return false;
}
}
/**
* 尝试注册用户级别的Native Messaging主机
*/
@@ -226,15 +248,25 @@ export async function tryRegisterUserLevelHost(): Promise<boolean> {
if (os.platform() === 'win32') {
const registryKey = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`;
try {
execSync(
`reg add "${registryKey}" /ve /t REG_SZ /d "${manifestPath.replace(/\\/g, '\\\\')}" /f`,
{ stdio: 'ignore' },
);
console.log(colorText('✓ Successfully created Windows registry entry', 'green'));
// 确保路径使用正确的转义格式
const escapedPath = manifestPath.replace(/\\/g, '\\\\');
const regCommand = `reg add "${registryKey}" /ve /t REG_SZ /d "${escapedPath}" /f`;
console.log(colorText(`Executing registry command: ${regCommand}`, 'blue'));
execSync(regCommand, { stdio: 'pipe' });
// 验证注册表项是否创建成功
if (verifyWindowsRegistryEntry(registryKey, manifestPath)) {
console.log(colorText('✓ Successfully created Windows registry entry', 'green'));
} else {
console.log(colorText('⚠️ Registry entry created but verification failed', 'yellow'));
}
} catch (error: any) {
console.log(
colorText(`⚠️ Unable to create Windows registry entry: ${error.message}`, 'yellow'),
);
console.log(colorText(`Registry key: ${registryKey}`, 'yellow'));
console.log(colorText(`Manifest path: ${manifestPath}`, 'yellow'));
return false; // Windows上如果注册表项创建失败,整个注册过程应该视为失败
}
}
@@ -301,7 +333,7 @@ export async function registerWithElevatedPermissions(): Promise<void> {
// 准备命令
const command =
os.platform() === 'win32'
? `mkdir -p "${path.dirname(manifestPath)}" && copy "${tempManifestPath}" "${manifestPath}"`
? `if not exist "${path.dirname(manifestPath)}" mkdir "${path.dirname(manifestPath)}" && copy "${tempManifestPath}" "${manifestPath}"`
: `mkdir -p "${path.dirname(manifestPath)}" && cp "${tempManifestPath}" "${manifestPath}" && chmod 644 "${manifestPath}"`;
if (hasElevatedPermissions) {
@@ -347,17 +379,29 @@ export async function registerWithElevatedPermissions(): Promise<void> {
// 6. Windows特殊处理 - 设置系统级注册表
if (os.platform() === 'win32') {
const registryKey = `HKLM\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`;
const regCommand = `reg add "${registryKey}" /ve /t REG_SZ /d "${manifestPath.replace(/\\/g, '\\\\')}" /f`;
// 确保路径使用正确的转义格式
const escapedPath = manifestPath.replace(/\\/g, '\\\\');
const regCommand = `reg add "${registryKey}" /ve /t REG_SZ /d "${escapedPath}" /f`;
console.log(colorText(`Creating system registry entry: ${registryKey}`, 'blue'));
console.log(colorText(`Manifest path: ${manifestPath}`, 'blue'));
if (hasElevatedPermissions) {
// 已经有管理员权限,直接执行注册表命令
try {
execSync(regCommand, { stdio: 'ignore' });
console.log(colorText('Windows registry entry created successfully!', 'green'));
execSync(regCommand, { stdio: 'pipe' });
// 验证注册表项是否创建成功
if (verifyWindowsRegistryEntry(registryKey, manifestPath)) {
console.log(colorText('Windows registry entry created successfully!', 'green'));
} else {
console.log(colorText('⚠️ Registry entry created but verification failed', 'yellow'));
}
} catch (error: any) {
console.error(
colorText(`Windows registry entry creation failed: ${error.message}`, 'red'),
);
console.error(colorText(`Command: ${regCommand}`, 'red'));
throw error;
}
} else {
@@ -368,6 +412,7 @@ export async function registerWithElevatedPermissions(): Promise<void> {
console.error(
colorText(`Windows registry entry creation failed: ${error.message}`, 'red'),
);
console.error(colorText(`Command: ${regCommand}`, 'red'));
reject(error);
} else {
console.log(colorText('Windows registry entry created successfully!', 'green'));