UNPKG

remcode

Version:

Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.

400 lines (399 loc) 19.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SetupMCPHandler = void 0; const logger_1 = require("../../utils/logger"); const detector_1 = require("../../setup/detector"); const initializer_1 = require("../../setup/initializer"); const prerequisites_1 = require("../../setup/prerequisites"); const remcode_config_1 = require("../../setup/remcode-config"); const secrets_1 = require("../../setup/secrets"); const generator_1 = require("../../workflows/generator"); const workflow_generator_1 = require("../../setup/workflow-generator"); const model_initializer_1 = require("../../setup/model-initializer"); const logger = (0, logger_1.getLogger)('SetupMCPHandler'); class SetupMCPHandler { constructor(githubToken) { this.setupDetector = new detector_1.SetupDetector(); this.setupInitializer = new initializer_1.SetupInitializer(); this.prerequisites = new prerequisites_1.Prerequisites(); this.configManager = new remcode_config_1.RemcodeConfigManager(); this.secretsManager = new secrets_1.SecretsManager(githubToken); this.workflowGenerator = new generator_1.WorkflowGenerator(); } /** * Handle repository setup request */ async handleSetupRepository(req, res, params) { const options = params || req.body; const { owner, repo, token, branch = 'main', configOverrides = {}, workflowType = 'basic', skipWorkflows = false, skipSecrets = false, confirm = false } = options; if (!owner || !repo) { res.status(400).json({ error: 'Owner and repo are required' }); return; } try { // Validation is now handled globally in the main MCP router logger.info(`Setting up repository: ${owner}/${repo}`); // Check prerequisites const prereqChecks = await this.prerequisites.checkAll(); const allPassed = prereqChecks.every(check => check.passed); const criticalFailed = prereqChecks.some(check => check.critical && !check.passed); const errors = prereqChecks.filter(check => !check.passed).map(check => check.message); if (criticalFailed) { res.status(400).json({ message: 'Critical prerequisites check failed', prerequisites: prereqChecks, errors: errors, allPassed: false, criticalFailed: true }); return; } // Detect setup needs const setupStatus = await this.setupDetector.detectSetupNeeds(); if (!setupStatus.needsSetup) { res.status(200).json({ message: 'Repository already set up', status: setupStatus, prerequisites: prereqChecks, allPassed: allPassed }); return; } // If confirmation is required and not provided, return setup needs if (!confirm) { res.status(200).json({ message: 'Setup required. Confirm to proceed.', setupRequired: setupStatus, prerequisites: prereqChecks, allPassed: allPassed, confirmationNeeded: true }); return; } // Initialize repository with proper options const initResult = await this.setupInitializer.initializeRepository({ owner, repo, token: token || '', defaultBranch: branch, skipSecrets: skipSecrets || false, skipWorkflow: skipWorkflows || false, forceUpdate: false, workflowType: workflowType === 'all' ? workflow_generator_1.WorkflowTemplateType.ADVANCED : (workflowType === 'advanced' ? workflow_generator_1.WorkflowTemplateType.ADVANCED : workflow_generator_1.WorkflowTemplateType.BASIC), customSecrets: {} }); // Create or update config with overrides if provided let configResult = { success: false, created: false, config: {} }; try { if (Object.keys(configOverrides).length > 0) { const exists = await this.configManager.configExists(); if (exists) { // Read existing config const currentConfig = await this.configManager.readConfig(); // Merge with overrides const newConfig = { ...currentConfig, ...configOverrides }; // Write updated config using updateConfig method await this.configManager.updateConfig(newConfig); configResult = { success: true, created: false, config: newConfig }; } else { // Create new config await this.configManager.updateConfig(configOverrides); configResult = { success: true, created: true, config: configOverrides }; } } else { // Ensure default config exists const exists = await this.configManager.configExists(); if (!exists) { // Create default config const defaultConfig = { version: '1.0.0' }; await this.configManager.updateConfig(defaultConfig); configResult = { success: true, created: true, config: defaultConfig }; } else { // Config already exists const currentConfig = await this.configManager.readConfig(); configResult = { success: true, created: false, config: currentConfig }; } } } catch (configError) { logger.error(`Config setup failed: ${configError instanceof Error ? configError.message : String(configError)}`); // Non-fatal, continue with setup } // Initialize embedding model (critical for vectorization) let modelInitResult = { success: false, modelId: '', modelName: '', embeddingDimension: 0, isHealthy: false, error: undefined, availableModels: [] }; try { const huggingfaceToken = process.env.HUGGINGFACE_TOKEN; if (huggingfaceToken) { logger.info('🔧 Initializing embedding model for codebase vectorization...'); const modelInitializer = new model_initializer_1.ModelInitializer(huggingfaceToken); modelInitResult = await modelInitializer.initializeEmbeddingModel({ token: huggingfaceToken, preferredModel: 'microsoft/codebert-base', testEmbedding: true }); if (modelInitResult.success) { logger.info(`✅ Embedding model initialized: ${modelInitResult.modelName} (${modelInitResult.modelId})`); // Update config with model information try { const modelConfig = model_initializer_1.ModelInitializer.getModelConfiguration(modelInitResult); const currentConfig = await this.configManager.readConfig(); const updatedConfig = { ...currentConfig, vectorization: { ...currentConfig.vectorization, ...modelConfig } }; await this.configManager.updateConfig(updatedConfig); logger.info('📝 Model configuration saved to .remcode file'); } catch (configUpdateError) { logger.warn(`Warning: Could not update config with model info: ${configUpdateError instanceof Error ? configUpdateError.message : String(configUpdateError)}`); } } else { logger.warn(`⚠️ Model initialization failed: ${modelInitResult.error || 'Unknown error'}`); logger.warn('🔄 Proceeding with setup - embeddings may use fallback models'); } } else { logger.warn('⚠️ No HuggingFace token found. Skipping model initialization.'); logger.info('💡 Add HUGGINGFACE_TOKEN to environment variables for embedding functionality'); } } catch (modelError) { logger.error(`❌ Model initialization failed: ${modelError instanceof Error ? modelError.message : String(modelError)}`); logger.warn('🔄 Proceeding with setup - embeddings will use fallback models'); } // Generate workflows if not skipped let workflowResult; if (!skipWorkflows) { try { // Determine which workflow type to generate if (workflowType === 'scheduled') { workflowResult = await this.workflowGenerator.generateScheduledWorkflow(`${owner}/${repo}`); } else if (workflowType === 'advanced') { workflowResult = await this.workflowGenerator.generateAdvancedWorkflow(`${owner}/${repo}`); } else if (workflowType === 'all') { workflowResult = await this.workflowGenerator.generateAllWorkflows(`${owner}/${repo}`); } else { // Default to basic workflow workflowResult = await this.workflowGenerator.generateRemcodeWorkflow(`${owner}/${repo}`); } } catch (workflowError) { logger.error(`Workflow generation failed: ${workflowError instanceof Error ? workflowError.message : String(workflowError)}`); // Non-fatal, continue with setup } } // Set up secrets if not skipped let secretsResult = { success: false, successCount: 0, failureCount: 0 }; if (!skipSecrets && token) { try { // Configure repository secrets using the secrets manager const secretsOperationResult = await this.secretsManager.configureRepositorySecrets(owner, repo); if (secretsOperationResult) { secretsResult = { success: secretsOperationResult.successful > 0, successCount: secretsOperationResult.successful, failureCount: secretsOperationResult.failed }; } } catch (secretsError) { logger.error(`Secrets setup failed: ${secretsError instanceof Error ? secretsError.message : String(secretsError)}`); // Non-fatal, continue with setup } } res.status(200).json({ message: 'Repository setup completed', status: 'initialized', repository: { owner, repo, branch }, config: configResult?.success ? 'configured' : 'skipped', workflows: workflowResult && 'success' in workflowResult && workflowResult.success ? 'generated' : (skipWorkflows ? 'skipped' : 'failed'), secrets: secretsResult?.success ? 'configured' : (skipSecrets ? 'skipped' : 'failed'), embeddings: { status: modelInitResult.success ? 'initialized' : 'failed', modelId: modelInitResult.modelId, modelName: modelInitResult.modelName, dimension: modelInitResult.embeddingDimension, healthy: modelInitResult.isHealthy, availableModels: modelInitResult.availableModels?.length || 0 } }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Setup failed: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } } /** * Check repository prerequisites */ async handleCheckPrerequisites(req, res, params) { try { const prereqCheck = await this.prerequisites.checkAll(); res.status(200).json(prereqCheck); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Prerequisites check failed: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } } /** * Configure repository settings */ async handleConfigureRepository(req, res, params) { const { config = {} } = params || req.body; try { const exists = await this.configManager.configExists(); if (exists) { // Update existing config const currentConfig = await this.configManager.readConfig(); const newConfig = { ...currentConfig, ...config }; await this.configManager.updateConfig(newConfig); res.status(200).json({ message: 'Configuration updated', config: newConfig, created: false }); } else { // Create new config await this.configManager.updateConfig(config); res.status(200).json({ message: 'Configuration created', config: config, created: true }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Configuration failed: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } } /** * Set up repository secrets */ async handleSetupSecrets(req, res, params) { const { owner, repo, secrets = {} } = params || req.body; if (!owner || !repo) { res.status(400).json({ error: 'Owner and repo are required' }); return; } try { // Configure repository secrets using the secrets manager const secretsOperationResult = await this.secretsManager.configureRepositorySecrets(owner, repo); if (!secretsOperationResult) { throw new Error('Failed to configure repository secrets'); } res.status(200).json({ message: 'Secrets configured successfully', secretsConfigured: secretsOperationResult.successful, secretsFailed: secretsOperationResult.failed, total: secretsOperationResult.total }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Secrets setup failed: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } } /** * Generate repository workflows */ async handleGenerateWorkflows(req, res, params) { const { owner, repo, type = 'basic' } = params || req.body; if (!owner || !repo) { res.status(400).json({ error: 'Owner and repo are required' }); return; } try { let result; const repoName = `${owner}/${repo}`; switch (type) { case 'scheduled': result = await this.workflowGenerator.generateScheduledWorkflow(repoName); break; case 'advanced': result = await this.workflowGenerator.generateAdvancedWorkflow(repoName); break; case 'all': result = await this.workflowGenerator.generateAllWorkflows(repoName); break; case 'basic': default: result = await this.workflowGenerator.generateRemcodeWorkflow(repoName); } // Handle the different result types if (type === 'all' && result instanceof Map) { // For all workflows, return a summary of each workflow generated const workflowResults = Array.from(result.entries()); const successfulWorkflows = workflowResults.filter(([_, r]) => r.success); if (successfulWorkflows.length > 0) { res.status(200).json({ message: `${successfulWorkflows.length} workflow(s) generated successfully`, workflows: workflowResults.map(([name, _]) => name), paths: successfulWorkflows.map(([_, result]) => result.filePath), successCount: successfulWorkflows.length, totalCount: workflowResults.length }); } else { res.status(500).json({ error: 'Failed to generate workflows', details: 'No workflows could be generated successfully' }); } } else if (result && 'success' in result) { // For single workflow results if (result.success) { res.status(200).json({ message: `${type} workflow generated successfully`, workflow: type, path: result.filePath }); } else { res.status(500).json({ error: 'Failed to generate workflow', details: result.error }); } } else { // Fallback for unknown result type res.status(500).json({ error: 'Failed to generate workflows', details: 'Unknown result format' }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Workflow generation failed: ${errorMessage}`); res.status(500).json({ error: errorMessage }); } } } exports.SetupMCPHandler = SetupMCPHandler;