From 2a54427aba8230afbffda0dc2f79d21545285df1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 30 Apr 2026 14:51:50 +0100 Subject: [PATCH] fix(plugins): keep runtime deps manifest complete Co-authored-by: HCL --- CHANGELOG.md | 1 + .../bundled-runtime-deps-materialization.ts | 2 +- src/plugins/bundled-runtime-deps.test.ts | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db8a32bb90c..4a35c445c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai - Slack: require bot-authored room messages with `allowBots=true` to come from an explicitly channel-allowlisted bot or from a room where an explicit Slack owner is present, so broad bot relays cannot run unattended. Fixes #59284. Thanks @andrewhong-translucent. - CLI/progress: suppress nested progress spinners and line clears while TUI input owns raw stdin, so Crestodian `/status` no longer disturbs the active input row. (#75003) Thanks @velvet-shark. - Models/OpenAI Codex: restore `openai-codex/gpt-5.4-mini` for ChatGPT/Codex OAuth PI runs after live OAuth proof, and align the manifest, forward-compat metadata, docs, and regression tests so stale cron and heartbeat configs resolve again. Fixes #74451. Thanks @0xCyda, @hclsys, and @Marvae. +- Plugins/runtime-deps: always write a dependency map in generated runtime-deps install manifests, so npm does not crash or prune staged bundled-plugin packages when the plan is empty. Fixes #74949. Thanks @hclsys. - Telegram: use durable message edits for streaming previews instead of native draft state, so generated replies no longer flicker through draft-to-message transitions that look like duplicates. (#75073) Thanks @obviyus. ## 2026.4.29 diff --git a/src/plugins/bundled-runtime-deps-materialization.ts b/src/plugins/bundled-runtime-deps-materialization.ts index ea7bf4becfc..cfbfbe2fd01 100644 --- a/src/plugins/bundled-runtime-deps-materialization.ts +++ b/src/plugins/bundled-runtime-deps-materialization.ts @@ -166,7 +166,7 @@ function createNpmInstallExecutionManifest(installSpecs: readonly string[]): Jso return { name: "openclaw-runtime-deps-install", private: true, - ...(Object.keys(sortedDependencies).length > 0 ? { dependencies: sortedDependencies } : {}), + dependencies: sortedDependencies, }; } diff --git a/src/plugins/bundled-runtime-deps.test.ts b/src/plugins/bundled-runtime-deps.test.ts index 73741a6c2f7..5735ee31a78 100644 --- a/src/plugins/bundled-runtime-deps.test.ts +++ b/src/plugins/bundled-runtime-deps.test.ts @@ -10,7 +10,10 @@ import { getActiveBundledRuntimeDepsInstallCount, waitForBundledRuntimeDepsInstallIdle, } from "./bundled-runtime-deps-activity.js"; -import { assertBundledRuntimeDepsInstalled } from "./bundled-runtime-deps-materialization.js"; +import { + assertBundledRuntimeDepsInstalled, + ensureNpmInstallExecutionManifest, +} from "./bundled-runtime-deps-materialization.js"; import { __testing as bundledRuntimeDepsTesting, createBundledRuntimeDependencyAliasMap, @@ -545,6 +548,18 @@ describe("installBundledRuntimeDeps", () => { ); }); + it("always includes a dependencies field in the install manifest, even when specs are empty", () => { + const installRoot = makeTempDir(); + + ensureNpmInstallExecutionManifest(installRoot, []); + + const written = JSON.parse(fs.readFileSync(path.join(installRoot, "package.json"), "utf8")) as { + dependencies?: unknown; + }; + expect(written).toHaveProperty("dependencies"); + expect(written.dependencies).toEqual({}); + }); + it("repairs external install roots from the complete generated dependency plan", async () => { const installRoot = makeTempDir(); writeInstalledPackage(installRoot, "alpha-runtime", "1.0.0");