UNPKG

cloudagent-deploy

Version:

CloudAgent Deploy - MCP Server for CloudFormation deployments via backend API

290 lines 10.6 kB
export class BackendApiClient { config; constructor(config) { this.config = config; } /** * Validate a CloudFormation template via backend API */ async validateTemplate(template) { try { const response = await this.makeRequest('/cfn/validate', 'POST', { template }); return { valid: response.valid || false, errors: response.errors || [], warnings: response.warnings || [], message: response.message }; } catch (error) { return { valid: false, errors: [`Backend validation error: ${error.message}`], warnings: [] }; } } /** * Deploy a CloudFormation stack via backend API */ async deployStack(params) { try { const payload = {}; if (params.template !== undefined) payload.template = params.template; if (params.templateType !== undefined) payload.templateType = params.templateType; if (params.appName !== undefined) payload.appName = params.appName; if (params.description !== undefined) payload.description = params.description; if (params.metadata !== undefined) payload.metadata = params.metadata; if (params.stackName !== undefined) payload.stackName = params.stackName; if (params.parameters !== undefined) payload.parameters = params.parameters; if (params.tags !== undefined) payload.tags = params.tags; if (params.capabilities !== undefined) payload.capabilities = params.capabilities; if (params.targetRegion !== undefined) payload.targetRegion = params.targetRegion; const response = await this.makeRequest('/cfn/deploy', 'POST', payload); // Check if this is a region selection error if (response.allowedRegions) { return { success: false, stackName: response.stackName || params.stackName || '', status: 'REGION_SELECTION_REQUIRED', requiresRegionSelection: true, allowedRegions: response.allowedRegions, regionSelectionMessage: response.message, error: response.error }; } // Override success determination for in-progress statuses const status = response.status || 'UNKNOWN'; const isInProgress = [ 'CREATE_IN_PROGRESS', 'UPDATE_IN_PROGRESS', 'DELETE_IN_PROGRESS' ].includes(status); // If stack is in progress, that means deployment started successfully const actualSuccess = response.success || isInProgress; return { success: actualSuccess, stackId: response.stackId, stackName: response.stackName || params.stackName || '', status: status, outputs: response.outputs, events: response.events, error: response.error }; } catch (error) { return { success: false, stackName: params.stackName || '', status: 'FAILED', error: `Backend deployment error: ${error.message}` }; } } /** * Start an intent-driven deploy (interactive): returns NEEDS_INPUT with questions */ async startDeployInteractive() { return this.makeRequest('/cfn/deploy', 'POST', {}); } /** * Continue an intent-driven deploy with answers (correlationId, appName, intent, targetRegion) */ async continueDeployInteractive(payload) { return this.makeRequest('/cfn/deploy', 'POST', payload); } /** * Delete a CloudFormation stack via backend API */ async deleteStack(stackName) { try { const response = await this.makeRequest('/cfn/delete', 'POST', { stackName }); // Override success determination for in-progress statuses const status = response.status || 'UNKNOWN'; const isInProgress = [ 'DELETE_IN_PROGRESS' ].includes(status); // If stack deletion is in progress, that means deletion started successfully const actualSuccess = response.success || isInProgress; return { success: actualSuccess, stackName, status: status, error: response.error }; } catch (error) { return { success: false, stackName, status: 'DELETE_FAILED', error: `Backend deletion error: ${error.message}` }; } } /** * Get stack status via backend API */ async getStackStatus(stackName, includeEvents = false) { try { const queryParams = `?stackName=${encodeURIComponent(stackName)}&includeEvents=${includeEvents}`; const response = await this.makeRequest('/cfn/status', 'GET', null, queryParams); return { success: response.success, stackId: response.stackId, stackName: response.stackName, status: response.status, outputs: response.outputs, error: response.error, failureDetails: response.failureAnalysis, rollbackInfo: response.rollbackInfo, recentEvents: response.recentEvents }; } catch (error) { return { success: false, status: 'ERROR', error: `Backend status error: ${error.message}` }; } } /** * List stacks via backend API */ async listStacks() { try { const response = await this.makeRequest('/cfn/list', 'GET'); return response.stacks || []; } catch (error) { console.error('Backend list error:', error); return []; } } /** * Get user profile to determine deployment preferences */ async getUserProfile() { try { const response = await this.makeRequest('/user/profile', 'GET'); return { deploymentType: response.deploymentType || 'FRESH', allowedSharedStacks: response.allowedSharedStacks || [] }; } catch (error) { // Default to FRESH if can't determine console.warn('Could not determine user deployment type, defaulting to FRESH'); return { deploymentType: 'FRESH' }; } } /** * Generate presigned URL for S3 operations via backend API */ async getPresignedUrl(params) { try { const response = await this.makeRequest('/storage/presigned-url', 'POST', { appName: params.appName, // Changed from stackName to appName objectKey: params.objectKey, operation: params.operation, expirationMinutes: params.expirationMinutes || 60 }); return { success: true, presignedUrl: response.presignedUrl, bucketName: response.bucketName, objectKey: response.objectKey, operation: response.operation, expiresAt: response.expiresAt, websiteUrl: response.websiteUrl }; } catch (error) { return { success: false, error: `Backend presigned URL error: ${error.message}` }; } } /** * Generate batch presigned URLs for multiple file operations */ async getBatchPresignedUrls(params) { try { const response = await this.makeRequest('/storage/batch-presigned-urls', 'POST', { appName: params.appName, files: params.files, operation: params.operation, expirationMinutes: params.expirationMinutes || 60 }); return { success: response.success || false, urls: response.urls, bucketName: response.bucketName, websiteUrl: response.websiteUrl, totalFiles: response.totalFiles, successfulUrls: response.successfulUrls, errors: response.errors, error: response.error }; } catch (error) { return { success: false, error: `Backend batch presigned URL error: ${error.message}` }; } } /** * Make HTTP request to backend API */ async makeRequest(endpoint, method, body, queryParams) { const url = `${this.config.apiEndpoint}${endpoint}${queryParams || ''}`; const options = { method, headers: { 'Content-Type': 'application/json', 'x-mcp-api-key': this.config.apiKey // Fixed: Use correct header name } }; if (body && method !== 'GET') { options.body = JSON.stringify(body); } const response = await fetch(url, options); if (!response.ok) { let errorMessage = `HTTP ${response.status}: ${response.statusText}`; let errorBody = null; try { errorBody = await response.json(); if (errorBody.error) { errorMessage = errorBody.error; } } catch { // Use default error message if can't parse response } // Special handling for region selection errors - don't throw, return the error body if (response.status === 400 && errorBody?.allowedRegions) { return errorBody; // Return the region selection response instead of throwing } throw new Error(errorMessage); } return await response.json(); } } //# sourceMappingURL=backend-client.js.map