← 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 });
  }
}