import { useState, useRef, useCallback } from "react"; import type { CoderResult, AuditorResult, TesterResult, AgentState } from "./types"; import { computeDiff, type DiffLine } from "./utils/diff"; import { INITIAL_AGENT_STATES, applyStepEvent, type StepEvent } from "./utils/status"; import { ArtifactPanel } from "./components/ArtifactPanel"; import { ControlPanel } from "./components/ControlPanel"; export type ArtifactTab = "contract" | "findings" | "poc"; export function App() { const [requirements, setRequirements] = useState(""); const [running, setRunning] = useState(false); const [logs, setLogs] = useState([]); const [coder, setCoder] = useState(null); const [auditor, setAuditor] = useState(null); const [tester, setTester] = useState(null); const [agentStates, setAgentStates] = useState(INITIAL_AGENT_STATES); const [selectedTab, setSelectedTab] = useState("contract"); const [contractDiff, setContractDiff] = useState(null); const [pocDiff, setPocDiff] = useState(null); const [diffMode, setDiffMode] = useState>({}); const prevContract = useRef(null); const prevPoc = useRef(null); const appendLog = useCallback((msg: string) => { setLogs((prev) => [...prev, msg]); }, []); const handleRun = async (e: React.FormEvent) => { e.preventDefault(); if (!requirements.trim() || running) return; setRunning(true); setLogs([]); setCoder(null); setAuditor(null); setTester(null); setAgentStates(INITIAL_AGENT_STATES); setContractDiff(null); setPocDiff(null); setSelectedTab("contract"); try { const res = await fetch("/api/run", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ requirements }), }); const reader = res.body?.getReader(); if (!reader) throw new Error("Stream não disponível"); const decoder = new TextDecoder(); let buffer = ""; let currentEvent = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() ?? ""; for (const line of lines) { if (line.startsWith("event:")) { currentEvent = line.slice(6).trim(); } else if (line.startsWith("data:")) { const data = line.slice(5).trim(); switch (currentEvent) { case "log": appendLog(data); break; case "step": { const event: StepEvent = JSON.parse(data); setAgentStates((prev) => applyStepEvent(prev, event)); break; } case "coder": { const result: CoderResult = JSON.parse(data); const hasPrev = prevContract.current !== null; const diff = computeDiff(prevContract.current, result.contract); prevContract.current = result.contract; setContractDiff(diff); setDiffMode((prev) => ({ ...prev, contract: hasPrev })); setCoder(result); setSelectedTab("contract"); setAgentStates((prev) => { const s = prev.map((a) => ({ ...a, steps: a.steps.map((st) => ({ ...st })) })); const a = s.find((x) => x.id === "coder")!; a.status = "done"; a.steps.forEach((st) => { if (st.status !== "error") st.status = "done"; }); return s; }); break; } case "auditor": { const result: AuditorResult = JSON.parse(data); setAuditor(result); if (result.findings.length > 0) setSelectedTab("findings"); setAgentStates((prev) => { const s = prev.map((a) => ({ ...a, steps: a.steps.map((st) => ({ ...st })) })); const a = s.find((x) => x.id === "auditor")!; a.status = "done"; a.steps.forEach((st) => { if (st.status !== "error") st.status = "done"; }); return s; }); break; } case "tester": { const result: TesterResult = JSON.parse(data); if (result.pocCode) { const hasPrev = prevPoc.current !== null; const diff = computeDiff(prevPoc.current, result.pocCode); prevPoc.current = result.pocCode; setPocDiff(diff); setDiffMode((prev) => ({ ...prev, poc: hasPrev })); setSelectedTab("poc"); } setTester(result); setAgentStates((prev) => { const s = prev.map((a) => ({ ...a, steps: a.steps.map((st) => ({ ...st })) })); const a = s.find((x) => x.id === "tester")!; if (result.status === "skipped") { a.status = "skipped"; a.steps.forEach((st) => { st.status = "skipped"; }); } else { a.status = result.status === "success" ? "done" : "error"; a.steps.forEach((st) => { if (st.status !== "error") st.status = "done"; }); } return s; }); break; } case "error": appendLog(`[ERRO] ${data}`); break; } currentEvent = ""; } } } } catch (err) { appendLog(`[ERRO] ${err instanceof Error ? err.message : String(err)}`); } finally { setRunning(false); } }; const toggleDiffMode = (key: string) => setDiffMode((prev) => ({ ...prev, [key]: !prev[key] })); return (
); }