fix(discord): avoid blocking startup on probe (#77129)

* fix(discord): avoid blocking startup on probe

* fix(discord): clear degraded probe status

* test(plugin-sdk): isolate jiti loader override

* test(plugin-sdk): fix circular facade fixture path

* fix(plugins): preserve sdk aliases for native loads

* fix(plugins): route sdk alias loads through transform
This commit is contained in:
Peter Steinberger
2026-05-04 07:41:42 +01:00
committed by GitHub
parent fa689295c6
commit 605e89468e
8 changed files with 211 additions and 54 deletions

View File

@@ -99,7 +99,6 @@ import {
restoreMemoryPluginState,
} from "./memory-state.js";
import { unwrapDefaultModuleExport } from "./module-export.js";
import { tryNativeRequireJavaScriptModule } from "./native-module-require.js";
import {
fingerprintPluginDiscoveryContext,
resolvePluginDiscoveryContext,
@@ -107,7 +106,7 @@ import {
import { withProfile } from "./plugin-load-profile.js";
import {
createPluginModuleLoaderCache,
getCachedPluginSourceModuleLoader,
getCachedPluginModuleLoader,
type PluginModuleLoaderCache,
} from "./plugin-module-loader-cache.js";
import type { PluginOrigin } from "./plugin-origin.types.js";
@@ -480,8 +479,8 @@ function runPluginRegisterSync(
function createPluginModuleLoader(options: Pick<PluginLoadOptions, "pluginSdkResolution">) {
const moduleLoaders: PluginModuleLoaderCache = createPluginModuleLoaderCache();
const loadSourceModule = (modulePath: string) => {
return getCachedPluginSourceModuleLoader({
const createLoaderForModule = (modulePath: string) => {
return getCachedPluginModuleLoader({
cache: moduleLoaders,
modulePath,
importerUrl: import.meta.url,
@@ -495,18 +494,8 @@ function createPluginModuleLoader(options: Pick<PluginLoadOptions, "pluginSdkRes
pluginSdkResolution: options.pluginSdkResolution,
});
};
return (modulePath: string): unknown => {
if (shouldPreferNativeModuleLoad(modulePath)) {
const native = tryNativeRequireJavaScriptModule(modulePath, { allowWindows: true });
if (native.ok) {
return native.moduleExport;
}
}
// Source .ts runtime shims import sibling ".js" specifiers that only exist
// after build. Jiti remains the dev/source fallback because it rewrites those
// imports against the source graph and applies SDK aliases.
return loadSourceModule(modulePath)(toSafeImportPath(modulePath));
};
return (modulePath: string): unknown =>
createLoaderForModule(modulePath)(toSafeImportPath(modulePath));
}
function resolveCanonicalDistRuntimeSource(source: string): string {

View File

@@ -1,3 +1,4 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import type { createJiti } from "jiti";
import { toSafeImportPath } from "../shared/import-specifier.js";
@@ -47,6 +48,8 @@ export type PluginModuleLoaderStatsSnapshot = {
const DEFAULT_PLUGIN_MODULE_LOADER_CACHE_ENTRIES = 128;
const MAX_TRACKED_SOURCE_TRANSFORM_TARGETS = 24;
const JITI_FACTORY_OVERRIDE_KEY = Symbol.for("openclaw.pluginModuleLoaderJitiFactoryOverride");
const PLUGIN_SDK_IMPORT_SPECIFIER_PATTERN =
/(?:\bfrom\s*["']|\bimport\s*\(\s*["']|\brequire\s*\(\s*["'])(?:openclaw|@openclaw)\/plugin-sdk(?:\/[^"']*)?["']/u;
const requireForJiti = createRequire(import.meta.url);
let createJitiLoaderFactory: PluginModuleLoaderFactory | undefined;
const pluginModuleLoaderStats = {
@@ -213,6 +216,29 @@ function createLazySourceTransformLoader(params: {
};
}
function shouldForceSourceTransformForPluginSdkAlias(params: {
target: string;
aliasMap: Record<string, string>;
}): boolean {
if (
!params.aliasMap["openclaw/plugin-sdk"] &&
!params.aliasMap["@openclaw/plugin-sdk"] &&
!Object.keys(params.aliasMap).some(
(key) => key.startsWith("openclaw/plugin-sdk/") || key.startsWith("@openclaw/plugin-sdk/"),
)
) {
return false;
}
if (!/\.[cm]?js$/iu.test(params.target)) {
return false;
}
try {
return PLUGIN_SDK_IMPORT_SPECIFIER_PATTERN.test(fs.readFileSync(params.target, "utf-8"));
} catch {
return false;
}
}
function createPluginModuleLoader(params: {
loaderFilename: string;
aliasMap: Record<string, string>;
@@ -242,8 +268,20 @@ function createPluginModuleLoader(params: {
// for TS / TSX sources and for the small set of require(esm) /
// async-module fallbacks `tryNativeRequireJavaScriptModule` declines to
// handle.
const getLoadWithAliasTransform = createLazySourceTransformLoader({
...params,
tryNative: false,
});
return ((target: string, ...rest: unknown[]) => {
pluginModuleLoaderStats.calls += 1;
if (shouldForceSourceTransformForPluginSdkAlias({ target, aliasMap: params.aliasMap })) {
pluginModuleLoaderStats.sourceTransformForced += 1;
recordSourceTransformTarget(target);
return (getLoadWithAliasTransform() as (t: string, ...a: unknown[]) => unknown)(
target,
...rest,
);
}
const native = tryNativeRequireJavaScriptModule(target, { allowWindows: true });
if (native.ok) {
pluginModuleLoaderStats.nativeHits += 1;

View File

@@ -10,7 +10,8 @@ function writeRuntimeModuleWrapper(sourcePath: string, targetPath: string): void
const relative = `./${path.relative(path.dirname(targetPath), sourcePath).split(path.sep).join("/")}`;
const content = [
`export * from ${JSON.stringify(relative)};`,
`export { default } from ${JSON.stringify(relative)};`,
`import * as moduleExports from ${JSON.stringify(relative)};`,
`export default moduleExports.default ?? moduleExports;`,
"",
].join("\n");
try {