mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-25 08:52:12 +00:00
307 lines
7.9 KiB
JavaScript
307 lines
7.9 KiB
JavaScript
"use strict";
|
|
|
|
const path = require("node:path");
|
|
const fs = require("node:fs");
|
|
|
|
let monolithicSdk = null;
|
|
let diagnosticEventsModule = null;
|
|
const jitiLoaders = new Map();
|
|
const pluginSdkSubpathsCache = new Map();
|
|
const shouldPreferSourceInTests = Boolean(process.env.VITEST) || process.env.NODE_ENV === "test";
|
|
|
|
function emptyPluginConfigSchema() {
|
|
function error(message) {
|
|
return { success: false, error: { issues: [{ path: [], message }] } };
|
|
}
|
|
|
|
return {
|
|
safeParse(value) {
|
|
if (value === undefined) {
|
|
return { success: true, data: undefined };
|
|
}
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return error("expected config object");
|
|
}
|
|
if (Object.keys(value).length > 0) {
|
|
return error("config must be empty");
|
|
}
|
|
return { success: true, data: value };
|
|
},
|
|
jsonSchema: {
|
|
type: "object",
|
|
additionalProperties: false,
|
|
properties: {},
|
|
},
|
|
};
|
|
}
|
|
|
|
function resolveCommandAuthorizedFromAuthorizers(params) {
|
|
const { useAccessGroups, authorizers } = params;
|
|
const mode = params.modeWhenAccessGroupsOff ?? "allow";
|
|
if (!useAccessGroups) {
|
|
if (mode === "allow") {
|
|
return true;
|
|
}
|
|
if (mode === "deny") {
|
|
return false;
|
|
}
|
|
const anyConfigured = authorizers.some((entry) => entry.configured);
|
|
if (!anyConfigured) {
|
|
return true;
|
|
}
|
|
return authorizers.some((entry) => entry.configured && entry.allowed);
|
|
}
|
|
return authorizers.some((entry) => entry.configured && entry.allowed);
|
|
}
|
|
|
|
function resolveControlCommandGate(params) {
|
|
const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
|
|
useAccessGroups: params.useAccessGroups,
|
|
authorizers: params.authorizers,
|
|
modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff,
|
|
});
|
|
const shouldBlock = params.allowTextCommands && params.hasControlCommand && !commandAuthorized;
|
|
return { commandAuthorized, shouldBlock };
|
|
}
|
|
|
|
function onDiagnosticEvent(listener) {
|
|
const monolithic = loadMonolithicSdk();
|
|
if (monolithic && typeof monolithic.onDiagnosticEvent === "function") {
|
|
return monolithic.onDiagnosticEvent(listener);
|
|
}
|
|
const diagnosticEvents = loadDiagnosticEventsModule();
|
|
if (!diagnosticEvents || typeof diagnosticEvents.onDiagnosticEvent !== "function") {
|
|
throw new Error("openclaw/plugin-sdk root alias could not resolve onDiagnosticEvent");
|
|
}
|
|
return diagnosticEvents.onDiagnosticEvent(listener);
|
|
}
|
|
|
|
function getPackageRoot() {
|
|
return path.resolve(__dirname, "..", "..");
|
|
}
|
|
|
|
function listPluginSdkExportedSubpaths() {
|
|
const packageRoot = getPackageRoot();
|
|
if (pluginSdkSubpathsCache.has(packageRoot)) {
|
|
return pluginSdkSubpathsCache.get(packageRoot);
|
|
}
|
|
|
|
let subpaths = [];
|
|
try {
|
|
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
subpaths = Object.keys(packageJson.exports ?? {})
|
|
.filter((key) => key.startsWith("./plugin-sdk/"))
|
|
.map((key) => key.slice("./plugin-sdk/".length));
|
|
} catch {
|
|
subpaths = [];
|
|
}
|
|
|
|
pluginSdkSubpathsCache.set(packageRoot, subpaths);
|
|
return subpaths;
|
|
}
|
|
|
|
function buildPluginSdkAliasMap(useDist) {
|
|
const packageRoot = getPackageRoot();
|
|
const pluginSdkDir = path.join(packageRoot, useDist ? "dist" : "src", "plugin-sdk");
|
|
const ext = useDist ? ".js" : ".ts";
|
|
const aliasMap = {
|
|
"openclaw/plugin-sdk": __filename,
|
|
};
|
|
|
|
for (const subpath of listPluginSdkExportedSubpaths()) {
|
|
const candidate = path.join(pluginSdkDir, `${subpath}${ext}`);
|
|
if (fs.existsSync(candidate)) {
|
|
aliasMap[`openclaw/plugin-sdk/${subpath}`] = candidate;
|
|
}
|
|
}
|
|
|
|
return aliasMap;
|
|
}
|
|
|
|
function getJiti(tryNative) {
|
|
if (jitiLoaders.has(tryNative)) {
|
|
return jitiLoaders.get(tryNative);
|
|
}
|
|
|
|
const { createJiti } = require("jiti");
|
|
const jitiLoader = createJiti(__filename, {
|
|
alias: buildPluginSdkAliasMap(tryNative),
|
|
interopDefault: true,
|
|
// Prefer Node's native sync ESM loader for built dist/plugin-sdk/*.js files
|
|
// so local plugins do not create a second transpiled OpenClaw core graph.
|
|
tryNative,
|
|
extensions: [".ts", ".tsx", ".mts", ".cts", ".mtsx", ".ctsx", ".js", ".mjs", ".cjs", ".json"],
|
|
});
|
|
jitiLoaders.set(tryNative, jitiLoader);
|
|
return jitiLoader;
|
|
}
|
|
|
|
function loadMonolithicSdk() {
|
|
if (monolithicSdk) {
|
|
return monolithicSdk;
|
|
}
|
|
|
|
const distCandidate = path.resolve(__dirname, "..", "..", "dist", "plugin-sdk", "compat.js");
|
|
if (!shouldPreferSourceInTests && fs.existsSync(distCandidate)) {
|
|
try {
|
|
monolithicSdk = getJiti(true)(distCandidate);
|
|
return monolithicSdk;
|
|
} catch {
|
|
// Fall through to source alias if dist is unavailable or stale.
|
|
}
|
|
}
|
|
|
|
monolithicSdk = getJiti(false)(path.join(__dirname, "compat.ts"));
|
|
return monolithicSdk;
|
|
}
|
|
|
|
function loadDiagnosticEventsModule() {
|
|
if (diagnosticEventsModule) {
|
|
return diagnosticEventsModule;
|
|
}
|
|
|
|
const distCandidate = path.resolve(
|
|
__dirname,
|
|
"..",
|
|
"..",
|
|
"dist",
|
|
"infra",
|
|
"diagnostic-events.js",
|
|
);
|
|
if (!shouldPreferSourceInTests && fs.existsSync(distCandidate)) {
|
|
try {
|
|
diagnosticEventsModule = getJiti(true)(distCandidate);
|
|
return diagnosticEventsModule;
|
|
} catch {
|
|
// Fall through to source path if dist is unavailable or stale.
|
|
}
|
|
}
|
|
|
|
diagnosticEventsModule = getJiti(false)(
|
|
path.join(__dirname, "..", "infra", "diagnostic-events.ts"),
|
|
);
|
|
return diagnosticEventsModule;
|
|
}
|
|
|
|
function tryLoadMonolithicSdk() {
|
|
try {
|
|
return loadMonolithicSdk();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const fastExports = {
|
|
emptyPluginConfigSchema,
|
|
onDiagnosticEvent,
|
|
resolveControlCommandGate,
|
|
};
|
|
|
|
const target = { ...fastExports };
|
|
let rootExports = null;
|
|
|
|
function shouldResolveMonolithic(prop) {
|
|
if (typeof prop !== "string") {
|
|
return false;
|
|
}
|
|
return prop !== "then";
|
|
}
|
|
|
|
function getMonolithicSdk() {
|
|
const loaded = tryLoadMonolithicSdk();
|
|
if (loaded && typeof loaded === "object") {
|
|
return loaded;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function getExportValue(prop) {
|
|
if (Reflect.has(target, prop)) {
|
|
return Reflect.get(target, prop);
|
|
}
|
|
if (!shouldResolveMonolithic(prop)) {
|
|
return undefined;
|
|
}
|
|
const monolithic = getMonolithicSdk();
|
|
if (!monolithic) {
|
|
return undefined;
|
|
}
|
|
return Reflect.get(monolithic, prop);
|
|
}
|
|
|
|
function getExportDescriptor(prop) {
|
|
const ownDescriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
if (ownDescriptor) {
|
|
return ownDescriptor;
|
|
}
|
|
if (!shouldResolveMonolithic(prop)) {
|
|
return undefined;
|
|
}
|
|
|
|
const monolithic = getMonolithicSdk();
|
|
if (!monolithic) {
|
|
return undefined;
|
|
}
|
|
|
|
const descriptor = Reflect.getOwnPropertyDescriptor(monolithic, prop);
|
|
if (!descriptor) {
|
|
return undefined;
|
|
}
|
|
|
|
// Proxy invariants require descriptors returned for dynamic properties to be configurable.
|
|
return {
|
|
...descriptor,
|
|
configurable: true,
|
|
};
|
|
}
|
|
|
|
rootExports = new Proxy(target, {
|
|
get(_target, prop, receiver) {
|
|
if (Reflect.has(target, prop)) {
|
|
return Reflect.get(target, prop, receiver);
|
|
}
|
|
return getExportValue(prop);
|
|
},
|
|
has(_target, prop) {
|
|
if (Reflect.has(target, prop)) {
|
|
return true;
|
|
}
|
|
if (!shouldResolveMonolithic(prop)) {
|
|
return false;
|
|
}
|
|
const monolithic = getMonolithicSdk();
|
|
return monolithic ? Reflect.has(monolithic, prop) : false;
|
|
},
|
|
ownKeys() {
|
|
const keys = new Set(Reflect.ownKeys(target));
|
|
if (monolithicSdk && typeof monolithicSdk === "object") {
|
|
for (const key of Reflect.ownKeys(monolithicSdk)) {
|
|
if (!keys.has(key)) {
|
|
keys.add(key);
|
|
}
|
|
}
|
|
}
|
|
return [...keys];
|
|
},
|
|
getOwnPropertyDescriptor(_target, prop) {
|
|
return getExportDescriptor(prop);
|
|
},
|
|
});
|
|
|
|
Object.defineProperty(target, "__esModule", {
|
|
configurable: true,
|
|
enumerable: false,
|
|
writable: false,
|
|
value: true,
|
|
});
|
|
Object.defineProperty(target, "default", {
|
|
configurable: true,
|
|
enumerable: false,
|
|
get() {
|
|
return rootExports;
|
|
},
|
|
});
|
|
|
|
module.exports = rootExports;
|