AUDIT / src /hooks /useTerminalAutoRepair.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
4.48 kB
/**
* 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 };
}