import "dotenv/config"; import { mkdirSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { tmpdir } from "node:os"; import { serve } from "@hono/node-server"; import { Hono } from "hono"; import { cors } from "hono/cors"; import { streamSSE } from "hono/streaming"; import { serveStatic } from "@hono/node-server/serve-static"; import { coderAgent } from "./agents/coder/agent.ts"; import { auditorAgent } from "./agents/auditor/agent.ts"; import { testerAgent } from "./agents/tester/agent.ts"; import { mapFindingToReport } from "./utils/mapFinding.js"; const app = new Hono(); app.use("/api/*", cors()); app.post("/api/run", (c) => { return streamSSE(c, async (stream) => { const body = await c.req.json<{ requirements: string }>(); const requirements = body.requirements?.trim(); if (!requirements) { await stream.writeSSE({ event: "error", data: "Requisitos não fornecidos." }); return; } let eventId = 0; const send = async (event: string, data: string) => { await stream.writeSSE({ id: String(eventId++), event, data }); }; try { // === CODER === await send("log", "[Coder] Gerando smart contract a partir dos requisitos..."); const coderResult = await coderAgent.invoke({ requirements: [requirements] }); await send("log", "[Coder] Contrato gerado com sucesso."); if (coderResult.compilationErrors.length > 0) { await send("log", `[Coder] Erros de compilação restantes: ${coderResult.compilationErrors.length}`); } else { await send("log", "[Coder] Contrato compilado sem erros."); } await send( "coder", JSON.stringify({ contract: coderResult.contract, compilationErrors: coderResult.compilationErrors, reviewSummary: coderResult.reviewSummary, }), ); // === AUDITOR === const outputDir = resolve(tmpdir(), `talp1-${Date.now()}`); mkdirSync(outputDir, { recursive: true }); writeFileSync(resolve(outputDir, "Contract.sol"), coderResult.contract, "utf-8"); writeFileSync(resolve(outputDir, "README.md"), requirements, "utf-8"); await send("log", "[Auditor] Iniciando auditoria de segurança..."); const auditorResult = await auditorAgent.invoke({ repoPath: outputDir }); await send("log", `[Auditor] ${auditorResult.findings.length} vulnerabilidade(s) encontrada(s).`); for (const f of auditorResult.findings) { await send("log", `[Auditor] [${f.severity.toUpperCase()}] ${f.title} — ${f.location}`); } await send( "auditor", JSON.stringify({ findings: auditorResult.findings, }), ); // === TESTER === await send("log", "[Tester] Gerando testes de prova de conceito..."); if (auditorResult.findings.length > 0) { const report = mapFindingToReport( auditorResult.findings[0], coderResult.contract, auditorResult.repoContext // ← now forwarded to tester ); report.customSandboxDir = outputDir; // ← tester runs in real project sandbox const testerResult = await testerAgent.invoke({ report }); await send("log", `[Tester] Execução concluída com status: ${testerResult.status}`); // Garante que o objeto enviado tem exatamente o que o front espera await send( "tester", JSON.stringify({ status: testerResult.status, pocCode: testerResult.pocCode, executionLogs: testerResult.executionLogs, iterations: testerResult.iterations, }), ); } else { await send("log", "[Tester] Nenhuma vulnerabilidade para testar."); await send("tester", JSON.stringify({ status: "skipped", iterations: 0 })); } await send("log", "Pipeline concluído."); await send("done", "ok"); } catch (err) { const message = err instanceof Error ? err.message : String(err); await send("error", message); } }); }); // Serve static frontend files (built React app) app.use("/*", serveStatic({ root: "./frontend/dist" })); const port = Number(process.env.PORT) || 7860; console.log(`Servidor rodando em http://localhost:${port}`); serve({ fetch: app.fetch, port });