From 4fef9233c8cec9de8e85726ff4dcdacaeceb0f01 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 25 Sep 2026 22:31:31 +0200 Subject: [PATCH] feat(agent-api): GET /api/agent/projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the route to fetch all projects across all clients the owner has. No status filtering—agents drafting a plan need to see paused/completed projects. Co-Authored-By: Claude Haiku 4.5 --- src/app/api/agent/projects/route.ts | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/app/api/agent/projects/route.ts diff --git a/src/app/api/agent/projects/route.ts b/src/app/api/agent/projects/route.ts new file mode 100644 index 0000000..9046920 --- /dev/null +++ b/src/app/api/agent/projects/route.ts @@ -0,0 +1,42 @@ +import { getAdminPb } from "@/lib/pb/admin"; +import { requireAgentToken } from "@/lib/agent/auth"; +import { jsonOk } from "@/lib/http/json"; +import type { ClientRecord, ProjectRecord } from "@/lib/pb/types"; + +/** + * GET /api/agent/projects — every project across every client the owner has. + * + * No status filtering: an agent drafting a plan needs to see a paused or + * completed project too, not just active ones — it is planning, not + * browsing the way the home page is. + */ +export async function GET(request: Request): Promise { + const denied = requireAgentToken(request); + if (denied) return denied; + + const pb = await getAdminPb(); + + const [projects, clients] = await Promise.all([ + pb.collection("projects").getFullList({ sort: "name" }), + pb.collection("clients").getFullList(), + ]); + + const clientsById = new Map(clients.map((c) => [c.id, c])); + + const out = projects + .map((project) => { + const client = clientsById.get(project.client); + if (!client) return null; + return { + id: project.id, + name: project.name, + slug: project.slug, + client: { id: client.id, name: client.name, slug: client.slug }, + kind: project.kind, + status: project.status, + }; + }) + .filter((p): p is NonNullable => p !== null); + + return jsonOk(out); +}