perf(plugins): resolve setup providers lazily

This commit is contained in:
Peter Steinberger
2026-04-05 21:08:44 +01:00
parent 36080283e4
commit 714ba48a6f

View File

@@ -63,9 +63,11 @@ const NOOP_LOGGER: PluginLogger = {
const jitiLoaders = new Map<string, ReturnType<typeof createJiti>>();
const setupRegistryCache = new Map<string, PluginSetupRegistry>();
const setupProviderCache = new Map<string, ProviderPlugin | null>();
export function clearPluginSetupRegistryCache(): void {
setupRegistryCache.clear();
setupProviderCache.clear();
}
function getJiti(modulePath: string) {
@@ -97,16 +99,47 @@ function buildSetupRegistryCacheKey(params: {
});
}
function buildSetupProviderCacheKey(params: {
provider: string;
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): string {
return JSON.stringify({
provider: normalizeProviderId(params.provider),
registry: buildSetupRegistryCacheKey(params),
});
}
function resolveSetupApiPath(rootDir: string): string | null {
const orderedExtensions = RUNNING_FROM_BUILT_ARTIFACT
? SETUP_API_EXTENSIONS
: ([...SETUP_API_EXTENSIONS.slice(3), ...SETUP_API_EXTENSIONS.slice(0, 3)] as const);
for (const extension of orderedExtensions) {
const candidate = path.join(rootDir, `setup-api${extension}`);
if (fs.existsSync(candidate)) {
return candidate;
const findSetupApi = (candidateRootDir: string): string | null => {
for (const extension of orderedExtensions) {
const candidate = path.join(candidateRootDir, `setup-api${extension}`);
if (fs.existsSync(candidate)) {
return candidate;
}
}
return null;
};
const direct = findSetupApi(rootDir);
if (direct) {
return direct;
}
const bundledExtensionDir = path.basename(rootDir);
const repoRoot = path.resolve(path.dirname(CURRENT_MODULE_PATH), "..", "..");
const sourceExtensionRoot = path.join(repoRoot, "extensions", bundledExtensionDir);
if (sourceExtensionRoot !== rootDir) {
const sourceFallback = findSetupApi(sourceExtensionRoot);
if (sourceFallback) {
return sourceFallback;
}
}
return null;
}
@@ -252,9 +285,99 @@ export function resolvePluginSetupProvider(params: {
workspaceDir?: string;
env?: NodeJS.ProcessEnv;
}): ProviderPlugin | undefined {
return resolvePluginSetupRegistry(params).providers.find((entry) =>
matchesProvider(entry.provider, params.provider),
)?.provider;
const cacheKey = buildSetupProviderCacheKey(params);
if (setupProviderCache.has(cacheKey)) {
return setupProviderCache.get(cacheKey) ?? undefined;
}
const env = params.env ?? process.env;
const normalizedProvider = normalizeProviderId(params.provider);
const discovery = discoverOpenClawPlugins({
workspaceDir: params.workspaceDir,
env,
cache: true,
});
const manifestRegistry = loadPluginManifestRegistry({
workspaceDir: params.workspaceDir,
env,
cache: true,
candidates: discovery.candidates,
diagnostics: discovery.diagnostics,
});
const record = manifestRegistry.plugins.find((entry) =>
entry.providers.some((providerId) => normalizeProviderId(providerId) === normalizedProvider),
);
if (!record) {
setupProviderCache.set(cacheKey, null);
return undefined;
}
const setupSource = record.setupSource ?? resolveSetupApiPath(record.rootDir);
if (!setupSource) {
setupProviderCache.set(cacheKey, null);
return undefined;
}
let mod: OpenClawPluginModule;
try {
mod = getJiti(setupSource)(setupSource) as OpenClawPluginModule;
} catch {
setupProviderCache.set(cacheKey, null);
return undefined;
}
const resolved = resolveRegister((mod as { default?: OpenClawPluginModule }).default ?? mod);
if (!resolved.register) {
setupProviderCache.set(cacheKey, null);
return undefined;
}
if (resolved.definition?.id && resolved.definition.id !== record.id) {
setupProviderCache.set(cacheKey, null);
return undefined;
}
let matchedProvider: ProviderPlugin | undefined;
const localProviderKeys = new Set<string>();
const api = buildPluginApi({
id: record.id,
name: record.name ?? record.id,
version: record.version,
description: record.description,
source: setupSource,
rootDir: record.rootDir,
registrationMode: "setup-only",
config: {} as OpenClawConfig,
runtime: EMPTY_RUNTIME,
logger: NOOP_LOGGER,
resolvePath: (input) => input,
handlers: {
registerProvider(provider) {
const key = normalizeProviderId(provider.id);
if (localProviderKeys.has(key)) {
return;
}
localProviderKeys.add(key);
if (matchesProvider(provider, normalizedProvider)) {
matchedProvider = provider;
}
},
registerConfigMigration() {},
registerAutoEnableProbe() {},
},
});
try {
const result = resolved.register(api);
if (result && typeof result.then === "function") {
// Keep setup registration sync-only.
}
} catch {
setupProviderCache.set(cacheKey, null);
return undefined;
}
setupProviderCache.set(cacheKey, matchedProvider ?? null);
return matchedProvider;
}
export function runPluginSetupConfigMigrations(params: {