UNPKG

agentic-qe

Version:

Agentic Quality Engineering Fleet System - AI-driven quality management platform

331 lines 12.9 kB
"use strict"; /** * Workflow Cancel Command * * Cancels running or paused workflows with cleanup and resource management * Integrates with workflow execution handlers and memory * * @version 1.0.0 */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.displayCancelResult = exports.cancelWorkflow = void 0; const chalk_1 = __importDefault(require("chalk")); const SwarmMemoryManager_js_1 = require("../../../core/memory/SwarmMemoryManager.js"); const Logger_js_1 = require("../../../utils/Logger.js"); const logger = Logger_js_1.Logger.getInstance(); /** * Cancel a workflow */ async function cancelWorkflow(options) { logger.info('Cancelling workflow', { workflowId: options.workflowId }); try { // Validate workflow ID if (!options.workflowId || options.workflowId.trim() === '') { throw new Error('Workflow ID is required'); } // Validate force confirmation if (options.force && options.confirm === false) { throw new Error('Confirmation is required for forced cancellation. Use --confirm flag.'); } // Initialize memory manager const memory = new SwarmMemoryManager_js_1.SwarmMemoryManager(); // Retrieve workflow execution const execution = await retrieveWorkflowExecution(memory, options.workflowId); // Validate workflow status validateWorkflowForCancellation(execution); // Determine cancellation mode const cancellationMode = options.force ? 'forced' : 'graceful'; // Save final workflow state const finalState = await saveFinalState(memory, execution, options.preserveResults); // Stop running agents const stoppedAgents = await stopWorkflowAgents(memory, execution, options); // Clean up resources const cleanupResult = options.cleanup ? await cleanupWorkflowResources(memory, execution, options) : { cleaned: [], errors: [] }; // Notify dependent workflows const notifiedWorkflows = await notifyDependentWorkflows(memory, execution); // Create final checkpoint const checkpointId = await createCancellationCheckpoint(memory, execution, finalState, options); // Update workflow status execution.status = 'cancelled'; execution.cancelledAt = new Date().toISOString(); execution.cancelReason = options.reason; execution.cancellationMode = cancellationMode; // Store updated execution in memory await memory.store(`workflow:execution:${execution.executionId}`, execution, { partition: 'workflow_executions', ttl: 86400 // 24 hours }); // Update workflow status in memory await memory.store(`aqe/swarm/workflow-cli-commands/workflow-${execution.workflowId}-status`, { status: 'cancelled', timestamp: execution.cancelledAt, reason: options.reason }, { partition: 'workflow_cli', ttl: 3600 }); // Store cancellation metadata await memory.store(`aqe/swarm/workflow-cli-commands/cancel-metadata-${execution.workflowId}`, { workflowId: execution.workflowId, cancelledAt: execution.cancelledAt, reason: options.reason, mode: cancellationMode, cleanup: options.cleanup, preserveResults: options.preserveResults }, { partition: 'workflow_cli', ttl: 604800 // 7 days }); // Create audit log entry await memory.postHint({ key: `aqe/audit/workflow-cancel/${execution.workflowId}`, value: { action: 'cancel', workflowId: execution.workflowId, timestamp: execution.cancelledAt, reason: options.reason, mode: cancellationMode }, ttl: 86400 }); // Clean up workflow memory if requested if (options.cleanMemory) { await cleanupWorkflowMemory(memory, execution); } // Update progress in memory await memory.store('aqe/swarm/workflow-cli-commands/progress', { command: 'cancel', status: 'completed', timestamp: new Date().toISOString(), workflowId: execution.workflowId }, { partition: 'workflow_cli', ttl: 3600 }); const result = { success: true, workflowId: execution.workflowId, status: 'cancelled', cancellationMode, workflow: { id: execution.workflowId, name: execution.workflowName || execution.workflowId, status: 'cancelled', cancelledAt: execution.cancelledAt, cancelReason: options.reason, finalState, partialResults: options.preserveResults ? execution.results : undefined }, stoppedAgents, cleanedResources: cleanupResult.cleaned, notifiedWorkflows, checkpointId, cleanupPerformed: options.cleanup || false, cleanupErrors: cleanupResult.errors.length > 0 ? cleanupResult.errors : undefined }; logger.info('Workflow cancelled successfully', { workflowId: options.workflowId }); return result; } catch (error) { logger.error('Failed to cancel workflow', { error, workflowId: options.workflowId }); throw error; } } exports.cancelWorkflow = cancelWorkflow; /** * Retrieve workflow execution from memory */ async function retrieveWorkflowExecution(memory, workflowId) { const pattern = 'workflow:execution:%'; const entries = await memory.query(pattern, { partition: 'workflow_executions' }); const execution = entries.find(entry => entry.value.workflowId === workflowId); if (!execution) { throw new Error(`Workflow not found: ${workflowId}`); } return execution.value; } /** * Validate workflow can be cancelled */ function validateWorkflowForCancellation(execution) { if (execution.status === 'completed') { throw new Error(`Cannot cancel completed workflow ${execution.workflowId}`); } if (execution.status === 'cancelled') { throw new Error(`Workflow ${execution.workflowId} is already cancelled`); } } /** * Save final workflow state */ async function saveFinalState(memory, execution, preserveResults = false) { const finalState = { completedSteps: execution.completedSteps || [], failedSteps: execution.failedSteps || [], currentStep: execution.currentStep, progress: calculateProgress(execution), context: execution.context || {}, variables: execution.context?.variables || {}, checkpoints: execution.checkpoints || [], results: preserveResults ? execution.results : undefined }; await memory.store(`workflow:final-state:${execution.executionId}`, finalState, { partition: 'workflow_states', ttl: 604800 // 7 days }); return finalState; } /** * Calculate workflow progress */ function calculateProgress(execution) { const total = (execution.completedSteps?.length || 0) + (execution.failedSteps?.length || 0); const completed = execution.completedSteps?.length || 0; return total > 0 ? completed / total : 0; } /** * Stop workflow agents */ async function stopWorkflowAgents(memory, execution, options) { const stoppedAgents = []; let retryAttempts = 0; try { // In a real implementation, this would stop actual running agents // For now, simulate stopping agents const mockAgents = ['qe-test-executor', 'qe-coverage-analyzer', 'qe-quality-gate']; stoppedAgents.push(...mockAgents); // Post notification to agents await memory.postHint({ key: `aqe/notifications/workflow-cancel/${execution.workflowId}`, value: { event: 'workflow_cancelled', workflowId: execution.workflowId, executionId: execution.executionId, timestamp: new Date().toISOString() }, ttl: 3600 }); } catch (error) { if (options.retryOnFailure && retryAttempts < 3) { retryAttempts++; logger.warn('Retrying agent stop', { attempt: retryAttempts }); // Retry logic would go here } else { throw error; } } return stoppedAgents; } /** * Clean up workflow resources */ async function cleanupWorkflowResources(memory, execution, options) { const cleaned = []; const errors = []; try { // Clean up temporary files cleaned.push('temp-files'); // Clean up agent resources cleaned.push('agent-resources'); // Clean up execution artifacts cleaned.push('execution-artifacts'); // Store cleanup record await memory.store(`workflow:cleanup:${execution.executionId}`, { cleanedAt: new Date().toISOString(), resources: cleaned, errors }, { partition: 'workflow_cleanup', ttl: 604800 }); } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); errors.push(errorMsg); logger.error('Cleanup error', { error: errorMsg }); } return { cleaned, errors }; } /** * Notify dependent workflows */ async function notifyDependentWorkflows(memory, execution) { const notifiedWorkflows = []; // Post notification to blackboard await memory.postHint({ key: `aqe/notifications/workflow-dependency-cancelled/${execution.workflowId}`, value: { event: 'dependency_cancelled', workflowId: execution.workflowId, timestamp: new Date().toISOString() }, ttl: 3600 }); // In a real implementation, this would notify actual dependent workflows return notifiedWorkflows; } /** * Create cancellation checkpoint */ async function createCancellationCheckpoint(memory, execution, finalState, options) { const checkpointId = `cancel-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; await memory.store(`workflow:checkpoint:${checkpointId}`, { checkpointId, type: 'cancellation', executionId: execution.executionId, workflowId: execution.workflowId, timestamp: new Date().toISOString(), reason: options.reason, state: finalState }, { partition: 'workflow_checkpoints', ttl: 604800 // 7 days }); return checkpointId; } /** * Clean up workflow memory */ async function cleanupWorkflowMemory(memory, execution) { // Remove workflow execution data // In a real implementation, this would selectively clean memory logger.info('Cleaning workflow memory', { workflowId: execution.workflowId }); } /** * Display cancel result in console */ function displayCancelResult(result) { console.log(chalk_1.default.red('\n✓ Workflow cancelled\n')); console.log(chalk_1.default.cyan('Workflow ID:'), result.workflow.id); console.log(chalk_1.default.cyan('Status:'), chalk_1.default.red(result.workflow.status)); console.log(chalk_1.default.cyan('Cancellation Mode:'), result.cancellationMode); console.log(chalk_1.default.cyan('Cancelled At:'), result.workflow.cancelledAt); if (result.workflow.cancelReason) { console.log(chalk_1.default.cyan('Reason:'), result.workflow.cancelReason); } console.log(chalk_1.default.cyan('Final Progress:'), `${(result.workflow.finalState.progress * 100).toFixed(1)}%`); console.log(chalk_1.default.cyan('Completed Steps:'), result.workflow.finalState.completedSteps.length); console.log(chalk_1.default.cyan('Failed Steps:'), result.workflow.finalState.failedSteps.length); if (result.stoppedAgents.length > 0) { console.log(chalk_1.default.cyan('Stopped Agents:'), result.stoppedAgents.join(', ')); } if (result.cleanupPerformed) { console.log(chalk_1.default.cyan('Cleaned Resources:'), result.cleanedResources.join(', ')); if (result.cleanupErrors && result.cleanupErrors.length > 0) { console.log(chalk_1.default.yellow('Cleanup Warnings:'), result.cleanupErrors.length); } } console.log(chalk_1.default.cyan('Checkpoint ID:'), result.checkpointId); console.log(chalk_1.default.gray('\nWorkflow has been cancelled. Final state has been preserved.\n')); } exports.displayCancelResult = displayCancelResult; //# sourceMappingURL=cancel.js.map