From 559bf7df1f0a2252ac15ea98cc99c1369f37856b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 2 May 2026 11:42:55 -0700 Subject: [PATCH] fix(plugins): accept legacy clawhub api ranges --- src/infra/clawhub.test.ts | 8 ++++++++ src/infra/clawhub.ts | 24 +++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/infra/clawhub.test.ts b/src/infra/clawhub.test.ts index 19e6697c518..bdb931d33ef 100644 --- a/src/infra/clawhub.test.ts +++ b/src/infra/clawhub.test.ts @@ -100,6 +100,14 @@ describe("clawhub helpers", () => { expect(satisfiesPluginApiRange("invalid", "^1.2.0")).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); + expect(satisfiesPluginApiRange("2026.3.99", "2026.4")).toBe(false); + expect(satisfiesPluginApiRange("2026.5.2", "=2026.4")).toBe(false); + expect(satisfiesPluginApiRange("invalid", "2026.4")).toBe(false); + }); + it.each(["*", "x", "X", "=*", "=x", ">=*", ">=x", "<=*", "^*", "~*"] as const)( "accepts plugin api wildcard range %s for valid runtime versions", (range) => { diff --git a/src/infra/clawhub.ts b/src/infra/clawhub.ts index cc976335c32..884dec093c6 100644 --- a/src/infra/clawhub.ts +++ b/src/infra/clawhub.ts @@ -444,12 +444,25 @@ export async function resolveClawHubAuthToken(): Promise { return undefined; } +function normalizePartialComparableVersion(version: string): { + version: string; + isPartial: boolean; +} { + const trimmed = version.trim(); + return /^[vV]?[0-9]+\.[0-9]+$/.test(trimmed) + ? { version: `${trimmed}.0`, isPartial: true } + : { version: trimmed, isPartial: false }; +} + function compareSemver(left: string, right: string): number | null { - return compareComparableSemver(parseComparableSemver(left), parseComparableSemver(right)); + return compareComparableSemver( + parseComparableSemver(normalizePartialComparableVersion(left).version), + parseComparableSemver(normalizePartialComparableVersion(right).version), + ); } function upperBoundForCaret(version: string): string | null { - const parsed = parseComparableSemver(version); + const parsed = parseComparableSemver(normalizePartialComparableVersion(version).version); if (!parsed) { return null; } @@ -492,12 +505,13 @@ function satisfiesComparator(version: string, token: string): boolean { if (!match) { return false; } - const operator = match[1] ?? "="; + const operator = match[1]; const target = match[2]?.trim(); if (!target) { return false; } - const cmp = compareSemver(version, target); + const normalizedTarget = normalizePartialComparableVersion(target); + const cmp = compareSemver(version, normalizedTarget.version); if (cmp == null) { return false; } @@ -512,7 +526,7 @@ function satisfiesComparator(version: string, token: string): boolean { return cmp < 0; case "=": default: - return cmp === 0; + return normalizedTarget.isPartial && !operator ? cmp >= 0 : cmp === 0; } }