87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { requireRequestSession } from "@/lib/boss-auth";
|
|
import { appendProjectMessage, buildCollaborationGate, readState } from "@/lib/boss-data";
|
|
import { canAccessProject } from "@/lib/boss-permissions";
|
|
import {
|
|
queueThreadForkTask,
|
|
ThreadConversationExecutionConflictError,
|
|
} from "@/lib/boss-master-agent";
|
|
|
|
function forbiddenResponse(message = "FORBIDDEN") {
|
|
return NextResponse.json({ ok: false, message }, { status: 403 });
|
|
}
|
|
|
|
function normalizeReason(value: unknown) {
|
|
const trimmed = String(value ?? "").trim();
|
|
return trimmed ? trimmed : undefined;
|
|
}
|
|
|
|
export async function POST(
|
|
request: NextRequest,
|
|
context: { params: Promise<{ projectId: string }> },
|
|
) {
|
|
const session = await requireRequestSession(request);
|
|
if (!session) {
|
|
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
|
}
|
|
|
|
const { projectId } = await context.params;
|
|
const body = (await request.json().catch(() => ({}))) as {
|
|
reason?: unknown;
|
|
ephemeral?: unknown;
|
|
};
|
|
const reason = normalizeReason(body.reason);
|
|
const ephemeral = body.ephemeral === true;
|
|
|
|
const state = await readState();
|
|
const projectExists = state.projects.some((project) => project.id === projectId);
|
|
if (!canAccessProject(state, session, projectId, "project.view")) {
|
|
return forbiddenResponse(projectExists ? "FORBIDDEN" : "PROJECT_NOT_FOUND");
|
|
}
|
|
if (!canAccessProject(state, session, projectId, "master_agent.ask")) {
|
|
return forbiddenResponse("MASTER_AGENT_FORBIDDEN");
|
|
}
|
|
|
|
try {
|
|
const message = await appendProjectMessage({
|
|
projectId,
|
|
account: session.account,
|
|
senderLabel: session.displayName || "你",
|
|
body: reason || "分叉当前 Codex 线程。",
|
|
kind: "text",
|
|
});
|
|
const task = await queueThreadForkTask({
|
|
projectId,
|
|
requestMessageId: message.id,
|
|
ephemeral,
|
|
reason,
|
|
requestedBy: session.displayName || session.account,
|
|
requestedByAccount: session.account,
|
|
});
|
|
const nextState = await readState();
|
|
const project = nextState.projects.find((item) => item.id === projectId);
|
|
return NextResponse.json({
|
|
ok: true,
|
|
message,
|
|
task,
|
|
collaborationGate: buildCollaborationGate(project),
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof ThreadConversationExecutionConflictError) {
|
|
return NextResponse.json(
|
|
{
|
|
ok: false,
|
|
code: error.message,
|
|
message: "THREAD_EXECUTION_CONFLICT",
|
|
executionConflict: error.conflict,
|
|
},
|
|
{ status: 409 },
|
|
);
|
|
}
|
|
return NextResponse.json(
|
|
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
}
|