mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
fix(plugins): treat CalVer correction versions as compatible with plugin API ranges (#77450)
* fix(plugins): accept CalVer correction plugin API hosts Fixes #77293 * docs(changelog): credit plugin api calver fix pr --------- Co-authored-by: pingu <pingu@penchan.co>
This commit is contained in:
@@ -64,6 +64,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Doctor/config: restore legacy group chat config migrations for `routing.allowFrom`, `routing.groupChat.*`, and `channels.telegram.requireMention` so upgrades keep WhatsApp, Telegram, and iMessage group mention gates and history settings instead of leaving configs invalid or silently blocked. Thanks @scoootscooob.
|
||||
- CLI/update: make package-update follow-up processes write completion results and exit explicitly, so Windows packaged upgrades do not hang after the new package finishes post-core plugin work. Thanks @vincentkoc.
|
||||
- Release validation: skip Slack live QA unless Slack credentials are explicitly configured, so release gates can keep proving non-Slack surfaces while Slack is still local and credential-gated. Thanks @vincentkoc.
|
||||
- Plugins/update: treat OpenClaw CalVer correction versions like `2026.5.3-1` as satisfying base plugin API ranges, so correction builds can install plugins that require the base runtime API. Fixes #77293. (#77450) Thanks @p3nchan.
|
||||
- fix(gateway): clamp unbound websocket auth scopes [AI]. (#77413) Thanks @pgondhi987.
|
||||
- Gate zalouser startup name matching [AI]. (#77411) Thanks @pgondhi987.
|
||||
- Active Memory: send a bounded latest-message search query to the recall worker so channel/runtime metadata does not become the memory search string. Fixes #65309. Thanks @joeykrug, @westley3601, @pimenov, and @tasi333.
|
||||
|
||||
@@ -100,6 +100,12 @@ describe("clawhub helpers", () => {
|
||||
expect(satisfiesPluginApiRange("invalid", "^1.2.0")).toBe(false);
|
||||
});
|
||||
|
||||
it("treats OpenClaw CalVer correction versions as stable plugin API hosts", () => {
|
||||
expect(satisfiesPluginApiRange("2026.5.3-1", ">=2026.5.3")).toBe(true);
|
||||
expect(satisfiesPluginApiRange("2026.5.3-2", ">=2026.5.3")).toBe(true);
|
||||
expect(satisfiesPluginApiRange("2026.5.3-beta.1", ">=2026.5.3")).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts legacy bare major.minor plugin api ranges as lower bounds", () => {
|
||||
expect(satisfiesPluginApiRange("2026.5.2", "2026.4")).toBe(true);
|
||||
expect(satisfiesPluginApiRange("2026.4.0", "2026.4")).toBe(true);
|
||||
|
||||
@@ -542,6 +542,13 @@ function satisfiesSemverRange(version: string, range: string): boolean {
|
||||
return tokens.every((token) => satisfiesComparator(version, token));
|
||||
}
|
||||
|
||||
const OPENCLAW_CALVER_STABLE_CORRECTION_PATTERN = /^[vV]?(\d{4}\.\d{1,2}\.\d{1,2})-\d+$/;
|
||||
|
||||
function normalizeCalVerCorrectionForPluginApi(pluginApiVersion: string): string {
|
||||
const match = OPENCLAW_CALVER_STABLE_CORRECTION_PATTERN.exec(pluginApiVersion.trim());
|
||||
return match?.[1] ?? pluginApiVersion;
|
||||
}
|
||||
|
||||
function buildUrl(params: Pick<ClawHubRequestParams, "baseUrl" | "path" | "search">): URL {
|
||||
const url = new URL(params.path, `${normalizeBaseUrl(params.baseUrl)}/`);
|
||||
for (const [key, value] of Object.entries(params.search ?? {})) {
|
||||
@@ -1046,7 +1053,10 @@ export function satisfiesPluginApiRange(
|
||||
if (!pluginApiRange) {
|
||||
return true;
|
||||
}
|
||||
return satisfiesSemverRange(pluginApiVersion, pluginApiRange);
|
||||
return satisfiesSemverRange(
|
||||
normalizeCalVerCorrectionForPluginApi(pluginApiVersion),
|
||||
pluginApiRange,
|
||||
);
|
||||
}
|
||||
|
||||
export function satisfiesGatewayMinimum(
|
||||
|
||||
@@ -852,6 +852,36 @@ describe("installPluginFromClawHub", () => {
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("installs when a CalVer correction runtime satisfies the base plugin API range", async () => {
|
||||
resolveCompatibilityHostVersionMock.mockReturnValueOnce("2026.5.3-1");
|
||||
fetchClawHubPackageVersionMock.mockResolvedValueOnce({
|
||||
version: {
|
||||
version: "2026.5.3",
|
||||
createdAt: 0,
|
||||
changelog: "",
|
||||
sha256hash: "a9eac48c6129bc44b6f93c9a9f48f6c700d191b7279a1e1915f28df6f59bb1af",
|
||||
compatibility: {
|
||||
pluginApiRange: ">=2026.5.3",
|
||||
minGatewayVersion: "2026.3.0",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const result = await installPluginFromClawHub({
|
||||
spec: "clawhub:demo",
|
||||
baseUrl: "https://clawhub.ai",
|
||||
});
|
||||
|
||||
expectSuccessfulClawHubInstall(result);
|
||||
expect(downloadClawHubPackageArchiveMock).toHaveBeenCalledTimes(1);
|
||||
expect(installPluginFromArchiveMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
archivePath: "/tmp/clawhub-demo/archive.zip",
|
||||
}),
|
||||
);
|
||||
expect(archiveCleanupMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not let a wildcard plugin API range hide an invalid runtime version", async () => {
|
||||
resolveCompatibilityHostVersionMock.mockReturnValueOnce("invalid");
|
||||
fetchClawHubPackageVersionMock.mockResolvedValueOnce({
|
||||
|
||||
Reference in New Issue
Block a user