Skip to main content
Glama
deployments.ts13.2 kB
/** * @file Deployment API Routes * @description REST API endpoints for infrastructure deployment workflow * * Endpoints: * POST /v1/deployments/check - Check if message is deployment request * POST /v1/deployments/generate - Generate commands * GET /v1/deployments/:id - Get deployment session * POST /v1/deployments/:id/confirm - Confirm/reject execution * POST /v1/deployments/:id/result - Record command result * GET /v1/deployments/:id/results - Get execution results * DELETE /v1/deployments/:id - Cancel deployment */ import { Router, Request, Response } from 'express'; import { z } from 'zod'; import { logger } from '../../logging/logger.js'; import ChatDeploymentHandler from '../../services/chat/chatDeploymentHandler.js'; import type { CommandGenerationResponse } from '../../services/chat/commandGeneration.js'; const router = Router(); const deploymentHandler = new ChatDeploymentHandler(); /** * Request validation schemas */ const CheckRequestSchema = z.object({ message: z.string().min(1), context: z.any().optional(), }); const GenerateRequestSchema = z.object({ message: z.string().min(1), taskType: z.string().optional(), targetDevice: z.string().optional(), connectionId: z.string().optional(), }); const ConfirmRequestSchema = z.object({ approved: z.boolean(), selectedCommandIds: z.array(z.string()).optional(), }); const ResultRequestSchema = z.object({ commandId: z.string(), success: z.boolean(), stdout: z.string().optional(), stderr: z.string().optional(), exitCode: z.number(), duration: z.number(), }); /** * POST /v1/deployments/check * Check if message is a deployment request */ router.post('/check', async (req: Request, res: Response) => { try { const { message, context: _context } = CheckRequestSchema.parse(req.body); logger.info('[DeploymentAPI] Checking message for deployment request', { messageLength: message.length, }); const detection = await deploymentHandler.detectDeploymentRequest(message); return res.json({ isDeploymentRequest: detection.isDeploymentRequest, taskType: detection.taskType, confidence: detection.confidence, reasoning: detection.reasoning, targetDevice: detection.targetDevice, }); } catch (error) { logger.error('[DeploymentAPI] Check failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(400).json({ error: 'Failed to check deployment request', details: error instanceof Error ? error.message : String(error), }); } }); /** * POST /v1/deployments/generate * Generate deployment commands */ router.post('/generate', async (req: Request, res: Response) => { try { const { message, taskType, targetDevice, connectionId } = GenerateRequestSchema.parse(req.body); logger.info('[DeploymentAPI] Generating deployment commands', { taskType, targetDevice, }); // First detect the deployment request const detection = await deploymentHandler.detectDeploymentRequest(message); if (!detection.isDeploymentRequest && detection.confidence < 0.5) { return res.status(400).json({ error: 'Message does not appear to be a deployment request', confidence: detection.confidence, reasoning: detection.reasoning, }); } // Generate commands const { generationId, response } = await deploymentHandler.generateDeploymentCommands( message, { isDeploymentRequest: true, taskType: (taskType as any) || detection.taskType, targetDevice: targetDevice || detection.targetDevice, connectionId, confidence: detection.confidence, } ); // Get display and session const displayInfo = deploymentHandler.getGenerationDisplay(generationId); if (!displayInfo) { return res.status(500).json({ error: 'Failed to prepare deployment display', }); } // Determine if the LLM response appears to be a final command set or intermediate reasoning const hasCommands = Array.isArray(response.commands) && response.commands.length > 0; const nonPlaceholder = hasCommands && response.commands.some((c: any) => { if (!c || typeof c.command !== 'string') return false; const cmd = c.command.trim().toLowerCase(); // Heuristic: placeholder/sample commands or echo indicate non-final if (cmd.includes('sample command') || cmd.includes('placeholder') || cmd.startsWith('echo')) return false; // Reject obviously short/no-op commands if (cmd.length < 10) return false; return true; }); const isFinal = !!nonPlaceholder; const isReasoning = !isFinal; return res.json({ generationId, sessionId: displayInfo.sessionId, taskDescription: response.taskDescription, commandCount: response.commands.length, commands: response.commands.map((cmd: CommandGenerationResponse['commands'][number]) => ({ id: cmd.id, description: cmd.description, riskLevel: cmd.riskLevel, })), explanation: response.explanation, warnings: response.warnings, affectedServices: response.affectedServices, estimatedDuration: response.estimatedDuration, display: displayInfo.display, // Meta flags to indicate whether this response is a final command set or intermediate reasoning isFinal, isReasoning, }); } catch (error) { logger.error('[DeploymentAPI] Generate failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(400).json({ error: 'Failed to generate deployment commands', details: error instanceof Error ? error.message : String(error), }); } }); /** * GET /v1/deployments/:id * Get deployment session details */ router.get('/:sessionId', (req: Request, res: Response) => { try { const { sessionId } = req.params; const session = deploymentHandler.getExecutionSession(sessionId); if (!session) { return res.status(404).json({ error: 'Session not found', sessionId, }); } return res.json(session); } catch (error) { logger.error('[DeploymentAPI] Get session failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(500).json({ error: 'Failed to retrieve session', }); } }); /** * POST /v1/deployments/:id/confirm * Confirm or reject deployment execution */ router.post('/:sessionId/confirm', async (req: Request, res: Response) => { try { const { sessionId } = req.params; const { approved, selectedCommandIds } = ConfirmRequestSchema.parse(req.body); logger.info('[DeploymentAPI] Processing confirmation', { sessionId, approved, commandCount: selectedCommandIds?.length, }); const result = await deploymentHandler.handleUserConfirmation( sessionId, approved, selectedCommandIds ); return res.json({ approved: result.approved, sessionId: result.session.sessionId, status: result.session.status, message: result.message, commandIds: result.session.commandIds, }); } catch (error) { logger.error('[DeploymentAPI] Confirmation failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(400).json({ error: 'Failed to process confirmation', details: error instanceof Error ? error.message : String(error), }); } }); /** * POST /v1/deployments/:id/result * Record command execution result */ router.post('/:sessionId/result', (req: Request, res: Response) => { try { const { sessionId } = req.params; const result = ResultRequestSchema.parse(req.body); logger.info('[DeploymentAPI] Recording execution result', { sessionId, commandId: result.commandId, success: result.success, }); const recorded = deploymentHandler.recordExecutionResult(sessionId, result.commandId, { success: result.success, stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode, duration: result.duration, }); if (!recorded) { return res.status(404).json({ error: 'Session not found', sessionId, }); } return res.json({ recorded: true, sessionId, commandId: result.commandId, }); } catch (error) { logger.error('[DeploymentAPI] Result recording failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(400).json({ error: 'Failed to record result', details: error instanceof Error ? error.message : String(error), }); } }); /** * POST /v1/deployments/:id/finalize * Finalize execution session */ router.post('/:sessionId/finalize', (req: Request, res: Response) => { try { const { sessionId } = req.params; const { success, error } = req.body; logger.info('[DeploymentAPI] Finalizing session', { sessionId, success, }); const finalSession = deploymentHandler.finalizeExecution(sessionId, success, error); if (!finalSession) { return res.status(404).json({ error: 'Session not found', sessionId, }); } const resultsDisplay = deploymentHandler.getResultsDisplay(sessionId); return res.json({ sessionId, status: finalSession.status, success, error: finalSession.errorMessage, resultCount: finalSession.results?.length || 0, resultsDisplay, }); } catch (error) { logger.error('[DeploymentAPI] Finalize failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(500).json({ error: 'Failed to finalize session', }); } }); /** * GET /v1/deployments/:id/results * Get formatted execution results */ router.get('/:sessionId/results', (req: Request, res: Response) => { try { const { sessionId } = req.params; const session = deploymentHandler.getExecutionSession(sessionId); if (!session) { return res.status(404).json({ error: 'Session not found', sessionId, }); } const display = deploymentHandler.getResultsDisplay(sessionId); return res.json({ sessionId, status: session.status, results: session.results, display, summary: { totalCommands: session.commandIds.length, executedCommands: session.results?.length || 0, successCount: session.results?.filter((r: any) => r.success).length || 0, }, }); } catch (error) { logger.error('[DeploymentAPI] Results fetch failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(500).json({ error: 'Failed to retrieve results', }); } }); /** * DELETE /v1/deployments/:id * Cancel deployment execution */ router.delete('/:sessionId', (req: Request, res: Response) => { try { const { sessionId } = req.params; const { reason } = req.body || {}; logger.info('[DeploymentAPI] Cancelling deployment', { sessionId, reason, }); const cancelled = deploymentHandler.cancelExecution(sessionId, reason); if (!cancelled) { return res.status(404).json({ error: 'Session not found', sessionId, }); } return res.json({ cancelled: true, sessionId, message: 'Deployment cancelled', }); } catch (error) { logger.error('[DeploymentAPI] Cancel failed', { error: error instanceof Error ? error.message : String(error), }); return res.status(500).json({ error: 'Failed to cancel deployment', }); } }); export default router;

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/babasida246/ai-mcp-gateway'

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