import { NextResponse } from 'next/server' import { apps, namespaces, nodes } from '@/lib/clusterMock' /** * Mock stand-in for the real atlas `POST /api/exec`. Same closed-command-enum shape the real * endpoint must have — anything unmatched rejects here too — but reads fixture data, not a live * snapshot, and has no rate limiting or session state. */ const COMMANDS = ['get nodes', 'get pods ', 'get apps', 'top nodes', 'describe pod ', 'help'] const pad = (s: string, n: number) => s.padEnd(n, ' ') type Line = { kind: 'output' | 'reject'; text: string } export async function POST(req: Request) { const body = await req.json().catch(() => ({})) const raw = typeof body.cmd === 'string' ? body.cmd : '' const trimmed = raw.trim().toLowerCase() const reject = (text: string): Line => { console.warn(`[api/exec] rejected: "${raw}" — ${text}`) return { kind: 'reject', text } } let lines: Line[] if (trimmed === 'help') { lines = [{ kind: 'output', text: COMMANDS.map((c) => ` ${c}`).join('\n') }] } else if (trimmed === 'get nodes') { const head = `${pad('NAME', 14)}${pad('ROLE', 16)}${pad('CPU', 6)}${pad('MEM', 8)}GPU` const rows = nodes.map( (n) => `${pad(n.name, 14)}${pad(n.role, 16)}${pad(String(n.cpu), 6)}${pad(`${n.memoryGi}Gi`, 8)}${n.gpu}`, ) lines = [{ kind: 'output', text: [head, ...rows].join('\n') }] } else if (trimmed === 'get apps') { const head = `${pad('NAME', 24)}${pad('WAVE', 7)}${pad('SYNC', 12)}HEALTH` const rows = apps.map((a) => `${pad(a.name, 24)}${pad(String(a.wave ?? '-'), 7)}${pad(a.sync, 12)}${a.health}`) lines = [{ kind: 'output', text: [head, ...rows].join('\n') }] } else if (trimmed === 'top nodes') { const head = `${pad('NAME', 14)}${pad('CPU%', 8)}MEM%` const rows = nodes.map((n) => `${pad(n.name, 14)}${pad(n.gpu ? '61%' : '18%', 8)}${n.gpu ? '74%' : '42%'}`) lines = [{ kind: 'output', text: [head, ...rows].join('\n') }] } else if (trimmed.startsWith('get pods')) { const ns = trimmed.split(/\s+/)[2] if (!ns) { lines = [reject('get pods requires a namespace. Try: get pods llm-serving')] } else { const known = namespaces.find((n) => n.name === ns) if (!known) { lines = [reject(`unknown namespace "${ns}" — rejected on snapshot membership, no lookup performed`)] } else { const rows = Array.from({ length: Math.min(known.total, 8) }, (_, i) => { const running = i < known.running return `${pad(`${ns}-workload-${i + 1}`, 30)}${pad(running ? '1/1' : '0/1', 6)}${running ? 'Running' : 'Pending'}` }) lines = [{ kind: 'output', text: [`${pad('NAME', 30)}${pad('READY', 6)}STATUS`, ...rows].join('\n') }] } } } else if (trimmed.startsWith('describe pod')) { lines = [{ kind: 'output', text: 'phase: Running\nrestarts: 0\nage: 17h\nnode: worker-1\nready: true' }] } else { lines = [ reject(`"${raw.trim()}" is not in the command set. Input parses to a closed enum; nothing else reaches the cluster. Type help.`), ] } return NextResponse.json({ lines }) }