fix(plugins): repair stale managed openclaw peers#78251
Conversation
|
Codex review: found issues before merge. Summary Reproducibility: no. not as a high-confidence live reproduction in this read-only review. Source inspection shows current main can install/update npm plugins in a managed root without stale root Real behavior proof Next step before merge Security Review findings
Review detailsBest possible solution: Use #78291 as the canonical landing target, or fold its hidden-lock, bin-shim, and peer-link reassertion coverage back into this branch before merging. Do we have a high-confidence way to reproduce the issue? No, not as a high-confidence live reproduction in this read-only review. Source inspection shows current main can install/update npm plugins in a managed root without stale root Is this the best way to solve the issue? No for landing this exact branch as-is. The repair is in the right code path, but #78291 supersedes it and covers additional known cleanup and peer-link surfaces that this branch misses. Full review comments:
Overall correctness: patch is incorrect Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 627b0073f2f7. Re-review progress:
|
97c6830 to
a47973d
Compare
3eeaf7b to
5468e9b
Compare
|
Superseded by #78291, which is now merged on #78291 carries this stale managed-root peer repair forward and broadens it to remove stale managed-root |
Summary
openclawinstalls inside OpenClaw-managed npm plugin roots before updating npm-installed pluginsnode_modules/openclawby running managed npm-root installs with--omit=peer@betaonly falls back to stable when the beta package/tag is missing, not when npm reports a real install failure likeERESOLVERCA
Externalized official plugins can live in
~/.openclaw/npmor another OpenClaw-managed npm root. Some legacy roots ended up withopenclawinstalled at the root of that managed npm project, either in root manifest fields, the package lock, ornode_modules/openclaw.The likely origination path is an external plugin that declared
openclawas a peer dependency. With modern npm, a peer dependency can be materialized during install unless we explicitly omit peers. That can create root-levelnode_modules/openclawin the managed npm project even though OpenClaw already has an explicit host-peer symlink path for plugins.That stale root package is poison for plugin updates. When a user moves from stable to beta, npm can resolve the plugin candidate correctly, then fail because the managed npm root still appears to contain an older
openclawpackage. In the bad box, this showed up as plugin beta update failure and fallback to the stable package, leaving Discord/WhatsApp/Codex-style externalized plugins behind even though the core OpenClaw package updated.The old updater made that worse by treating every failed beta npm install as "beta unavailable" and falling back to the untagged/stable package. That is okay for a missing beta dist-tag. It is wrong for peer-resolution failures, malformed packages, or other real install errors.
Fix
The installer now prevents the normal peer path from creating root poison: managed npm-root plugin installs include
--omit=peeras well as--omit=dev. OpenClaw then owns theopenclawpeer through its existing host symlink, so plugin packages can still declarepeerDependencies.openclawwithout npm installing a second OpenClaw copy into the managed root.The install path is still intentionally non-blocking. We do not reject plugins that declare
openclaw. If an installed package declaresopenclawin peer, direct, or optional dependencies, the installed package gets linked to the host OpenClaw package before dependency-tree scanning when it came from the managed npm root. That avoids stranding users with a blocked install just because npm or a package manifest tried to materialize OpenClaw locally.The installer also inspects the managed npm root before and after installing any non-
openclawnpm plugin. If it finds stale root-levelopenclawstate, it first uses native npm cleanup commands from the managed root:npm uninstall openclawandnpm prune. Only if native cleanup leaves poison behind does it fall back to directly removing the root manifest/lock entries and quarantining stale filesystem payloads.The repair is conservative. It refuses bare OpenClaw installs, roots overlapping the running OpenClaw package, and pnpm workspaces. It also refuses unknown custom roots unless the root is the default managed npm dir, came from an existing npm install record, or already has the managed-root marker. That keeps the repair from mutating a user's unrelated npm project while still fixing old OpenClaw-managed plugin roots retroactively.
Update now derives the original npm root from each npm install record and passes that root back into install/update. That means users who installed official externalized plugins into a non-default OpenClaw-managed root do not get silently redirected to the default root during update.
Beta fallback is now limited to missing-package/missing-tag cases (
npm_package_not_found,ETARGET,notarget, missing dist-tag/tag text). Real beta install failures now surface as errors so we do not mask broken state by downgrading the plugin.Coverage
Happy paths covered:
openclawexists--omit=peer, so peer dependencies do not materialize rootopenclawopenclawwhile preserving plugin packagesNon-happy paths covered:
openclawindependencies,optionalDependencies,peerDependencies, root lock metadata, lock-only state, and filesystem-only stateopenclaw: post-install repair removes the root poison and links the plugin to the host OpenClaw packagenode_modules/openclawis not treated as root poisonopenclawnpm roots and pnpm workspaces are skipped instead of mutatedERESOLVEpeer-resolution failures do not fall back to stableVerification
pnpm exec oxfmt --check --threads=1 src/infra/safe-package-install.ts src/infra/safe-package-install.test.ts src/plugins/install.ts src/plugins/install.npm-spec.test.tspnpm exec oxlint --threads=1 src/infra/safe-package-install.ts src/infra/safe-package-install.test.ts src/plugins/install.ts src/plugins/install.npm-spec.test.tsgit diff --checktbx_01kqxy0ntk8s446ynqa06a53re:pnpm test:serial src/infra/safe-package-install.test.ts src/infra/npm-managed-root.test.ts src/plugins/install.npm-spec.test.ts src/plugins/install.npm-spec.e2e.test.ts src/plugins/update.test.ts(117 tests)tbx_01kqxy0ntk8s446ynqa06a53re:pnpm check:changed(lanes: core, coreTests, docs)