| |
| |
| |
| |
| |
| import { Hono } from 'hono'; |
| import { z } from 'zod'; |
| import type { Env } from '../types/env'; |
| import type { WorkflowJob } from '../types/workflow'; |
| import { createAuditEvent } from '../utils/audit'; |
|
|
| export const simulateWorkflowRoute = new Hono<{ Bindings: Env }>(); |
|
|
| const SimulateRequestSchema = z.object({ |
| jobId: z.string().min(1), |
| mockData: z.record(z.unknown()).optional(), |
| }); |
|
|
| simulateWorkflowRoute.post('/simulate', async (c) => { |
| const body = await c.req.json().catch(() => null); |
| if (!body) return c.json({ success: false, error: 'Invalid JSON body' }, 400); |
|
|
| const parsed = SimulateRequestSchema.safeParse(body); |
| if (!parsed.success) { |
| return c.json({ success: false, error: 'Validation failed', details: parsed.error.flatten() }, 400); |
| } |
|
|
| const { jobId, mockData } = parsed.data; |
| const raw = await c.env.WFO_CACHE.get(`job:${jobId}`, 'text'); |
| if (!raw) return c.json({ success: false, error: `Job ${jobId} not found` }, 404); |
|
|
| const job = JSON.parse(raw) as WorkflowJob; |
|
|
| if (!job.compiledWorkflow || !job.graph) { |
| return c.json({ |
| success: false, |
| error: 'Workflow not compiled or validated. Run /generate and /validate first.', |
| }, 409); |
| } |
|
|
| if (job.validationReport && !job.validationReport.valid) { |
| return c.json({ |
| success: false, |
| error: 'Cannot simulate an invalid workflow. Fix validation issues first.', |
| validationReport: job.validationReport, |
| }, 409); |
| } |
|
|
| |
| const resp = await fetch(`${c.env.SIMULATOR_URL}/process/simulate`, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| 'X-Internal-Secret': c.env.INTERNAL_API_SECRET, |
| }, |
| body: JSON.stringify({ |
| jobId, |
| workflow: job.compiledWorkflow, |
| graph: job.graph, |
| architecturePlan: job.architecturePlan, |
| mockData: mockData ?? {}, |
| }), |
| signal: AbortSignal.timeout(25000), |
| }).catch((e) => { throw new Error(`Simulator error: ${e instanceof Error ? e.message : 'unknown'}`); }); |
|
|
| if (!resp.ok) { |
| return c.json({ success: false, error: `Simulator returned ${resp.status}` }, 502); |
| } |
|
|
| const result = await resp.json() as { simulationReport: WorkflowJob['simulationReport'] }; |
|
|
| const updated: WorkflowJob = { |
| ...job, |
| state: result.simulationReport?.passed ? 'simulated' : 'validated', |
| simulationReport: result.simulationReport, |
| updatedAt: new Date().toISOString(), |
| auditLog: [ |
| ...job.auditLog, |
| createAuditEvent(jobId, 'simulation.completed', 'system', { |
| passed: result.simulationReport?.passed, |
| readinessScore: result.simulationReport?.readinessScore, |
| }), |
| ], |
| }; |
|
|
| await c.env.WFO_CACHE.put(`job:${jobId}`, JSON.stringify(updated), { expirationTtl: 86400 * 7 }); |
|
|
| return c.json({ success: true, jobId, simulationReport: result.simulationReport }); |
| }); |
|
|