UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

440 lines (439 loc) 19.4 kB
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 };