




















@@ -54,6 +54,9 @@ const { CLAWHUB_INSTALL_ERROR_CODE, formatClawHubSpecifier, installPluginFromCla
54545555const DEMO_ARCHIVE_INTEGRITY = "sha256-qerEjGEpvES2+Tyan0j2xwDRkbcnmh4ZFfKN9vWbsa8=";
5656const DEMO_CLAWPACK_SHA256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
57+const DEMO_CLAWPACK_INTEGRITY = `sha256-${Buffer.from(DEMO_CLAWPACK_SHA256, "hex").toString(
58+ "base64",
59+)}`;
5760const DEMO_CLAWPACK_MANIFEST_SHA256 =
5861"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
5962const tempDirs: string[] = [];
@@ -320,6 +323,15 @@ describe("installPluginFromClawHub", () => {
320323},
321324},
322325});
326+downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
327+archivePath: "/tmp/clawhub-demo/clawpack.zip",
328+integrity: DEMO_CLAWPACK_INTEGRITY,
329+sha256Hex: DEMO_CLAWPACK_SHA256,
330+artifact: "clawpack",
331+clawpackHeaderSha256: DEMO_CLAWPACK_SHA256,
332+clawpackHeaderSpecVersion: 1,
333+cleanup: archiveCleanupMock,
334+});
323335324336const result = await installPluginFromClawHub({
325337spec: "clawhub:demo",
@@ -329,12 +341,116 @@ describe("installPluginFromClawHub", () => {
329341expect(result).toMatchObject({
330342ok: true,
331343clawhub: {
344+integrity: DEMO_CLAWPACK_INTEGRITY,
332345clawpackSha256: DEMO_CLAWPACK_SHA256,
333346clawpackSpecVersion: 1,
334347clawpackManifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
335348clawpackSize: 4096,
336349},
337350});
351+expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
352+expect.objectContaining({
353+artifact: "clawpack",
354+name: "demo",
355+version: "2026.3.22",
356+}),
357+);
358+});
359+360+it("installs ClawPack artifacts when version metadata has no legacy archive hash", async () => {
361+fetchClawHubPackageVersionMock.mockResolvedValueOnce({
362+version: {
363+version: "2026.3.22",
364+createdAt: 0,
365+changelog: "",
366+compatibility: {
367+pluginApiRange: ">=2026.3.22",
368+minGatewayVersion: "2026.3.0",
369+},
370+clawpack: {
371+available: true,
372+specVersion: 1,
373+format: "clawpack.zip",
374+sha256: DEMO_CLAWPACK_SHA256,
375+size: 4096,
376+manifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
377+},
378+},
379+});
380+downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
381+archivePath: "/tmp/clawhub-demo/clawpack.zip",
382+integrity: DEMO_CLAWPACK_INTEGRITY,
383+sha256Hex: DEMO_CLAWPACK_SHA256,
384+artifact: "clawpack",
385+clawpackHeaderSha256: DEMO_CLAWPACK_SHA256,
386+clawpackHeaderSpecVersion: 1,
387+cleanup: archiveCleanupMock,
388+});
389+390+const result = await installPluginFromClawHub({
391+spec: "clawhub:demo",
392+baseUrl: "https://clawhub.ai",
393+});
394+395+expect(result).toMatchObject({
396+ok: true,
397+clawhub: {
398+integrity: DEMO_CLAWPACK_INTEGRITY,
399+clawpackSha256: DEMO_CLAWPACK_SHA256,
400+},
401+});
402+expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
403+expect.objectContaining({
404+artifact: "clawpack",
405+}),
406+);
407+expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
408+expect.objectContaining({
409+archivePath: "/tmp/clawhub-demo/clawpack.zip",
410+}),
411+);
412+});
413+414+it("rejects ClawPack artifacts when the download digest does not match version metadata", async () => {
415+const mismatchedSha256 = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
416+fetchClawHubPackageVersionMock.mockResolvedValueOnce({
417+version: {
418+version: "2026.3.22",
419+createdAt: 0,
420+changelog: "",
421+compatibility: {
422+pluginApiRange: ">=2026.3.22",
423+minGatewayVersion: "2026.3.0",
424+},
425+clawpack: {
426+available: true,
427+specVersion: 1,
428+format: "clawpack.zip",
429+sha256: DEMO_CLAWPACK_SHA256,
430+},
431+},
432+});
433+downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
434+archivePath: "/tmp/clawhub-demo/clawpack.zip",
435+integrity: `sha256-${Buffer.from(mismatchedSha256, "hex").toString("base64")}`,
436+sha256Hex: mismatchedSha256,
437+artifact: "clawpack",
438+clawpackHeaderSha256: mismatchedSha256,
439+cleanup: archiveCleanupMock,
440+});
441+442+const result = await installPluginFromClawHub({
443+spec: "clawhub:demo",
444+baseUrl: "https://clawhub.ai",
445+});
446+447+expect(result).toMatchObject({
448+ok: false,
449+code: CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
450+error: `ClawHub ClawPack integrity mismatch for "demo@2026.3.22": expected ${DEMO_CLAWPACK_SHA256}, got ${mismatchedSha256}.`,
451+});
452+expect(installPluginFromArchiveMock).not.toHaveBeenCalled();
453+expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
338454});
339455340456it("does not persist package-level ClawPack metadata for version records without ClawPack facts", async () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。