UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

753 lines • 34.1 kB
#!/usr/bin/env node /** * NeuroLink Setup Command - Main Provider Setup Wizard * * Provides a beautiful welcome experience for new users and guided * provider selection, while delegating to existing setup commands. */ import inquirer from "inquirer"; import chalk from "chalk"; import ora from "ora"; import { logger } from "../../utils/logger.js"; import { NeuroLink } from "../../neurolink.js"; // Import existing setup handlers import { handleOpenAISetup } from "./setup-openai.js"; import { handleGoogleAISetup } from "./setup-google-ai.js"; import { handleAnthropicSetup } from "./setup-anthropic.js"; import { handleAzureSetup } from "./setup-azure.js"; import { handleBedrockSetup } from "./setup-bedrock.js"; import { handleGCPSetup } from "./setup-gcp.js"; import { handleHuggingFaceSetup } from "./setup-huggingface.js"; import { handleMistralSetup } from "./setup-mistral.js"; import { OpenRouterModels, } from "../../constants/enums.js"; import { PROVIDER_DESCRIPTORS_BY_NAME } from "../../factories/providerDescriptors.js"; import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createLayaConfig, createXorConfig, createPerplexityDeciderConfig, createCloudflareClefConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js"; import { getCatalogJsonEntries, buildCatalogConfigOptions, } from "../../providers/catalog/loader.js"; // Provider information database const PROVIDERS = [ { id: "google-ai", name: "Google AI Studio", emoji: "🟡", description: "Fast setup, free tier, latest models", setupTime: "2 min", cost: "Free tier", bestFor: "General tasks", models: "Gemini 2.5 Pro, Gemini 2.5 Flash", strengths: "Latest technology, multimodal, generous free tier", pricing: "Free tier → $7 per 1M tokens", setupCommand: "neurolink setup --provider google-ai", }, { id: "openai", name: "OpenAI", emoji: "🔵", description: "Industry standard, reliable", setupTime: "2 min", cost: "Pay-per-use", bestFor: "Code & reasoning", models: "GPT-4o, GPT-4o-mini, GPT-4-turbo", strengths: "Industry standard, proven reliability, fast", pricing: "$2.50-$10 per 1M tokens", setupCommand: "neurolink setup --provider openai", }, { id: "anthropic", name: "Anthropic Claude", emoji: "🟣", description: "Best for analysis and safety", setupTime: "3 min", cost: "Pay-per-use", bestFor: "Analysis & chat", models: "Claude 3.5 Sonnet, Claude 3 Haiku", strengths: "Safety-focused, excellent analysis, long context", pricing: "$3-$15 per 1M tokens", setupCommand: "neurolink setup --provider anthropic", }, { id: "azure", name: "Azure OpenAI", emoji: "🔷", description: "Enterprise features and compliance", setupTime: "5 min", cost: "Enterprise", bestFor: "Business use", models: "GPT-4, GPT-3.5, Embeddings", strengths: "Enterprise compliance, SLA, data residency", pricing: "Azure pricing model", setupCommand: "neurolink setup --provider azure", }, { id: "bedrock", name: "AWS Bedrock", emoji: "🟠", description: "Integrated with AWS ecosystem", setupTime: "10 min", cost: "AWS pricing", bestFor: "Cloud native", models: "Claude, Llama, Titan, Command", strengths: "AWS integration, multiple model access, enterprise", pricing: "AWS Bedrock pricing", setupCommand: "neurolink setup --provider bedrock", }, { id: "vertex", name: "Google Cloud", emoji: "🔴", description: "Advanced GCP integration", setupTime: "10 min", cost: "GCP pricing", bestFor: "Advanced ML", models: "Gemini, PaLM, Codey", strengths: "Advanced ML pipelines, GCP integration", pricing: "Google Cloud pricing", setupCommand: "neurolink setup --provider vertex", }, { id: "huggingface", name: "Hugging Face", emoji: "🤗", description: "Open source models", setupTime: "3 min", cost: "Free tier", bestFor: "Open source", models: "Llama, Mistral, CodeLlama, FLAN-T5", strengths: "Open source, community models, free tier", pricing: "Free tier available", setupCommand: "neurolink setup --provider huggingface", }, { id: "mistral", name: "Mistral", emoji: "🌟", description: "European, privacy-focused", setupTime: "2 min", cost: "Free tier", bestFor: "European privacy", models: "Mistral 7B, Mixtral 8x7B, Mistral Large", strengths: "European privacy, efficient models, free tier", pricing: "Free tier → €7 per 1M tokens", setupCommand: "neurolink setup --provider mistral", }, { id: "openrouter", name: "OpenRouter", emoji: "🔀", description: "100+ models, unified API access", setupTime: "2 min", cost: "Pay-per-use", bestFor: "Model variety", models: "GPT-4, Claude, Gemini, Llama, Mixtral, etc.", strengths: "Access 100+ models via single API, model routing, fallbacks", pricing: "Per-model pricing, free tier available", setupCommand: "neurolink setup --provider openrouter", }, ]; /** * ProviderConfigOptions for the 21 canonical AIProviderName values the * interactive wizard above doesn't have a bespoke handleXSetup() for. 8 are * the JSON-catalog providers (cerebras, cloudflare, fireworks, groq, * perplexity, sambanova, together-ai, xai — mistral is the 9th catalog * provider but keeps its own handleMistralSetup() flow above, so it's * excluded here), spread directly from the catalog JSON via * buildCatalogConfigOptions(). 10 more reuse the existing createXConfig() * factories in providerConfig.ts; the remaining 5 (ollama, litellm, * sagemaker, lm-studio, llamacpp) don't have a factory and are defined * inline using their real env var names. lm-studio and llamacpp previously * had createLmStudioConfig()/createLlamaCppConfig() factories, but those * were removed as dead code in a later cleanup — this mirrors that removal * rather than re-adding them. */ export const EXTRA_PROVIDER_CONFIGS = { "openai-compatible": createOpenAICompatibleConfig(), deepseek: createDeepSeekConfig(), "nvidia-nim": createNvidiaNimConfig(), cohere: createCohereConfig(), replicate: createReplicateConfig(), voyage: createVoyageConfig(), jina: createJinaConfig(), stability: createStabilityConfig(), ideogram: createIdeogramConfig(), recraft: createRecraftConfig(), typesafe: createTypeSafeConfig(), laya: createLayaConfig(), xor: createXorConfig(), "perplexity-decider": createPerplexityDeciderConfig(), "cloudflare-clef": createCloudflareClefConfig(), ...Object.fromEntries(getCatalogJsonEntries() .filter((e) => e.id !== "mistral") .map((e) => [e.id, buildCatalogConfigOptions(e)])), ollama: { providerName: "Ollama", envVarName: "OLLAMA_BASE_URL", setupUrl: "https://ollama.com/download", description: "Run open-source models locally via Ollama's OpenAI-compatible API.", instructions: [ "Install Ollama from https://ollama.com/download", "Pull a model: ollama pull llama3.1", "Ollama serves its API at http://localhost:11434 by default — set OLLAMA_BASE_URL only to override.", "Optionally set OLLAMA_MODEL to choose the default model.", ], optional: true, }, litellm: { providerName: "LiteLLM", envVarName: "LITELLM_API_KEY", setupUrl: "https://docs.litellm.ai/docs/simple_proxy", description: "Route through a LiteLLM proxy server for unified access to 100+ upstream providers.", instructions: [ "Start a LiteLLM proxy server (see https://docs.litellm.ai/docs/simple_proxy).", "Set LITELLM_BASE_URL to the proxy's URL (defaults to http://localhost:4000).", "Set LITELLM_API_KEY to the proxy's virtual key (defaults to a permissive placeholder for local proxies without auth).", ], fallbackEnvVars: ["LITELLM_BASE_URL"], optional: true, }, sagemaker: { providerName: "Amazon SageMaker", envVarName: "SAGEMAKER_ENDPOINT_NAME", setupUrl: "https://docs.aws.amazon.com/sagemaker/latest/dg/realtime-endpoints.html", description: "Invoke a self-hosted model on an Amazon SageMaker real-time inference endpoint.", instructions: [ "Deploy a model to a SageMaker real-time endpoint (see AWS docs above).", "Set SAGEMAKER_ENDPOINT_NAME to the deployed endpoint's name.", "Set SAGEMAKER_REGION to the AWS region hosting the endpoint.", "Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (or use an AWS credential provider chain) for authentication.", ], fallbackEnvVars: [ "SAGEMAKER_REGION", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", ], optional: false, }, "lm-studio": { providerName: "LM Studio", envVarName: "LM_STUDIO_BASE_URL", setupUrl: "https://lmstudio.ai/", description: "Run open-source models locally via LM Studio's OpenAI-compatible API.", instructions: [ "Install LM Studio from https://lmstudio.ai/", 'Load a model in the LM Studio app and click "Start Server".', "LM Studio serves its API at http://localhost:1234/v1 by default — set LM_STUDIO_BASE_URL only to override.", "Optionally set LM_STUDIO_API_KEY if running behind an auth-proxying reverse proxy.", "Optionally set LM_STUDIO_MODEL to choose the default model.", ], fallbackEnvVars: ["LM_STUDIO_API_KEY", "LM_STUDIO_MODEL"], optional: true, }, llamacpp: { providerName: "llama.cpp", envVarName: "LLAMACPP_BASE_URL", setupUrl: "https://github.com/ggerganov/llama.cpp", description: "Run a self-hosted GGUF model locally via llama.cpp's OpenAI-compatible llama-server.", instructions: [ "Build or install llama.cpp from https://github.com/ggerganov/llama.cpp", "Start the server: ./llama-server -m model.gguf --port 8080", "llama-server serves its API at http://localhost:8080/v1 by default — set LLAMACPP_BASE_URL only to override.", "Optionally set LLAMACPP_API_KEY if running behind an auth-proxying reverse proxy.", "Optionally set LLAMACPP_MODEL to choose the default model.", ], fallbackEnvVars: ["LLAMACPP_API_KEY", "LLAMACPP_MODEL"], optional: true, }, }; /** * Generic, data-driven setup printout for providers without a bespoke * interactive handler — reads directly from a ProviderConfigOptions entry. */ function printGenericProviderSetup(providerId, config) { logger.always(""); logger.always(chalk.blue(`🔧 ${config.providerName} Setup`)); logger.always(""); logger.always(config.description); logger.always(""); logger.always(chalk.yellow("Setup steps:")); config.instructions.forEach((step, index) => { logger.always(` ${index + 1}. ${step}`); }); logger.always(""); logger.always(chalk.yellow("Environment variable:")); const optionalNote = config.optional ? " (optional — has a working local default)" : ""; logger.always(chalk.cyan(` export ${config.envVarName}=your_value_here${optionalNote}`)); if (config.fallbackEnvVars?.length) { logger.always(chalk.cyan(` Also relevant: ${config.fallbackEnvVars.join(", ")}`)); } logger.always(""); logger.always(chalk.yellow("Test the configuration:")); logger.always(chalk.cyan(` neurolink generate "Hello!" --provider ${providerId}`)); logger.always(""); logger.always(chalk.gray(`Docs: ${config.setupUrl}`)); } /** * Main setup command handler */ export async function handleSetup(argv) { try { // Handle specific flags. `--list`/`--status` are documented as one-shot, // non-interactive informational commands (see the `setup --list` / // `setup --status` examples in setupCommandFactory.ts) — pass // interactive: false so they print and return instead of chaining into // the wizard's follow-up inquirer prompt below. if (argv.list) { return await showProviderList(false); } if (argv.status) { return await showProviderStatus(false); } if (argv.provider && argv.provider !== "auto") { return await delegateToProviderSetup(argv.provider, { check: argv.check, nonInteractive: argv.nonInteractive, }); } // Main setup wizard await showWelcomeScreen(); await runSetupWizard(); } catch (error) { logger.error(chalk.red("❌ Setup failed:")); logger.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); process.exit(1); } } /** * Show the welcome screen */ async function showWelcomeScreen() { // Clear screen for better presentation process.stdout.write("\x1B[2J\x1B[0f"); logger.always(chalk.blue("╭─────────────────────────────────────────────────────────────╮")); logger.always(chalk.blue("│ │")); logger.always(chalk.blue("│ ") + chalk.bold.white("🧠 Welcome to NeuroLink - Enterprise AI Platform") + chalk.blue(" │")); logger.always(chalk.blue("│ │")); logger.always(chalk.blue("│ ") + chalk.gray("Unified interface for many AI providers") + chalk.blue(" │")); logger.always(chalk.blue("│ ") + chalk.gray("• Generate text, code, and creative content") + chalk.blue(" │")); logger.always(chalk.blue("│ ") + chalk.gray("• Stream responses in real-time") + chalk.blue(" │")); logger.always(chalk.blue("│ ") + chalk.gray("• Built-in analytics and evaluation") + chalk.blue(" │")); logger.always(chalk.blue("│ ") + chalk.gray("• Enterprise-grade tool integration") + chalk.blue(" │")); logger.always(chalk.blue("│ │")); logger.always(chalk.blue("╰─────────────────────────────────────────────────────────────╯")); logger.always(""); } /** * Run the main setup wizard */ async function runSetupWizard() { const spinner = ora("🔍 Scanning for existing configurations...").start(); // Check existing provider configurations const configuredProviders = await checkExistingConfigurations(); spinner.stop(); // Show current status await displayCurrentStatus(configuredProviders); // Show provider comparison table await displayProviderComparison(); // Main menu const { action } = await inquirer.prompt([ { type: "select", name: "action", message: "What would you like to do?", choices: [ { name: "🚀 Set up a new AI provider", value: "setup", }, { name: "📖 Learn more about providers", value: "learn", }, { name: "📊 Check provider status", value: "status", }, { name: "❌ Exit", value: "exit", }, ], }, ]); switch (action) { case "setup": await runProviderSelection(); break; case "learn": await showProviderList(); break; case "status": await showProviderStatus(); break; case "exit": logger.always(chalk.blue("👋 Come back anytime with 'neurolink setup'")); break; } } /** * Check which providers are already configured */ export async function checkExistingConfigurations() { const configured = []; for (const p of PROVIDERS) { const descriptor = PROVIDER_DESCRIPTORS_BY_NAME.get(p.id); if (!descriptor) { continue; } const { apiKey, fallbacks, extraRequired, extraRequiredFallbacks } = descriptor.envVars; const hasPrimary = !!apiKey && (!!process.env[apiKey] || (fallbacks ?? []).some((v) => !!process.env[v])); if (!hasPrimary) { continue; } const requiredOk = (extraRequired ?? []).every((v) => !!process.env[v]) || satisfiesFallbacks(extraRequiredFallbacks, process.env); if (requiredOk) { configured.push(p.id); } } return configured; } /** * Display current configuration status */ async function displayCurrentStatus(configuredProviders) { logger.always("Current Status:"); if (configuredProviders.length === 0) { logger.always(chalk.red("❌ No AI providers configured yet")); } else { logger.always(chalk.green(`✅ ${configuredProviders.length} provider(s) configured:`)); configuredProviders.forEach((id) => { const provider = PROVIDERS.find((p) => p.id === id); if (provider) { logger.always(` ${provider.emoji} ${provider.name}`); } }); } logger.always(""); } /** * Display provider comparison table */ async function displayProviderComparison() { logger.always("Available Providers:"); logger.always(""); // Create a simple table logger.always("┌─────────────────┬──────────────┬─────────────┬─────────────────┐"); logger.always("│ Provider │ Setup Time │ Cost │ Best For │"); logger.always("├─────────────────┼──────────────┼─────────────┼─────────────────┤"); PROVIDERS.forEach((provider) => { const name = `${provider.emoji} ${provider.name}`.padEnd(15); const setupTime = provider.setupTime.padEnd(12); const cost = provider.cost.padEnd(11); const bestFor = provider.bestFor.padEnd(15); logger.always(`│ ${name} │ ${setupTime} │ ${cost} │ ${bestFor} │`); }); logger.always("└─────────────────┴──────────────┴─────────────┴─────────────────┘"); logger.always(""); logger.always(chalk.yellow("💡 Recommendation: Start with Google AI (free) or OpenAI (industry standard)")); logger.always(""); } /** * Run provider selection */ async function runProviderSelection() { logger.always(chalk.blue("🎯 Perfect! Let's get you connected.")); logger.always(""); const choices = PROVIDERS.map((provider) => ({ name: `${provider.emoji} ${provider.name.padEnd(18)} - ${provider.description}`, value: provider.id, })); // Add a non-selectable separator row choices.push(new inquirer.Separator("─".repeat(60))); choices.push({ name: chalk.yellow("💡 New to AI? Google AI Studio is perfect for getting started!"), value: "tip", }); const { selectedProvider } = await inquirer.prompt([ { type: "select", name: "selectedProvider", message: "Which AI provider would you like to configure?", choices, pageSize: 12, }, ]); if (selectedProvider !== "tip") { logger.always(""); logger.always(chalk.blue("🔄 Launching provider setup...")); logger.always(""); await delegateToProviderSetup(selectedProvider); } } /** * Delegate to existing provider setup commands */ export async function delegateToProviderSetup(providerId, flags = { check: false, nonInteractive: false, }) { const nonInteractive = flags.nonInteractive ?? false; const check = flags.check ?? false; const setupArgs = { nonInteractive, "non-interactive": nonInteractive, check, _: [], $0: "neurolink", }; switch (providerId) { case "google-ai": await handleGoogleAISetup(setupArgs); break; case "openai": await handleOpenAISetup(setupArgs); break; case "anthropic": await handleAnthropicSetup(setupArgs); break; case "azure": await handleAzureSetup(setupArgs); break; case "bedrock": await handleBedrockSetup(setupArgs); break; case "vertex": await handleGCPSetup(setupArgs); break; case "huggingface": await handleHuggingFaceSetup(setupArgs); break; case "mistral": await handleMistralSetup(setupArgs); break; case "openrouter": await handleOpenRouterSetup(); break; default: { const genericConfig = EXTRA_PROVIDER_CONFIGS[providerId]; if (!genericConfig) { throw new Error(`Unknown provider: ${providerId}`); } printGenericProviderSetup(providerId, genericConfig); break; } } // After successful setup, show completion message await showSetupCompletion(providerId); } /** * Handle OpenRouter provider setup */ async function handleOpenRouterSetup() { logger.always(""); logger.always(chalk.blue("🔀 OpenRouter Setup")); logger.always(""); logger.always("OpenRouter provides unified access to 100+ AI models from multiple providers"); logger.always("including OpenAI, Anthropic, Google, Meta, Mistral, and more."); logger.always(""); logger.always(chalk.yellow("Step 1: Get your API key")); logger.always(" 1. Go to https://openrouter.ai/keys"); logger.always(" 2. Sign in or create an account"); logger.always(" 3. Create a new API key"); logger.always(""); logger.always(chalk.yellow("Step 2: Set the environment variable")); logger.always(" Add to your shell profile (~/.bashrc, ~/.zshrc, etc.):"); logger.always(""); logger.always(chalk.cyan(" export OPENROUTER_API_KEY=your_api_key_here")); logger.always(""); logger.always(chalk.yellow("Step 3: Test the configuration")); logger.always(chalk.cyan(` neurolink generate "Hello!" --provider openrouter --model ${OpenRouterModels.GEMINI_2_5_FLASH}`)); logger.always(""); logger.always(chalk.green("Available models include:")); logger.always(` • ${OpenRouterModels.CLAUDE_SONNET_4_6} - Best for analysis`); logger.always(` • ${OpenRouterModels.GPT_4O} - Industry standard`); logger.always(` • ${OpenRouterModels.GEMINI_2_5_FLASH} - Fast and efficient`); logger.always(` • ${OpenRouterModels.LLAMA_3_1_70B} - Open source`); logger.always(""); logger.always(chalk.gray("See all models at: https://openrouter.ai/models")); } /** * Show setup completion message */ async function showSetupCompletion(providerId) { const provider = PROVIDERS.find((p) => p.id === providerId); if (!provider) { return; } logger.always(""); logger.always(chalk.green("🎉 Welcome to NeuroLink! Your provider is ready.")); logger.always(""); logger.always("Next steps:"); logger.always(chalk.cyan(`• Test: neurolink generate "Hello world!" --provider ${providerId}`)); logger.always(chalk.cyan("• Status: neurolink provider status")); logger.always(chalk.cyan("• Help: neurolink --help")); logger.always(""); logger.always(chalk.blue("Happy generating! 🚀")); } /** * Show detailed provider information. * * @param interactive - When true (the default, used by the interactive * wizard's "learn more" menu choice), follows up with a prompt offering to * launch the setup wizard. When false (used by the direct `setup --list` * flag), returns immediately after printing so the command exits cleanly * without waiting on stdin. */ async function showProviderList(interactive = true) { logger.always(chalk.blue("📚 NeuroLink Supported AI Providers")); logger.always(""); for (const provider of PROVIDERS) { logger.always(`╭─────────────────────────────────────────────────────────────╮`); logger.always(`│ ${provider.emoji} ${provider.name.padEnd(25)}│`); logger.always(`├─────────────────────────────────────────────────────────────┤`); logger.always(`│ Setup: ${provider.setupCommand.padEnd(48)}│`); logger.always(`│ Models: ${provider.models.padEnd(47)}│`); logger.always(`│ Strengths: ${provider.strengths.padEnd(43)}│`); logger.always(`│ Best for: ${provider.bestFor.padEnd(44)}│`); logger.always(`│ Pricing: ${provider.pricing.padEnd(45)}│`); logger.always(`│ Setup time: ${provider.setupTime.padEnd(42)}│`); logger.always(`╰─────────────────────────────────────────────────────────────╯`); logger.always(""); } logger.always(chalk.blue("🎯 Quick Start Recommendations:")); logger.always(""); logger.always(chalk.yellow("🆓 Free Tier Starter Pack:")); logger.always("• neurolink setup --provider google-ai"); logger.always("• neurolink setup --provider huggingface"); logger.always(""); logger.always(chalk.yellow("💼 Professional Setup:")); logger.always("• neurolink setup --provider openai"); logger.always("• neurolink setup --provider anthropic"); logger.always(""); logger.always(chalk.yellow("🏢 Enterprise Ready:")); logger.always("• neurolink setup --provider azure"); logger.always("• neurolink setup --provider bedrock"); logger.always(""); if (!interactive) { return; } const { action } = await inquirer.prompt([ { type: "select", name: "action", message: "Ready to set up a provider?", choices: [ { name: "Yes, show me the setup wizard", value: "wizard" }, { name: "No, I'll set up providers manually later", value: "exit" }, ], }, ]); if (action === "wizard") { await runProviderSelection(); } } /** * Show provider status with detailed information. * * @param interactive - When true (the default, used by the interactive * wizard's "check status" menu choice), follows up with a prompt offering to * set up another provider. When false (used by the direct `setup --status` * flag), returns immediately after printing so the command exits cleanly * without waiting on stdin. */ async function showProviderStatus(interactive = true) { const spinner = ora("🔍 Checking all AI provider configurations...").start(); try { // Get provider status using NeuroLink SDK const sdk = new NeuroLink(); const results = await sdk.getProviderStatus({ quiet: true }); spinner.stop(); logger.always(""); logger.always("╭─────────────────────────────────────────────────────────────╮"); logger.always("│ NeuroLink Provider Status │"); logger.always("╰─────────────────────────────────────────────────────────────╯"); logger.always(""); const working = results.filter((r) => r.status === "working"); const _configured = results.filter((r) => r.configured); const available = PROVIDERS.filter((p) => !results.some((r) => r.provider === p.id && r.configured)); if (working.length > 0) { logger.always("✅ Working Providers:"); logger.always("┌─────────────────┬─────────────┬─────────────┬─────────────────┐"); logger.always("│ Provider │ Status │ Model │ Last Tested │"); logger.always("├─────────────────┼─────────────┼─────────────┼─────────────────┤"); working.forEach((result) => { const provider = PROVIDERS.find((p) => p.id === result.provider); const emoji = provider?.emoji || "⚪"; const name = `${emoji} ${provider?.name || result.provider}`.padEnd(15); const status = "✅ Working".padEnd(11); const model = (result.model || "default").padEnd(11); const tested = result.responseTime ? "Just now" : "Unknown"; logger.always(`│ ${name} │ ${status} │ ${model} │ ${tested.padEnd(15)} │`); }); logger.always("└─────────────────┴─────────────┴─────────────┴─────────────────┘"); logger.always(""); } if (available.length > 0) { logger.always("⚪ Available for Setup:"); logger.always("┌─────────────────┬──────────────┬─────────────┬─────────────────┐"); logger.always("│ Provider │ Setup Time │ Cost Model │ Specialty │"); logger.always("├─────────────────┼──────────────┼─────────────┼─────────────────┤"); available.forEach((provider) => { const name = `${provider.emoji} ${provider.name}`.padEnd(15); const setupTime = provider.setupTime.padEnd(12); const cost = provider.cost.padEnd(11); const specialty = provider.bestFor.padEnd(15); logger.always(`│ ${name} │ ${setupTime} │ ${cost} │ ${specialty} │`); }); logger.always("└─────────────────┴──────────────┴─────────────┴─────────────────┘"); logger.always(""); } // Summary and recommendations logger.always(chalk.blue(`🎯 You have ${working.length} provider(s) working!`)); logger.always(""); if (available.length > 0) { logger.always(chalk.yellow("💡 Suggestions:")); if (!working.some((p) => p.provider === "anthropic")) { logger.always("• Add Anthropic for better analysis: neurolink setup --provider anthropic"); } if (!working.some((p) => p.provider === "huggingface")) { logger.always("• Try Hugging Face for free open source models: neurolink setup --provider huggingface"); } logger.always("• Check performance: neurolink provider status"); logger.always(""); } if (!interactive) { return; } const { setupAnother } = await inquirer.prompt([ { type: "confirm", name: "setupAnother", message: "Would you like to set up another provider now?", default: false, }, ]); if (setupAnother) { await runProviderSelection(); } else { logger.always(chalk.blue("👍 All set! Use 'neurolink setup' anytime to add more providers.")); } } catch (error) { spinner.fail("Failed to check provider status"); logger.error(chalk.red("Error checking provider status:"), error); } } //# sourceMappingURL=setup.js.map