fix(release): reject blank plugin runtime entries

This commit is contained in:
Vincent Koc
2026-05-04 02:14:31 -07:00
parent b7ce9439e7
commit 05d6c62152
3 changed files with 48 additions and 5 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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",
]);
});
});