fix(plugins): clarify installed plugin replacement

This commit is contained in:
Peter Steinberger
2026-04-23 01:24:48 +01:00
parent fa43cbfcba
commit 87a64a33f1
4 changed files with 29 additions and 1 deletions

View File

@@ -36,7 +36,7 @@ Docs: https://docs.openclaw.ai
- Providers/Amazon Bedrock Mantle: refresh IAM-backed bearer tokens at runtime instead of baking discovery-time tokens into provider config, so long-lived Mantle sessions keep working after the initial token ages out. Thanks @wirjo.
- Config/includes: write through single-file top-level includes for isolated OpenClaw-owned mutations, so `plugins install` and `plugins update` update an included `plugins.json5` file instead of flattening modular `$include` configs. Fixes #41050 and #66048.
- Config/reload: plan gateway reloads from source-authored config instead of runtime-materialized snapshots, so plugin update writes no longer trigger false restarts from derived provider/plugin config paths. Fixes #68732.
- Plugins/update: skip npm plugin reinstall/config rewrites when the installed version and recorded artifact identity already match the registry target, and let bare npm package names resolve back to tracked install records. Fixes #46955 and #67957.
- Plugins/update: skip npm plugin reinstall/config rewrites when the installed version and recorded artifact identity already match the registry target, let bare npm package names resolve back to tracked install records, and point already-installed `plugins install` attempts at `plugins update` / `--force` instead of a hook-pack fallback. Fixes #46955, #67957, and #68073.
- Agents/MCP: keep `mcp.servers` and bundle MCP tools available in Pi embedded
`coding` and `messaging` sessions while preserving `minimal` profile and
`tools.deny: ["bundle-mcp"]` opt-out behavior. Fixes #68875 and #68818.

View File

@@ -76,6 +76,8 @@ bundled-plugin recovery path for plugins that explicitly opt into
`--force` reuses the existing install target and overwrites an already-installed
plugin or hook pack in place. Use it when you are intentionally reinstalling
the same id from a new local path, archive, ClawHub package, or npm artifact.
For routine upgrades of an already tracked npm plugin, prefer
`openclaw plugins update <id-or-npm-spec>`.
`--pin` applies to npm installs only. It is not supported with `--marketplace`,
because marketplace installs persist marketplace source metadata instead of an

View File

@@ -678,6 +678,29 @@ describe("plugins cli install", () => {
);
});
it("suggests update or --force when npm plugin install target already exists", async () => {
loadConfig.mockReturnValue({} as OpenClawConfig);
mockClawHubPackageNotFound("@example/lossless-claw");
installPluginFromNpmSpec.mockResolvedValue({
ok: false,
error:
"plugin already exists: /home/openclaw/.openclaw/extensions/lossless-claw (delete it first)",
});
installHooksFromNpmSpec.mockResolvedValue({
ok: false,
error: "package.json missing openclaw.hooks",
});
await expect(
runPluginsCommand(["plugins", "install", "@example/lossless-claw"]),
).rejects.toThrow("__exit__:1");
expect(runtimeErrors.at(-1)).toContain(
"Use `openclaw plugins update <id-or-npm-spec>` to upgrade the tracked plugin, or rerun install with `--force` to replace it.",
);
expect(runtimeErrors.at(-1)).not.toContain("Also not a valid hook pack");
});
it("passes the install logger to the --link dry-run probe", async () => {
const localPluginDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-link-plugin-"));
const cfg = {

View File

@@ -103,6 +103,9 @@ export function formatPluginInstallWithHookFallbackError(
pluginError: string,
hookError: string,
): string {
if (/plugin already exists: .+ \(delete it first\)/.test(pluginError)) {
return `${pluginError}\nUse \`openclaw plugins update <id-or-npm-spec>\` to upgrade the tracked plugin, or rerun install with \`--force\` to replace it.`;
}
return `${pluginError}\nAlso not a valid hook pack: ${hookError}`;
}