mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-12 15:59:04 +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>
44 lines
1.2 KiB
TypeScript
44 lines
1.2 KiB
TypeScript
import { afterEach, describe, expect, it, rs } from "@rstest/core";
|
|
|
|
import { loadArtifactContent } from "@/core/artifacts/loader";
|
|
|
|
afterEach(() => {
|
|
rs.restoreAllMocks();
|
|
});
|
|
|
|
describe("loadArtifactContent", () => {
|
|
it("uses the server content revision when available", async () => {
|
|
rs.spyOn(globalThis, "fetch").mockResolvedValue(
|
|
new Response("content", {
|
|
status: 200,
|
|
headers: { ETag: `"${"a".repeat(64)}"` },
|
|
}),
|
|
);
|
|
|
|
const loaded = await loadArtifactContent({
|
|
filepath: "/mnt/user-data/outputs/report.md",
|
|
threadId: "thread-1",
|
|
});
|
|
|
|
expect(loaded.sha256).toBe("a".repeat(64));
|
|
});
|
|
|
|
it("computes a revision for active text responses without a SHA-256 ETag", async () => {
|
|
rs.spyOn(globalThis, "fetch").mockResolvedValue(
|
|
new Response("content", {
|
|
status: 200,
|
|
headers: { ETag: '"starlette-file-etag"' },
|
|
}),
|
|
);
|
|
|
|
const loaded = await loadArtifactContent({
|
|
filepath: "/mnt/user-data/outputs/page.html",
|
|
threadId: "thread-1",
|
|
});
|
|
|
|
expect(loaded.sha256).toBe(
|
|
"ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73",
|
|
);
|
|
});
|
|
});
|