UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

457 lines 18.3 kB
#!/usr/bin/env node /** * AWS Bedrock Setup Command for New Developers * * Checks for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY * Auto-detects AWS CLI configuration, prompts for missing config, updates .env safely */ import * as fs from "fs"; import * as path from "path"; import * as os from "os"; import inquirer from "inquirer"; import chalk from "chalk"; import ora from "ora"; import { logger } from "../../lib/utils/logger.js"; import { updateEnvFile as envUpdate } from "../utils/envManager.js"; import { getTopModelChoices } from "../../lib/utils/modelChoices.js"; import { AIProviderName, } from "../../lib/types/index.js"; import { maskCredential } from "../utils/maskCredential.js"; export async function handleBedrockSetup(argv) { try { const options = { checkOnly: argv.check || false, interactive: !argv.nonInteractive, }; logger.always(chalk.blue("🔍 Checking for existing AWS Bedrock configuration...")); const configStatus = checkExistingConfiguration(); const config = {}; // Handle existing credentials if (configStatus.hasAccessKey && configStatus.hasSecretKey) { const result = await handleExistingCredentials(configStatus, options, config); if (result.shouldReturn) { return; } } else { displayCurrentStatus(configStatus); if (options.checkOnly) { return; } } // Auto-detect AWS CLI configuration await detectAndDisplayAWSConfig(configStatus, config); // Interactive credential setup if (options.interactive) { const setupResult = await handleInteractiveCredentialSetup(configStatus, config, options); if (setupResult.shouldReturn) { return; } } // Model selection if (options.interactive) { await handleModelSelection(config); } // Update .env and show completion await finalizeSetup(config, options); } catch (error) { logger.error(chalk.red("❌ AWS Bedrock setup failed:")); logger.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); process.exit(1); } } async function detectAWSCredentials() { try { const credentialsPath = path.join(os.homedir(), ".aws", "credentials"); if (!fs.existsSync(credentialsPath)) { return {}; } const credentialsContent = fs.readFileSync(credentialsPath, "utf-8"); // Parse AWS credentials file format const lines = credentialsContent.split("\n"); let inDefaultProfile = false; const credentials = {}; for (const line of lines) { const trimmed = line.trim(); // Profile section headers if (trimmed.startsWith("[") && trimmed.endsWith("]")) { const currentProfile = trimmed.slice(1, -1); inDefaultProfile = currentProfile === "default"; continue; } // Skip if not in default profile (for simplicity) if (!inDefaultProfile) { continue; } // Key-value pairs if (trimmed.includes("=")) { const [key, ...valueParts] = trimmed.split("="); const value = valueParts.join("=").trim(); credentials[key.trim()] = value; } } return { accessKeyId: credentials.aws_access_key_id, secretAccessKey: credentials.aws_secret_access_key, }; } catch { // Silently fail - this is just auto-detection return {}; } } async function detectAWSConfig() { try { const configPath = path.join(os.homedir(), ".aws", "config"); if (!fs.existsSync(configPath)) { return {}; } const configContent = fs.readFileSync(configPath, "utf-8"); // Parse AWS config file format const lines = configContent.split("\n"); let inDefaultProfile = false; const config = {}; for (const line of lines) { const trimmed = line.trim(); // Profile section headers - config uses [profile name] format except for default if (trimmed.startsWith("[") && trimmed.endsWith("]")) { const profileMatch = trimmed.match(/^\[(?:profile\s+)?(.+)\]$/); if (profileMatch) { const currentProfile = profileMatch[1]; inDefaultProfile = currentProfile === "default"; } continue; } // Skip if not in default profile if (!inDefaultProfile) { continue; } // Key-value pairs if (trimmed.includes("=")) { const [key, ...valueParts] = trimmed.split("="); const value = valueParts.join("=").trim(); config[key.trim()] = value; } } return { region: config.region, }; } catch { // Silently fail - this is just auto-detection return {}; } } function checkExistingConfiguration() { return { hasAccessKey: !!process.env.AWS_ACCESS_KEY_ID, hasSecretKey: !!process.env.AWS_SECRET_ACCESS_KEY, hasRegion: !!process.env.AWS_REGION, }; } function displayCurrentStatus(configStatus) { if (configStatus.hasAccessKey) { logger.always(chalk.green("✔ AWS_ACCESS_KEY_ID found in environment")); } else { logger.always(chalk.red("✘ AWS_ACCESS_KEY_ID not found")); } if (configStatus.hasSecretKey) { logger.always(chalk.green("✔ AWS_SECRET_ACCESS_KEY found in environment")); } else { logger.always(chalk.red("✘ AWS_SECRET_ACCESS_KEY not found")); } if (configStatus.hasRegion) { logger.always(chalk.green(`✔ AWS_REGION found in environment: ${process.env.AWS_REGION}`)); } else { logger.always(chalk.yellow("⚠️ AWS_REGION not set (will default to us-east-1)")); } } async function handleExistingCredentials(configStatus, options, config) { logger.always(chalk.green("✔ AWS_ACCESS_KEY_ID found in environment")); logger.always(chalk.green("✔ AWS_SECRET_ACCESS_KEY found in environment")); logger.always(chalk.green(`✔ AWS_REGION: ${process.env.AWS_REGION || "us-east-1 (default)"}`)); if (options.checkOnly) { logger.always(chalk.green("✅ Configuration check complete - credentials are available!")); return { shouldReturn: true }; } if (options.interactive) { const { useExisting } = await inquirer.prompt([ { type: "confirm", name: "useExisting", message: "AWS credentials detected. Do you want to use the existing credentials?", default: true, }, ]); if (useExisting) { logger.always(chalk.green("✅ Using existing AWS credentials.")); await handleModelSelection(config); await finalizeSetup(config, options); return { shouldReturn: true }; } else { logger.always(chalk.blue("📝 Setting up new AWS credentials...")); } } else { logger.always(chalk.green("✅ Setup complete! Using existing AWS credentials.")); return { shouldReturn: true }; } return { shouldReturn: false }; } async function detectAndDisplayAWSConfig(configStatus, config) { if (!configStatus.hasAccessKey || !configStatus.hasSecretKey) { logger.always(chalk.blue("🔍 Checking for AWS CLI configuration...")); const awsCredentials = await detectAWSCredentials(); if (!configStatus.hasAccessKey && awsCredentials.accessKeyId) { logger.always(chalk.green("✔ Found AWS_ACCESS_KEY_ID in AWS CLI configuration")); config.accessKeyId = awsCredentials.accessKeyId; } if (!configStatus.hasSecretKey && awsCredentials.secretAccessKey) { logger.always(chalk.green("✔ Found AWS_SECRET_ACCESS_KEY in AWS CLI configuration")); config.secretAccessKey = awsCredentials.secretAccessKey; } } if (!configStatus.hasRegion) { const awsConfig = await detectAWSConfig(); if (awsConfig.region) { logger.always(chalk.green(`✔ Found AWS_REGION in AWS CLI configuration: ${awsConfig.region}`)); config.region = awsConfig.region; } } } async function handleInteractiveCredentialSetup(configStatus, config, _options) { const isReconfiguring = configStatus.hasAccessKey && configStatus.hasSecretKey; // If no credentials found at all, offer choice of setup methods if (!configStatus.hasAccessKey && !configStatus.hasSecretKey && !config.accessKeyId && !config.secretAccessKey) { const setupMethod = await promptForSetupMethod(); if (setupMethod === "terminal") { displayTerminalInstructions(); return { shouldReturn: true }; } logger.always(chalk.blue("📝 Setting up AWS credentials manually...")); } // Prompt for credentials await promptForCredentials(configStatus, config, isReconfiguring); return { shouldReturn: false }; } async function promptForSetupMethod() { logger.always(""); logger.always(chalk.yellow("📋 No AWS credentials found. Choose your setup method:")); logger.always(""); logger.always(chalk.cyan("Option 1: Use temporary AWS credentials from AWS Console")); logger.always(" - Go to AWS Console → IAM → Security credentials"); logger.always(" - Create temporary credentials"); logger.always(" - Export them in your terminal, then restart this command"); logger.always(""); logger.always(chalk.cyan("Option 2: Manually enter your credentials")); logger.always(" - Enter Access Key ID and Secret Access Key step by step"); logger.always(""); const { setupMethod } = await inquirer.prompt([ { type: "select", name: "setupMethod", message: "How would you like to provide your AWS credentials?", choices: [ { name: "Paste temporary credentials in terminal and restart", value: "terminal", }, { name: "Manually enter credentials step by step", value: "manual", }, ], }, ]); return setupMethod; } function displayTerminalInstructions() { logger.always(""); logger.always(chalk.blue("📝 Follow these steps:")); logger.always(""); logger.always("1. Copy and paste these commands in your terminal:"); logger.always(chalk.gray(' export AWS_ACCESS_KEY_ID="your-access-key-id"')); logger.always(chalk.gray(' export AWS_SECRET_ACCESS_KEY="your-secret-access-key"')); logger.always(chalk.gray(' export AWS_SESSION_TOKEN="your-session-token" # if using temporary credentials')); logger.always(chalk.gray(' export AWS_REGION="your-preferred-region" # optional')); logger.always(""); logger.always("2. Then run this command again:"); logger.always(chalk.cyan(" pnpm cli setup-bedrock")); logger.always(""); logger.always(chalk.green("✅ Instructions provided! Set your credentials and restart the command.")); } async function promptForCredentials(configStatus, config, isReconfiguring) { // Prompt for access key if (isReconfiguring || (!configStatus.hasAccessKey && !config.accessKeyId)) { const { accessKey } = await inquirer.prompt([ { type: "input", name: "accessKey", message: isReconfiguring ? `Enter your AWS Access Key ID ${configStatus.hasAccessKey ? "(replacing existing)" : ""}:` : "Enter your AWS Access Key ID:", validate: (input) => { if (!input.trim()) { return "AWS Access Key ID is required"; } if (!input.startsWith("AKIA") && !input.startsWith("ASIA")) { return "AWS Access Key ID should start with AKIA or ASIA"; } return true; }, }, ]); config.accessKeyId = accessKey.trim(); } // Prompt for secret key if (isReconfiguring || (!configStatus.hasSecretKey && !config.secretAccessKey)) { const { secretKey } = await inquirer.prompt([ { type: "password", name: "secretKey", message: isReconfiguring ? `Enter your AWS Secret Access Key ${configStatus.hasSecretKey ? "(replacing existing)" : ""}:` : "Enter your AWS Secret Access Key:", validate: (input) => { if (!input.trim()) { return "AWS Secret Access Key is required"; } if (input.length < 20) { return "AWS Secret Access Key seems too short"; } return true; }, }, ]); config.secretAccessKey = secretKey.trim(); } // Prompt for region if (isReconfiguring || (!configStatus.hasRegion && !config.region)) { const { region } = await inquirer.prompt([ { type: "input", name: "region", message: isReconfiguring ? `Enter your AWS Region ${configStatus.hasRegion ? "(current: " + process.env.AWS_REGION + ")" : "(or press Enter for us-east-1)"}:` : "Enter your AWS Region (or press Enter for us-east-1):", default: configStatus.hasRegion ? process.env.AWS_REGION : "us-east-1", validate: (input) => { const trimmed = input.trim(); if (!trimmed) { return true; // Allow default } if (!/^[a-z0-9-]+$/.test(trimmed)) { return "AWS Region should contain only lowercase letters, numbers, and hyphens"; } return true; }, }, ]); config.region = region.trim() || (configStatus.hasRegion ? process.env.AWS_REGION : "us-east-1"); } } async function handleModelSelection(config) { const hasModel = !!(process.env.BEDROCK_MODEL || process.env.BEDROCK_MODEL_ID); const { wantsCustomModel } = await inquirer.prompt([ { type: "confirm", name: "wantsCustomModel", message: hasModel ? `Do you want to change the Bedrock model? (current: ${process.env.BEDROCK_MODEL || process.env.BEDROCK_MODEL_ID})` : "Do you want to specify a custom Bedrock model? (optional - will use default if not specified)", default: false, }, ]); if (wantsCustomModel) { const { model } = await inquirer.prompt([ { type: "select", name: "model", message: "Select a Bedrock model:", choices: getTopModelChoices(AIProviderName.BEDROCK, 5), }, ]); if (model === "custom") { const { customModel } = await inquirer.prompt([ { type: "input", name: "customModel", message: "Enter your custom Bedrock model ID or ARN:", validate: (input) => { if (!input.trim()) { return "Model ID/ARN is required"; } return true; }, }, ]); config.model = customModel.trim(); } else { config.model = model; } } } async function finalizeSetup(config, options) { if (config.accessKeyId || config.secretAccessKey || config.region || config.model) { const newVars = {}; if (config.accessKeyId) { newVars.AWS_ACCESS_KEY_ID = config.accessKeyId; } if (config.secretAccessKey) { newVars.AWS_SECRET_ACCESS_KEY = config.secretAccessKey; } if (config.region) { newVars.AWS_REGION = config.region; } if (config.model) { newVars.BEDROCK_MODEL_ID = config.model; } const spinner = ora("💾 Updating .env file...").start(); try { const result = envUpdate(newVars, ".env", true); spinner.succeed(chalk.green(`✔ .env updated (added: ${result.added.length}, updated: ${result.updated.length})`)); } catch (e) { spinner.fail(chalk.red("❌ Failed to update .env file")); throw e; } logger.always(chalk.green("✅ Setup complete!")); if (config.accessKeyId) { logger.always(` AWS_ACCESS_KEY_ID=${maskCredential(config.accessKeyId)}`); } if (config.secretAccessKey) { logger.always(` AWS_SECRET_ACCESS_KEY=${maskCredential(config.secretAccessKey)}`); } if (config.region) { logger.always(` AWS_REGION=${config.region}`); } if (config.model) { logger.always(` BEDROCK_MODEL_ID=${config.model}`); } displayUsageExample(); } else if (options.interactive && !options.checkOnly) { logger.always(chalk.green("✅ Setup complete!")); displayUsageExample(); } } function displayUsageExample() { logger.always(""); logger.always(chalk.green("🚀 You can now use AWS Bedrock with the NeuroLink CLI:")); logger.always(chalk.cyan(" pnpm cli generate 'Hello from Bedrock!' --provider bedrock")); } //# sourceMappingURL=setup-bedrock.js.map