Files
openclaw/src/plugins/setup-registry.runtime.ts
rolandrscheel aeac10f5ce fix(plugins): reuse workspace metadata for session model refs (#76655)
Fixes the session-list plugin metadata hot path by reusing the active workspace-scoped plugin metadata snapshot for manifest model-id normalization and setup CLI backend fallback. Also keeps model-pricing collection on its provided manifest registry and fixes the CI-only hoisted test mock failure.

Validated with targeted plugin/gateway/model-selection tests, CI-shaped gateway startup shard, Testbox `pnpm check:changed`, and green PR CI.

Thanks @rolandrscheel.
2026-05-03 15:58:14 +01:00

134 lines
4.1 KiB
TypeScript

import { createRequire } from "node:module";
import { normalizeProviderId } from "../agents/provider-id.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot.js";
import { isInstalledPluginEnabled } from "./installed-plugin-index.js";
import {
loadPluginMetadataSnapshot,
type PluginMetadataSnapshot,
} from "./plugin-metadata-snapshot.js";
import { getActivePluginRegistryWorkspaceDirFromState } from "./runtime-state.js";
type SetupRegistryRuntimeModule = Pick<
typeof import("./setup-registry.js"),
"resolvePluginSetupCliBackend"
>;
type SetupCliBackendRuntimeEntry = {
pluginId: string;
backend: {
id: string;
};
};
type SetupCliBackendRuntimeLookupParams = {
backend: string;
config?: OpenClawConfig;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
};
const require = createRequire(import.meta.url);
const SETUP_REGISTRY_RUNTIME_CANDIDATES = ["./setup-registry.js", "./setup-registry.ts"] as const;
type BundledSetupCliBackendCache = {
configFingerprint: string;
entries: SetupCliBackendRuntimeEntry[];
};
let setupRegistryRuntimeModule: SetupRegistryRuntimeModule | null | undefined;
let cachedBundledSetupCliBackends: BundledSetupCliBackendCache | undefined;
export const __testing = {
resetRuntimeState(): void {
setupRegistryRuntimeModule = undefined;
cachedBundledSetupCliBackends = undefined;
},
setRuntimeModuleForTest(module: SetupRegistryRuntimeModule | null | undefined): void {
setupRegistryRuntimeModule = module;
},
};
function resolveMetadataSnapshotForSetupCliBackends(
params: Omit<SetupCliBackendRuntimeLookupParams, "backend"> = {},
): {
snapshot: PluginMetadataSnapshot;
cacheable: boolean;
} {
const env = params.env ?? process.env;
const workspaceDir = params.workspaceDir ?? getActivePluginRegistryWorkspaceDirFromState();
const current = getCurrentPluginMetadataSnapshot({
config: params.config,
env,
workspaceDir,
});
if (current) {
return { snapshot: current, cacheable: true };
}
return {
snapshot: loadPluginMetadataSnapshot({
config: params.config ?? {},
env,
workspaceDir,
}),
cacheable: false,
};
}
function resolveBundledSetupCliBackends(
params: Omit<SetupCliBackendRuntimeLookupParams, "backend"> = {},
): SetupCliBackendRuntimeEntry[] {
const { snapshot, cacheable } = resolveMetadataSnapshotForSetupCliBackends(params);
const configFingerprint = snapshot.configFingerprint;
if (
cacheable &&
configFingerprint &&
cachedBundledSetupCliBackends?.configFingerprint === configFingerprint
) {
return cachedBundledSetupCliBackends.entries;
}
const entries = snapshot.plugins.flatMap((plugin) => {
if (plugin.origin !== "bundled" || !isInstalledPluginEnabled(snapshot.index, plugin.id)) {
return [];
}
return [...plugin.cliBackends, ...(plugin.setup?.cliBackends ?? [])].map(
(backendId) =>
({
pluginId: plugin.id,
backend: { id: backendId },
}) satisfies SetupCliBackendRuntimeEntry,
);
});
if (cacheable && configFingerprint) {
cachedBundledSetupCliBackends = { configFingerprint, entries };
}
return entries;
}
function loadSetupRegistryRuntime(): SetupRegistryRuntimeModule | null {
if (setupRegistryRuntimeModule !== undefined) {
return setupRegistryRuntimeModule;
}
for (const candidate of SETUP_REGISTRY_RUNTIME_CANDIDATES) {
try {
setupRegistryRuntimeModule = require(candidate) as SetupRegistryRuntimeModule;
return setupRegistryRuntimeModule;
} catch {
// Try source/runtime candidates in order.
}
}
setupRegistryRuntimeModule = null;
return null;
}
export function resolvePluginSetupCliBackendRuntime(params: SetupCliBackendRuntimeLookupParams) {
const normalized = normalizeProviderId(params.backend);
const runtime = loadSetupRegistryRuntime();
if (runtime !== null) {
return runtime.resolvePluginSetupCliBackend(params);
}
return resolveBundledSetupCliBackends(params).find(
(entry) => normalizeProviderId(entry.backend.id) === normalized,
);
}