openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
440 lines (439 loc) • 19.4 kB
JavaScript
import { r as asNullableRecord } from "./record-coerce-DItp3I4t.js";
import { c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { p as normalizeTrimmedStringList } from "./string-normalization-DsCfAx8q.js";
import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js";
import { a as parseProviderModelRef } from "./model-catalog-refs-BdjEHOKQ.js";
import { t as isChannelConfigMetadataKey } from "./config-metadata-aX1D2IMg.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { t as getCachedPluginModuleLoader } from "./plugin-module-loader-cache-lf3kAaBw.js";
import { u as coerceDoctorSessionRouteStateOwners } from "./manifest-ByRdkf9X.js";
import { d as resolvePluginDoctorContractArtifactPath } from "./installed-plugin-index-wrDsjyMD.js";
import { a as GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA } from "./ids-BVZRYG0I.js";
import { l as normalizePluginsConfig } from "./config-state-BkU1frVq.js";
import { t as unwrapDefaultModuleExport } from "./module-export-DsZgGIbX.js";
import { n as loadBundledPluginPublicArtifactModuleSync, t as loadBundledPluginPublicArtifactModuleFromCandidatesSync } from "./public-surface-loader-i-BsDNoA.js";
import { r as collectConfiguredModelRefs } from "./configured-model-refs-DEXTV_K3.js";
import { i as passesManifestOwnerBasePolicy, n as isActivatedManifestOwner } from "./manifest-owner-policy-D98oU3cV.js";
import { t as definePluginDoctorMigrationFromPlans } from "./doctor-migration-plan-adapter-1pCc-VjV.js";
import { n as hasPluginConfigMigrationSource } from "./config-contract-matches-CISXlkyE.js";
import { n as loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry-contributions-BwuDvm89.js";
import "./plugin-registry-DD_0nL_t.js";
//#region src/channels/plugins/doctor-contract-api.ts
/**
* Loads a bundled channel's public doctor contract.
*/
function loadBundledChannelDoctorContractApi(channelId) {
try {
return loadBundledPluginPublicArtifactModuleSync({
dirName: channelId,
artifactBasename: "doctor-contract-api.js"
});
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unable to resolve bundled plugin public surface ")) return;
throw error;
}
}
//#endregion
//#region src/channels/plugins/bundled-setup-policy.ts
function shouldIncludeChannelSetupFeatureForConfig(params) {
if (!params.config) return true;
const pluginId = params.plugin.id;
if (!passesManifestOwnerBasePolicy({
plugin: { id: pluginId },
normalizedConfig: params.normalizedConfig,
allowRestrictiveAllowlistBypass: true
})) return false;
let hasExplicitChannelDisable = false;
for (const channelId of params.plugin.channels ?? [pluginId]) {
const normalizedChannelId = normalizeOptionalLowercaseString(channelId);
if (!normalizedChannelId) continue;
const channelConfig = params.config.channels?.[normalizedChannelId];
if (!channelConfig || typeof channelConfig !== "object" || Array.isArray(channelConfig)) continue;
if (channelConfig.enabled === false) {
hasExplicitChannelDisable = true;
continue;
}
return true;
}
return !hasExplicitChannelDisable;
}
//#endregion
//#region src/plugins/doctor-contract-module.ts
function coerceLegacyConfigRules(value) {
if (!Array.isArray(value)) return [];
return value.filter((entry) => {
if (!entry || typeof entry !== "object") return false;
const candidate = entry;
return Array.isArray(candidate.path) && typeof candidate.message === "string";
});
}
function coerceNormalizeCompatibilityConfig(value) {
return typeof value === "function" ? value : void 0;
}
function coerceSessionStoreAgentIdsResolver(value) {
return typeof value === "function" ? value : void 0;
}
function isPluginDoctorStateMigration(value) {
if (!value || typeof value !== "object") return false;
const candidate = value;
return typeof candidate.id === "string" && candidate.id.trim().length > 0 && typeof candidate.label === "string" && candidate.label.trim().length > 0 && typeof candidate.detectLegacyState === "function" && typeof candidate.migrateLegacyState === "function";
}
function coercePluginDoctorStateMigrations(value) {
if (!Array.isArray(value)) return [];
return value.filter(isPluginDoctorStateMigration).map((migration) => ({
id: migration.id.trim(),
label: migration.label.trim(),
doctorOnly: migration.doctorOnly === true ? true : void 0,
phase: migration.phase === "after-session-repair" ? migration.phase : void 0,
detectLegacyState: migration.detectLegacyState,
migrateLegacyState: migration.migrateLegacyState
}));
}
/** Coerce a loaded doctor contract once for both registry use and declaration validation. */
function coercePluginDoctorContractModule(mod) {
const defaultExport = mod.default;
const rules = coerceLegacyConfigRules(defaultExport?.legacyConfigRules ?? mod.legacyConfigRules);
const normalizeCompatibilityConfig = coerceNormalizeCompatibilityConfig(mod.normalizeCompatibilityConfig ?? defaultExport?.normalizeCompatibilityConfig);
const resolveSessionStoreAgentIds = coerceSessionStoreAgentIdsResolver(mod.resolveSessionStoreAgentIds ?? defaultExport?.resolveSessionStoreAgentIds);
const sessionRouteStateOwners = coerceDoctorSessionRouteStateOwners(mod.sessionRouteStateOwners ?? defaultExport?.sessionRouteStateOwners);
const stateMigrations = coercePluginDoctorStateMigrations(mod.stateMigrations ?? defaultExport?.stateMigrations);
return {
rules,
normalizeCompatibilityConfig,
resolveSessionStoreAgentIds,
sessionRouteStateOwners,
stateMigrations,
summary: {
configRepair: rules.length > 0 || Boolean(normalizeCompatibilityConfig),
resolveSessionStoreAgentIds: Boolean(resolveSessionStoreAgentIds),
sessionRouteStateOwners: sessionRouteStateOwners.length > 0,
stateMigrations: stateMigrations.length > 0
}
};
}
//#endregion
//#region src/plugins/doctor-contract-registry-loader-state.ts
const pluginDoctorContractRegistryLoaderState = { moduleLoaderFactory: void 0 };
//#endregion
//#region src/plugins/doctor-contract-registry.ts
const log = createSubsystemLogger("plugins/doctor-contracts");
function isTrustedForDurableStores(record) {
return record.origin === "bundled" || record.trustedOfficialInstall === true;
}
function loadPluginDoctorContractModule(params) {
return getCachedPluginModuleLoader({
modulePath: params.modulePath,
rootDir: params.rootDir,
importerUrl: import.meta.url,
...pluginDoctorContractRegistryLoaderState.moduleLoaderFactory ? { createLoader: pluginDoctorContractRegistryLoaderState.moduleLoaderFactory } : {}
})(params.modulePath);
}
function hasLegacyElevenLabsTalkFields(raw) {
const talk = asNullableRecord(asNullableRecord(raw)?.talk);
if (!talk) return false;
return [
"voiceId",
"voiceAliases",
"modelId",
"outputFormat",
"apiKey"
].some((key) => Object.hasOwn(talk, key));
}
function collectMediaProviderIds(root, ids) {
const media = asNullableRecord(asNullableRecord(root.tools)?.media);
if (!media) return;
const modelLists = [
media.models,
asNullableRecord(media.audio)?.models,
asNullableRecord(media.image)?.models,
asNullableRecord(media.video)?.models
];
for (const models of modelLists) {
if (!Array.isArray(models)) continue;
for (const model of models) {
const provider = asNullableRecord(model)?.provider;
if (typeof provider === "string" && provider.trim()) ids.add(normalizeProviderId(provider));
}
}
}
function collectConfiguredModelProviderIds(params) {
const addRef = (value) => {
const parsed = typeof value === "string" ? parseProviderModelRef(value) : null;
if (parsed) params.ids.add(normalizeProviderId(parsed.provider));
};
for (const ref of collectConfiguredModelRefs(params.root)) addRef(ref.value);
const collectAgentPolicy = (value) => {
const allow = asNullableRecord(asNullableRecord(value)?.modelPolicy)?.allow;
if (Array.isArray(allow)) allow.forEach(addRef);
};
const agents = asNullableRecord(params.root.agents) ?? {};
collectAgentPolicy(agents.defaults);
if (Object.hasOwn(agents, "entries")) {
const entries = asNullableRecord(agents.entries);
if (entries) Object.values(entries).forEach(collectAgentPolicy);
} else if (Array.isArray(agents.list)) agents.list.forEach(collectAgentPolicy);
}
function hasScopedProviderAuthAlias(record, scopedProviderIds) {
return Object.entries(record.providerAuthAliases ?? {}).some(([rawAlias, rawTarget]) => {
const target = normalizeProviderId(rawTarget);
return scopedProviderIds.has(normalizeProviderId(rawAlias)) && target !== "" && record.providers.some((providerId) => normalizeProviderId(providerId) === target);
});
}
function collectRelevantDoctorPluginIds(raw) {
const ids = /* @__PURE__ */ new Set();
const root = asNullableRecord(raw);
if (!root) return [];
const channels = asNullableRecord(root.channels);
if (channels) for (const rawChannelId of Object.keys(channels)) {
const channelId = rawChannelId.trim();
if (channelId && !isChannelConfigMetadataKey(channelId)) ids.add(channelId);
}
const pluginsEntries = asNullableRecord(asNullableRecord(root.plugins)?.entries);
if (pluginsEntries) for (const pluginId of Object.keys(pluginsEntries)) ids.add(pluginId);
const modelProviders = asNullableRecord(asNullableRecord(root.models)?.providers);
if (modelProviders) for (const providerId of Object.keys(modelProviders)) ids.add(providerId);
collectMediaProviderIds(root, ids);
collectConfiguredModelProviderIds({
root,
ids
});
if (hasLegacyElevenLabsTalkFields(root)) ids.add("elevenlabs");
return [...ids].toSorted();
}
function collectRelevantDoctorPluginIdsForTouchedPaths(params) {
const root = asNullableRecord(params.raw);
if (!root) return [];
const ids = /* @__PURE__ */ new Set();
collectConfiguredModelProviderIds({
root,
ids
});
for (const touchedPath of params.touchedPaths) {
const [first, second, third] = touchedPath;
if (first === "channels") {
if (!second) return collectRelevantDoctorPluginIds(params.raw);
const channelId = second.trim();
if (channelId && !isChannelConfigMetadataKey(channelId)) ids.add(channelId);
continue;
}
if (first === "plugins") {
if (second !== "entries" || !third) return collectRelevantDoctorPluginIds(params.raw);
ids.add(third);
continue;
}
if (first === "models") {
if (second !== "providers" || !third) return collectRelevantDoctorPluginIds(params.raw);
ids.add(third);
continue;
}
if (first === "tools" && second === "media") {
collectMediaProviderIds(root, ids);
continue;
}
if (first === "talk" && hasLegacyElevenLabsTalkFields(root)) ids.add("elevenlabs");
}
return [...ids].toSorted();
}
/** Include manifest-owned legacy roots for config repair, never session ownership. */
function collectDoctorConfigRepairPluginIds(raw, touchedPaths) {
const config = asNullableRecord(raw);
if (!config) return [];
const ids = new Set(touchedPaths ? collectRelevantDoctorPluginIdsForTouchedPaths({
raw,
touchedPaths
}) : collectRelevantDoctorPluginIds(raw));
const registry = loadPluginManifestRegistryForPluginRegistry({
config,
includeDisabled: true
});
for (const plugin of registry.plugins) if (hasPluginConfigMigrationSource({
root: raw,
pathPatterns: plugin.configContracts?.compatibilityMigrationPaths,
touchedPaths
})) ids.add(plugin.id);
return [...ids].toSorted();
}
function loadPluginDoctorContractEntry(record, surface) {
const declaration = record.doctorContract;
if (declaration && declaration[surface] !== true) return null;
const contractSource = resolvePluginDoctorContractArtifactPath(record.rootDir);
if (!contractSource) return null;
let mod;
try {
mod = loadPluginDoctorContractModule({
modulePath: contractSource,
rootDir: record.rootDir
});
} catch (error) {
log.warn(`failed to load doctor contract for ${record.id} from ${contractSource}: ${formatErrorMessage(error)}`);
return null;
}
const { summary, ...contract } = coercePluginDoctorContractModule(mod);
if (!Object.values(summary).some(Boolean)) return null;
return {
pluginId: record.id,
...contract
};
}
function resolvePluginDoctorManifestRecords(params) {
const env = params?.env ?? process.env;
if (params?.pluginIds && params.pluginIds.length === 0) return [];
const manifestRegistry = loadPluginManifestRegistryForPluginRegistry({
config: params?.config,
workspaceDir: params?.workspaceDir,
env,
includeDisabled: true
});
const scopedPluginIds = params?.pluginIds ? new Set(params.pluginIds) : null;
const scopedProviderIds = params?.pluginIds ? new Set(params.pluginIds.map(normalizeProviderId).filter(Boolean)) : null;
return manifestRegistry.plugins.filter((record) => !(scopedPluginIds && !scopedPluginIds.has(record.id) && !(record.packageName && scopedPluginIds.has(record.packageName)) && !record.legacyPluginIds?.some((pluginId) => scopedPluginIds.has(pluginId)) && !record.channels.some((channelId) => scopedPluginIds.has(channelId)) && !record.providers.some((providerId) => scopedPluginIds.has(providerId)) && !(scopedProviderIds && hasScopedProviderAuthAlias(record, scopedProviderIds))));
}
function resolvePluginDoctorContracts(params) {
const records = resolvePluginDoctorManifestRecords(params);
const entries = loadPluginDoctorContractEntries({
records,
surface: params.surface
});
if (params.surface !== "configRepair") return entries;
const ownedChannels = new Set(records.flatMap((record) => record.channels));
const installedPluginIds = new Set(records.map((record) => record.id));
for (const { channelId, pluginId } of GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA) {
if (!Object.hasOwn(params.config?.channels ?? {}, channelId) && !Object.hasOwn(params.config?.plugins?.entries ?? {}, pluginId) || ownedChannels.has(channelId) || installedPluginIds.has(pluginId) || params.pluginIds && !params.pluginIds.includes(channelId) && !params.pluginIds.includes(pluginId)) continue;
const mod = loadBundledPluginPublicArtifactModuleFromCandidatesSync({
dirName: channelId,
artifactCandidates: ["config-doctor-api.js"],
env: params.env
});
if (!mod) continue;
const { summary: _summary, ...contract } = coercePluginDoctorContractModule(mod);
entries.push({
pluginId,
...contract
});
}
return entries;
}
function loadPluginDoctorContractEntries(params) {
const entries = [];
for (const record of params.records) {
const entry = loadPluginDoctorContractEntry(record, params.surface);
if (entry) entries.push(entry);
}
return entries;
}
function listPluginDoctorLegacyConfigRules(params) {
return resolvePluginDoctorContracts({
...params,
surface: "configRepair"
}).flatMap((entry) => entry.rules);
}
function listPluginDoctorSessionRouteStateOwners(params) {
const owners = /* @__PURE__ */ new Map();
const records = resolvePluginDoctorManifestRecords(params ?? {});
const manifestOwners = records.flatMap((record) => record.sessionRouteStateOwners ?? []);
const legacyModuleOwners = loadPluginDoctorContractEntries({
records: records.filter((record) => record.sessionRouteStateOwners === void 0),
surface: "sessionRouteStateOwners"
}).flatMap((entry) => entry.sessionRouteStateOwners);
for (const owner of [...manifestOwners, ...legacyModuleOwners]) if (!owners.has(owner.id)) owners.set(owner.id, owner);
return [...owners.values()].toSorted((left, right) => left.id.localeCompare(right.id));
}
/** Resolve plugin-owned agent IDs whose core session stores need migration. */
function listPluginDoctorSessionStoreAgentIds(params) {
const cfg = params?.config ?? {};
const agentIds = /* @__PURE__ */ new Set();
for (const entry of resolvePluginDoctorContracts({
...params,
surface: "resolveSessionStoreAgentIds"
})) {
let resolved;
try {
resolved = entry.resolveSessionStoreAgentIds?.({ cfg });
} catch {
continue;
}
for (const agentId of normalizeTrimmedStringList(resolved)) agentIds.add(agentId);
}
return [...agentIds].toSorted();
}
function loadLegacyChannelStateMigrationDetector(record) {
if (!record.setupSource) return null;
try {
const entry = unwrapDefaultModuleExport(loadPluginDoctorContractModule({
modulePath: record.setupSource,
rootDir: record.rootDir
}));
if (entry?.kind !== "bundled-channel-setup-entry" || typeof entry.loadSetupPlugin !== "function") return null;
const directDetector = typeof entry.loadLegacyStateMigrationDetector === "function" ? entry.loadLegacyStateMigrationDetector() : void 0;
if (typeof directDetector === "function") return directDetector;
if (entry.features?.legacyStateMigrations !== true) return null;
const lifecycleDetector = entry.loadSetupPlugin().lifecycle?.detectLegacyStateMigrations;
return typeof lifecycleDetector === "function" ? lifecycleDetector : null;
} catch (error) {
log.warn(`failed to load legacy state migration for ${record.id} from ${record.setupSource}: ${formatErrorMessage(error)}`);
return null;
}
}
function listPluginDoctorStateMigrationEntries(params) {
const entries = [];
const normalizedConfig = normalizePluginsConfig(params?.config?.plugins);
for (const record of resolvePluginDoctorManifestRecords(params ?? {})) {
const channelOwner = record.channels.length > 0;
if (channelOwner && !shouldIncludeChannelSetupFeatureForConfig({
plugin: record,
config: params?.config,
normalizedConfig
})) continue;
if (record.origin !== "bundled" && !isActivatedManifestOwner({
plugin: record,
normalizedConfig,
rootConfig: params?.config
})) continue;
const modern = loadPluginDoctorContractEntry(record, "stateMigrations");
if (modern?.stateMigrations.length) {
for (const migration of modern.stateMigrations) entries.push({
pluginId: modern.pluginId,
channelIds: record.channels,
trustedForDurableStores: isTrustedForDurableStores(record),
migration
});
continue;
}
if (record.doctorContract?.stateMigrations === true) continue;
if (!channelOwner || record.origin === "bundled") continue;
const detector = loadLegacyChannelStateMigrationDetector(record);
if (!detector) continue;
entries.push({
pluginId: record.id,
channelIds: record.channels,
trustedForDurableStores: isTrustedForDurableStores(record),
migration: definePluginDoctorMigrationFromPlans({
id: `${record.id}-legacy-channel-state`,
label: `${record.id} legacy channel state`,
resolvePlans: detector
})
});
}
return entries;
}
function applyPluginDoctorCompatibilityMigrations(cfg, params) {
let nextCfg = cfg;
const changes = [];
for (const entry of resolvePluginDoctorContracts({
...params,
config: params?.config ?? cfg,
surface: "configRepair"
})) {
const mutation = entry.normalizeCompatibilityConfig?.({ cfg: nextCfg });
if (!mutation || mutation.changes.length === 0) continue;
nextCfg = mutation.config;
changes.push(...mutation.changes);
}
return {
config: nextCfg,
changes
};
}
//#endregion
export { listPluginDoctorSessionRouteStateOwners as a, shouldIncludeChannelSetupFeatureForConfig as c, listPluginDoctorLegacyConfigRules as i, loadBundledChannelDoctorContractApi as l, collectDoctorConfigRepairPluginIds as n, listPluginDoctorSessionStoreAgentIds as o, collectRelevantDoctorPluginIds as r, listPluginDoctorStateMigrationEntries as s, applyPluginDoctorCompatibilityMigrations as t };