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
+126
View File
@@ -0,0 +1,126 @@
import type { LucideIcon } from 'lucide-react'
import {
Bot,
Cpu,
Database,
Layers,
Network,
Radio,
Server,
Shield,
TreePine,
Workflow,
} from 'lucide-react'
import type { AgentAnim } from '../types'
export type AgentMeta = {
icon: LucideIcon
accent: string
idleTask: string
activeTask: string
domain: string
}
export const AGENT_META: Record<string, AgentMeta> = {
'etl-guardian': {
icon: Workflow,
accent: '#38bdf8',
domain: 'ETL / CDC',
idleTask: 'Monitoring Airflow DAGs & Kafka connectors',
activeTask: 'Automating ETL layer — CDC sync validation',
},
'lakehouse-ops': {
icon: Layers,
accent: '#818cf8',
domain: 'Lakehouse',
idleTask: 'Watching Spark, Trino & Iceberg catalogs',
activeTask: 'Optimizing lakehouse queries & table health',
},
'data-custodian': {
icon: Database,
accent: '#34d399',
domain: 'Databases',
idleTask: 'Guarding PostgreSQL, MySQL & document stores',
activeTask: 'Running database health & replication checks',
},
'hadoop-ranger': {
icon: TreePine,
accent: '#4ade80',
domain: 'Hadoop',
idleTask: 'Patrolling HDFS capacity & YARN nodes',
activeTask: 'Analyzing HDFS blocks & cluster balance',
},
'infra-sentinel': {
icon: Server,
accent: '#94a3b8',
domain: 'Infrastructure',
idleTask: 'Observing Docker hosts & platform services',
activeTask: 'Correlating infra events across the lab',
},
'mo-commander': {
icon: Shield,
accent: '#60a5fa',
domain: 'Supervision',
idleTask: 'Ingress intel & approval oversight',
activeTask: 'Reviewing agent dispatch & approvals',
},
'bart-commander': {
icon: Radio,
accent: '#2dd4bf',
domain: 'Supervision',
idleTask: 'Egress monitoring & MCP comms relay',
activeTask: 'Tracking outbound agent communications',
},
'network-watcher': {
icon: Network,
accent: '#38bdf8',
domain: 'Network',
idleTask: 'VLAN 20/21 traffic path analysis',
activeTask: 'Mapping data ingress & egress flows',
},
'mcp-coordinator': {
icon: Cpu,
accent: '#c084fc',
domain: 'MCP Hub',
idleTask: 'Routing tool calls between agents',
activeTask: 'Orchestrating MCP tool execution',
},
}
const DEFAULT_META: AgentMeta = {
icon: Bot,
accent: '#94a3b8',
domain: 'Agent',
idleTask: 'Standing by',
activeTask: 'Executing mission',
}
export function getAgentMeta(agentId: string): AgentMeta {
return AGENT_META[agentId] || DEFAULT_META
}
export function agentTaskLabel(agentId: string, anim?: AgentAnim): string {
const meta = getAgentMeta(agentId)
if (!anim || anim.state === 'idle') return meta.idleTask
if (anim.state === 'walk') return `Routing to ${anim.zone || 'target zone'}`
if (anim.state === 'fetch') return meta.activeTask
if (anim.state === 'return') return 'Publishing mission results…'
return meta.activeTask
}
export function pseudoAgentLoad(stats?: { tasks: number; alerts: number }) {
const tasks = stats?.tasks ?? 0
const alerts = stats?.alerts ?? 0
const cpu = Math.min(94, 8 + tasks * 3 + alerts * 5)
const mem = Math.min(88, 12 + tasks * 2 + alerts * 4)
return { cpu, mem }
}
export const DOMAIN_LABELS: Record<string, string> = {
docker: 'Docker Platform',
databases: 'Database Vault',
lakehouse: 'Lakehouse',
etl: 'ETL / Streaming',
hadoop: 'Hadoop Cluster',
gpu: 'GPU / AI',
}
+101
View File
@@ -0,0 +1,101 @@
import type {
PresentationData,
Agent,
Approval,
FeedEntry,
GpuStatus,
StatusData,
TerminalLine,
WorkloadData,
} from '../types'
async function fetchJson<T>(url: string, timeoutMs = 10000): Promise<T | null> {
const ctrl = new AbortController()
const timer = setTimeout(() => ctrl.abort(), timeoutMs)
try {
const r = await fetch(url, { signal: ctrl.signal })
if (!r.ok) return null
return (await r.json()) as T
} catch {
return null
} finally {
clearTimeout(timer)
}
}
export async function fetchAgents() {
const j = await fetchJson<{ agents?: Agent[] }>('/api/agents', 8000)
return j?.agents || []
}
export async function fetchStatus() {
return (await fetchJson<StatusData>('/api/status', 8000)) as StatusData
}
export async function fetchFeed() {
const j = await fetchJson<{ entries?: FeedEntry[] }>('/api/feed', 8000)
return j?.entries || []
}
export async function fetchApprovals() {
const j = await fetchJson<{ approvals?: Approval[] }>('/api/approvals', 8000)
return j?.approvals || []
}
export async function fetchApprovalHistory(status: string = 'pending') {
const j = await fetchJson<{
approvals?: Approval[]
stats?: { pending: number; approved: number; denied: number; total: number }
}>(`/api/approvals?status=${encodeURIComponent(status)}&limit=200`, 8000)
return { approvals: j?.approvals || [], stats: j?.stats }
}
export async function fetchGpu(): Promise<GpuStatus | null> {
return fetchJson<GpuStatus>('/api/gpu', 8000)
}
export async function fetchTerminals(): Promise<Record<string, TerminalLine[]>> {
const j = await fetchJson<{ terminals?: Record<string, TerminalLine[]> }>('/api/terminals', 8000)
return j?.terminals || {}
}
export async function fetchWorkload(): Promise<WorkloadData | null> {
return fetchJson<WorkloadData>('/api/workload?fast=true', 25000)
}
export async function fetchNodeDetail(nodeId: string) {
const j = await fetchJson<Record<string, unknown>>(`/api/nodes/${nodeId}`, 15000)
return j || { error: 'timeout' }
}
export function probeNode(nodeId: string) {
return fetch(`/api/nodes/${nodeId}/probe`, { method: 'POST' })
}
export function askNode(nodeId: string, message: string) {
return fetch(`/api/nodes/${nodeId}/ask`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
})
}
export function sendPrompt(message: string, agentId?: string) {
return fetch('/api/prompt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, agent_id: agentId || undefined }),
})
}
export async function fetchPresentation(): Promise<PresentationData | null> {
return fetchJson<PresentationData>('/api/presentation', 60000)
}
export async function decideApproval(id: string, approved: boolean, decidedBy: string, note: string) {
return fetch(`/api/approvals/${id}/decide`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ approved, decided_by: decidedBy, note }),
})
}
+47
View File
@@ -0,0 +1,47 @@
export const NODE_ALIASES: Record<string, string> = {
'src-postgres': 'db',
'src-mysql': 'db',
'src-mongo': 'db',
'src-cassandra': 'db',
'src-airflow': 'airflow',
'cdc-postgres': 'debezium',
'cdc-mysql': 'debezium',
'cdc-mongo': 'debezium',
'cdc-cassandra': 'debezium',
'stream-kafka': 'kafka',
'stream-schema': 'kafka',
'stream-spark': 'lakehouse',
'lake-iceberg': 'lakehouse',
'lake-s3': 's3',
'query-trino': 'lakehouse',
'query-dbt': 'lakehouse',
'cons-bi': 'docker',
'cons-notebooks': 'lakehouse',
'cons-ml': 'gpu',
}
export const AGENT_NODE: Record<string, string> = {
'infra-sentinel': 'docker',
'data-custodian': 'db',
'lakehouse-ops': 'lakehouse',
'etl-guardian': 'kafka',
'hadoop-ranger': 'hadoop',
'network-watcher': 'network-watcher',
'mcp-coordinator': 'mcp-coordinator',
'mo-commander': 'mo-commander',
'bart-commander': 'bart-commander',
}
export const ZONE_NODE: Record<string, string> = {
docker: 'docker',
db: 'db',
etl: 'kafka',
lakehouse: 'lakehouse',
s3: 's3',
hadoop: 'hadoop',
}
export function wsUrl() {
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
return `${proto}://${window.location.host}/api/ws/ops`
}
+210
View File
@@ -0,0 +1,210 @@
import type { LucideIcon } from 'lucide-react'
import {
Database,
HardDrive,
Layers,
MessageSquare,
Server,
Sparkles,
Workflow,
} from 'lucide-react'
export type InfraApp = {
label: string
url: string
port?: string
}
export type InfraNode = {
id: string
label: string
vm: string
ip: string
zone: string
agentId: string
icon: LucideIcon
accent: string
description: string
ssh: string
apps: InfraApp[]
topoIds: string[]
}
export const INFRA_CATALOG: InfraNode[] = [
{
id: 'db',
label: 'DB Vault',
vm: 'atc-db02',
ip: '10.0.21.51',
zone: 'db',
agentId: 'data-custodian',
icon: Database,
accent: '#34d399',
description: 'PostgreSQL, MySQL, MongoDB, Cassandra, Neo4j — CDC sources',
ssh: 'ssh root@10.0.21.51',
apps: [
{ label: 'Dockhand env 5', url: 'http://10.0.21.45:8082', port: '8082' },
{ label: 'PostgreSQL', url: 'postgresql://10.0.21.51:5432/postgres', port: '5432' },
{ label: 'MongoDB', url: 'mongodb://10.0.21.51:27017/', port: '27017' },
],
topoIds: ['postgresql', 'mysql', 'mongodb', 'cassandra', 'src-postgres', 'src-mysql', 'src-mongo', 'src-cassandra'],
},
{
id: 'airflow',
label: 'Airflow',
vm: 'atc-airflow01',
ip: '10.0.21.55',
zone: 'etl',
agentId: 'etl-guardian',
icon: Workflow,
accent: '#38bdf8',
description: 'DAG orchestration — daily data generation on all sources',
ssh: 'ssh root@10.0.21.55',
apps: [{ label: 'Airflow UI', url: 'http://10.0.21.55:8080', port: '8080' }],
topoIds: ['airflow', 'src-airflow'],
},
{
id: 'kafka',
label: 'Kafka Bus',
vm: 'atc-kafka01',
ip: '10.0.21.36',
zone: 'etl',
agentId: 'etl-guardian',
icon: MessageSquare,
accent: '#4c9aed',
description: 'Event bus — CDC topics & consumer streams',
ssh: 'ssh root@10.0.21.36',
apps: [{ label: 'Kafka UI', url: 'http://10.0.21.36:9000', port: '9000' }],
topoIds: ['kafka', 'stream-kafka'],
},
{
id: 'debezium',
label: 'Debezium CDC',
vm: 'atc-lake01',
ip: '10.0.21.50',
zone: 'lakehouse',
agentId: 'etl-guardian',
icon: Workflow,
accent: '#c77dff',
description: 'Kafka Connect — row-level CDC from source DBs',
ssh: 'ssh root@10.0.21.50',
apps: [{ label: 'Kafka Connect', url: 'http://10.0.21.50:8083', port: '8083' }],
topoIds: ['debezium', 'cdc-postgres', 'cdc-mysql', 'cdc-mongo', 'cdc-cassandra'],
},
{
id: 'lakehouse',
label: 'Lakehouse Hub',
vm: 'atc-lake01',
ip: '10.0.21.50',
zone: 'lakehouse',
agentId: 'lakehouse-ops',
icon: Layers,
accent: '#818cf8',
description: 'Spark, Trino, s3-kafka-consumer, Iceberg catalog',
ssh: 'ssh root@10.0.21.50',
apps: [
{ label: 'Trino', url: 'http://10.0.21.50:8089', port: '8089' },
{ label: 'Spark UI', url: 'http://10.0.21.50:8080', port: '8080' },
{ label: 'Kafka Connect', url: 'http://10.0.21.50:8083', port: '8083' },
],
topoIds: ['spark', 'trino', 'iceberg', 'stream-spark', 'lake-iceberg', 'query-trino', 'cons-notebooks'],
},
{
id: 's3',
label: 'ObjectScale S3',
vm: 'atc-objectscale',
ip: '10.0.20.111',
zone: 's3',
agentId: 'lakehouse-ops',
icon: HardDrive,
accent: '#d4a017',
description: 'Dell ECS S3 — Iceberg landing zone (bucket: data)',
ssh: 'ssh root@10.0.20.111',
apps: [{ label: 'S3 API', url: 'http://10.0.20.111:9020', port: '9020' }],
topoIds: ['s3', 'lake-s3'],
},
{
id: 'docker',
label: 'Docker Rack',
vm: 'atc-docker01',
ip: '10.0.21.45',
zone: 'docker',
agentId: 'infra-sentinel',
icon: Server,
accent: '#94a3b8',
description: 'Homepage, Dockhand, Superset, monitoring stack',
ssh: 'ssh root@10.0.21.45',
apps: [
{ label: 'Homepage', url: 'http://10.0.21.45', port: '80' },
{ label: 'Dockhand', url: 'http://10.0.21.45:8082', port: '8082' },
{ label: 'Superset', url: 'http://10.0.21.45:8088', port: '8088' },
],
topoIds: ['bi', 'cons-bi'],
},
{
id: 'hadoop',
label: 'Hadoop HDFS',
vm: 'atc-hadoop-m01',
ip: '10.0.21.61',
zone: 'hadoop',
agentId: 'hadoop-ranger',
icon: Server,
accent: '#4ade80',
description: '9-node HDFS cluster — parallel storage layer',
ssh: 'ssh root@10.0.21.61',
apps: [{ label: 'NameNode UI', url: 'http://10.0.21.61:9870', port: '9870' }],
topoIds: [],
},
{
id: 'gpu',
label: 'GPU Lab',
vm: 'atc-gpu-dev',
ip: '10.0.20.106',
zone: 'gpu',
agentId: 'infra-sentinel',
icon: Sparkles,
accent: '#3fb950',
description: 'vLLM inference — Llama 3 70B on 4× V100',
ssh: 'ssh root@10.0.20.106',
apps: [
{ label: 'GPU Lab UI', url: 'http://10.0.20.106:9000', port: '9000' },
{ label: 'vLLM API', url: 'http://10.0.20.106:8001/v1', port: '8001' },
],
topoIds: ['llm', 'cons-ml'],
},
{
id: 'command',
label: 'Command Center',
vm: 'MCP · VM304',
ip: '10.0.21.33',
zone: 'command',
agentId: 'mcp-coordinator',
icon: Server,
accent: '#60a5fa',
description: 'This dashboard — agents, approvals, LLM routing',
ssh: 'ssh root@10.0.21.33',
apps: [
{ label: 'Dashboard', url: 'http://10.0.21.33/', port: '80' },
{ label: 'API', url: 'http://10.0.21.33/api', port: '80' },
],
topoIds: [],
},
]
export function resolveInfraNode(nodeId: string | null): InfraNode | null {
if (!nodeId) return null
return (
INFRA_CATALOG.find((n) => n.id === nodeId) ||
INFRA_CATALOG.find((n) => n.topoIds.includes(nodeId)) ||
null
)
}
export async function copyShellCommand(cmd: string) {
try {
await navigator.clipboard.writeText(cmd)
return true
} catch {
return false
}
}
+12
View File
@@ -0,0 +1,12 @@
/** Shared active-tab styles — dark-mode safe (no white docker-light backgrounds). */
export const viewTabActive =
'border-l-[3px] border-l-docker bg-docker/20 text-docker border-docker/40 shadow-docker dark:bg-docker/25'
export const viewTabIdle =
'border border-transparent text-foreground-muted hover:border-border hover:bg-surface-overlay'
export const subTabActive =
'border border-docker/50 bg-docker/20 text-docker shadow-sm dark:bg-docker/25 dark:border-docker/40'
export const subTabIdle =
'border border-transparent text-foreground-muted hover:bg-surface-overlay hover:border-border'
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}