import { z } from "zod"; import { limitWrite, limitAccountWrite } from "../limits"; import { TOKEN_AREAS } from "../token-scope"; import { createAgent, deleteAgent, listAgents, MAX_AGENT_TOKEN_DAYS, mintAgentToken } from "../mcp/agents"; import { usageForAccount } from "../mcp/usage"; import { assertChildWithinParent, mintingParent } from "./admin-writes"; import { noContent, readJson, requireAccountScope } from "./http"; import type { ApiContext } from "./http"; const areaAccess = z.enum(["read", "write"]).optional(); const DEFAULT_USAGE_DAYS = 30; export async function listAgentsApi(ctx: ApiContext): Promise { requireAccountScope(ctx); return Response.json(await listAgents(ctx.viewer.id)); } const agentInput = z.object({ name: z.string().trim().min(1).max(64), description: z.string().trim().max(500).nullable().optional(), }); export async function createAgentApi(ctx: ApiContext): Promise { requireAccountScope(ctx); const data = await readJson(ctx.request, agentInput); await limitWrite(ctx.viewer); return Response.json(await createAgent(ctx.viewer, data.name, data.description ?? null), { status: 201 }); } export async function deleteAgentApi(ctx: ApiContext, id: string): Promise { requireAccountScope(ctx); await limitAccountWrite(ctx.viewer); await deleteAgent(ctx.viewer, id); return noContent(); } const tokenInput = z.object({ repos: z.array(z.string()).max(50).default([]), all_repos: z.boolean().default(false), permissions: z.object(Object.fromEntries(TOKEN_AREAS.map((area) => [area, areaAccess]))), expires_in_days: z.number().int().min(1).max(MAX_AGENT_TOKEN_DAYS).default(30), forge_access: z.boolean().default(false), }); /** * Mints an agent-bound token; it only works on /mcp unless `forge_access` is set. Like any token minted by a * token it is no broader than the caller's grant, never outlives it and is revoked with it (INT-P1). */ export async function createAgentTokenApi(ctx: ApiContext, id: string): Promise { requireAccountScope(ctx); const data = await readJson(ctx.request, tokenInput); await limitWrite(ctx.viewer); const parent = await mintingParent(ctx); await assertChildWithinParent(ctx, data.permissions, data.all_repos, data.repos); const minted = await mintAgentToken( ctx.viewer, id, { repos: data.repos, allRepos: data.all_repos, permissions: data.permissions, expiresInDays: data.expires_in_days, forgeAccess: data.forge_access, }, { parentTokenId: parent.tokenId, notAfter: parent.expiresAt }, ); return Response.json({ id: minted.id, token: minted.token, expires_at: minted.expiresAt }, { status: 201 }); } /** MCP tool calls by day and tool for the authenticated account. */ export async function mcpUsageApi(ctx: ApiContext): Promise { requireAccountScope(ctx); const days = Math.floor(Number(ctx.url.searchParams.get("days") ?? DEFAULT_USAGE_DAYS)); return Response.json( await usageForAccount(ctx.viewer.id, Number.isFinite(days) && days > 0 ? days : DEFAULT_USAGE_DAYS), ); }