mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:00:42 +00:00
fix(plugins): prune old runtime deps package roots
This commit is contained in:
@@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Discord/voice: run voice-channel turns under a voice-output policy that hides the agent `tts` tool and asks for spoken reply text, so `/vc join` sessions synthesize and play agent replies instead of ending with `NO_REPLY`. Fixes #61536. Thanks @aounakram.
|
||||
- Plugins/runtime-deps: prune inactive same-package versioned runtime-deps roots after bundled dependency repair, so upgrades do not leave old `openclaw-<version>-<hash>` package caches behind after doctor runs. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: prune legacy version-scoped plugin runtime-deps roots during bundled dependency repair and cover the path in Package Acceptance's upgrade-survivor matrix, so upgrades from 2026.4.x no longer leave stale per-plugin runtime trees after doctor runs. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: keep Gateway startup plugin imports and runtime plugin fallback loads verify-only after startup/config repair planning, so packaged installs no longer spawn package-manager repair from hot paths after readiness. Refs #75283 and #75069. Thanks @brokemac79 and @xiaohuaxi.
|
||||
- Voice Call/realtime: add default-off fast memory/session context for `openclaw_agent_consult`, giving live calls a bounded answer-or-miss path before the full agent consult. Fixes #71849. Thanks @amzzzzzzz.
|
||||
|
||||
@@ -269,6 +269,57 @@ export function listSiblingExternalBundledRuntimeDepsRoots(params: {
|
||||
.map((entry) => entry.root);
|
||||
}
|
||||
|
||||
export function pruneSiblingExternalBundledRuntimeDepsRoots(params: {
|
||||
installRoot: string;
|
||||
nowMs?: number;
|
||||
warn?: (message: string) => void;
|
||||
}): { scanned: number; removed: number; skippedLocked: number } {
|
||||
const installRoot = path.resolve(params.installRoot);
|
||||
const installRootHash = readPackageKeyPathHash(path.basename(installRoot));
|
||||
if (!installRootHash) {
|
||||
return { scanned: 0, removed: 0, skippedLocked: 0 };
|
||||
}
|
||||
const parentDir = path.dirname(installRoot);
|
||||
const nowMs = params.nowMs ?? Date.now();
|
||||
let entries: fs.Dirent[];
|
||||
try {
|
||||
entries = fs.readdirSync(parentDir, { withFileTypes: true });
|
||||
} catch {
|
||||
return { scanned: 0, removed: 0, skippedLocked: 0 };
|
||||
}
|
||||
|
||||
let scanned = 0;
|
||||
let removed = 0;
|
||||
let skippedLocked = 0;
|
||||
for (const entry of entries) {
|
||||
if (
|
||||
!entry.isDirectory() ||
|
||||
!entry.name.startsWith("openclaw-") ||
|
||||
readPackageKeyPathHash(entry.name) !== installRootHash
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const root = path.join(parentDir, entry.name);
|
||||
if (path.resolve(root) === installRoot) {
|
||||
continue;
|
||||
}
|
||||
scanned += 1;
|
||||
const lockDir = path.join(root, BUNDLED_RUNTIME_DEPS_LOCK_DIR);
|
||||
if (fs.existsSync(lockDir) && !removeRuntimeDepsLockIfStale(lockDir, nowMs)) {
|
||||
skippedLocked += 1;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
removed += 1;
|
||||
} catch (error) {
|
||||
params.warn?.(`failed to remove sibling bundled runtime deps root ${root}: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { scanned, removed, skippedLocked };
|
||||
}
|
||||
|
||||
function readPackageKeyPathHash(packageKey: string): string | null {
|
||||
return PACKAGE_KEY_PATH_HASH_RE.exec(packageKey)?.[1] ?? null;
|
||||
}
|
||||
|
||||
@@ -1860,6 +1860,7 @@ describe("createBundledRuntimeDepsPackagePlan config policy", () => {
|
||||
]);
|
||||
expect(fs.lstatSync(path.join(installRoot, "node_modules")).isSymbolicLink()).toBe(true);
|
||||
expect(isRuntimeDepsPlanMaterialized(installRoot, ["alpha-runtime@1.0.0"])).toBe(true);
|
||||
expect(fs.existsSync(previousRoot)).toBe(true);
|
||||
expect(JSON.parse(fs.readFileSync(path.join(installRoot, "package.json"), "utf8"))).toEqual({
|
||||
name: "openclaw-runtime-deps-install",
|
||||
private: true,
|
||||
@@ -1904,6 +1905,7 @@ describe("createBundledRuntimeDepsPackagePlan config policy", () => {
|
||||
},
|
||||
]);
|
||||
expect(fs.lstatSync(path.join(installRoot, "node_modules")).isSymbolicLink()).toBe(false);
|
||||
expect(fs.existsSync(previousRoot)).toBe(true);
|
||||
});
|
||||
|
||||
it("does not create a reuse symlink when an earlier configured layer already satisfies the plan", async () => {
|
||||
@@ -1974,6 +1976,7 @@ describe("createBundledRuntimeDepsPackagePlan config policy", () => {
|
||||
},
|
||||
]);
|
||||
expect(fs.lstatSync(path.join(installRoot, "node_modules")).isSymbolicLink()).toBe(false);
|
||||
expect(fs.existsSync(previousRoot)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not reuse a compatible external runtime deps root from a different package key", async () => {
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import {
|
||||
isSourceCheckoutRoot,
|
||||
listSiblingExternalBundledRuntimeDepsRoots,
|
||||
pruneSiblingExternalBundledRuntimeDepsRoots,
|
||||
pruneUnknownBundledRuntimeDepsRoots,
|
||||
resolveBundledRuntimeDependencyInstallRootPlan,
|
||||
resolveBundledRuntimeDependencyPackageInstallRootPlan,
|
||||
@@ -404,6 +405,10 @@ export async function repairBundledRuntimeDepsPackagePlanAsync(params: {
|
||||
});
|
||||
const plan = createBundledRuntimeDepsPackagePlan(params);
|
||||
if (plan.missingSpecs.length === 0) {
|
||||
pruneSiblingExternalBundledRuntimeDepsRoots({
|
||||
installRoot: plan.installRootPlan.installRoot,
|
||||
...(params.warn ? { warn: params.warn } : {}),
|
||||
});
|
||||
return { plan, repairedSpecs: [] };
|
||||
}
|
||||
const reuseResult = withBundledRuntimeDepsInstallRootLock(plan.installRootPlan.installRoot, () =>
|
||||
@@ -416,6 +421,12 @@ export async function repairBundledRuntimeDepsPackagePlanAsync(params: {
|
||||
);
|
||||
if (reuseResult) {
|
||||
const refreshedPlan = createBundledRuntimeDepsPackagePlan(params);
|
||||
if (reuseResult.status === "materialized") {
|
||||
pruneSiblingExternalBundledRuntimeDepsRoots({
|
||||
installRoot: refreshedPlan.installRootPlan.installRoot,
|
||||
...(params.warn ? { warn: params.warn } : {}),
|
||||
});
|
||||
}
|
||||
return {
|
||||
plan: refreshedPlan,
|
||||
repairedSpecs: [],
|
||||
@@ -442,6 +453,10 @@ export async function repairBundledRuntimeDepsPackagePlanAsync(params: {
|
||||
...(params.onProgress ? { onProgress: params.onProgress } : {}),
|
||||
...(params.warn ? { warn: params.warn } : {}),
|
||||
});
|
||||
pruneSiblingExternalBundledRuntimeDepsRoots({
|
||||
installRoot: plan.installRootPlan.installRoot,
|
||||
...(params.warn ? { warn: params.warn } : {}),
|
||||
});
|
||||
return { plan, repairedSpecs: result.installSpecs };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user