Final sweep: Crucible fixes, Path Tracer polish, forge progress, tests green.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Align dashboard subtitle default and UpsertAgent tests with fleet label behavior; WebSocket coalesce and PathForge hardening; Crucible expanded ops and visual DV fixes; Vitest 610/610 and full test-suite pass; trim PROBLEMS.md to open items only.
This commit is contained in:
@@ -113,6 +113,8 @@ export default function PathTracerPage() {
|
||||
const [showQR, setShowQR] = useState(false);
|
||||
|
||||
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
const [autoEndCountdown, setAutoEndCountdown] = useState<number | null>(null);
|
||||
const autoEndRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
// Use WebSocket agents; fall back to REST on mount if WebSocket hasn't populated yet.
|
||||
const agents = wsAgents.length > 0 ? wsAgents : restAgents;
|
||||
@@ -121,8 +123,11 @@ export default function PathTracerPage() {
|
||||
api.listAgents().then(setRestAgents).catch(() => {});
|
||||
}, []);
|
||||
|
||||
// Stop polling on unmount.
|
||||
useEffect(() => () => { if (pollRef.current) clearInterval(pollRef.current); }, []);
|
||||
// Stop polling and auto-end timer on unmount.
|
||||
useEffect(() => () => {
|
||||
if (pollRef.current) clearInterval(pollRef.current);
|
||||
if (autoEndRef.current) clearInterval(autoEndRef.current);
|
||||
}, []);
|
||||
|
||||
const toggleAgent = (id: string, offline: boolean) => {
|
||||
if (offline) return;
|
||||
@@ -186,6 +191,8 @@ export default function PathTracerPage() {
|
||||
};
|
||||
|
||||
const handleEndSession = useCallback(async () => {
|
||||
if (autoEndRef.current) { clearInterval(autoEndRef.current); autoEndRef.current = null; }
|
||||
setAutoEndCountdown(null);
|
||||
if (!sessionID) return;
|
||||
try {
|
||||
await api.deleteTrace(sessionID);
|
||||
@@ -201,6 +208,30 @@ export default function PathTracerPage() {
|
||||
setError('');
|
||||
}, [sessionID]);
|
||||
|
||||
// Auto-delete the session 10 seconds after an error, with a visible countdown.
|
||||
useEffect(() => {
|
||||
if (!error || !sessionID) return;
|
||||
if (autoEndRef.current) clearInterval(autoEndRef.current);
|
||||
const COUNTDOWN = 10;
|
||||
setAutoEndCountdown(COUNTDOWN);
|
||||
let remaining = COUNTDOWN;
|
||||
autoEndRef.current = setInterval(() => {
|
||||
remaining -= 1;
|
||||
if (remaining <= 0) {
|
||||
clearInterval(autoEndRef.current!);
|
||||
autoEndRef.current = null;
|
||||
setAutoEndCountdown(null);
|
||||
handleEndSession();
|
||||
} else {
|
||||
setAutoEndCountdown(remaining);
|
||||
}
|
||||
}, 1000);
|
||||
return () => {
|
||||
if (autoEndRef.current) { clearInterval(autoEndRef.current); autoEndRef.current = null; }
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [error, sessionID]);
|
||||
|
||||
const isWindows = (a: Agent) =>
|
||||
!!(a.platform?.toLowerCase().includes('win') || a.platform?.toLowerCase().includes('windows'));
|
||||
|
||||
@@ -222,7 +253,17 @@ export default function PathTracerPage() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{error && <div className="pt-error-banner">⚠ {error}</div>}
|
||||
{error && (
|
||||
<div className="pt-error-banner" role="alert">
|
||||
<strong>⚠ Session Error</strong>
|
||||
<div style={{ marginTop: '0.3rem' }}>{error}</div>
|
||||
{sessionID && autoEndCountdown !== null && (
|
||||
<div style={{ marginTop: '0.3rem', opacity: 0.8 }}>
|
||||
Session will be terminated in {autoEndCountdown}s…
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tracing && !allHopsReady && !error && (
|
||||
<div className="pt-info-banner">
|
||||
@@ -338,7 +379,7 @@ export default function PathTracerPage() {
|
||||
|
||||
{/* Controls */}
|
||||
<div className="pt-actions">
|
||||
{!tracing && (
|
||||
{!tracing && !sessionID && (
|
||||
<button
|
||||
className="pt-btn pt-btn-primary"
|
||||
disabled={selected.length === 0 || loading}
|
||||
@@ -354,13 +395,13 @@ export default function PathTracerPage() {
|
||||
</button>
|
||||
)}
|
||||
|
||||
{tracing && (
|
||||
{(tracing || (!!error && !!sessionID)) && (
|
||||
<button className="pt-btn pt-btn-danger" onClick={handleEndSession}>
|
||||
End Session
|
||||
End Session{autoEndCountdown !== null && ` (${autoEndCountdown}s)`}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!tracing && selected.length > 0 && (
|
||||
{!tracing && !sessionID && selected.length > 0 && (
|
||||
<button className="pt-btn pt-btn-ghost" onClick={() => setSelected([])}>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user