UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with external MCP server integration, multi-provider support, and professional CLI. Connect to 65+ MCP servers for filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major pr

640 lines (639 loc) 23.5 kB
#!/usr/bin/env node /** * NeuroLink CLI Configuration Management * * Enhanced configuration system with interactive setup, * multi-profile support, and smart validation. */ import inquirer from 'inquirer'; import fs from 'fs'; import path from 'path'; import os from 'os'; import chalk from 'chalk'; import { z } from 'zod'; // Configuration schema for validation const ConfigSchema = z.object({ defaultProvider: z.enum(['auto', 'openai', 'bedrock', 'vertex', 'anthropic', 'azure', 'google-ai', 'huggingface', 'ollama', 'mistral']).default('auto'), providers: z.object({ openai: z.object({ apiKey: z.string().optional(), model: z.string().default('gpt-4'), baseURL: z.string().optional() }).optional(), bedrock: z.object({ region: z.string().optional(), accessKeyId: z.string().optional(), secretAccessKey: z.string().optional(), sessionToken: z.string().optional(), model: z.string().default('arn:aws:bedrock:us-east-2:225681119357:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0') }).optional(), vertex: z.object({ projectId: z.string().optional(), location: z.string().default('us-east5'), credentials: z.string().optional(), serviceAccountKey: z.string().optional(), clientEmail: z.string().optional(), privateKey: z.string().optional(), model: z.string().default('gemini-1.5-pro') }).optional(), anthropic: z.object({ apiKey: z.string().optional(), model: z.string().default('claude-3-5-sonnet-20241022') }).optional(), azure: z.object({ apiKey: z.string().optional(), endpoint: z.string().optional(), deploymentId: z.string().optional(), model: z.string().default('gpt-4') }).optional(), 'google-ai': z.object({ apiKey: z.string().optional(), model: z.string().default('gemini-1.5-pro-latest') }).optional(), huggingface: z.object({ apiKey: z.string().optional(), model: z.string().default('microsoft/DialoGPT-large') }).optional(), ollama: z.object({ baseUrl: z.string().default('http://localhost:11434'), model: z.string().default('llama2'), timeout: z.number().default(60000) }).optional(), mistral: z.object({ apiKey: z.string().optional(), model: z.string().default('mistral-small') }).optional() }).default({}), profiles: z.record(z.string(), z.any()).default({}), preferences: z.object({ outputFormat: z.enum(['text', 'json', 'yaml']).default('text'), temperature: z.number().min(0).max(2).default(0.7), maxTokens: z.number().min(1).max(4000).default(500), enableLogging: z.boolean().default(false), enableCaching: z.boolean().default(true), cacheStrategy: z.enum(['memory', 'file', 'redis']).default('memory') }).default({}) }); export class ConfigManager { configDir; configFile; config; constructor() { this.configDir = path.join(os.homedir(), '.neurolink'); this.configFile = path.join(this.configDir, 'config.json'); this.config = this.loadConfig(); } /** * Load configuration from file or create default */ loadConfig() { try { if (fs.existsSync(this.configFile)) { const configData = JSON.parse(fs.readFileSync(this.configFile, 'utf8')); return ConfigSchema.parse(configData); } } catch (error) { console.warn(chalk.yellow(`⚠️ Invalid config file: ${error instanceof Error ? error.message : 'Unknown error'}`)); } return ConfigSchema.parse({}); } /** * Save configuration to file */ saveConfig() { try { // Ensure config directory exists if (!fs.existsSync(this.configDir)) { fs.mkdirSync(this.configDir, { recursive: true }); } // Validate before saving const validatedConfig = ConfigSchema.parse(this.config); fs.writeFileSync(this.configFile, JSON.stringify(validatedConfig, null, 2)); console.log(chalk.green(`✅ Configuration saved to ${this.configFile}`)); } catch (error) { console.error(chalk.red(`❌ Failed to save config: ${error instanceof Error ? error.message : 'Unknown error'}`)); process.exit(1); } } /** * Interactive configuration setup */ async initInteractive() { console.log(chalk.blue('🧠 NeuroLink Configuration Setup\n')); try { // Basic preferences const preferences = await inquirer.prompt([ { type: 'list', name: 'defaultProvider', message: 'Select your default AI provider:', choices: [ { name: 'Auto (recommended) - Automatically select best available', value: 'auto' }, { name: 'OpenAI - GPT models', value: 'openai' }, { name: 'Amazon Bedrock - Claude, Llama, Titan', value: 'bedrock' }, { name: 'Google Vertex AI - Gemini models', value: 'vertex' }, { name: 'Anthropic - Claude models (direct)', value: 'anthropic' }, { name: 'Azure OpenAI - Enterprise GPT', value: 'azure' }, { name: 'Google AI Studio - Gemini models (direct)', value: 'google-ai' }, { name: 'Hugging Face - Open source models', value: 'huggingface' }, { name: 'Mistral AI - European AI with competitive pricing', value: 'mistral' } ], default: this.config.defaultProvider }, { type: 'list', name: 'outputFormat', message: 'Preferred output format:', choices: ['text', 'json', 'yaml'], default: this.config.preferences.outputFormat }, { type: 'number', name: 'temperature', message: 'Default creativity level (0.0 = focused, 1.0 = creative):', default: this.config.preferences.temperature, validate: (value) => value >= 0 && value <= 2 }, { type: 'confirm', name: 'setupProviders', message: 'Would you like to configure provider credentials now?', default: true } ]); // Update config with preferences this.config.defaultProvider = preferences.defaultProvider; this.config.preferences.outputFormat = preferences.outputFormat; this.config.preferences.temperature = preferences.temperature; // Setup providers if requested if (preferences.setupProviders) { await this.setupProviders(); } this.saveConfig(); console.log(chalk.green('\n✅ Configuration setup complete!')); console.log(chalk.blue('💡 You can modify settings anytime with: neurolink config edit')); console.log(chalk.blue('💡 Test your setup with: neurolink status')); } catch (error) { if (error instanceof Error && error.message === 'User force closed the prompt with 0 null') { console.log(chalk.yellow('\n⚠️ Setup cancelled by user')); process.exit(0); } throw error; } } /** * Setup individual providers */ async setupProviders() { const { selectedProviders } = await inquirer.prompt([ { type: 'checkbox', name: 'selectedProviders', message: 'Select providers to configure:', choices: [ { name: 'OpenAI (GPT-4, GPT-3.5)', value: 'openai' }, { name: 'Amazon Bedrock (Claude, Llama)', value: 'bedrock' }, { name: 'Google Vertex AI (Gemini)', value: 'vertex' }, { name: 'Anthropic Direct (Claude)', value: 'anthropic' }, { name: 'Azure OpenAI (Enterprise)', value: 'azure' }, { name: 'Google AI Studio (Gemini Direct)', value: 'google-ai' }, { name: 'Hugging Face (Open Source)', value: 'huggingface' }, { name: 'Ollama (Local AI Models)', value: 'ollama' }, { name: 'Mistral AI (European AI)', value: 'mistral' } ] } ]); for (const provider of selectedProviders) { await this.setupProvider(provider); } } /** * Setup individual provider */ async setupProvider(provider) { console.log(chalk.blue(`\n🔧 Configuring ${provider.toUpperCase()}`)); switch (provider) { case 'openai': await this.setupOpenAI(); break; case 'bedrock': await this.setupBedrock(); break; case 'vertex': await this.setupVertex(); break; case 'anthropic': await this.setupAnthropic(); break; case 'azure': await this.setupAzure(); break; case 'google-ai': await this.setupGoogleAI(); break; case 'huggingface': await this.setupHuggingFace(); break; case 'ollama': await this.setupOllama(); break; case 'mistral': await this.setupMistral(); break; } } /** * OpenAI provider setup */ async setupOpenAI() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'OpenAI API Key (sk-...):', validate: (value) => value.startsWith('sk-') || 'API key should start with "sk-"' }, { type: 'list', name: 'model', message: 'Default model:', choices: ['gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo'], default: 'gpt-4' }, { type: 'input', name: 'baseURL', message: 'Custom base URL (optional):', default: '' } ]); this.config.providers.openai = { apiKey: answers.apiKey, model: answers.model, ...(answers.baseURL && { baseURL: answers.baseURL }) }; } /** * Amazon Bedrock provider setup */ async setupBedrock() { const answers = await inquirer.prompt([ { type: 'input', name: 'region', message: 'AWS Region:', default: 'us-east-1' }, { type: 'input', name: 'accessKeyId', message: 'AWS Access Key ID (optional if using IAM roles):' }, { type: 'password', name: 'secretAccessKey', message: 'AWS Secret Access Key (optional if using IAM roles):' }, { type: 'password', name: 'sessionToken', message: 'AWS Session Token (optional):' }, { type: 'input', name: 'model', message: 'Model ARN:', default: 'arn:aws:bedrock:us-east-2:225681119357:inference-profile/us.anthropic.claude-3-7-sonnet-20250219-v1:0' } ]); this.config.providers.bedrock = { region: answers.region, ...(answers.accessKeyId && { accessKeyId: answers.accessKeyId }), ...(answers.secretAccessKey && { secretAccessKey: answers.secretAccessKey }), ...(answers.sessionToken && { sessionToken: answers.sessionToken }), model: answers.model }; } /** * Google Vertex AI provider setup */ async setupVertex() { const { authMethod } = await inquirer.prompt([ { type: 'list', name: 'authMethod', message: 'Authentication method:', choices: [ { name: 'Service Account File', value: 'file' }, { name: 'Service Account JSON String', value: 'json' }, { name: 'Individual Environment Variables', value: 'env' } ] } ]); const commonAnswers = await inquirer.prompt([ { type: 'input', name: 'projectId', message: 'Google Cloud Project ID:', validate: (value) => value.length > 0 || 'Project ID is required' }, { type: 'input', name: 'location', message: 'Vertex AI Location:', default: 'us-east5' }, { type: 'list', name: 'model', message: 'Default model:', choices: ['gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-pro'], default: 'gemini-1.5-pro' } ]); let authConfig = {}; switch (authMethod) { case 'file': const fileAnswers = await inquirer.prompt([ { type: 'input', name: 'credentials', message: 'Path to service account JSON file:', validate: (value) => fs.existsSync(value) || 'File does not exist' } ]); authConfig = { credentials: fileAnswers.credentials }; break; case 'json': const jsonAnswers = await inquirer.prompt([ { type: 'input', name: 'serviceAccountKey', message: 'Service account JSON string:', validate: (value) => { try { JSON.parse(value); return true; } catch { return 'Invalid JSON'; } } } ]); authConfig = { serviceAccountKey: jsonAnswers.serviceAccountKey }; break; case 'env': const envAnswers = await inquirer.prompt([ { type: 'input', name: 'clientEmail', message: 'Service account email:', validate: (value) => value.includes('@') || 'Invalid email format' }, { type: 'password', name: 'privateKey', message: 'Private key:' } ]); authConfig = { clientEmail: envAnswers.clientEmail, privateKey: envAnswers.privateKey }; break; } this.config.providers.vertex = { projectId: commonAnswers.projectId, location: commonAnswers.location, model: commonAnswers.model, ...authConfig }; } /** * Anthropic provider setup */ async setupAnthropic() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'Anthropic API Key:', validate: (value) => value.length > 0 || 'API key is required' }, { type: 'list', name: 'model', message: 'Default model:', choices: [ 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229' ], default: 'claude-3-5-sonnet-20241022' } ]); this.config.providers.anthropic = answers; } /** * Azure OpenAI provider setup */ async setupAzure() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'Azure OpenAI API Key:' }, { type: 'input', name: 'endpoint', message: 'Azure OpenAI Endpoint:', validate: (value) => value.startsWith('https://') || 'Endpoint should start with https://' }, { type: 'input', name: 'deploymentId', message: 'Deployment ID:' }, { type: 'list', name: 'model', message: 'Model:', choices: ['gpt-4', 'gpt-4-turbo', 'gpt-35-turbo'], default: 'gpt-4' } ]); this.config.providers.azure = answers; } /** * Google AI Studio provider setup */ async setupGoogleAI() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'Google AI API Key:', validate: (value) => value.length > 0 || 'API key is required' }, { type: 'list', name: 'model', message: 'Default model:', choices: [ 'gemini-1.5-pro-latest', 'gemini-2.0-flash-exp', 'gemini-1.5-flash-latest', 'gemini-1.0-pro' ], default: 'gemini-1.5-pro-latest' } ]); this.config.providers['google-ai'] = answers; } /** * Hugging Face provider setup */ async setupHuggingFace() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'Hugging Face API Key:' }, { type: 'input', name: 'model', message: 'Model name:', default: 'microsoft/DialoGPT-large' } ]); this.config.providers.huggingface = answers; } /** * Ollama provider setup */ async setupOllama() { const answers = await inquirer.prompt([ { type: 'input', name: 'baseUrl', message: 'Ollama base URL:', default: 'http://localhost:11434', validate: (value) => value.startsWith('http') || 'URL should start with http:// or https://' }, { type: 'input', name: 'model', message: 'Default model:', default: 'llama2' }, { type: 'number', name: 'timeout', message: 'Request timeout (milliseconds):', default: 60000, validate: (value) => value > 0 || 'Timeout must be positive' } ]); this.config.providers.ollama = answers; } /** * Mistral AI provider setup */ async setupMistral() { const answers = await inquirer.prompt([ { type: 'password', name: 'apiKey', message: 'Mistral AI API Key:', validate: (value) => value.length > 0 || 'API key is required' }, { type: 'list', name: 'model', message: 'Default model:', choices: [ 'mistral-small', 'mistral-medium', 'mistral-large', 'mistral-tiny' ], default: 'mistral-small' } ]); this.config.providers.mistral = answers; } /** * Get current configuration */ getConfig() { return this.config; } /** * Update configuration */ updateConfig(updates) { this.config = { ...this.config, ...updates }; this.saveConfig(); } /** * Show current configuration */ showConfig() { console.log(chalk.blue('📋 Current NeuroLink Configuration\n')); console.log(chalk.cyan('General Settings:')); console.log(` Default Provider: ${chalk.white(this.config.defaultProvider)}`); console.log(` Output Format: ${chalk.white(this.config.preferences.outputFormat)}`); console.log(` Temperature: ${chalk.white(this.config.preferences.temperature)}`); console.log(` Max Tokens: ${chalk.white(this.config.preferences.maxTokens)}`); console.log(chalk.cyan('\nConfigured Providers:')); Object.entries(this.config.providers).forEach(([name, config]) => { if (config && Object.keys(config).length > 0) { console.log(` ${chalk.green('✅')} ${name.toUpperCase()}`); if ('model' in config) { console.log(` Model: ${chalk.white(config.model)}`); } } }); console.log(chalk.cyan('\nConfiguration File:')); console.log(` Location: ${chalk.white(this.configFile)}`); } /** * Validate configuration */ validateConfig() { const errors = []; try { ConfigSchema.parse(this.config); } catch (error) { if (error instanceof z.ZodError) { errors.push(...error.errors.map(e => `${e.path.join('.')}: ${e.message}`)); } } // Check for at least one configured provider const hasProvider = Object.values(this.config.providers).some(provider => provider && Object.keys(provider).length > 0); if (!hasProvider) { errors.push('No providers configured. Run "neurolink config init" to set up providers.'); } return { valid: errors.length === 0, errors }; } /** * Reset configuration to defaults */ resetConfig() { this.config = ConfigSchema.parse({}); this.saveConfig(); console.log(chalk.green('✅ Configuration reset to defaults')); } } // Export for use in other CLI commands export const configManager = new ConfigManager();