Add Command Center v2: DQ/RAG integration, S3 browser, Jupyter, GPU matrix.

Mirror mo/atc-GPU layout with config/, docs/, scripts/ for Gitea deploy.
This commit is contained in:
mo
2026-06-25 00:28:23 +00:00
parent fb9cc21c9a
commit a11621b21f
110 changed files with 14622 additions and 529 deletions
+296
View File
@@ -0,0 +1,296 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { ActivityFeed } from './components/ActivityFeed'
import { AgentRoster } from './components/AgentRoster'
import { AgentTerminalGrid } from './components/AgentTerminalGrid'
import { ChatPanel } from './components/ChatPanel'
import { CommandDock } from './components/CommandDock'
import { GpuPanel } from './components/GpuPanel'
import { AmbientBackground } from './components/AmbientBackground'
import { LiveClusterMap } from './components/LiveClusterMap'
import { LiveDomainGrid } from './components/LiveDomainGrid'
import { ThemeToggle } from './components/ThemeToggle'
import type { Agent, AgentAnim, Approval, ChatMessage, FeedEntry, GpuStatus, StatusData, TerminalLine, WorkloadData } from './types'
const TABS = ['Overview', 'Terminals', 'Activity', 'Approvals', 'GPU'] as const
type Tab = (typeof TABS)[number]
function wsUrl() {
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
return `${proto}://${window.location.host}/api/ws/ops`
}
function LiveClock() {
const [now, setNow] = useState(new Date())
useEffect(() => {
const t = setInterval(() => setNow(new Date()), 1000)
return () => clearInterval(t)
}, [])
return (
<span className="text-xs font-mono text-[var(--text-faint)] hidden lg:inline tabular-nums">
{now.toLocaleTimeString()}
</span>
)
}
export default function App() {
const [tab, setTab] = useState<Tab>('Overview')
const [agents, setAgents] = useState<Agent[]>([])
const [status, setStatus] = useState<StatusData | null>(null)
const [workload, setWorkload] = useState<WorkloadData | null>(null)
const [gpu, setGpu] = useState<GpuStatus | null>(null)
const [feed, setFeed] = useState<FeedEntry[]>([])
const [approvals, setApprovals] = useState<Approval[]>([])
const [chat, setChat] = useState<ChatMessage[]>([])
const [anims, setAnims] = useState<Record<string, AgentAnim>>({})
const [selectedId, setSelectedId] = useState<string | null>(null)
const [busy, setBusy] = useState(false)
const [terminals, setTerminals] = useState<Record<string, TerminalLine[]>>({})
const [terminalLayout, setTerminalLayout] = useState<'grid' | 'focus'>('grid')
const appendTerminal = useCallback((line: TerminalLine) => {
setTerminals((prev) => {
const cur = prev[line.agent_id] || []
return { ...prev, [line.agent_id]: [...cur, line].slice(-300) }
})
}, [])
const selectedAgent = useMemo(
() => agents.find((a) => a.id === selectedId) || null,
[agents, selectedId],
)
const load = useCallback(async () => {
const [a, s, f, ap, g, t, w] = await Promise.all([
fetch('/api/agents').then((r) => r.json()),
fetch('/api/status').then((r) => r.json()),
fetch('/api/feed').then((r) => r.json()),
fetch('/api/approvals').then((r) => r.json()),
fetch('/api/gpu').then((r) => r.json()).catch(() => null),
fetch('/api/terminals').then((r) => r.json()).catch(() => ({ terminals: {} })),
fetch('/api/workload').then((r) => r.json()).catch(() => null),
])
setAgents(a.agents || [])
setStatus(s)
setGpu(g || s.gpu || null)
setFeed(f.entries || [])
setApprovals(ap.approvals || [])
if (t.terminals) setTerminals(t.terminals)
if (w?.zones) setWorkload(w)
}, [])
useEffect(() => {
load()
const ws = new WebSocket(wsUrl())
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data)
if (msg.type === 'status') {
setStatus(msg.data)
if (msg.data.gpu) setGpu(msg.data.gpu)
}
if (msg.type === 'workload') setWorkload(msg.data)
if (msg.type === 'terminal') appendTerminal(msg.line)
if (msg.type === 'terminal_history' && msg.terminals) setTerminals(msg.terminals)
if (msg.type === 'feed') setFeed((prev) => [msg.entry, ...prev].slice(0, 100))
if (msg.type === 'agent_dispatch') {
setSelectedId(msg.agent_id)
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'walk', zone: msg.zone } }))
}
if (msg.type === 'agent_fetch') {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'fetch', zone: msg.zone } }))
}
if (msg.type === 'agent_return') {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'return', zone: msg.zone } }))
setTimeout(() => {
setAnims((p) => ({ ...p, [msg.agent_id]: { agentId: msg.agent_id, state: 'idle' } }))
}, 1200)
}
if (msg.type === 'prompt_result') {
setChat((c) => [...c, { role: 'agent', text: msg.answer, agent: msg.agent_id, ts: new Date().toISOString() }])
setBusy(false)
load()
}
}
const iv = setInterval(load, 15000)
return () => { ws.close(); clearInterval(iv) }
}, [load, appendTerminal])
const sendPrompt = async (message: string, agentId?: string) => {
setBusy(true)
setChat((c) => [...c, { role: 'user', text: message, ts: new Date().toISOString() }])
if (agentId) setSelectedId(agentId)
await fetch('/api/prompt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, agent_id: agentId || undefined }),
})
}
const decide = async (id: string, approved: boolean) => {
await fetch(`/api/approvals/${id}/decide`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ approved }),
})
load()
}
const allOk = status && Object.values(status.domains).every((d) => d.level === 'ok')
const totalTasks = agents.reduce((n, a) => n + (a.stats?.tasks || 0), 0)
return (
<div className="min-h-screen p-4 md:p-6 max-w-[96rem] mx-auto font-display flex flex-col gap-5 relative">
<AmbientBackground />
<header className="panel rounded-2xl px-5 py-4 flex flex-wrap justify-between items-center gap-4 relative overflow-hidden">
<div className="header-aurora" />
<div className="flex items-center gap-4 relative z-10">
<div className="logo-mark">ATC</div>
<div>
<h1 className="text-2xl md:text-3xl font-bold tracking-tight neon-text" style={{ color: 'var(--accent)' }}>
Command Center
</h1>
<p className="text-xs font-mono text-[var(--text-muted)]">
{agents.length} agents · {totalTasks} missions · autonomous ops
</p>
</div>
</div>
<div className="flex items-center gap-3 flex-wrap relative z-10">
<LiveClock />
{status && (
<span className={`status-pill ${allOk ? 'ok' : 'warn'}`}>
{allOk ? 'All systems operational' : 'Attention required'}
</span>
)}
{gpu?.ok && gpu.active_model && (
<span className="status-pill gpu hidden md:inline-flex">
GPU · {gpu.active_model}
</span>
)}
<ThemeToggle />
</div>
</header>
<AgentRoster
agents={agents}
animations={anims}
selectedId={selectedId}
onSelect={setSelectedId}
onDelegate={(a) => setSelectedId(a.id)}
/>
<section className="panel rounded-2xl p-5">
<div className="flex flex-wrap items-end justify-between gap-3 mb-4">
<div>
<p className="section-eyebrow">Live agent shells</p>
<h2 className="text-lg font-bold" style={{ color: 'var(--accent)' }}>Agent Terminals</h2>
<p className="text-xs text-[var(--text-muted)] mt-1">Volg live hoe elke agent data ophaalt HTTP probes, Dockhand, JMX, vLLM.</p>
</div>
<div className="flex gap-2">
<button type="button" className={`tab-btn text-xs ${terminalLayout === 'grid' ? 'active' : ''}`} onClick={() => setTerminalLayout('grid')}>Grid</button>
<button type="button" className={`tab-btn text-xs ${terminalLayout === 'focus' ? 'active' : ''}`} onClick={() => setTerminalLayout('focus')}>Focus</button>
</div>
</div>
<AgentTerminalGrid
agents={agents}
terminals={terminals}
animations={anims}
selectedId={selectedId}
onSelect={setSelectedId}
layout={terminalLayout}
/>
</section>
<div className="grid grid-cols-1 xl:grid-cols-12 gap-5">
<div className="xl:col-span-8 flex flex-col gap-5">
<LiveClusterMap agents={agents} workload={workload} animations={anims} selectedId={selectedId} />
<nav className="flex gap-2 flex-wrap">
{TABS.map((t) => (
<button key={t} type="button" onClick={() => setTab(t)} className={`tab-btn ${tab === t ? 'active' : ''}`}>
{t}
{t === 'Approvals' && approvals.length > 0 && (
<span className="tab-badge">{approvals.length}</span>
)}
</button>
))}
</nav>
<main className="panel rounded-2xl p-5 min-h-[280px]">
{tab === 'Overview' && (
<div>
<p className="section-eyebrow mb-1">Infrastructure</p>
<h3 className="text-lg font-bold mb-4" style={{ color: 'var(--text)' }}>Live Cluster Workload</h3>
<LiveDomainGrid workload={workload} />
</div>
)}
{tab === 'Terminals' && (
<div>
<p className="section-eyebrow mb-1">Mission trace</p>
<h3 className="text-lg font-bold mb-4" style={{ color: 'var(--text)' }}>
{selectedAgent ? `${selectedAgent.name} — full terminal` : 'Select an agent'}
</h3>
{selectedAgent ? (
<AgentTerminalGrid
agents={agents}
terminals={terminals}
animations={anims}
selectedId={selectedId}
onSelect={setSelectedId}
layout="focus"
/>
) : (
<p className="text-sm text-[var(--text-muted)]">Klik een agent in de roster of stuur een prompt.</p>
)}
</div>
)}
{tab === 'Activity' && (
<div>
<div className="flex items-center justify-between mb-4">
<div>
<p className="section-eyebrow">Event stream</p>
<h3 className="text-lg font-bold" style={{ color: 'var(--text)' }}>Agent Activity</h3>
</div>
{selectedAgent && (
<button type="button" className="btn-secondary text-xs" onClick={() => setSelectedId(null)}>
Clear filter
</button>
)}
</div>
<ActivityFeed feed={feed} agents={agents} filterAgentId={selectedId} />
</div>
)}
{tab === 'Approvals' && (
<div className="space-y-3">
{approvals.length === 0 && <p className="text-[var(--text-muted)] text-sm">Geen pending approvals.</p>}
{approvals.map((a) => {
const agent = agents.find((ag) => ag.id === a.agent_id)
return (
<div key={a.id} className="status-card rounded-xl p-4 flex gap-4" style={{ borderColor: 'var(--accent-secondary)' }}>
{agent && <span className="text-2xl">{agent.icon}</span>}
<div className="flex-1">
<div className="text-sm font-semibold" style={{ color: 'var(--accent-secondary)' }}>{a.action}</div>
<div className="text-xs text-[var(--text-muted)] mt-1">{a.reason}</div>
<div className="text-[10px] font-mono text-[var(--text-faint)] mt-1">via {agent?.name || a.agent_id}</div>
<div className="flex gap-2 mt-3">
<button type="button" onClick={() => decide(a.id, true)} className="btn-secondary text-[var(--status-ok)]">Approve</button>
<button type="button" onClick={() => decide(a.id, false)} className="btn-secondary text-[var(--status-down)]">Deny</button>
</div>
</div>
</div>
)
})}
</div>
)}
{tab === 'GPU' && <GpuPanel gpu={gpu} />}
</main>
</div>
<div className="xl:col-span-4 flex flex-col gap-5">
<GpuPanel gpu={gpu} compact />
<ChatPanel messages={chat} agents={agents} selectedAgent={selectedAgent} busy={busy} />
</div>
</div>
<CommandDock onSubmit={sendPrompt} busy={busy} selectedAgent={selectedAgent} />
</div>
)
}