UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

285 lines (284 loc) 13.3 kB
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js"; import { i as openRootFileSync } from "./root-file-jRMCpJW4.js"; import "./boundary-file-read-CBe_wA_B.js"; import { l as tryNativeRequireJavaScriptModule, r as getCachedPluginSourceModuleLoader, u as toSafeImportPath } from "./plugin-module-loader-cache-lz0qhu-X.js"; import { t as buildPluginLoaderAliasMap, u as resolveLoaderPackageRoot } from "./sdk-alias-ZgondhPg.js"; import { o as emptyChannelConfigSchema } from "./config-schema-CO2ikh7C.js"; import { n as formatPluginLoadProfileLine, r as shouldProfilePluginLoader, t as createProfiler } from "./plugin-load-profile-Dw-hJkNg.js"; import { fileURLToPath } from "node:url"; import fs from "node:fs"; import path from "node:path"; //#region src/plugin-sdk/channel-entry-contract.ts const moduleLoaders = /* @__PURE__ */ new Map(); const entryBoundaryInfoCache = /* @__PURE__ */ new Map(); const resolvedModulePaths = /* @__PURE__ */ new Map(); const loadedModuleExports = /* @__PURE__ */ new Map(); const disableBundledEntrySourceFallbackEnv = "OPENCLAW_DISABLE_BUNDLED_ENTRY_SOURCE_FALLBACK"; function isTruthyEnvFlag(value) { return value !== void 0 && !/^(?:0|false)$/iu.test(value.trim()); } function resolveSpecifierCandidates(modulePath) { const ext = normalizeLowercaseStringOrEmpty(path.extname(modulePath)); if (ext === ".js") return [modulePath, modulePath.slice(0, -3) + ".ts"]; if (ext === ".mjs") return [modulePath, modulePath.slice(0, -4) + ".mts"]; if (ext === ".cjs") return [modulePath, modulePath.slice(0, -4) + ".cts"]; return [modulePath]; } function resolveEntryBoundaryRoot(importMetaUrl) { return path.dirname(fileURLToPath(importMetaUrl)); } function resolveBundledEntryBoundaryInfo(importMetaUrl) { const cacheKey = `${process.argv[1] ?? ""}\0${importMetaUrl}`; const cached = entryBoundaryInfoCache.get(cacheKey); if (cached) return cached; const importerPath = fileURLToPath(importMetaUrl); const importerDir = path.dirname(importerPath); const info = { importerPath, importerDir, boundaryRoot: path.dirname(importerPath), packageRoot: resolveLoaderPackageRoot({ modulePath: importerPath, moduleUrl: importMetaUrl, cwd: importerDir, argv1: process.argv[1] }) ?? null }; entryBoundaryInfoCache.set(cacheKey, info); return info; } function addBundledEntryCandidates(candidates, basePath, boundaryRoot) { for (const candidate of resolveSpecifierCandidates(basePath)) { if (candidates.some((entry) => entry.path === candidate && entry.boundaryRoot === boundaryRoot)) continue; candidates.push({ path: candidate, boundaryRoot }); } } function resolveBundledEntryModuleCandidates(importMetaUrl, specifier) { const { importerPath, importerDir, boundaryRoot, packageRoot } = resolveBundledEntryBoundaryInfo(importMetaUrl); const candidates = []; addBundledEntryCandidates(candidates, path.resolve(importerDir, specifier), boundaryRoot); const sourceRelativeSpecifier = specifier.replace(/^\.\/src\//u, "./"); if (sourceRelativeSpecifier !== specifier) addBundledEntryCandidates(candidates, path.resolve(importerDir, sourceRelativeSpecifier), boundaryRoot); if (!packageRoot) return candidates; const distExtensionsRoot = path.join(packageRoot, "dist", "extensions") + path.sep; if (!importerPath.startsWith(distExtensionsRoot)) return candidates; if (isTruthyEnvFlag(process.env[disableBundledEntrySourceFallbackEnv])) return candidates; const pluginDirName = path.basename(importerDir); const sourcePluginRoot = path.join(packageRoot, "extensions", pluginDirName); if (sourcePluginRoot === boundaryRoot) return candidates; addBundledEntryCandidates(candidates, path.resolve(sourcePluginRoot, specifier), sourcePluginRoot); if (sourceRelativeSpecifier !== specifier) addBundledEntryCandidates(candidates, path.resolve(sourcePluginRoot, sourceRelativeSpecifier), sourcePluginRoot); return candidates; } function formatBundledEntryUnknownError(error) { if (typeof error === "string") return error; if (error === void 0) return "boundary validation failed"; try { return JSON.stringify(error); } catch { return "non-serializable error"; } } function formatBundledEntryModuleOpenFailure(params) { const importerPath = fileURLToPath(params.importMetaUrl); const errorDetail = params.failure.error instanceof Error ? params.failure.error.message : formatBundledEntryUnknownError(params.failure.error); return [ `bundled plugin entry "${params.specifier}" failed to open`, `from "${importerPath}"`, `(resolved "${params.resolvedPath}", plugin root "${params.boundaryRoot}",`, `reason "${params.failure.reason}"): ${errorDetail}` ].join(" "); } function createBundledEntryModulePathCacheKey(importMetaUrl, specifier) { return `${isTruthyEnvFlag(process.env[disableBundledEntrySourceFallbackEnv]) ? "1" : "0"}\0${importMetaUrl}\0${specifier}`; } function resolveBundledEntryModulePath(importMetaUrl, specifier) { const cacheKey = createBundledEntryModulePathCacheKey(importMetaUrl, specifier); const cached = resolvedModulePaths.get(cacheKey); if (cached) return cached; const candidates = resolveBundledEntryModuleCandidates(importMetaUrl, specifier); const fallbackCandidate = candidates[0] ?? { path: path.resolve(path.dirname(fileURLToPath(importMetaUrl)), specifier), boundaryRoot: resolveEntryBoundaryRoot(importMetaUrl) }; let firstFailure = null; for (const candidate of candidates) { const opened = openRootFileSync({ absolutePath: candidate.path, rootPath: candidate.boundaryRoot, boundaryLabel: "plugin root", rejectHardlinks: false, skipLexicalRootCheck: true }); if (opened.ok) { fs.closeSync(opened.fd); resolvedModulePaths.set(cacheKey, opened.path); return opened.path; } firstFailure ??= { candidate, failure: opened }; } const failure = firstFailure; if (!failure) throw new Error(formatBundledEntryModuleOpenFailure({ importMetaUrl, specifier, resolvedPath: fallbackCandidate.path, boundaryRoot: fallbackCandidate.boundaryRoot, failure: { ok: false, reason: "path", error: /* @__PURE__ */ new Error(`ENOENT: no such file or directory, lstat '${fallbackCandidate.path}'`) } })); throw new Error(formatBundledEntryModuleOpenFailure({ importMetaUrl, specifier, resolvedPath: failure.candidate.path, boundaryRoot: failure.candidate.boundaryRoot, failure: failure.failure })); } function getSourceModuleLoader(modulePath, options) { return getCachedPluginSourceModuleLoader({ cache: moduleLoaders, modulePath, importerUrl: import.meta.url, preferBuiltDist: true, loaderFilename: import.meta.url, ...options.createLoaderForTest ? { createLoader: options.createLoaderForTest } : {} }); } function canTryNodeRequireBuiltModule(modulePath) { return (modulePath.includes(`${path.sep}dist${path.sep}`) || modulePath.includes(`${path.sep}dist-runtime${path.sep}`)) && [ ".js", ".mjs", ".cjs" ].includes(normalizeLowercaseStringOrEmpty(path.extname(modulePath))); } function loadBundledEntryModuleSync(importMetaUrl, specifier, options = {}) { const modulePath = resolveBundledEntryModulePath(importMetaUrl, specifier); const cached = loadedModuleExports.get(modulePath); if (cached !== void 0) return cached; let loaded; const profile = shouldProfilePluginLoader(); const loadStartMs = profile ? performance.now() : 0; let sourceLoaderReadyMs = 0; if (canTryNodeRequireBuiltModule(modulePath)) { const native = tryNativeRequireJavaScriptModule(modulePath, { allowWindows: true, aliasMap: buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url, "dist"), fallbackOnMissingDependency: true, fallbackOnNativeError: true }); if (native.ok) loaded = native.moduleExport; else { const moduleLoader = getSourceModuleLoader(modulePath, options); sourceLoaderReadyMs = profile ? performance.now() : 0; loaded = moduleLoader(toSafeImportPath(modulePath)); } } else { const moduleLoader = getSourceModuleLoader(modulePath, options); sourceLoaderReadyMs = profile ? performance.now() : 0; loaded = moduleLoader(toSafeImportPath(modulePath)); } if (profile) { const endMs = performance.now(); console.error(formatPluginLoadProfileLine({ phase: "bundled-entry-module-load", pluginId: "(bundled-entry)", source: modulePath, elapsedMs: endMs - loadStartMs, extras: [["sourceLoaderCreateMs", sourceLoaderReadyMs ? sourceLoaderReadyMs - loadStartMs : 0], ["sourceLoaderCallMs", sourceLoaderReadyMs ? endMs - sourceLoaderReadyMs : 0]] })); } loadedModuleExports.set(modulePath, loaded); return loaded; } /** Loads one export from a bundled channel sidecar module through the guarded entry boundary. */ function loadBundledEntryExportSync(importMetaUrl, reference, options) { const loaded = loadBundledEntryModuleSync(importMetaUrl, reference.specifier, options); const resolved = loaded && typeof loaded === "object" && "default" in loaded ? loaded.default : loaded; if (!reference.exportName) return resolved; const record = resolved ?? loaded; if (!record || !(reference.exportName in record)) throw new Error(`missing export "${reference.exportName}" from bundled entry module ${reference.specifier}`); return record[reference.exportName]; } /** Defines the full bundled channel entry contract used by core plugin registration. */ function defineBundledChannelEntry({ id, name, description, importMetaUrl, plugin, outbound, secrets, configSchema, runtime, accountInspect, features, registerCliMetadata, registerFull }) { const resolvedConfigSchema = typeof configSchema === "function" ? configSchema() : configSchema ?? emptyChannelConfigSchema(); const loadChannelPlugin = (options) => loadBundledEntryExportSync(importMetaUrl, plugin, options); const loadChannelOutbound = outbound ? (options) => loadBundledEntryExportSync(importMetaUrl, outbound, options) : void 0; const loadChannelSecrets = secrets ? (options) => loadBundledEntryExportSync(importMetaUrl, secrets, options) : void 0; const loadChannelAccountInspector = accountInspect ? (options) => loadBundledEntryExportSync(importMetaUrl, accountInspect, options) : void 0; const setChannelRuntime = runtime ? (pluginRuntime) => { loadBundledEntryExportSync(importMetaUrl, runtime)(pluginRuntime); } : void 0; return { kind: "bundled-channel-entry", id, name, description, configSchema: resolvedConfigSchema, ...features || accountInspect ? { features: { ...features, ...accountInspect ? { accountInspect: true } : {} } } : {}, register(api) { if (api.registrationMode === "cli-metadata") { registerCliMetadata?.(api); return; } if (api.registrationMode === "tool-discovery") { createProfiler({ pluginId: id, source: importMetaUrl })("bundled-register:registerFull", () => registerFull?.(api)); return; } const profile = createProfiler({ pluginId: id, source: importMetaUrl }); const channelPlugin = profile("bundled-register:loadChannelPlugin", loadChannelPlugin); profile("bundled-register:registerChannel", () => api.registerChannel({ plugin: channelPlugin })); profile("bundled-register:setChannelRuntime", () => setChannelRuntime?.(api.runtime)); if (api.registrationMode === "discovery") { profile("bundled-register:registerCliMetadata", () => registerCliMetadata?.(api)); return; } if (api.registrationMode !== "full") return; profile("bundled-register:registerCliMetadata", () => registerCliMetadata?.(api)); profile("bundled-register:registerFull", () => registerFull?.(api)); }, loadChannelPlugin, ...loadChannelOutbound ? { loadChannelOutbound } : {}, ...loadChannelSecrets ? { loadChannelSecrets } : {}, ...loadChannelAccountInspector ? { loadChannelAccountInspector } : {}, ...setChannelRuntime ? { setChannelRuntime } : {} }; } /** Defines the setup-only bundled channel entry contract for onboarding and migration surfaces. */ function defineBundledChannelSetupEntry({ importMetaUrl, plugin, secrets, runtime, legacyStateMigrations, legacySessionSurface, registerSetupRuntime, features }) { const setChannelRuntime = runtime ? (pluginRuntime) => { loadBundledEntryExportSync(importMetaUrl, runtime)(pluginRuntime); } : void 0; const loadLegacyStateMigrationDetector = legacyStateMigrations ? (options) => loadBundledEntryExportSync(importMetaUrl, legacyStateMigrations, options) : void 0; const loadLegacySessionSurface = legacySessionSurface ? (options) => loadBundledEntryExportSync(importMetaUrl, legacySessionSurface, options) : void 0; return { kind: "bundled-channel-setup-entry", loadSetupPlugin: (options) => loadBundledEntryExportSync(importMetaUrl, plugin, options), ...secrets ? { loadSetupSecrets: (options) => loadBundledEntryExportSync(importMetaUrl, secrets, options) } : {}, ...loadLegacyStateMigrationDetector ? { loadLegacyStateMigrationDetector } : {}, ...loadLegacySessionSurface ? { loadLegacySessionSurface } : {}, ...setChannelRuntime ? { setChannelRuntime } : {}, ...registerSetupRuntime ? { registerSetupRuntime } : {}, ...features ? { features } : {} }; } //#endregion export { defineBundledChannelSetupEntry as n, loadBundledEntryExportSync as r, defineBundledChannelEntry as t };