feat(agent-api): POST /api/agent/cards/:cardId/comments
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a297fe5f15
commit
0e74dbdf14
1 changed files with 55 additions and 0 deletions
55
src/app/api/agent/cards/[cardId]/comments/route.ts
Normal file
55
src/app/api/agent/cards/[cardId]/comments/route.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { getAdminPb } from "@/lib/pb/admin";
|
||||||
|
import { requireAgentToken } from "@/lib/agent/auth";
|
||||||
|
import { getOwnerUser } from "@/lib/agent/owner";
|
||||||
|
import { jsonError, jsonOk } from "@/lib/http/json";
|
||||||
|
import type { CardRecord } from "@/lib/pb/types";
|
||||||
|
|
||||||
|
/** Matches the comments collection's own `body` field limit. */
|
||||||
|
const MAX_BODY_LENGTH = 20000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/agent/cards/:cardId/comments — post a drafted plan as a comment.
|
||||||
|
*
|
||||||
|
* The stored body is always prefixed before being written, so no caller can
|
||||||
|
* accidentally or deliberately post something indistinguishable from the
|
||||||
|
* owner's own words. This prefix is the one thing this whole feature exists
|
||||||
|
* to guarantee — do not make it conditional or configurable.
|
||||||
|
*/
|
||||||
|
export async function POST(
|
||||||
|
request: Request,
|
||||||
|
context: { params: Promise<{ cardId: string }> }
|
||||||
|
): Promise<Response> {
|
||||||
|
const denied = requireAgentToken(request);
|
||||||
|
if (denied) return denied;
|
||||||
|
|
||||||
|
const { cardId } = await context.params;
|
||||||
|
|
||||||
|
let json: unknown;
|
||||||
|
try {
|
||||||
|
json = await request.json();
|
||||||
|
} catch {
|
||||||
|
return jsonError("Invalid request body", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = (json as { body?: unknown } | null)?.body;
|
||||||
|
if (typeof body !== "string" || !body.trim()) {
|
||||||
|
return jsonError("A non-empty body is required", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pb = await getAdminPb();
|
||||||
|
const card = await pb.collection("cards").getOne<CardRecord>(cardId).catch(() => null);
|
||||||
|
if (!card || card.archived) return jsonError("Card not found", 404);
|
||||||
|
|
||||||
|
const owner = await getOwnerUser();
|
||||||
|
|
||||||
|
const prefix = "🤖 Drafted by your agent:\n\n";
|
||||||
|
const trimmed = body.trim().slice(0, MAX_BODY_LENGTH - prefix.length);
|
||||||
|
|
||||||
|
const comment = await pb.collection("comments").create({
|
||||||
|
card: cardId,
|
||||||
|
author: owner.id,
|
||||||
|
body: `${prefix}${trimmed}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return jsonOk({ id: comment.id }, 201);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue