|
| 1 | +/** |
| 2 | + * 执行调度器 |
| 3 | + * |
| 4 | + * 职责: |
| 5 | + * - 确定可执行的任务(依赖已满足) |
| 6 | + * - 支持顺序和并行执行 |
| 7 | + * - 处理任务失败和依赖传播 |
| 8 | + */ |
| 9 | + |
| 10 | +import { logger } from '@utils/Logger' |
| 11 | +import type { |
| 12 | + TaskPlan, |
| 13 | + OrchestratorTask, |
| 14 | + TaskExecutionResult, |
| 15 | + ExecutionStats, |
| 16 | + OrchestratorConfig, |
| 17 | +} from './types' |
| 18 | +import { DEFAULT_ORCHESTRATOR_CONFIG } from './types' |
| 19 | + |
| 20 | +// ============================================ |
| 21 | +// 执行调度器 |
| 22 | +// ============================================ |
| 23 | + |
| 24 | +export class ExecutionScheduler { |
| 25 | + private config: OrchestratorConfig |
| 26 | + private runningTasks: Set<string> = new Set() |
| 27 | + private abortController: AbortController | null = null |
| 28 | + |
| 29 | + constructor(config: Partial<OrchestratorConfig> = {}) { |
| 30 | + this.config = { ...DEFAULT_ORCHESTRATOR_CONFIG, ...config } |
| 31 | + } |
| 32 | + |
| 33 | + // ============================================ |
| 34 | + // 任务查询 |
| 35 | + // ============================================ |
| 36 | + |
| 37 | + /** |
| 38 | + * 获取所有可执行的任务(依赖已满足且未执行) |
| 39 | + */ |
| 40 | + getExecutableTasks(plan: TaskPlan): OrchestratorTask[] { |
| 41 | + const executable: OrchestratorTask[] = [] |
| 42 | + |
| 43 | + for (const task of plan.tasks) { |
| 44 | + if (task.status !== 'pending') continue |
| 45 | + if (this.runningTasks.has(task.id)) continue |
| 46 | + |
| 47 | + const depStatus = this.checkDependencies(task, plan) |
| 48 | + |
| 49 | + if (depStatus === 'ready') { |
| 50 | + executable.push(task) |
| 51 | + } else if (depStatus === 'blocked') { |
| 52 | + // 依赖失败,自动跳过 |
| 53 | + if (this.config.autoSkipOnDependencyFailure) { |
| 54 | + this.markTaskSkipped(task, 'Dependency failed or skipped') |
| 55 | + } |
| 56 | + } |
| 57 | + // depStatus === 'waiting' -> 继续等待 |
| 58 | + } |
| 59 | + |
| 60 | + return executable |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * 检查任务的依赖状态 |
| 65 | + */ |
| 66 | + private checkDependencies(task: OrchestratorTask, plan: TaskPlan): 'ready' | 'waiting' | 'blocked' { |
| 67 | + if (task.dependencies.length === 0) return 'ready' |
| 68 | + |
| 69 | + let allCompleted = true |
| 70 | + |
| 71 | + for (const depId of task.dependencies) { |
| 72 | + const depTask = plan.tasks.find(t => t.id === depId) |
| 73 | + if (!depTask) { |
| 74 | + logger.agent.warn(`[Scheduler] Dependency not found: ${depId}`) |
| 75 | + continue |
| 76 | + } |
| 77 | + |
| 78 | + if (depTask.status === 'failed' || depTask.status === 'skipped') { |
| 79 | + return 'blocked' |
| 80 | + } |
| 81 | + |
| 82 | + if (depTask.status !== 'completed') { |
| 83 | + allCompleted = false |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + return allCompleted ? 'ready' : 'waiting' |
| 88 | + } |
| 89 | + |
| 90 | + /** |
| 91 | + * 标记任务为跳过 |
| 92 | + */ |
| 93 | + private markTaskSkipped(task: OrchestratorTask, reason: string): void { |
| 94 | + task.status = 'skipped' |
| 95 | + task.error = reason |
| 96 | + logger.agent.info(`[Scheduler] Task skipped: ${task.id} - ${reason}`) |
| 97 | + } |
| 98 | + |
| 99 | + /** |
| 100 | + * 获取下一个待执行任务(顺序模式) |
| 101 | + */ |
| 102 | + getNextTask(plan: TaskPlan): OrchestratorTask | null { |
| 103 | + const executable = this.getExecutableTasks(plan) |
| 104 | + return executable[0] || null |
| 105 | + } |
| 106 | + |
| 107 | + // ============================================ |
| 108 | + // 执行控制 |
| 109 | + // ============================================ |
| 110 | + |
| 111 | + /** |
| 112 | + * 开始执行 |
| 113 | + */ |
| 114 | + start(): void { |
| 115 | + this.abortController = new AbortController() |
| 116 | + this.runningTasks.clear() |
| 117 | + } |
| 118 | + |
| 119 | + /** |
| 120 | + * 停止执行 |
| 121 | + */ |
| 122 | + stop(): void { |
| 123 | + if (this.abortController) { |
| 124 | + this.abortController.abort() |
| 125 | + this.abortController = null |
| 126 | + } |
| 127 | + this.runningTasks.clear() |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * 暂停执行 |
| 132 | + */ |
| 133 | + pause(): void { |
| 134 | + this.abortController?.abort() |
| 135 | + } |
| 136 | + |
| 137 | + /** |
| 138 | + * 恢复执行 |
| 139 | + */ |
| 140 | + resume(): void { |
| 141 | + this.abortController = new AbortController() |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * 检查是否已中止 |
| 146 | + */ |
| 147 | + get isAborted(): boolean { |
| 148 | + return this.abortController?.signal.aborted ?? true |
| 149 | + } |
| 150 | + |
| 151 | + /** |
| 152 | + * 获取中止信号 |
| 153 | + */ |
| 154 | + get abortSignal(): AbortSignal | undefined { |
| 155 | + return this.abortController?.signal |
| 156 | + } |
| 157 | + |
| 158 | + // ============================================ |
| 159 | + // 任务状态管理 |
| 160 | + // ============================================ |
| 161 | + |
| 162 | + /** |
| 163 | + * 标记任务开始执行 |
| 164 | + */ |
| 165 | + markTaskRunning(task: OrchestratorTask): void { |
| 166 | + task.status = 'running' |
| 167 | + task.startedAt = Date.now() |
| 168 | + this.runningTasks.add(task.id) |
| 169 | + } |
| 170 | + |
| 171 | + /** |
| 172 | + * 标记任务完成 |
| 173 | + */ |
| 174 | + markTaskCompleted(task: OrchestratorTask, output: string): TaskExecutionResult { |
| 175 | + const duration = Date.now() - (task.startedAt || Date.now()) |
| 176 | + task.status = 'completed' |
| 177 | + task.output = output |
| 178 | + task.completedAt = Date.now() |
| 179 | + this.runningTasks.delete(task.id) |
| 180 | + |
| 181 | + return { taskId: task.id, success: true, output, duration } |
| 182 | + } |
| 183 | + |
| 184 | + /** |
| 185 | + * 标记任务失败 |
| 186 | + */ |
| 187 | + markTaskFailed(task: OrchestratorTask, error: string): TaskExecutionResult { |
| 188 | + const duration = Date.now() - (task.startedAt || Date.now()) |
| 189 | + task.status = 'failed' |
| 190 | + task.error = error |
| 191 | + task.completedAt = Date.now() |
| 192 | + this.runningTasks.delete(task.id) |
| 193 | + |
| 194 | + return { taskId: task.id, success: false, output: '', error, duration } |
| 195 | + } |
| 196 | + |
| 197 | + // ============================================ |
| 198 | + // 进度统计 |
| 199 | + // ============================================ |
| 200 | + |
| 201 | + /** |
| 202 | + * 检查计划是否完成 |
| 203 | + */ |
| 204 | + isComplete(plan: TaskPlan): boolean { |
| 205 | + return plan.tasks.every(t => |
| 206 | + t.status === 'completed' || t.status === 'failed' || t.status === 'skipped' |
| 207 | + ) |
| 208 | + } |
| 209 | + |
| 210 | + /** |
| 211 | + * 检查是否有任务正在运行 |
| 212 | + */ |
| 213 | + hasRunningTasks(): boolean { |
| 214 | + return this.runningTasks.size > 0 |
| 215 | + } |
| 216 | + |
| 217 | + /** |
| 218 | + * 计算执行统计 |
| 219 | + */ |
| 220 | + calculateStats(plan: TaskPlan, startedAt: number): ExecutionStats { |
| 221 | + const tasks = plan.tasks |
| 222 | + return { |
| 223 | + totalTasks: tasks.length, |
| 224 | + completedTasks: tasks.filter(t => t.status === 'completed').length, |
| 225 | + failedTasks: tasks.filter(t => t.status === 'failed').length, |
| 226 | + skippedTasks: tasks.filter(t => t.status === 'skipped').length, |
| 227 | + totalDuration: Date.now() - startedAt, |
| 228 | + startedAt, |
| 229 | + completedAt: this.isComplete(plan) ? Date.now() : undefined, |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | + // ============================================ |
| 234 | + // 并行执行支持 |
| 235 | + // ============================================ |
| 236 | + |
| 237 | + /** |
| 238 | + * 获取可并行执行的任务批次 |
| 239 | + */ |
| 240 | + getParallelBatch(plan: TaskPlan): OrchestratorTask[] { |
| 241 | + const executable = this.getExecutableTasks(plan) |
| 242 | + // 限制并发数 |
| 243 | + return executable.slice(0, this.config.maxConcurrency) |
| 244 | + } |
| 245 | +} |
0 commit comments