UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

612 lines (611 loc) 21.3 kB
import { l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js"; import { t as discoverOpenClawPlugins } from "./discovery-BDSfxwT6.js"; import { i as openRootFileSync } from "./root-file-jRMCpJW4.js"; import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js"; import "./boundary-file-read-CBe_wA_B.js"; import { t as loadPluginManifestRegistry } from "./manifest-registry-BywZIeAq.js"; import { n as resolveOpenClawDevSourceRoot } from "./dev-source-root-BS5CseCe.js"; import { n as getCachedPluginModuleLoader, t as createPluginModuleLoaderCache } from "./plugin-module-loader-cache-lz0qhu-X.js"; import { b as shouldPreferNativeModuleLoad, t as buildPluginLoaderAliasMap } from "./sdk-alias-ZgondhPg.js"; import { n as resolveBundledPluginRepoEntryPath } from "./bundled-plugin-metadata-CnZA4QSH.js"; import { t as unwrapDefaultModuleExport } from "./module-export-DsZgGIbX.js"; import { n as withBundledPluginVitestCompat, t as withBundledPluginEnablementCompat } from "./bundled-compat-DLBZcHgM.js"; import { t as buildPluginApi } from "./api-builder-CX43eAAh.js"; import { I as createEmptyPluginRegistry } from "./runtime-B2ROiq5l.js"; import { n as normalizePluginToolContractNames, o as normalizeAgentToolResultMiddlewareRuntimes, t as findUndeclaredPluginToolNames } from "./tool-contracts-CiMkRadV.js"; import { fileURLToPath } from "node:url"; import fs from "node:fs"; //#region src/plugins/captured-registration.ts function createCapturedPluginRegistration(params) { const providers = []; const agentHarnesses = []; const cliRegistrars = []; const cliBackends = []; const textTransforms = []; const codexAppServerExtensionFactories = []; const agentToolResultMiddlewares = []; const embeddingProviders = []; const speechProviders = []; const realtimeTranscriptionProviders = []; const realtimeVoiceProviders = []; const mediaUnderstandingProviders = []; const transcriptSourceProviders = []; const imageGenerationProviders = []; const videoGenerationProviders = []; const musicGenerationProviders = []; const webFetchProviders = []; const webSearchProviders = []; const migrationProviders = []; const memoryEmbeddingProviders = []; const sessionExtensions = []; const trustedToolPolicies = []; const toolMetadata = []; const controlUiDescriptors = []; const runtimeLifecycles = []; const agentEventSubscriptions = []; const sessionSchedulerJobs = []; const sessionActions = []; let capturedSessionTurnCount = 0; const tools = []; const modelCatalogProviders = []; const pluginId = params?.id ?? "captured-plugin-registration"; const pluginName = params?.name ?? "Captured Plugin Registration"; const pluginSource = params?.source ?? "captured-plugin-registration"; return { providers, agentHarnesses, cliRegistrars, cliBackends, textTransforms, codexAppServerExtensionFactories, agentToolResultMiddlewares, embeddingProviders, speechProviders, realtimeTranscriptionProviders, realtimeVoiceProviders, mediaUnderstandingProviders, transcriptSourceProviders, imageGenerationProviders, videoGenerationProviders, musicGenerationProviders, webFetchProviders, webSearchProviders, migrationProviders, memoryEmbeddingProviders, sessionExtensions, trustedToolPolicies, toolMetadata, controlUiDescriptors, runtimeLifecycles, agentEventSubscriptions, sessionSchedulerJobs, sessionActions, tools, modelCatalogProviders, api: buildPluginApi({ id: pluginId, name: pluginName, source: pluginSource, registrationMode: params?.registrationMode ?? "full", config: params?.config ?? {}, runtime: {}, logger: { info() {}, warn() {}, error() {}, debug() {} }, resolvePath: (input) => input, handlers: { registerCli(registrar, opts) { const parentPath = normalizeStringEntries(opts?.parentPath ?? []); const descriptors = (opts?.descriptors ?? []).map((descriptor) => ({ name: descriptor.name.trim(), description: descriptor.description.trim(), hasSubcommands: descriptor.hasSubcommands })).filter((descriptor) => descriptor.name && descriptor.description); const commands = normalizeStringEntries([...opts?.commands ?? [], ...descriptors.map((descriptor) => descriptor.name)]); if (commands.length === 0) return; cliRegistrars.push({ register: registrar, parentPath, commands, descriptors }); }, registerProvider(provider) { providers.push(provider); }, registerModelCatalogProvider(provider) { modelCatalogProviders.push(provider); }, registerAgentHarness(harness) { agentHarnesses.push(harness); }, registerCodexAppServerExtensionFactory(factory) { codexAppServerExtensionFactories.push(factory); }, registerAgentToolResultMiddleware(handler, options) { const runtimes = normalizeAgentToolResultMiddlewareRuntimes(options); agentToolResultMiddlewares.push({ pluginId, pluginName, rawHandler: handler, handler, runtimes, source: pluginSource }); }, registerCliBackend(backend) { cliBackends.push(backend); }, registerTextTransforms(transforms) { textTransforms.push(transforms); }, registerEmbeddingProvider(provider) { embeddingProviders.push(provider); }, registerSpeechProvider(provider) { speechProviders.push(provider); }, registerRealtimeTranscriptionProvider(provider) { realtimeTranscriptionProviders.push(provider); }, registerRealtimeVoiceProvider(provider) { realtimeVoiceProviders.push(provider); }, registerMediaUnderstandingProvider(provider) { mediaUnderstandingProviders.push(provider); }, registerTranscriptSourceProvider(provider) { transcriptSourceProviders.push(provider); }, registerImageGenerationProvider(provider) { imageGenerationProviders.push(provider); }, registerVideoGenerationProvider(provider) { videoGenerationProviders.push(provider); }, registerMusicGenerationProvider(provider) { musicGenerationProviders.push(provider); }, registerWebFetchProvider(provider) { webFetchProviders.push(provider); }, registerWebSearchProvider(provider) { webSearchProviders.push(provider); }, registerMigrationProvider(provider) { migrationProviders.push(provider); }, registerMemoryEmbeddingProvider(adapter) { memoryEmbeddingProviders.push(adapter); }, registerSessionExtension(extension) { sessionExtensions.push(extension); }, registerTrustedToolPolicy(policy) { trustedToolPolicies.push(policy); }, registerToolMetadata(metadata) { toolMetadata.push(metadata); }, registerControlUiDescriptor(descriptor) { controlUiDescriptors.push(descriptor); }, registerRuntimeLifecycle(lifecycle) { runtimeLifecycles.push(lifecycle); }, registerAgentEventSubscription(subscription) { agentEventSubscriptions.push(subscription); }, emitAgentEvent: () => ({ emitted: false, reason: "captured registration" }), registerSessionSchedulerJob(job) { sessionSchedulerJobs.push(job); return { id: job.id, pluginId, sessionKey: job.sessionKey, kind: job.kind }; }, registerSessionAction(action) { sessionActions.push(action); }, sendSessionAttachment: async () => ({ ok: false, error: "captured registration" }), scheduleSessionTurn: async (schedule) => { capturedSessionTurnCount += 1; return { id: `captured-session-turn-${capturedSessionTurnCount}`, pluginId, sessionKey: schedule.sessionKey, kind: "session-turn" }; }, unscheduleSessionTurnsByTag: async () => ({ removed: 0, failed: 0 }), registerTool(tool) { if (typeof tool !== "function") tools.push(tool); } } }) }; } //#endregion //#region src/plugins/bundled-capability-runtime.ts /** Loads capability providers from bundled plugin public runtime artifacts. */ const log = createSubsystemLogger("plugins"); const CAPABILITY_VITEST_SHIM_ALIASES = [ { subpath: "config-runtime", target: new URL("./capability-runtime-vitest-shims/config-runtime.ts", import.meta.url) }, { subpath: "media-runtime", target: new URL("./capability-runtime-vitest-shims/media-runtime.ts", import.meta.url) }, { subpath: "provider-onboard", target: new URL("../plugin-sdk/provider-onboard.ts", import.meta.url) }, { subpath: "speech-core", target: new URL("./capability-runtime-vitest-shims/speech-core.ts", import.meta.url) } ]; function buildVitestCapabilityShimAliasMap() { return Object.fromEntries(CAPABILITY_VITEST_SHIM_ALIASES.flatMap(({ subpath, target }) => { const targetPath = fileURLToPath(target); return [[`openclaw/plugin-sdk/${subpath}`, targetPath], [`@openclaw/plugin-sdk/${subpath}`, targetPath]]; })); } function applyVitestCapabilityAliasOverrides(params) { if (!params.env?.VITEST || params.pluginSdkResolution !== "dist") return params.aliasMap; const { "openclaw/plugin-sdk": _ignoredLegacyRootAlias, "@openclaw/plugin-sdk": _ignoredScopedRootAlias, ...scopedAliasMap } = params.aliasMap; return { ...scopedAliasMap, ...buildVitestCapabilityShimAliasMap() }; } function shouldApplyVitestCapabilityAliasOverrides(params) { return Boolean(params.env?.VITEST && params.pluginSdkResolution === "dist"); } function buildBundledCapabilityRuntimeConfig(pluginIds, env) { return withBundledPluginVitestCompat({ config: withBundledPluginEnablementCompat({ config: void 0, pluginIds }), pluginIds, env }); } function resolvePluginModuleExport(moduleExport) { const resolved = unwrapDefaultModuleExport(moduleExport); if (typeof resolved === "function") return { register: resolved }; if (resolved && typeof resolved === "object") { const definition = resolved; return { definition, register: definition.register ?? definition.activate }; } return {}; } function createCapabilityPluginRecord(params) { return { id: params.id, name: params.name ?? params.id, version: params.version, description: params.description, source: params.source, rootDir: params.rootDir, origin: "bundled", workspaceDir: params.workspaceDir, enabled: true, status: "loaded", toolNames: [], hookNames: [], channelIds: [], cliBackendIds: [], providerIds: [], embeddingProviderIds: [], speechProviderIds: [], realtimeTranscriptionProviderIds: [], realtimeVoiceProviderIds: [], mediaUnderstandingProviderIds: [], transcriptSourceProviderIds: [], imageGenerationProviderIds: [], videoGenerationProviderIds: [], musicGenerationProviderIds: [], webFetchProviderIds: [], webSearchProviderIds: [], migrationProviderIds: [], memoryEmbeddingProviderIds: [], agentHarnessIds: [], cliCommands: [], services: [], gatewayDiscoveryServiceIds: [], commands: [], httpRoutes: 0, hookCount: 0, configSchema: true }; } function recordCapabilityLoadError(registry, record, message) { record.status = "error"; record.error = message; registry.plugins.push(record); registry.diagnostics.push({ level: "error", pluginId: record.id, source: record.source, message: `failed to load plugin: ${message}` }); log.error(`[plugins] ${record.id} failed to load from ${record.source}: ${message}`); } function loadBundledCapabilityRuntimeRegistry(params) { const env = params.env ?? process.env; const devSourceRoot = resolveOpenClawDevSourceRoot(env); const pluginIds = new Set(params.pluginIds); const registry = createEmptyPluginRegistry(); const moduleLoaders = createPluginModuleLoaderCache(); const getModuleLoader = (modulePath) => { const tryNative = shouldPreferNativeModuleLoad(modulePath) && !(env?.VITEST && params.pluginSdkResolution === "dist"); const aliasMap = shouldApplyVitestCapabilityAliasOverrides({ pluginSdkResolution: params.pluginSdkResolution, env }) ? applyVitestCapabilityAliasOverrides({ aliasMap: buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url, params.pluginSdkResolution, devSourceRoot), pluginSdkResolution: params.pluginSdkResolution, env }) : void 0; return getCachedPluginModuleLoader({ cache: moduleLoaders, modulePath, importerUrl: import.meta.url, devSourceRoot, loaderFilename: import.meta.url, ...aliasMap ? { aliasMap } : {}, pluginSdkResolution: params.pluginSdkResolution, tryNative }); }; const discovery = params.discovery ?? discoverOpenClawPlugins({ env }); const manifestRegistry = loadPluginManifestRegistry({ config: buildBundledCapabilityRuntimeConfig(params.pluginIds, env), env, candidates: discovery.candidates, diagnostics: discovery.diagnostics }); registry.diagnostics.push(...manifestRegistry.diagnostics); const manifestByRoot = new Map(manifestRegistry.plugins.map((record) => [record.rootDir, record])); const seenPluginIds = /* @__PURE__ */ new Set(); const repoRoot = process.cwd(); for (const candidate of discovery.candidates) { const manifest = manifestByRoot.get(candidate.rootDir); if (!manifest || manifest.origin !== "bundled" || !pluginIds.has(manifest.id)) continue; if (seenPluginIds.has(manifest.id)) continue; seenPluginIds.add(manifest.id); const record = createCapabilityPluginRecord({ id: manifest.id, name: manifest.name, description: manifest.description, version: manifest.version, source: env?.VITEST && params.pluginSdkResolution === "dist" ? resolveBundledPluginRepoEntryPath({ rootDir: repoRoot, pluginId: manifest.id, preferBuilt: true }) ?? candidate.source : candidate.source, rootDir: candidate.rootDir, workspaceDir: candidate.workspaceDir }); const opened = openRootFileSync({ absolutePath: record.source, rootPath: record.source === candidate.source ? candidate.rootDir : repoRoot, boundaryLabel: record.source === candidate.source ? "plugin root" : "repo root", rejectHardlinks: false, skipLexicalRootCheck: true }); if (!opened.ok) { recordCapabilityLoadError(registry, record, "plugin entry path escapes plugin root or fails alias checks"); continue; } const safeSource = opened.path; fs.closeSync(opened.fd); let mod; try { mod = getModuleLoader(safeSource)(safeSource); } catch (error) { recordCapabilityLoadError(registry, record, String(error)); continue; } const register = resolvePluginModuleExport(mod).register; if (typeof register !== "function") { record.status = "disabled"; record.error = "plugin export missing register(api)"; registry.plugins.push(record); continue; } try { const captured = createCapturedPluginRegistration(); register(captured.api); record.cliBackendIds.push(...captured.cliBackends.map((entry) => entry.id)); record.providerIds.push(...captured.providers.map((entry) => entry.id)); record.embeddingProviderIds.push(...captured.embeddingProviders.map((entry) => entry.id)); record.speechProviderIds.push(...captured.speechProviders.map((entry) => entry.id)); record.realtimeTranscriptionProviderIds.push(...captured.realtimeTranscriptionProviders.map((entry) => entry.id)); record.realtimeVoiceProviderIds.push(...captured.realtimeVoiceProviders.map((entry) => entry.id)); record.mediaUnderstandingProviderIds.push(...captured.mediaUnderstandingProviders.map((entry) => entry.id)); record.transcriptSourceProviderIds.push(...captured.transcriptSourceProviders.map((entry) => entry.id)); record.imageGenerationProviderIds.push(...captured.imageGenerationProviders.map((entry) => entry.id)); record.videoGenerationProviderIds.push(...captured.videoGenerationProviders.map((entry) => entry.id)); record.musicGenerationProviderIds.push(...captured.musicGenerationProviders.map((entry) => entry.id)); record.webFetchProviderIds.push(...captured.webFetchProviders.map((entry) => entry.id)); record.webSearchProviderIds.push(...captured.webSearchProviders.map((entry) => entry.id)); record.migrationProviderIds.push(...captured.migrationProviders.map((entry) => entry.id)); record.memoryEmbeddingProviderIds.push(...captured.memoryEmbeddingProviders.map((entry) => entry.id)); record.agentHarnessIds.push(...captured.agentHarnesses.map((entry) => entry.id)); record.toolNames.push(...captured.tools.map((entry) => entry.name)); registry.cliBackends?.push(...captured.cliBackends.map((backend) => ({ pluginId: record.id, pluginName: record.name, backend, source: record.source, rootDir: record.rootDir }))); registry.textTransforms.push(...captured.textTransforms.map((transforms) => ({ pluginId: record.id, pluginName: record.name, transforms, source: record.source, rootDir: record.rootDir }))); registry.providers.push(...captured.providers.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.embeddingProviders.push(...captured.embeddingProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.speechProviders.push(...captured.speechProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.realtimeTranscriptionProviders.push(...captured.realtimeTranscriptionProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.realtimeVoiceProviders.push(...captured.realtimeVoiceProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.mediaUnderstandingProviders.push(...captured.mediaUnderstandingProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.transcriptSourceProviders.push(...captured.transcriptSourceProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.imageGenerationProviders.push(...captured.imageGenerationProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.videoGenerationProviders.push(...captured.videoGenerationProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.musicGenerationProviders.push(...captured.musicGenerationProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.webFetchProviders.push(...captured.webFetchProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.webSearchProviders.push(...captured.webSearchProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.migrationProviders.push(...captured.migrationProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.memoryEmbeddingProviders.push(...captured.memoryEmbeddingProviders.map((provider) => ({ pluginId: record.id, pluginName: record.name, provider, source: record.source, rootDir: record.rootDir }))); registry.agentHarnesses.push(...captured.agentHarnesses.map((harness) => ({ pluginId: record.id, pluginName: record.name, harness, source: record.source, rootDir: record.rootDir }))); const declaredToolNames = normalizePluginToolContractNames(record.contracts); for (const tool of captured.tools) { const undeclared = findUndeclaredPluginToolNames({ declaredNames: declaredToolNames, toolNames: [tool.name] }); if (undeclared.length > 0) { registry.diagnostics.push({ level: "error", pluginId: record.id, source: record.source, message: `plugin must declare contracts.tools for: ${undeclared.join(", ")}` }); continue; } registry.tools.push({ pluginId: record.id, pluginName: record.name, factory: () => tool, names: [tool.name], declaredNames: declaredToolNames, optional: false, source: record.source, rootDir: record.rootDir }); } registry.plugins.push(record); } catch (error) { recordCapabilityLoadError(registry, record, String(error)); } } return registry; } //#endregion export { loadBundledCapabilityRuntimeRegistry as t };