feat(agent): 统一专业拆镜入口与真实镜头生成

This commit is contained in:
王志伟
2026-09-08 23:28:56 +08:00
parent 45de256deb
commit b33de36976
12 changed files with 215 additions and 22 deletions

View File

@@ -0,0 +1,35 @@
import { nanoid } from "nanoid";
import { creationRuns, type CreationSubmission } from "./api/creation-runs";
import type { CreateTaskInput } from "./api/task-center";
// Both the node composer and Agent submit the same quoted professional task.
export async function submitStoryboardTask(request: CreateTaskInput, options: {
signal: AbortSignal;
assertCurrent: () => void;
confirm: (submission: CreationSubmission) => Promise<boolean>;
}, api = creationRuns) {
const { signal, assertCurrent } = options;
const detail = await api.create({ clientKey: `storyboard:${nanoid()}`, canvasId: request.projectId, state: { kind: "storyboard" } }, signal);
const run = await api.claim(detail.run.id, { expectedEpoch: detail.run.executionEpoch, owner: `storyboard:${nanoid()}` }, signal);
const guard = { executionEpoch: run.executionEpoch, owner: run.executionOwner };
let connectionError: unknown;
const heartbeat = setInterval(() => {
void api.heartbeat(run.id, guard, signal).catch((error) => { connectionError = error; });
}, 15_000);
try {
const submission = await api.prepare(run.id, { ...guard, itemKey: "storyboard", request }, signal);
if (!await options.confirm(submission)) return undefined;
signal.throwIfAborted();
if (connectionError) throw connectionError;
assertCurrent();
if (Date.parse(submission.quote.expiresAt) <= Date.now()) throw new Error("分镜报价已过期,请重新发起拆镜并确认最新费用");
const approved = await api.approve(run.id, { ...guard, submissionIds: [submission.id] }, signal);
if (!approved.submissions.some((item) => item.id === submission.id && item.approvedAt)) throw new Error("费用确认回执不完整,尚未提交拆镜任务");
signal.throwIfAborted();
assertCurrent();
return await api.execute(run.id, { ...guard, submissionId: submission.id }, signal);
} finally {
clearInterval(heartbeat);
void api.release(run.id, guard).catch(() => { /* Offline leases expire on the server. */ });
}
}