UNPKG

kindlyguard

Version:

Security-focused MCP server protecting against unicode attacks, injection threats, and other AI vulnerabilities

424 lines (370 loc) • 15.3 kB
#!/usr/bin/env node /** * Post-install script for KindlyGuard * Downloads or copies platform-specific binaries */ const fs = require('fs'); const path = require('path'); const { spawnSync } = require('child_process'); const https = require('https'); const tar = require('tar'); const unzipper = require('unzipper'); const { pipeline } = require('stream'); const { promisify } = require('util'); const platform = require('./platform'); const pipelineAsync = promisify(pipeline); // Skip in CI or when explicitly requested if (process.env.CI || process.env.KINDLYGUARD_SKIP_DOWNLOAD) { console.log('ā„¹ļø Skipping KindlyGuard binary installation'); if (process.env.CI) { console.log(' (Running in CI environment)'); } if (process.env.KINDLYGUARD_SKIP_DOWNLOAD) { console.log(' (KINDLYGUARD_SKIP_DOWNLOAD is set)'); } process.exit(0); } async function downloadBinary(url, destPath) { console.log(`šŸ“¦ Downloading from ${url}...`); return new Promise((resolve, reject) => { https.get(url, (response) => { if (response.statusCode === 302 || response.statusCode === 301) { // Follow redirect return downloadBinary(response.headers.location, destPath) .then(resolve) .catch(reject); } if (response.statusCode === 404) { const error = new Error('DOWNLOAD_NOT_FOUND'); error.statusCode = 404; reject(error); return; } if (response.statusCode !== 200) { const error = new Error(`Download failed with status: ${response.statusCode}`); error.statusCode = response.statusCode; reject(error); return; } const totalSize = parseInt(response.headers['content-length'], 10); let downloadedSize = 0; response.on('data', (chunk) => { downloadedSize += chunk.length; const percent = Math.round((downloadedSize / totalSize) * 100); const progressBar = 'ā–ˆ'.repeat(Math.floor(percent / 2)) + 'ā–‘'.repeat(50 - Math.floor(percent / 2)); process.stdout.write(`\ršŸ“„ Downloading: [${progressBar}] ${percent}%`); }); // Handle different archive types const isZip = url.endsWith('.zip'); const extractStream = isZip ? unzipper.Extract({ path: path.dirname(destPath) }) : tar.x({ C: path.dirname(destPath), strip: 1 }); response.pipe(extractStream) .on('finish', () => { console.log('\nāœ… Download complete!'); resolve(); }) .on('error', (err) => { if (err.code === 'ENOSPC') { const error = new Error('DISK_SPACE'); error.originalError = err; reject(error); } else { reject(err); } }); }).on('error', (err) => { if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED') { const error = new Error('NETWORK_ERROR'); error.originalError = err; reject(error); } else { reject(err); } }); }); } async function tryNativeDependency() { try { const packageName = platform.getPackageName(); const platformPackagePath = require.resolve(`${packageName}/package.json`); const platformPackageDir = path.dirname(platformPackagePath); console.log(`šŸ“Œ Found native dependency: ${packageName}`); const binDir = path.join(__dirname, '..', 'bin'); // Ensure bin directory exists await fs.promises.mkdir(binDir, { recursive: true }); // Copy binaries const binaries = ['kindlyguard', 'kindlyguard-cli']; for (const binary of binaries) { const srcName = platform.getBinaryName(binary); const srcPath = path.join(platformPackageDir, srcName); const dstPath = path.join(binDir, srcName); if (fs.existsSync(srcPath)) { console.log(` šŸ“„ Copying ${binary}...`); await fs.promises.copyFile(srcPath, dstPath); // Make executable on Unix-like systems if (process.platform !== 'win32') { await fs.promises.chmod(dstPath, 0o755); } console.log(` āœ“ ${binary} copied successfully`); } } console.log('āœ… Native binaries installed'); return true; } catch (error) { // Only log if it's not a module not found error (which is expected) if (error.code !== 'MODULE_NOT_FOUND') { console.log(`āš ļø Native dependency error: ${error.message}`); } return false; } } async function tryDirectDownload() { const version = process.env.npm_package_version || 'latest'; const url = platform.downloadUrl(version); const binDir = path.join(__dirname, '..', 'bin'); try { console.log('šŸ“” Attempting direct download...'); await fs.promises.mkdir(binDir, { recursive: true }); await downloadBinary(url, binDir); // Make binaries executable const binaries = ['kindlyguard', 'kindlyguard-cli']; for (const binary of binaries) { const binaryPath = path.join(binDir, platform.getBinaryName(binary)); if (fs.existsSync(binaryPath)) { if (process.platform !== 'win32') { await fs.promises.chmod(binaryPath, 0o755); } console.log(` āœ“ ${binary} ready`); } } console.log('āœ… Direct download successful'); return true; } catch (error) { // For known error types, let them bubble up for proper handling if (['NETWORK_ERROR', 'DISK_SPACE', 'DOWNLOAD_NOT_FOUND'].includes(error.message) || error.code === 'ENOSPC' || error.statusCode === 404) { throw error; } console.error(`āš ļø Download failed: ${error.message}`); return false; } } async function verifyInstallation() { const binPath = platform.getBinaryPath('kindlyguard'); const validation = platform.validateBinary(binPath); if (!validation.valid) { const error = new Error(validation.error); if (validation.error.includes('not found')) { error.code = 'ENOENT'; } else if (validation.error.includes('architecture')) { error.code = 'ARCH_MISMATCH'; } throw error; } // Try to run --version to verify it works const result = spawnSync(binPath, ['--version'], { encoding: 'utf8', stdio: 'pipe' }); if (result.error) { const error = new Error(result.error.message); error.code = result.error.code; error.originalError = result.error; // Check for common error codes if (result.error.code === 'ENOENT') { error.message = 'Binary not found'; } else if (result.error.code === 'EACCES') { error.message = 'Permission denied'; } else if (result.error.code === 'EISDIR') { error.message = 'Expected a file but found a directory'; } throw error; } if (result.status === 2) { const error = new Error('Missing dependencies'); error.code = 'MISSING_DEPS'; error.status = result.status; error.stderr = result.stderr; throw error; } if (result.status !== 0) { const error = new Error(`Binary exited with code ${result.status}`); error.status = result.status; error.stderr = result.stderr; throw error; } console.log(`✨ KindlyGuard ${result.stdout.trim()} installed successfully!`); return true; } function displayErrorHelp(error) { console.error('\nāŒ Installation failed!\n'); // Handle specific error types with friendly messages if (error.code === 'EACCES' || error.message?.includes('Permission denied')) { console.error('šŸ”’ Permission Denied'); console.error(''); console.error(' KindlyGuard needs permission to install binaries.'); console.error(''); console.error(' Try one of these solutions:'); console.error(' • Run with elevated permissions: sudo npm install -g kindlyguard'); console.error(' • Install locally instead: npm install kindlyguard'); console.error(' • Fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-errors'); console.error(''); console.error(' For more help: https://github.com/kindlyguard/kindlyguard/wiki/Permission-Issues'); } else if (error.code === 'ENOENT' || error.message?.includes('not found')) { console.error('šŸ” Binary Not Found'); console.error(''); console.error(' The KindlyGuard binary could not be found or executed.'); console.error(''); console.error(' Possible causes:'); console.error(' • Download was incomplete or corrupted'); console.error(' • Antivirus software may have quarantined the file'); console.error(' • File system permissions issue'); console.error(''); console.error(' Try:'); console.error(' • Clear npm cache: npm cache clean --force'); console.error(' • Reinstall: npm install kindlyguard --force'); console.error(' • Manual installation (see below)'); } else if (error.code === 'ARCH_MISMATCH' || error.message?.includes('architecture')) { console.error('šŸ–„ļø Architecture Mismatch'); console.error(''); console.error(` Your system: ${process.platform} ${process.arch}`); console.error(''); console.error(' KindlyGuard supports:'); console.error(' • macOS: x64, arm64 (Apple Silicon)'); console.error(' • Linux: x64, arm64'); console.error(' • Windows: x64'); console.error(''); console.error(' If your platform should be supported, please report:'); console.error(' https://github.com/kindlyguard/kindlyguard/issues'); } else if (error.code === 'MISSING_DEPS' || error.status === 2) { console.error('šŸ“¦ Missing Dependencies'); console.error(''); console.error(' KindlyGuard requires some system libraries to run.'); console.error(''); if (error.stderr) { console.error(' Error details:', error.stderr.trim()); } console.error(''); console.error(' On Ubuntu/Debian:'); console.error(' • sudo apt-get update && sudo apt-get install -y libssl-dev'); console.error(''); console.error(' On RHEL/CentOS/Fedora:'); console.error(' • sudo yum install openssl-devel'); console.error(''); console.error(' On macOS:'); console.error(' • Should work out of the box. Try: xcode-select --install'); } else if (error.code === 'NETWORK_ERROR' || error.message === 'NETWORK_ERROR') { console.error('🌐 Network Error'); console.error(''); console.error(' Failed to download KindlyGuard binary.'); console.error(''); console.error(' Possible causes:'); console.error(' • No internet connection'); console.error(' • Firewall blocking GitHub releases'); console.error(' • Corporate proxy settings'); console.error(''); console.error(' Try:'); console.error(' • Check your internet connection'); console.error(' • Configure proxy: npm config set proxy http://proxy.company.com:8080'); console.error(' • Use manual installation (see below)'); } else if (error.code === 'DISK_SPACE' || error.message === 'DISK_SPACE') { console.error('šŸ’¾ Insufficient Disk Space'); console.error(''); console.error(' Not enough disk space to install KindlyGuard.'); console.error(''); console.error(' KindlyGuard requires approximately 50MB of free space.'); console.error(''); console.error(' Try:'); console.error(' • Free up disk space'); console.error(' • Clear npm cache: npm cache clean --force'); console.error(' • Install to a different location'); } else if (error.message === 'DOWNLOAD_NOT_FOUND' || error.statusCode === 404) { console.error('āš ļø Download Not Found'); console.error(''); console.error(' The requested KindlyGuard version could not be found.'); console.error(''); console.error(' This might happen if:'); console.error(' • The version is too new and binaries aren\'t released yet'); console.error(' • The version number is incorrect'); console.error(''); console.error(' Try:'); console.error(' • Install the latest stable version: npm install kindlyguard@latest'); console.error(' • Check available versions: npm view kindlyguard versions'); } else { console.error('āš ļø Unexpected Error'); console.error(''); console.error(' Error:', error.message || error); if (error.stderr) { console.error(' Details:', error.stderr.trim()); } console.error(''); console.error(' Please report this issue:'); console.error(' https://github.com/kindlyguard/kindlyguard/issues'); } console.error(''); console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.error(''); console.error('šŸ“‹ Alternative Solutions:'); console.error(''); console.error('1. Skip automatic download:'); console.error(' KINDLYGUARD_SKIP_DOWNLOAD=1 npm install kindlyguard'); console.error(''); console.error('2. Manual installation:'); console.error(` • Download from: ${platform.downloadUrl()}`); console.error(` • Extract to: ${path.join(__dirname, '..', 'bin')}`); console.error(' • Make executable: chmod +x bin/kindlyguard*'); console.error(''); console.error('3. Build from source:'); console.error(' • git clone https://github.com/kindlyguard/kindlyguard'); console.error(' • cd kindlyguard && cargo build --release'); console.error(''); console.error('šŸ“š Documentation: https://kindlyguard.dev/docs/installation'); console.error('šŸ’¬ Discord: https://discord.gg/kindlyguard'); console.error('šŸ› Issues: https://github.com/kindlyguard/kindlyguard/issues'); console.error(''); } async function main() { try { console.log(`šŸš€ Installing KindlyGuard for ${platform.getPlatformKey()}...\n`); // Try native dependency first let installed = await tryNativeDependency(); // Fall back to direct download if (!installed && !process.env.KINDLYGUARD_SKIP_DOWNLOAD) { try { installed = await tryDirectDownload(); } catch (downloadError) { // Re-throw known errors for proper handling throw downloadError; } } if (!installed) { throw new Error('Failed to install KindlyGuard binary'); } // Verify installation await verifyInstallation(); console.log('\nšŸŽ‰ Installation complete!\n'); console.log('šŸ“– Quick Start:'); console.log(' kindlyguard --help # Run the MCP server'); console.log(' kindlyguard-cli --help # Use the CLI tool\n'); console.log('šŸ”§ Claude Desktop Integration:'); console.log(' Add this to your Claude Desktop config:\n'); console.log(JSON.stringify({ mcpServers: { "kindly-guard": { command: "npx", args: ["kindlyguard", "--stdio"] } } }, null, 2)); console.log('\n✨ Happy coding with KindlyGuard!\n'); } catch (error) { displayErrorHelp(error); process.exit(1); } } // Only run if called directly if (require.main === module) { main(); }