fix(plugins): preserve package deps for runtime mirrors

This commit is contained in:
Peter Steinberger
2026-04-25 12:50:49 +01:00
parent 63241bf1e0
commit d2ab6b4fd5
6 changed files with 176 additions and 41 deletions

View File

@@ -1,6 +1,5 @@
import { createHash } from "node:crypto";
import fs from "node:fs";
import { Module } from "node:module";
import path from "node:path";
import {
clearAgentHarnesses,
@@ -33,9 +32,12 @@ import { resolvePluginActivationSourceConfig } from "./activation-source-config.
import { buildPluginApi } from "./api-builder.js";
import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js";
import {
clearBundledRuntimeDependencyNodePaths,
ensureBundledPluginRuntimeDeps,
installBundledRuntimeDeps,
resolveBundledRuntimeDependencyInstallRoot,
resolveBundledRuntimeDependencyPackageRoot,
registerBundledRuntimeDependencyNodePath,
type BundledRuntimeDepsInstallParams,
} from "./bundled-runtime-deps.js";
import {
@@ -255,6 +257,7 @@ export function clearPluginLoaderCache(): void {
inFlightPluginRegistryLoads.clear();
openAllowlistWarningCache.clear();
clearBundledRuntimeDependencyNodePaths();
registeredBundledRuntimeDepMirrorRoots.clear();
clearAgentHarnesses();
clearPluginCommands();
clearCompactionProviders();
@@ -484,12 +487,11 @@ function resolveCanonicalDistRuntimeSource(source: string): string {
return fs.existsSync(candidate) ? candidate : source;
}
const registeredBundledRuntimeDepNodePaths = new Set<string>();
const registeredBundledRuntimeDepMirrorRoots = new Set<string>();
function isBundledRuntimeDependencyMirrorPath(modulePath: string): boolean {
const resolvedModulePath = path.resolve(modulePath);
for (const nodeModulesDir of registeredBundledRuntimeDepNodePaths) {
const installRoot = path.dirname(nodeModulesDir);
for (const installRoot of registeredBundledRuntimeDepMirrorRoots) {
if (
resolvedModulePath === installRoot ||
resolvedModulePath.startsWith(`${installRoot}${path.sep}`)
@@ -500,37 +502,8 @@ function isBundledRuntimeDependencyMirrorPath(modulePath: string): boolean {
return false;
}
function registerBundledRuntimeDependencyNodePath(installRoot: string): void {
const nodeModulesDir = path.join(installRoot, "node_modules");
if (registeredBundledRuntimeDepNodePaths.has(nodeModulesDir) || !fs.existsSync(nodeModulesDir)) {
return;
}
const currentPaths = (process.env.NODE_PATH ?? "")
.split(path.delimiter)
.map((entry) => entry.trim())
.filter((entry) => entry.length > 0);
process.env.NODE_PATH = [
nodeModulesDir,
...currentPaths.filter((entry) => entry !== nodeModulesDir),
].join(path.delimiter);
(Module as unknown as { _initPaths?: () => void })._initPaths?.();
registeredBundledRuntimeDepNodePaths.add(nodeModulesDir);
}
function clearBundledRuntimeDependencyNodePaths(): void {
if (registeredBundledRuntimeDepNodePaths.size === 0) {
return;
}
const retainedPaths = (process.env.NODE_PATH ?? "")
.split(path.delimiter)
.filter((entry) => entry.length > 0 && !registeredBundledRuntimeDepNodePaths.has(entry));
if (retainedPaths.length > 0) {
process.env.NODE_PATH = retainedPaths.join(path.delimiter);
} else {
delete process.env.NODE_PATH;
}
registeredBundledRuntimeDepNodePaths.clear();
(Module as unknown as { _initPaths?: () => void })._initPaths?.();
function registerBundledRuntimeDependencyMirrorRoot(installRoot: string): void {
registeredBundledRuntimeDepMirrorRoots.add(path.resolve(installRoot));
}
function mirrorBundledPluginRuntimeRoot(params: {
@@ -2382,7 +2355,12 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
}
}
if (path.resolve(installRoot) !== path.resolve(pluginRoot)) {
const packageRoot = resolveBundledRuntimeDependencyPackageRoot(pluginRoot);
if (packageRoot) {
registerBundledRuntimeDependencyNodePath(packageRoot);
}
registerBundledRuntimeDependencyNodePath(installRoot);
registerBundledRuntimeDependencyMirrorRoot(installRoot);
runtimePluginRoot = mirrorBundledPluginRuntimeRoot({
pluginId: record.id,
pluginRoot,