mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 15:50:43 +00:00
refactor(plugins): split loader runtime helpers (#74545)
* refactor(plugins): split loader runtime helpers * test(scripts): include discord api barrel lane * test(ci): align built artifact guard expectations * fix(plugins): avoid redundant cache key assertion
This commit is contained in:
committed by
GitHub
parent
648ed69f82
commit
4aedffd37a
224
src/plugins/loader-channel-setup.ts
Normal file
224
src/plugins/loader-channel-setup.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
|
||||
import { isChannelConfigured } from "../config/channel-configured.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { unwrapDefaultModuleExport } from "./module-export.js";
|
||||
import type { PluginRuntime } from "./runtime/types.js";
|
||||
|
||||
function mergeChannelPluginSection<T>(
|
||||
baseValue: T | undefined,
|
||||
overrideValue: T | undefined,
|
||||
): T | undefined {
|
||||
if (
|
||||
baseValue &&
|
||||
overrideValue &&
|
||||
typeof baseValue === "object" &&
|
||||
typeof overrideValue === "object"
|
||||
) {
|
||||
const merged = {
|
||||
...(baseValue as Record<string, unknown>),
|
||||
};
|
||||
for (const [key, value] of Object.entries(overrideValue as Record<string, unknown>)) {
|
||||
if (value !== undefined) {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...merged,
|
||||
} as T;
|
||||
}
|
||||
return overrideValue ?? baseValue;
|
||||
}
|
||||
|
||||
export function mergeSetupRuntimeChannelPlugin(
|
||||
runtimePlugin: ChannelPlugin,
|
||||
setupPlugin: ChannelPlugin,
|
||||
): ChannelPlugin {
|
||||
return {
|
||||
...runtimePlugin,
|
||||
...setupPlugin,
|
||||
meta: mergeChannelPluginSection(runtimePlugin.meta, setupPlugin.meta),
|
||||
capabilities: mergeChannelPluginSection(runtimePlugin.capabilities, setupPlugin.capabilities),
|
||||
commands: mergeChannelPluginSection(runtimePlugin.commands, setupPlugin.commands),
|
||||
doctor: mergeChannelPluginSection(runtimePlugin.doctor, setupPlugin.doctor),
|
||||
reload: mergeChannelPluginSection(runtimePlugin.reload, setupPlugin.reload),
|
||||
config: mergeChannelPluginSection(runtimePlugin.config, setupPlugin.config),
|
||||
setup: mergeChannelPluginSection(runtimePlugin.setup, setupPlugin.setup),
|
||||
messaging: mergeChannelPluginSection(runtimePlugin.messaging, setupPlugin.messaging),
|
||||
actions: mergeChannelPluginSection(runtimePlugin.actions, setupPlugin.actions),
|
||||
secrets: mergeChannelPluginSection(runtimePlugin.secrets, setupPlugin.secrets),
|
||||
} as ChannelPlugin;
|
||||
}
|
||||
|
||||
export type BundledRuntimeChannelRegistration = {
|
||||
id?: string;
|
||||
loadChannelPlugin?: () => ChannelPlugin;
|
||||
loadChannelSecrets?: () => ChannelPlugin["secrets"] | undefined;
|
||||
setChannelRuntime?: (runtime: PluginRuntime) => void;
|
||||
};
|
||||
|
||||
export function resolveBundledRuntimeChannelRegistration(
|
||||
moduleExport: unknown,
|
||||
): BundledRuntimeChannelRegistration {
|
||||
const resolved = unwrapDefaultModuleExport(moduleExport);
|
||||
if (!resolved || typeof resolved !== "object") {
|
||||
return {};
|
||||
}
|
||||
const entryRecord = resolved as {
|
||||
kind?: unknown;
|
||||
id?: unknown;
|
||||
loadChannelPlugin?: unknown;
|
||||
loadChannelSecrets?: unknown;
|
||||
setChannelRuntime?: unknown;
|
||||
};
|
||||
if (
|
||||
entryRecord.kind !== "bundled-channel-entry" ||
|
||||
typeof entryRecord.id !== "string" ||
|
||||
typeof entryRecord.loadChannelPlugin !== "function"
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
id: entryRecord.id,
|
||||
loadChannelPlugin: entryRecord.loadChannelPlugin as () => ChannelPlugin,
|
||||
...(typeof entryRecord.loadChannelSecrets === "function"
|
||||
? {
|
||||
loadChannelSecrets: entryRecord.loadChannelSecrets as () =>
|
||||
| ChannelPlugin["secrets"]
|
||||
| undefined,
|
||||
}
|
||||
: {}),
|
||||
...(typeof entryRecord.setChannelRuntime === "function"
|
||||
? {
|
||||
setChannelRuntime: entryRecord.setChannelRuntime as (runtime: PluginRuntime) => void,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function loadBundledRuntimeChannelPlugin(params: {
|
||||
registration: BundledRuntimeChannelRegistration;
|
||||
}): {
|
||||
plugin?: ChannelPlugin;
|
||||
loadError?: unknown;
|
||||
} {
|
||||
if (typeof params.registration.loadChannelPlugin !== "function") {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const loadedPlugin = params.registration.loadChannelPlugin();
|
||||
const loadedSecrets = params.registration.loadChannelSecrets?.();
|
||||
if (!loadedPlugin || typeof loadedPlugin !== "object") {
|
||||
return {};
|
||||
}
|
||||
const mergedSecrets = mergeChannelPluginSection(loadedPlugin.secrets, loadedSecrets);
|
||||
return {
|
||||
plugin: {
|
||||
...loadedPlugin,
|
||||
...(mergedSecrets !== undefined ? { secrets: mergedSecrets } : {}),
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
return { loadError: err };
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveSetupChannelRegistration(
|
||||
moduleExport: unknown,
|
||||
params: { installRuntimeDeps?: boolean } = {},
|
||||
): {
|
||||
plugin?: ChannelPlugin;
|
||||
setChannelRuntime?: (runtime: PluginRuntime) => void;
|
||||
usesBundledSetupContract?: boolean;
|
||||
loadError?: unknown;
|
||||
} {
|
||||
const resolved = unwrapDefaultModuleExport(moduleExport);
|
||||
if (!resolved || typeof resolved !== "object") {
|
||||
return {};
|
||||
}
|
||||
const setupEntryRecord = resolved as {
|
||||
kind?: unknown;
|
||||
loadSetupPlugin?: unknown;
|
||||
loadSetupSecrets?: unknown;
|
||||
setChannelRuntime?: unknown;
|
||||
};
|
||||
if (
|
||||
setupEntryRecord.kind === "bundled-channel-setup-entry" &&
|
||||
typeof setupEntryRecord.loadSetupPlugin === "function"
|
||||
) {
|
||||
try {
|
||||
const setupLoadOptions =
|
||||
params.installRuntimeDeps === false ? { installRuntimeDeps: false } : undefined;
|
||||
const loadedPlugin = setupEntryRecord.loadSetupPlugin(setupLoadOptions);
|
||||
const loadedSecrets =
|
||||
typeof setupEntryRecord.loadSetupSecrets === "function"
|
||||
? (setupEntryRecord.loadSetupSecrets(setupLoadOptions) as
|
||||
| ChannelPlugin["secrets"]
|
||||
| undefined)
|
||||
: undefined;
|
||||
if (loadedPlugin && typeof loadedPlugin === "object") {
|
||||
const mergedSecrets = mergeChannelPluginSection(
|
||||
(loadedPlugin as ChannelPlugin).secrets,
|
||||
loadedSecrets,
|
||||
);
|
||||
return {
|
||||
plugin: {
|
||||
...(loadedPlugin as ChannelPlugin),
|
||||
...(mergedSecrets !== undefined ? { secrets: mergedSecrets } : {}),
|
||||
},
|
||||
usesBundledSetupContract: true,
|
||||
...(typeof setupEntryRecord.setChannelRuntime === "function"
|
||||
? {
|
||||
setChannelRuntime: setupEntryRecord.setChannelRuntime as (
|
||||
runtime: PluginRuntime,
|
||||
) => void,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
return { loadError: err };
|
||||
}
|
||||
}
|
||||
const setup = resolved as {
|
||||
plugin?: unknown;
|
||||
};
|
||||
if (!setup.plugin || typeof setup.plugin !== "object") {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
plugin: setup.plugin as ChannelPlugin,
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldLoadChannelPluginInSetupRuntime(params: {
|
||||
manifestChannels: string[];
|
||||
setupSource?: string;
|
||||
startupDeferConfiguredChannelFullLoadUntilAfterListen?: boolean;
|
||||
cfg: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
preferSetupRuntimeForChannelPlugins?: boolean;
|
||||
}): boolean {
|
||||
if (!params.setupSource || params.manifestChannels.length === 0) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.preferSetupRuntimeForChannelPlugins &&
|
||||
params.startupDeferConfiguredChannelFullLoadUntilAfterListen === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return !params.manifestChannels.some((channelId) =>
|
||||
isChannelConfigured(params.cfg, channelId, params.env),
|
||||
);
|
||||
}
|
||||
|
||||
export function channelPluginIdBelongsToManifest(params: {
|
||||
channelId: string | undefined;
|
||||
pluginId: string;
|
||||
manifestChannels: readonly string[];
|
||||
}): boolean {
|
||||
if (!params.channelId) {
|
||||
return true;
|
||||
}
|
||||
return params.channelId === params.pluginId || params.manifestChannels.includes(params.channelId);
|
||||
}
|
||||
Reference in New Issue
Block a user