mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-02 21:30:21 +00:00
refactor: dedupe helpers and source seams
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
import { VERSION } from "../../version.js";
|
||||
import { listWebSearchProviders, runWebSearch } from "../../web-search/runtime.js";
|
||||
import { createRuntimeAgent } from "./runtime-agent.js";
|
||||
import { defineCachedValue } from "./runtime-cache.js";
|
||||
import { createRuntimeChannel } from "./runtime-channel.js";
|
||||
import { createRuntimeConfig } from "./runtime-config.js";
|
||||
import { createRuntimeEvents } from "./runtime-events.js";
|
||||
@@ -21,26 +22,6 @@ import { createRuntimeSystem } from "./runtime-system.js";
|
||||
import { createRuntimeTools } from "./runtime-tools.js";
|
||||
import type { PluginRuntime } from "./types.js";
|
||||
|
||||
function defineCachedValue<T extends object, K extends PropertyKey>(
|
||||
target: T,
|
||||
key: K,
|
||||
create: () => unknown,
|
||||
): void {
|
||||
let cached: unknown;
|
||||
let ready = false;
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
if (!ready) {
|
||||
cached = create();
|
||||
ready = true;
|
||||
}
|
||||
return cached;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const loadTtsRuntime = createLazyRuntimeModule(() => import("./runtime-tts.runtime.js"));
|
||||
const loadMediaUnderstandingRuntime = createLazyRuntimeModule(
|
||||
() => import("./runtime-media-understanding.runtime.js"),
|
||||
|
||||
@@ -7,28 +7,9 @@ import { ensureAgentWorkspace } from "../../agents/workspace.js";
|
||||
import { resolveSessionFilePath, resolveStorePath } from "../../config/sessions/paths.js";
|
||||
import { loadSessionStore, saveSessionStore } from "../../config/sessions/store.js";
|
||||
import { createLazyRuntimeMethod, createLazyRuntimeModule } from "../../shared/lazy-runtime.js";
|
||||
import { defineCachedValue } from "./runtime-cache.js";
|
||||
import type { PluginRuntime } from "./types.js";
|
||||
|
||||
function defineCachedValue<T extends object, K extends PropertyKey>(
|
||||
target: T,
|
||||
key: K,
|
||||
create: () => unknown,
|
||||
): void {
|
||||
let cached: unknown;
|
||||
let ready = false;
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
if (!ready) {
|
||||
cached = create();
|
||||
ready = true;
|
||||
}
|
||||
return cached;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const loadEmbeddedPiRuntime = createLazyRuntimeModule(
|
||||
() => import("./runtime-embedded-pi.runtime.js"),
|
||||
);
|
||||
|
||||
19
src/plugins/runtime/runtime-cache.ts
Normal file
19
src/plugins/runtime/runtime-cache.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export function defineCachedValue<T extends object, K extends PropertyKey>(
|
||||
target: T,
|
||||
key: K,
|
||||
create: () => unknown,
|
||||
): void {
|
||||
let cached: unknown;
|
||||
let ready = false;
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
if (!ready) {
|
||||
cached = create();
|
||||
ready = true;
|
||||
}
|
||||
return cached;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -76,6 +76,7 @@ import {
|
||||
resolveLineAccount,
|
||||
} from "../../plugin-sdk/line.js";
|
||||
import { buildAgentSessionKey, resolveAgentRoute } from "../../routing/resolve-route.js";
|
||||
import { defineCachedValue } from "./runtime-cache.js";
|
||||
import { createRuntimeDiscord } from "./runtime-discord.js";
|
||||
import { createRuntimeIMessage } from "./runtime-imessage.js";
|
||||
import { createRuntimeMatrix } from "./runtime-matrix.js";
|
||||
@@ -85,26 +86,6 @@ import { createRuntimeTelegram } from "./runtime-telegram.js";
|
||||
import { createRuntimeWhatsApp } from "./runtime-whatsapp.js";
|
||||
import type { PluginRuntime } from "./types.js";
|
||||
|
||||
function defineCachedValue<T extends object, K extends PropertyKey>(
|
||||
target: T,
|
||||
key: K,
|
||||
create: () => unknown,
|
||||
): void {
|
||||
let cached: unknown;
|
||||
let ready = false;
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
if (!ready) {
|
||||
cached = create();
|
||||
ready = true;
|
||||
}
|
||||
return cached;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createRuntimeChannel(): PluginRuntime["channel"] {
|
||||
const channelRuntime = {
|
||||
text: {
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createJiti } from "jiti";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { loadPluginManifestRegistry } from "../manifest-registry.js";
|
||||
import {
|
||||
buildPluginLoaderJitiOptions,
|
||||
resolvePluginSdkAliasFile,
|
||||
resolvePluginSdkScopedAliasMap,
|
||||
shouldPreferNativeJiti,
|
||||
} from "../sdk-alias.js";
|
||||
loadPluginBoundaryModuleWithJiti,
|
||||
resolvePluginRuntimeModulePath,
|
||||
resolvePluginRuntimeRecord,
|
||||
} from "./runtime-plugin-boundary.js";
|
||||
|
||||
const MATRIX_PLUGIN_ID = "matrix";
|
||||
|
||||
@@ -24,70 +19,12 @@ let cachedModule: MatrixModule | null = null;
|
||||
|
||||
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
function readConfigSafely() {
|
||||
try {
|
||||
return loadConfig();
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function resolveMatrixPluginRecord(): MatrixPluginRecord | null {
|
||||
const manifestRegistry = loadPluginManifestRegistry({
|
||||
config: readConfigSafely(),
|
||||
cache: true,
|
||||
});
|
||||
const record = manifestRegistry.plugins.find((plugin) => plugin.id === MATRIX_PLUGIN_ID);
|
||||
if (!record?.source) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
rootDir: record.rootDir,
|
||||
source: record.source,
|
||||
};
|
||||
return resolvePluginRuntimeRecord(MATRIX_PLUGIN_ID) as MatrixPluginRecord | null;
|
||||
}
|
||||
|
||||
function resolveMatrixRuntimeModulePath(record: MatrixPluginRecord): string | null {
|
||||
const candidates = [
|
||||
path.join(path.dirname(record.source), "runtime-api.js"),
|
||||
path.join(path.dirname(record.source), "runtime-api.ts"),
|
||||
...(record.rootDir
|
||||
? [path.join(record.rootDir, "runtime-api.js"), path.join(record.rootDir, "runtime-api.ts")]
|
||||
: []),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getJiti(modulePath: string) {
|
||||
const tryNative = shouldPreferNativeJiti(modulePath);
|
||||
const cached = jitiLoaders.get(tryNative);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const pluginSdkAlias = resolvePluginSdkAliasFile({
|
||||
srcFile: "root-alias.cjs",
|
||||
distFile: "root-alias.cjs",
|
||||
modulePath,
|
||||
});
|
||||
const aliasMap = {
|
||||
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
|
||||
...resolvePluginSdkScopedAliasMap({ modulePath }),
|
||||
};
|
||||
const loader = createJiti(import.meta.url, {
|
||||
...buildPluginLoaderJitiOptions(aliasMap),
|
||||
tryNative,
|
||||
});
|
||||
jitiLoaders.set(tryNative, loader);
|
||||
return loader;
|
||||
}
|
||||
|
||||
function loadWithJiti<TModule>(modulePath: string): TModule {
|
||||
return getJiti(modulePath)(modulePath) as TModule;
|
||||
return resolvePluginRuntimeModulePath(record, "runtime-api");
|
||||
}
|
||||
|
||||
function loadMatrixModule(): MatrixModule | null {
|
||||
@@ -102,7 +39,7 @@ function loadMatrixModule(): MatrixModule | null {
|
||||
if (cachedModule && cachedModulePath === modulePath) {
|
||||
return cachedModule;
|
||||
}
|
||||
const loaded = loadWithJiti<MatrixModule>(modulePath);
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<MatrixModule>(modulePath, jitiLoaders);
|
||||
cachedModulePath = modulePath;
|
||||
cachedModule = loaded;
|
||||
return loaded;
|
||||
|
||||
106
src/plugins/runtime/runtime-plugin-boundary.ts
Normal file
106
src/plugins/runtime/runtime-plugin-boundary.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createJiti } from "jiti";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { loadPluginManifestRegistry } from "../manifest-registry.js";
|
||||
import {
|
||||
buildPluginLoaderJitiOptions,
|
||||
resolvePluginSdkAliasFile,
|
||||
resolvePluginSdkScopedAliasMap,
|
||||
shouldPreferNativeJiti,
|
||||
} from "../sdk-alias.js";
|
||||
|
||||
type PluginRuntimeRecord = {
|
||||
origin?: string;
|
||||
rootDir?: string;
|
||||
source: string;
|
||||
};
|
||||
|
||||
export function readPluginBoundaryConfigSafely() {
|
||||
try {
|
||||
return loadConfig();
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function resolvePluginRuntimeRecord(
|
||||
pluginId: string,
|
||||
onMissing?: () => never,
|
||||
): PluginRuntimeRecord | null {
|
||||
const manifestRegistry = loadPluginManifestRegistry({
|
||||
config: readPluginBoundaryConfigSafely(),
|
||||
cache: true,
|
||||
});
|
||||
const record = manifestRegistry.plugins.find((plugin) => plugin.id === pluginId);
|
||||
if (!record?.source) {
|
||||
if (onMissing) {
|
||||
onMissing();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
...(record.origin ? { origin: record.origin } : {}),
|
||||
rootDir: record.rootDir,
|
||||
source: record.source,
|
||||
};
|
||||
}
|
||||
|
||||
export function resolvePluginRuntimeModulePath(
|
||||
record: Pick<PluginRuntimeRecord, "rootDir" | "source">,
|
||||
entryBaseName: string,
|
||||
onMissing?: () => never,
|
||||
): string | null {
|
||||
const candidates = [
|
||||
path.join(path.dirname(record.source), `${entryBaseName}.js`),
|
||||
path.join(path.dirname(record.source), `${entryBaseName}.ts`),
|
||||
...(record.rootDir
|
||||
? [
|
||||
path.join(record.rootDir, `${entryBaseName}.js`),
|
||||
path.join(record.rootDir, `${entryBaseName}.ts`),
|
||||
]
|
||||
: []),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
if (onMissing) {
|
||||
onMissing();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getPluginBoundaryJiti(
|
||||
modulePath: string,
|
||||
loaders: Map<boolean, ReturnType<typeof createJiti>>,
|
||||
) {
|
||||
const tryNative = shouldPreferNativeJiti(modulePath);
|
||||
const cached = loaders.get(tryNative);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const pluginSdkAlias = resolvePluginSdkAliasFile({
|
||||
srcFile: "root-alias.cjs",
|
||||
distFile: "root-alias.cjs",
|
||||
modulePath,
|
||||
});
|
||||
const aliasMap = {
|
||||
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
|
||||
...resolvePluginSdkScopedAliasMap({ modulePath }),
|
||||
};
|
||||
const loader = createJiti(import.meta.url, {
|
||||
...buildPluginLoaderJitiOptions(aliasMap),
|
||||
tryNative,
|
||||
});
|
||||
loaders.set(tryNative, loader);
|
||||
return loader;
|
||||
}
|
||||
|
||||
export function loadPluginBoundaryModuleWithJiti<TModule>(
|
||||
modulePath: string,
|
||||
loaders: Map<boolean, ReturnType<typeof createJiti>>,
|
||||
): TModule {
|
||||
return getPluginBoundaryJiti(modulePath, loaders)(modulePath) as TModule;
|
||||
}
|
||||
@@ -1,21 +1,16 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createJiti } from "jiti";
|
||||
import { resolveWhatsAppHeartbeatRecipients } from "../../channels/plugins/whatsapp-heartbeat.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
getDefaultLocalRoots as getDefaultLocalRootsImpl,
|
||||
loadWebMedia as loadWebMediaImpl,
|
||||
loadWebMediaRaw as loadWebMediaRawImpl,
|
||||
optimizeImageToJpeg as optimizeImageToJpegImpl,
|
||||
} from "../../media/web-media.js";
|
||||
import { loadPluginManifestRegistry } from "../manifest-registry.js";
|
||||
import {
|
||||
buildPluginLoaderJitiOptions,
|
||||
resolvePluginSdkAliasFile,
|
||||
resolvePluginSdkScopedAliasMap,
|
||||
shouldPreferNativeJiti,
|
||||
} from "../sdk-alias.js";
|
||||
loadPluginBoundaryModuleWithJiti,
|
||||
resolvePluginRuntimeModulePath,
|
||||
resolvePluginRuntimeRecord,
|
||||
} from "./runtime-plugin-boundary.js";
|
||||
|
||||
const WHATSAPP_PLUGIN_ID = "whatsapp";
|
||||
|
||||
@@ -35,86 +30,34 @@ let cachedLightModule: WhatsAppLightModule | null = null;
|
||||
|
||||
const jitiLoaders = new Map<boolean, ReturnType<typeof createJiti>>();
|
||||
|
||||
function readConfigSafely() {
|
||||
try {
|
||||
return loadConfig();
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function resolveWhatsAppPluginRecord(): WhatsAppPluginRecord {
|
||||
const manifestRegistry = loadPluginManifestRegistry({
|
||||
config: readConfigSafely(),
|
||||
cache: true,
|
||||
});
|
||||
const record = manifestRegistry.plugins.find((plugin) => plugin.id === WHATSAPP_PLUGIN_ID);
|
||||
if (!record?.source) {
|
||||
return resolvePluginRuntimeRecord(WHATSAPP_PLUGIN_ID, () => {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
origin: record.origin,
|
||||
rootDir: record.rootDir,
|
||||
source: record.source,
|
||||
};
|
||||
}) as WhatsAppPluginRecord;
|
||||
}
|
||||
|
||||
function resolveWhatsAppRuntimeModulePath(
|
||||
record: WhatsAppPluginRecord,
|
||||
entryBaseName: "light-runtime-api" | "runtime-api",
|
||||
): string {
|
||||
const candidates = [
|
||||
path.join(path.dirname(record.source), `${entryBaseName}.js`),
|
||||
path.join(path.dirname(record.source), `${entryBaseName}.ts`),
|
||||
...(record.rootDir
|
||||
? [
|
||||
path.join(record.rootDir, `${entryBaseName}.js`),
|
||||
path.join(record.rootDir, `${entryBaseName}.ts`),
|
||||
]
|
||||
: []),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
}
|
||||
|
||||
function getJiti(modulePath: string) {
|
||||
const tryNative = shouldPreferNativeJiti(modulePath);
|
||||
const cached = jitiLoaders.get(tryNative);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const pluginSdkAlias = resolvePluginSdkAliasFile({
|
||||
srcFile: "root-alias.cjs",
|
||||
distFile: "root-alias.cjs",
|
||||
modulePath: modulePath,
|
||||
const modulePath = resolvePluginRuntimeModulePath(record, entryBaseName, () => {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
});
|
||||
const aliasMap = {
|
||||
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
|
||||
...resolvePluginSdkScopedAliasMap({ modulePath }),
|
||||
};
|
||||
const loader = createJiti(import.meta.url, {
|
||||
...buildPluginLoaderJitiOptions(aliasMap),
|
||||
tryNative,
|
||||
});
|
||||
jitiLoaders.set(tryNative, loader);
|
||||
return loader;
|
||||
}
|
||||
|
||||
function loadWithJiti<TModule>(modulePath: string): TModule {
|
||||
return getJiti(modulePath)(modulePath) as TModule;
|
||||
if (!modulePath) {
|
||||
throw new Error(
|
||||
`WhatsApp plugin runtime is unavailable: missing ${entryBaseName} for plugin '${WHATSAPP_PLUGIN_ID}'`,
|
||||
);
|
||||
}
|
||||
return modulePath;
|
||||
}
|
||||
|
||||
function loadCurrentHeavyModuleSync(): WhatsAppHeavyModule {
|
||||
const modulePath = resolveWhatsAppRuntimeModulePath(resolveWhatsAppPluginRecord(), "runtime-api");
|
||||
return loadWithJiti<WhatsAppHeavyModule>(modulePath);
|
||||
return loadPluginBoundaryModuleWithJiti<WhatsAppHeavyModule>(modulePath, jitiLoaders);
|
||||
}
|
||||
|
||||
function loadWhatsAppLightModule(): WhatsAppLightModule {
|
||||
@@ -125,7 +68,7 @@ function loadWhatsAppLightModule(): WhatsAppLightModule {
|
||||
if (cachedLightModule && cachedLightModulePath === modulePath) {
|
||||
return cachedLightModule;
|
||||
}
|
||||
const loaded = loadWithJiti<WhatsAppLightModule>(modulePath);
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WhatsAppLightModule>(modulePath, jitiLoaders);
|
||||
cachedLightModulePath = modulePath;
|
||||
cachedLightModule = loaded;
|
||||
return loaded;
|
||||
@@ -137,7 +80,7 @@ async function loadWhatsAppHeavyModule(): Promise<WhatsAppHeavyModule> {
|
||||
if (cachedHeavyModule && cachedHeavyModulePath === modulePath) {
|
||||
return cachedHeavyModule;
|
||||
}
|
||||
const loaded = loadWithJiti<WhatsAppHeavyModule>(modulePath);
|
||||
const loaded = loadPluginBoundaryModuleWithJiti<WhatsAppHeavyModule>(modulePath, jitiLoaders);
|
||||
cachedHeavyModulePath = modulePath;
|
||||
cachedHeavyModule = loaded;
|
||||
return loaded;
|
||||
|
||||
Reference in New Issue
Block a user