@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
JavaScript
/**
* 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