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:
@@ -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',
|
||||
}
|
||||
@@ -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 }),
|
||||
})
|
||||
}
|
||||
@@ -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`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user