mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-11-08 03:30:34 +00:00
* feat(artifacts): inline editing for text artifacts in the panel
Add a PUT /api/threads/{id}/artifacts/{path} endpoint that atomically
replaces an existing UTF-8 text file under /mnt/user-data/outputs after
verifying its SHA-256 revision. Active runs conflict (409); binary,
symlink, oversized, and non-output paths are rejected.
Frontend: edit/save/discard buttons, draft state with conflict detection,
CodeEditor onChange/onSave, loader SHA-256 from ETag, i18n, beforeunload guard.
Backend: PUT endpoint with thread reservation, atomic temp-file replacement,
sandbox sync for non-mounted providers, rollback on failure, ETag on GET.
Tests: 8 backend + 1 blocking-IO + 3 frontend test files.
* fix(artifacts): scope replacement permissions and release sandboxes
---------
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
55 lines
1.3 KiB
TypeScript
55 lines
1.3 KiB
TypeScript
import { fetch } from "@/core/api/fetcher";
|
|
|
|
import { urlOfArtifact } from "./utils";
|
|
|
|
export interface ArtifactUpdateResponse {
|
|
path: string;
|
|
sha256: string;
|
|
size: number;
|
|
}
|
|
|
|
export class ArtifactRequestError extends Error {
|
|
readonly status: number;
|
|
|
|
constructor(status: number, message: string) {
|
|
super(message);
|
|
this.name = "ArtifactRequestError";
|
|
this.status = status;
|
|
}
|
|
}
|
|
|
|
async function readErrorDetail(response: Response): Promise<string> {
|
|
const data = (await response.json().catch(() => ({}))) as {
|
|
detail?: string;
|
|
};
|
|
return data.detail ?? `HTTP ${response.status}: ${response.statusText}`;
|
|
}
|
|
|
|
export async function updateArtifactContent({
|
|
threadId,
|
|
filepath,
|
|
content,
|
|
expectedSha256,
|
|
}: {
|
|
threadId: string;
|
|
filepath: string;
|
|
content: string;
|
|
expectedSha256: string;
|
|
}): Promise<ArtifactUpdateResponse> {
|
|
const response = await fetch(urlOfArtifact({ filepath, threadId }), {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
content,
|
|
expected_sha256: expectedSha256,
|
|
}),
|
|
});
|
|
if (!response.ok) {
|
|
throw new ArtifactRequestError(
|
|
response.status,
|
|
await readErrorDetail(response),
|
|
);
|
|
}
|
|
return response.json() as Promise<ArtifactUpdateResponse>;
|
|
}
|