import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js"; import type { PluginManifestRecord } from "../plugins/manifest-registry.js"; import { normalizeBundledPluginArtifactSubpath, resolveBundledPluginPublicSurfacePath, } from "../plugins/public-surface-runtime.js"; import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js"; import { loadBundledPluginPublicSurfaceModuleSync as loadBundledPluginPublicSurfaceModuleSyncLight, loadFacadeModuleAtLocationSync as loadFacadeModuleAtLocationSyncShared, resetFacadeLoaderStateForTest, type FacadeModuleLocation, } from "./facade-loader.js"; export { createLazyFacadeArrayValue, createLazyFacadeObjectValue, listImportedBundledPluginFacadeIds, } from "./facade-loader.js"; const OPENCLAW_PACKAGE_ROOT = resolveLoaderPackageRoot({ modulePath: fileURLToPath(import.meta.url), moduleUrl: import.meta.url, }) ?? fileURLToPath(new URL("../..", import.meta.url)); const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url); const PUBLIC_SURFACE_SOURCE_EXTENSIONS = [".ts", ".mts", ".js", ".mjs", ".cts", ".cjs"] as const; const OPENCLAW_SOURCE_EXTENSIONS_ROOT = path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"); const cachedFacadeModuleLocationsByKey = new Map< string, { modulePath: string; boundaryRoot: string; } | null >(); function createFacadeResolutionKey(params: { dirName: string; artifactBasename: string }): string { const bundledPluginsDir = resolveBundledPluginsDir(); return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : ""}`; } function resolveSourceFirstPublicSurfacePath(params: { bundledPluginsDir?: string; dirName: string; artifactBasename: string; }): string | null { const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename); const sourceBaseName = artifactBasename.replace(/\.js$/u, ""); const sourceRoot = params.bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"); for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) { const candidate = path.resolve(sourceRoot, params.dirName, `${sourceBaseName}${ext}`); if (fs.existsSync(candidate)) { return candidate; } } return null; } function resolveRegistryPluginModuleLocationFromRegistry(params: { registry: readonly Pick[]; dirName: string; artifactBasename: string; }): { modulePath: string; boundaryRoot: string } | null { type RegistryRecord = (typeof params.registry)[number]; const tiers: Array<(plugin: RegistryRecord) => boolean> = [ (plugin) => plugin.id === params.dirName, (plugin) => path.basename(plugin.rootDir) === params.dirName, (plugin) => plugin.channels.includes(params.dirName), ]; const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename); const sourceBaseName = artifactBasename.replace(/\.js$/u, ""); for (const matchFn of tiers) { for (const record of params.registry.filter(matchFn)) { const rootDir = path.resolve(record.rootDir); const builtCandidate = path.join(rootDir, artifactBasename); if (fs.existsSync(builtCandidate)) { return { modulePath: builtCandidate, boundaryRoot: rootDir }; } for (const ext of PUBLIC_SURFACE_SOURCE_EXTENSIONS) { const sourceCandidate = path.join(rootDir, `${sourceBaseName}${ext}`); if (fs.existsSync(sourceCandidate)) { return { modulePath: sourceCandidate, boundaryRoot: rootDir }; } } } } return null; } function resolveRegistryPluginModuleLocation(params: { dirName: string; artifactBasename: string; }): { modulePath: string; boundaryRoot: string } | null { return loadFacadeActivationCheckRuntime().resolveRegistryPluginModuleLocation({ ...params, resolutionKey: createFacadeResolutionKey(params), }); } function resolveFacadeModuleLocationUncached(params: { dirName: string; artifactBasename: string; }): { modulePath: string; boundaryRoot: string } | null { const bundledPluginsDir = resolveBundledPluginsDir(); const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`); if (preferSource) { const modulePath = resolveSourceFirstPublicSurfacePath({ ...params, ...(bundledPluginsDir ? { bundledPluginsDir } : {}), }) ?? resolveSourceFirstPublicSurfacePath(params) ?? resolveBundledPluginPublicSurfacePath({ rootDir: OPENCLAW_PACKAGE_ROOT, ...(bundledPluginsDir ? { bundledPluginsDir } : {}), dirName: params.dirName, artifactBasename: params.artifactBasename, }); if (modulePath) { return { modulePath, boundaryRoot: bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep) ? path.resolve(bundledPluginsDir) : OPENCLAW_PACKAGE_ROOT, }; } return resolveRegistryPluginModuleLocation(params); } const modulePath = resolveBundledPluginPublicSurfacePath({ rootDir: OPENCLAW_PACKAGE_ROOT, ...(bundledPluginsDir ? { bundledPluginsDir } : {}), dirName: params.dirName, artifactBasename: params.artifactBasename, }); if (modulePath) { return { modulePath, boundaryRoot: bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep) ? path.resolve(bundledPluginsDir) : OPENCLAW_PACKAGE_ROOT, }; } return resolveRegistryPluginModuleLocation(params); } function resolveFacadeModuleLocation(params: { dirName: string; artifactBasename: string; }): { modulePath: string; boundaryRoot: string } | null { const key = createFacadeResolutionKey(params); if (cachedFacadeModuleLocationsByKey.has(key)) { return cachedFacadeModuleLocationsByKey.get(key) ?? null; } const resolved = resolveFacadeModuleLocationUncached(params); cachedFacadeModuleLocationsByKey.set(key, resolved); return resolved; } type BundledPluginPublicSurfaceParams = { dirName: string; artifactBasename: string; }; type FacadeActivationCheckRuntimeModule = typeof import("./facade-activation-check.runtime.js"); type JitiLoader = ReturnType<(typeof import("jiti"))["createJiti"]>; const nodeRequire = createRequire(import.meta.url); const FACADE_ACTIVATION_CHECK_RUNTIME_CANDIDATES = [ "./facade-activation-check.runtime.js", "./facade-activation-check.runtime.ts", ] as const; let facadeActivationCheckRuntimeModule: FacadeActivationCheckRuntimeModule | undefined; let facadeActivationCheckRuntimeJiti: JitiLoader | undefined; function getFacadeActivationCheckRuntimeJiti(): JitiLoader { if (facadeActivationCheckRuntimeJiti) { return facadeActivationCheckRuntimeJiti; } const { createJiti } = nodeRequire("jiti") as typeof import("jiti"); facadeActivationCheckRuntimeJiti = createJiti(import.meta.url, { tryNative: false }); return facadeActivationCheckRuntimeJiti; } function loadFacadeActivationCheckRuntime(): FacadeActivationCheckRuntimeModule { if (facadeActivationCheckRuntimeModule) { return facadeActivationCheckRuntimeModule; } for (const candidate of FACADE_ACTIVATION_CHECK_RUNTIME_CANDIDATES) { try { facadeActivationCheckRuntimeModule = nodeRequire( candidate, ) as FacadeActivationCheckRuntimeModule; return facadeActivationCheckRuntimeModule; } catch { // Try source/runtime candidates in order. } } const jiti = getFacadeActivationCheckRuntimeJiti(); for (const candidate of FACADE_ACTIVATION_CHECK_RUNTIME_CANDIDATES) { try { facadeActivationCheckRuntimeModule = jiti(candidate) as FacadeActivationCheckRuntimeModule; return facadeActivationCheckRuntimeModule; } catch { // Try source/runtime candidates in order. } } throw new Error("Unable to load facade activation check runtime"); } function loadFacadeModuleAtLocationSync(params: { location: FacadeModuleLocation; trackedPluginId: string | (() => string); loadModule?: (modulePath: string) => T; }): T { return loadFacadeModuleAtLocationSyncShared(params); } function buildFacadeActivationCheckParams( params: BundledPluginPublicSurfaceParams, location: FacadeModuleLocation | null = resolveFacadeModuleLocation(params), ) { return { ...params, location, sourceExtensionsRoot: OPENCLAW_SOURCE_EXTENSIONS_ROOT, resolutionKey: createFacadeResolutionKey(params), }; } export function loadBundledPluginPublicSurfaceModuleSync( params: BundledPluginPublicSurfaceParams, ): T { const location = resolveFacadeModuleLocation(params); const trackedPluginId = () => loadFacadeActivationCheckRuntime().resolveTrackedFacadePluginId( buildFacadeActivationCheckParams(params, location), ); if (!location) { return loadBundledPluginPublicSurfaceModuleSyncLight({ ...params, trackedPluginId, }); } return loadFacadeModuleAtLocationSync({ location, trackedPluginId, }); } export function canLoadActivatedBundledPluginPublicSurface(params: { dirName: string; artifactBasename: string; }): boolean { return loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess( buildFacadeActivationCheckParams(params), ).allowed; } export function loadActivatedBundledPluginPublicSurfaceModuleSync(params: { dirName: string; artifactBasename: string; }): T { loadFacadeActivationCheckRuntime().resolveActivatedBundledPluginPublicSurfaceAccessOrThrow( buildFacadeActivationCheckParams(params), ); return loadBundledPluginPublicSurfaceModuleSync(params); } export function tryLoadActivatedBundledPluginPublicSurfaceModuleSync(params: { dirName: string; artifactBasename: string; }): T | null { const access = loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess( buildFacadeActivationCheckParams(params), ); if (!access.allowed) { return null; } return loadBundledPluginPublicSurfaceModuleSync(params); } export function resetFacadeRuntimeStateForTest(): void { resetFacadeLoaderStateForTest(); facadeActivationCheckRuntimeModule?.resetFacadeActivationCheckRuntimeStateForTest(); facadeActivationCheckRuntimeModule = undefined; facadeActivationCheckRuntimeJiti = undefined; cachedFacadeModuleLocationsByKey.clear(); } export const __testing = { loadFacadeModuleAtLocationSync, resolveRegistryPluginModuleLocationFromRegistry, resolveFacadeModuleLocation, evaluateBundledPluginPublicSurfaceAccess: (( ...args: Parameters< FacadeActivationCheckRuntimeModule["evaluateBundledPluginPublicSurfaceAccess"] > ) => loadFacadeActivationCheckRuntime().evaluateBundledPluginPublicSurfaceAccess( ...args, )) as FacadeActivationCheckRuntimeModule["evaluateBundledPluginPublicSurfaceAccess"], throwForBundledPluginPublicSurfaceAccess: (( ...args: Parameters< FacadeActivationCheckRuntimeModule["throwForBundledPluginPublicSurfaceAccess"] > ) => loadFacadeActivationCheckRuntime().throwForBundledPluginPublicSurfaceAccess( ...args, )) as FacadeActivationCheckRuntimeModule["throwForBundledPluginPublicSurfaceAccess"], resolveActivatedBundledPluginPublicSurfaceAccessOrThrow: (( params: BundledPluginPublicSurfaceParams, ) => loadFacadeActivationCheckRuntime().resolveActivatedBundledPluginPublicSurfaceAccessOrThrow( buildFacadeActivationCheckParams(params), )) as (params: BundledPluginPublicSurfaceParams) => { allowed: boolean; pluginId?: string; reason?: string; }, resolveBundledPluginPublicSurfaceAccess: ((params: BundledPluginPublicSurfaceParams) => loadFacadeActivationCheckRuntime().resolveBundledPluginPublicSurfaceAccess( buildFacadeActivationCheckParams(params), )) as (params: BundledPluginPublicSurfaceParams) => { allowed: boolean; pluginId?: string; reason?: string; }, resolveTrackedFacadePluginId: ((params: BundledPluginPublicSurfaceParams) => loadFacadeActivationCheckRuntime().resolveTrackedFacadePluginId( buildFacadeActivationCheckParams(params), )) as (params: BundledPluginPublicSurfaceParams) => string, };