openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
293 lines (292 loc) • 12.6 kB
JavaScript
import { o as getPluginCacheRoot, s as getPluginCacheSource } from "./plugin-cache-DGWspMEc.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { c as readPluginCacheFile, n as parsePluginCacheJson } from "./plugin-cache-files-DLPF_Tw2.js";
import { i as resolveBundledPluginsDir, t as areBundledPluginsDisabled } from "./bundled-dir-BEfbEeCF.js";
import { a as resolvePluginRootPublicSurfacePath, i as resolveBundledPluginSourcePublicSurfacePath, n as normalizeBundledPluginArtifactSubpath, r as resolveBundledPluginPublicSurfacePath } from "./public-surface-runtime-DBNrrUv_.js";
import { i as loadPluginPublicSurfaceModuleSync, t as getCachedPluginModuleLoader } from "./plugin-module-loader-cache-lf3kAaBw.js";
import { s as resolveLoaderPackageRoot } from "./sdk-alias-BMTmaiMP.js";
import { t as shouldRejectHardlinkedPluginFiles } from "./hardlink-policy-CslDqyMD.js";
import { fileURLToPath } from "node:url";
import path from "node:path";
//#region src/plugin-sdk/facade-resolution-shared.ts
/**
* Shared resolver for bundled plugin facade module paths and registry fallbacks.
*/
function readBundledPluginManifestRecordFromDir(params) {
const file = readPluginCacheFile({
rootDir: path.join(params.pluginsRoot, params.resolvedDirName),
relativePath: "openclaw.plugin.json",
rejectHardlinks: false
});
if (!file.ok) return null;
try {
const parsed = parsePluginCacheJson(file, { json5: true });
if (!parsed.ok || !isRecord(parsed.value)) return null;
const raw = parsed.value;
if (typeof raw.id !== "string" || raw.id.trim().length === 0) return null;
return {
id: raw.id,
origin: "bundled",
enabledByDefault: raw.enabledByDefault === true,
rootDir: path.join(params.pluginsRoot, params.resolvedDirName),
channels: Array.isArray(raw.channels) ? raw.channels.filter((entry) => typeof entry === "string") : []
};
} catch {
return null;
}
}
/** Resolve bundled facade metadata without importing activation or registry runtime. */
function resolveBundledMetadataManifestRecord(params) {
if (!params.location) return null;
if (params.location.modulePath.startsWith(`${params.sourceExtensionsRoot}${path.sep}`)) {
const resolvedDirName = path.relative(params.sourceExtensionsRoot, params.location.modulePath).split(path.sep)[0];
if (!resolvedDirName) return null;
return readBundledPluginManifestRecordFromDir({
pluginsRoot: params.sourceExtensionsRoot,
resolvedDirName
});
}
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
if (!bundledPluginsDir) return null;
const normalizedBundledPluginsDir = path.resolve(bundledPluginsDir);
if (!params.location.modulePath.startsWith(`${normalizedBundledPluginsDir}${path.sep}`)) return null;
const resolvedDirName = path.relative(normalizedBundledPluginsDir, params.location.modulePath).split(path.sep)[0];
if (!resolvedDirName) return null;
return readBundledPluginManifestRecordFromDir({
pluginsRoot: normalizedBundledPluginsDir,
resolvedDirName
});
}
/** Builds the cache key for one facade lookup under the current bundled-plugin mode. */
function createFacadeResolutionKey(params) {
const disabledKey = areBundledPluginsDisabled(params.env ?? process.env) ? "disabled" : "enabled";
return `${params.dirName}::${params.artifactBasename}::${params.bundledPluginsDir ? path.resolve(params.bundledPluginsDir) : "<default>"}::${disabledKey}`;
}
/** Chooses the boundary root that should constrain a resolved facade module. */
function resolveFacadeBoundaryRoot(params) {
if (!params.bundledPluginsDir) return params.packageRoot;
const resolvedBundledPluginsDir = path.resolve(params.bundledPluginsDir);
return params.modulePath.startsWith(`${resolvedBundledPluginsDir}${path.sep}`) ? resolvedBundledPluginsDir : params.packageRoot;
}
/** Resolves a bundled facade from source in dev and built artifacts in dist installs. */
function resolveBundledFacadeModuleLocation(params) {
const env = params.env ?? process.env;
if (areBundledPluginsDisabled(env)) return null;
const preferSource = !params.currentModulePath.includes(`${path.sep}dist${path.sep}`);
const packageSourceRoot = path.resolve(params.packageRoot, "extensions");
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 ?? packageSourceRoot
}) ?? (params.bundledPluginsDir && !areBundledPluginsDisabled(env) ? resolveBundledPluginSourcePublicSurfacePath({
dirName: params.dirName,
artifactBasename: params.artifactBasename,
sourceRoot: packageSourceRoot
}) : null) ?? resolveBundledPluginPublicSurfacePath(publicSurfaceParams) : resolveBundledPluginPublicSurfacePath(publicSurfaceParams);
return modulePath ? {
modulePath,
boundaryRoot: resolveFacadeBoundaryRoot({
modulePath,
bundledPluginsDir: params.bundledPluginsDir,
packageRoot: params.packageRoot
})
} : null;
}
/** Resolves a facade path from manifest registry records using id, folder, then channel matches. */
function resolveRegistryPluginModuleLocationFromRecords(params) {
const tiers = [
(plugin) => plugin.id === params.dirName,
(plugin) => path.basename(plugin.rootDir) === params.dirName,
(plugin) => plugin.channels.includes(params.dirName)
];
const artifactBasename = normalizeBundledPluginArtifactSubpath(params.artifactBasename);
for (const matchFn of tiers) for (const record of params.registry.filter(matchFn)) {
const rootDir = path.resolve(record.rootDir);
const modulePath = resolvePluginRootPublicSurfacePath({
pluginRoot: rootDir,
artifactBasename
});
if (modulePath) return {
modulePath,
boundaryRoot: rootDir
};
}
return null;
}
//#endregion
//#region src/plugin-sdk/facade-loader.ts
/** Error thrown when a bundled plugin public surface artifact cannot be resolved. */
var MissingPublicSurfaceError = class extends Error {
constructor(message, options) {
super(message, options);
this.name = "MissingPublicSurfaceError";
}
};
const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url);
const loadedFacadePluginIds = /* @__PURE__ */ new Set();
function getOpenClawPackageRoot() {
return resolveLoaderPackageRoot({
modulePath: fileURLToPath(import.meta.url),
moduleUrl: import.meta.url
}) ?? fileURLToPath(new URL("../..", import.meta.url));
}
function resolveFacadeModuleLocation(params) {
const bundledPluginsDir = resolveBundledPluginsDir(params.env ?? process.env);
const key = `facade:${createFacadeResolutionKey({
...params,
bundledPluginsDir
})}`;
const artifacts = getPluginCacheRoot(getOpenClawPackageRoot()).artifacts;
const cached = artifacts.get(key);
if (cached !== void 0) return cached;
const location = resolveBundledFacadeModuleLocation({
...params,
currentModulePath: CURRENT_MODULE_PATH,
packageRoot: getOpenClawPackageRoot(),
bundledPluginsDir
});
artifacts.set(key, location);
return location;
}
function getModuleLoader(modulePath) {
return getCachedPluginModuleLoader({
modulePath,
importerUrl: import.meta.url,
preferBuiltDist: true,
loaderFilename: import.meta.url
});
}
/** Create an object proxy that loads the underlying facade only on first use. */
function createLazyFacadeObjectValue(load) {
let resolvedValue;
const resolve = () => resolvedValue ??= load();
const target = {};
const syncProperty = (property) => {
const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), property);
if (descriptor) Object.defineProperty(target, property, descriptor);
else Reflect.deleteProperty(target, property);
return descriptor;
};
const syncTarget = () => {
const original = resolve();
const descriptors = Object.getOwnPropertyDescriptors(original);
for (const property of Reflect.ownKeys(target)) if (!Object.hasOwn(descriptors, property)) Reflect.deleteProperty(target, property);
Object.defineProperties(target, descriptors);
Reflect.setPrototypeOf(target, Reflect.getPrototypeOf(original));
if (!Reflect.isExtensible(original)) Reflect.preventExtensions(target);
};
return new Proxy(target, {
defineProperty(_target, property, descriptor) {
const defined = Reflect.defineProperty(resolve(), property, descriptor);
if (defined) syncProperty(property);
return defined;
},
deleteProperty(_target, property) {
return Reflect.deleteProperty(resolve(), property) && Reflect.deleteProperty(target, property);
},
get(_target, property, receiver) {
return Reflect.get(resolve(), property, receiver);
},
getOwnPropertyDescriptor(_target, property) {
return syncProperty(property);
},
getPrototypeOf() {
return Reflect.getPrototypeOf(resolve());
},
has(_target, property) {
const present = Reflect.has(resolve(), property);
if (!present) Reflect.deleteProperty(target, property);
return present;
},
isExtensible() {
const extensible = Reflect.isExtensible(resolve());
if (!extensible) syncTarget();
return extensible;
},
ownKeys() {
syncTarget();
return Reflect.ownKeys(resolve());
},
preventExtensions() {
if (!Reflect.preventExtensions(resolve())) return false;
syncTarget();
return true;
},
set(_target, property, value, receiver) {
return Reflect.set(resolve(), property, value, receiver);
},
setPrototypeOf(_target, prototype) {
return Reflect.setPrototypeOf(resolve(), prototype) && Reflect.setPrototypeOf(target, prototype);
}
});
}
function trackFacadeModule(modulePath, pluginId) {
const source = getPluginCacheSource(modulePath);
if (source.facadeTracked) return;
source.facadeTracked = true;
try {
loadedFacadePluginIds.add(typeof pluginId === "function" ? pluginId() : pluginId);
} catch (error) {
delete source.facadeTracked;
throw error;
}
}
function isPathAtOrInside(target, root) {
const resolvedRoot = path.resolve(root);
const resolvedTarget = path.resolve(target);
return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);
}
function resolveFacadeBoundaryOpenParams(boundaryRoot) {
const checked = getPluginCacheRoot(boundaryRoot).publicSurfaceBoundary;
if (checked) return checked;
if (isPathAtOrInside(boundaryRoot, getOpenClawPackageRoot())) return {
boundaryLabel: "OpenClaw package root",
rejectHardlinks: false
};
const bundledDir = resolveBundledPluginsDir();
if (bundledDir && isPathAtOrInside(boundaryRoot, bundledDir)) return {
boundaryLabel: "bundled plugin directory",
rejectHardlinks: false
};
return {
boundaryLabel: "plugin root",
rejectHardlinks: shouldRejectHardlinkedPluginFiles({
origin: "global",
rootDir: boundaryRoot
})
};
}
/** Load and cache a facade module after verifying it is inside its declared boundary root. */
function loadFacadeModuleAtLocationSync(params) {
const location = params.location;
const loaded = loadPluginPublicSurfaceModuleSync({
...location,
...resolveFacadeBoundaryOpenParams(location.boundaryRoot),
surfaceLabel: `bundled plugin public surface ${location.modulePath}`,
loadModule: params.loadModule ?? ((modulePath) => getModuleLoader(modulePath)(modulePath))
});
trackFacadeModule(location.modulePath, params.trackedPluginId);
return loaded;
}
/** Resolve and synchronously load a bundled plugin public surface by plugin dir and artifact name. */
function loadBundledPluginPublicSurfaceModuleSyncCore(params) {
const location = resolveFacadeModuleLocation(params);
if (!location) throw new MissingPublicSurfaceError(`Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`);
return loadFacadeModuleAtLocationSync({
location,
trackedPluginId: params.trackedPluginId ?? params.dirName
});
}
/** List plugin ids whose public facades have been loaded in this process. */
function listImportedBundledPluginFacadeIds() {
return [...loadedFacadePluginIds].toSorted((left, right) => left.localeCompare(right));
}
//#endregion
export { loadFacadeModuleAtLocationSync as a, resolveBundledMetadataManifestRecord as c, loadBundledPluginPublicSurfaceModuleSyncCore as i, resolveRegistryPluginModuleLocationFromRecords as l, createLazyFacadeObjectValue as n, createFacadeResolutionKey as o, listImportedBundledPluginFacadeIds as r, resolveBundledFacadeModuleLocation as s, MissingPublicSurfaceError as t };