← back to Patty
app/api/orbit/agents/route.ts
134 lines
import { NextRequest, NextResponse } from 'next/server';
import { verifyAuth } from '@/lib/auth';
import { SISTER_AUTH, SISTER_OK, sisterUnconfigured } from '@/lib/sister-auth';
const AGENTS = [
{ name: 'Phoebe', role: 'Figma QA', port: 9668, health: '/health' },
{ name: 'Sandy', role: 'Scene Assembler', port: 9652, health: '/health' },
{ name: 'Sherman', role: 'Material Librarian', port: 9650, health: '/health' },
{ name: 'Sheldon', role: 'Performance Auditor', port: 9651, health: '/health' },
{ name: 'Shane', role: 'Asset Validator', port: 9653, health: '/health' },
{ name: 'Graphic', role: 'Chart Generator', port: 9846, health: '/health' },
{ name: 'Pulse', role: 'News Intelligence', port: 9845, health: '/health' },
];
async function checkAgent(agent: typeof AGENTS[0]): Promise<{ name: string; role: string; port: number; status: string; uptime?: number }> {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
const res = await fetch(`http://127.0.0.1:${agent.port}${agent.health}`, {
signal: controller.signal,
headers: { 'Authorization': SISTER_AUTH },
});
clearTimeout(timeout);
if (res.ok) {
const data = await res.json().catch(() => ({}));
return { name: agent.name, role: agent.role, port: agent.port, status: 'online', uptime: data.uptime };
}
return { name: agent.name, role: agent.role, port: agent.port, status: 'error' };
} catch {
return { name: agent.name, role: agent.role, port: agent.port, status: 'offline' };
}
}
export async function GET(request: NextRequest) {
const user = verifyAuth(request);
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
if (!SISTER_OK) return sisterUnconfigured();
const results = await Promise.all(AGENTS.map(checkAgent));
const online = results.filter(r => r.status === 'online').length;
return NextResponse.json({
agents: results,
summary: { total: AGENTS.length, online, offline: AGENTS.length - online },
});
}
export async function POST(request: NextRequest) {
const user = verifyAuth(request);
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
if (!SISTER_OK) return sisterUnconfigured();
try {
const body = await request.json();
const { action, agent_name } = body as { action: string; agent_name?: string };
if (action === 'screenshot' || action === 'phoebe/screenshot') {
const res = await fetch('http://127.0.0.1:9668/api/screenshot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': SISTER_AUTH,
},
body: JSON.stringify({ url: 'http://45.61.58.125:7460', viewport: { width: 1920, height: 1080 } }),
});
if (res.ok) {
const data = await res.json();
return NextResponse.json({ success: true, action: 'screenshot', data });
}
return NextResponse.json({ error: 'Screenshot failed' }, { status: 502 });
}
if (action === 'gif' || action === 'phoebe/gif') {
const res = await fetch('http://127.0.0.1:9668/api/record-gif', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': SISTER_AUTH,
},
body: JSON.stringify({ url: 'http://45.61.58.125:7460', frames: 15, delay: 500 }),
});
if (res.ok) {
const data = await res.json();
return NextResponse.json({ success: true, action: 'gif', data });
}
return NextResponse.json({ error: 'GIF recording failed' }, { status: 502 });
}
if (action === 'perf' || action === 'sheldon/audit') {
const res = await fetch('http://127.0.0.1:9651/api/perf/audit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': SISTER_AUTH,
},
});
if (res.ok) {
const data = await res.json();
return NextResponse.json({ success: true, action: 'perf_audit', data });
}
return NextResponse.json({ error: 'Performance audit failed' }, { status: 502 });
}
if (action === 'chart/signatures') {
const { petition_id } = body;
if (!petition_id) return NextResponse.json({ error: 'petition_id required' }, { status: 400 });
const res = await fetch(`http://127.0.0.1:9846/api/chart/signature-progress?petition_id=${petition_id}`, {
headers: { 'Authorization': SISTER_AUTH },
});
if (res.ok) {
const svg = await res.text();
return new NextResponse(svg, { headers: { 'Content-Type': 'image/svg+xml' } });
}
return NextResponse.json({ error: 'Chart generation failed' }, { status: 502 });
}
if (action === 'chart/sentiment') {
const res = await fetch('http://127.0.0.1:9846/api/chart/sentiment-map', {
headers: { 'Authorization': SISTER_AUTH },
});
if (res.ok) {
const svg = await res.text();
return new NextResponse(svg, { headers: { 'Content-Type': 'image/svg+xml' } });
}
return NextResponse.json({ error: 'Sentiment map failed' }, { status: 502 });
}
return NextResponse.json({ error: `Unknown action: ${action}` }, { status: 400 });
} catch (err) {
console.error('[api/orbit/agents] Error:', (err as Error).message);
return NextResponse.json({ error: 'Agent action failed' }, { status: 500 });
}
}