Files
ModelForge-Public/frontend/src/App.tsx
T

977 lines
46 KiB
TypeScript

import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import {
Boxes, Command, Cpu, Database, FlaskConical, Menu, Monitor, Moon, Plus, RefreshCw,
ShieldCheck, Sun, X,
} from "lucide-react";
import { CommandCenter } from "./components/CommandCenter";
import { CommandPalette } from "./components/CommandPalette";
import { ModelsRegistry } from "./components/ModelsRegistry";
import { DiscoverWorkspace } from "./components/DiscoverWorkspace";
import { RuntimeWorkspace } from "./components/RuntimeWorkspace";
import { CapabilityWorkspace } from "./components/CapabilityWorkspace";
import { EvaluationWorkspace } from "./components/EvaluationWorkspace";
import { AdvisorWorkspace } from "./components/AdvisorWorkspace";
import { LifecycleWorkspace } from "./components/LifecycleWorkspace";
import { MigrationWorkspace } from "./components/MigrationWorkspace";
import { OperationsWorkspace } from "./components/OperationsWorkspace";
import { RecoveryWorkspace } from "./components/RecoveryWorkspace";
import {
api, ApiError, clearOperatorCredential, setOperatorCredential,
onOperatorSessionInvalidated, operatorCredentialReference,
type PublicHealth, type PublicRelease,
} from "./lib/api";
import { navigation, navigationGroups, type RouteKey } from "./navigation";
import type {
AcceleratorState, CapabilityDeployment, EnrollmentCreated, HardwareState, ModelSummary,
NodeDecommissionPreview, NodeDecommissionRecordCount, NodeDecommissionResult, NodeState,
ObservedValue, ProjectIntegration, ProjectSummary, SchedulerBudget, SystemMetadata,
} from "./types";
type Theme = "dark" | "light" | "system";
const THEME_STORAGE_KEY = "modelforge.theme";
const MOBILE_NAV_MEDIA = "(max-width: 860px)";
function readStoredTheme(): Theme {
// Storage can throw outright in a private window or when site data is blocked, so a failure to
// read a cosmetic preference must never stop the console from rendering.
try {
const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
if (stored === "dark" || stored === "light" || stored === "system") return stored;
} catch { /* fall through to the default */ }
return "system";
}
function currentRoute(): string {
const route = window.location.hash.replace("#/", "");
if (route.startsWith("nodes/")) return route;
return navigation.some((item) => item.key === route) ? route : "overview";
}
function routeKey(route: string): RouteKey {
return route.startsWith("nodes/") ? "nodes" : (route as RouteKey);
}
function LoadingState() {
return <div className="state-card"><span className="state-spinner" />Loading control-plane state…</div>;
}
function EmptyState({ children }: { children: string }) {
return <div className="state-card"><Database size={18} />{children}</div>;
}
export default function App() {
const [route, setRoute] = useState(currentRoute);
const [models, setModels] = useState<ModelSummary[]>([]);
const [projects, setProjects] = useState<ProjectSummary[]>([]);
const [projectIntegrations, setProjectIntegrations] = useState<ProjectIntegration[]>([]);
const [hardware, setHardware] = useState<HardwareState | null>(null);
const [deployments, setDeployments] = useState<CapabilityDeployment[]>([]);
const [budgets, setBudgets] = useState<SchedulerBudget[]>([]);
const [deploymentsLoaded, setDeploymentsLoaded] = useState(false);
const [budgetsLoaded, setBudgetsLoaded] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [system, setSystem] = useState<SystemMetadata | null>(null);
const [lastUpdated, setLastUpdated] = useState<Date | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [pollError, setPollError] = useState<string | null>(null);
const [operatorUnlocked, setOperatorUnlocked] = useState(false);
const [operatorDraft, setOperatorDraft] = useState("");
const [unlocking, setUnlocking] = useState(false);
const [publicHealth, setPublicHealth] = useState<PublicHealth | null>(null);
const [publicRelease, setPublicRelease] = useState<PublicRelease | null>(null);
const [theme, setTheme] = useState<Theme>(readStoredTheme);
const [navOpen, setNavOpen] = useState(false);
const [paletteOpen, setPaletteOpen] = useState(false);
const menuButtonRef = useRef<HTMLButtonElement>(null);
const closeButtonRef = useRef<HTMLButtonElement>(null);
const sidebarRef = useRef<HTMLElement>(null);
const navWasOpen = useRef(false);
const navOpenRef = useRef(false);
const closeFocusTarget = useRef<"menu" | "active-route">("menu");
const loadControlPlane = useCallback(async () => {
setLoading(true);
setError(null);
await Promise.all([api.system(), api.models(), api.projects(), api.projectIntegrations(), api.hardware()])
.then(async ([systemData, modelData, projectData, integrationData, hardwareData]) => {
setSystem(systemData);
setModels(modelData.items);
setProjects(projectData);
setProjectIntegrations(integrationData);
setHardware(hardwareData);
const [deploymentResult, budgetResult] = await Promise.allSettled([
api.capabilityDeployments(), api.scheduler(),
]);
setDeploymentsLoaded(deploymentResult.status === "fulfilled" && Array.isArray(deploymentResult.value));
setBudgetsLoaded(budgetResult.status === "fulfilled" && Array.isArray(budgetResult.value));
if (deploymentResult.status === "fulfilled" && Array.isArray(deploymentResult.value)) setDeployments(deploymentResult.value);
if (budgetResult.status === "fulfilled" && Array.isArray(budgetResult.value)) setBudgets(budgetResult.value);
setLastUpdated(new Date());
})
.catch((cause: unknown) => setError(cause instanceof Error ? cause.message : "Unable to load ModelForge API"))
.finally(() => setLoading(false));
}, []);
useEffect(() => {
const onHashChange = () => setRoute(currentRoute());
window.addEventListener("hashchange", onHashChange);
Promise.allSettled([api.liveness(), api.version()]).then(([healthResult, releaseResult]) => {
if (healthResult.status === "fulfilled") setPublicHealth(healthResult.value);
if (releaseResult.status === "fulfilled") setPublicRelease(releaseResult.value);
});
return () => window.removeEventListener("hashchange", onHashChange);
}, []);
useEffect(() => {
if (!operatorUnlocked) return;
void loadControlPlane();
}, [loadControlPlane, operatorUnlocked]);
useEffect(() => onOperatorSessionInvalidated(() => {
lockOperatorSession();
setError("Operator session is no longer valid; unlock again to continue");
}), []);
useEffect(() => {
if (!operatorUnlocked) return undefined;
const interval = window.setInterval(() => {
api.hardware()
.then((state) => { setHardware(state); setPollError(null); setLastUpdated(new Date()); })
.catch((cause: unknown) => setPollError(cause instanceof Error ? cause.message : "Hardware polling failed"));
}, 5000);
return () => window.clearInterval(interval);
}, [operatorUnlocked]);
useEffect(() => {
const root = document.documentElement;
if (theme === "system") root.removeAttribute("data-theme");
else root.setAttribute("data-theme", theme);
try { window.localStorage.setItem(THEME_STORAGE_KEY, theme); } catch { /* preference is not load-bearing */ }
}, [theme]);
// Escape closes the mobile drawer; a drawer you can open but not close is a trap.
useEffect(() => {
if (!navOpen) return undefined;
const onKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") setNavOpen(false); };
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [navOpen]);
useEffect(() => {
const onShortcut = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
if (document.querySelector('[aria-modal="true"]')) return;
event.preventDefault();
setPaletteOpen((current) => !current);
}
};
window.addEventListener("keydown", onShortcut);
return () => window.removeEventListener("keydown", onShortcut);
}, []);
useEffect(() => {
if (typeof window.matchMedia !== "function") return undefined;
const mobile = window.matchMedia(MOBILE_NAV_MEDIA);
const closeAfterResize = (event: MediaQueryListEvent) => {
if (!event.matches && navOpenRef.current) {
closeFocusTarget.current = "active-route";
setNavOpen(false);
}
};
mobile.addEventListener("change", closeAfterResize);
return () => mobile.removeEventListener("change", closeAfterResize);
}, []);
useEffect(() => {
navOpenRef.current = navOpen;
if (navOpen) {
navWasOpen.current = true;
closeButtonRef.current?.focus();
} else if (navWasOpen.current) {
navWasOpen.current = false;
if (closeFocusTarget.current === "active-route") {
sidebarRef.current?.querySelector<HTMLButtonElement>('button[aria-current="page"]')?.focus();
} else {
menuButtonRef.current?.focus();
}
closeFocusTarget.current = "menu";
}
}, [navOpen]);
const activeRoute = routeKey(route);
const routeInfo = navigation.find((item) => item.key === activeRoute)!;
const detailNode = route.startsWith("nodes/")
? hardware?.nodes.find((node) => node.id === route.slice(6))
: undefined;
const navigate = useCallback((key: string) => {
window.location.hash = `#/${key}`;
setRoute(key);
setNavOpen(false);
}, []);
async function refreshHardware() {
if (refreshing) return;
setRefreshing(true);
setError(null);
try { setHardware(await api.refreshHardware()); }
catch (cause: unknown) { setError(cause instanceof Error ? cause.message : "Hardware refresh failed"); }
finally { setRefreshing(false); }
}
async function refreshDashboard() {
if (refreshing) return;
setRefreshing(true);
setError(null);
try {
const [hardwareResult, deploymentResult, budgetResult] = await Promise.allSettled([
api.hardware(), api.capabilityDeployments(), api.scheduler(),
]);
if (hardwareResult.status === "fulfilled") setHardware(hardwareResult.value);
setDeploymentsLoaded(deploymentResult.status === "fulfilled" && Array.isArray(deploymentResult.value));
setBudgetsLoaded(budgetResult.status === "fulfilled" && Array.isArray(budgetResult.value));
if (deploymentResult.status === "fulfilled" && Array.isArray(deploymentResult.value)) setDeployments(deploymentResult.value);
if (budgetResult.status === "fulfilled" && Array.isArray(budgetResult.value)) setBudgets(budgetResult.value);
const failures = [hardwareResult, deploymentResult, budgetResult].filter((result) => result.status === "rejected");
setPollError(failures.length ? `${failures.length} command-center evidence source${failures.length === 1 ? "" : "s"} unavailable` : null);
setLastUpdated(new Date());
} finally { setRefreshing(false); }
}
function cycleTheme() {
setTheme((current) => (current === "system" ? "dark" : current === "dark" ? "light" : "system"));
}
async function unlockOperatorSession(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
if (!operatorDraft || unlocking) return;
setUnlocking(true);
setError(null);
try {
const verifiedSystem = await api.verifyOperatorCredential(operatorDraft);
setOperatorCredential(operatorDraft);
setOperatorDraft("");
setSystem(verifiedSystem);
setOperatorUnlocked(true);
} catch (cause: unknown) {
clearOperatorCredential();
setError(cause instanceof Error ? cause.message : "Operator authentication failed");
} finally {
setUnlocking(false);
}
}
function lockOperatorSession() {
clearOperatorCredential();
setOperatorDraft("");
setOperatorUnlocked(false);
setModels([]);
setProjects([]);
setProjectIntegrations([]);
setHardware(null);
setDeployments([]);
setBudgets([]);
setDeploymentsLoaded(false);
setBudgetsLoaded(false);
setSystem(null);
setError(null);
setPollError(null);
setLastUpdated(null);
}
const title = detailNode?.display_name ?? routeInfo.label;
const description = detailNode
? `Observed inventory and telemetry reported by this node's agent.`
: routeInfo.description;
if (!operatorUnlocked) {
return <OperatorSessionLock
credential={operatorDraft}
unlocking={unlocking}
error={error}
health={publicHealth}
release={publicRelease}
theme={theme}
onCredential={setOperatorDraft}
onSubmit={unlockOperatorSession}
onTheme={cycleTheme}
/>;
}
return <div className="shell">
<a className="skip-link" href="#workspace-content" onClick={(event) => {
event.preventDefault();
document.getElementById("workspace-content")?.focus();
}}>Skip to workspace</a>
<div className="mobile-bar" inert={navOpen ? true : undefined}>
<button
ref={menuButtonRef}
onClick={() => setNavOpen(true)}
aria-label="Open navigation"
aria-expanded={navOpen}
aria-controls="primary-sidebar"
>
<Menu size={17} />
</button>
<strong>{title}</strong>
<ThemeButton theme={theme} onClick={cycleTheme} />
</div>
{navOpen ? <div className="nav-scrim" aria-hidden="true" onClick={() => setNavOpen(false)} /> : null}
<aside
ref={sidebarRef}
id="primary-sidebar"
className={navOpen ? "sidebar open" : "sidebar"}
role={navOpen ? "dialog" : undefined}
aria-modal={navOpen ? true : undefined}
aria-label={navOpen ? "Primary navigation" : undefined}
>
<div className="brand">
<div className="brand-mark"><Boxes size={18} /></div>
<div className="brand-copy">
<strong>ModelForge</strong>
<span>Operator console</span>
</div>
{navOpen ? (
<button
ref={closeButtonRef}
className="theme-toggle"
onClick={() => setNavOpen(false)}
aria-label="Close navigation"
>
<X size={15} />
</button>
) : null}
</div>
<div className="nav-groups">
{navigationGroups.map((group) => (
<div className="nav-group" key={group}>
<span className="nav-group-label">{group}</span>
<nav aria-label={group}>
{navigation.filter((item) => item.group === group).map((item) => (
<button
key={item.key}
className={activeRoute === item.key ? "active" : ""}
onClick={() => navigate(item.key)}
aria-current={activeRoute === item.key ? "page" : undefined}
>
<item.icon size={16} />
<span className="nav-copy"><strong>{item.label}</strong><small>{item.shortDescription}</small></span>
</button>
))}
</nav>
</div>
))}
</div>
<div className="sidebar-footer">
<span className={`status-dot ${error || pollError ? "error" : ""}`} />
{error || pollError ? "Control plane unavailable" : `Control plane ${system?.version ?? "…"}`}
</div>
</aside>
<main id="workspace-content" tabIndex={-1} inert={navOpen || paletteOpen ? true : undefined}>
<div className="workspace-toolbar" role="toolbar" aria-label="Workspace controls">
<div className="breadcrumbs"><span>ModelForge</span><span aria-hidden="true">/</span><strong>{routeInfo.group}</strong><span aria-hidden="true">/</span><span>{title}</span></div>
<div className="workspace-actions">
<button className="quick-jump" onClick={() => setPaletteOpen(true)}><Command size={14} /><span>Quick jump</span><kbd>Ctrl K</kbd></button>
<button className="session-lock" onClick={lockOperatorSession}><ShieldCheck size={14} /><span>Lock console</span></button>
<ThemeButton theme={theme} onClick={cycleTheme} />
</div>
</div>
<header className="page-header">
<div>
<span className="eyebrow">{routeInfo.group.toUpperCase()}</span>
<h1>{title}</h1>
<p>{description}</p>
</div>
<span className="foundation-badge"><span className={`status-dot ${error || pollError ? "error" : ""}`} />{system?.environment ?? "control plane"} · v{system?.version ?? "…"}</span>
</header>
{error ? (
<div className="error-banner" role="alert">
<strong>Operation failed</strong>
<span>{error}. No operational state is being inferred.</span>
</div>
) : null}
{pollError ? (
<div className="error-banner" role="status">
<strong>Live node updates paused</strong>
<span>{pollError}. Last-good state is retained.</span>
</div>
) : null}
{loading ? <LoadingState /> : null}
{!loading && activeRoute === "overview" ? (
<CommandCenter
system={system}
models={models}
projects={projects}
integrations={projectIntegrations}
hardware={hardware}
deployments={deployments}
budgets={budgets}
deploymentsLoaded={deploymentsLoaded}
budgetsLoaded={budgetsLoaded}
pollError={pollError}
lastUpdated={lastUpdated}
refreshing={refreshing}
onRefresh={refreshDashboard}
onNavigate={navigate}
/>
) : null}
{!loading && activeRoute === "models" ? <ModelsRegistry /> : null}
{!loading && activeRoute === "discover" ? <DiscoverWorkspace /> : null}
{!loading && activeRoute === "runtime" ? <RuntimeWorkspace /> : null}
{!loading && activeRoute === "capabilities" ? <CapabilityWorkspace onError={setError} /> : null}
{!loading && activeRoute === "evaluation" ? <EvaluationWorkspace /> : null}
{!loading && activeRoute === "recommendations" ? <AdvisorWorkspace /> : null}
{!loading && activeRoute === "lifecycle" ? <LifecycleWorkspace /> : null}
{!loading && activeRoute === "migrations" ? <MigrationWorkspace /> : null}
{!loading && activeRoute === "operations" ? <OperationsWorkspace /> : null}
{!loading && activeRoute === "recovery" ? <RecoveryWorkspace /> : null}
{!loading && activeRoute === "projects" ? <Projects projects={projects} integrations={projectIntegrations} /> : null}
{!loading && activeRoute === "nodes" && !route.startsWith("nodes/") ? (
<Nodes
hardware={hardware}
refreshing={refreshing}
onRefresh={refreshHardware}
onSelect={(id) => navigate(`nodes/${id}`)}
onError={setError}
/>
) : null}
{!loading && route.startsWith("nodes/") ? (
detailNode ? (
<NodeDetail
node={detailNode}
onBack={() => navigate("nodes")}
onChanged={async () => setHardware(await api.hardware())}
onError={setError}
/>
) : <EmptyState>Compute node not found.</EmptyState>
) : null}
{!loading && !routeInfo.implemented ? <FutureRoute title={routeInfo.label} /> : null}
</main>
<CommandPalette open={paletteOpen} navigation={navigation} onClose={() => setPaletteOpen(false)} onNavigate={navigate} />
</div>;
}
function OperatorSessionLock({
credential, unlocking, error, health, release, theme,
onCredential, onSubmit, onTheme,
}: {
credential: string;
unlocking: boolean;
error: string | null;
health: PublicHealth | null;
release: PublicRelease | null;
theme: Theme;
onCredential: (value: string) => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
onTheme: () => void;
}) {
return <main className="operator-lock-shell">
<section className="operator-lock-card" aria-labelledby="operator-lock-title">
<div className="operator-lock-heading">
<div className="brand-mark"><Boxes size={20} /></div>
<div><span className="eyebrow">MODELFORGE CONTROL PLANE</span><h1 id="operator-lock-title">Operator session locked</h1></div>
<ThemeButton theme={theme} onClick={onTheme} />
</div>
<p>Unlock once to load governed models, projects, hardware and operational evidence. The credential stays only in this page's memory and is cleared when you lock or reload the console.</p>
<div className="operator-public-status" aria-label="Public platform status">
<div><span>API liveness</span><strong>{health?.status ?? "checking"}</strong></div>
<div><span>Release</span><strong>{release?.version ?? "checking"}</strong></div>
</div>
<form onSubmit={onSubmit}>
<label htmlFor="operator-session-key">Operator API key</label>
<input
id="operator-session-key"
name="operator-api-key"
type="password"
autoComplete="off"
value={credential}
onChange={(event) => onCredential(event.target.value)}
autoFocus
/>
<button type="submit" disabled={!credential || unlocking}>
<ShieldCheck size={16} />{unlocking ? "Verifying…" : "Unlock operator session"}
</button>
</form>
{error ? <div className="error-banner" role="alert"><strong>Unlock denied</strong><span>{error}</span></div> : null}
<small>No credential is written to localStorage, sessionStorage, a URL, telemetry or logs.</small>
</section>
</main>;
}
function ThemeButton({ theme, onClick }: { theme: Theme; onClick: () => void }) {
const Icon = theme === "dark" ? Moon : theme === "light" ? Sun : Monitor;
const next = theme === "system" ? "dark" : theme === "dark" ? "light" : "system";
return (
<button className="theme-toggle" onClick={onClick} title={`Theme: ${theme}. Switch to ${next}.`} aria-label={`Theme: ${theme}. Switch to ${next}.`}>
<Icon size={15} />
</button>
);
}
function Nodes({ hardware, refreshing, onRefresh, onSelect, onError }: {
hardware: HardwareState | null; refreshing: boolean; onRefresh: () => void;
onSelect: (id: string) => void; onError: (message: string | null) => void;
}) {
const [enrolling, setEnrolling] = useState(false);
return <div className="hardware-stack">
<div className="hardware-toolbar">
<span>{hardware?.overview.node_count ?? 0} registered nodes · {hardware?.overview.accelerator_count ?? 0} accelerators</span>
<div className="toolbar-actions">
<button onClick={() => setEnrolling((value) => !value)}><Plus size={15} />Enroll node</button>
<button onClick={onRefresh} disabled={refreshing}>
<RefreshCw size={15} className={refreshing ? "spin" : ""} />{refreshing ? "Refreshing…" : "Refresh local"}
</button>
</div>
</div>
{enrolling ? <EnrollmentPanel onClose={() => setEnrolling(false)} onError={onError} /> : null}
{!hardware?.nodes.length ? (
<EmptyState>No compute nodes are registered. Create an enrollment token to connect one.</EmptyState>
) : (
<section className="node-grid">
{hardware.nodes.map((node) => {
const gpu = node.accelerators[0];
return (
<button className="node-card panel" key={node.id} onClick={() => onSelect(node.id)}>
<div className="panel-header">
<div>
<span className="eyebrow">{node.observation_source.replace("_", " ")}</span>
<h2>{node.display_name}</h2>
</div>
<Liveness state={node.liveness} />
</div>
<div className="property-grid compact">
<Property label="Hostname" value={node.hostname} />
<Property label="Role" value={node.role ?? "unassigned"} />
<Property label="Agent" value={node.agent_version ?? "not reported"} />
<Property label="Last heartbeat" value={formatAge(node.last_heartbeat_at)} />
<Property label="Inventory age" value={formatSeconds(node.inventory_age_seconds)} />
<Property label="CPU / RAM" value={`${formatObserved(node.cpu_model)} · ${formatObservedBytes(node.total_ram_bytes)}`} />
</div>
<div className="node-card-footer">
<span>{gpu ? `${gpu.name} · ${formatObservedBytes(gpu.total_vram_bytes)}` : "No accelerator"}</span>
<span>{eligibility(node).join(" · ") || "not scheduler eligible"}</span>
</div>
</button>
);
})}
</section>
)}
</div>;
}
function EnrollmentPanel({ onClose, onError }: { onClose: () => void; onError: (message: string | null) => void }) {
const sessionCredential = operatorCredentialReference();
const [adminToken, setAdminToken] = useState(sessionCredential);
const [displayName, setDisplayName] = useState("");
const [role, setRole] = useState("worker");
const [labelsJson, setLabelsJson] = useState("{}");
const [productionEligible, setProductionEligible] = useState(false);
const [labEligible, setLabEligible] = useState(true);
const [benchmarkEligible, setBenchmarkEligible] = useState(false);
const [result, setResult] = useState<EnrollmentCreated | null>(null);
const [submitting, setSubmitting] = useState(false);
async function submit(event: FormEvent) {
event.preventDefault();
setSubmitting(true);
onError(null);
try {
const labels = JSON.parse(labelsJson) as Record<string, string | boolean>;
if (!labels || Array.isArray(labels) || typeof labels !== "object") throw new Error("Labels must be a JSON object");
if (Object.values(labels).some((value) => typeof value !== "string" && typeof value !== "boolean")) {
throw new Error("Label values must be strings or booleans");
}
setResult(await api.createEnrollment({
expires_in_seconds: 900,
display_name: displayName || undefined,
role: role || undefined,
labels,
production_eligible: productionEligible,
lab_eligible: labEligible,
benchmark_eligible: benchmarkEligible,
}, adminToken));
setAdminToken("");
} catch (cause: unknown) {
onError(cause instanceof Error ? cause.message : "Enrollment creation failed");
} finally {
setSubmitting(false);
}
}
if (result) {
return <article className="panel enrollment-panel">
<span className="eyebrow">ONE-TIME SECRET</span>
<h2>Enrollment token created</h2>
<p>Copy this now. ModelForge stores only its hash and will never display it again.</p>
<code className="secret-output">{result.enrollment_token}</code>
<pre>{Object.entries(result.setup_environment).map(([key, value]) => `${key}=${value}`).join("\n")}</pre>
<small>
Expires {new Date(result.expires_at).toLocaleString()} and becomes invalid after first use.
Waiting for the first authenticated report; the node list updates automatically.
</small>
<button onClick={onClose}>Done</button>
</article>;
}
return <form className="panel enrollment-panel" onSubmit={submit}>
<span className="eyebrow">SECURE ONBOARDING</span>
<h2>Create one-time enrollment</h2>
<p>The operator credential stays in memory for this request and is never persisted by the UI.</p>
{!sessionCredential ? <label>Operator API key
<input type="password" required value={adminToken} onChange={(event) => setAdminToken(event.target.value)} autoComplete="off" />
</label> : null}
<label>Display name
<input value={displayName} onChange={(event) => setDisplayName(event.target.value)} placeholder="Inference server" />
</label>
<label>Role
<input value={role} onChange={(event) => setRole(event.target.value)} placeholder="worker" />
</label>
<label>Labels (JSON)
<input value={labelsJson} onChange={(event) => setLabelsJson(event.target.value)} />
</label>
<div className="eligibility-options">
<label><input type="checkbox" checked={productionEligible} onChange={(event) => setProductionEligible(event.target.checked)} />Production eligible</label>
<label><input type="checkbox" checked={labEligible} onChange={(event) => setLabEligible(event.target.checked)} />Lab eligible</label>
<label><input type="checkbox" checked={benchmarkEligible} onChange={(event) => setBenchmarkEligible(event.target.checked)} />Benchmark eligible</label>
</div>
<div className="form-actions">
<button type="button" onClick={onClose}>Cancel</button>
<button type="submit" disabled={submitting}>{submitting ? "Creating…" : "Create token"}</button>
</div>
</form>;
}
function NodeDetail({ node, onBack, onChanged, onError }: {
node: NodeState; onBack: () => void; onChanged: () => Promise<void>;
onError: (message: string | null) => void;
}) {
return <div className="hardware-stack">
<div className="hardware-toolbar">
<button onClick={onBack}>← All compute nodes</button>
<Liveness state={node.liveness} />
</div>
{node.last_connection_error ? (
<div className="error-banner"><strong>Agent report warning</strong><span>{node.last_connection_error}</span></div>
) : null}
<article className="panel hardware-node">
<div className="panel-header">
<div><span className="eyebrow">NODE ID {node.id}</span><h2>{node.hostname}</h2></div>
<span className={`status-chip ${node.status}`}>{node.status}</span>
</div>
<div className="property-grid">
<Property label="Source" value={node.observation_source.replaceAll("_", " ")} />
<Property label="Environment" value={node.environment ?? "unknown"} />
<Property label="OS / architecture" value={`${node.os_name ?? "unknown"} / ${node.architecture ?? "unknown"}`} />
<Property label="Fingerprint" value={node.hardware_fingerprint ? `${node.hardware_fingerprint.slice(0, 16)}…` : "unknown"} />
<Property label="Agent health" value={node.agent_health} />
<Property label="Agent / protocol" value={`${node.agent_version ?? "unknown"} / v${node.protocol_version ?? "?"}`} />
<Property label="Heartbeat" value={formatAge(node.last_heartbeat_at)} />
<Property label="Inventory age" value={formatSeconds(node.inventory_age_seconds)} />
<Property label="Telemetry age" value={formatSeconds(node.telemetry_age_seconds)} />
<Property label="Role" value={node.role ?? "unassigned"} />
<Property label="CPU" value={formatObserved(node.cpu_model)} />
<Property label="RAM" value={`${formatObservedBytes(node.available_ram_bytes)} free / ${formatObservedBytes(node.total_ram_bytes)}`} />
</div>
<div className="tags">
{Object.entries(node.labels).map(([key, value]) => <code key={key}>{key}={String(value)}</code>)}
{eligibility(node).map((item) => <code key={item}>{item}</code>)}
</div>
<div className="accelerator-grid">
{node.accelerators.length
? node.accelerators.map((gpu) => <GpuCard key={gpu.id} gpu={gpu} />)
: <EmptyState>No NVIDIA accelerator detected.</EmptyState>}
</div>
<div className="storage-list">
{node.storage.map((disk) => (
<div key={disk.id}>
<strong>{disk.purpose}</strong>
<code>{disk.path}</code>
<span>{formatObservedBytes(disk.free_bytes)} free / {formatObservedBytes(disk.total_bytes)}</span>
</div>
))}
</div>
</article>
<NodeDecommissionPanel node={node} onChanged={onChanged} onError={onError} />
</div>;
}
function NodeDecommissionPanel({ node, onChanged, onError }: {
node: NodeState; onChanged: () => Promise<void>; onError: (message: string | null) => void;
}) {
const sessionCredential = operatorCredentialReference();
const [adminToken, setAdminToken] = useState(sessionCredential);
const [operator, setOperator] = useState("");
const [reason, setReason] = useState("");
const [confirmation, setConfirmation] = useState("");
const [preview, setPreview] = useState<NodeDecommissionPreview | null>(null);
const [result, setResult] = useState<NodeDecommissionResult | null>(null);
const [busy, setBusy] = useState(false);
const [localError, setLocalError] = useState<string | null>(null);
const [idempotencyKey] = useState(() => `node-decommission:${node.id}:${Date.now()}`);
async function loadPreview() {
setBusy(true);
setLocalError(null);
onError(null);
try {
setPreview(await api.previewNodeDecommission(node.id, adminToken));
setResult(null);
} catch (cause: unknown) {
setLocalError(cause instanceof Error ? cause.message : "Decommission preview failed");
} finally {
setBusy(false);
}
}
async function execute() {
if (!preview?.safe) return;
setBusy(true);
setLocalError(null);
onError(null);
try {
const completed = await api.decommissionNode(node.id, {
expected_generation: preview.node_generation,
preview_digest: preview.dependency_digest,
idempotency_key: idempotencyKey,
operator,
reason,
confirmation,
}, adminToken);
setResult(completed);
setAdminToken("");
await onChanged();
} catch (cause: unknown) {
const message = cause instanceof Error ? cause.message : "Node decommission failed";
setLocalError(message);
if (cause instanceof ApiError && cause.code === "decommission_preview_stale") setPreview(null);
} finally {
setBusy(false);
}
}
if (node.decommissioned_at || result) {
return <section className="panel danger-zone decommission-complete" aria-labelledby="decommission-title">
<span className="eyebrow">TERMINAL NODE TOMBSTONE</span>
<h2 id="decommission-title">Node decommissioned</h2>
<p>Old credentials are revoked and this persisted identity cannot automatically enroll, publish inventory, or return online.</p>
<div className="property-grid compact">
<Property label="When" value={new Date(result?.decommissioned_at ?? node.decommissioned_at!).toLocaleString()} />
<Property label="Operator" value={(node.decommissioned_by ?? operator) || "recorded in audit"} />
<Property label="Operation" value={result?.operation_id ?? "retained in authoritative history"} />
<Property label="Reason" value={(node.decommission_reason ?? reason) || "retained in audit"} />
</div>
</section>;
}
return <section className="panel danger-zone" aria-labelledby="decommission-title">
<span className="eyebrow">DANGER ZONE · AUDITED LIFECYCLE</span>
<h2 id="decommission-title">Decommission this node</h2>
<p>This is not a delete. Preview first; execution fails closed if work, residency, leases, approvals, migrations, enrollment, or production safety still depends on the node.</p>
{localError ? <div className="error-banner" role="alert"><strong>Decommission refused</strong><span>{localError}</span></div> : null}
<div className="decommission-form">
{!sessionCredential ? <label>Operator API key<input type="password" autoComplete="off" value={adminToken} onChange={(event) => setAdminToken(event.target.value)} /></label> : null}
<label>Operator identity<input value={operator} onChange={(event) => setOperator(event.target.value)} placeholder="name or change owner" /></label>
<label className="wide">Reason<textarea value={reason} onChange={(event) => setReason(event.target.value)} placeholder="Why this node is permanently leaving service" /></label>
<button onClick={loadPreview} disabled={busy || !adminToken}>{busy ? "Checking…" : "Preview decommission"}</button>
</div>
{preview ? <div className="decommission-preview" aria-live="polite">
<div className={`decommission-verdict ${preview.safe ? "safe" : "blocked"}`}>
<strong>{preview.safe ? "Safe to decommission" : `${preview.blockers.length} safety blocker${preview.blockers.length === 1 ? "" : "s"}`}</strong>
<span>Generation {preview.node_generation} · dependency digest {preview.dependency_digest.slice(0, 12)}…</span>
</div>
{preview.blockers.length ? <div className="decommission-blockers">
<h3>Blocking dependencies</h3>
{preview.blockers.map((blocker) => <article key={`${blocker.code}-${blocker.record_type}`}>
<strong>{blocker.code.replaceAll("_", " ")}</strong>
<span>{blocker.message}</span>
<code>{blocker.record_type} · {blocker.count}</code>
</article>)}
</div> : null}
<div className="decommission-columns">
<DecommissionCounts title="Current state affected" rows={preview.cleanup} />
<DecommissionCounts title="Historical evidence retained" rows={preview.preserved} />
</div>
{preview.safe ? <div className="decommission-confirmation">
<label>Type <code>{preview.persisted_identity}</code>, <code>{preview.hostname}</code>, or <code>{preview.display_name}</code> to confirm
<input value={confirmation} onChange={(event) => setConfirmation(event.target.value)} autoComplete="off" />
</label>
<button
className="danger-action"
onClick={execute}
disabled={busy || operator.trim().length === 0 || reason.trim().length < 10 || ![preview.persisted_identity, preview.hostname, preview.display_name].includes(confirmation)}
>{busy ? "Decommissioning…" : "Permanently decommission node"}</button>
</div> : null}
</div> : null}
</section>;
}
function DecommissionCounts({ title, rows }: { title: string; rows: NodeDecommissionRecordCount[] }) {
return <div><h3>{title}</h3><dl>{rows.map((row) => <div key={`${row.record_type}-${row.action}`}>
<dt>{row.record_type.replaceAll("_", " ")}</dt><dd>{row.count} · {row.action.replaceAll("_", " ")}</dd>
</div>)}</dl></div>;
}
function Liveness({ state }: { state: NodeState["liveness"] }) {
return <span className={`liveness ${state}`}><i />{state}</span>;
}
function eligibility(node: NodeState): string[] {
return [node.production_eligible && "production", node.lab_eligible && "lab", node.benchmark_eligible && "benchmark"]
.filter((value): value is string => Boolean(value));
}
function formatAge(value?: string | null): string {
if (!value) return "never";
const seconds = Math.max(0, Math.round((Date.now() - new Date(value).getTime()) / 1000));
return `${formatSeconds(seconds)} ago`;
}
function formatSeconds(value?: number | null): string {
if (value == null) return "unknown";
if (value < 60) return `${value}s`;
if (value < 3600) return `${Math.floor(value / 60)}m`;
return `${Math.floor(value / 3600)}h`;
}
function formatObserved<T>(observation?: ObservedValue<T> | null): string {
if (!observation || observation.availability !== "known") {
return observation?.availability?.replace("_", " ") ?? "unknown";
}
return String(observation.value);
}
function formatObservedBytes(observation?: ObservedValue<number> | null): string {
return observation?.availability === "known" && observation.value != null
? `${(observation.value / 1024 ** 3).toFixed(1)} GB`
: formatObserved(observation);
}
function Meter({ value }: { value?: ObservedValue<number> | null }) {
const percent = value?.availability === "known" && value.value != null
? Math.max(0, Math.min(100, value.value))
: null;
return (
<div className="meter" aria-label={percent == null ? formatObserved(value) : `${percent}%`}>
<span style={{ width: percent == null ? "0%" : `${percent}%` }} />
</div>
);
}
function Property({ label, value }: { label: string; value: string }) {
return <div className="property"><span>{label}</span><strong>{value}</strong></div>;
}
function GpuCard({ gpu }: { gpu: AcceleratorState }) {
const compute = gpu.compute_capability_major.availability === "known" && gpu.compute_capability_minor.availability === "known"
? `${gpu.compute_capability_major.value}.${gpu.compute_capability_minor.value}`
: "unknown";
return <div className="gpu-card">
<div className="gpu-title">
<div>
<span className="eyebrow">NVIDIA ACCELERATOR</span>
<h3>{gpu.name}</h3>
<code>{gpu.device_uuid.length > 22 ? `${gpu.device_uuid.slice(0, 18)}…` : gpu.device_uuid}</code>
</div>
<span className={`status-chip ${gpu.status}`}>{gpu.status}</span>
</div>
<div className="gpu-meter-row">
<span>GPU utilization</span>
<strong>
{gpu.telemetry
? formatObserved(gpu.telemetry.gpu_utilization_percent) + (gpu.telemetry.gpu_utilization_percent.availability === "known" ? "%" : "")
: "unknown"}
</strong>
</div>
<Meter value={gpu.telemetry?.gpu_utilization_percent} />
<div className="property-grid compact">
<Property label="VRAM" value={`${formatObservedBytes(gpu.telemetry?.used_vram_bytes)} / ${formatObservedBytes(gpu.total_vram_bytes)}`} />
<Property label="Temperature" value={gpu.telemetry?.temperature_c.availability === "known" ? `${gpu.telemetry.temperature_c.value} °C` : formatObserved(gpu.telemetry?.temperature_c)} />
<Property label="Power" value={gpu.telemetry?.power_draw_w.availability === "known" ? `${gpu.telemetry.power_draw_w.value} W` : formatObserved(gpu.telemetry?.power_draw_w)} />
<Property label="Driver" value={formatObserved(gpu.driver_version)} />
<Property label="Compute" value={compute} />
<Property label="PCI" value={formatObserved(gpu.pci_bus_id)} />
</div>
</div>;
}
function ModelRows({ models }: { models: ModelSummary[] }) {
if (!models.length) return <EmptyState>No candidate metadata has been registered.</EmptyState>;
return <div className="model-list">
{models.map((model) => {
const capabilities = Array.isArray(model.interpretation_metadata.intended_capabilities)
? model.interpretation_metadata.intended_capabilities.map(String)
: [];
return <div className="model-row" key={model.id}>
<div className="model-icon"><Cpu size={17} /></div>
<div className="model-copy">
<strong>{model.display_name}</strong>
<span>{String(model.interpretation_metadata.proposed_role ?? "No local role interpretation")}</span>
<div className="tags">{capabilities.map((capability) => <code key={capability}>{capability}</code>)}</div>
</div>
<div className="model-meta">
<span className="pill candidate">{model.verification_status}</span>
<small>{model.deployment_status.replace("_", " ")}</small>
</div>
</div>;
})}
</div>;
}
function Projects({ projects, integrations }: { projects: ProjectSummary[]; integrations: ProjectIntegration[] }) {
if (!projects.length) return <EmptyState>No project manifests are registered.</EmptyState>;
const activeIntegrations = integrations.filter((item) => item.state.toLowerCase() === "active").length;
const requestVolume = integrations.reduce((sum, item) => sum + item.usage.request_volume, 0);
return <div className="projects-workspace">
<section className="projects-summary" aria-label="Project integration summary">
<div><span>Registered projects</span><strong>{projects.length}</strong><small>Manifest-backed consumers</small></div>
<div><span>Capability bindings</span><strong>{projects.reduce((sum, item) => sum + item.bindings.length, 0)}</strong><small>No concrete model paths</small></div>
<div><span>Active integrations</span><strong>{activeIntegrations}/{integrations.length}</strong><small>{requestVolume.toLocaleString()} observed requests</small></div>
</section>
<section className="projects-list">
{projects.map((project) => {
const active = integrations.filter((item) => item.project_key === project.id);
return <article className="panel project-workspace-card" key={project.id}>
<header className="project-title-row"><div><span className="eyebrow">PROJECT · {project.id}</span><h2>{project.name}</h2><p>{project.description}</p></div><span className="project-count">{project.bindings.length} bindings · {active.length} integrations</span></header>
<div className="project-columns">
<section className="project-bindings" aria-label={`${project.name} capability bindings`}>
<div className="section-label"><span>Declared capability contracts</span><small>Manifest source of truth</small></div>
{project.bindings.map((binding) => <div className="project-binding-row" key={binding.capability}>
<code>{binding.capability}@{binding.contract_version}</code>
<span>{binding.binding.channel} · {binding.binding.priority}{binding.binding.optional ? " · optional" : ""}</span>
</div>)}
</section>
<section className="project-integrations" aria-label={`${project.name} operational integrations`}>
<div className="section-label"><span>Operational integrations</span><small>Credential identity, usage and project fit</small></div>
{active.length ? active.map((item) => <article className="integration-row" key={item.client_id}>
<div className="integration-title"><div><code>{item.capability}</code><small>{item.client_name} · {item.environment}</small></div><span className={`registry-status ${item.state.toLowerCase()}`}>{item.state}</span></div>
<p>{item.purpose}</p>
<div className="integration-metrics"><span><small>Requests</small><strong>{item.usage.request_volume.toLocaleString()}</strong></span><span><small>Success</small><strong>{item.usage.successful_requests.toLocaleString()}</strong></span><span><small>Errors</small><strong>{item.usage.error_count.toLocaleString()}</strong></span><span><small>p95</small><strong>{item.usage.latency_p95_ms == null ? "—" : `${item.usage.latency_p95_ms.toFixed(1)} ms`}</strong></span></div>
<div className="project-fit-row"><span>Engineering <strong>{item.project_fit?.engineering_integration ?? "INCOMPLETE"}</strong></span><span>Fit <strong>{item.project_fit?.recommendation ?? "REQUIRES_MORE_EVIDENCE"}</strong></span><span>Production <strong>{item.project_fit?.production_validation ?? "REQUIRED"}</strong></span></div>
</article>) : <div className="inline-empty"><ShieldCheck size={18} /><div><strong>Planning bindings only</strong><p>No scoped consumer identity has used ModelForge yet.</p></div></div>}
</section>
</div>
</article>;
})}
</section>
</div>;
}
function FutureRoute({ title }: { title: string }) {
return <div className="future-state">
<FlaskConical size={28} />
<span className="eyebrow">NOT AVAILABLE IN THIS RELEASE</span>
<h2>{title} has no console workspace in this release</h2>
<p>The route is reserved in the information architecture. ModelForge will not fabricate data or expose premature controls.</p>
</div>;
}