mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-13 02:01:16 +00:00
218 lines
6.6 KiB
TypeScript
218 lines
6.6 KiB
TypeScript
import fs from "node:fs";
|
|
import { createRequire } from "node:module";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { createJiti } from "jiti";
|
|
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
|
import { resolveBundledPluginsDir } from "./bundled-dir.js";
|
|
import { resolveBundledPluginPublicSurfacePath } from "./public-surface-runtime.js";
|
|
import {
|
|
buildPluginLoaderAliasMap,
|
|
buildPluginLoaderJitiOptions,
|
|
isBundledPluginExtensionPath,
|
|
resolvePluginLoaderJitiConfig,
|
|
resolveLoaderPackageRoot,
|
|
} from "./sdk-alias.js";
|
|
|
|
const OPENCLAW_PACKAGE_ROOT =
|
|
resolveLoaderPackageRoot({
|
|
modulePath: fileURLToPath(import.meta.url),
|
|
moduleUrl: import.meta.url,
|
|
}) ?? fileURLToPath(new URL("../..", import.meta.url));
|
|
const loadedPublicSurfaceModules = new Map<string, unknown>();
|
|
const sourceArtifactRequire = createRequire(import.meta.url);
|
|
const publicSurfaceLocations = new Map<
|
|
string,
|
|
{
|
|
modulePath: string;
|
|
boundaryRoot: string;
|
|
} | null
|
|
>();
|
|
const jitiLoaders = new Map<string, ReturnType<typeof createJiti>>();
|
|
const sharedBundledPublicSurfaceJitiLoaders = new Map<string, ReturnType<typeof createJiti>>();
|
|
|
|
function isSourceArtifactPath(modulePath: string): boolean {
|
|
switch (path.extname(modulePath).toLowerCase()) {
|
|
case ".ts":
|
|
case ".tsx":
|
|
case ".mts":
|
|
case ".cts":
|
|
case ".mtsx":
|
|
case ".ctsx":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function canUseSourceArtifactRequire(params: { modulePath: string; tryNative: boolean }): boolean {
|
|
return (
|
|
!params.tryNative &&
|
|
isSourceArtifactPath(params.modulePath) &&
|
|
typeof sourceArtifactRequire.extensions?.[".ts"] === "function"
|
|
);
|
|
}
|
|
|
|
function createResolutionKey(params: { dirName: string; artifactBasename: string }): string {
|
|
const bundledPluginsDir = resolveBundledPluginsDir();
|
|
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
|
|
}
|
|
|
|
function resolvePublicSurfaceLocationUncached(params: {
|
|
dirName: string;
|
|
artifactBasename: string;
|
|
}): { modulePath: string; boundaryRoot: string } | null {
|
|
const bundledPluginsDir = resolveBundledPluginsDir();
|
|
const modulePath = resolveBundledPluginPublicSurfacePath({
|
|
rootDir: OPENCLAW_PACKAGE_ROOT,
|
|
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
|
dirName: params.dirName,
|
|
artifactBasename: params.artifactBasename,
|
|
});
|
|
if (!modulePath) {
|
|
return null;
|
|
}
|
|
return {
|
|
modulePath,
|
|
boundaryRoot:
|
|
bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep)
|
|
? path.resolve(bundledPluginsDir)
|
|
: OPENCLAW_PACKAGE_ROOT,
|
|
};
|
|
}
|
|
|
|
function resolvePublicSurfaceLocation(params: {
|
|
dirName: string;
|
|
artifactBasename: string;
|
|
}): { modulePath: string; boundaryRoot: string } | null {
|
|
const key = createResolutionKey(params);
|
|
if (publicSurfaceLocations.has(key)) {
|
|
return publicSurfaceLocations.get(key) ?? null;
|
|
}
|
|
const resolved = resolvePublicSurfaceLocationUncached(params);
|
|
publicSurfaceLocations.set(key, resolved);
|
|
return resolved;
|
|
}
|
|
|
|
function getJiti(modulePath: string) {
|
|
const { tryNative, aliasMap, cacheKey } = resolvePluginLoaderJitiConfig({
|
|
modulePath,
|
|
argv1: process.argv[1],
|
|
moduleUrl: import.meta.url,
|
|
preferBuiltDist: true,
|
|
});
|
|
const sharedLoader = getSharedBundledPublicSurfaceJiti(modulePath, tryNative);
|
|
if (sharedLoader) {
|
|
return sharedLoader;
|
|
}
|
|
const cached = jitiLoaders.get(cacheKey);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const loader = createJiti(import.meta.url, {
|
|
...buildPluginLoaderJitiOptions(aliasMap),
|
|
tryNative,
|
|
});
|
|
jitiLoaders.set(cacheKey, loader);
|
|
return loader;
|
|
}
|
|
|
|
function loadPublicSurfaceModule(modulePath: string): unknown {
|
|
const { tryNative } = resolvePluginLoaderJitiConfig({
|
|
modulePath,
|
|
argv1: process.argv[1],
|
|
moduleUrl: import.meta.url,
|
|
preferBuiltDist: true,
|
|
});
|
|
if (canUseSourceArtifactRequire({ modulePath, tryNative })) {
|
|
return sourceArtifactRequire(modulePath);
|
|
}
|
|
return getJiti(modulePath)(modulePath);
|
|
}
|
|
|
|
function getSharedBundledPublicSurfaceJiti(
|
|
modulePath: string,
|
|
tryNative: boolean,
|
|
): ReturnType<typeof createJiti> | null {
|
|
const bundledPluginsDir = resolveBundledPluginsDir();
|
|
if (
|
|
!isBundledPluginExtensionPath({
|
|
modulePath,
|
|
openClawPackageRoot: OPENCLAW_PACKAGE_ROOT,
|
|
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
|
})
|
|
) {
|
|
return null;
|
|
}
|
|
const cacheKey = tryNative ? "bundled:native" : "bundled:source";
|
|
const cached = sharedBundledPublicSurfaceJitiLoaders.get(cacheKey);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url);
|
|
const loader = createJiti(import.meta.url, {
|
|
...buildPluginLoaderJitiOptions(aliasMap),
|
|
tryNative,
|
|
});
|
|
sharedBundledPublicSurfaceJitiLoaders.set(cacheKey, loader);
|
|
return loader;
|
|
}
|
|
|
|
export function loadBundledPluginPublicArtifactModuleSync<T extends object>(params: {
|
|
dirName: string;
|
|
artifactBasename: string;
|
|
}): T {
|
|
const location = resolvePublicSurfaceLocation(params);
|
|
if (!location) {
|
|
throw new Error(
|
|
`Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
|
|
);
|
|
}
|
|
const cached = loadedPublicSurfaceModules.get(location.modulePath);
|
|
if (cached) {
|
|
return cached as T;
|
|
}
|
|
|
|
const opened = openBoundaryFileSync({
|
|
absolutePath: location.modulePath,
|
|
rootPath: location.boundaryRoot,
|
|
boundaryLabel:
|
|
location.boundaryRoot === OPENCLAW_PACKAGE_ROOT
|
|
? "OpenClaw package root"
|
|
: "bundled plugin directory",
|
|
rejectHardlinks: false,
|
|
});
|
|
if (!opened.ok) {
|
|
throw new Error(
|
|
`Unable to open bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
|
|
{ cause: opened.error },
|
|
);
|
|
}
|
|
fs.closeSync(opened.fd);
|
|
|
|
const sentinel = {} as T;
|
|
loadedPublicSurfaceModules.set(location.modulePath, sentinel);
|
|
try {
|
|
const loaded = loadPublicSurfaceModule(location.modulePath) as T;
|
|
Object.assign(sentinel, loaded);
|
|
return sentinel;
|
|
} catch (error) {
|
|
loadedPublicSurfaceModules.delete(location.modulePath);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export function resolveBundledPluginPublicArtifactPath(params: {
|
|
dirName: string;
|
|
artifactBasename: string;
|
|
}): string | null {
|
|
return resolvePublicSurfaceLocation(params)?.modulePath ?? null;
|
|
}
|
|
|
|
export function resetBundledPluginPublicArtifactLoaderForTest(): void {
|
|
loadedPublicSurfaceModules.clear();
|
|
publicSurfaceLocations.clear();
|
|
jitiLoaders.clear();
|
|
sharedBundledPublicSurfaceJitiLoaders.clear();
|
|
}
|