UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

465 lines (464 loc) 16.6 kB
import { l as normalizeStringEntries, p as normalizeUniqueStringEntries } from "./string-normalization-WNUDCpXX.js"; import { i as normalizeProviderId } from "./provider-id-Dq06Bcx6.js"; import { n as loadPluginManifestRegistryForPluginRegistry } from "./plugin-registry-ChcJPHWl.js"; import { t as collectPluginConfigContractMatches } from "./config-contract-matches-XtluDaw1.js"; import "./config-contracts-C_yTLoXu.js"; import { n as getCachedPluginModuleLoader, t as createPluginModuleLoaderCache } from "./plugin-module-loader-cache-lz0qhu-X.js"; import { n as listSetupProviderIds, t as listSetupCliBackendIds } from "./setup-descriptors-C0e3UIv7.js"; import { t as buildPluginApi } from "./api-builder-CX43eAAh.js"; import { fileURLToPath } from "node:url"; import fs from "node:fs"; import path from "node:path"; //#region src/plugins/setup-registry.ts const SETUP_API_EXTENSIONS = [ ".js", ".mjs", ".cjs", ".ts", ".mts", ".cts" ]; const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url); const RUNNING_FROM_BUILT_ARTIFACT = CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`) || CURRENT_MODULE_PATH.includes(`${path.sep}dist-runtime${path.sep}`); const EMPTY_RUNTIME = {}; const NOOP_LOGGER = { info() {}, warn() {}, error() {} }; const moduleLoaders = createPluginModuleLoaderCache(); let moduleLoaderFactoryForTest; function clearPluginSetupRegistryCache() { moduleLoaders.clear(); } function setPluginSetupRegistryModuleLoaderFactoryForTest(factory) { moduleLoaderFactoryForTest = factory; moduleLoaders.clear(); } function getModuleLoader(modulePath) { return getCachedPluginModuleLoader({ cache: moduleLoaders, modulePath, importerUrl: import.meta.url, ...moduleLoaderFactoryForTest ? { createLoader: moduleLoaderFactoryForTest } : {} }); } function resolveSetupApiPath(rootDir, options) { const orderedExtensions = RUNNING_FROM_BUILT_ARTIFACT ? SETUP_API_EXTENSIONS : [...SETUP_API_EXTENSIONS.slice(3), ...SETUP_API_EXTENSIONS.slice(0, 3)]; const findSetupApi = (candidateRootDir) => { for (const extension of orderedExtensions) { const candidate = path.join(candidateRootDir, `setup-api${extension}`); if (fs.existsSync(candidate)) return candidate; } return null; }; const direct = findSetupApi(rootDir); if (direct) return direct; if (options?.includeBundledSourceFallback === false) return null; const bundledExtensionDir = path.basename(rootDir); const repoRootCandidates = [path.resolve(path.dirname(CURRENT_MODULE_PATH), "..", "..")]; for (const repoRoot of repoRootCandidates) { const sourceExtensionRoot = path.join(repoRoot, "extensions", bundledExtensionDir); if (sourceExtensionRoot === rootDir) continue; const sourceFallback = findSetupApi(sourceExtensionRoot); if (sourceFallback) return sourceFallback; } return null; } function collectConfiguredPluginEntryIds(config) { const entries = config.plugins?.entries; if (!entries || typeof entries !== "object") return []; return normalizeStringEntries(Object.keys(entries)).toSorted(); } function resolveRelevantSetupMigrationPluginIds(params) { const ids = new Set(collectConfiguredPluginEntryIds(params.config)); const registry = loadSetupManifestRegistry({ config: params.config, workspaceDir: params.workspaceDir, env: params.env }); for (const plugin of registry.plugins) { const paths = plugin.configContracts?.compatibilityMigrationPaths; if (!paths?.length) continue; if (paths.some((pathPattern) => collectPluginConfigContractMatches({ root: params.config, pathPattern }).length > 0)) ids.add(plugin.id); } return [...ids].toSorted(); } function resolveRegister(mod) { if (typeof mod === "function") return { register: mod }; if (mod && typeof mod === "object" && typeof mod.register === "function") return { definition: mod, register: mod.register.bind(mod) }; return {}; } function rewriteBundledSetupSourceToBuiltArtifact(source, record) { if (record.origin !== "bundled") return source; const rootDir = path.resolve(record.rootDir); const sourcePath = path.resolve(source); const extensionsDir = path.dirname(rootDir); if (path.basename(extensionsDir) !== "extensions") return source; const packageRoot = path.dirname(extensionsDir); if (path.basename(packageRoot) === "dist" || path.basename(packageRoot) === "dist-runtime") return source; const relativeSource = path.relative(rootDir, sourcePath); if (relativeSource === "" || relativeSource.startsWith("..") || path.isAbsolute(relativeSource)) return source; const artifactRelativePath = relativeSource.replace(/\.[^.]+$/u, ".js"); for (const artifactRootName of ["dist-runtime", "dist"]) { const candidate = path.join(packageRoot, artifactRootName, "extensions", path.basename(rootDir), artifactRelativePath); if (fs.existsSync(candidate)) return candidate; } return source; } function resolveLoadableSetupRuntimeSource(record) { const source = record.setupSource ?? resolveSetupApiPath(record.rootDir); return source ? rewriteBundledSetupSourceToBuiltArtifact(source, record) : null; } function resolveDeclaredSetupRuntimeSource(record) { return record.setupSource ?? resolveSetupApiPath(record.rootDir, { includeBundledSourceFallback: false }); } function resolveSetupRegistration(record) { if (record.setup?.requiresRuntime === false) return null; const setupSource = resolveLoadableSetupRuntimeSource(record); if (!setupSource) return null; let mod; try { mod = getModuleLoader(setupSource)(setupSource); } catch { return null; } const resolved = resolveRegister(mod.default ?? mod); if (!resolved.register) return null; if (resolved.definition?.id && resolved.definition.id !== record.id) return null; return { setupSource, register: resolved.register }; } function buildSetupPluginApi(params) { return buildPluginApi({ id: params.record.id, name: params.record.name ?? params.record.id, version: params.record.version, description: params.record.description, source: params.setupSource, rootDir: params.record.rootDir, registrationMode: "setup-only", config: {}, runtime: EMPTY_RUNTIME, logger: NOOP_LOGGER, resolvePath: (input) => input, handlers: params.handlers }); } function ignoreAsyncSetupRegisterResult(result) { if (!result || typeof result.then !== "function") return; Promise.resolve(result).catch(() => void 0); } function matchesProvider(provider, providerId) { const normalized = normalizeProviderId(providerId); if (normalizeProviderId(provider.id) === normalized) return true; return [...provider.aliases ?? [], ...provider.hookAliases ?? []].some((alias) => normalizeProviderId(alias) === normalized); } function loadSetupManifestRegistry(params) { const env = params?.env ?? process.env; return loadPluginManifestRegistryForPluginRegistry({ config: params?.config, workspaceDir: params?.workspaceDir, env, pluginIds: params?.pluginIds, includeDisabled: true }); } function findUniqueSetupManifestOwner(params) { const matches = params.registry.plugins.filter((entry) => params.listIds(entry).some((id) => normalizeProviderId(id) === params.normalizedId)); if (matches.length === 0) return; return matches.length === 1 ? matches[0] : void 0; } function mapNormalizedIds(ids) { const mapped = /* @__PURE__ */ new Map(); for (const id of ids) { const normalized = normalizeProviderId(id); if (!normalized || mapped.has(normalized)) continue; mapped.set(normalized, id); } return mapped; } function pushDescriptorRuntimeDisabledDiagnostic(params) { if (!resolveDeclaredSetupRuntimeSource(params.record)) return; params.diagnostics.push({ pluginId: params.record.id, code: "setup-descriptor-runtime-disabled", message: "setup.requiresRuntime is false, so OpenClaw ignored the plugin setup runtime entry. Remove setup-api/openclaw.setupEntry or set requiresRuntime true if setup lookup still needs plugin code." }); } function pushSetupDescriptorDriftDiagnostics(params) { const declaredProviderIds = params.record.setup?.providers?.map((entry) => entry.id); if (declaredProviderIds) { for (const declaredId of declaredProviderIds) if (!params.providers.some((provider) => matchesProvider(provider, declaredId))) params.diagnostics.push({ pluginId: params.record.id, code: "setup-descriptor-provider-missing-runtime", declaredId, message: `setup.providers declares "${declaredId}" but setup runtime did not register a matching provider.` }); for (const provider of params.providers) if (!declaredProviderIds.some((declaredId) => matchesProvider(provider, declaredId))) params.diagnostics.push({ pluginId: params.record.id, code: "setup-descriptor-provider-runtime-undeclared", runtimeId: provider.id, message: `setup runtime registered provider "${provider.id}" but setup.providers does not declare it.` }); } const declaredCliBackendIds = params.record.setup?.cliBackends; if (declaredCliBackendIds) { const declaredCliBackends = mapNormalizedIds(declaredCliBackendIds); const runtimeCliBackends = mapNormalizedIds(params.cliBackends.map((backend) => backend.id)); for (const [normalized, declaredId] of declaredCliBackends) if (!runtimeCliBackends.has(normalized)) params.diagnostics.push({ pluginId: params.record.id, code: "setup-descriptor-cli-backend-missing-runtime", declaredId, message: `setup.cliBackends declares "${declaredId}" but setup runtime did not register a matching CLI backend.` }); for (const [normalized, runtimeId] of runtimeCliBackends) if (!declaredCliBackends.has(normalized)) params.diagnostics.push({ pluginId: params.record.id, code: "setup-descriptor-cli-backend-runtime-undeclared", runtimeId, message: `setup runtime registered CLI backend "${runtimeId}" but setup.cliBackends does not declare it.` }); } } function resolvePluginSetupRegistry(params) { const env = params?.env ?? process.env; const scopedPluginIds = params?.pluginIds ? new Set(normalizeUniqueStringEntries(params.pluginIds)) : null; if (scopedPluginIds && scopedPluginIds.size === 0) return { providers: [], cliBackends: [], configMigrations: [], autoEnableProbes: [], diagnostics: [] }; const providers = []; const cliBackends = []; const configMigrations = []; const autoEnableProbes = []; const diagnostics = []; const providerKeys = /* @__PURE__ */ new Set(); const cliBackendKeys = /* @__PURE__ */ new Set(); const manifestRegistry = params?.manifestRegistry ?? loadSetupManifestRegistry({ config: params?.config, workspaceDir: params?.workspaceDir, env, pluginIds: params?.pluginIds }); for (const record of manifestRegistry.plugins) { if (scopedPluginIds && !scopedPluginIds.has(record.id)) continue; if (record.setup?.requiresRuntime === false) { pushDescriptorRuntimeDisabledDiagnostic({ record, diagnostics }); continue; } const setupRegistration = resolveSetupRegistration(record); if (!setupRegistration) continue; const recordProviders = []; const recordCliBackends = []; const api = buildSetupPluginApi({ record, setupSource: setupRegistration.setupSource, handlers: { registerProvider(provider) { const key = `${record.id}:${normalizeProviderId(provider.id)}`; if (providerKeys.has(key)) return; providerKeys.add(key); providers.push({ pluginId: record.id, provider }); recordProviders.push(provider); }, registerCliBackend(backend) { const key = `${record.id}:${normalizeProviderId(backend.id)}`; if (cliBackendKeys.has(key)) return; cliBackendKeys.add(key); cliBackends.push({ pluginId: record.id, backend }); recordCliBackends.push(backend); }, registerConfigMigration(migrate) { configMigrations.push({ pluginId: record.id, migrate }); }, registerAutoEnableProbe(probe) { autoEnableProbes.push({ pluginId: record.id, probe }); } } }); try { const result = setupRegistration.register(api); if (result && typeof result.then === "function") ignoreAsyncSetupRegisterResult(result); } catch { continue; } pushSetupDescriptorDriftDiagnostics({ record, providers: recordProviders, cliBackends: recordCliBackends, diagnostics }); } return { providers, cliBackends, configMigrations, autoEnableProbes, diagnostics }; } function resolvePluginSetupProvider(params) { const env = params.env ?? process.env; const normalizedProvider = normalizeProviderId(params.provider); const record = findUniqueSetupManifestOwner({ registry: loadSetupManifestRegistry({ config: params.config, workspaceDir: params.workspaceDir, env, pluginIds: params.pluginIds }), normalizedId: normalizedProvider, listIds: listSetupProviderIds }); if (!record) return; const setupRegistration = resolveSetupRegistration(record); if (!setupRegistration) return; let matchedProvider; const localProviderKeys = /* @__PURE__ */ new Set(); const api = buildSetupPluginApi({ record, setupSource: setupRegistration.setupSource, handlers: { registerProvider(provider) { const key = normalizeProviderId(provider.id); if (localProviderKeys.has(key)) return; localProviderKeys.add(key); if (matchesProvider(provider, normalizedProvider)) matchedProvider = provider; }, registerConfigMigration() {}, registerAutoEnableProbe() {} } }); try { const result = setupRegistration.register(api); if (result && typeof result.then === "function") ignoreAsyncSetupRegisterResult(result); } catch { return; } return matchedProvider; } function resolvePluginSetupCliBackend(params) { const normalized = normalizeProviderId(params.backend); const env = params.env ?? process.env; const record = findUniqueSetupManifestOwner({ registry: loadSetupManifestRegistry({ config: params.config, workspaceDir: params.workspaceDir, env }), normalizedId: normalized, listIds: listSetupCliBackendIds }); if (!record) return; const setupRegistration = resolveSetupRegistration(record); if (!setupRegistration) return; let matchedBackend; const localBackendKeys = /* @__PURE__ */ new Set(); const api = buildSetupPluginApi({ record, setupSource: setupRegistration.setupSource, handlers: { registerProvider() {}, registerConfigMigration() {}, registerAutoEnableProbe() {}, registerCliBackend(backend) { const key = normalizeProviderId(backend.id); if (localBackendKeys.has(key)) return; localBackendKeys.add(key); if (key === normalized) matchedBackend = backend; } } }); try { const result = setupRegistration.register(api); if (result && typeof result.then === "function") ignoreAsyncSetupRegisterResult(result); } catch { return; } return (matchedBackend ? { pluginId: record.id, backend: matchedBackend } : null) ?? void 0; } function runPluginSetupConfigMigrations(params) { let next = params.config; const changes = []; const pluginIds = resolveRelevantSetupMigrationPluginIds(params); if (pluginIds.length === 0) return { config: next, changes }; for (const entry of resolvePluginSetupRegistry({ config: params.config, workspaceDir: params.workspaceDir, env: params.env, pluginIds }).configMigrations) { const migration = entry.migrate(next); if (!migration || migration.changes.length === 0) continue; next = migration.config; changes.push(...migration.changes); } return { config: next, changes }; } function resolvePluginSetupAutoEnableReasons(params) { const env = params.env ?? process.env; const reasons = []; const seen = /* @__PURE__ */ new Set(); for (const entry of resolvePluginSetupRegistry({ config: params.config, workspaceDir: params.workspaceDir, env, pluginIds: params.pluginIds, manifestRegistry: params.manifestRegistry }).autoEnableProbes) { const raw = entry.probe({ config: params.config, env }); const values = Array.isArray(raw) ? raw : raw ? [raw] : []; for (const reason of values) { const normalized = reason.trim(); if (!normalized) continue; const key = `${entry.pluginId}:${normalized}`; if (seen.has(key)) continue; seen.add(key); reasons.push({ pluginId: entry.pluginId, reason: normalized }); } } return reasons; } //#endregion export { resolvePluginSetupRegistry as a, resolvePluginSetupProvider as i, resolvePluginSetupAutoEnableReasons as n, runPluginSetupConfigMigrations as o, resolvePluginSetupCliBackend as r, setPluginSetupRegistryModuleLoaderFactoryForTest as s, clearPluginSetupRegistryCache as t };