




















@@ -1,6 +1,7 @@
11import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
22import type { OpenClawConfig } from "../../config/types.openclaw.js";
33import {
4+hasExplicitChannelConfig,
45listConfiguredChannelIdsForReadOnlyScope,
56resolveDiscoverableScopedChannelPluginIds,
67} from "../../plugins/channel-plugin-ids.js";
@@ -9,6 +10,7 @@ import {
910loadPluginManifestRegistry,
1011type PluginManifestRecord,
1112} from "../../plugins/manifest-registry.js";
13+import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
1214import { getBundledChannelSetupPlugin } from "./bundled.js";
1315import { listChannelPlugins } from "./registry.js";
1416import type { ChannelPlugin } from "./types.plugin.js";
@@ -110,6 +112,99 @@ function restoreReboundChannelConfig(params: {
110112};
111113}
112114115+function getChannelConfigRecord(cfg: OpenClawConfig, channelId: string): Record<string, unknown> {
116+const channels = cfg.channels;
117+if (!channels || typeof channels !== "object" || Array.isArray(channels)) {
118+return {};
119+}
120+const entry = (channels as Record<string, unknown>)[channelId];
121+return entry && typeof entry === "object" && !Array.isArray(entry)
122+ ? (entry as Record<string, unknown>)
123+ : {};
124+}
125+126+function listManifestChannelAccountIds(cfg: OpenClawConfig, channelId: string): string[] {
127+const channelConfig = getChannelConfigRecord(cfg, channelId);
128+const accounts = channelConfig.accounts;
129+if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
130+return Object.keys(accounts).toSorted((left, right) => left.localeCompare(right));
131+}
132+return hasExplicitChannelConfig({ config: cfg, channelId }) ? [DEFAULT_ACCOUNT_ID] : [];
133+}
134+135+function resolveManifestChannelAccountConfig(params: {
136+cfg: OpenClawConfig;
137+channelId: string;
138+accountId?: string | null;
139+}): Record<string, unknown> {
140+const channelConfig = getChannelConfigRecord(params.cfg, params.channelId);
141+const resolvedAccountId = params.accountId?.trim() || DEFAULT_ACCOUNT_ID;
142+const accounts = channelConfig.accounts;
143+if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
144+const accountConfig = (accounts as Record<string, unknown>)[resolvedAccountId];
145+if (accountConfig && typeof accountConfig === "object" && !Array.isArray(accountConfig)) {
146+return accountConfig as Record<string, unknown>;
147+}
148+}
149+return channelConfig;
150+}
151+152+function buildManifestChannelPlugin(params: {
153+record: PluginManifestRecord;
154+channelId: string;
155+}): ChannelPlugin | undefined {
156+const channelConfig = params.record.channelConfigs?.[params.channelId];
157+if (!channelConfig) {
158+return undefined;
159+}
160+const label = channelConfig.label?.trim() || params.record.name || params.channelId;
161+const blurb = channelConfig.description?.trim() || params.record.description || "";
162+return {
163+id: params.channelId,
164+meta: {
165+id: params.channelId,
166+ label,
167+selectionLabel: label,
168+docsPath: `/channels/${params.channelId}`,
169+ blurb,
170+ ...(channelConfig.preferOver?.length ? { preferOver: channelConfig.preferOver } : {}),
171+},
172+capabilities: { chatTypes: ["direct"] },
173+configSchema: {
174+schema: channelConfig.schema,
175+ ...(channelConfig.uiHints ? { uiHints: channelConfig.uiHints } : {}),
176+ ...(channelConfig.runtime ? { runtime: channelConfig.runtime } : {}),
177+},
178+config: {
179+listAccountIds: (cfg) => listManifestChannelAccountIds(cfg, params.channelId),
180+defaultAccountId: () => DEFAULT_ACCOUNT_ID,
181+resolveAccount: (cfg, accountId) => ({
182+accountId: accountId?.trim() || DEFAULT_ACCOUNT_ID,
183+config: resolveManifestChannelAccountConfig({
184+ cfg,
185+channelId: params.channelId,
186+ accountId,
187+}),
188+}),
189+isEnabled: (_account, cfg) => getChannelConfigRecord(cfg, params.channelId).enabled !== false,
190+isConfigured: (_account, cfg) =>
191+hasExplicitChannelConfig({
192+config: cfg,
193+channelId: params.channelId,
194+}),
195+hasConfiguredState: ({ cfg }) =>
196+hasExplicitChannelConfig({
197+config: cfg,
198+channelId: params.channelId,
199+}),
200+},
201+};
202+}
203+204+function canUseManifestChannelPlugin(record: PluginManifestRecord): boolean {
205+return record.setup?.requiresRuntime === false || !record.setupSource;
206+}
207+113208function rebindChannelPluginConfig(
114209config: ChannelPlugin["config"],
115210sourceChannelId: string,
@@ -283,6 +378,34 @@ function addSetupChannelPlugins(
283378}
284379}
285380381+function addManifestChannelPlugins(
382+byId: Map<string, ChannelPlugin>,
383+records: readonly PluginManifestRecord[],
384+options: {
385+pluginIds: ReadonlySet<string>;
386+channelIds: readonly string[];
387+},
388+): void {
389+const channelIds = new Set(options.channelIds);
390+for (const record of records) {
391+if (!options.pluginIds.has(record.id)) {
392+continue;
393+}
394+if (!canUseManifestChannelPlugin(record)) {
395+continue;
396+}
397+for (const channelId of record.channels) {
398+if (!channelIds.has(channelId)) {
399+continue;
400+}
401+addChannelPlugins(byId, [buildManifestChannelPlugin({ record, channelId })], {
402+onlyIds: channelIds,
403+allowOverwrite: false,
404+});
405+}
406+}
407+}
408+286409function resolveReadOnlyWorkspaceDir(
287410cfg: OpenClawConfig,
288411options: ReadOnlyChannelPluginOptions,
@@ -389,8 +512,16 @@ export function resolveReadOnlyChannelPluginsForConfig(
389512cache: options.cache,
390513});
391514if (externalPluginIds.length > 0) {
392-const missingChannelIdSet = new Set(missingConfiguredChannelIds);
393515const externalPluginIdSet = new Set(externalPluginIds);
516+addManifestChannelPlugins(byId, externalManifestRecords, {
517+pluginIds: externalPluginIdSet,
518+channelIds: missingConfiguredChannelIds,
519+});
520+521+const setupMissingChannelIds = missingConfiguredChannelIds.filter(
522+(channelId) => !byId.has(channelId),
523+);
524+const missingChannelIdSet = new Set(setupMissingChannelIds);
394525const ownedChannelIdsByPluginId = new Map(
395526externalManifestRecords
396527.filter((record) => externalPluginIdSet.has(record.id))
@@ -402,22 +533,24 @@ export function resolveReadOnlyChannelPluginsForConfig(
402533[pluginId, channelIds.filter((channelId) => missingChannelIdSet.has(channelId))] as const,
403534),
404535);
405-const registry = loadOpenClawPlugins({
406-config: cfg,
407-activationSourceConfig: options.activationSourceConfig ?? cfg,
408- env,
409- workspaceDir,
410-cache: false,
411-activate: false,
412-includeSetupOnlyChannelPlugins: true,
413-forceSetupOnlyChannelPlugins: true,
414-requireSetupEntryForSetupOnlyChannelPlugins: true,
415-onlyPluginIds: externalPluginIds,
416-});
417-addSetupChannelPlugins(byId, registry.channelSetups, {
418- ownedChannelIdsByPluginId,
419- ownedMissingChannelIdsByPluginId,
420-});
536+if (setupMissingChannelIds.length > 0) {
537+const registry = loadOpenClawPlugins({
538+config: cfg,
539+activationSourceConfig: options.activationSourceConfig ?? cfg,
540+ env,
541+ workspaceDir,
542+cache: false,
543+activate: false,
544+includeSetupOnlyChannelPlugins: true,
545+forceSetupOnlyChannelPlugins: true,
546+requireSetupEntryForSetupOnlyChannelPlugins: true,
547+onlyPluginIds: externalPluginIds,
548+});
549+addSetupChannelPlugins(byId, registry.channelSetups, {
550+ ownedChannelIdsByPluginId,
551+ ownedMissingChannelIdsByPluginId,
552+});
553+}
421554}
422555423556const plugins = [...byId.values()];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。