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

371 lines 15.2 kB
#!/usr/bin/env node /** * OpenAI Setup Command * * Simple setup for OpenAI API integration: * - OPENAI_API_KEY (required) * - OPENAI_MODEL (optional, with popular choices) * * Follows the same UX patterns as setup-gcp and setup-bedrock */ import fs from "fs"; import path from "path"; import inquirer from "inquirer"; import chalk from "chalk"; import ora from "ora"; import { logger } from "../../lib/utils/logger.js"; import { getTopModelChoices } from "../../lib/utils/modelChoices.js"; import { AIProviderName, } from "../../lib/types/index.js"; import { maskCredential } from "../utils/maskCredential.js"; export async function handleOpenAISetup(argv) { try { const options = { checkOnly: argv.check || false, interactive: !argv.nonInteractive, }; logger.always(chalk.blue("🔍 Checking OpenAI configuration...")); // Step 1: Check for existing configuration const hasApiKey = !!process.env.OPENAI_API_KEY; const hasModel = !!process.env.OPENAI_MODEL; // Display current status displayCurrentStatus(hasApiKey, hasModel); // Check-only mode - show status and exit if (options.checkOnly) { if (hasApiKey && process.env.OPENAI_API_KEY) { logger.always(chalk.green("✅ OpenAI setup complete")); logger.always(` API Key: ${maskCredential(process.env.OPENAI_API_KEY)}`); if (hasModel) { logger.always(` Model: ${process.env.OPENAI_MODEL}`); } else { logger.always(" Model: (using provider default)"); } } else { logger.always(chalk.yellow("⚠️ OpenAI setup incomplete")); } return; } const config = {}; // Step 2: Handle existing configuration if (hasApiKey && process.env.OPENAI_API_KEY) { logger.always(chalk.green("✅ OpenAI API key found in environment")); logger.always(` API Key: ${maskCredential(process.env.OPENAI_API_KEY)}`); if (hasModel) { logger.always(` Model: ${process.env.OPENAI_MODEL}`); } else { logger.always(" Model: (using provider default)"); } if (options.interactive) { const { reconfigure } = await inquirer.prompt([ { type: "confirm", name: "reconfigure", message: "OpenAI is already configured. Do you want to reconfigure?", default: false, }, ]); if (!reconfigure) { // Still offer model selection if no model is set if (!hasModel) { const { wantsCustomModel } = await inquirer.prompt([ { type: "confirm", name: "wantsCustomModel", message: "Do you want to specify a custom OpenAI model? (optional)", default: false, }, ]); if (wantsCustomModel) { config.model = await promptForModel(); } } else { // Offer to change existing model const { wantsChangeModel } = await inquirer.prompt([ { type: "confirm", name: "wantsChangeModel", message: `Do you want to change the OpenAI model? (current: ${process.env.OPENAI_MODEL})`, default: false, }, ]); if (wantsChangeModel) { config.model = await promptForModel(); } } if (config.model) { await updateEnvFile(config); logger.always(chalk.green("✅ Model configuration updated!")); logger.always(` OPENAI_MODEL=${config.model}`); } else { logger.always(chalk.blue("👍 Keeping existing configuration.")); } // Show usage example showUsageExample(); return; } else { // User chose to reconfigure - mark this for proper handling logger.always(chalk.blue("📝 Reconfiguring OpenAI setup...")); config.isReconfiguring = true; } } else { // Non-interactive mode - just use existing credentials logger.always(chalk.green("✅ Setup complete! Using existing OpenAI configuration.")); return; } } // Step 3: Interactive setup for missing or reconfiguring credentials if (options.interactive) { const isReconfiguring = config.isReconfiguring === true; // Handle API key setup/reconfiguration if (!hasApiKey) { // No API key exists - prompt for it logger.always(""); logger.always(chalk.yellow("📋 To get your OpenAI API key:")); logger.always("1. Visit: https://platform.openai.com/api-keys"); logger.always("2. Log in to your OpenAI account"); logger.always("3. Click 'Create new secret key'"); logger.always("4. Copy the API key (starts with sk-)"); logger.always(""); const { apiKey } = await inquirer.prompt([ { type: "password", name: "apiKey", message: "Enter your OpenAI API key:", validate: validateApiKey, }, ]); config.apiKey = apiKey.trim(); } else if (isReconfiguring) { // API key exists and user is reconfiguring - ask if they want to change it const { wantsChangeApiKey } = await inquirer.prompt([ { type: "confirm", name: "wantsChangeApiKey", message: `Do you want to change the OpenAI API key? (current: ${process.env.OPENAI_API_KEY ? maskCredential(process.env.OPENAI_API_KEY) : "****"})`, default: false, }, ]); if (wantsChangeApiKey) { logger.always(""); logger.always(chalk.yellow("📋 To get your OpenAI API key:")); logger.always("1. Visit: https://platform.openai.com/api-keys"); logger.always("2. Log in to your OpenAI account"); logger.always("3. Click 'Create new secret key'"); logger.always("4. Copy the API key (starts with sk-)"); logger.always(""); const { apiKey } = await inquirer.prompt([ { type: "password", name: "apiKey", message: "Enter your new OpenAI API key (replacing existing):", validate: validateApiKey, }, ]); config.apiKey = apiKey.trim(); } } // Prompt for model selection const { wantsCustomModel } = await inquirer.prompt([ { type: "confirm", name: "wantsCustomModel", message: hasModel ? `Do you want to change the OpenAI model? (current: ${process.env.OPENAI_MODEL})` : "Do you want to specify a custom OpenAI model? (optional - will use default if not specified)", default: false, }, ]); if (wantsCustomModel) { config.model = await promptForModel(); } } else { // Non-interactive mode logger.always(chalk.yellow("⚠️ Non-interactive mode: setup incomplete")); logger.always(chalk.yellow("💡 Run without --non-interactive to configure OpenAI")); return; } // Step 4: Update .env file if (config.apiKey || config.model) { await updateEnvFile(config); logger.always(chalk.green("✅ OpenAI setup complete!")); if (config.apiKey) { logger.always(` API Key: ${maskCredential(config.apiKey)}`); } if (config.model) { logger.always(` Model: ${config.model}`); } // Show usage example showUsageExample(); } else if (options.interactive && !options.checkOnly) { logger.always(chalk.green("✅ Setup complete!")); showUsageExample(); } } catch (error) { logger.error(chalk.red("❌ OpenAI setup failed:")); logger.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); process.exit(1); } } /** * Display current configuration status */ function displayCurrentStatus(hasApiKey, hasModel) { if (hasApiKey) { logger.always(chalk.green("✔ OPENAI_API_KEY found in environment")); } else { logger.always(chalk.red("✘ OPENAI_API_KEY not found")); } if (hasModel) { logger.always(chalk.green(`✔ OPENAI_MODEL found: ${process.env.OPENAI_MODEL}`)); } else { logger.always(chalk.yellow("⚠ OPENAI_MODEL not set (will use provider default)")); } } /** * Validate OpenAI API key format */ function validateApiKey(input) { if (!input.trim()) { return "OpenAI API key is required"; } const trimmed = input.trim(); if (!trimmed.startsWith("sk-")) { return "OpenAI API key should start with 'sk-'"; } if (trimmed.length < 20) { return "OpenAI API key seems too short"; } // Basic format check: sk-[project id or old format][32+ char random string] if (!/^sk-[a-zA-Z0-9_-]{20,}$/.test(trimmed)) { return "Invalid OpenAI API key format"; } return true; } /** * Prompt user for model selection */ async function promptForModel() { const { modelChoice } = await inquirer.prompt([ { type: "select", name: "modelChoice", message: "Select an OpenAI model:", choices: getTopModelChoices(AIProviderName.OPENAI, 5), }, ]); if (modelChoice === "custom") { const { customModel } = await inquirer.prompt([ { type: "input", name: "customModel", message: "Enter your custom OpenAI model name:", validate: (input) => { if (!input.trim()) { return "Model name is required"; } // Basic validation - OpenAI models typically follow certain patterns const trimmed = input.trim(); if (!/^[a-z0-9-._]+$/i.test(trimmed)) { return "Model name should contain only letters, numbers, hyphens, dots, and underscores"; } return true; }, }, ]); return customModel.trim(); } return modelChoice; } /** * Update .env file with OpenAI configuration */ async function updateEnvFile(config) { const envPath = path.join(process.cwd(), ".env"); const spinner = ora("💾 Updating .env file...").start(); try { let envContent = ""; // Read existing .env file if it exists if (fs.existsSync(envPath)) { envContent = fs.readFileSync(envPath, "utf8"); } // Parse existing environment variables const envLines = envContent.split("\n"); const existingVars = new Map(); const otherLines = []; for (const line of envLines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith("#")) { const equalsIndex = trimmed.indexOf("="); if (equalsIndex > 0) { const key = trimmed.substring(0, equalsIndex); const value = trimmed.substring(equalsIndex + 1); existingVars.set(key, value); } else { otherLines.push(line); } } else { otherLines.push(line); } } // Update OpenAI variables if (config.apiKey) { existingVars.set("OPENAI_API_KEY", config.apiKey); } if (config.model) { existingVars.set("OPENAI_MODEL", config.model); } // Reconstruct .env content preserving structure const newEnvLines = []; // Add non-variable lines first (comments, empty lines) for (const line of otherLines) { newEnvLines.push(line); } // Add separator comment for OpenAI if needed if ((config.apiKey || config.model) && !envContent.includes("OPENAI CONFIGURATION") && !envContent.includes("# OpenAI")) { if (newEnvLines.length > 0 && newEnvLines[newEnvLines.length - 1].trim()) { newEnvLines.push(""); } newEnvLines.push("# OpenAI Configuration"); } // Add all environment variables for (const [key, value] of existingVars.entries()) { newEnvLines.push(`${key}=${value}`); } // Write updated content const finalContent = newEnvLines.join("\n") + (newEnvLines.length > 0 ? "\n" : ""); fs.writeFileSync(envPath, finalContent, "utf8"); spinner.succeed(chalk.green("✔ .env file updated successfully")); } catch (error) { spinner.fail(chalk.red("❌ Failed to update .env file")); logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`)); throw error; } } /** * Show usage example */ function showUsageExample() { logger.always(""); logger.always(chalk.green("🚀 You can now use OpenAI with the NeuroLink CLI:")); logger.always(chalk.cyan(" pnpm cli generate 'Hello from OpenAI!' --provider openai")); logger.always(chalk.cyan(" pnpm cli generate 'Explain quantum computing' --provider openai")); } //# sourceMappingURL=setup-openai.js.map