mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 16:30:42 +00:00
fix(plugins): align clawhub clawpack downloads
This commit is contained in:
@@ -57,8 +57,6 @@ const DEMO_CLAWPACK_SHA256 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
const DEMO_CLAWPACK_INTEGRITY = `sha256-${Buffer.from(DEMO_CLAWPACK_SHA256, "hex").toString(
|
||||
"base64",
|
||||
)}`;
|
||||
const DEMO_CLAWPACK_MANIFEST_SHA256 =
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
function sha256Hex(value: string): string {
|
||||
@@ -336,29 +334,24 @@ describe("installPluginFromClawHub", () => {
|
||||
pluginApiRange: ">=2026.3.22",
|
||||
minGatewayVersion: "2026.3.0",
|
||||
},
|
||||
clawpack: {
|
||||
available: true,
|
||||
specVersion: 1,
|
||||
format: "clawpack.zip",
|
||||
artifact: {
|
||||
kind: "npm-pack",
|
||||
format: "tgz",
|
||||
sha256: DEMO_CLAWPACK_SHA256,
|
||||
size: 4096,
|
||||
fileCount: 7,
|
||||
manifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
|
||||
builtAt: 1774200000000,
|
||||
buildVersion: "2026.3.22",
|
||||
hostTargets: [],
|
||||
environment: null,
|
||||
runtimeBundles: [],
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
npmShasum: "1".repeat(40),
|
||||
npmTarballName: "demo-2026.3.22.tgz",
|
||||
},
|
||||
},
|
||||
});
|
||||
downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
|
||||
archivePath: "/tmp/clawhub-demo/clawpack.zip",
|
||||
archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
|
||||
integrity: DEMO_CLAWPACK_INTEGRITY,
|
||||
sha256Hex: DEMO_CLAWPACK_SHA256,
|
||||
artifact: "clawpack",
|
||||
clawpackHeaderSha256: DEMO_CLAWPACK_SHA256,
|
||||
clawpackHeaderSpecVersion: 1,
|
||||
npmIntegrity: "sha512-clawpack",
|
||||
cleanup: archiveCleanupMock,
|
||||
});
|
||||
|
||||
@@ -372,8 +365,6 @@ describe("installPluginFromClawHub", () => {
|
||||
clawhub: {
|
||||
integrity: DEMO_CLAWPACK_INTEGRITY,
|
||||
clawpackSha256: DEMO_CLAWPACK_SHA256,
|
||||
clawpackSpecVersion: 1,
|
||||
clawpackManifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
|
||||
clawpackSize: 4096,
|
||||
},
|
||||
});
|
||||
@@ -396,23 +387,20 @@ describe("installPluginFromClawHub", () => {
|
||||
pluginApiRange: ">=2026.3.22",
|
||||
minGatewayVersion: "2026.3.0",
|
||||
},
|
||||
clawpack: {
|
||||
available: true,
|
||||
specVersion: 1,
|
||||
format: "clawpack.zip",
|
||||
artifact: {
|
||||
kind: "npm-pack",
|
||||
format: "tgz",
|
||||
sha256: DEMO_CLAWPACK_SHA256,
|
||||
size: 4096,
|
||||
manifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
|
||||
},
|
||||
},
|
||||
});
|
||||
downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
|
||||
archivePath: "/tmp/clawhub-demo/clawpack.zip",
|
||||
archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
|
||||
integrity: DEMO_CLAWPACK_INTEGRITY,
|
||||
sha256Hex: DEMO_CLAWPACK_SHA256,
|
||||
artifact: "clawpack",
|
||||
clawpackHeaderSha256: DEMO_CLAWPACK_SHA256,
|
||||
clawpackHeaderSpecVersion: 1,
|
||||
cleanup: archiveCleanupMock,
|
||||
});
|
||||
|
||||
@@ -435,7 +423,7 @@ describe("installPluginFromClawHub", () => {
|
||||
);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/clawpack.zip",
|
||||
archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -451,16 +439,15 @@ describe("installPluginFromClawHub", () => {
|
||||
pluginApiRange: ">=2026.3.22",
|
||||
minGatewayVersion: "2026.3.0",
|
||||
},
|
||||
clawpack: {
|
||||
available: true,
|
||||
specVersion: 1,
|
||||
format: "clawpack.zip",
|
||||
artifact: {
|
||||
kind: "npm-pack",
|
||||
format: "tgz",
|
||||
sha256: DEMO_CLAWPACK_SHA256,
|
||||
},
|
||||
},
|
||||
});
|
||||
downloadClawHubPackageArchiveMock.mockResolvedValueOnce({
|
||||
archivePath: "/tmp/clawhub-demo/clawpack.zip",
|
||||
archivePath: "/tmp/clawhub-demo/demo-2026.3.22.tgz",
|
||||
integrity: `sha256-${Buffer.from(mismatchedSha256, "hex").toString("base64")}`,
|
||||
sha256Hex: mismatchedSha256,
|
||||
artifact: "clawpack",
|
||||
@@ -497,19 +484,11 @@ describe("installPluginFromClawHub", () => {
|
||||
pluginApiRange: ">=2026.3.22",
|
||||
minGatewayVersion: "2026.3.0",
|
||||
},
|
||||
clawpack: {
|
||||
available: true,
|
||||
specVersion: 1,
|
||||
format: "clawpack.zip",
|
||||
artifact: {
|
||||
kind: "npm-pack",
|
||||
format: "tgz",
|
||||
sha256: DEMO_CLAWPACK_SHA256,
|
||||
size: 4096,
|
||||
fileCount: 7,
|
||||
manifestSha256: DEMO_CLAWPACK_MANIFEST_SHA256,
|
||||
builtAt: 1774200000000,
|
||||
buildVersion: "2026.3.22",
|
||||
hostTargets: [],
|
||||
environment: null,
|
||||
runtimeBundles: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
satisfiesGatewayMinimum,
|
||||
satisfiesPluginApiRange,
|
||||
type ClawHubPackageChannel,
|
||||
type ClawHubPackageArtifactSummary,
|
||||
type ClawHubPackageCompatibility,
|
||||
type ClawHubPackageDetail,
|
||||
type ClawHubPackageFamily,
|
||||
@@ -128,22 +129,26 @@ type ClawHubArchiveEntryLimits = {
|
||||
};
|
||||
|
||||
function normalizeClawHubClawPackInstallFields(
|
||||
clawpack: ClawHubPackageClawPackSummary | null | undefined,
|
||||
clawpack: ClawHubPackageArtifactSummary | ClawHubPackageClawPackSummary | null | undefined,
|
||||
): Pick<
|
||||
ClawHubPluginInstallRecordFields,
|
||||
"clawpackSha256" | "clawpackSpecVersion" | "clawpackManifestSha256" | "clawpackSize"
|
||||
> {
|
||||
if (clawpack?.available !== true) {
|
||||
const isNpmPackArtifact =
|
||||
clawpack && "kind" in clawpack && normalizeOptionalString(clawpack.kind) === "npm-pack";
|
||||
const isLegacyClawPack = clawpack && "available" in clawpack && clawpack.available;
|
||||
if (!isNpmPackArtifact && !isLegacyClawPack) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const clawpackSha256 =
|
||||
typeof clawpack.sha256 === "string" ? normalizeClawHubSha256Hex(clawpack.sha256) : null;
|
||||
const clawpackManifestSha256 =
|
||||
typeof clawpack.manifestSha256 === "string"
|
||||
"manifestSha256" in clawpack && typeof clawpack.manifestSha256 === "string"
|
||||
? normalizeClawHubSha256Hex(clawpack.manifestSha256)
|
||||
: null;
|
||||
const clawpackSpecVersion =
|
||||
"specVersion" in clawpack &&
|
||||
typeof clawpack.specVersion === "number" &&
|
||||
Number.isSafeInteger(clawpack.specVersion) &&
|
||||
clawpack.specVersion >= 0
|
||||
@@ -174,14 +179,35 @@ function isTrustedSourceLinkedOfficialPackage(pkg: NonNullable<ClawHubPackageDet
|
||||
}
|
||||
|
||||
function resolveClawHubClawPackArtifactSha256(
|
||||
clawpack: ClawHubPackageClawPackSummary | null | undefined,
|
||||
clawpack: ClawHubPackageArtifactSummary | ClawHubPackageClawPackSummary | null | undefined,
|
||||
): string | null {
|
||||
if (clawpack?.available !== true || typeof clawpack.sha256 !== "string") {
|
||||
const isNpmPackArtifact =
|
||||
clawpack && "kind" in clawpack && normalizeOptionalString(clawpack.kind) === "npm-pack";
|
||||
const isLegacyClawPack = clawpack && "available" in clawpack && clawpack.available;
|
||||
if ((!isNpmPackArtifact && !isLegacyClawPack) || typeof clawpack.sha256 !== "string") {
|
||||
return null;
|
||||
}
|
||||
return normalizeClawHubSha256Hex(clawpack.sha256);
|
||||
}
|
||||
|
||||
function resolveClawHubNpmIntegrity(
|
||||
clawpack: ClawHubPackageArtifactSummary | ClawHubPackageClawPackSummary | null | undefined,
|
||||
): string | null {
|
||||
return normalizeOptionalString(clawpack?.npmIntegrity) ?? null;
|
||||
}
|
||||
|
||||
function resolveClawHubNpmPackArtifact(
|
||||
version: NonNullable<ClawHubPackageVersion["version"]>,
|
||||
): ClawHubPackageArtifactSummary | ClawHubPackageClawPackSummary | null {
|
||||
if (version.artifact?.kind === "npm-pack") {
|
||||
return version.artifact;
|
||||
}
|
||||
if (version.clawpack?.available === true) {
|
||||
return version.clawpack;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function formatClawHubSpecifier(params: { name: string; version?: string }): string {
|
||||
return `clawhub:${params.name}${params.version ? `@${params.version}` : ""}`;
|
||||
}
|
||||
@@ -661,7 +687,7 @@ async function resolveCompatiblePackageVersion(params: {
|
||||
version: string;
|
||||
compatibility?: ClawHubPackageCompatibility | null;
|
||||
verification: ClawHubArchiveVerification | null;
|
||||
clawpack?: ClawHubPackageClawPackSummary | null;
|
||||
clawpack?: ClawHubPackageArtifactSummary | ClawHubPackageClawPackSummary | null;
|
||||
}
|
||||
| ClawHubInstallFailure
|
||||
> {
|
||||
@@ -699,7 +725,9 @@ async function resolveCompatiblePackageVersion(params: {
|
||||
clawpack: versionDetail.version?.clawpack ?? null,
|
||||
};
|
||||
}
|
||||
const clawpack = versionDetail.version?.clawpack ?? null;
|
||||
const clawpack = versionDetail.version
|
||||
? resolveClawHubNpmPackArtifact(versionDetail.version)
|
||||
: null;
|
||||
const verificationState = resolveClawHubArchiveVerification(
|
||||
versionDetail,
|
||||
params.detail.package?.name ?? "unknown",
|
||||
@@ -910,6 +938,7 @@ export async function installPluginFromClawHub(
|
||||
try {
|
||||
if (expectedClawPackSha256) {
|
||||
const expectedIntegrity = normalizeClawHubSha256Integrity(expectedClawPackSha256);
|
||||
const expectedNpmIntegrity = resolveClawHubNpmIntegrity(versionState.clawpack);
|
||||
if (
|
||||
archive.artifact !== "clawpack" ||
|
||||
archive.clawpackHeaderSha256 !== expectedClawPackSha256 ||
|
||||
@@ -921,6 +950,12 @@ export async function installPluginFromClawHub(
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
);
|
||||
}
|
||||
if (expectedNpmIntegrity && archive.npmIntegrity !== expectedNpmIntegrity) {
|
||||
return buildClawHubInstallFailure(
|
||||
`ClawHub ClawPack npm integrity mismatch for "${parsed.name}@${versionState.version}": expected ${expectedNpmIntegrity}, got ${archive.npmIntegrity ?? "unknown"}.`,
|
||||
CLAWHUB_INSTALL_ERROR_CODE.ARCHIVE_INTEGRITY_MISMATCH,
|
||||
);
|
||||
}
|
||||
} else if (versionState.verification?.kind === "archive-integrity") {
|
||||
if (archive.integrity !== versionState.verification.integrity) {
|
||||
return buildClawHubInstallFailure(
|
||||
|
||||
Reference in New Issue
Block a user