mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-03 04:41:47 +00:00
On git-channel installs where both extensions/<id>/index.ts and dist/extensions/<id>/index.js exist, two registry assemblies resolved the same plugin id to different physical files (preferBuiltPluginArtifacts drives both the artifact choice and the load-cache key), so the path-keyed module cache evaluated the entry twice — two live plugin instances with separate state, binding hooks to one and registerTool to the other. Memoize the first resolved runtime entry path per plugin-id + real root + entry-kind so both assemblies converge on one module instance; clear the memo on activated-runtime-state and registry-load-cache resets so reload/install/doctor re-resolve. No-op on dist-only installs. Closes #107933
85 lines
2.7 KiB
TypeScript
85 lines
2.7 KiB
TypeScript
import { pluginLoaderCacheInstances, type CachedPluginState } from "./loader-cache-instances.js";
|
|
import { resolvePluginLoadCacheContext } from "./loader-load-context.js";
|
|
import type { PluginLoadOptions, PluginRuntimeSubagentMode } from "./loader-types.js";
|
|
import { clearPluginRuntimeArtifactResolutionMemo } from "./plugin-runtime-artifact-resolution.js";
|
|
|
|
export const pluginLoaderCacheState = pluginLoaderCacheInstances.scoped;
|
|
const fullWorkspacePluginLoaderCacheState = pluginLoaderCacheInstances.fullWorkspace;
|
|
|
|
function getPluginRegistryCache(onlyPluginIds?: string[]) {
|
|
return onlyPluginIds ? pluginLoaderCacheState : fullWorkspacePluginLoaderCacheState;
|
|
}
|
|
|
|
function getCachedPluginRegistry(
|
|
cacheKey: string,
|
|
onlyPluginIds?: string[],
|
|
): CachedPluginState | undefined {
|
|
return getPluginRegistryCache(onlyPluginIds).get(cacheKey);
|
|
}
|
|
|
|
export function setCachedPluginRegistry(
|
|
cacheKey: string,
|
|
state: CachedPluginState,
|
|
onlyPluginIds?: string[],
|
|
): void {
|
|
getPluginRegistryCache(onlyPluginIds).set(cacheKey, state);
|
|
}
|
|
|
|
export function getReusableCachedPluginRegistry(params: {
|
|
cacheKey: string;
|
|
onlyPluginIds: string[] | undefined;
|
|
runtimeSubagentMode: PluginRuntimeSubagentMode;
|
|
options: PluginLoadOptions;
|
|
}):
|
|
| {
|
|
state: CachedPluginState;
|
|
cacheKey: string;
|
|
runtimeSubagentMode: PluginRuntimeSubagentMode;
|
|
}
|
|
| undefined {
|
|
const exact = getCachedPluginRegistry(params.cacheKey, params.onlyPluginIds);
|
|
if (exact) {
|
|
return {
|
|
state: exact,
|
|
cacheKey: params.cacheKey,
|
|
runtimeSubagentMode: params.runtimeSubagentMode,
|
|
};
|
|
}
|
|
if (params.runtimeSubagentMode !== "default") {
|
|
return undefined;
|
|
}
|
|
const gatewayBindableContext = resolvePluginLoadCacheContext({
|
|
...params.options,
|
|
runtimeOptions: {
|
|
...params.options.runtimeOptions,
|
|
allowGatewaySubagentBinding: true,
|
|
},
|
|
});
|
|
const gatewayBindable = getCachedPluginRegistry(
|
|
gatewayBindableContext.cacheKey,
|
|
gatewayBindableContext.onlyPluginIds,
|
|
);
|
|
if (!gatewayBindable) {
|
|
return undefined;
|
|
}
|
|
return {
|
|
state: gatewayBindable,
|
|
cacheKey: gatewayBindableContext.cacheKey,
|
|
runtimeSubagentMode: gatewayBindableContext.runtimeSubagentMode,
|
|
};
|
|
}
|
|
|
|
export function clearPluginRegistryLoadCache(): void {
|
|
clearPluginRuntimeArtifactResolutionMemo();
|
|
pluginLoaderCacheState.clearCachedRegistries();
|
|
fullWorkspacePluginLoaderCacheState.clearCachedRegistries();
|
|
}
|
|
|
|
export function resolvePluginRegistryLoadCacheKey(options: PluginLoadOptions = {}): string {
|
|
return resolvePluginLoadCacheContext(options).cacheKey;
|
|
}
|
|
|
|
export function isPluginRegistryLoadInFlight(options: PluginLoadOptions = {}): boolean {
|
|
return pluginLoaderCacheState.isLoadInFlight(resolvePluginRegistryLoadCacheKey(options));
|
|
}
|