























@@ -51,10 +51,12 @@ vi.mock("../infra/archive.js", async () => {
5151});
52525353const { ClawHubRequestError } = await import("../infra/clawhub.js");
54+type ClawHubResolvedArtifact = import("../infra/clawhub.js").ClawHubResolvedArtifact;
5455const { CLAWHUB_INSTALL_ERROR_CODE, formatClawHubSpecifier, installPluginFromClawHub } =
5556await import("./clawhub.js");
56575758const DEMO_ARCHIVE_INTEGRITY = "sha256-qerEjGEpvES2+Tyan0j2xwDRkbcnmh4ZFfKN9vWbsa8=";
59+const DEMO_ARCHIVE_SHA256 = "a9eac48c6129bc44b6f93c9a9f48f6c700d191b7279a1e1915f28df6f59bb1af";
5860const DEMO_CLAWPACK_SHA256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
5961const DEMO_CLAWPACK_INTEGRITY = `sha256-${Buffer.from(DEMO_CLAWPACK_SHA256, "hex").toString(
6062 "base64",
@@ -463,6 +465,102 @@ describe("installPluginFromClawHub", () => {
463465);
464466});
465467468+it("accepts the live ClawHub artifact resolver shape with kind/sha256 field names", async () => {
469+fetchClawHubPackageVersionMock.mockClear();
470+fetchClawHubPackageArtifactMock.mockResolvedValueOnce({
471+package: {
472+name: "demo",
473+displayName: "Demo",
474+family: "code-plugin",
475+},
476+version: "2026.3.22",
477+artifact: {
478+kind: "npm-pack",
479+sha256: DEMO_CLAWPACK_SHA256,
480+npmIntegrity: "sha512-clawpack",
481+npmShasum: "1".repeat(40),
482+} as unknown as ClawHubResolvedArtifact,
483+});
484+downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
485+archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
486+integrity: DEMO_CLAWPACK_INTEGRITY,
487+sha256Hex: DEMO_CLAWPACK_SHA256,
488+artifact: "clawpack",
489+clawpackHeaderSha256: DEMO_CLAWPACK_SHA256,
490+npmIntegrity: "sha512-clawpack",
491+npmShasum: "1".repeat(40),
492+cleanup: archiveCleanupMock,
493+});
494+495+const result = await installPluginFromClawHub({
496+spec: "clawhub:demo",
497+baseUrl: "https://clawhub.ai",
498+});
499+500+expect(result).toMatchObject({
501+ok: true,
502+clawhub: {
503+artifactKind: "npm-pack",
504+artifactFormat: "tgz",
505+npmIntegrity: "sha512-clawpack",
506+npmShasum: "1".repeat(40),
507+clawpackSha256: DEMO_CLAWPACK_SHA256,
508+},
509+});
510+expect(fetchClawHubPackageVersionMock).not.toHaveBeenCalled();
511+expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
512+expect.objectContaining({
513+artifact: "clawpack",
514+name: "demo",
515+version: "2026.3.22",
516+}),
517+);
518+});
519+520+it("accepts the live ClawHub legacy zip resolver shape with kind/sha256 field names", async () => {
521+fetchClawHubPackageVersionMock.mockClear();
522+fetchClawHubPackageArtifactMock.mockResolvedValueOnce({
523+package: {
524+name: "demo",
525+displayName: "Demo",
526+family: "code-plugin",
527+},
528+version: "2026.3.22",
529+artifact: {
530+kind: "legacy-zip",
531+sha256: DEMO_ARCHIVE_SHA256,
532+} as unknown as ClawHubResolvedArtifact,
533+});
534+downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
535+archivePath: "/tmp/clawhub-demo/archive.zip",
536+integrity: DEMO_ARCHIVE_INTEGRITY,
537+cleanup: archiveCleanupMock,
538+});
539+540+const result = await installPluginFromClawHub({
541+spec: "clawhub:demo",
542+baseUrl: "https://clawhub.ai",
543+});
544+545+expect(result).toMatchObject({
546+ok: true,
547+pluginId: "demo",
548+clawhub: {
549+artifactKind: "legacy-zip",
550+artifactFormat: "zip",
551+integrity: DEMO_ARCHIVE_INTEGRITY,
552+},
553+});
554+expect(fetchClawHubPackageVersionMock).not.toHaveBeenCalled();
555+expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledWith(
556+expect.objectContaining({
557+artifact: "archive",
558+name: "demo",
559+version: "2026.3.22",
560+}),
561+);
562+});
563+466564it("falls back to version metadata when the ClawHub artifact resolver route is missing", async () => {
467565fetchClawHubPackageArtifactMock.mockRejectedValueOnce(
468566new ClawHubRequestError({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。