@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
815 lines (814 loc) • 29.7 kB
JavaScript
#!/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";
import { CLI_LIMITS } from "../../lib/core/constants.js";
import { logger } from "../../lib/utils/logger.js";
// 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-2.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-2.5-pro"),
})
.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(CLI_LIMITS.maxTokens.min)
.max(CLI_LIMITS.maxTokens.max)
.default(CLI_LIMITS.maxTokens.default),
enableLogging: z.boolean().default(false),
enableCaching: z.boolean().default(true),
cacheStrategy: z.enum(["memory", "file", "redis"]).default("memory"),
defaultEvaluationDomain: z.string().optional(),
enableAnalyticsByDefault: z.boolean().default(false),
enableEvaluationByDefault: z.boolean().default(false),
})
.default({}),
domains: z
.object({
healthcare: z
.object({
evaluationCriteria: z
.array(z.string())
.default(["accuracy", "safety", "compliance", "clarity"]),
analyticsConfig: z
.object({
trackPatientData: z.boolean().default(false),
trackDiagnosticAccuracy: z.boolean().default(true),
trackTreatmentOutcomes: z.boolean().default(true),
})
.default({}),
})
.default({}),
analytics: z
.object({
evaluationCriteria: z
.array(z.string())
.default(["accuracy", "relevance", "completeness", "insight"]),
analyticsConfig: z
.object({
trackDataQuality: z.boolean().default(true),
trackModelPerformance: z.boolean().default(true),
trackBusinessImpact: z.boolean().default(true),
})
.default({}),
})
.default({}),
finance: z
.object({
evaluationCriteria: z
.array(z.string())
.default([
"accuracy",
"risk-awareness",
"compliance",
"timeliness",
]),
analyticsConfig: z
.object({
trackRiskMetrics: z.boolean().default(true),
trackRegulatory: z.boolean().default(true),
trackPortfolioImpact: z.boolean().default(false),
})
.default({}),
})
.default({}),
ecommerce: z
.object({
evaluationCriteria: z
.array(z.string())
.default([
"conversion-potential",
"user-experience",
"revenue-impact",
"practicality",
]),
analyticsConfig: z
.object({
trackConversions: z.boolean().default(true),
trackUserBehavior: z.boolean().default(true),
trackRevenueImpact: z.boolean().default(true),
})
.default({}),
})
.default({}),
})
.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) {
logger.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));
logger.always(chalk.green(`✅ Configuration saved to ${this.configFile}`));
}
catch (error) {
logger.error(chalk.red(`❌ Failed to save config: ${error instanceof Error ? error.message : "Unknown error"}`));
process.exit(1);
}
}
/**
* Interactive configuration setup
*/
async initInteractive() {
logger.always(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: "list",
name: "defaultEvaluationDomain",
message: "Default evaluation domain (optional):",
choices: [
{ name: "None (manual selection)", value: undefined },
{ name: "Healthcare", value: "healthcare" },
{ name: "Analytics", value: "analytics" },
{ name: "Finance", value: "finance" },
{ name: "E-commerce", value: "ecommerce" },
],
default: this.config.preferences.defaultEvaluationDomain,
},
{
type: "confirm",
name: "enableAnalyticsByDefault",
message: "Enable analytics by default?",
default: this.config.preferences.enableAnalyticsByDefault,
},
{
type: "confirm",
name: "enableEvaluationByDefault",
message: "Enable evaluation by default?",
default: this.config.preferences.enableEvaluationByDefault,
},
{
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;
this.config.preferences.defaultEvaluationDomain =
preferences.defaultEvaluationDomain;
this.config.preferences.enableAnalyticsByDefault =
preferences.enableAnalyticsByDefault;
this.config.preferences.enableEvaluationByDefault =
preferences.enableEvaluationByDefault;
// Setup providers if requested
if (preferences.setupProviders) {
await this.setupProviders();
}
this.saveConfig();
logger.always(chalk.green("\n✅ Configuration setup complete!"));
logger.always(chalk.blue("💡 You can modify settings anytime with: neurolink config edit"));
logger.always(chalk.blue("💡 Test your setup with: neurolink status"));
}
catch (error) {
if (error instanceof Error &&
error.message === "User force closed the prompt with 0 null") {
logger.always(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) {
logger.always(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-2.5-pro", "gemini-2.5-flash", "gemini-pro"],
default: "gemini-2.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-2.5-pro", "gemini-2.5-flash"],
default: "gemini-2.5-pro",
},
]);
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() {
logger.always(chalk.blue("📋 Current NeuroLink Configuration\n"));
logger.always(chalk.cyan("General Settings:"));
logger.always(` Default Provider: ${chalk.white(this.config.defaultProvider)}`);
logger.always(` Output Format: ${chalk.white(this.config.preferences.outputFormat)}`);
logger.always(` Temperature: ${chalk.white(this.config.preferences.temperature)}`);
logger.always(` Max Tokens: ${chalk.white(this.config.preferences.maxTokens)}`);
logger.always(` Default Evaluation Domain: ${chalk.white(this.config.preferences.defaultEvaluationDomain || "None")}`);
logger.always(` Analytics by Default: ${chalk.white(this.config.preferences.enableAnalyticsByDefault)}`);
logger.always(` Evaluation by Default: ${chalk.white(this.config.preferences.enableEvaluationByDefault)}`);
logger.always(chalk.cyan("\nConfigured Providers:"));
Object.entries(this.config.providers).forEach(([name, config]) => {
if (config && Object.keys(config).length > 0) {
logger.always(` ${chalk.green("✅")} ${name.toUpperCase()}`);
if ("model" in config) {
logger.always(` Model: ${chalk.white(config.model)}`);
}
}
});
logger.always(chalk.cyan("\nConfigured Domains:"));
Object.entries(this.config.domains).forEach(([name, config]) => {
if (config && Object.keys(config).length > 0) {
logger.always(` ${chalk.green("✅")} ${name.toUpperCase()}`);
if (config.evaluationCriteria && config.evaluationCriteria.length > 0) {
logger.always(` Evaluation Criteria: ${chalk.white(config.evaluationCriteria.join(", "))}`);
}
if (config.analyticsConfig) {
const analyticsEnabled = Object.values(config.analyticsConfig).some(Boolean);
logger.always(` Analytics: ${chalk.white(analyticsEnabled ? "Configured" : "Disabled")}`);
}
}
});
logger.always(chalk.cyan("\nConfiguration File:"));
logger.always(` 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();
logger.always(chalk.green("✅ Configuration reset to defaults"));
}
}
// Export for use in other CLI commands
export const configManager = new ConfigManager();