fix: install script permission fix

This commit is contained in:
hangerye
2025-06-10 15:50:07 +08:00
parent 24bb809074
commit 0c4942a2da
6 changed files with 237 additions and 39 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
"description": "a chrome extension to use your own chrome as a mcp server",
"author": "hangye",
"private": true,
"version": "0.0.0",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "wxt",
+58 -4
View File
@@ -229,23 +229,77 @@ manifest.json
- 用户级别安装需要对用户目录有写入权限
- 系统级别安装需要管理员/root权限
3. **修复执行权限问题macOS/Linux**
3. **修复执行权限问题**
- 如果遇到 "Failed to start native messaging host."" 错误
- 运行以下命令修复执行权限:
**macOS/Linux 平台**
**问题描述**
- npm 安装通常会保留文件权限,但 pnpm 可能不会
- 可能遇到 "Permission denied" 或 "Native host has exited" 错误
- Chrome 扩展无法启动 native host 进程
**解决方案**
a) **使用内置修复命令(推荐)**
```bash
chrome-mcp-bridge fix-permissions
```
- 或者手动设置权限:
b) **手动设置权限**
```bash
# 查找安装路径
npm list -g chrome-mcp-bridge
# 或者对于 pnpm
pnpm list -g chrome-mcp-bridge
# 设置执行权限(替换为实际路径)
chmod +x /path/to/node_modules/chrome-mcp-bridge/run_host.sh
chmod +x /path/to/node_modules/chrome-mcp-bridge/index.js
chmod +x /path/to/node_modules/chrome-mcp-bridge/cli.js
```
**Windows 平台**
**问题描述**
- Windows 上 `.bat` 文件通常不需要执行权限,但可能遇到其他问题
- 文件可能被标记为只读
- 可能遇到 "Access denied" 或文件无法执行的错误
**解决方案**
a) **使用内置修复命令(推荐)**:
```cmd
chrome-mcp-bridge fix-permissions
```
b) **手动检查文件属性**:
```cmd
# 查找安装路径
npm list -g chrome-mcp-bridge
# 检查文件属性(在文件资源管理器中右键 -> 属性)
# 确保 run_host.bat 不是只读文件
```
c) **重新安装并强制权限**:
```bash
# 卸载
npm uninstall -g chrome-mcp-bridge
# 或 pnpm uninstall -g chrome-mcp-bridge
# 重新安装
npm install -g chrome-mcp-bridge
# 或 pnpm install -g chrome-mcp-bridge
# 如果仍有问题,运行权限修复
chrome-mcp-bridge fix-permissions
```
4. 在 Windows 上,确保注册表访问没有被限制
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mcp-chrome-bridge",
"version": "1.0.9",
"version": "1.0.10",
"description": "Chrome Native-Messaging host (Node)",
"main": "dist/index.js",
"bin": {
+126 -28
View File
@@ -8,43 +8,130 @@ import { colorText, tryRegisterUserLevelHost } from './utils';
// Check if this script is run directly
const isDirectRun = require.main === module;
const isGlobalInstall = process.env.npm_config_global === 'true';
// Detect global installation for both npm and pnpm
function detectGlobalInstall(): boolean {
// npm uses npm_config_global
if (process.env.npm_config_global === 'true') {
return true;
}
// pnpm detection methods
// Method 1: Check if PNPM_HOME is set and current path contains it
if (process.env.PNPM_HOME && __dirname.includes(process.env.PNPM_HOME)) {
return true;
}
// 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/'];
if (globalPnpmPatterns.some((pattern) => __dirname.includes(pattern))) {
return true;
}
// Method 3: Check npm_config_prefix for pnpm
if (process.env.npm_config_prefix && __dirname.includes(process.env.npm_config_prefix)) {
return true;
}
return false;
}
const isGlobalInstall = detectGlobalInstall();
/**
* 尝试注册Native Messaging主机
*/
/**
* 确保执行权限(无论是否为全局安装)
*/
async function ensureExecutionPermissions(): Promise<void> {
if (process.platform === 'win32') {
// Windows 平台处理
await ensureWindowsFilePermissions();
return;
}
// Unix/Linux 平台处理
const filesToCheck = [
path.join(__dirname, '..', 'index.js'),
path.join(__dirname, '..', 'run_host.sh'),
path.join(__dirname, '..', 'cli.js'),
];
for (const filePath of filesToCheck) {
if (fs.existsSync(filePath)) {
try {
fs.chmodSync(filePath, '755');
console.log(
colorText(`✓ Set execution permissions for ${path.basename(filePath)}`, 'green'),
);
} catch (err: any) {
console.warn(
colorText(
`⚠️ Unable to set execution permissions for ${path.basename(filePath)}: ${err.message}`,
'yellow',
),
);
}
} else {
console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow'));
}
}
}
/**
* Windows 平台文件权限处理
*/
async function ensureWindowsFilePermissions(): Promise<void> {
const filesToCheck = [
path.join(__dirname, '..', 'index.js'),
path.join(__dirname, '..', 'run_host.bat'),
path.join(__dirname, '..', 'cli.js'),
];
for (const filePath of filesToCheck) {
if (fs.existsSync(filePath)) {
try {
// 检查文件是否为只读,如果是则移除只读属性
const stats = fs.statSync(filePath);
if (!(stats.mode & parseInt('200', 8))) {
// 检查写权限
// 尝试移除只读属性
fs.chmodSync(filePath, stats.mode | parseInt('200', 8));
console.log(
colorText(`✓ Removed read-only attribute from ${path.basename(filePath)}`, 'green'),
);
}
// 验证文件可读性
fs.accessSync(filePath, fs.constants.R_OK);
console.log(
colorText(`✓ Verified file accessibility for ${path.basename(filePath)}`, 'green'),
);
} catch (err: any) {
console.warn(
colorText(
`⚠️ Unable to verify file permissions for ${path.basename(filePath)}: ${err.message}`,
'yellow',
),
);
}
} else {
console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow'));
}
}
}
async function tryRegisterNativeHost(): Promise<void> {
try {
console.log(colorText('Attempting to register Chrome Native Messaging host...', 'blue'));
// Always ensure execution permissions, regardless of installation type
await ensureExecutionPermissions();
if (isGlobalInstall) {
// 1. Ensure native host has execution permissions
const launcherPath = path.join(__dirname, '..', 'index.js');
const wrapperScriptPath = path.join(
__dirname,
'..',
process.platform === 'win32' ? 'run_host.bat' : 'run_host.sh',
);
try {
// Set execution permissions on non-Windows platforms
if (process.platform !== 'win32') {
fs.chmodSync(launcherPath, '755');
console.log('✓ Set launcher execution permissions');
// Also set execution permissions for the wrapper script
if (fs.existsSync(wrapperScriptPath)) {
fs.chmodSync(wrapperScriptPath, '755');
console.log('✓ Set wrapper script execution permissions');
} else {
console.warn('⚠️ Wrapper script not found:', wrapperScriptPath);
}
}
} catch (err: any) {
console.warn('⚠️ Unable to set execution permissions:', err.message);
// Non-critical error, don't block
}
// First try user-level installation (no elevated permissions required)
const userLevelSuccess = await tryRegisterUserLevelHost();
@@ -141,6 +228,17 @@ function printManualInstructions(): void {
async function main(): Promise<void> {
console.log(colorText(`Installing ${COMMAND_NAME}...`, 'green'));
// Debug information
console.log(colorText('Installation environment debug info:', 'blue'));
console.log(` __dirname: ${__dirname}`);
console.log(` npm_config_global: ${process.env.npm_config_global}`);
console.log(` PNPM_HOME: ${process.env.PNPM_HOME}`);
console.log(` npm_config_prefix: ${process.env.npm_config_prefix}`);
console.log(` isGlobalInstall: ${isGlobalInstall}`);
// Always ensure execution permissions first
await ensureExecutionPermissions();
// If global installation, try automatic registration
if (isGlobalInstall) {
await tryRegisterNativeHost();
+51 -5
View File
@@ -101,13 +101,16 @@ export async function getMainPath(): Promise<string> {
* 确保关键文件具有执行权限
*/
export async function ensureExecutionPermissions(): Promise<void> {
if (process.platform === 'win32') {
// Windows 平台不需要设置执行权限
return;
}
try {
const packageDistDir = path.join(__dirname, '..');
if (process.platform === 'win32') {
// Windows 平台处理
await ensureWindowsFilePermissions(packageDistDir);
return;
}
// Unix/Linux 平台处理
const filesToCheck = [
path.join(packageDistDir, 'index.js'),
path.join(packageDistDir, 'run_host.sh'),
@@ -138,6 +141,49 @@ export async function ensureExecutionPermissions(): Promise<void> {
}
}
/**
* Windows 平台文件权限处理
*/
async function ensureWindowsFilePermissions(packageDistDir: string): Promise<void> {
const filesToCheck = [
path.join(packageDistDir, 'index.js'),
path.join(packageDistDir, 'run_host.bat'),
path.join(packageDistDir, 'cli.js'),
];
for (const filePath of filesToCheck) {
if (fs.existsSync(filePath)) {
try {
// 检查文件是否为只读,如果是则移除只读属性
const stats = fs.statSync(filePath);
if (!(stats.mode & parseInt('200', 8))) {
// 检查写权限
// 尝试移除只读属性
fs.chmodSync(filePath, stats.mode | parseInt('200', 8));
console.log(
colorText(`✓ Removed read-only attribute from ${path.basename(filePath)}`, 'green'),
);
}
// 验证文件可读性
fs.accessSync(filePath, fs.constants.R_OK);
console.log(
colorText(`✓ Verified file accessibility for ${path.basename(filePath)}`, 'green'),
);
} catch (err: any) {
console.warn(
colorText(
`⚠️ Unable to verify file permissions for ${path.basename(filePath)}: ${err.message}`,
'yellow',
),
);
}
} else {
console.warn(colorText(`⚠️ File not found: ${filePath}`, 'yellow'));
}
}
}
/**
* Create Native Messaging host manifest content
*/