Initial public ModelForge release
This commit is contained in:
commit
7082ab955a
490 files changed
+104252
No files matched your search
@@ -0,0 +1,976 @@
|
||||
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>;
|
||||
}
|
||||
Reference in new issue
Block a user