61 lines
1.7 KiB
TypeScript
61 lines
1.7 KiB
TypeScript
import { apiError } from "@/lib/api";
|
|
import {
|
|
getBuilderRunEvents,
|
|
getOrStartBuilderRun,
|
|
} from "@/lib/builder-runs";
|
|
import { encodeSse, sseResponse } from "@/lib/sse";
|
|
import { builderRequestSchema } from "@/lib/validation";
|
|
|
|
export const runtime = "nodejs";
|
|
export const maxDuration = 120;
|
|
|
|
const terminalStatuses = new Set(["complete", "stopped", "error"]);
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const input = builderRequestSchema.parse(await request.json());
|
|
const run = getOrStartBuilderRun(input);
|
|
let cursor = input.afterSeq;
|
|
|
|
const stream = new ReadableStream<Uint8Array>({
|
|
async start(controller) {
|
|
try {
|
|
while (!request.signal.aborted) {
|
|
const snapshot = getBuilderRunEvents(run.id, cursor);
|
|
if (!snapshot) {
|
|
controller.enqueue(
|
|
encodeSse({ type: "error", message: "节点任务已过期" }),
|
|
);
|
|
controller.close();
|
|
return;
|
|
}
|
|
|
|
for (const event of snapshot.events) {
|
|
controller.enqueue(encodeSse(event));
|
|
cursor = Math.max(cursor, event.seq ?? cursor);
|
|
}
|
|
|
|
if (terminalStatuses.has(snapshot.status)) {
|
|
controller.close();
|
|
return;
|
|
}
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
} catch {
|
|
try {
|
|
controller.close();
|
|
} catch {
|
|
// A disconnected editor can reconnect with the last event seq.
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
return sseResponse(stream);
|
|
} catch (error) {
|
|
return apiError(error, "无法处理 Skill 对话");
|
|
}
|
|
}
|
|
|