UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

564 lines (489 loc) 13.8 kB
/** * Automation Pipeline Manager * Handles task execution, scheduling, and monitoring for CMS automation */ import { AutomationTask, AutomationPipeline, AutomationEvent, ExecutionResult, ScheduleConfig, } from "./types.js"; export class AutomationPipelineManager { private pipelines: Map<string, AutomationPipeline> = new Map(); private runningTasks: Map<string, Promise<ExecutionResult>> = new Map(); private eventHandlers: Map<string, ((event: AutomationEvent) => void)[]> = new Map(); private nextTaskId = 1; private nextPipelineId = 1; /** * Create a new automation task */ createTask( taskData: Omit<AutomationTask, "id" | "createdAt" | "status"> ): AutomationTask { const task: AutomationTask = { id: `task_${this.nextTaskId++}`, createdAt: new Date(), status: "pending", ...taskData, }; this.emitEvent({ type: "task_created", taskId: task.id, timestamp: new Date(), data: { task }, }); return task; } /** * Create a new automation pipeline */ createPipeline( pipelineData: Omit<AutomationPipeline, "id" | "createdAt" | "status"> ): AutomationPipeline { const pipeline: AutomationPipeline = { id: `pipeline_${this.nextPipelineId++}`, createdAt: new Date(), status: "pending", ...pipelineData, }; this.pipelines.set(pipeline.id, pipeline); this.emitEvent({ type: "pipeline_created", pipelineId: pipeline.id, timestamp: new Date(), data: { pipeline }, }); return pipeline; } /** * Execute a single automation task */ async executeTask(task: AutomationTask): Promise<ExecutionResult> { const startTime = Date.now(); this.emitEvent({ type: "task_started", taskId: task.id, timestamp: new Date(), data: { task }, }); try { // Simulate task execution based on type const result = await this.performTaskExecution(task); const executionTime = Date.now() - startTime; const finalResult: ExecutionResult = { ...result, taskId: task.id, executionTime, timestamp: new Date(), }; this.emitEvent({ type: result.success ? "task_completed" : "task_failed", taskId: task.id, timestamp: new Date(), data: { result: finalResult }, }); return finalResult; } catch (error) { const executionTime = Date.now() - startTime; const errorResult: ExecutionResult = { taskId: task.id, success: false, error: error instanceof Error ? error.message : "Unknown error", executionTime, timestamp: new Date(), }; this.emitEvent({ type: "task_failed", taskId: task.id, timestamp: new Date(), data: { error: errorResult.error }, }); return errorResult; } } /** * Execute an automation pipeline */ async executePipeline(pipelineId: string): Promise<{ success: boolean; results: ExecutionResult[]; failedTasks: string[]; skippedTasks: string[]; }> { const pipeline = this.pipelines.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } this.emitEvent({ type: "pipeline_started", pipelineId, timestamp: new Date(), data: { pipeline }, }); const results: ExecutionResult[] = []; const failedTasks: string[] = []; const skippedTasks: string[] = []; try { if (pipeline.executionMode === "parallel") { // Execute all tasks in parallel const taskPromises = pipeline.tasks.map((task) => this.executeTask(task) ); const taskResults = await Promise.allSettled(taskPromises); taskResults.forEach((result, index) => { if (result.status === "fulfilled") { results.push(result.value); if (!result.value.success) { failedTasks.push(pipeline.tasks[index].id); } } else { const task = pipeline.tasks[index]; failedTasks.push(task.id); results.push({ taskId: task.id, success: false, error: result.reason?.message || "Task execution failed", executionTime: 0, timestamp: new Date(), }); } }); } else { // Execute tasks sequentially for (const task of pipeline.tasks) { // Check if we should skip this task due to previous failures if ( pipeline.failureStrategy === "stop_on_failure" && failedTasks.length > 0 ) { skippedTasks.push(task.id); continue; } const result = await this.executeTask(task); results.push(result); if (!result.success) { failedTasks.push(task.id); if (pipeline.failureStrategy === "stop_on_failure") { // Skip remaining tasks const remainingTasks = pipeline.tasks.slice( pipeline.tasks.indexOf(task) + 1 ); skippedTasks.push(...remainingTasks.map((t) => t.id)); break; } } } } const success = failedTasks.length === 0; this.emitEvent({ type: success ? "pipeline_completed" : "pipeline_failed", pipelineId, timestamp: new Date(), data: { success, totalTasks: pipeline.tasks.length, failedTasks: failedTasks.length, skippedTasks: skippedTasks.length, }, }); return { success, results, failedTasks, skippedTasks, }; } catch (error) { this.emitEvent({ type: "pipeline_failed", pipelineId, timestamp: new Date(), data: { error: error instanceof Error ? error.message : "Unknown error", }, }); throw error; } } /** * Retry a failed task with exponential backoff */ async retryTask( task: AutomationTask, maxRetries: number = 3, backoffMs: number = 1000 ): Promise<ExecutionResult> { let lastError: string = ""; for (let attempt = 1; attempt <= maxRetries; attempt++) { this.emitEvent({ type: "task_retry", taskId: task.id, timestamp: new Date(), data: { attempt, maxRetries }, }); const result = await this.executeTask(task); if (result.success) { return result; } lastError = result.error || "Unknown error"; if (attempt < maxRetries) { const delay = backoffMs * Math.pow(2, attempt - 1); await new Promise((resolve) => setTimeout(resolve, delay)); } } return { taskId: task.id, success: false, error: `Failed after ${maxRetries} attempts. Last error: ${lastError}`, executionTime: 0, timestamp: new Date(), }; } /** * Schedule a pipeline for execution */ schedulePipeline(pipelineId: string, schedule: ScheduleConfig): void { const pipeline = this.pipelines.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } // In a real implementation, this would integrate with a job scheduler // For now, we'll just emit an event this.emitEvent({ type: "pipeline_scheduled", pipelineId, timestamp: new Date(), data: { schedule }, }); } /** * Add event listener */ addEventListener( eventType: string, handler: (event: AutomationEvent) => void ): void { if (!this.eventHandlers.has(eventType)) { this.eventHandlers.set(eventType, []); } this.eventHandlers.get(eventType)!.push(handler); } /** * Remove event listener */ removeEventListener( eventType: string, handler: (event: AutomationEvent) => void ): void { const handlers = this.eventHandlers.get(eventType); if (handlers) { const index = handlers.indexOf(handler); if (index > -1) { handlers.splice(index, 1); } } } /** * Get pipeline status */ getPipelineStatus(pipelineId: string): AutomationPipeline | undefined { return this.pipelines.get(pipelineId); } /** * Get all pipelines */ getAllPipelines(): AutomationPipeline[] { return Array.from(this.pipelines.values()); } /** * Cancel a running pipeline */ async cancelPipeline(pipelineId: string): Promise<boolean> { const pipeline = this.pipelines.get(pipelineId); if (!pipeline) { return false; } if (pipeline.status === "running") { pipeline.status = "cancelled"; this.emitEvent({ type: "pipeline_cancelled", pipelineId, timestamp: new Date(), data: { pipeline }, }); return true; } return false; } /** * Rollback a pipeline execution */ async rollbackPipeline(pipelineId: string): Promise<{ success: boolean; rollbackResults: ExecutionResult[]; }> { const pipeline = this.pipelines.get(pipelineId); if (!pipeline) { throw new Error(`Pipeline not found: ${pipelineId}`); } this.emitEvent({ type: "pipeline_rollback_started", pipelineId, timestamp: new Date(), data: { pipeline }, }); const rollbackResults: ExecutionResult[] = []; // Execute rollback tasks in reverse order for (const task of [...pipeline.tasks].reverse()) { if (task.rollbackConfiguration) { const rollbackTask: AutomationTask = { ...task, id: `rollback_${task.id}`, type: "rollback", configuration: task.rollbackConfiguration, }; const result = await this.executeTask(rollbackTask); rollbackResults.push(result); } } const success = rollbackResults.every((result) => result.success); this.emitEvent({ type: success ? "pipeline_rollback_completed" : "pipeline_rollback_failed", pipelineId, timestamp: new Date(), data: { success, results: rollbackResults }, }); return { success, rollbackResults, }; } /** * Get system health status */ getSystemHealth(): { status: "healthy" | "warning" | "critical"; activePipelines: number; runningTasks: number; failedTasksLast24h: number; systemLoad: number; } { const activePipelines = Array.from(this.pipelines.values()).filter( (p) => p.status === "running" ).length; const runningTasks = this.runningTasks.size; // Simplified health calculation let status: "healthy" | "warning" | "critical" = "healthy"; if (runningTasks > 10) { status = "warning"; } if (runningTasks > 20) { status = "critical"; } return { status, activePipelines, runningTasks, failedTasksLast24h: 0, // Would track this in a real implementation systemLoad: Math.random() * 100, // Simulated system load }; } /** * Perform the actual task execution based on task type */ private async performTaskExecution( task: AutomationTask ): Promise<Omit<ExecutionResult, "taskId" | "executionTime" | "timestamp">> { // Simulate execution time const executionTime = Math.random() * 2000 + 500; await new Promise((resolve) => setTimeout(resolve, executionTime)); // Simulate success/failure based on task type const successRate = this.getTaskSuccessRate(task.type); const success = Math.random() < successRate; if (success) { return { success: true, message: `${task.type} task completed successfully`, output: this.generateTaskOutput(task), }; } else { return { success: false, error: `${task.type} task failed: Simulated failure`, }; } } /** * Get success rate for different task types */ private getTaskSuccessRate(taskType: string): number { const rates: Record<string, number> = { validation: 0.95, deployment: 0.85, backup: 0.98, migration: 0.8, test: 0.9, cleanup: 0.95, monitoring: 0.99, rollback: 0.85, }; return rates[taskType] || 0.85; } /** * Generate sample output for tasks */ private generateTaskOutput(task: AutomationTask): Record<string, unknown> { const baseOutput = { taskId: task.id, taskType: task.type, environment: task.environment, completedAt: new Date().toISOString(), }; switch (task.type) { case "deployment": return { ...baseOutput, deployedVersion: "1.2.3", deploymentUrl: "https://example.com", healthChecksPassed: true, }; case "backup": return { ...baseOutput, backupLocation: "/backups/cms_backup_" + Date.now(), backupSize: "2.3GB", compressionRatio: 0.65, }; case "test": return { ...baseOutput, testsRun: 142, testsPassed: 140, testsFailed: 2, coverage: 0.94, }; default: return baseOutput; } } /** * Emit an automation event */ private emitEvent(event: AutomationEvent): void { const handlers = this.eventHandlers.get(event.type); if (handlers) { handlers.forEach((handler) => { try { handler(event); } catch (error) { console.error("Error in event handler:", error); } }); } } } // Export a default instance export const automationPipeline = new AutomationPipelineManager();