243 lines
8.1 KiB
TypeScript
243 lines
8.1 KiB
TypeScript
/**
|
|
* Agent Definitions Routes — Phase 6
|
|
* Dev Center: Fábrica de Agentes (Design → Assemble → Deploy)
|
|
*/
|
|
|
|
import { Router, Request, Response } from "express";
|
|
import { db } from "../../db/index";
|
|
import { arcadiaAgentDefs, insertAgentDefSchema } from "@shared/schema";
|
|
import { eq, desc, and } from "drizzle-orm";
|
|
import { blackboardService } from "../blackboard/service";
|
|
|
|
const router = Router();
|
|
|
|
// GET /api/agent-defs — listar todos (filtro por status opcional)
|
|
router.get("/", async (req: Request, res: Response) => {
|
|
try {
|
|
const { status } = req.query;
|
|
const tenantId = (req.user as any)?.tenantId?.toString() || null;
|
|
|
|
let query = db.select().from(arcadiaAgentDefs).orderBy(desc(arcadiaAgentDefs.updatedAt));
|
|
|
|
const defs = await query;
|
|
|
|
const filtered = defs.filter(d => {
|
|
const tenantMatch = !tenantId || !d.tenantId || d.tenantId === tenantId;
|
|
const statusMatch = !status || d.status === status;
|
|
return tenantMatch && statusMatch;
|
|
});
|
|
|
|
res.json({ success: true, data: filtered });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/agent-defs/:id
|
|
router.get("/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
res.json({ success: true, data: def });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs — criar
|
|
router.post("/", async (req: Request, res: Response) => {
|
|
try {
|
|
const userId = (req.user as any)?.id || "anonymous";
|
|
const tenantId = (req.user as any)?.tenantId?.toString() || null;
|
|
|
|
const parsed = insertAgentDefSchema.safeParse({ ...req.body, userId, tenantId });
|
|
if (!parsed.success) {
|
|
return res.status(400).json({ success: false, error: parsed.error.message });
|
|
}
|
|
|
|
const [created] = await db.insert(arcadiaAgentDefs).values(parsed.data).returning();
|
|
res.json({ success: true, data: created });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// PATCH /api/agent-defs/:id — atualizar
|
|
router.patch("/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const { name, description, spec } = req.body;
|
|
|
|
const updates: any = { updatedAt: new Date() };
|
|
if (name !== undefined) updates.name = name;
|
|
if (description !== undefined) updates.description = description;
|
|
if (spec !== undefined) updates.spec = spec;
|
|
|
|
const [updated] = await db
|
|
.update(arcadiaAgentDefs)
|
|
.set(updates)
|
|
.where(eq(arcadiaAgentDefs.id, id))
|
|
.returning();
|
|
|
|
if (!updated) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
res.json({ success: true, data: updated });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// DELETE /api/agent-defs/:id
|
|
router.delete("/:id", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
await db.delete(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
res.json({ success: true });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs/:id/assemble — montar via Blackboard
|
|
router.post("/:id/assemble", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const userId = (req.user as any)?.id || "anonymous";
|
|
|
|
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
|
|
const spec = def.spec as any;
|
|
const specContent = spec?.content || JSON.stringify(spec);
|
|
|
|
const task = await blackboardService.createMainTask(
|
|
`Montar agente: ${def.name}`,
|
|
specContent,
|
|
userId,
|
|
{ agentDefId: id, spec, source: "agent-factory" }
|
|
);
|
|
|
|
await blackboardService.createSubtask(
|
|
task.id,
|
|
"Projetar agente",
|
|
`Analisar spec e criar estrutura do agente: ${def.name}\n\n${specContent}`,
|
|
"architect",
|
|
[],
|
|
{ phase: "design", agentDefId: id }
|
|
);
|
|
|
|
const [updated] = await db
|
|
.update(arcadiaAgentDefs)
|
|
.set({ status: "assembling", lastTaskId: task.id, updatedAt: new Date() })
|
|
.where(eq(arcadiaAgentDefs.id, id))
|
|
.returning();
|
|
|
|
res.json({ success: true, data: updated, taskId: task.id });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs/:id/deploy — marcar como deployed
|
|
router.post("/:id/deploy", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
|
|
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
if (def.status !== "ready") {
|
|
return res.status(400).json({ success: false, error: "Agente precisa estar 'ready' para deploy" });
|
|
}
|
|
|
|
const [updated] = await db
|
|
.update(arcadiaAgentDefs)
|
|
.set({ status: "deployed", version: (def.version || 1) + 1, updatedAt: new Date() })
|
|
.where(eq(arcadiaAgentDefs.id, id))
|
|
.returning();
|
|
|
|
res.json({ success: true, data: updated });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs/:id/redraft — volta para draft para remontar
|
|
router.post("/:id/redraft", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
|
|
const [updated] = await db
|
|
.update(arcadiaAgentDefs)
|
|
.set({ status: "draft", updatedAt: new Date() })
|
|
.where(eq(arcadiaAgentDefs.id, id))
|
|
.returning();
|
|
|
|
if (!updated) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
res.json({ success: true, data: updated });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs/:id/run — executar agente via Blackboard
|
|
router.post("/:id/run", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const userId = (req.user as any)?.id || "anonymous";
|
|
|
|
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
|
|
const spec = def.spec as any;
|
|
const specContent = spec?.content || JSON.stringify(spec);
|
|
|
|
const task = await blackboardService.createMainTask(
|
|
`Executar agente: ${def.name}`,
|
|
specContent,
|
|
userId,
|
|
{ agentDefId: id, spec, source: "agent-run" }
|
|
);
|
|
|
|
await blackboardService.createSubtask(
|
|
task.id,
|
|
"Executar agente",
|
|
`Executar o agente conforme spec:\n\n${specContent}`,
|
|
"run-executor",
|
|
[],
|
|
{ phase: "execution", agentDefId: id }
|
|
);
|
|
|
|
res.json({ success: true, taskId: task.id });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/agent-defs/:id/fork — duplicar como novo draft
|
|
router.post("/:id/fork", async (req: Request, res: Response) => {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const userId = (req.user as any)?.id || "anonymous";
|
|
const tenantId = (req.user as any)?.tenantId?.toString() || null;
|
|
|
|
const [def] = await db.select().from(arcadiaAgentDefs).where(eq(arcadiaAgentDefs.id, id));
|
|
if (!def) return res.status(404).json({ success: false, error: "Não encontrado" });
|
|
|
|
const [forked] = await db.insert(arcadiaAgentDefs).values({
|
|
name: `${def.name} (cópia)`,
|
|
description: def.description,
|
|
spec: def.spec as any,
|
|
status: "draft",
|
|
version: 1,
|
|
tenantId,
|
|
userId,
|
|
}).returning();
|
|
|
|
res.json({ success: true, data: forked });
|
|
} catch (error: any) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
export default router;
|