From df3aab97be13689284f0d6cc47f1e57027c1a572 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Wed, 17 Jun 2026 21:01:47 +0800 Subject: [PATCH] fix(artifacts): update export artifacts module --- src/exportArtifacts.test.ts | 24 +++++++++++++++++++++ src/exportArtifacts.ts | 43 ++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/exportArtifacts.test.ts b/src/exportArtifacts.test.ts index bd36bb4..5673839 100644 --- a/src/exportArtifacts.test.ts +++ b/src/exportArtifacts.test.ts @@ -177,6 +177,30 @@ describe("exportXWorkmateArtifacts", () => { expect(result.missingRequiredExtensions).toEqual(["pdf"]); }); + it("reports missing required artifact file counts", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); + const prepared = await prepareXWorkmateArtifacts({ + params: { openclawSessionKey: "thread-main", runId: "run-missing-count" }, + pluginConfig: { workspaceDir: root }, + }); + await fs.mkdir(path.join(prepared.artifactDirectory, "assets", "images"), { recursive: true }); + await fs.writeFile(path.join(prepared.artifactDirectory, "assets", "images", "001.png"), "png"); + + const result = await exportXWorkmateArtifacts({ + params: { + openclawSessionKey: "thread-main", + runId: "run-missing-count", + requiredArtifactExtensions: ["png"], + expectedFileCountByExtension: { png: 7 }, + }, + pluginConfig: { workspaceDir: root }, + }); + + expect(result.constraintSatisfied).toBe(false); + expect(result.missingRequiredExtensions).toEqual([]); + expect(result.missingRequiredFileCounts).toEqual({ png: { expected: 7, actual: 1 } }); + }); + it("treats an empty required artifact extension list as satisfied", async () => { const root = await fs.mkdtemp(path.join(os.tmpdir(), "tmp-openclaw-multi-session-plugins-")); const prepared = await prepareXWorkmateArtifacts({ diff --git a/src/exportArtifacts.ts b/src/exportArtifacts.ts index c6247ef..19dcc9d 100644 --- a/src/exportArtifacts.ts +++ b/src/exportArtifacts.ts @@ -49,6 +49,7 @@ type XWorkmateArtifactExport = { expectedArtifactDirStatus: XWorkmateExpectedArtifactDirStatus[]; constraintSatisfied: boolean; missingRequiredExtensions: string[]; + missingRequiredFileCounts: Record; }; type XWorkmateArtifactPrepare = { @@ -238,6 +239,7 @@ export async function exportXWorkmateArtifacts(input: ExportInput): Promise { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return {}; + } + const result: Record = {}; + for (const [rawExtension, rawCount] of Object.entries(value as Record)) { + const extension = rawExtension + .toLowerCase() + .trim() + .replace(/^\.+/u, ""); + if (!extension || extension.includes("/") || extension.includes("\\") || extension.includes("\0")) { + continue; + } + const count = typeof rawCount === "number" ? rawCount : Number.parseInt(optionalString(rawCount), 10); + if (!Number.isFinite(count) || count <= 0) { + continue; + } + result[extension] = Math.floor(count); + } + return result; +} + +function missingRequiredArtifactFileCounts( + artifacts: XWorkmateArtifact[], + expectedFileCountByExtension: Record, +): Record { + const missing: Record = {}; + for (const [extension, expected] of Object.entries(expectedFileCountByExtension)) { + const actual = artifacts.filter((artifact) => artifact.relativePath.toLowerCase().endsWith(`.${extension}`)).length; + if (actual < expected) { + missing[extension] = { expected, actual }; + } + } + return missing; +} + async function expectedArtifactDirStatuses( workspaceRoot: string, expectedArtifactDirs: string[],