openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
264 lines (263 loc) • 10.4 kB
JavaScript
import { s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { a as getBundledChannelSetupPlugin, r as getBundledChannelPlugin } from "./bundled-DcYUmgzC.js";
import { n as getLoadedChannelPlugin } from "./registry-MkxNn1Ue.js";
import { r as resolveReadOnlyChannelPluginsForConfig } from "./read-only-7UA2_pQ4.js";
//#region src/commands/doctor/shared/channel-doctor.ts
const channelDoctorFunctionKeys = new Set([
"normalizeCompatibilityConfig",
"collectPreviewWarnings",
"collectMutableAllowlistWarnings",
"repairConfig",
"runConfigSequence",
"cleanStaleConfig",
"collectEmptyAllowlistExtraWarnings",
"shouldSkipDefaultEmptyGroupAllowlistWarning"
]);
const channelDoctorBooleanKeys = new Set(["groupAllowFromFallbackToAllowFrom", "warnOnEmptyGroupSenderAllowlist"]);
const channelDoctorEnumValues = {
dmAllowFromMode: new Set([
"topOnly",
"topOrNested",
"nestedOnly"
]),
groupModel: new Set([
"sender",
"route",
"hybrid"
])
};
function collectConfiguredChannelIds(cfg) {
if (cfg.plugins?.enabled === false) return [];
const channels = cfg.channels && typeof cfg.channels === "object" && !Array.isArray(cfg.channels) ? cfg.channels : null;
if (!channels) return [];
const channelEntries = channels;
return Object.keys(channels).filter((channelId) => {
if (channelId === "defaults") return false;
if (isChannelDoctorBlockedByConfig(channelId, cfg)) return false;
const entry = channelEntries[channelId];
return !entry || typeof entry !== "object" || Array.isArray(entry) || entry.enabled !== false;
}).toSorted();
}
function isChannelDoctorBlockedByConfig(channelId, cfg) {
if (cfg.plugins?.enabled === false) return true;
const normalizedChannelId = normalizeOptionalLowercaseString(channelId) ?? channelId;
if (cfg.plugins?.entries?.[normalizedChannelId]?.enabled === false) return true;
const channelEntry = cfg.channels?.[normalizedChannelId];
return Boolean(channelEntry && typeof channelEntry === "object" && !Array.isArray(channelEntry)) && channelEntry.enabled === false;
}
function safeGetLoadedChannelPlugin(id) {
try {
return getLoadedChannelPlugin(id);
} catch {
return;
}
}
function safeGetBundledChannelSetupPlugin(id) {
try {
return getBundledChannelSetupPlugin(id);
} catch {
return;
}
}
function safeGetBundledChannelPlugin(id) {
try {
return getBundledChannelPlugin(id);
} catch {
return;
}
}
function safeListReadOnlyChannelPlugins(context) {
try {
return resolveReadOnlyChannelPluginsForConfig(context.cfg, {
...context.env ? { env: context.env } : {},
includePersistedAuthState: false,
includeSetupFallbackPlugins: true
}).plugins;
} catch {
return [];
}
}
function listReadOnlyChannelPluginsById(context) {
return new Map(safeListReadOnlyChannelPlugins(context).map((plugin) => [plugin.id, plugin]));
}
function mergeDoctorAdapters(adapters) {
const merged = {};
for (const adapter of adapters) {
if (!adapter) continue;
for (const [key, value] of Object.entries(adapter)) {
if (merged[key] !== void 0) continue;
if (!isValidChannelDoctorAdapterValue(key, value)) continue;
merged[key] = value;
}
}
return Object.keys(merged).length > 0 ? merged : void 0;
}
function isValidChannelDoctorAdapterValue(key, value) {
if (value == null) return false;
if (channelDoctorFunctionKeys.has(key)) return typeof value === "function";
if (channelDoctorBooleanKeys.has(key)) return typeof value === "boolean";
const enumValues = channelDoctorEnumValues[key];
if (enumValues) return typeof value === "string" && enumValues.has(value);
if (key === "legacyConfigRules") return Array.isArray(value);
return false;
}
function listChannelDoctorEntries(channelIds, context, options = {}) {
if (channelIds.length === 0) return [];
const selectedIds = new Set(channelIds.filter((id) => !isChannelDoctorBlockedByConfig(id, context.cfg)));
if (selectedIds.size === 0) return [];
const readOnlyPluginsById = options.readOnlyPluginsById ?? listReadOnlyChannelPluginsById(context);
const entries = [];
for (const id of selectedIds) {
const doctor = mergeDoctorAdapters([
readOnlyPluginsById.get(id)?.doctor,
safeGetLoadedChannelPlugin(id)?.doctor,
safeGetBundledChannelSetupPlugin(id)?.doctor,
safeGetBundledChannelPlugin(id)?.doctor
]);
if (!doctor) continue;
entries.push({ doctor });
}
return entries;
}
function toPluginEmptyAllowlistContext({ cfg: _cfg, ...params }) {
return params;
}
function collectEmptyAllowlistExtraWarningsForEntries(entries, params) {
const warnings = [];
const pluginParams = toPluginEmptyAllowlistContext(params);
for (const entry of entries) {
const lines = entry.doctor.collectEmptyAllowlistExtraWarnings?.(pluginParams);
if (lines?.length) warnings.push(...lines);
}
return warnings;
}
function shouldSkipDefaultEmptyGroupAllowlistWarningForEntries(entries, params) {
const pluginParams = toPluginEmptyAllowlistContext(params);
return entries.some((entry) => entry.doctor.shouldSkipDefaultEmptyGroupAllowlistWarning?.(pluginParams) === true);
}
/** Build cached empty-allowlist hooks backed by channel doctor adapters. */
function createChannelDoctorEmptyAllowlistPolicyHooks(context) {
const readOnlyPluginsById = listReadOnlyChannelPluginsById(context);
const entriesByChannel = /* @__PURE__ */ new Map();
const entriesForChannel = (channelName) => {
const existing = entriesByChannel.get(channelName);
if (existing) return existing;
const entries = listChannelDoctorEntries([channelName], context, { readOnlyPluginsById });
entriesByChannel.set(channelName, entries);
return entries;
};
return {
extraWarningsForAccount: (params) => collectEmptyAllowlistExtraWarningsForEntries(entriesForChannel(params.channelName), params),
shouldSkipDefaultEmptyGroupAllowlistWarning: (params) => shouldSkipDefaultEmptyGroupAllowlistWarningForEntries(entriesForChannel(params.channelName), params)
};
}
/** Run interactive/non-interactive channel setup repair sequences and collect notes. */
async function runChannelDoctorConfigSequences(params) {
const changeNotes = [];
const warningNotes = [];
for (const entry of listChannelDoctorEntries(collectConfiguredChannelIds(params.cfg), {
cfg: params.cfg,
env: params.env
})) {
const result = await entry.doctor.runConfigSequence?.(params);
if (!result) continue;
changeNotes.push(...result.changeNotes);
warningNotes.push(...result.warningNotes);
}
return {
changeNotes,
warningNotes
};
}
/** Collect compatibility migrations from configured channel doctor adapters in order. */
function collectChannelDoctorCompatibilityMutations(cfg, options = {}) {
const channelIds = collectConfiguredChannelIds(cfg);
if (channelIds.length === 0) return [];
const mutations = [];
let nextCfg = cfg;
for (const entry of listChannelDoctorEntries(channelIds, {
cfg,
env: options.env
})) {
const mutation = entry.doctor.normalizeCompatibilityConfig?.({ cfg: nextCfg });
if (!mutation || mutation.changes.length === 0) continue;
mutations.push(mutation);
nextCfg = mutation.config;
}
return mutations;
}
/** Collect stale channel config cleanup mutations from configured channel doctor adapters. */
async function collectChannelDoctorStaleConfigMutations(cfg, options = {}) {
const mutations = [];
let nextCfg = cfg;
for (const entry of listChannelDoctorEntries(collectConfiguredChannelIds(cfg), {
cfg,
env: options.env
})) {
const mutation = await entry.doctor.cleanStaleConfig?.({ cfg: nextCfg });
if (!mutation || mutation.changes.length === 0) continue;
mutations.push(mutation);
nextCfg = mutation.config;
}
return mutations;
}
/** Collect channel-specific doctor preview warnings for configured channels. */
async function collectChannelDoctorPreviewWarnings(params) {
const warnings = [];
for (const entry of listChannelDoctorEntries(collectConfiguredChannelIds(params.cfg), {
cfg: params.cfg,
env: params.env
})) {
const lines = await entry.doctor.collectPreviewWarnings?.(params);
if (lines?.length) warnings.push(...lines);
}
return warnings;
}
/** Collect warnings for mutable channel allowlists that doctor cannot safely edit. */
async function collectChannelDoctorMutableAllowlistWarnings(params) {
const warnings = [];
for (const entry of listChannelDoctorEntries(collectConfiguredChannelIds(params.cfg), {
cfg: params.cfg,
env: params.env
})) {
const lines = await entry.doctor.collectMutableAllowlistWarnings?.(params);
if (lines?.length) warnings.push(...lines);
}
return warnings;
}
/** Collect channel repair mutations and warning-only repair results from doctor adapters. */
async function collectChannelDoctorRepairMutations(params) {
const mutations = [];
let nextCfg = params.cfg;
for (const entry of listChannelDoctorEntries(collectConfiguredChannelIds(params.cfg), {
cfg: params.cfg,
env: params.env
})) {
const mutation = await entry.doctor.repairConfig?.({
cfg: nextCfg,
doctorFixCommand: params.doctorFixCommand,
...params.env ? { env: params.env } : {}
});
if (!mutation || mutation.changes.length === 0) {
if (mutation?.warnings?.length) mutations.push({
config: nextCfg,
changes: [],
warnings: mutation.warnings
});
continue;
}
mutations.push(mutation);
nextCfg = mutation.config;
}
return mutations;
}
/** Collect plugin-provided empty allowlist warning lines for one channel/account context. */
function collectChannelDoctorEmptyAllowlistExtraWarnings(params) {
return collectEmptyAllowlistExtraWarningsForEntries(listChannelDoctorEntries([params.channelName], { cfg: params.cfg ?? {} }), params);
}
/** Return true when a channel doctor owns empty group-allowlist warning behavior. */
function shouldSkipChannelDoctorDefaultEmptyGroupAllowlistWarning(params) {
return shouldSkipDefaultEmptyGroupAllowlistWarningForEntries(listChannelDoctorEntries([params.channelName], { cfg: params.cfg ?? {} }), params);
}
//#endregion
export { collectChannelDoctorRepairMutations as a, runChannelDoctorConfigSequences as c, collectChannelDoctorPreviewWarnings as i, shouldSkipChannelDoctorDefaultEmptyGroupAllowlistWarning as l, collectChannelDoctorEmptyAllowlistExtraWarnings as n, collectChannelDoctorStaleConfigMutations as o, collectChannelDoctorMutableAllowlistWarnings as r, createChannelDoctorEmptyAllowlistPolicyHooks as s, collectChannelDoctorCompatibilityMutations as t };