mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 14:00:42 +00:00
fix(plugins): resolve official plugin install aliases
Resolve bare official external plugin IDs through the official catalog before generic npm fallback, preserving explicit npm semantics and catalog integrity through the hook-pack fallback.\n\nFixes #76373.\n\nThanks @bek91 and @vincentkoc.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
applyExclusiveSlotSelection,
|
||||
buildPluginSnapshotReport,
|
||||
enablePluginInConfig,
|
||||
findBundledPluginSourceMock,
|
||||
installHooksFromNpmSpec,
|
||||
installHooksFromPath,
|
||||
installPluginFromClawHub,
|
||||
@@ -652,7 +653,91 @@ describe("plugins cli install", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("installs bare plugin specs through npm without ClawHub lookup", async () => {
|
||||
it("resolves exact official external plugin ids through their npm package", async () => {
|
||||
const cfg = createEmptyPluginConfig();
|
||||
const enabledCfg = createEnabledPluginConfig("brave");
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
findBundledPluginSourceMock.mockReturnValue(undefined);
|
||||
installPluginFromNpmSpec.mockResolvedValue(createNpmPluginInstallResult("brave"));
|
||||
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
|
||||
applyExclusiveSlotSelection.mockReturnValue({
|
||||
config: enabledCfg,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "install", "brave"]);
|
||||
|
||||
expect(findBundledPluginSourceMock).toHaveBeenCalledWith({
|
||||
lookup: { kind: "pluginId", value: "brave" },
|
||||
});
|
||||
expect(installPluginFromClawHub).not.toHaveBeenCalled();
|
||||
expect(installPluginFromNpmSpec).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "@openclaw/brave-plugin",
|
||||
expectedPluginId: "brave",
|
||||
}),
|
||||
);
|
||||
expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
|
||||
brave: expect.objectContaining({
|
||||
source: "npm",
|
||||
spec: "@openclaw/brave-plugin",
|
||||
installPath: cliInstallPath("brave"),
|
||||
version: "1.2.3",
|
||||
}),
|
||||
});
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
|
||||
});
|
||||
|
||||
it("passes official external catalog integrity to npm installs", async () => {
|
||||
const cfg = createEmptyPluginConfig();
|
||||
const enabledCfg = createEnabledPluginConfig("wecom");
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
findBundledPluginSourceMock.mockReturnValue(undefined);
|
||||
installPluginFromNpmSpec.mockResolvedValue(createNpmPluginInstallResult("wecom"));
|
||||
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
|
||||
applyExclusiveSlotSelection.mockReturnValue({
|
||||
config: enabledCfg,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "install", "wecom"]);
|
||||
|
||||
expect(installPluginFromNpmSpec).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "@wecom/wecom-openclaw-plugin@2026.4.23",
|
||||
expectedPluginId: "wecom",
|
||||
expectedIntegrity:
|
||||
"sha512-bnzfdIEEu1/LFvcdyjaTkyxt27w6c7dqhkPezU62OWaqmcdFsUGR3T55USK/O9pIKsNcnL1Tnu1pqKYCWHFgWQ==",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes official external catalog integrity to hook-pack fallback", async () => {
|
||||
loadConfig.mockReturnValue(createEmptyPluginConfig());
|
||||
findBundledPluginSourceMock.mockReturnValue(undefined);
|
||||
installPluginFromNpmSpec.mockResolvedValue({
|
||||
ok: false,
|
||||
error: "package.json missing openclaw.extensions",
|
||||
code: "missing_openclaw_extensions",
|
||||
});
|
||||
installHooksFromNpmSpec.mockResolvedValue({
|
||||
ok: false,
|
||||
error:
|
||||
"aborted: npm package integrity drift detected for @wecom/wecom-openclaw-plugin@2026.4.23",
|
||||
});
|
||||
|
||||
await expect(runPluginsCommand(["plugins", "install", "wecom"])).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(installHooksFromNpmSpec).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "@wecom/wecom-openclaw-plugin@2026.4.23",
|
||||
expectedIntegrity:
|
||||
"sha512-bnzfdIEEu1/LFvcdyjaTkyxt27w6c7dqhkPezU62OWaqmcdFsUGR3T55USK/O9pIKsNcnL1Tnu1pqKYCWHFgWQ==",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("installs ordinary bare plugin specs through npm without ClawHub lookup", async () => {
|
||||
const cfg = createEmptyPluginConfig();
|
||||
const enabledCfg = createEnabledPluginConfig("demo");
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
@@ -735,6 +820,34 @@ describe("plugins cli install", () => {
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
|
||||
});
|
||||
|
||||
it("keeps npm-prefixed official plugin ids on explicit npm semantics", async () => {
|
||||
const cfg = createEmptyPluginConfig();
|
||||
const enabledCfg = createEnabledPluginConfig("brave");
|
||||
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
installPluginFromNpmSpec.mockResolvedValue(createNpmPluginInstallResult("brave"));
|
||||
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
|
||||
recordPluginInstall.mockReturnValue(enabledCfg);
|
||||
applyExclusiveSlotSelection.mockReturnValue({
|
||||
config: enabledCfg,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "install", "npm:brave"]);
|
||||
|
||||
expect(installPluginFromNpmSpec).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "brave",
|
||||
}),
|
||||
);
|
||||
expect(installPluginFromNpmSpec).toHaveBeenCalledWith(
|
||||
expect.not.objectContaining({
|
||||
expectedPluginId: "brave",
|
||||
}),
|
||||
);
|
||||
expect(installPluginFromClawHub).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("passes the active profile extensions dir to npm installs", async () => {
|
||||
const extensionsDir = useProfileExtensionsDir();
|
||||
const cfg = createEmptyPluginConfig();
|
||||
|
||||
Reference in New Issue
Block a user