fix(plugins): use manifest id as config entry key instead of npm package name (#24796)

* fix(plugins): use manifest id as config key instead of npm package name

Plugin manifests (openclaw.plugin.json) define a canonical 'id' field that
is used as the authoritative plugin identifier by the manifest registry.
However, the install command was deriving the config entry key from the npm
package name (e.g. 'cognee-openclaw') rather than the manifest id (e.g.
'memory-cognee'), causing a latent mismatch.

On the next gateway reload the plugin could not be found under the config key
derived from the npm package name, causing 'plugin not found' errors and
potentially shutting the gateway down.

Fix: after extracting the package directory, read openclaw.plugin.json and
prefer its 'id' field over the npm package name when registering the config
entry. Falls back to the npm-derived id if the manifest file is absent or
has no valid id. A diagnostic info message is emitted when the two values
differ so the mismatch is visible in the install log.

The update path (src/plugins/update.ts) already correctly reads the manifest
id and is unaffected.

Fixes #24429

* fix: format plugin install manifest-id path (#24796)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
zerone0x
2026-02-24 11:33:51 +08:00
committed by GitHub
parent f5cab29ec7
commit bf91b347c1

View File

@@ -26,6 +26,7 @@ import { validateRegistryNpmSpec } from "../infra/npm-registry-spec.js";
import { extensionUsesSkippedScannerPath, isPathInside } from "../security/scan-paths.js";
import * as skillScanner from "../security/skill-scanner.js";
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
import { loadPluginManifest } from "./manifest.js";
type PluginInstallLogger = {
info?: (message: string) => void;
@@ -149,7 +150,17 @@ async function installPluginFromPackageDir(params: {
}
const pkgName = typeof manifest.name === "string" ? manifest.name : "";
const pluginId = pkgName ? unscopedPackageName(pkgName) : "plugin";
const npmPluginId = pkgName ? unscopedPackageName(pkgName) : "plugin";
// Prefer the canonical `id` from openclaw.plugin.json over the npm package name.
// This avoids a latent key-mismatch bug: if the manifest id (e.g. "memory-cognee")
// differs from the npm package name (e.g. "cognee-openclaw"), the plugin registry
// uses the manifest id as the authoritative key, so the config entry must match it.
const ocManifestResult = loadPluginManifest(params.packageDir);
const manifestPluginId =
ocManifestResult.ok && ocManifestResult.manifest.id ? ocManifestResult.manifest.id : undefined;
const pluginId = manifestPluginId ?? npmPluginId;
const pluginIdError = validatePluginId(pluginId);
if (pluginIdError) {
return { ok: false, error: pluginIdError };
@@ -161,6 +172,12 @@ async function installPluginFromPackageDir(params: {
};
}
if (manifestPluginId && manifestPluginId !== npmPluginId) {
logger.info?.(
`Plugin manifest id "${manifestPluginId}" differs from npm package name "${npmPluginId}"; using manifest id as the config key.`,
);
}
const packageDir = path.resolve(params.packageDir);
const forcedScanEntries: string[] = [];
for (const entry of extensions) {