plugins: quiet scoped manifest id warnings

This commit is contained in:
Gustavo Madeira Santana 2026-03-30 19:35:09 -04:00
parent 2b2edaa01d
commit bbd495ed63
No known key found for this signature in database
4 changed files with 55 additions and 2 deletions

View File

@ -4,6 +4,7 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
import {
assertCanonicalPathWithinBase,
packageNameMatchesId,
resolveSafeInstallDir,
safeDirName,
safePathSegmentHashed,
@ -20,6 +21,18 @@ describe("unscopedPackageName", () => {
});
});
describe("packageNameMatchesId", () => {
it.each([
{ packageName: "@openclaw/matrix", id: "matrix", expected: true },
{ packageName: "@openclaw/matrix", id: "@openclaw/matrix", expected: true },
{ packageName: "@openclaw/matrix", id: "signal", expected: false },
{ packageName: " ", id: "matrix", expected: false },
{ packageName: "@openclaw/matrix", id: " ", expected: false },
])("matches ids for %j", ({ packageName, id, expected }) => {
expect(packageNameMatchesId(packageName, id)).toBe(expected);
});
});
describe("safeDirName", () => {
it.each([
{ value: " matrix ", expected: "matrix" },

View File

@ -11,6 +11,20 @@ export function unscopedPackageName(name: string): string {
return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
}
export function packageNameMatchesId(packageName: string, id: string): boolean {
const trimmedId = id.trim();
if (!trimmedId) {
return false;
}
const trimmedPackageName = packageName.trim();
if (!trimmedPackageName) {
return false;
}
return trimmedId === trimmedPackageName || trimmedId === unscopedPackageName(trimmedPackageName);
}
export function safeDirName(input: string): string {
const trimmed = input.trim();
if (!trimmed) {

View File

@ -264,12 +264,20 @@ async function installFromFileWithWarnings(params: { extensionsDir: string; file
return { result, warnings };
}
function setupManifestInstallFixture(params: { manifestId: string }) {
function setupManifestInstallFixture(params: { manifestId: string; packageName?: string }) {
const caseDir = makeTempDir();
const stateDir = path.join(caseDir, "state");
const pluginDir = path.join(caseDir, "plugin-src");
fs.mkdirSync(stateDir, { recursive: true });
fs.cpSync(manifestInstallTemplateDir, pluginDir, { recursive: true });
if (params.packageName) {
const packageJsonPath = path.join(pluginDir, "package.json");
const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
name?: string;
};
manifest.name = params.packageName;
fs.writeFileSync(packageJsonPath, JSON.stringify(manifest), "utf-8");
}
fs.writeFileSync(
path.join(pluginDir, "openclaw.plugin.json"),
JSON.stringify({
@ -1080,6 +1088,23 @@ describe("installPluginFromDir", () => {
).toBe(true);
});
it("does not warn when a scoped npm package name matches the manifest id", async () => {
const { pluginDir, extensionsDir } = setupManifestInstallFixture({
manifestId: "matrix",
packageName: "@openclaw/matrix",
});
const infoMessages: string[] = [];
const res = await installPluginFromDir({
dirPath: pluginDir,
extensionsDir,
logger: { info: (msg: string) => infoMessages.push(msg), warn: () => {} },
});
expectInstalledWithPluginId(res, extensionsDir, "matrix");
expect(infoMessages.some((msg) => msg.includes("differs from npm package name"))).toBe(false);
});
it.each([
{
name: "manifest id wins for scoped plugin ids",

View File

@ -1,6 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import {
packageNameMatchesId,
resolveSafeInstallDir,
safeDirName,
safePathSegmentHashed,
@ -546,7 +547,7 @@ async function installPluginFromPackageDir(
};
}
if (manifestPluginId && manifestPluginId !== npmPluginId) {
if (manifestPluginId && !packageNameMatchesId(npmPluginId, manifestPluginId)) {
logger.info?.(
`Plugin manifest id "${manifestPluginId}" differs from npm package name "${npmPluginId}"; using manifest id as the config key.`,
);