From 05d6c62152c8d236dcf2c0d98ac9c06d151c08ef Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 4 May 2026 02:14:31 -0700 Subject: [PATCH] fix(release): reject blank plugin runtime entries --- CHANGELOG.md | 1 + .../verify-plugin-npm-published-runtime.mjs | 34 ++++++++++++++++--- ...erify-plugin-npm-published-runtime.test.ts | 18 ++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 951fad963d8..09ca90a94aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Plugins/release: make the published npm runtime verifier reject blank `openclaw.runtimeExtensions` entries instead of treating them as absent and passing via inferred outputs. Thanks @vincentkoc. - Web fetch: scope provider fallback cache entries by the selected fetch provider so config reloads cannot reuse another provider's cached fallback payload. Thanks @vincentkoc. - Web search: honor late-bound `tools.web.search.enabled: false` during tool execution so config reloads cannot leave an already-created `web_search` tool runnable. Thanks @vincentkoc. - Plugins/packages: reject inferred built runtime entries that exist but fail package-boundary checks instead of falling back to TypeScript source for installed packages. Thanks @vincentkoc. diff --git a/scripts/verify-plugin-npm-published-runtime.mjs b/scripts/verify-plugin-npm-published-runtime.mjs index db23860042c..551bab08aa2 100644 --- a/scripts/verify-plugin-npm-published-runtime.mjs +++ b/scripts/verify-plugin-npm-published-runtime.mjs @@ -7,11 +7,21 @@ import path from "node:path"; import { pathToFileURL } from "node:url"; import * as tar from "tar"; -function normalizeStringList(value) { +function readPackageStringList(packageLabel, fieldName, value) { if (!Array.isArray(value)) { - return []; + return { entries: [], errors: [] }; } - return value.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean); + const entries = []; + const errors = []; + for (const [index, entry] of value.entries()) { + const normalized = typeof entry === "string" ? entry.trim() : ""; + if (!normalized) { + errors.push(`${packageLabel} package.json ${fieldName}[${index}] must be a non-empty string`); + continue; + } + entries.push(normalized); + } + return { entries, errors }; } function normalizePackagePath(value) { @@ -61,9 +71,23 @@ export function collectPluginNpmPublishedRuntimeErrors(params) { const packageJson = params.packageJson ?? {}; const packageFiles = new Set([...params.files].map(normalizePackagePath)); const packageLabel = formatPackageLabel(packageJson, params.spec); - const extensions = normalizeStringList(packageJson.openclaw?.extensions); - const runtimeExtensions = normalizeStringList(packageJson.openclaw?.runtimeExtensions); const errors = []; + const extensionsResult = readPackageStringList( + packageLabel, + "openclaw.extensions", + packageJson.openclaw?.extensions, + ); + const runtimeExtensionsResult = readPackageStringList( + packageLabel, + "openclaw.runtimeExtensions", + packageJson.openclaw?.runtimeExtensions, + ); + errors.push(...extensionsResult.errors, ...runtimeExtensionsResult.errors); + if (errors.length > 0) { + return errors; + } + const extensions = extensionsResult.entries; + const runtimeExtensions = runtimeExtensionsResult.entries; if (extensions.length === 0) { return errors; diff --git a/test/scripts/verify-plugin-npm-published-runtime.test.ts b/test/scripts/verify-plugin-npm-published-runtime.test.ts index d89f0ddb101..ad01bf1b3ea 100644 --- a/test/scripts/verify-plugin-npm-published-runtime.test.ts +++ b/test/scripts/verify-plugin-npm-published-runtime.test.ts @@ -69,4 +69,22 @@ describe("collectPluginNpmPublishedRuntimeErrors", () => { "@openclaw/acpx@2026.5.3 package.json openclaw.runtimeExtensions length (1) must match openclaw.extensions length (2)", ]); }); + + it("flags blank runtimeExtensions entries instead of falling back to inferred outputs", () => { + expect( + collectPluginNpmPublishedRuntimeErrors({ + packageJson: { + name: "@openclaw/whatsapp", + version: "2026.5.3", + openclaw: { + extensions: ["./src/index.ts"], + runtimeExtensions: [" "], + }, + }, + files: ["package.json", "src/index.ts", "dist/index.js"], + }), + ).toEqual([ + "@openclaw/whatsapp@2026.5.3 package.json openclaw.runtimeExtensions[0] must be a non-empty string", + ]); + }); });