fix(plugins): lazily initialize runtime and split plugin-sdk startup imports (#28620)

Merged via squash.

Prepared head SHA: 8bd7d6c13b
Co-authored-by: hmemcpy <601206+hmemcpy@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Igal Tabachnik
2026-03-04 02:58:48 +02:00
committed by GitHub
parent 4b17d6d882
commit a4850b1b8f
17 changed files with 226 additions and 19 deletions

View File

@@ -22,6 +22,7 @@ import { isPathInside, safeStatSync } from "./path-safety.js";
import { createPluginRegistry, type PluginRecord, type PluginRegistry } from "./registry.js";
import { setActivePluginRegistry } from "./runtime.js";
import { createPluginRuntime } from "./runtime/index.js";
import type { PluginRuntime } from "./runtime/types.js";
import { validateJsonSchemaValue } from "./schema-validator.js";
import type {
OpenClawPluginDefinition,
@@ -91,6 +92,14 @@ const resolvePluginSdkAccountIdAlias = (): string | null => {
return resolvePluginSdkAliasFile({ srcFile: "account-id.ts", distFile: "account-id.js" });
};
const resolvePluginSdkCoreAlias = (): string | null => {
return resolvePluginSdkAliasFile({ srcFile: "core.ts", distFile: "core.js" });
};
const resolvePluginSdkTelegramAlias = (): string | null => {
return resolvePluginSdkAliasFile({ srcFile: "telegram.ts", distFile: "telegram.js" });
};
export const __testing = {
resolvePluginSdkAliasFile,
};
@@ -393,7 +402,39 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
// Clear previously registered plugin commands before reloading
clearPluginCommands();
const runtime = createPluginRuntime();
// Lazily initialize the runtime so startup paths that discover/skip plugins do
// not eagerly load every channel runtime dependency.
let resolvedRuntime: PluginRuntime | null = null;
const resolveRuntime = (): PluginRuntime => {
resolvedRuntime ??= createPluginRuntime();
return resolvedRuntime;
};
const runtime = new Proxy({} as PluginRuntime, {
get(_target, prop, receiver) {
return Reflect.get(resolveRuntime(), prop, receiver);
},
set(_target, prop, value, receiver) {
return Reflect.set(resolveRuntime(), prop, value, receiver);
},
has(_target, prop) {
return Reflect.has(resolveRuntime(), prop);
},
ownKeys() {
return Reflect.ownKeys(resolveRuntime() as object);
},
getOwnPropertyDescriptor(_target, prop) {
return Reflect.getOwnPropertyDescriptor(resolveRuntime() as object, prop);
},
defineProperty(_target, prop, attributes) {
return Reflect.defineProperty(resolveRuntime() as object, prop, attributes);
},
deleteProperty(_target, prop) {
return Reflect.deleteProperty(resolveRuntime() as object, prop);
},
getPrototypeOf() {
return Reflect.getPrototypeOf(resolveRuntime() as object);
},
});
const { registry, createApi } = createPluginRegistry({
logger,
runtime,
@@ -435,17 +476,22 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
}
const pluginSdkAlias = resolvePluginSdkAlias();
const pluginSdkAccountIdAlias = resolvePluginSdkAccountIdAlias();
const pluginSdkCoreAlias = resolvePluginSdkCoreAlias();
const pluginSdkTelegramAlias = resolvePluginSdkTelegramAlias();
const aliasMap = {
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
...(pluginSdkCoreAlias ? { "openclaw/plugin-sdk/core": pluginSdkCoreAlias } : {}),
...(pluginSdkTelegramAlias ? { "openclaw/plugin-sdk/telegram": pluginSdkTelegramAlias } : {}),
...(pluginSdkAccountIdAlias
? { "openclaw/plugin-sdk/account-id": pluginSdkAccountIdAlias }
: {}),
};
jitiLoader = createJiti(import.meta.url, {
interopDefault: true,
extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"],
...(pluginSdkAlias || pluginSdkAccountIdAlias
...(Object.keys(aliasMap).length > 0
? {
alias: {
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
...(pluginSdkAccountIdAlias
? { "openclaw/plugin-sdk/account-id": pluginSdkAccountIdAlias }
: {}),
},
alias: aliasMap,
}
: {}),
});