// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= /* global console, process */ import AdmZip from 'adm-zip'; import fs from 'fs'; import os from 'os'; import path from 'path'; import { downloadWithRedirects } from './download.js'; // Base URL for downloading bun binaries const BUN_RELEASE_BASE_URL = 'https://github.com/oven-sh/bun/releases/download'; const DEFAULT_BUN_VERSION = '1.2.9'; // Default fallback version // Mapping of platform+arch to binary package name const BUN_PACKAGES = { 'darwin-arm64': 'bun-darwin-aarch64.zip', 'darwin-x64': 'bun-darwin-x64.zip', 'win32-x64': 'bun-windows-x64.zip', 'win32-x64-baseline': 'bun-windows-x64-baseline.zip', 'linux-x64': 'bun-linux-x64.zip', 'linux-x64-baseline': 'bun-linux-x64-baseline.zip', 'linux-arm64': 'bun-linux-aarch64.zip', // MUSL variants 'linux-musl-x64': 'bun-linux-x64-musl.zip', 'linux-musl-x64-baseline': 'bun-linux-x64-musl-baseline.zip', 'linux-musl-arm64': 'bun-linux-aarch64-musl.zip', }; /** * Downloads and extracts the bun binary for the specified platform and architecture * @param {string} platform Platform to download for (e.g., 'darwin', 'win32', 'linux') * @param {string} arch Architecture to download for (e.g., 'x64', 'arm64') * @param {string} version Version of bun to download * @param {boolean} isMusl Whether to use MUSL variant for Linux * @param {boolean} isBaseline Whether to use baseline variant */ async function downloadBunBinary( bun_download_url, platform, arch, version = DEFAULT_BUN_VERSION, isMusl = false, isBaseline = false ) { let platformKey = isMusl ? `${platform}-musl-${arch}` : `${platform}-${arch}`; if (isBaseline) { platformKey += '-baseline'; } const packageName = BUN_PACKAGES[platformKey]; if (!packageName) { console.error(`No binary available for ${platformKey}`); return false; } // Create output directory structure const binDir = path.join(os.homedir(), '.eigent', 'bin'); // Ensure directories exist fs.mkdirSync(binDir, { recursive: true }); // Download URL for the specific binary const downloadUrl = `${bun_download_url}/bun-v${version}/${packageName}`; const tempdir = os.tmpdir(); // Create a temporary file for the downloaded binary // Use unique temp filename to avoid race conditions when multiple processes install simultaneously const uniqueSuffix = `${process.pid}-${Date.now()}`; const tempFilename = path.join(tempdir, `${uniqueSuffix}-${packageName}`); try { console.log(`Downloading bun ${version} for ${platformKey}...`); console.log(`URL: ${downloadUrl}`); if (fs.existsSync(tempFilename)) fs.unlinkSync(tempFilename); // Use the new download function await downloadWithRedirects(downloadUrl, tempFilename); // Extract the zip file using adm-zip console.log(`Extracting ${packageName} to ${binDir}...`); const zip = new AdmZip(tempFilename); // Get all entries and find the bun binary const entries = zip.getEntries(); let bunFound = false; for (const entry of entries) { const entryName = entry.entryName; // Look for the bun binary (could be in root or in a subdirectory) if ( entryName === 'bun' || entryName === 'bun.exe' || entryName.endsWith('/bun') || entryName.endsWith('/bun.exe') || entryName.endsWith('\\bun') || entryName.endsWith('\\bun.exe') ) { // Extract just the binary name const fileName = path.basename(entryName); const destPath = path.join(binDir, fileName); // Extract the specific file zip.extractEntryTo(entry, binDir, false, true); // Set executable permissions for non-Windows platforms if (platform !== 'win32' && fileName === 'bun') { try { fs.chmodSync(destPath, '755'); } catch (error) { console.warn( `Warning: Failed to set executable permissions: ${error.message}` ); } } bunFound = true; } } if (!bunFound) { throw new Error('bun binary not found in archive'); } // Clean up fs.unlinkSync(tempFilename); console.log(`Successfully installed bun ${version} for ${platformKey}`); return true; } catch (error) { console.error(`Error installing bun for ${platformKey}: ${error.message}`); // Clean up temporary file if it exists if (fs.existsSync(tempFilename)) { fs.unlinkSync(tempFilename); } // Check if binDir is empty and remove it if so try { const files = fs.readdirSync(binDir); if (files.length === 0) { fs.rmSync(binDir, { recursive: true }); console.log(`Removed empty directory: ${binDir}`); } } catch (cleanupError) { console.warn( `Warning: Failed to clean up directory: ${cleanupError.message}` ); } return false; } } /** * Detects current platform and architecture */ function detectPlatformAndArch() { const platform = os.platform(); const arch = os.arch(); const isMusl = platform === 'linux' && detectIsMusl(); const isBaseline = platform === 'win32'; return { platform, arch, isMusl, isBaseline }; } /** * Attempts to detect if running on MUSL libc */ function detectIsMusl() { try { // Simple check for Alpine Linux which uses MUSL const output = fs.readFileSync('/etc/os-release', 'utf8'); return output.toLowerCase().includes('alpine'); } catch (error) { console.error(`Error detecting MUSL: ${error}`); return false; } } /** * Main function to install bun */ async function installBun() { // Get the latest version if no specific version is provided const version = DEFAULT_BUN_VERSION; console.log(`Using bun version: ${version}`); const { platform, arch, isMusl, isBaseline } = detectPlatformAndArch(); console.log( `Installing bun ${version} for ${platform}-${arch}${isMusl ? ' (MUSL)' : ''}${isBaseline ? ' (baseline)' : ''}...` ); const isInstalled = await downloadBunBinary( BUN_RELEASE_BASE_URL, platform, arch, version, isMusl, isBaseline ); if (!isInstalled) { throw new Error(`Failed to download bun ${version} from default source`); } } // Run the installation installBun() .then(() => { console.log('Installation successful'); process.exit(0); }) .catch((error) => { console.error('Installation failed:', error); process.exit(1); });