mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-21 14:11:26 +00:00
perf(plugins): lazy-load channel setup entrypoints
This commit is contained in:
@@ -1885,6 +1885,107 @@ module.exports = {
|
||||
expect(setupRegistry.channels).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("uses package setupEntry for enabled but unconfigured channel loads", () => {
|
||||
useNoBundledPlugins();
|
||||
const pluginDir = makeTempDir();
|
||||
const fullMarker = path.join(pluginDir, "full-loaded.txt");
|
||||
const setupMarker = path.join(pluginDir, "setup-loaded.txt");
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/setup-runtime-test",
|
||||
openclaw: {
|
||||
extensions: ["./index.cjs"],
|
||||
setupEntry: "./setup-entry.cjs",
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: "setup-runtime-test",
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
channels: ["setup-runtime-test"],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "index.cjs"),
|
||||
`require("node:fs").writeFileSync(${JSON.stringify(fullMarker)}, "loaded", "utf-8");
|
||||
module.exports = {
|
||||
id: "setup-runtime-test",
|
||||
register(api) {
|
||||
api.registerChannel({
|
||||
plugin: {
|
||||
id: "setup-runtime-test",
|
||||
meta: {
|
||||
id: "setup-runtime-test",
|
||||
label: "Setup Runtime Test",
|
||||
selectionLabel: "Setup Runtime Test",
|
||||
docsPath: "/channels/setup-runtime-test",
|
||||
blurb: "full entry should not run while unconfigured",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
listAccountIds: () => [],
|
||||
resolveAccount: () => ({ accountId: "default" }),
|
||||
},
|
||||
outbound: { deliveryMode: "direct" },
|
||||
},
|
||||
});
|
||||
},
|
||||
};`,
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "setup-entry.cjs"),
|
||||
`require("node:fs").writeFileSync(${JSON.stringify(setupMarker)}, "loaded", "utf-8");
|
||||
module.exports = {
|
||||
plugin: {
|
||||
id: "setup-runtime-test",
|
||||
meta: {
|
||||
id: "setup-runtime-test",
|
||||
label: "Setup Runtime Test",
|
||||
selectionLabel: "Setup Runtime Test",
|
||||
docsPath: "/channels/setup-runtime-test",
|
||||
blurb: "setup runtime",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
listAccountIds: () => [],
|
||||
resolveAccount: () => ({ accountId: "default" }),
|
||||
},
|
||||
outbound: { deliveryMode: "direct" },
|
||||
},
|
||||
};`,
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const registry = loadOpenClawPlugins({
|
||||
cache: false,
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [pluginDir] },
|
||||
allow: ["setup-runtime-test"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(fs.existsSync(setupMarker)).toBe(true);
|
||||
expect(fs.existsSync(fullMarker)).toBe(false);
|
||||
expect(registry.channelSetups).toHaveLength(1);
|
||||
expect(registry.channels).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("blocks before_prompt_build but preserves legacy model overrides when prompt injection is disabled", async () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createJiti } from "jiti";
|
||||
import type { ChannelDock } from "../channels/dock.js";
|
||||
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { isChannelConfigured } from "../config/plugin-auto-enable.js";
|
||||
import type { PluginInstallRecord } from "../config/types.plugins.js";
|
||||
import type { GatewayRequestHandler } from "../gateway/server-methods/types.js";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
@@ -357,6 +358,20 @@ function resolveSetupChannelRegistration(moduleExport: unknown): {
|
||||
};
|
||||
}
|
||||
|
||||
function shouldLoadChannelPluginInSetupRuntime(params: {
|
||||
manifestChannels: string[];
|
||||
setupSource?: string;
|
||||
cfg: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): boolean {
|
||||
if (!params.setupSource || params.manifestChannels.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return !params.manifestChannels.some((channelId) =>
|
||||
isChannelConfigured(params.cfg, channelId, params.env),
|
||||
);
|
||||
}
|
||||
|
||||
function createPluginRecord(params: {
|
||||
id: string;
|
||||
name?: string;
|
||||
@@ -924,7 +939,15 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
};
|
||||
|
||||
const registrationMode = enableState.enabled
|
||||
? "full"
|
||||
? !validateOnly &&
|
||||
shouldLoadChannelPluginInSetupRuntime({
|
||||
manifestChannels: manifestRecord.channels,
|
||||
setupSource: manifestRecord.setupSource,
|
||||
cfg,
|
||||
env,
|
||||
})
|
||||
? "setup-runtime"
|
||||
: "full"
|
||||
: includeSetupOnlyChannelPlugins && !validateOnly && manifestRecord.channels.length > 0
|
||||
? "setup-only"
|
||||
: null;
|
||||
@@ -994,7 +1017,8 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
|
||||
const pluginRoot = safeRealpathOrResolve(candidate.rootDir);
|
||||
const loadSource =
|
||||
registrationMode === "setup-only" && manifestRecord.setupSource
|
||||
(registrationMode === "setup-only" || registrationMode === "setup-runtime") &&
|
||||
manifestRecord.setupSource
|
||||
? manifestRecord.setupSource
|
||||
: candidate.source;
|
||||
const opened = openBoundaryFileSync({
|
||||
@@ -1029,7 +1053,10 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
continue;
|
||||
}
|
||||
|
||||
if (registrationMode === "setup-only" && manifestRecord.setupSource) {
|
||||
if (
|
||||
(registrationMode === "setup-only" || registrationMode === "setup-runtime") &&
|
||||
manifestRecord.setupSource
|
||||
) {
|
||||
const setupRegistration = resolveSetupChannelRegistration(mod);
|
||||
if (setupRegistration.plugin) {
|
||||
if (setupRegistration.plugin.id && setupRegistration.plugin.id !== record.id) {
|
||||
|
||||
@@ -481,7 +481,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
|
||||
return;
|
||||
}
|
||||
const existingRuntime = registry.channels.find((entry) => entry.plugin.id === id);
|
||||
if (mode === "full" && existingRuntime) {
|
||||
if (mode !== "setup-only" && existingRuntime) {
|
||||
pushDiagnostic({
|
||||
level: "error",
|
||||
pluginId: record.id,
|
||||
|
||||
@@ -956,7 +956,7 @@ export type OpenClawPluginModule =
|
||||
| OpenClawPluginDefinition
|
||||
| ((api: OpenClawPluginApi) => void | Promise<void>);
|
||||
|
||||
export type PluginRegistrationMode = "full" | "setup-only";
|
||||
export type PluginRegistrationMode = "full" | "setup-only" | "setup-runtime";
|
||||
|
||||
export type OpenClawPluginApi = {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user