diff --git a/CHANGELOG.md b/CHANGELOG.md index fab7a9ad243..21ceba2ac81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai - Channels/Telegram: normalize accidental full `/bot` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris. - Zalo Personal: persist refreshed `zca-js` session cookies after QR login, session restore, and successful API calls so gateway restarts restore the freshest local session. (#73277) Thanks @darkamenosa. - Logging/security: redact sensitive tokens (sk-\* keys, Bearer/Authorization values, etc.) at the subsystem console sink so `createSubsystemLogger().info/warn/error` output that bypasses the patched console-capture handler still applies the same redaction the file transport already does. Fixes #73284; refs #67953 and #64046. Thanks @edwin-rivera-dev. +- Plugins/runtime deps: reuse enclosing versioned cache roots when bundled plugins resolve from nested staged paths, so plugin-runtime-deps no longer mints `openclaw-unknown-*` directories or loops on `ENOTEMPTY`. Fixes #72956. (#73205) Thanks @SymbolStar. ## 2026.4.27 diff --git a/src/plugins/bundled-runtime-deps.test.ts b/src/plugins/bundled-runtime-deps.test.ts index 70b9d911eef..4ffb3144833 100644 --- a/src/plugins/bundled-runtime-deps.test.ts +++ b/src/plugins/bundled-runtime-deps.test.ts @@ -1696,7 +1696,7 @@ describe("ensureBundledPluginRuntimeDeps", () => { ).toEqual({ installedSpecs: [], retainSpecs: [] }); }); - it("resolves nested cache pluginRoot to enclosing versioned cache (regression for #72956)", () => { + it("resolves nested cache pluginRoot to enclosing versioned cache", () => { const packageRoot = makeTempDir(); const stageDir = makeTempDir(); fs.writeFileSync( @@ -1712,11 +1712,6 @@ describe("ensureBundledPluginRuntimeDeps", () => { const env = { OPENCLAW_PLUGIN_STAGE_DIR: stageDir }; const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, { env }); - // Simulate a deeply-nested pluginRoot inside the existing cache directory - // (e.g. plugin-sdk loaded as a transitive dep). The path no longer matches - // `/dist/extensions/`, so resolveBundledPluginPackageRoot() - // returns null and the caller previously fell back to the raw pluginRoot, - // generating a self-referential `openclaw-unknown-*` cache directory. const nestedPluginRoot = path.join( installRoot, "dist", diff --git a/src/plugins/bundled-runtime-deps.ts b/src/plugins/bundled-runtime-deps.ts index 324eb3eae1e..521f52602f0 100644 --- a/src/plugins/bundled-runtime-deps.ts +++ b/src/plugins/bundled-runtime-deps.ts @@ -980,12 +980,6 @@ function resolveExistingExternalBundledRuntimeDepsRoots(params: { if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) { continue; } - // Accept both `/` and any descendant such as - // `//dist/extensions/node_modules/openclaw/plugin-sdk`. - // Without this, when a bundled package re-enters resolution via a nested - // `pluginRoot` (e.g. plugin-sdk loaded as a dependency), the caller falls - // back to `params.pluginRoot`, which lacks a `package.json`, producing a - // self-referential `openclaw-unknown-*` cache directory (#72956). const packageKey = relative.split(path.sep)[0]; if (!packageKey || !packageKey.startsWith("openclaw-")) { continue;