UNPKG

@hivetechs/hive-ai

Version:

Real-time streaming AI consensus platform with HTTP+SSE MCP integration for Claude Code, VS Code, Cursor, and Windsurf - powered by OpenRouter's unified API

1,056 lines โ€ข 80.3 kB
/** * OpenRouter-Exclusive Guided Setup Wizard * * Simplified setup focusing on: * 1. OpenRouter API key configuration * 2. Model selection for 4-stage consensus pipeline * 3. Profile creation * * No more complex provider management - OpenRouter handles everything. */ import { z } from "zod"; import * as readline from 'readline'; import * as os from 'os'; import * as path from 'path'; // Removed openRouterSyncManager - now using direct database access import { providerUtils } from './provider-config.js'; import { isOpenRouterConfigured } from './provider-client.js'; import { structuredLogger } from '../structured-logger.js'; const STAGE_RECOMMENDATIONS = { generator: { stage: "Generator", purpose: "Initial analysis and creative problem decomposition", bestFor: ["Complex reasoning", "Creative thinking", "Problem analysis"], temperatureRange: [0.6, 0.9], hints: [ "Best for initial brainstorming and analysis", "Models like Claude 3.5 Sonnet, GPT-4, DeepSeek R1 excel here", "Higher creativity helps with ideation" ] }, refiner: { stage: "Refiner", purpose: "Technical enhancement and specification", bestFor: ["Code generation", "Technical depth", "Implementation details"], temperatureRange: [0.2, 0.5], hints: [ "Perfect for technical refinement and coding", "GPT-4, Codestral, Claude Sonnet work well", "Lower temperature for precise responses" ] }, validator: { stage: "Validator", purpose: "Fact-checking and validation with different perspective", bestFor: ["Fact checking", "Error detection", "Quality assurance"], temperatureRange: [0.0, 0.3], hints: [ "Choose models with different training data", "Gemini, Llama, Qwen provide good validation", "Very low temperature for consistent checking" ] }, curator: { stage: "Curator", purpose: "Final synthesis and polishing", bestFor: ["Writing quality", "Communication", "Final polish"], temperatureRange: [0.3, 0.7], hints: [ "Excels at final writing and communication", "Claude Sonnet, Grok, GPT-4 are excellent here", "Moderate temperature for natural output" ] } }; // Removed Quick Setup - now using direct Advanced Setup only /** * Helper function to get all models from all providers */ async function getAllModels() { const { getDatabase } = await import('../../storage/unified-database.js'); const database = await getDatabase(); try { const models = await database.all(` SELECT * FROM openrouter_models WHERE is_active = 1 ORDER BY provider_name, name `); return models; } catch (error) { console.error('Error loading models from database:', error); return []; } } export class OpenRouterSetupWizard { rl = null; initializeReadline() { if (!this.rl) { this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); } } async prompt(question) { if (!this.rl) { throw new Error('Readline interface not initialized'); } // Ensure the prompt is on a new line and properly displayed if (!question.startsWith('\n')) { process.stdout.write('\n'); } return new Promise((resolve) => { this.rl.question(question, (answer) => { resolve(answer.trim()); }); }); } /** * Main setup wizard flow */ async runSetup(isFirstTimeUser = false) { try { if (!process.stdin.isTTY) { console.log('โš ๏ธ Interactive terminal required for setup wizard'); throw new Error('Non-interactive environment detected'); } // Enable quiet mode for license gate during setup try { const { ModernLicenseGate } = await import('../../core/modern-license-gate.js'); const licenseGate = ModernLicenseGate.getInstance(); licenseGate.setQuietMode(true); } catch (error) { // Ignore if module not available } this.initializeReadline(); // Skip the duplicate welcome if we're coming from a first-time user flow if (!isFirstTimeUser) { console.log('๐Ÿš€ Welcome to hive-ai Setup!\n'); console.log('Building your 4-stage AI consensus pipeline:'); console.log('๐Ÿง  Generator โ†’ โšก Refiner โ†’ ๐Ÿ” Validator โ†’ โœจ Curator\n'); } // Check if this is a first-time user const { checkLicenseStatus } = await import('./license-configuration.js'); const licenseStatus = await checkLicenseStatus(); const openRouterConfigured = await isOpenRouterConfigured(); // Debug logging to help trace setup flow decisions console.debug(`๐Ÿ” Setup wizard: isFirstTimeUser=${isFirstTimeUser}, License=${licenseStatus.hasLicense && licenseStatus.isValid}, OpenRouter=${openRouterConfigured}`); if (isFirstTimeUser || (!licenseStatus.hasLicense || !licenseStatus.isValid || !openRouterConfigured)) { // First-time user flow console.debug('๐Ÿ” Entering first-time user flow'); await this.firstTimeUserFlow(); } else { // Regular menu for existing users console.debug('๐Ÿ” Entering regular main menu'); await this.showMainMenu(); } } catch (error) { console.error(`\nโŒ Setup error: ${error instanceof Error ? error.message : 'Unknown error'}`); throw error; } finally { this.close(); } } /** * First-time user flow - guided setup experience */ async firstTimeUserFlow() { console.log('Let\'s get you set up step by step:\n'); // Step 1: License configuration const { checkLicenseStatus } = await import('./license-configuration.js'); let licenseStatus = await checkLicenseStatus(); if (!licenseStatus.hasLicense || !licenseStatus.isValid) { console.log('๐Ÿ“‹ Step 1: Hive License\n'); console.log('First, let\'s set up your Hive license to unlock all features.\n'); console.log('๐Ÿ’ก Quick tip: Get your license at https://hivetechs.io/pricing\n'); await this.setupLicense(); // Re-check license status licenseStatus = await checkLicenseStatus(); if (!licenseStatus.hasLicense || !licenseStatus.isValid) { console.log('\nโš ๏ธ License configuration is required to continue.'); console.log('You can configure it later with: hive quickstart\n'); return; } console.log('\nโœ… Great! Your Hive license is configured.\n'); } // Step 2: OpenRouter configuration - Always check, regardless of license status let openRouterConfigured = await isOpenRouterConfigured(); if (!openRouterConfigured) { console.log('๐Ÿ”— Step 2: OpenRouter API Key\n'); console.log('Next, let\'s connect to OpenRouter for access to 300+ AI models.\n'); console.log('๐Ÿ’ก Quick setup:'); console.log('1. Sign up at: https://openrouter.ai/signup'); console.log('2. Add credits ($5 minimum): https://openrouter.ai/credits'); console.log('3. Get your API key: https://openrouter.ai/keys\n'); await this.setupOpenRouter(); // Re-check OpenRouter configuration after setup openRouterConfigured = await isOpenRouterConfigured(); if (!openRouterConfigured) { console.log('\nโš ๏ธ OpenRouter configuration is required to continue.'); console.log('You can configure it later with: hive quickstart\n'); return; } console.log('\nโœ… Excellent! OpenRouter is now connected.\n'); } // Step 3: Create first profile with recommendations console.log('๐Ÿš€ Step 3: Create Your First AI Profile\n'); console.log('Now for the fun part - setting up your AI consensus pipeline!\n'); console.log('We recommend starting with one of our Expert Templates.\n'); console.log('Would you like to create your first AI profile?\n'); console.log('1. โœ… Yes, show me the expert templates'); console.log('2. ๐ŸŽฏ Skip for now (can create later)\n'); const choice = await this.prompt('Choose option (1-2): '); if (choice === '2') { console.log('\nโœ… Setup complete! You can create profiles anytime with: hive quickstart\n'); return; } else if (choice !== '1') { console.log('โŒ Please enter 1 or 2\n'); // Ask again return await this.firstTimeUserFlow(); } // Show expert templates for first-time users await this.expertTemplatesFlow(); // Congratulations message console.log('\n๐ŸŽ‰ Congratulations! You\'re all set up!\n'); console.log('Here are some commands to get you started:\n'); console.log('๐Ÿ“ Try your first consensus:'); console.log(' hive consensus "Explain quantum computing in simple terms"\n'); console.log('๐Ÿ”ง Manage your setup:'); console.log(' hive quickstart # Access setup menu'); console.log(' hive pipeline list # View your profiles'); console.log(' hive usage # Check your usage stats\n'); console.log('๐Ÿ’ก Need help? Try:'); console.log(' hive help # Show all commands'); console.log(' hive help consensus # Learn about consensus\n'); console.log('Welcome to the HiveTechs Collective! ๐Ÿ\n'); } /** * Main menu with optional configuration */ async showMainMenu() { while (true) { // Check current configuration status on every iteration to ensure real-time updates const { checkLicenseStatus } = await import('./license-configuration.js'); const licenseStatus = await checkLicenseStatus(); const openRouterConfigured = await isOpenRouterConfigured(); // Check for existing profiles and cleanup opportunities const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); const existingProfiles = await getAllPipelineProfiles(); const cleanupNeeded = await this.checkForOutdatedProfiles(); console.log('\n๐Ÿ  Main Menu\n'); // Always show all profile management options prominently console.log('Profile Management:'); console.log('1. ๐Ÿ“‹ Expert Profile Templates'); console.log(' Use professionally crafted consensus presets (Recommended for new users)'); console.log('2. ๐Ÿš€ Create New Profile'); console.log(' Build a custom 4-stage consensus pipeline with guided selection'); console.log('3. ๐Ÿ”ง Custom Model Selection'); console.log(' Full control - browse all models and choose manually'); console.log('4. ๐ŸŒ Browse All Models'); console.log(' Explore 300+ models from OpenRouter without creating a profile'); if (existingProfiles.length > 0) { console.log('5. ๐Ÿ‘๏ธ View Current Profiles'); console.log('6. ๐ŸŽฏ Set Default Profile'); console.log('7. ๐Ÿ—‘๏ธ Delete Profile'); } if (cleanupNeeded) { console.log(`${existingProfiles.length > 0 ? '8' : '5'}. ๐Ÿงน Cleanup Outdated Profiles`); } console.log('\nConfiguration:'); const configStart = existingProfiles.length > 0 ? (cleanupNeeded ? 9 : 8) : (cleanupNeeded ? 6 : 5); console.log(`${configStart}. ๐Ÿ”‘ Configure Hive & OpenRouter Keys`); console.log('\n0. ๐Ÿšช Exit Setup\n'); // Show current status console.log('๐Ÿ“Š Current Status:'); console.log(` License: ${licenseStatus.hasLicense && licenseStatus.isValid ? 'โœ… Configured' : licenseStatus.hasLicense ? 'โŒ Invalid/Expired' : 'โš ๏ธ Not configured'}`); console.log(` OpenRouter: ${openRouterConfigured ? 'โœ… Configured' : 'โš ๏ธ Not configured'}`); console.log(` Profiles: ${existingProfiles.length} configured`); // Show helpful guidance based on current status if (existingProfiles.length === 0 && licenseStatus.hasLicense && licenseStatus.isValid && openRouterConfigured) { console.log('\n๐Ÿ’ก You\'re all set up! Start with Expert Profile Templates or browse models to explore.\n'); } else if (existingProfiles.length === 0) { console.log('\n๐Ÿ’ก Create profiles after configuring your license and OpenRouter key. You can still browse models!\n'); } else { console.log('\n๐Ÿ’ก Use existing profiles or create new ones. Browse models to explore new options.\n'); } const choice = await this.prompt('Choose option: '); const choiceNum = parseInt(choice); try { if (choice === '1') { await this.expertTemplatesFlow(); } else if (choice === '2') { await this.createNewProfileFlow(); } else if (choice === '3') { await this.customModelSelectionFlow(); } else if (choice === '4') { await this.browseAllModelsFlow(); } else if (choice === '5' && existingProfiles.length > 0) { await this.viewProfilesFlow(); } else if (choice === '6' && existingProfiles.length > 0) { await this.setDefaultProfileFlow(); } else if (choice === '7' && existingProfiles.length > 0) { await this.deleteProfileFlow(); } else if ((choice === '8' && cleanupNeeded && existingProfiles.length > 0)) { await this.cleanupOutdatedProfiles(); } else if ((choice === '5' && cleanupNeeded && existingProfiles.length === 0)) { await this.cleanupOutdatedProfiles(); } else if (choiceNum === configStart) { await this.configurationFlow(); console.log('\n๐ŸŽ‰ Configuration updated! Refreshing status...\n'); // Small delay to ensure configuration is properly saved await new Promise(resolve => setTimeout(resolve, 500)); } else if (choice === '0') { console.log('\n๐Ÿ‘‹ Goodbye! Your profiles are ready to use.\n'); return; } else { console.log('โŒ Invalid choice. Please try again.\n'); } } catch (error) { if (error instanceof Error && error.message.includes('back to menu')) { continue; // User chose to go back } throw error; } } } /** * Check if user wants to continue with setup */ async shouldContinueSetup() { // Check for existing profiles const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); const existingProfiles = await getAllPipelineProfiles(); console.log('๐Ÿ”ง Ready to create your AI consensus pipeline!\n'); if (existingProfiles.length > 0) { console.log(`โ„น๏ธ You currently have ${existingProfiles.length} profile(s) configured.\n`); console.log('What would you like to do?\n'); console.log('1. ๐Ÿš€ Create a new profile'); console.log('2. ๐Ÿ‘๏ธ View current profiles'); console.log('3. ๐Ÿšช Exit (profiles already configured)'); console.log('0. โฌ…๏ธ Cancel\n'); while (true) { const choice = await this.prompt('Choose option (1-Create, 2-View, 3-Exit, 0-Cancel): '); if (choice === '1' || choice.toLowerCase().includes('create')) { console.log('\n๐Ÿš€ Creating new profile...\n'); return true; } else if (choice === '2' || choice.toLowerCase().includes('view')) { console.log('\n๐Ÿ‘๏ธ Current Profiles:\n'); this.displayProfiles(existingProfiles); console.log('\nWhat would you like to do?\n'); console.log('1. ๐Ÿš€ Create a new profile'); console.log('2. ๐Ÿ‘๏ธ View current profiles'); console.log('3. ๐Ÿšช Exit (profiles already configured)'); console.log('0. โฌ…๏ธ Cancel\n'); continue; } else if (choice === '3' || choice.toLowerCase().includes('exit')) { console.log('\n๐Ÿšช Exiting setup. Your existing profiles are ready to use!\n'); return false; } else if (choice === '0' || choice.toLowerCase().includes('cancel')) { return false; } else { console.log('โŒ Please enter 1, 2, 3, or 0.\n'); } } } else { console.log('Let\'s create your first consensus pipeline profile!\n'); console.log('1. ๐Ÿš€ Yes, let\'s get started!'); console.log('2. โŒ Not right now\n'); const proceed = await this.prompt('Choose option (1-2): '); if (proceed === '2') { return false; } else if (proceed !== '1') { console.log('โŒ Please enter 1 or 2\n'); return await this.shouldContinueSetup(); } console.log('\n๐Ÿš€ Let\'s get started!\n'); return true; } } /** * Display current profiles in a nice format */ displayProfiles(profiles) { profiles.forEach((profile, index) => { const isDefault = profile.is_default ? ' (DEFAULT)' : ''; console.log(`${index + 1}. ๐Ÿค– ${profile.name}${isDefault}`); console.log(` ๐Ÿง  Generator: ${profile.generator_model} (${profile.generator_provider})`); console.log(` โšก Refiner: ${profile.refiner_model} (${profile.refiner_provider})`); console.log(` ๐Ÿ” Validator: ${profile.validator_model} (${profile.validator_provider})`); console.log(` โœจ Curator: ${profile.curator_model} (${profile.curator_provider})`); console.log(` ๐Ÿ“… Created: ${new Date(profile.created_at).toLocaleDateString()}`); if (index < profiles.length - 1) { console.log(''); } }); } /** * Create new profile flow - guided model selection */ async createNewProfileFlow() { // Ensure prerequisites are met if (!await this.ensurePrerequisites()) { throw new Error('back to menu'); } console.log('\n๐Ÿš€ Create New Profile\n'); console.log('This will guide you through creating a custom 4-stage consensus profile.\n'); console.log('1. ๐Ÿš€ Yes, let\'s start creating a custom profile'); console.log('2. โฌ…๏ธ Back to main menu\n'); const proceed = await this.prompt('Choose option (1-2): '); if (proceed === '2') { throw new Error('back to menu'); } else if (proceed !== '1') { console.log('โŒ Please enter 1 or 2\n'); return await this.createNewProfileFlow(); } await this.ensureModelData(); const selectedModels = await this.selectModelsForPipeline(); const profileName = await this.createProfile(selectedModels); this.showCompletionSummary(selectedModels, profileName); console.log('\nWhat next?\n'); console.log('1. ๐Ÿš€ Create another profile'); console.log('2. โฌ…๏ธ Back to main menu'); console.log('3. ๐Ÿšช Exit setup\n'); const nextAction = await this.prompt('Choose option (1-3): '); if (nextAction === '1') { await this.createNewProfileFlow(); } else if (nextAction === '3') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } // Otherwise return to menu (option 2) } /** * Get available templates that haven't been implemented yet */ async getAvailableTemplates() { try { const { EXPERT_TEMPLATES } = await import('../expert-profile-templates.js'); const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); // Get existing profile names const existingProfiles = await getAllPipelineProfiles(); const existingNames = new Set(existingProfiles.map(p => p.name.toLowerCase())); // Filter out templates that already have profiles const availableTemplates = EXPERT_TEMPLATES.filter((template) => { const expectedProfileName = `${template.name} Profile`.toLowerCase(); return !existingNames.has(expectedProfileName); }); return availableTemplates; } catch (error) { console.error('Error getting available templates:', error); return []; } } /** * Add all expert templates at once */ async addAllExpertTemplates() { console.log('\n๐Ÿš€ Adding All Expert Templates\n'); console.log('This will create profiles for all available expert templates...\n'); const availableTemplates = await this.getAvailableTemplates(); if (availableTemplates.length === 0) { console.log('โœ… All expert templates are already configured!\n'); await this.prompt('Press Enter to continue...'); return; } console.log(`๐Ÿ“Š Found ${availableTemplates.length} templates to configure:\n`); availableTemplates.forEach((template, index) => { console.log(` ${index + 1}. ${template.name}`); }); console.log('\n๐ŸŽฏ Default Profile Selection:'); console.log('1. Lightning Fast (Recommended for beginners)'); console.log('2. Startup MVP (Balanced for general use)'); console.log('3. Precision Architect (Quality-focused)'); console.log('4. Let me choose later\n'); const defaultChoice = await this.prompt('Which should be your default profile? (1-4): '); const confirm = await this.prompt(`\n๐Ÿ”„ Create ${availableTemplates.length} profiles? (y/N): `); if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') { console.log('โŒ Cancelled template creation.\n'); return; } console.log('\n๐Ÿ”„ Creating all expert profiles...\n'); let createdCount = 0; let defaultSet = false; const defaultTemplateMap = { '1': 'lightning-fast', '2': 'startup-mvp', '3': 'precision-architect' }; for (const template of availableTemplates) { try { console.log(`โณ Creating: ${template.name}...`); await this.implementExpertTemplate(template, true); // Silent mode createdCount++; // Set default if this is the selected template if (!defaultSet && defaultTemplateMap[defaultChoice] === template.id) { await this.setProfileAsDefault(`${template.name} Profile`); console.log(`๐ŸŽฏ Set "${template.name} Profile" as default`); defaultSet = true; } console.log(`โœ… Created: ${template.name} Profile`); } catch (error) { console.log(`โŒ Failed to create ${template.name}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } console.log(`\nโœ… Successfully created ${createdCount}/${availableTemplates.length} expert profiles!`); if (!defaultSet && createdCount > 0) { // Auto-set first created profile as default const firstTemplate = availableTemplates[0]; await this.setProfileAsDefault(`${firstTemplate.name} Profile`); console.log(`๐ŸŽฏ Auto-set "${firstTemplate.name} Profile" as default`); } await this.prompt('\nPress Enter to continue...'); } /** * Set a profile as default by name */ async setProfileAsDefault(profileName) { try { const { setDefaultPipelineProfile } = await import('../../storage/unified-database.js'); await setDefaultPipelineProfile(profileName); } catch (error) { console.warn(`Warning: Could not set ${profileName} as default:`, error); } } /** * Expert templates flow */ async expertTemplatesFlow() { // Ensure prerequisites are met if (!await this.ensurePrerequisites()) { throw new Error('back to menu'); } // Ensure model data is available for profile creation await this.ensureModelData(); while (true) { console.log('\n๐Ÿ“‹ Expert Profile Templates\n'); const availableTemplates = await this.getAvailableTemplates(); const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); const existingProfiles = await getAllPipelineProfiles(); if (availableTemplates.length === 0) { console.log('โœ… All expert templates are already configured!\n'); console.log(`You have ${existingProfiles.length} expert profiles configured.\n`); console.log('1. ๐Ÿ‘๏ธ View configured profiles'); console.log('2. โฌ…๏ธ Back to main menu'); console.log('3. ๐Ÿšช Exit setup\n'); const choice = await this.prompt('Choose option (1-3): '); if (choice === '1') { await this.viewProfilesFlow(); continue; } else if (choice === '2') { return; } else if (choice === '3') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } continue; } console.log('Choose from professionally crafted consensus profiles:\n'); console.log('๐Ÿš€ 1. Add All Expert Templates (Recommended)'); console.log(` Create all ${availableTemplates.length} available templates at once\n`); try { availableTemplates.forEach((template, index) => { const budgetTier = template.budgetProfile?.preferredCostRange || template.budgetProfile?.priority || 'balanced'; const performanceTier = template.performanceProfile?.priority || 'balanced'; console.log(`${index + 2}. ${template.name}`); console.log(` ${template.description}`); console.log(` ๐Ÿ’ฐ ${budgetTier} โ€ข โšก ${performanceTier}\n`); }); console.log(`${availableTemplates.length + 2}. โฌ…๏ธ Back to main menu`); console.log(`${availableTemplates.length + 3}. ๐Ÿšช Exit setup\n`); const choice = await this.prompt(`Select option (1-${availableTemplates.length + 3}): `); const choiceNum = parseInt(choice); if (choiceNum === 1) { // Add all templates await this.addAllExpertTemplates(); continue; } else if (choiceNum >= 2 && choiceNum <= availableTemplates.length + 1) { // Individual template selection const selectedTemplate = availableTemplates[choiceNum - 2]; try { await this.implementExpertTemplate(selectedTemplate); // Ask about setting as default if this is the first profile const existingProfilesAfter = await getAllPipelineProfiles(); if (existingProfilesAfter.length === 1) { console.log(`\n๐ŸŽฏ "${selectedTemplate.name} Profile" has been set as your default profile.`); } // Continue in loop to allow creating more templates continue; } catch (error) { if (error instanceof Error && error.message === 'back to menu') { return; // Back to main menu } throw error; // Re-throw other errors } } else if (choiceNum === availableTemplates.length + 2) { return; // Back to main menu } else if (choiceNum === availableTemplates.length + 3) { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } else { console.log(`โŒ Please enter a number from 1 to ${availableTemplates.length + 3}\n`); } } catch (error) { console.log('โš ๏ธ Expert templates not available. Returning to main menu.\n'); return; // Back to main menu instead of forcing profile creation } } } /** * Custom model selection flow */ async customModelSelectionFlow() { // Ensure prerequisites are met if (!await this.ensurePrerequisites()) { throw new Error('back to menu'); } console.log('\n๐Ÿ”ง Custom Model Selection\n'); console.log('โ„น๏ธ Build your own consensus profile by selecting specific models for each stage.\n'); console.log('๐Ÿ“ Instructions:'); console.log(' โ€ข You will select a model for each of the 4 consensus stages'); console.log(' โ€ข Each stage has different purposes and optimal model characteristics'); console.log(' โ€ข You can browse all available models or use our recommendations\n'); console.log('1. ๐Ÿš€ Yes, let\'s build a custom profile'); console.log('2. โฌ…๏ธ Back to main menu\n'); const proceed = await this.prompt('Choose option (1-2): '); if (proceed === '2') { throw new Error('back to menu'); } else if (proceed !== '1') { console.log('โŒ Please enter 1 or 2\n'); return await this.customModelSelectionFlow(); } await this.ensureModelData(); const selectedModels = await this.selectModelsForPipeline(); const profileName = await this.createProfile(selectedModels); this.showCompletionSummary(selectedModels, profileName); console.log('\nWhat next?\n'); console.log('1. ๐Ÿš€ Create another profile'); console.log('2. โฌ…๏ธ Back to main menu'); console.log('3. ๐Ÿšช Exit setup\n'); const nextAction = await this.prompt('Choose option (1-3): '); if (nextAction === '1') { await this.customModelSelectionFlow(); } else if (nextAction === '3') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } // Otherwise return to menu (option 2) } /** * Browse all models flow - explore models without creating a profile */ async browseAllModelsFlow() { // Ensure prerequisites are met if (!await this.ensurePrerequisites()) { throw new Error('back to menu'); } await this.ensureModelData(); console.log('\n๐ŸŒ Browse All Models\n'); console.log('โ„น๏ธ Explore 300+ models from 50+ providers on OpenRouter\n'); console.log('You can browse by provider or search specific models without creating a profile.\n'); try { // Use the existing browse models functionality but don't create a profile await this.browseAllModels('browsing'); } catch (error) { if (error instanceof Error && error.message.includes('back to menu')) { throw error; } console.log('โŒ Error browsing models. Returning to main menu.\n'); } } /** * View profiles flow */ async viewProfilesFlow() { const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); while (true) { // Always fetch fresh profile data to show current state const existingProfiles = await getAllPipelineProfiles(); console.log('\n๐Ÿ‘๏ธ Current Profiles\n'); if (existingProfiles.length === 0) { console.log('No profiles configured yet.\n'); console.log('1. โฌ…๏ธ Back to main menu\n'); const choice = await this.prompt('Choose option: '); if (choice === '1') { return; } } else { this.displayProfiles(existingProfiles); console.log('\n1. โฌ…๏ธ Back to main menu'); console.log('2. ๐Ÿšช Exit setup\n'); const choice = await this.prompt('Choose option: '); if (choice === '1') { return; } else if (choice === '2') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } else { console.log('โŒ Invalid choice. Please try again.\n'); } } } } /** * Set default profile flow */ async setDefaultProfileFlow() { const { getAllPipelineProfiles, setDefaultPipelineProfile } = await import('../../storage/unified-database.js'); const existingProfiles = await getAllPipelineProfiles(); while (true) { console.log('\n๐ŸŽฏ Set Default Profile\n'); if (existingProfiles.length === 0) { console.log('No profiles available to set as default.\n'); return; } if (existingProfiles.length === 1) { console.log('Only one profile exists - it\'s already the default.\n'); return; } console.log('Current profiles:\n'); existingProfiles.forEach((profile, index) => { const isDefault = profile.is_default ? ' โญ (CURRENT DEFAULT)' : ''; console.log(`${index + 1}. ๐Ÿค– ${profile.name}${isDefault}`); }); console.log(`\n${existingProfiles.length + 1}. โฌ…๏ธ Back to main menu`); console.log(`${existingProfiles.length + 2}. ๐Ÿšช Exit setup\n`); const choice = await this.prompt(`Select profile to set as default (1-${existingProfiles.length + 2}): `); const choiceNum = parseInt(choice); if (choiceNum >= 1 && choiceNum <= existingProfiles.length) { const profileToSetDefault = existingProfiles[choiceNum - 1]; if (profileToSetDefault.is_default) { console.log(`\nโœ… "${profileToSetDefault.name}" is already the default profile.\n`); continue; } try { const success = await setDefaultPipelineProfile(profileToSetDefault.name); if (success) { console.log(`\nโœ… Default profile changed to "${profileToSetDefault.name}"!\n`); console.log('๐Ÿ’ก This profile will now be used for all new consensus queries.\n'); return; // Back to main menu } else { console.log(`\nโŒ Failed to set "${profileToSetDefault.name}" as default.\n`); } } catch (error) { console.log(`\nโŒ Error setting default profile: ${error instanceof Error ? error.message : 'Unknown error'}\n`); } } else if (choiceNum === existingProfiles.length + 1) { return; // Back to main menu } else if (choiceNum === existingProfiles.length + 2) { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } else { console.log(`โŒ Please enter a number from 1 to ${existingProfiles.length + 2}\n`); } } } /** * Delete profile flow with number selection */ async deleteProfileFlow() { const { getAllPipelineProfiles, deletePipelineProfile } = await import('../../storage/unified-database.js'); const existingProfiles = await getAllPipelineProfiles(); while (true) { console.log('\n๐Ÿ—‘๏ธ Delete Profile\n'); if (existingProfiles.length === 0) { console.log('No profiles to delete.\n'); return; } console.log('Current profiles:\n'); existingProfiles.forEach((profile, index) => { const isDefault = profile.is_default ? ' (DEFAULT)' : ''; console.log(`${index + 1}. ๐Ÿค– ${profile.name}${isDefault}`); }); console.log(`\n${existingProfiles.length + 1}. โฌ…๏ธ Back to main menu`); console.log(`${existingProfiles.length + 2}. ๐Ÿšช Exit setup\n`); const choice = await this.prompt(`Select profile to delete (1-${existingProfiles.length + 2}): `); const choiceNum = parseInt(choice); if (choiceNum >= 1 && choiceNum <= existingProfiles.length) { const profileToDelete = existingProfiles[choiceNum - 1]; console.log(`\nโš ๏ธ Are you sure you want to delete "${profileToDelete.name}"?`); const confirm = await this.prompt('Type "yes" to confirm, or anything else to cancel: '); if (confirm.toLowerCase() === 'yes') { try { await deletePipelineProfile(profileToDelete.name); console.log(`\nโœ… Profile "${profileToDelete.name}" deleted successfully!\n`); return; // Back to main menu } catch (error) { console.log(`\nโŒ Error deleting profile: ${error instanceof Error ? error.message : 'Unknown error'}\n`); } } else { console.log('\nโŒ Deletion cancelled.\n'); } } else if (choiceNum === existingProfiles.length + 1) { return; // Back to main menu } else if (choiceNum === existingProfiles.length + 2) { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } else { console.log(`โŒ Please enter a number from 1 to ${existingProfiles.length + 2}\n`); } } } /** * Configuration flow for keys */ async configurationFlow() { while (true) { console.log('\n๐Ÿ”‘ Configure Hive & OpenRouter Keys\n'); console.log('1. ๐Ÿท๏ธ Configure Hive License'); console.log('2. ๐Ÿ”— Configure OpenRouter API Key'); console.log('3. โฌ…๏ธ Back to main menu'); console.log('4. ๐Ÿšช Exit setup\n'); const choice = await this.prompt('Choose option: '); if (choice === '1') { await this.setupLicense(); } else if (choice === '2') { await this.setupOpenRouter(); } else if (choice === '3') { return; // Back to main menu } else if (choice === '4') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } else { console.log('โŒ Invalid choice. Please try again.\n'); } } } /** * Check for outdated profiles that need cleanup */ async checkForOutdatedProfiles() { try { const { getAllPipelineProfiles } = await import('../../storage/unified-database.js'); const profiles = await getAllPipelineProfiles(); // Check if any profiles have outdated model references for (const profile of profiles) { const models = await getAllModels(); const generatorExists = models.some(m => m.openrouter_id === profile.generator_model); const refinerExists = models.some(m => m.openrouter_id === profile.refiner_model); const validatorExists = models.some(m => m.openrouter_id === profile.validator_model); const curatorExists = models.some(m => m.openrouter_id === profile.curator_model); if (!generatorExists || !refinerExists || !validatorExists || !curatorExists) { return true; } } return false; } catch (error) { return false; } } /** * Cleanup outdated profiles */ async cleanupOutdatedProfiles() { console.log('\n๐Ÿงน Cleanup Outdated Profiles\n'); console.log('Scanning for profiles with outdated model references...\n'); try { const { getAllPipelineProfiles, deletePipelineProfile } = await import('../../storage/unified-database.js'); const profiles = await getAllPipelineProfiles(); const models = await getAllModels(); const outdatedProfiles = []; for (const profile of profiles) { const generatorExists = models.some(m => m.openrouter_id === profile.generator_model); const refinerExists = models.some(m => m.openrouter_id === profile.refiner_model); const validatorExists = models.some(m => m.openrouter_id === profile.validator_model); const curatorExists = models.some(m => m.openrouter_id === profile.curator_model); if (!generatorExists || !refinerExists || !validatorExists || !curatorExists) { outdatedProfiles.push(profile); } } if (outdatedProfiles.length === 0) { console.log('โœ… No outdated profiles found!\n'); return; } console.log(`Found ${outdatedProfiles.length} outdated profile(s):\n`); outdatedProfiles.forEach((profile, index) => { console.log(`${index + 1}. ${profile.name}`); }); const confirmAll = await this.prompt('\nDelete all outdated profiles? (y/N): '); if (confirmAll.toLowerCase() === 'y' || confirmAll.toLowerCase() === 'yes') { for (const profile of outdatedProfiles) { await deletePipelineProfile(profile.name); console.log(`๐Ÿ—‘๏ธ Deleted: ${profile.name}`); } console.log('\nโœ… Cleanup completed!\n'); } else { console.log('\nโŒ Cleanup cancelled.\n'); } } catch (error) { console.log(`โŒ Error during cleanup: ${error instanceof Error ? error.message : 'Unknown error'}\n`); } } /** * Ensure prerequisites (license and OpenRouter) are configured */ async ensurePrerequisites() { const { checkLicenseStatus } = await import('./license-configuration.js'); const licenseStatus = await checkLicenseStatus(); const openRouterConfigured = await isOpenRouterConfigured(); if (!licenseStatus.hasLicense || !licenseStatus.isValid || !openRouterConfigured) { console.log('\nโš ๏ธ Prerequisites Required\n'); if (!licenseStatus.hasLicense || !licenseStatus.isValid) { console.log('โŒ Hive license not configured'); } if (!openRouterConfigured) { console.log('โŒ OpenRouter API key not configured'); } console.log('\nPlease configure these first via:'); console.log('๐Ÿ”‘ Configure Hive & OpenRouter Keys (from main menu)\n'); console.log('1. ๐Ÿ”ง Go to configuration now'); console.log('2. โฌ…๏ธ Back to main menu\n'); const configure = await this.prompt('Choose option (1-2): '); if (configure === '1') { await this.configurationFlow(); // Check again after configuration return await this.ensurePrerequisites(); } return false; } return true; } /** * Implement expert template */ async implementExpertTemplate(template, silent = false) { if (!silent) { console.log(`\n๐Ÿš€ Implementing: ${template.name}\n`); console.log(`๐Ÿ“‹ ${template.description}\n`); } try { // Enhanced logging for Windows debugging (only if not silent) // Debug statements removed for cleaner user experience const { createExpertProfile } = await import('../expert-profile-templates.js'); const result = await createExpertProfile(template.id); const profileName = result.profile.name; // Check if this is the first profile and set as default const { getAllPipelineProfiles, setDefaultPipelineProfile } = await import('../../storage/unified-database.js'); const allProfiles = await getAllPipelineProfiles(); // Set as default if this is the first profile if (allProfiles.length === 1) { await setDefaultPipelineProfile(profileName); if (!silent) { console.log(`๐ŸŽฏ Set "${profileName}" as your default profile`); } } const createdProfile = allProfiles.find(p => p.name === profileName); if (!createdProfile) { throw new Error(`Profile creation completed but profile "${profileName}" not found in database`); } if (!silent) { console.log(`โœ… Expert profile "${profileName}" created successfully!\n`); console.log(`๐Ÿ’ก This profile is optimized for: ${template.useCases?.join(', ') || 'General use'}\n`); // Ask if they want to create another profile console.log('\nWhat would you like to do next?\n'); console.log('1. ๐Ÿš€ Create another expert profile'); console.log('2. โฌ…๏ธ Back to main menu'); console.log('3. ๐Ÿšช Exit setup\n'); const nextChoice = await this.prompt('Choose option (1-3): '); if (nextChoice === '1') { // Continue in the expert templates flow return; } else if (nextChoice === '2') { throw new Error('back to menu'); } else if (nextChoice === '3') { console.log('\n๐Ÿ‘‹ Goodbye!\n'); process.exit(0); } } } catch (error) { if (error instanceof Error && error.message === 'back to menu') { throw error; } // Enhanced error logging for Windows debugging (only if not silent) if (!silent) { console.log(`โŒ Error creating expert profile: ${error instanceof Error ? error.message : 'Unknown error'}\n`); if (error instanceof Error && error.message.includes('Failed to resolve internal ID')) { console.log('๐Ÿ’ก This usually means model data needs to be synced. Try running: hive models update\n'); } } // Re-throw error for silent mode so caller can handle it if (silent) { throw error; } if (error instanceof Error && (error.message.includes('SQLITE') || error.message.includes('database'))) { console.log('๐Ÿ’ก Try running: hive diagnose'); console.log('๐Ÿ’ก If that fails, try: hive repair'); } console.log('Returning to expert templates menu...\n'); return; // Return to expert templates loop instead of forcing manual creation } } // Quick Setup methods removed - using simplified setup flow only /** * Setup Hive.AI License */ async setupLicense() { console.log('๐Ÿ”‘ Step 0: hive-ai License Configuration\n'); try { const { checkLicenseStatus, LicenseConfigurationWizard } = await import('./license-configuration.js'); const licenseStatus = await checkLicenseStatus(); if (licenseStatus.hasLicense && licenseStatus.isValid) { console.log(`โœ… License already configured - ${licenseStatus.tier} tier!\n`); const reconfigure = await this.prompt('Would