mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
refactor: share facade resolution helpers
This commit is contained in:
@@ -16,10 +16,7 @@ import {
|
||||
loadPluginManifestRegistry,
|
||||
type PluginManifestRecord,
|
||||
} from "../plugins/manifest-registry.js";
|
||||
import {
|
||||
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
|
||||
normalizeBundledPluginArtifactSubpath,
|
||||
} from "../plugins/public-surface-runtime.js";
|
||||
import { resolveRegistryPluginModuleLocationFromRecords } from "./facade-resolution-shared.js";
|
||||
|
||||
const ALWAYS_ALLOWED_RUNTIME_DIR_NAMES = new Set([
|
||||
"image-generation-core",
|
||||
@@ -171,30 +168,11 @@ export function resolveRegistryPluginModuleLocation(params: {
|
||||
cacheKey: params.resolutionKey,
|
||||
...(params.env ? { env: params.env } : {}),
|
||||
});
|
||||
type RegistryRecord = (typeof 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 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;
|
||||
return resolveRegistryPluginModuleLocationFromRecords({
|
||||
registry,
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
});
|
||||
}
|
||||
|
||||
function readBundledPluginManifestRecordFromDir(params: {
|
||||
|
||||
@@ -9,11 +9,12 @@ import {
|
||||
type PluginJitiLoaderCache,
|
||||
type PluginJitiLoaderFactory,
|
||||
} from "../plugins/jiti-loader-cache.js";
|
||||
import {
|
||||
resolveBundledPluginSourcePublicSurfacePath,
|
||||
resolveBundledPluginPublicSurfacePath,
|
||||
} from "../plugins/public-surface-runtime.js";
|
||||
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
|
||||
import {
|
||||
createFacadeResolutionKey as createFacadeResolutionKeyShared,
|
||||
resolveBundledFacadeModuleLocation,
|
||||
resolveCachedFacadeModuleLocation,
|
||||
} from "./facade-resolution-shared.js";
|
||||
|
||||
const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url);
|
||||
|
||||
@@ -58,7 +59,7 @@ function createFacadeResolutionKey(params: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
|
||||
return createFacadeResolutionKeyShared({ ...params, bundledPluginsDir });
|
||||
}
|
||||
|
||||
function resolveFacadeModuleLocationUncached(params: {
|
||||
@@ -67,48 +68,12 @@ function resolveFacadeModuleLocationUncached(params: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
|
||||
if (preferSource) {
|
||||
const modulePath =
|
||||
resolveBundledPluginSourcePublicSurfacePath({
|
||||
...params,
|
||||
sourceRoot: bundledPluginsDir ?? path.resolve(getOpenClawPackageRoot(), "extensions"),
|
||||
}) ??
|
||||
resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: getOpenClawPackageRoot(),
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
});
|
||||
if (modulePath) {
|
||||
return {
|
||||
modulePath,
|
||||
boundaryRoot:
|
||||
bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep)
|
||||
? path.resolve(bundledPluginsDir)
|
||||
: getOpenClawPackageRoot(),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const modulePath = resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: getOpenClawPackageRoot(),
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
return resolveBundledFacadeModuleLocation({
|
||||
...params,
|
||||
currentModulePath: CURRENT_MODULE_PATH,
|
||||
packageRoot: getOpenClawPackageRoot(),
|
||||
bundledPluginsDir,
|
||||
});
|
||||
if (!modulePath) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
modulePath,
|
||||
boundaryRoot:
|
||||
bundledPluginsDir && modulePath.startsWith(path.resolve(bundledPluginsDir) + path.sep)
|
||||
? path.resolve(bundledPluginsDir)
|
||||
: getOpenClawPackageRoot(),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveFacadeModuleLocation(params: {
|
||||
@@ -116,13 +81,11 @@ function resolveFacadeModuleLocation(params: {
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { 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;
|
||||
return resolveCachedFacadeModuleLocation({
|
||||
cache: cachedFacadeModuleLocationsByKey,
|
||||
key: createFacadeResolutionKey(params),
|
||||
resolve: () => resolveFacadeModuleLocationUncached(params),
|
||||
});
|
||||
}
|
||||
|
||||
function getJiti(modulePath: string) {
|
||||
|
||||
121
src/plugin-sdk/facade-resolution-shared.ts
Normal file
121
src/plugin-sdk/facade-resolution-shared.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
|
||||
normalizeBundledPluginArtifactSubpath,
|
||||
resolveBundledPluginPublicSurfacePath,
|
||||
resolveBundledPluginSourcePublicSurfacePath,
|
||||
} from "../plugins/public-surface-runtime.js";
|
||||
|
||||
export type FacadeModuleLocationLike = {
|
||||
modulePath: string;
|
||||
boundaryRoot: string;
|
||||
};
|
||||
|
||||
type FacadeRegistryRecordLike = {
|
||||
id: string;
|
||||
rootDir: string;
|
||||
channels: readonly string[];
|
||||
};
|
||||
|
||||
export function createFacadeResolutionKey(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
bundledPluginsDir?: string | null;
|
||||
}): string {
|
||||
return `${params.dirName}::${params.artifactBasename}::${
|
||||
params.bundledPluginsDir ? path.resolve(params.bundledPluginsDir) : "<default>"
|
||||
}`;
|
||||
}
|
||||
|
||||
export function resolveCachedFacadeModuleLocation<TLocation>(params: {
|
||||
cache: Map<string, TLocation | null>;
|
||||
key: string;
|
||||
resolve: () => TLocation | null;
|
||||
}): TLocation | null {
|
||||
if (params.cache.has(params.key)) {
|
||||
return params.cache.get(params.key) ?? null;
|
||||
}
|
||||
const resolved = params.resolve();
|
||||
params.cache.set(params.key, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function resolveFacadeBoundaryRoot(params: {
|
||||
modulePath: string;
|
||||
bundledPluginsDir?: string | null;
|
||||
packageRoot: string;
|
||||
}): string {
|
||||
if (!params.bundledPluginsDir) {
|
||||
return params.packageRoot;
|
||||
}
|
||||
const resolvedBundledPluginsDir = path.resolve(params.bundledPluginsDir);
|
||||
return params.modulePath.startsWith(`${resolvedBundledPluginsDir}${path.sep}`)
|
||||
? resolvedBundledPluginsDir
|
||||
: params.packageRoot;
|
||||
}
|
||||
|
||||
export function resolveBundledFacadeModuleLocation(params: {
|
||||
currentModulePath: string;
|
||||
packageRoot: string;
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
bundledPluginsDir?: string | null;
|
||||
}): FacadeModuleLocationLike | null {
|
||||
const preferSource = !params.currentModulePath.includes(`${path.sep}dist${path.sep}`);
|
||||
const publicSurfaceParams = {
|
||||
rootDir: params.packageRoot,
|
||||
env: params.env,
|
||||
...(params.bundledPluginsDir ? { bundledPluginsDir: params.bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
};
|
||||
const modulePath = preferSource
|
||||
? (resolveBundledPluginSourcePublicSurfacePath({
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
sourceRoot: params.bundledPluginsDir ?? path.resolve(params.packageRoot, "extensions"),
|
||||
}) ?? resolveBundledPluginPublicSurfacePath(publicSurfaceParams))
|
||||
: resolveBundledPluginPublicSurfacePath(publicSurfaceParams);
|
||||
return modulePath
|
||||
? {
|
||||
modulePath,
|
||||
boundaryRoot: resolveFacadeBoundaryRoot({
|
||||
modulePath,
|
||||
bundledPluginsDir: params.bundledPluginsDir,
|
||||
packageRoot: params.packageRoot,
|
||||
}),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
export function resolveRegistryPluginModuleLocationFromRecords(params: {
|
||||
registry: readonly FacadeRegistryRecordLike[];
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
}): FacadeModuleLocationLike | null {
|
||||
const tiers: Array<(plugin: FacadeRegistryRecordLike) => 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;
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -7,13 +6,6 @@ import {
|
||||
getCachedPluginJitiLoader,
|
||||
type PluginJitiLoaderCache,
|
||||
} from "../plugins/jiti-loader-cache.js";
|
||||
import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
|
||||
import {
|
||||
PUBLIC_SURFACE_SOURCE_EXTENSIONS,
|
||||
normalizeBundledPluginArtifactSubpath,
|
||||
resolveBundledPluginSourcePublicSurfacePath,
|
||||
resolveBundledPluginPublicSurfacePath,
|
||||
} from "../plugins/public-surface-runtime.js";
|
||||
import { resolveLoaderPackageRoot } from "../plugins/sdk-alias.js";
|
||||
import {
|
||||
loadBundledPluginPublicSurfaceModuleSync as loadBundledPluginPublicSurfaceModuleSyncLight,
|
||||
@@ -21,6 +13,12 @@ import {
|
||||
resetFacadeLoaderStateForTest,
|
||||
type FacadeModuleLocation,
|
||||
} from "./facade-loader.js";
|
||||
import {
|
||||
createFacadeResolutionKey as createFacadeResolutionKeyShared,
|
||||
resolveBundledFacadeModuleLocation,
|
||||
resolveCachedFacadeModuleLocation,
|
||||
resolveRegistryPluginModuleLocationFromRecords,
|
||||
} from "./facade-resolution-shared.js";
|
||||
export {
|
||||
createLazyFacadeArrayValue,
|
||||
createLazyFacadeObjectValue,
|
||||
@@ -61,38 +59,7 @@ function createFacadeResolutionKey(params: {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): string {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
return `${params.dirName}::${params.artifactBasename}::${bundledPluginsDir ? path.resolve(bundledPluginsDir) : "<default>"}`;
|
||||
}
|
||||
|
||||
function resolveRegistryPluginModuleLocationFromRegistry(params: {
|
||||
registry: readonly Pick<PluginManifestRecord, "id" | "rootDir" | "channels">[];
|
||||
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;
|
||||
return createFacadeResolutionKeyShared({ ...params, bundledPluginsDir });
|
||||
}
|
||||
|
||||
function resolveRegistryPluginModuleLocation(params: {
|
||||
@@ -106,56 +73,20 @@ function resolveRegistryPluginModuleLocation(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function resolveFacadeBoundaryRoot(modulePath: string, bundledPluginsDir: string | undefined) {
|
||||
if (!bundledPluginsDir) {
|
||||
return OPENCLAW_PACKAGE_ROOT;
|
||||
}
|
||||
const resolvedBundledPluginsDir = path.resolve(bundledPluginsDir);
|
||||
return modulePath.startsWith(`${resolvedBundledPluginsDir}${path.sep}`)
|
||||
? resolvedBundledPluginsDir
|
||||
: OPENCLAW_PACKAGE_ROOT;
|
||||
}
|
||||
|
||||
function resolveFacadeModuleLocationUncached(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { modulePath: string; boundaryRoot: string } | null {
|
||||
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
|
||||
const preferSource = !CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`);
|
||||
if (preferSource) {
|
||||
const modulePath =
|
||||
resolveBundledPluginSourcePublicSurfacePath({
|
||||
...params,
|
||||
sourceRoot: bundledPluginsDir ?? path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions"),
|
||||
}) ??
|
||||
resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: OPENCLAW_PACKAGE_ROOT,
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
});
|
||||
if (modulePath) {
|
||||
return {
|
||||
modulePath,
|
||||
boundaryRoot: resolveFacadeBoundaryRoot(modulePath, bundledPluginsDir),
|
||||
};
|
||||
}
|
||||
return resolveRegistryPluginModuleLocation(params);
|
||||
}
|
||||
const modulePath = resolveBundledPluginPublicSurfacePath({
|
||||
rootDir: OPENCLAW_PACKAGE_ROOT,
|
||||
env: params.env,
|
||||
...(bundledPluginsDir ? { bundledPluginsDir } : {}),
|
||||
dirName: params.dirName,
|
||||
artifactBasename: params.artifactBasename,
|
||||
const bundledLocation = resolveBundledFacadeModuleLocation({
|
||||
...params,
|
||||
currentModulePath: CURRENT_MODULE_PATH,
|
||||
packageRoot: OPENCLAW_PACKAGE_ROOT,
|
||||
bundledPluginsDir,
|
||||
});
|
||||
if (modulePath) {
|
||||
return {
|
||||
modulePath,
|
||||
boundaryRoot: resolveFacadeBoundaryRoot(modulePath, bundledPluginsDir),
|
||||
};
|
||||
if (bundledLocation) {
|
||||
return bundledLocation;
|
||||
}
|
||||
return resolveRegistryPluginModuleLocation(params);
|
||||
}
|
||||
@@ -165,13 +96,11 @@ function resolveFacadeModuleLocation(params: {
|
||||
artifactBasename: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
}): { 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;
|
||||
return resolveCachedFacadeModuleLocation({
|
||||
cache: cachedFacadeModuleLocationsByKey,
|
||||
key: createFacadeResolutionKey(params),
|
||||
resolve: () => resolveFacadeModuleLocationUncached(params),
|
||||
});
|
||||
}
|
||||
|
||||
type BundledPluginPublicSurfaceParams = {
|
||||
@@ -324,7 +253,7 @@ export function resetFacadeRuntimeStateForTest(): void {
|
||||
|
||||
export const __testing = {
|
||||
loadFacadeModuleAtLocationSync,
|
||||
resolveRegistryPluginModuleLocationFromRegistry,
|
||||
resolveRegistryPluginModuleLocationFromRegistry: resolveRegistryPluginModuleLocationFromRecords,
|
||||
resolveFacadeModuleLocation,
|
||||
evaluateBundledPluginPublicSurfaceAccess: ((
|
||||
...args: Parameters<
|
||||
|
||||
Reference in New Issue
Block a user