mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:20:44 +00:00
fix: preserve external read-only channel metadata
This commit is contained in:
204
src/channels/plugins/read-only.test.ts
Normal file
204
src/channels/plugins/read-only.test.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
cleanupPluginLoaderFixturesForTest,
|
||||
EMPTY_PLUGIN_SCHEMA,
|
||||
makeTempDir,
|
||||
resetPluginLoaderTestStateForTest,
|
||||
useNoBundledPlugins,
|
||||
} from "../../plugins/loader.test-fixtures.js";
|
||||
import { listReadOnlyChannelPluginsForConfig } from "./read-only.js";
|
||||
|
||||
function writeExternalSetupChannelPlugin(options: { setupEntry?: boolean } = {}) {
|
||||
useNoBundledPlugins();
|
||||
const pluginDir = makeTempDir();
|
||||
const fullMarker = path.join(pluginDir, "full-loaded.txt");
|
||||
const setupMarker = path.join(pluginDir, "setup-loaded.txt");
|
||||
const setupEntry = options.setupEntry !== false;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@example/openclaw-external-chat",
|
||||
version: "1.0.0",
|
||||
openclaw: {
|
||||
extensions: ["./index.cjs"],
|
||||
...(setupEntry ? { setupEntry: "./setup-entry.cjs" } : {}),
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "openclaw.plugin.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: "external-chat",
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
channels: ["external-chat"],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "index.cjs"),
|
||||
`require("node:fs").writeFileSync(${JSON.stringify(fullMarker)}, "loaded", "utf-8");
|
||||
module.exports = {
|
||||
id: "external-chat",
|
||||
register(api) {
|
||||
api.registerChannel({
|
||||
plugin: {
|
||||
id: "external-chat",
|
||||
meta: {
|
||||
id: "external-chat",
|
||||
label: "External Chat",
|
||||
selectionLabel: "External Chat",
|
||||
docsPath: "/channels/external-chat",
|
||||
blurb: "full entry",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
listAccountIds: () => ["default"],
|
||||
resolveAccount: () => ({ accountId: "default", token: "configured" }),
|
||||
},
|
||||
outbound: { deliveryMode: "direct" },
|
||||
secrets: {
|
||||
secretTargetRegistryEntries: [
|
||||
{
|
||||
id: "channels.external-chat.token",
|
||||
targetType: "channel",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.external-chat.token",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};`,
|
||||
"utf-8",
|
||||
);
|
||||
if (setupEntry) {
|
||||
fs.writeFileSync(
|
||||
path.join(pluginDir, "setup-entry.cjs"),
|
||||
`require("node:fs").writeFileSync(${JSON.stringify(setupMarker)}, "loaded", "utf-8");
|
||||
module.exports = {
|
||||
plugin: {
|
||||
id: "external-chat",
|
||||
meta: {
|
||||
id: "external-chat",
|
||||
label: "External Chat",
|
||||
selectionLabel: "External Chat",
|
||||
docsPath: "/channels/external-chat",
|
||||
blurb: "setup entry",
|
||||
},
|
||||
capabilities: { chatTypes: ["direct"] },
|
||||
config: {
|
||||
listAccountIds: () => ["default"],
|
||||
resolveAccount: () => ({ accountId: "default", token: "configured" }),
|
||||
},
|
||||
outbound: { deliveryMode: "direct" },
|
||||
secrets: {
|
||||
secretTargetRegistryEntries: [
|
||||
{
|
||||
id: "channels.external-chat.token",
|
||||
targetType: "channel",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.external-chat.token",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};`,
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
return { pluginDir, fullMarker, setupMarker };
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
resetPluginLoaderTestStateForTest();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
cleanupPluginLoaderFixturesForTest();
|
||||
});
|
||||
|
||||
describe("listReadOnlyChannelPluginsForConfig", () => {
|
||||
it("loads configured external channel setup metadata without importing full runtime", () => {
|
||||
const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin();
|
||||
const plugins = listReadOnlyChannelPluginsForConfig(
|
||||
{
|
||||
channels: {
|
||||
"external-chat": { token: "configured" },
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [pluginDir] },
|
||||
allow: ["external-chat"],
|
||||
},
|
||||
} as never,
|
||||
{
|
||||
env: { ...process.env },
|
||||
includePersistedAuthState: false,
|
||||
},
|
||||
);
|
||||
|
||||
const plugin = plugins.find((entry) => entry.id === "external-chat");
|
||||
expect(plugin?.meta.blurb).toBe("setup entry");
|
||||
expect(
|
||||
plugin?.secrets?.secretTargetRegistryEntries?.some(
|
||||
(entry) => entry.id === "channels.external-chat.token",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(fs.existsSync(setupMarker)).toBe(true);
|
||||
expect(fs.existsSync(fullMarker)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps configured external channels visible when no setup entry exists", () => {
|
||||
const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin({
|
||||
setupEntry: false,
|
||||
});
|
||||
const plugins = listReadOnlyChannelPluginsForConfig(
|
||||
{
|
||||
channels: {
|
||||
"external-chat": { token: "configured" },
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [pluginDir] },
|
||||
allow: ["external-chat"],
|
||||
},
|
||||
} as never,
|
||||
{
|
||||
env: { ...process.env },
|
||||
includePersistedAuthState: false,
|
||||
},
|
||||
);
|
||||
|
||||
const plugin = plugins.find((entry) => entry.id === "external-chat");
|
||||
expect(plugin?.meta.blurb).toBe("full entry");
|
||||
expect(
|
||||
plugin?.secrets?.secretTargetRegistryEntries?.some(
|
||||
(entry) => entry.id === "channels.external-chat.token",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(fs.existsSync(setupMarker)).toBe(false);
|
||||
expect(fs.existsSync(fullMarker)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,27 +1,145 @@
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { resolveDiscoverableScopedChannelPluginIds } from "../../plugins/channel-plugin-ids.js";
|
||||
import { loadOpenClawPlugins } from "../../plugins/loader.js";
|
||||
import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
import { listPotentialConfiguredChannelIds } from "../config-presence.js";
|
||||
import { getBundledChannelSetupPlugin } from "./bundled.js";
|
||||
import { listChannelPlugins } from "./registry.js";
|
||||
import type { ChannelPlugin } from "./types.plugin.js";
|
||||
|
||||
export function listReadOnlyChannelPluginsForConfig(
|
||||
cfg: OpenClawConfig,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): ChannelPlugin[] {
|
||||
const byId = new Map<string, ChannelPlugin>();
|
||||
type ReadOnlyChannelPluginOptions = {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
workspaceDir?: string;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
includePersistedAuthState?: boolean;
|
||||
cache?: boolean;
|
||||
};
|
||||
|
||||
for (const plugin of listChannelPlugins()) {
|
||||
byId.set(plugin.id, plugin);
|
||||
function resolveReadOnlyChannelPluginOptions(
|
||||
envOrOptions?: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions,
|
||||
): ReadOnlyChannelPluginOptions {
|
||||
if (!envOrOptions) {
|
||||
return {};
|
||||
}
|
||||
if (
|
||||
"env" in envOrOptions ||
|
||||
"workspaceDir" in envOrOptions ||
|
||||
"activationSourceConfig" in envOrOptions ||
|
||||
"includePersistedAuthState" in envOrOptions ||
|
||||
"cache" in envOrOptions
|
||||
) {
|
||||
return envOrOptions as ReadOnlyChannelPluginOptions;
|
||||
}
|
||||
return { env: envOrOptions as NodeJS.ProcessEnv };
|
||||
}
|
||||
|
||||
function addChannelPlugins(
|
||||
byId: Map<string, ChannelPlugin>,
|
||||
plugins: Iterable<ChannelPlugin | undefined>,
|
||||
): void {
|
||||
for (const plugin of plugins) {
|
||||
if (plugin) {
|
||||
byId.set(plugin.id, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resolveExternalReadOnlyChannelPluginIds(params: {
|
||||
cfg: OpenClawConfig;
|
||||
activationSourceConfig?: OpenClawConfig;
|
||||
channelIds: readonly string[];
|
||||
workspaceDir?: string;
|
||||
env: NodeJS.ProcessEnv;
|
||||
cache?: boolean;
|
||||
}): string[] {
|
||||
if (params.channelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const candidatePluginIds = resolveDiscoverableScopedChannelPluginIds({
|
||||
config: params.cfg,
|
||||
activationSourceConfig: params.activationSourceConfig,
|
||||
channelIds: params.channelIds,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
cache: params.cache,
|
||||
});
|
||||
if (candidatePluginIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const channelId of listPotentialConfiguredChannelIds(cfg, env)) {
|
||||
const requestedChannelIds = new Set(params.channelIds);
|
||||
const candidatePluginIdSet = new Set(candidatePluginIds);
|
||||
return loadPluginManifestRegistry({
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
env: params.env,
|
||||
cache: params.cache,
|
||||
})
|
||||
.plugins.filter(
|
||||
(plugin) =>
|
||||
candidatePluginIdSet.has(plugin.id) &&
|
||||
plugin.origin !== "bundled" &&
|
||||
plugin.channels.some((channelId) => requestedChannelIds.has(channelId)),
|
||||
)
|
||||
.map((plugin) => plugin.id)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function listReadOnlyChannelPluginsForConfig(
|
||||
cfg: OpenClawConfig,
|
||||
env?: NodeJS.ProcessEnv,
|
||||
): ChannelPlugin[];
|
||||
export function listReadOnlyChannelPluginsForConfig(
|
||||
cfg: OpenClawConfig,
|
||||
options?: ReadOnlyChannelPluginOptions,
|
||||
): ChannelPlugin[];
|
||||
export function listReadOnlyChannelPluginsForConfig(
|
||||
cfg: OpenClawConfig,
|
||||
envOrOptions?: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions,
|
||||
): ChannelPlugin[] {
|
||||
const options = resolveReadOnlyChannelPluginOptions(envOrOptions);
|
||||
const env = options.env ?? process.env;
|
||||
const configuredChannelIds = listPotentialConfiguredChannelIds(cfg, env, {
|
||||
includePersistedAuthState: options.includePersistedAuthState,
|
||||
});
|
||||
const byId = new Map<string, ChannelPlugin>();
|
||||
|
||||
addChannelPlugins(byId, listChannelPlugins());
|
||||
|
||||
for (const channelId of configuredChannelIds) {
|
||||
if (byId.has(channelId)) {
|
||||
continue;
|
||||
}
|
||||
const setupPlugin = getBundledChannelSetupPlugin(channelId);
|
||||
if (setupPlugin) {
|
||||
byId.set(setupPlugin.id, setupPlugin);
|
||||
}
|
||||
addChannelPlugins(byId, [getBundledChannelSetupPlugin(channelId)]);
|
||||
}
|
||||
|
||||
const missingConfiguredChannelIds = configuredChannelIds.filter(
|
||||
(channelId) => !byId.has(channelId),
|
||||
);
|
||||
const externalPluginIds = resolveExternalReadOnlyChannelPluginIds({
|
||||
cfg,
|
||||
activationSourceConfig: options.activationSourceConfig ?? cfg,
|
||||
channelIds: missingConfiguredChannelIds,
|
||||
workspaceDir: options.workspaceDir,
|
||||
env,
|
||||
cache: options.cache,
|
||||
});
|
||||
if (externalPluginIds.length > 0) {
|
||||
const registry = loadOpenClawPlugins({
|
||||
config: cfg,
|
||||
activationSourceConfig: options.activationSourceConfig ?? cfg,
|
||||
env,
|
||||
workspaceDir: options.workspaceDir,
|
||||
cache: false,
|
||||
activate: false,
|
||||
includeSetupOnlyChannelPlugins: true,
|
||||
forceSetupOnlyChannelPlugins: true,
|
||||
onlyPluginIds: externalPluginIds,
|
||||
});
|
||||
addChannelPlugins(
|
||||
byId,
|
||||
registry.channelSetups.map((setup) => setup.plugin),
|
||||
);
|
||||
}
|
||||
|
||||
return [...byId.values()];
|
||||
|
||||
@@ -32,32 +32,33 @@ describe("command secret targets module import", () => {
|
||||
const listSecretTargetRegistryEntries = vi.fn(() => {
|
||||
throw new Error("registry touched too early");
|
||||
});
|
||||
const loadBundledChannelSecretContractApi = vi.fn((channelId: string) =>
|
||||
channelId === "telegram"
|
||||
? {
|
||||
secretTargetRegistryEntries: [
|
||||
{
|
||||
id: "channels.telegram.botToken",
|
||||
targetType: "channels.telegram.botToken",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.telegram.botToken",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
const listReadOnlyChannelPluginsForConfig = vi.fn(() => [
|
||||
{
|
||||
id: "telegram",
|
||||
secrets: {
|
||||
secretTargetRegistryEntries: [
|
||||
{
|
||||
id: "channels.telegram.botToken",
|
||||
targetType: "channels.telegram.botToken",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.telegram.botToken",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
vi.doMock("../secrets/target-registry.js", () => ({
|
||||
discoverConfigSecretTargetsByIds: vi.fn(() => []),
|
||||
listSecretTargetRegistryEntries,
|
||||
}));
|
||||
vi.doMock("../secrets/channel-contract-api.js", () => ({
|
||||
loadBundledChannelSecretContractApi,
|
||||
vi.doMock("../channels/plugins/read-only.js", () => ({
|
||||
listReadOnlyChannelPluginsForConfig,
|
||||
}));
|
||||
|
||||
const mod = await import("./command-secret-targets.js");
|
||||
@@ -67,7 +68,10 @@ describe("command secret targets module import", () => {
|
||||
|
||||
expect(targets.has("channels.telegram.botToken")).toBe(true);
|
||||
expect(targets.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
|
||||
expect(loadBundledChannelSecretContractApi).toHaveBeenCalledWith("telegram");
|
||||
expect(listReadOnlyChannelPluginsForConfig).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ includePersistedAuthState: false }),
|
||||
);
|
||||
expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { listPotentialConfiguredChannelIds } from "../channels/config-presence.js";
|
||||
import { listReadOnlyChannelPluginsForConfig } from "../channels/plugins/read-only.js";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { normalizeOptionalAccountId } from "../routing/session-key.js";
|
||||
import { loadBundledChannelSecretContractApi } from "../secrets/channel-contract-api.js";
|
||||
import {
|
||||
discoverConfigSecretTargetsByIds,
|
||||
listSecretTargetRegistryEntries,
|
||||
@@ -69,37 +68,28 @@ type CommandSecretTargets = {
|
||||
|
||||
let cachedCommandSecretTargets: CommandSecretTargets | undefined;
|
||||
let cachedChannelSecretTargetIds: string[] | undefined;
|
||||
const cachedBundledChannelSecretTargetIds = new Map<string, string[] | null>();
|
||||
|
||||
function getChannelSecretTargetIds(): string[] {
|
||||
cachedChannelSecretTargetIds ??= idsByPrefix(["channels."]);
|
||||
return cachedChannelSecretTargetIds;
|
||||
}
|
||||
|
||||
function getBundledChannelSecretTargetIds(channelId: string): string[] {
|
||||
const normalizedChannelId = channelId.trim();
|
||||
if (!normalizedChannelId) {
|
||||
return [];
|
||||
}
|
||||
if (cachedBundledChannelSecretTargetIds.has(normalizedChannelId)) {
|
||||
return cachedBundledChannelSecretTargetIds.get(normalizedChannelId) ?? [];
|
||||
}
|
||||
const targetIds =
|
||||
loadBundledChannelSecretContractApi(normalizedChannelId)
|
||||
?.secretTargetRegistryEntries?.map((entry) => entry.id)
|
||||
.filter((id) => id.startsWith(`channels.${normalizedChannelId}.`))
|
||||
.toSorted() ?? null;
|
||||
cachedBundledChannelSecretTargetIds.set(normalizedChannelId, targetIds);
|
||||
return targetIds ?? [];
|
||||
}
|
||||
|
||||
function getConfiguredChannelSecretTargetIds(
|
||||
config: OpenClawConfig,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): string[] {
|
||||
return listPotentialConfiguredChannelIds(config, env, { includePersistedAuthState: false })
|
||||
.toSorted()
|
||||
.flatMap((channelId) => getBundledChannelSecretTargetIds(channelId));
|
||||
const targetIds = new Set<string>();
|
||||
for (const plugin of listReadOnlyChannelPluginsForConfig(config, {
|
||||
env,
|
||||
includePersistedAuthState: false,
|
||||
})) {
|
||||
for (const entry of plugin.secrets?.secretTargetRegistryEntries ?? []) {
|
||||
if (entry.id.startsWith(`channels.${plugin.id}.`)) {
|
||||
targetIds.add(entry.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...targetIds].toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function buildCommandSecretTargets(): CommandSecretTargets {
|
||||
|
||||
@@ -134,6 +134,7 @@ export type PluginLoadOptions = {
|
||||
mode?: "full" | "validate";
|
||||
onlyPluginIds?: string[];
|
||||
includeSetupOnlyChannelPlugins?: boolean;
|
||||
forceSetupOnlyChannelPlugins?: boolean;
|
||||
/**
|
||||
* Prefer `setupEntry` for configured channel plugins that explicitly opt in
|
||||
* via package metadata because their setup entry covers the pre-listen startup surface.
|
||||
@@ -505,6 +506,7 @@ function buildCacheKey(params: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
onlyPluginIds?: string[];
|
||||
includeSetupOnlyChannelPlugins?: boolean;
|
||||
forceSetupOnlyChannelPlugins?: boolean;
|
||||
preferSetupRuntimeForChannelPlugins?: boolean;
|
||||
loadModules?: boolean;
|
||||
runtimeSubagentMode?: "default" | "explicit" | "gateway-bindable";
|
||||
@@ -534,6 +536,8 @@ function buildCacheKey(params: {
|
||||
);
|
||||
const scopeKey = serializePluginIdScope(params.onlyPluginIds);
|
||||
const setupOnlyKey = params.includeSetupOnlyChannelPlugins === true ? "setup-only" : "runtime";
|
||||
const setupOnlyModeKey =
|
||||
params.forceSetupOnlyChannelPlugins === true ? "force-setup" : "normal-setup";
|
||||
const startupChannelMode =
|
||||
params.preferSetupRuntimeForChannelPlugins === true ? "prefer-setup" : "full";
|
||||
const moduleLoadMode = params.loadModules === false ? "manifest-only" : "load-modules";
|
||||
@@ -544,7 +548,7 @@ function buildCacheKey(params: {
|
||||
installs,
|
||||
loadPaths,
|
||||
activationMetadataKey: params.activationMetadataKey ?? "",
|
||||
})}::${scopeKey}::${setupOnlyKey}::${startupChannelMode}::${moduleLoadMode}::${runtimeSubagentMode}::${params.pluginSdkResolution ?? "auto"}::${gatewayMethodsKey}`;
|
||||
})}::${scopeKey}::${setupOnlyKey}::${setupOnlyModeKey}::${startupChannelMode}::${moduleLoadMode}::${runtimeSubagentMode}::${params.pluginSdkResolution ?? "auto"}::${gatewayMethodsKey}`;
|
||||
}
|
||||
|
||||
function matchesScopedPluginRequest(params: {
|
||||
@@ -619,6 +623,7 @@ function hasExplicitCompatibilityInputs(options: PluginLoadOptions): boolean {
|
||||
options.pluginSdkResolution !== undefined ||
|
||||
options.coreGatewayHandlers !== undefined ||
|
||||
options.includeSetupOnlyChannelPlugins === true ||
|
||||
options.forceSetupOnlyChannelPlugins === true ||
|
||||
options.preferSetupRuntimeForChannelPlugins === true ||
|
||||
options.loadModules === false
|
||||
);
|
||||
@@ -634,6 +639,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
||||
});
|
||||
const onlyPluginIds = normalizePluginIdScope(options.onlyPluginIds);
|
||||
const includeSetupOnlyChannelPlugins = options.includeSetupOnlyChannelPlugins === true;
|
||||
const forceSetupOnlyChannelPlugins = options.forceSetupOnlyChannelPlugins === true;
|
||||
const preferSetupRuntimeForChannelPlugins = options.preferSetupRuntimeForChannelPlugins === true;
|
||||
const runtimeSubagentMode = resolveRuntimeSubagentMode(options.runtimeOptions);
|
||||
const coreGatewayMethodNames = Object.keys(options.coreGatewayHandlers ?? {}).toSorted();
|
||||
@@ -648,6 +654,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
||||
env,
|
||||
onlyPluginIds,
|
||||
includeSetupOnlyChannelPlugins,
|
||||
forceSetupOnlyChannelPlugins,
|
||||
preferSetupRuntimeForChannelPlugins,
|
||||
loadModules: options.loadModules,
|
||||
runtimeSubagentMode,
|
||||
@@ -663,6 +670,7 @@ function resolvePluginLoadCacheContext(options: PluginLoadOptions = {}) {
|
||||
autoEnabledReasons: options.autoEnabledReasons ?? {},
|
||||
onlyPluginIds,
|
||||
includeSetupOnlyChannelPlugins,
|
||||
forceSetupOnlyChannelPlugins,
|
||||
preferSetupRuntimeForChannelPlugins,
|
||||
shouldActivate: options.activate !== false,
|
||||
shouldLoadModules: options.loadModules !== false,
|
||||
@@ -1410,6 +1418,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
autoEnabledReasons,
|
||||
onlyPluginIds,
|
||||
includeSetupOnlyChannelPlugins,
|
||||
forceSetupOnlyChannelPlugins,
|
||||
preferSetupRuntimeForChannelPlugins,
|
||||
shouldActivate,
|
||||
shouldLoadModules,
|
||||
@@ -1740,25 +1749,28 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
}
|
||||
}
|
||||
|
||||
const registrationMode = enableState.enabled
|
||||
? shouldLoadModules &&
|
||||
!validateOnly &&
|
||||
shouldLoadChannelPluginInSetupRuntime({
|
||||
manifestChannels: manifestRecord.channels,
|
||||
setupSource: manifestRecord.setupSource,
|
||||
startupDeferConfiguredChannelFullLoadUntilAfterListen:
|
||||
manifestRecord.startupDeferConfiguredChannelFullLoadUntilAfterListen,
|
||||
cfg,
|
||||
env,
|
||||
preferSetupRuntimeForChannelPlugins,
|
||||
})
|
||||
? "setup-runtime"
|
||||
: "full"
|
||||
: includeSetupOnlyChannelPlugins &&
|
||||
const canLoadScopedSetupOnlyChannelPlugin =
|
||||
includeSetupOnlyChannelPlugins &&
|
||||
!validateOnly &&
|
||||
onlyPluginIdSet &&
|
||||
manifestRecord.channels.length > 0 &&
|
||||
(!enableState.enabled || forceSetupOnlyChannelPlugins);
|
||||
const registrationMode = canLoadScopedSetupOnlyChannelPlugin
|
||||
? "setup-only"
|
||||
: enableState.enabled
|
||||
? shouldLoadModules &&
|
||||
!validateOnly &&
|
||||
onlyPluginIdSet &&
|
||||
manifestRecord.channels.length > 0
|
||||
? "setup-only"
|
||||
shouldLoadChannelPluginInSetupRuntime({
|
||||
manifestChannels: manifestRecord.channels,
|
||||
setupSource: manifestRecord.setupSource,
|
||||
startupDeferConfiguredChannelFullLoadUntilAfterListen:
|
||||
manifestRecord.startupDeferConfiguredChannelFullLoadUntilAfterListen,
|
||||
cfg,
|
||||
env,
|
||||
preferSetupRuntimeForChannelPlugins,
|
||||
})
|
||||
? "setup-runtime"
|
||||
: "full"
|
||||
: null;
|
||||
|
||||
if (!registrationMode) {
|
||||
|
||||
Reference in New Issue
Block a user