import { useEffect, useState } from "react"; import { fetchAgentStatus, type AgentStatus } from "../api"; import { createHighlighter } from "shiki"; import { Pulse, HardDrives, Shield, Cpu, Clock, CaretDown, CaretRight, CheckCircle, XCircle, Heartbeat, Lightning } from "@phosphor-icons/react"; import { useLocale } from "../hooks/useLocale"; function StatusCard({ title, icon: Icon, children, className = "" }: any) { return (

{title}

{children}
); } function StatusRow({ label, value, sub }: any) { return (
{label}
{value}
{sub &&
{sub}
}
); } function TimeAgo({ date }: { date: string | number }) { if (!date) return -; const d = new Date(date); const now = new Date(); const diff = Math.floor((now.getTime() - d.getTime()) / 1000); let text = ""; if (diff < 60) text = `${diff}s ago`; else if (diff < 3600) text = `${Math.floor(diff / 60)}m ago`; else if (diff < 86400) text = `${Math.floor(diff / 3600)}h ago`; else text = `${Math.floor(diff / 86400)}d ago`; return {text}; } // Module-level cache so switching tabs doesn't re-fetch let _cache: { data: AgentStatus; html: string } | null = null; export function AgentStatusPage() { const [data, setData] = useState(_cache?.data ?? null); const [loading, setLoading] = useState(!_cache); const [html, setHtml] = useState(_cache?.html ?? ""); const [configExpanded, setConfigExpanded] = useState(false); const { t } = useLocale(); useEffect(() => { if (_cache) return; // Already have data, skip fetch fetchAgentStatus() .then(d => { setData(d); setLoading(false); // Highlight config with dual theme createHighlighter({ themes: ['github-dark', 'github-light'], langs: ['json'] }).then(highlighter => { const code = JSON.stringify(d.config, null, 2); const out = highlighter.codeToHtml(code, { lang: 'json', themes: { dark: 'github-dark', light: 'github-light' }, defaultColor: false, }); setHtml(out); _cache = { data: d, html: out }; }); }) .catch(err => { console.error(err); setLoading(false); }); }, []); if (loading) { return (
{/* Header skeleton */}
{/* Cards skeleton */}
{[1, 2, 3].map((i) => (
))}
{/* Config skeleton */}
); } if (!data) { return (
{t("agent.error")}
); } const gw = data.gateway || {}; // Support both flat (gw.runtime) and nested (gw.service.runtime) structures const runtime = gw.runtime || gw.service?.runtime || {}; const gwInfo = gw.gateway || {}; const isGwRunning = runtime.status === "running" || runtime.state === "active" || (runtime.pid && runtime.pid > 0); const hb = data.heartbeat || {}; const checks = hb.checks || {}; return (
{/* Header */}

{t("agent.title")}

v{data.config.version || "0.0.0"} {data.config.update?.channel || "stable"} {t("agent.channel")}
{/* Gateway Card */}
{isGwRunning ? t("agent.running") : t("agent.stopped")} {isGwRunning && gw.gateway?.uptime && {t("agent.uptime")}}
{/* Models Card */}
{t("agent.primary")}
{data.config.agents?.defaults?.model?.primary || "default"}
{/* If we had more model stats they would go here */}
{t("agent.ready")}
{/* Heartbeat Card */}
{hb.lastRun ? ( <>
{t("agent.lastActivity")}
) : (
{t("agent.noHeartbeat")}
)}
{Object.entries(checks).map(([k, v]) => ( : "-"} /> ))}
{/* Config Section */}
{configExpanded && (
Loading..." }} />
)}
); }