fix: stabilize plugin-sdk test loading

This commit is contained in:
Peter Steinberger
2026-03-27 13:14:11 +00:00
parent 9d10a2e242
commit b5a8d5a230
10 changed files with 524 additions and 104 deletions

View File

@@ -23,6 +23,7 @@ export function createDetectedBinaryStatus(params: {
detectBinary?: (path: string) => Promise<boolean>;
}): ChannelSetupWizardStatus {
const detectBinary = params.detectBinary ?? defaultDetectBinary;
return {
configuredLabel: params.configuredLabel,
unconfiguredLabel: params.unconfiguredLabel,
@@ -31,7 +32,7 @@ export function createDetectedBinaryStatus(params: {
configuredScore: params.configuredScore,
unconfiguredScore: params.unconfiguredScore,
resolveConfigured: params.resolveConfigured,
resolveStatusLines: async ({ cfg, configured }: SetupStatusParams) => {
async resolveStatusLines({ cfg, configured }: SetupStatusParams): Promise<string[]> {
const binaryPath = params.resolveBinaryPath({ cfg });
const detected = await detectBinary(binaryPath);
return [
@@ -39,14 +40,26 @@ export function createDetectedBinaryStatus(params: {
`${params.binaryLabel}: ${detected ? "found" : "missing"} (${binaryPath})`,
];
},
resolveSelectionHint: async ({ cfg }) =>
(await detectBinary(params.resolveBinaryPath({ cfg })))
async resolveSelectionHint({
cfg,
}: {
cfg: OpenClawConfig;
configured: boolean;
}): Promise<string | undefined> {
return (await detectBinary(params.resolveBinaryPath({ cfg })))
? params.configuredHint
: params.unconfiguredHint,
resolveQuickstartScore: async ({ cfg }) =>
(await detectBinary(params.resolveBinaryPath({ cfg })))
: params.unconfiguredHint;
},
async resolveQuickstartScore({
cfg,
}: {
cfg: OpenClawConfig;
configured: boolean;
}): Promise<number | undefined> {
return (await detectBinary(params.resolveBinaryPath({ cfg })))
? params.configuredScore
: params.unconfiguredScore,
: params.unconfiguredScore;
},
};
}
@@ -78,12 +91,15 @@ export function createDelegatedSetupWizardStatusResolvers(
"resolveStatusLines" | "resolveSelectionHint" | "resolveQuickstartScore"
> {
return {
resolveStatusLines: async (params) =>
(await loadWizard()).status.resolveStatusLines?.(params) ?? [],
resolveSelectionHint: async (params) =>
await (await loadWizard()).status.resolveSelectionHint?.(params),
resolveQuickstartScore: async (params) =>
await (await loadWizard()).status.resolveQuickstartScore?.(params),
async resolveStatusLines(params) {
return (await loadWizard()).status.resolveStatusLines?.(params) ?? [];
},
async resolveSelectionHint(params) {
return await (await loadWizard()).status.resolveSelectionHint?.(params);
},
async resolveQuickstartScore(params) {
return await (await loadWizard()).status.resolveQuickstartScore?.(params);
},
};
}

View File

@@ -0,0 +1,67 @@
import type { OpenClawConfig } from "../../../config/config.js";
import type { SecretInput } from "../../../config/types.secrets.js";
import { applyLitellmConfig } from "../../../plugin-sdk/litellm.js";
import { applyAuthProfileConfig } from "../../../plugins/provider-auth-helpers.js";
import { setLitellmApiKey } from "../../../plugins/provider-auth-storage.js";
import type { RuntimeEnv } from "../../../runtime.js";
import type { AuthChoice, OnboardOptions } from "../../onboard-types.js";
type ApiKeyStorageOptions = {
secretInputMode: "plaintext" | "ref";
};
type ResolvedNonInteractiveApiKey = {
key: string;
source: "profile" | "env" | "flag";
};
export async function applySimpleNonInteractiveApiKeyChoice(params: {
authChoice: AuthChoice;
nextConfig: OpenClawConfig;
baseConfig: OpenClawConfig;
opts: OnboardOptions;
runtime: RuntimeEnv;
apiKeyStorageOptions?: ApiKeyStorageOptions;
resolveApiKey: (input: {
provider: string;
cfg: OpenClawConfig;
flagValue?: string;
flagName: `--${string}`;
envVar: string;
runtime: RuntimeEnv;
}) => Promise<ResolvedNonInteractiveApiKey | null>;
maybeSetResolvedApiKey: (
resolved: ResolvedNonInteractiveApiKey,
setter: (value: SecretInput) => Promise<void> | void,
) => Promise<boolean>;
}): Promise<OpenClawConfig | null | undefined> {
if (params.authChoice !== "litellm-api-key") {
return undefined;
}
const resolved = await params.resolveApiKey({
provider: "litellm",
cfg: params.baseConfig,
flagValue: params.opts.litellmApiKey,
flagName: "--litellm-api-key",
envVar: "LITELLM_API_KEY",
runtime: params.runtime,
});
if (!resolved) {
return null;
}
if (
!(await params.maybeSetResolvedApiKey(resolved, (value) =>
setLitellmApiKey(value, undefined, params.apiKeyStorageOptions),
))
) {
return null;
}
return applyLitellmConfig(
applyAuthProfileConfig(params.nextConfig, {
profileId: "litellm:default",
provider: "litellm",
mode: "api_key",
}),
);
}

View File

@@ -307,103 +307,345 @@ function logToFile(
export function createSubsystemLogger(subsystem: string): SubsystemLogger {
let fileLogger: TsLogger<LogObj> | null = null;
const getFileLogger = (): TsLogger<LogObj> => {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
return fileLogger;
};
const emit = (level: LogLevel, message: string, meta?: Record<string, unknown>): void => {
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
logToFile(getFileLogger(), level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
const line = formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
});
writeConsoleLine(level, line);
};
const isConsoleEnabled = (level: LogLevel): boolean => {
const consoleSettings = getConsoleSettings();
return (
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem)
);
};
const isFileEnabled = (level: LogLevel): boolean => {
return isFileLogLevelEnabled(level);
};
const logger: SubsystemLogger = {
subsystem,
isEnabled(level, target = "any") {
const isConsoleEnabled =
shouldLogToConsole(level, { level: getConsoleSettings().level }) &&
shouldLogSubsystemToConsole(subsystem);
const isFileEnabled = isFileLogLevelEnabled(level);
if (target === "console") {
return isConsoleEnabled(level);
return isConsoleEnabled;
}
if (target === "file") {
return isFileEnabled(level);
return isFileEnabled;
}
return isConsoleEnabled(level) || isFileEnabled(level);
return isConsoleEnabled || isFileEnabled;
},
trace(message, meta) {
emit("trace", message, meta);
const level: LogLevel = "trace";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
debug(message, meta) {
emit("debug", message, meta);
const level: LogLevel = "debug";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
info(message, meta) {
emit("info", message, meta);
const level: LogLevel = "info";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
warn(message, meta) {
emit("warn", message, meta);
const level: LogLevel = "warn";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
error(message, meta) {
emit("error", message, meta);
const level: LogLevel = "error";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
fatal(message, meta) {
emit("fatal", message, meta);
const level: LogLevel = "fatal";
const consoleSettings = getConsoleSettings();
const consoleEnabled =
shouldLogToConsole(level, { level: consoleSettings.level }) &&
shouldLogSubsystemToConsole(subsystem);
const fileEnabled = isFileLogLevelEnabled(level);
if (!consoleEnabled && !fileEnabled) {
return;
}
let consoleMessageOverride: string | undefined;
let fileMeta = meta;
if (meta && Object.keys(meta).length > 0) {
const { consoleMessage, ...rest } = meta as Record<string, unknown> & {
consoleMessage?: unknown;
};
if (typeof consoleMessage === "string") {
consoleMessageOverride = consoleMessage;
}
fileMeta = Object.keys(rest).length > 0 ? rest : undefined;
}
if (fileEnabled) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, level, message, fileMeta);
}
if (!consoleEnabled) {
return;
}
const consoleMessage = consoleMessageOverride ?? message;
if (
shouldSuppressProbeConsoleLine({
level,
subsystem,
message: consoleMessage,
meta: fileMeta,
})
) {
return;
}
writeConsoleLine(
level,
formatConsoleLine({
level,
subsystem,
message: consoleSettings.style === "json" ? message : consoleMessage,
style: consoleSettings.style,
meta: fileMeta,
}),
);
},
raw(message) {
if (isFileEnabled("info")) {
logToFile(getFileLogger(), "info", message, { raw: true });
if (isFileLogLevelEnabled("info")) {
if (!fileLogger) {
fileLogger = getChildLogger({ subsystem });
}
logToFile(fileLogger, "info", message, { raw: true });
}
if (isConsoleEnabled("info")) {
if (
shouldLogToConsole("info", { level: getConsoleSettings().level }) &&
shouldLogSubsystemToConsole(subsystem)
) {
if (shouldSuppressProbeConsoleLine({ level: "info", subsystem, message })) {
return;
}
@@ -421,16 +663,27 @@ export function runtimeForLogger(
logger: SubsystemLogger,
exit: RuntimeEnv["exit"] = defaultRuntime.exit,
): OutputRuntimeEnv {
const formatArgs = (...args: unknown[]) =>
args
.map((arg) => formatRuntimeArg(arg))
.join(" ")
.trim();
return {
log: (...args: unknown[]) => logger.info(formatArgs(...args)),
error: (...args: unknown[]) => logger.error(formatArgs(...args)),
writeStdout: (value: string) => logger.info(value),
writeJson: (value: unknown, space = 2) => {
log(...args) {
logger.info(
args
.map((arg) => formatRuntimeArg(arg))
.join(" ")
.trim(),
);
},
error(...args) {
logger.error(
args
.map((arg) => formatRuntimeArg(arg))
.join(" ")
.trim(),
);
},
writeStdout(value) {
logger.info(value);
},
writeJson(value: unknown, space = 2) {
logger.info(JSON.stringify(value, null, space > 0 ? space : undefined));
},
exit,

View File

@@ -0,0 +1,8 @@
export {
applyLitellmConfig,
applyLitellmProviderConfig,
buildLitellmModelDefinition,
LITELLM_BASE_URL,
LITELLM_DEFAULT_MODEL_ID,
LITELLM_DEFAULT_MODEL_REF,
} from "../../extensions/litellm/onboard.js";

View File

@@ -1,10 +1,48 @@
import type { OpenClawConfig } from "../config/config.js";
import { emptyPluginConfigSchema } from "../plugins/config-schema.js";
import type {
AnyAgentTool,
MediaUnderstandingProviderPlugin,
OpenClawPluginApi,
OpenClawPluginCommandDefinition,
OpenClawPluginConfigSchema,
OpenClawPluginDefinition,
OpenClawPluginService,
OpenClawPluginServiceContext,
OpenClawPluginToolContext,
OpenClawPluginToolFactory,
PluginInteractiveTelegramHandlerContext,
PluginLogger,
ProviderAugmentModelCatalogContext,
ProviderAuthContext,
ProviderAuthDoctorHintContext,
ProviderAuthMethod,
ProviderAuthMethodNonInteractiveContext,
ProviderAuthResult,
ProviderBuildMissingAuthMessageContext,
ProviderBuildUnknownModelHintContext,
ProviderBuiltInModelSuppressionContext,
ProviderBuiltInModelSuppressionResult,
ProviderCacheTtlEligibilityContext,
ProviderCatalogContext,
ProviderCatalogResult,
ProviderDefaultThinkingPolicyContext,
ProviderDiscoveryContext,
ProviderFetchUsageSnapshotContext,
ProviderModernModelPolicyContext,
ProviderNormalizeResolvedModelContext,
ProviderPrepareDynamicModelContext,
ProviderPrepareExtraParamsContext,
ProviderPrepareRuntimeAuthContext,
ProviderPreparedRuntimeAuth,
ProviderResolvedUsageAuth,
ProviderResolveDynamicModelContext,
ProviderResolveUsageAuthContext,
ProviderRuntimeModel,
ProviderThinkingPolicyContext,
ProviderWrapStreamFnContext,
SpeechProviderPlugin,
PluginCommandContext,
} from "../plugins/types.js";
export type {
@@ -50,8 +88,8 @@ export type {
OpenClawPluginDefinition,
PluginLogger,
PluginInteractiveTelegramHandlerContext,
} from "../plugins/types.js";
export type { OpenClawConfig } from "../config/config.js";
};
export type { OpenClawConfig };
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";

View File

@@ -7,7 +7,12 @@ 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";
const shouldPreferSourceInTests =
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST === "1"
? false
: Boolean(process.env.VITEST) ||
process.env.NODE_ENV === "test" ||
process.env.OPENCLAW_PLUGIN_SDK_SOURCE_IN_TESTS === "1";
function emptyPluginConfigSchema() {
function error(message) {

View File

@@ -1,11 +1,14 @@
import { Command } from "commander";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { registerPluginCliCommands } from "./cli.js";
import { clearPluginLoaderCache } from "./loader.js";
import { clearPluginManifestRegistryCache } from "./manifest-registry.js";
import { resetPluginRuntimeStateForTest } from "./runtime.js";
const previousPreferDistPluginSdk = process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = "1";
function resetPluginState() {
clearPluginLoaderCache();
clearPluginManifestRegistryCache();
@@ -21,6 +24,14 @@ describe("registerPluginCliCommands browser plugin integration", () => {
resetPluginState();
});
afterAll(() => {
if (previousPreferDistPluginSdk === undefined) {
delete process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
} else {
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = previousPreferDistPluginSdk;
}
});
it("registers the browser command from the bundled browser plugin", () => {
const program = new Command();
registerPluginCliCommands(program, {

View File

@@ -1,6 +1,18 @@
import { describe, expect, it } from "vitest";
import { webSearchProviderContractRegistry } from "./registry.js";
import { installWebSearchProviderContractSuite } from "./suites.js";
import { afterAll, describe, expect, it } from "vitest";
const previousPreferDistPluginSdk = process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = "1";
const { webSearchProviderContractRegistry } = await import("./registry.js");
const { installWebSearchProviderContractSuite } = await import("./suites.js");
afterAll(() => {
if (previousPreferDistPluginSdk === undefined) {
delete process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
} else {
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = previousPreferDistPluginSdk;
}
});
describe("web search provider contract registry load", () => {
it("loads bundled web search providers", () => {

View File

@@ -54,6 +54,8 @@ function mkdirSafe(dir: string) {
const fixtureRoot = mkdtempSafe(path.join(os.tmpdir(), "openclaw-plugin-"));
let tempDirIndex = 0;
const prevBundledDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
const prevPreferDistPluginSdk = process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = "1";
const EMPTY_PLUGIN_SCHEMA = { type: "object", additionalProperties: false, properties: {} };
let cachedBundledTelegramDir = "";
let cachedBundledMemoryDir = "";
@@ -721,6 +723,11 @@ afterAll(() => {
} finally {
cachedBundledTelegramDir = "";
cachedBundledMemoryDir = "";
if (prevPreferDistPluginSdk === undefined) {
delete process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST;
} else {
process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST = prevPreferDistPluginSdk;
}
}
});

View File

@@ -161,7 +161,10 @@ export function resolvePluginSdkAliasCandidateOrder(params: {
}): PluginSdkAliasCandidateKind[] {
const normalizedModulePath = params.modulePath.replace(/\\/g, "/");
const isDistRuntime = normalizedModulePath.includes("/dist/");
return isDistRuntime || params.isProduction ? ["dist", "src"] : ["src", "dist"];
const preferDistInTests = process.env.OPENCLAW_PLUGIN_SDK_PREFER_DIST === "1";
return isDistRuntime || params.isProduction || preferDistInTests
? ["dist", "src"]
: ["src", "dist"];
}
export function listPluginSdkAliasCandidates(params: {