UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

279 lines (278 loc) 12.5 kB
"use strict"; /** * Deploy Manifests Tool - Apply Kubernetes manifests or execute Helm installations * Supports both capability-based solutions (kubectl apply) and Helm-based solutions (helm install) */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA = exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = exports.DEPLOYMANIFESTS_TOOL_NAME = void 0; exports.handleDeployManifestsTool = handleDeployManifestsTool; const zod_1 = require("zod"); const error_handling_1 = require("../core/error-handling"); const deploy_operation_1 = require("../core/deploy-operation"); const generic_session_manager_1 = require("../core/generic-session-manager"); const solution_utils_1 = require("../core/solution-utils"); const plugin_registry_1 = require("../core/plugin-registry"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); // PRD #343: Inline utilities (helm-utils.ts removed - all helm operations via plugin) /** * Get the path for Helm values file */ function getHelmValuesPath(solutionId) { const tmpDir = path.join(process.cwd(), 'tmp'); return path.join(tmpDir, `${solutionId}-values.yaml`); } /** * Check if Helm values file exists for a solution */ function helmValuesExist(solutionId) { return fs.existsSync(getHelmValuesPath(solutionId)); } // Tool metadata for direct MCP registration exports.DEPLOYMANIFESTS_TOOL_NAME = 'deployManifests'; exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = 'Deploy Kubernetes manifests from generated solution with kubectl apply --wait'; // Zod schema for MCP registration exports.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA = { solutionId: zod_1.z.string().regex(/^sol-\d+-[a-f0-9]{8}$/).describe('Solution ID to deploy (e.g., sol-1762983784617-9ddae2b8)'), timeout: zod_1.z.number().min(1).max(600).optional().describe('Deployment timeout in seconds (default: 30)') }; /** * Direct MCP tool handler for deployManifests functionality * PRD #343: pluginManager required for kubectl operations */ /** * PRD #359: Uses unified plugin registry for kubectl operations */ async function handleDeployManifestsTool(args, dotAI, logger, requestId) { return await error_handling_1.ErrorHandler.withErrorHandling(async () => { logger.debug('Handling deployManifests request', { requestId, solutionId: args?.solutionId, timeout: args?.timeout }); // Input validation is handled automatically by MCP SDK with Zod schema // args are already validated and typed when we reach this point // Load solution session to determine solution type const sessionManager = new generic_session_manager_1.GenericSessionManager('sol'); const session = sessionManager.getSession(args.solutionId); if (!session) { throw new Error(`Solution not found: ${args.solutionId}`); } const solution = session.data; const timeout = args.timeout || 30; logger.debug('Solution loaded successfully', { solutionId: args.solutionId, solutionType: solution.type }); // Branch based on solution type if (solution.type === 'helm') { logger.info('Detected Helm solution, using Helm deployment flow', { solutionId: args.solutionId, chart: solution.chart ? `${solution.chart.repositoryName}/${solution.chart.chartName}` : 'unknown' }); if (!solution.chart) { throw new Error('Helm solution missing chart information'); } const chart = solution.chart; const userAnswers = (0, solution_utils_1.extractUserAnswers)(solution); const releaseName = userAnswers.name; const namespace = userAnswers.namespace || 'default'; if (!releaseName) { throw new Error('Release name (name) is required for Helm deployment'); } // Get values content if values file exists // PRD #343: Read values and pass to plugin (no file path to plugin) let valuesYaml; if (helmValuesExist(args.solutionId)) { valuesYaml = fs.readFileSync(getHelmValuesPath(args.solutionId), 'utf8'); } logger.info('Starting Helm deployment via plugin', { solutionId: args.solutionId, chart: `${chart.repositoryName}/${chart.chartName}`, releaseName, namespace, hasValuesFile: !!valuesYaml, timeout, requestId }); // PRD #359: All Helm operations go through unified plugin registry // First, add/update the Helm repository const repoResult = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'helm_repo_add', { name: chart.repositoryName, url: chart.repository }); if (!repoResult.success) { logger.warn('Helm repo add failed', { error: repoResult.error?.message }); } // Deploy using helm_install with wait const installResult = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'helm_install', { releaseName, chart: `${chart.repositoryName}/${chart.chartName}`, namespace, values: valuesYaml, version: chart.version, dryRun: false, wait: true, timeout: `${timeout}s`, createNamespace: true }); // Check for nested error in result let nestedError; if (installResult.success && typeof installResult.result === 'object' && installResult.result !== null) { const nestedResult = installResult.result; if (nestedResult.success === false) { nestedError = nestedResult.error || nestedResult.message || 'Helm install failed'; } } // Extract only the data field - never pass JSON wrapper let output = ''; if (installResult.success && !nestedError) { if (typeof installResult.result === 'object' && installResult.result !== null) { const resultData = installResult.result; if (resultData.data !== undefined) { output = String(resultData.data); } else if (typeof resultData === 'string') { output = resultData; } // Don't throw error here - empty output is acceptable for Helm } else { output = String(installResult.result || ''); } } const result = { success: installResult.success && !nestedError, output, error: nestedError || (!installResult.success ? installResult.error?.message : undefined) }; logger.info('Helm deployment completed', { success: result.success, solutionId: args.solutionId, releaseName, namespace, requestId }); // Update session with deployed stage for UI page refresh support if (result.success) { sessionManager.updateSession(args.solutionId, { stage: 'deployed' }); } const response = { success: result.success, solutionId: args.solutionId, solutionType: 'helm', releaseName, namespace, chart: { repository: chart.repository, repositoryName: chart.repositoryName, chartName: chart.chartName, version: chart.version }, message: result.success ? `Helm release "${releaseName}" deployed successfully to namespace "${namespace}"` : `Helm deployment failed: ${result.error}`, helmOutput: result.output || result.error, deploymentComplete: result.success, timestamp: new Date().toISOString(), agentInstructions: 'Deployment command executed. To verify the deployment is running correctly and resources are healthy, use the query tool to check pod status, events, and logs.' }; return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; } // Capability-based solution: Use existing DeployOperation // PRD #359: Uses unified plugin registry for kubectl operations logger.info('Using capability-based deployment flow', { solutionId: args.solutionId }); const deployOp = new deploy_operation_1.DeployOperation(); const deployOptions = { solutionId: args.solutionId, timeout }; logger.info('Starting deployment operation', { solutionId: args.solutionId, timeout: deployOptions.timeout, requestId }); const result = await deployOp.deploy(deployOptions); logger.info('Deployment operation completed', { success: result.success, solutionId: result.solutionId, manifestPath: result.manifestPath, readinessTimeout: result.readinessTimeout, requestId }); // Update session with deployed stage for UI page refresh support if (result.success) { sessionManager.updateSession(args.solutionId, { stage: 'deployed' }); } // Prepare response with deployment status const response = { success: result.success, solutionId: result.solutionId, solutionType: 'capability', manifestPath: result.manifestPath, readinessTimeout: result.readinessTimeout, message: result.message, kubectlOutput: result.kubectlOutput, // Additional deployment status info deploymentComplete: result.success && !result.readinessTimeout, requiresStatusCheck: result.success && result.readinessTimeout, timestamp: new Date().toISOString(), agentInstructions: 'Deployment command executed. To verify the deployment is running correctly and resources are healthy, use the query tool to check pod status, events, and logs.' }; return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; }, { operation: 'deploy_manifests', component: 'DeployManifestsTool', requestId, input: args }); }