diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index f32d04d0d80..efc93d1abdb 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -66,7 +66,7 @@ describe("loadOpenClawPlugins", () => { id: "bundled", body: `export default { id: "bundled", register() {} };`, dir: bundledDir, - filename: "bundled.ts", + filename: "bundled.js", }); process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledDir; @@ -121,9 +121,9 @@ describe("loadOpenClawPlugins", () => { outbound: { deliveryMode: "direct" } } }); -} };`, + } };`, dir: bundledDir, - filename: "telegram.ts", + filename: "telegram.js", }); process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledDir; @@ -150,7 +150,7 @@ describe("loadOpenClawPlugins", () => { id: "memory-core", body: `export default { id: "memory-core", kind: "memory", register() {} };`, dir: bundledDir, - filename: "memory-core.ts", + filename: "memory-core.js", }); process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledDir; @@ -180,7 +180,7 @@ describe("loadOpenClawPlugins", () => { name: "@openclaw/memory-core", version: "1.2.3", description: "Memory plugin package", - openclaw: { extensions: ["./index.ts"] }, + openclaw: { extensions: ["./index.js"] }, }), "utf-8", ); @@ -188,7 +188,7 @@ describe("loadOpenClawPlugins", () => { id: "memory-core", body: `export default { id: "memory-core", kind: "memory", name: "Memory (Core)", register() {} };`, dir: pluginDir, - filename: "index.ts", + filename: "index.js", }); process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledDir; diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 9365f584fe3..1d16939b8a3 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -220,22 +220,30 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi }); pushDiagnostics(registry.diagnostics, manifestRegistry.diagnostics); - const pluginSdkAlias = resolvePluginSdkAlias(); - const pluginSdkAccountIdAlias = resolvePluginSdkAccountIdAlias(); - const jiti = createJiti(import.meta.url, { - interopDefault: true, - extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"], - ...(pluginSdkAlias || pluginSdkAccountIdAlias - ? { - alias: { - ...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}), - ...(pluginSdkAccountIdAlias - ? { "openclaw/plugin-sdk/account-id": pluginSdkAccountIdAlias } - : {}), - }, - } - : {}), - }); + // Lazy: avoid creating the Jiti loader when all plugins are disabled (common in unit tests). + let jitiLoader: ReturnType | null = null; + const getJiti = () => { + if (jitiLoader) { + return jitiLoader; + } + const pluginSdkAlias = resolvePluginSdkAlias(); + const pluginSdkAccountIdAlias = resolvePluginSdkAccountIdAlias(); + jitiLoader = createJiti(import.meta.url, { + interopDefault: true, + extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"], + ...(pluginSdkAlias || pluginSdkAccountIdAlias + ? { + alias: { + ...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}), + ...(pluginSdkAccountIdAlias + ? { "openclaw/plugin-sdk/account-id": pluginSdkAccountIdAlias } + : {}), + }, + } + : {}), + }); + return jitiLoader; + }; const manifestByRoot = new Map( manifestRegistry.plugins.map((record) => [record.rootDir, record]), @@ -312,7 +320,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi let mod: OpenClawPluginModule | null = null; try { - mod = jiti(candidate.source) as OpenClawPluginModule; + mod = getJiti()(candidate.source) as OpenClawPluginModule; } catch (err) { logger.error(`[plugins] ${record.id} failed to load from ${record.source}: ${String(err)}`); record.status = "error"; diff --git a/src/plugins/manifest-registry.ts b/src/plugins/manifest-registry.ts index eb249066dad..9dc3102a6f8 100644 --- a/src/plugins/manifest-registry.ts +++ b/src/plugins/manifest-registry.ts @@ -89,7 +89,14 @@ function buildCacheKey(params: { plugins: NormalizedPluginsConfig; }): string { const workspaceKey = params.workspaceDir ? resolveUserPath(params.workspaceDir) : ""; - return `${workspaceKey}::${JSON.stringify(params.plugins)}`; + // The manifest registry only depends on where plugins are discovered from (workspace + load paths). + // It does not depend on allow/deny/entries enable-state, so exclude those for higher cache hit rates. + const loadPaths = params.plugins.loadPaths + .map((p) => resolveUserPath(p)) + .map((p) => p.trim()) + .filter(Boolean) + .toSorted(); + return `${workspaceKey}::${JSON.stringify(loadPaths)}`; } function safeStatMtimeMs(filePath: string): number | null {