selah-cli
Version:
Zero-setup AWS deployment for Bolt.new apps. Build in Bolt, deploy to production in 3 minutes with AI guidance.
290 lines (245 loc) ⢠9.62 kB
JavaScript
import { spawn } from 'cross-spawn';
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import which from 'which';
import { fileURLToPath } from 'url';
/**
* Cross-platform AWS CLI installer for Selah
* Automatically installs AWS CLI when users run: npm install selah-cli
*/
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Check if running in WebContainers (Bolt.new, StackBlitz, etc.) - DO THIS FIRST!
function isWebContainer() {
// More comprehensive WebContainer detection
const webContainerIndicators = [
process.env.SHELL === '/bin/jsh',
process.env.TERM === 'xterm-color',
process.env.STACKBLITZ,
process.env.WEBCONTAINER,
process.env.BOLT_NEW,
process.env.CODESANDBOX_SSE,
process.cwd().includes('/home/project'),
process.cwd().includes('/tmp/'),
// Bolt.new specific indicators
process.env.HOME === '/home/project',
process.env.PWD?.includes('/tmp/'),
// Check if we're running in a sandboxed environment
!process.env.SUDO_UID && process.getuid && process.getuid() !== 0 && process.cwd().includes('/tmp'),
// Additional browser environment checks
typeof window !== 'undefined' || process.title === 'browser'
];
return webContainerIndicators.some(indicator => indicator);
}
// Exit immediately if in WebContainer environment
if (isWebContainer()) {
console.log(`š WebContainer environment detected (Bolt.new/StackBlitz)`);
console.log(`āļø Skipping AWS CLI installation - not needed in browser environment`);
console.log(`ā
Selah CLI ready to use with serverless backend!`);
console.log(`\nš Selah works in Bolt.new without AWS CLI:`);
console.log(` - Uses Supabase Edge Functions for backend`);
console.log(` - Generates infrastructure files locally`);
console.log(` - No native binaries required`);
console.log(`\nš” Commands available:`);
console.log(` npm run bolt:init # Initialize Selah project`);
console.log(` npm run bolt:analyze # Analyze with AI Cloud Engineer`);
console.log(` npm run bolt:deploy # Generate AWS infrastructure`);
process.exit(0);
}
const platform = os.platform();
const arch = os.arch();
console.log(`š§ Selah AWS CLI Auto-Installer`);
console.log(`š± Platform: ${platform} (${arch})`);
async function checkAwsCliExists() {
try {
const awsPath = await which('aws');
console.log(`ā
AWS CLI already installed at: ${awsPath}`);
// Check version
const result = spawn.sync('aws', ['--version'], { encoding: 'utf8' });
if (result.stdout) {
console.log(`š Version: ${result.stdout.trim()}`);
return true;
}
} catch (error) {
console.log(`ā AWS CLI not found in PATH`);
return false;
}
}
async function downloadFile(url, outputPath) {
console.log(`ā¬ļø Downloading: ${url}`);
try {
const fetch = (await import('node-fetch')).default;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
await fs.writeFile(outputPath, buffer);
console.log(`ā
Downloaded to: ${outputPath}`);
return true;
} catch (error) {
console.error(`ā Download failed: ${error.message}`);
return false;
}
}
async function installAwsCliWindows() {
console.log(`šŖ Installing AWS CLI for Windows...`);
const tempDir = os.tmpdir();
const installerPath = path.join(tempDir, 'AWSCLIV2.msi');
const downloadUrl = 'https://awscli.amazonaws.com/AWSCLIV2.msi';
try {
// Download MSI installer
const downloaded = await downloadFile(downloadUrl, installerPath);
if (!downloaded) return false;
// Run MSI installer silently
console.log(`š§ Installing AWS CLI...`);
const result = spawn.sync('msiexec', ['/i', installerPath, '/qn'], {
stdio: 'inherit',
timeout: 300000 // 5 minutes
});
if (result.status === 0) {
console.log(`ā
AWS CLI installed successfully!`);
// Clean up
await fs.unlink(installerPath).catch(() => {});
return true;
} else {
console.error(`ā Installation failed with exit code: ${result.status}`);
return false;
}
} catch (error) {
console.error(`ā Installation error: ${error.message}`);
return false;
}
}
async function installAwsCliMacOS() {
console.log(`š Installing AWS CLI for macOS...`);
const tempDir = os.tmpdir();
const installerPath = path.join(tempDir, 'AWSCLIV2.pkg');
const downloadUrl = 'https://awscli.amazonaws.com/AWSCLIV2.pkg';
try {
// Download PKG installer
const downloaded = await downloadFile(downloadUrl, installerPath);
if (!downloaded) return false;
// Run PKG installer with sudo
console.log(`š§ Installing AWS CLI (may require admin password)...`);
const result = spawn.sync('sudo', ['installer', '-pkg', installerPath, '-target', '/'], {
stdio: 'inherit',
timeout: 300000 // 5 minutes
});
if (result.status === 0) {
console.log(`ā
AWS CLI installed successfully!`);
// Clean up
await fs.unlink(installerPath).catch(() => {});
return true;
} else {
console.error(`ā Installation failed with exit code: ${result.status}`);
return false;
}
} catch (error) {
console.error(`ā Installation error: ${error.message}`);
return false;
}
}
async function installAwsCliLinux() {
console.log(`š§ Installing AWS CLI for Linux...`);
const tempDir = os.tmpdir();
const zipPath = path.join(tempDir, 'awscliv2.zip');
const extractDir = path.join(tempDir, 'aws-cli-install');
// Determine download URL based on architecture
const downloadUrl = arch === 'arm64' || arch === 'aarch64'
? 'https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip'
: 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip';
try {
// Download ZIP file
const downloaded = await downloadFile(downloadUrl, zipPath);
if (!downloaded) return false;
// Create extraction directory
await fs.mkdir(extractDir, { recursive: true });
// Extract ZIP file
console.log(`š¦ Extracting AWS CLI...`);
const unzipResult = spawn.sync('unzip', ['-q', zipPath, '-d', extractDir], {
stdio: 'inherit'
});
if (unzipResult.status !== 0) {
console.error(`ā Failed to extract AWS CLI`);
return false;
}
// Run installer
console.log(`š§ Installing AWS CLI (may require admin password)...`);
const installResult = spawn.sync('sudo', [path.join(extractDir, 'aws', 'install')], {
stdio: 'inherit',
timeout: 300000 // 5 minutes
});
if (installResult.status === 0) {
console.log(`ā
AWS CLI installed successfully!`);
// Clean up
await fs.rmdir(extractDir, { recursive: true }).catch(() => {});
await fs.unlink(zipPath).catch(() => {});
return true;
} else {
console.error(`ā Installation failed with exit code: ${installResult.status}`);
return false;
}
} catch (error) {
console.error(`ā Installation error: ${error.message}`);
return false;
}
}
async function showFallbackInstructions() {
console.log(`\nā ļø Automatic installation failed. Please install AWS CLI manually:`);
console.log(`\nš Installation Instructions:`);
if (platform === 'win32') {
console.log(`šŖ Windows:`);
console.log(` 1. Download: https://awscli.amazonaws.com/AWSCLIV2.msi`);
console.log(` 2. Run the installer`);
} else if (platform === 'darwin') {
console.log(`š macOS:`);
console.log(` 1. Download: https://awscli.amazonaws.com/AWSCLIV2.pkg`);
console.log(` 2. Run: sudo installer -pkg AWSCLIV2.pkg -target /`);
} else {
console.log(`š§ Linux:`);
console.log(` 1. curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"`);
console.log(` 2. unzip awscliv2.zip`);
console.log(` 3. sudo ./aws/install`);
}
console.log(`\nš Official Guide: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html`);
}
async function main() {
try {
// Skip installation if AWS CLI already exists
const exists = await checkAwsCliExists();
if (exists) {
console.log(`\nš Selah is ready to use with existing AWS CLI!`);
return;
}
console.log(`\nš Installing AWS CLI automatically...`);
let success = false;
if (platform === 'win32') {
success = await installAwsCliWindows();
} else if (platform === 'darwin') {
success = await installAwsCliMacOS();
} else if (platform === 'linux') {
success = await installAwsCliLinux();
} else {
console.log(`ā Unsupported platform: ${platform}`);
}
if (success) {
console.log(`\nš AWS CLI installation complete!`);
console.log(`⨠Selah is now ready to deploy your infrastructure to AWS!`);
console.log(`\nš Next steps:`);
console.log(` 1. Configure AWS credentials: aws configure`);
console.log(` 2. Run: npx selah analyze`);
} else {
await showFallbackInstructions();
}
} catch (error) {
console.error(`ā Installation failed: ${error.message}`);
await showFallbackInstructions();
}
}
// Run main function
main().catch(console.error);
export { main as installAwsCli };