Spaces:
Sleeping
Sleeping
| /** | |
| * useTerminalAutoRepair.ts — S761: estratto da MainWorkspace (era ~60L inline). | |
| * | |
| * Encapsula: | |
| * - terminalBadge state ("error" | "repairing" | null) | |
| * - tutti i refs interni di polling/guard | |
| * - handleTerminalError (fetch buffer backend → sendMessage auto-repair) | |
| * - startPolling (da passare come onSessionId a TerminalPanel) | |
| * - useEffect polling reset dopo isStreaming → false (3s debounce) | |
| * - useEffect cleanup interval al dismount | |
| * | |
| * Props minimale: { sendMessage } — unica dipendenza esterna da MainWorkspace. | |
| * terminalBadge viene RITORNATO al chiamante (usato da WorkspaceTopBar + WorkspaceTabBar). | |
| */ | |
| import { useRef, useState, useEffect, useCallback } from "react"; | |
| import { useChatStore } from "@/store/chatStore"; | |
| interface UseTerminalAutoRepairOptions { | |
| sendMessage: (text: string) => Promise<void>; | |
| } | |
| export function useTerminalAutoRepair({ sendMessage }: UseTerminalAutoRepairOptions) { | |
| // S-FIX2-BADGE: stato visuale auto-repair terminale | |
| const [terminalBadge, setTerminalBadge] = useState<"error" | "repairing" | null>(null); | |
| // S-FIX2: guard + session ref + poll cleanup | |
| const _terminalAutoRepairFired = useRef(false); | |
| const _terminalSidRef = useRef<string>(""); | |
| // S-FIX2-POLL: ref stabile per polling — evita stale closure nel setInterval | |
| const _handleTerminalErrorRef = useRef<((sid: string) => Promise<void>) | null>(null); | |
| const _terminalPollCleanup = useRef<ReturnType<typeof setInterval> | null>(null); | |
| const isStreaming = useChatStore(s => s.isStreaming); | |
| // S-FIX2-POLL: reset auto-repair flag dopo ogni risposta agente (3s debounce) | |
| // → se il terminale ha ancora errori, il prossimo tick del polling li rileva senza aspettare l'utente | |
| useEffect(() => { | |
| if (isStreaming) return; | |
| const t = setTimeout(() => { | |
| _terminalAutoRepairFired.current = false; | |
| setTerminalBadge(null); // S-FIX2-BADGE: repair completato | |
| }, 3_000); | |
| return () => clearTimeout(t); | |
| }, [isStreaming]); | |
| // S-FIX2-POLL: cleanup interval al dismount del componente host | |
| useEffect(() => () => { | |
| if (_terminalPollCleanup.current) clearInterval(_terminalPollCleanup.current); | |
| }, []); | |
| // S-FIX2 — Terminal Error Feedback Loop: agente auto-ripara errori terminale | |
| const handleTerminalError = useCallback(async (sessionId: string) => { | |
| if (_terminalAutoRepairFired.current) return; | |
| const _cs = useChatStore.getState(); | |
| if (_cs.isStreaming) return; | |
| const backendUrl = (import.meta.env.VITE_EXEC_BACKEND_URL ?? "").replace(/\/$/, ""); | |
| if (!backendUrl) return; | |
| try { | |
| const res = await fetch(`${backendUrl}/api/terminal/buffer/${encodeURIComponent(sessionId)}`); | |
| if (!res.ok) return; | |
| const data = await res.json() as { buffer: string; active: boolean }; | |
| if (!data.active || !data.buffer) return; | |
| const errorPatterns = [ | |
| /Error:/i, /Traceback/i, /FAILED/i, | |
| /npm ERR!/i, /SyntaxError/i, /ModuleNotFoundError/i, | |
| /Cannot find module/i, /command not found/i, | |
| ]; | |
| if (!errorPatterns.some(p => p.test(data.buffer))) return; | |
| _terminalAutoRepairFired.current = true; | |
| setTerminalBadge("repairing"); // S-FIX2-BADGE: segnala repair in corso | |
| const snippet = data.buffer.slice(-1500); | |
| void sendMessage( | |
| `🔴 **Errore rilevato nel terminale** (auto-repair S-FIX2)\n\n` + | |
| `\`\`\`\n${snippet}\n\`\`\`\n\n` + | |
| `Analizza l'errore, identifica il file responsabile nel VFS, correggilo e riesegui il comando.` | |
| ); | |
| } catch { /* non-blocking */ } | |
| }, [sendMessage]); | |
| // S-FIX2-POLL: tieni il ref sempre aggiornato all'ultima versione di handleTerminalError | |
| useEffect(() => { | |
| _handleTerminalErrorRef.current = handleTerminalError; | |
| }, [handleTerminalError]); | |
| // startPolling: da passare come onSessionId={startPolling} a TerminalPanel. | |
| // Avvia polling automatico ogni 20s — rileva errori terminale senza input utente. | |
| const startPolling = useCallback((sid: string) => { | |
| _terminalSidRef.current = sid; | |
| // S-FIX2-POLL: resetta interval precedente (es: reconnect terminale) | |
| if (_terminalPollCleanup.current) clearInterval(_terminalPollCleanup.current); | |
| _terminalPollCleanup.current = setInterval(() => { | |
| const _s = _terminalSidRef.current; | |
| if (_s) void _handleTerminalErrorRef.current?.(_s); | |
| }, 20_000); | |
| }, []); | |
| return { terminalBadge, handleTerminalError, startPolling }; | |
| } | |