Skip to main content
Glama
setup.ts8.21 kB
/** * @fileoverview Main setup orchestration for interactive model configuration */ import chalk from 'chalk'; import { getConfig, getModelConfiguration, setModel, writeConfig } from '../../lib/model-management.js'; import { customProviderConfigs, handleCustomProvider } from './custom-providers.js'; import { buildPromptChoices, displaySetupIntro, promptForModel } from './prompts.js'; import type { CurrentModels, CustomProviderId, ModelRole } from './types.js'; /** * Check if a value is a custom provider ID */ function isCustomProviderId(value: unknown): value is CustomProviderId { if (typeof value !== 'string') return false; return Object.values(customProviderConfigs).some( (config) => config.id === value ); } /** * Handle setting a model for a specific role */ async function handleSetModel( role: ModelRole, selectedValue: string | { id: string; provider: string } | null, currentModel: { modelId?: string | null; provider?: string | null; baseURL?: string | null; } | null, projectRoot: string ): Promise<{ success: boolean; modified: boolean }> { const currentModelId = currentModel?.modelId ?? null; const currentProvider = currentModel?.provider ?? null; const currentBaseURL = currentModel?.baseURL ?? null; // Handle cancellation if (selectedValue === '__CANCEL__') { console.log( chalk.yellow(`\nSetup canceled during ${role} model selection.`) ); return { success: false, modified: false }; } // Handle no change if (selectedValue === '__NO_CHANGE__') { console.log(chalk.gray(`No change selected for ${role} model.`)); return { success: true, modified: false }; } let modelIdToSet: string | null = null; let providerHint: string | null = null; let baseURL: string | null = null; // Handle custom providers if (isCustomProviderId(selectedValue)) { const result = await handleCustomProvider( selectedValue, role, currentModel, projectRoot ); if (!result.success) { return { success: false, modified: false }; } if (!result.modelId) { return { success: true, modified: false }; } modelIdToSet = result.modelId; providerHint = result.provider; baseURL = result.baseURL || null; } // Handle standard model selection else if ( selectedValue && typeof selectedValue === 'object' && 'id' in selectedValue ) { modelIdToSet = selectedValue.id; providerHint = selectedValue.provider; } // Handle disabling fallback else if (selectedValue === null && role === 'fallback') { modelIdToSet = null; providerHint = null; } // Unknown selection else if (selectedValue) { console.error( chalk.red( `Internal Error: Unexpected selection value for ${role}: ${JSON.stringify(selectedValue)}` ) ); return { success: false, modified: false }; } // Check if there's actually a change to make if ( modelIdToSet === currentModelId && (providerHint ?? null) === currentProvider && (baseURL ?? null) === currentBaseURL ) { return { success: true, modified: false }; } // Set the model if (modelIdToSet) { const result = await setModel(role, modelIdToSet, { projectRoot, providerHint: providerHint || undefined, baseURL: baseURL || undefined }); if (result.success) { console.log( chalk.blue( `Set ${role} model: ${result.data?.provider} / ${result.data?.modelId}` ) ); if (result.data?.warning) { console.log(chalk.yellow(result.data?.warning)); } return { success: true, modified: true }; } else { console.error( chalk.red( `Error setting ${role} model: ${result.error?.message || 'Unknown'}` ) ); return { success: false, modified: false }; } } // Disable fallback model else if (role === 'fallback') { const currentCfg = getConfig(projectRoot); if (currentCfg?.models?.fallback?.modelId) { currentCfg.models.fallback = { ...currentCfg.models.fallback, provider: undefined, modelId: undefined }; if (writeConfig(currentCfg, projectRoot)) { console.log(chalk.blue('Fallback model disabled.')); return { success: true, modified: true }; } else { console.error( chalk.red('Failed to disable fallback model in config file.') ); return { success: false, modified: false }; } } else { console.log(chalk.blue('Fallback model was already disabled.')); return { success: true, modified: false }; } } return { success: true, modified: false }; } /** * Run interactive model setup */ export async function runInteractiveSetup( projectRoot: string ): Promise<boolean> { if (!projectRoot) { console.error( chalk.red( 'Error: Could not determine project root for interactive setup.' ) ); process.exit(1); } // Get current configuration const currentConfigResult = await getModelConfiguration({ projectRoot }); const currentModels: CurrentModels = currentConfigResult.success && currentConfigResult.data ? { main: currentConfigResult.data.activeModels.main ? { modelId: currentConfigResult.data.activeModels.main.modelId, provider: currentConfigResult.data.activeModels.main.provider, baseURL: currentConfigResult.data.activeModels.main.baseURL } : null, research: currentConfigResult.data.activeModels.research ? { modelId: currentConfigResult.data.activeModels.research.modelId, provider: currentConfigResult.data.activeModels.research.provider, baseURL: currentConfigResult.data.activeModels.research.baseURL } : null, fallback: currentConfigResult.data.activeModels.fallback ? { modelId: currentConfigResult.data.activeModels.fallback.modelId, provider: currentConfigResult.data.activeModels.fallback.provider, baseURL: currentConfigResult.data.activeModels.fallback.baseURL } : null } : { main: null, research: null, fallback: null }; // Handle config load failure gracefully if ( !currentConfigResult.success && currentConfigResult.error?.code !== 'CONFIG_MISSING' ) { console.warn( chalk.yellow( `Warning: Could not load current model configuration: ${currentConfigResult.error?.message || 'Unknown error'}. Proceeding with defaults.` ) ); } // Build prompt data const mainPromptData = buildPromptChoices('main', currentModels); const researchPromptData = buildPromptChoices('research', currentModels); const fallbackPromptData = buildPromptChoices( 'fallback', currentModels, true ); // Display intro displaySetupIntro(); // Prompt for main model const mainModel = await promptForModel('main', mainPromptData); if (mainModel === '__CANCEL__') { return false; } // Prompt for research model const researchModel = await promptForModel('research', researchPromptData); if (researchModel === '__CANCEL__') { return false; } // Prompt for fallback model const fallbackModel = await promptForModel('fallback', fallbackPromptData); if (fallbackModel === '__CANCEL__') { return false; } // Process all model selections let setupSuccess = true; let setupConfigModified = false; const mainResult = await handleSetModel( 'main', mainModel, currentModels.main, projectRoot ); if (!mainResult.success) setupSuccess = false; if (mainResult.modified) setupConfigModified = true; const researchResult = await handleSetModel( 'research', researchModel, currentModels.research, projectRoot ); if (!researchResult.success) setupSuccess = false; if (researchResult.modified) setupConfigModified = true; const fallbackResult = await handleSetModel( 'fallback', fallbackModel, currentModels.fallback, projectRoot ); if (!fallbackResult.success) setupSuccess = false; if (fallbackResult.modified) setupConfigModified = true; // Display final result if (setupSuccess && setupConfigModified) { console.log(chalk.green.bold('\nModel setup complete!')); } else if (setupSuccess && !setupConfigModified) { console.log(chalk.yellow('\nNo changes made to model configuration.')); } else { console.error( chalk.red( '\nErrors occurred during model selection. Please review and try again.' ) ); } return setupSuccess; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/eyaltoledano/claude-task-master'

If you have feedback or need assistance with the MCP directory API, please join our Discord server