UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

105 lines (104 loc) 4.21 kB
import { o as getPluginCacheRoot, s as getPluginCacheSource } from "./plugin-cache-DGWspMEc.js"; import { n as describeRootFileOpenFailure, s as openRootFileSync } from "./boundary-file-read-uaJcf6X6.js"; import { t as hasErrnoCode } from "./errno-CkbDOfLk.js"; import { o as recordPluginModuleRoot, t as getCachedPluginModuleLoader } from "./plugin-module-loader-cache-lf3kAaBw.js"; import { f as isJavaScriptModulePath, u as PLUGIN_SOURCE_MODULE_EXTENSIONS } from "./sdk-alias-BMTmaiMP.js"; import { createRequire } from "node:module"; import fs from "node:fs"; import path from "node:path"; //#region src/channels/plugins/module-loader.ts /** * Channel plugin module loader. * * Loads JavaScript or source plugin modules through native require or cached TS loaders. */ const nodeRequire = createRequire(import.meta.url); function loadModuleWithJiti(modulePath, rootDir) { return getCachedPluginModuleLoader({ modulePath, rootDir, importerUrl: import.meta.url, loaderFilename: import.meta.url, tryNative: false, cacheScopeKey: "channel-plugin-module-loader" })(modulePath); } function loadModule(modulePath, rootDir) { const extension = path.extname(modulePath).toLowerCase(); const isSource = PLUGIN_SOURCE_MODULE_EXTENSIONS.includes(extension); if (!isJavaScriptModulePath(modulePath) && !(isSource && typeof nodeRequire.extensions?.[extension] === "function")) { if (isSource) return loadModuleWithJiti(modulePath, rootDir); throw new Error(`channel plugin module must be built JavaScript: ${modulePath}`); } try { return nodeRequire(modulePath); } catch (error) { if (isSource) return loadModuleWithJiti(modulePath, rootDir); throw new Error(`failed to load channel plugin module with native require: ${modulePath}`, { cause: error }); } } function resolveSourceModuleCandidates(rootDir, specifier) { const normalizedSpecifier = specifier.replace(/\\/g, "/"); const resolvedPath = path.resolve(rootDir, normalizedSpecifier); if (path.extname(resolvedPath)) return []; return PLUGIN_SOURCE_MODULE_EXTENSIONS.map((extension) => `${resolvedPath}${extension}`); } /** * Resolves a plugin-relative module specifier to an existing candidate path. */ function resolveExistingPluginModulePath(rootDir, specifier) { const artifacts = getPluginCacheRoot(rootDir).artifacts; const key = `channel-specifier:${specifier}`; const cached = artifacts.get(key); if (cached) return cached.modulePath; const modulePath = resolvePluginModulePath(rootDir, specifier); artifacts.set(key, { modulePath, boundaryRoot: rootDir }); return modulePath; } function resolvePluginModulePath(rootDir, specifier) { const resolvedPath = path.resolve(rootDir, specifier.replace(/\\/g, "/")); try { return nodeRequire.resolve(resolvedPath); } catch (error) { if (!hasErrnoCode(error, "MODULE_NOT_FOUND")) throw error; } for (const candidate of resolveSourceModuleCandidates(rootDir, specifier)) if (fs.existsSync(candidate)) return candidate; return resolvedPath; } /** * Loads a channel plugin module after enforcing plugin-root file boundaries. * * `rootDir` is always the plugin's own directory, so the containment failure is * reported against that one root; no caller boundary override exists. */ function loadChannelPluginModule(params) { const source = getPluginCacheSource(params.modulePath); const key = `channel-plugin-module:${path.resolve(params.rootDir)}`; const cached = source.variants.get(key)?.exports; if (cached) return cached.value; const boundaryLabel = "plugin root"; const opened = openRootFileSync({ absolutePath: params.modulePath, rootPath: params.rootDir, boundaryLabel, rejectHardlinks: false, skipLexicalRootCheck: true }); if (!opened.ok) throw new Error(describeRootFileOpenFailure({ failure: opened, subject: "plugin module path", boundaryLabel, filePath: params.modulePath }), { cause: opened.error }); const safePath = opened.path; fs.closeSync(opened.fd); recordPluginModuleRoot(safePath, params.rootDir); const value = loadModule(safePath, params.rootDir); source.variants.set(key, { exports: { value } }); return value; } //#endregion export { resolveExistingPluginModulePath as n, loadChannelPluginModule as t };