perf: speed up security audit test imports

This commit is contained in:
Peter Steinberger
2026-04-16 21:54:01 +01:00
parent cd45f53b4e
commit 8a37bb4ed6
19 changed files with 321 additions and 37 deletions

View File

@@ -18,9 +18,13 @@ type BundledChannelEntryRuntimeContract = {
id: string;
name: string;
description: string;
features?: {
accountInspect?: boolean;
};
register: (api: unknown) => void;
loadChannelPlugin: () => ChannelPlugin;
loadChannelSecrets?: () => ChannelPlugin["secrets"] | undefined;
loadChannelAccountInspector?: () => NonNullable<ChannelPlugin["config"]["inspectAccount"]>;
setChannelRuntime?: (runtime: PluginRuntime) => void;
};
@@ -49,6 +53,10 @@ type BundledChannelCacheContext = {
lazySetupPluginsById: Map<ChannelId, ChannelPlugin>;
lazySecretsById: Map<ChannelId, ChannelPlugin["secrets"] | null>;
lazySetupSecretsById: Map<ChannelId, ChannelPlugin["secrets"] | null>;
lazyAccountInspectorsById: Map<
ChannelId,
NonNullable<ChannelPlugin["config"]["inspectAccount"]> | null
>;
};
const log = createSubsystemLogger("channels");
@@ -100,6 +108,13 @@ function hasSetupEntryFeature(
return entry?.features?.[feature] === true;
}
function hasChannelEntryFeature(
entry: BundledChannelEntryRuntimeContract | undefined,
feature: keyof NonNullable<BundledChannelEntryRuntimeContract["features"]>,
): boolean {
return entry?.features?.[feature] === true;
}
function resolveBundledChannelBoundaryRoot(params: {
packageRoot: string;
pluginsDir?: string;
@@ -222,6 +237,7 @@ function createBundledChannelCacheContext(): BundledChannelCacheContext {
lazySetupPluginsById: new Map(),
lazySecretsById: new Map(),
lazySetupSecretsById: new Map(),
lazyAccountInspectorsById: new Map(),
};
}
@@ -368,6 +384,24 @@ function getBundledChannelSecretsForRoot(
return secrets;
}
function getBundledChannelAccountInspectorForRoot(
id: ChannelId,
rootScope: BundledChannelRootScope,
cacheContext: BundledChannelCacheContext,
): NonNullable<ChannelPlugin["config"]["inspectAccount"]> | undefined {
if (cacheContext.lazyAccountInspectorsById.has(id)) {
return cacheContext.lazyAccountInspectorsById.get(id) ?? undefined;
}
const entry = getLazyGeneratedBundledChannelEntryForRoot(id, rootScope, cacheContext)?.entry;
if (!entry?.loadChannelAccountInspector) {
cacheContext.lazyAccountInspectorsById.set(id, null);
return undefined;
}
const inspector = entry.loadChannelAccountInspector();
cacheContext.lazyAccountInspectorsById.set(id, inspector);
return inspector;
}
function getBundledChannelSetupPluginForRoot(
id: ChannelId,
rootScope: BundledChannelRootScope,
@@ -449,6 +483,22 @@ export function listBundledChannelSetupPluginsByFeature(
});
}
export function hasBundledChannelEntryFeature(
id: ChannelId,
feature: keyof NonNullable<BundledChannelEntryRuntimeContract["features"]>,
): boolean {
const { rootScope, cacheContext } = resolveActiveBundledChannelCacheScope();
const entry = getLazyGeneratedBundledChannelEntryForRoot(id, rootScope, cacheContext)?.entry;
return hasChannelEntryFeature(entry, feature);
}
export function getBundledChannelAccountInspector(
id: ChannelId,
): NonNullable<ChannelPlugin["config"]["inspectAccount"]> | undefined {
const { rootScope, cacheContext } = resolveActiveBundledChannelCacheScope();
return getBundledChannelAccountInspectorForRoot(id, rootScope, cacheContext);
}
export function getBundledChannelPlugin(id: ChannelId): ChannelPlugin | undefined {
const { rootScope, cacheContext } = resolveActiveBundledChannelCacheScope();
return getBundledChannelPluginForRoot(id, rootScope, cacheContext);

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { getChannelPlugin } from "./plugins/registry.js";
import { getBundledChannelAccountInspector } from "./plugins/bundled.js";
import { getLoadedChannelPlugin } from "./plugins/registry.js";
import type { ChannelId } from "./plugins/types.public.js";
export type ReadOnlyInspectedAccount = Record<string, unknown>;
@@ -9,7 +10,9 @@ export async function inspectReadOnlyChannelAccount(params: {
cfg: OpenClawConfig;
accountId?: string | null;
}): Promise<ReadOnlyInspectedAccount | null> {
const inspectAccount = getChannelPlugin(params.channelId)?.config.inspectAccount;
const inspectAccount =
getLoadedChannelPlugin(params.channelId)?.config.inspectAccount ??
getBundledChannelAccountInspector(params.channelId);
if (!inspectAccount) {
return null;
}

View File

@@ -36,6 +36,8 @@ type DefineBundledChannelEntryOptions<TPlugin = ChannelPlugin> = {
secrets?: BundledEntryModuleRef;
configSchema?: ChannelEntryConfigSchema<TPlugin> | (() => ChannelEntryConfigSchema<TPlugin>);
runtime?: BundledEntryModuleRef;
accountInspect?: BundledEntryModuleRef;
features?: BundledChannelEntryFeatures;
registerCliMetadata?: (api: OpenClawPluginApi) => void;
registerFull?: (api: OpenClawPluginApi) => void;
};
@@ -53,15 +55,21 @@ export type BundledChannelSetupEntryFeatures = {
legacySessionSurfaces?: boolean;
};
export type BundledChannelEntryFeatures = {
accountInspect?: boolean;
};
export type BundledChannelEntryContract<TPlugin = ChannelPlugin> = {
kind: "bundled-channel-entry";
id: string;
name: string;
description: string;
configSchema: ChannelEntryConfigSchema<TPlugin>;
features?: BundledChannelEntryFeatures;
register: (api: OpenClawPluginApi) => void;
loadChannelPlugin: () => TPlugin;
loadChannelSecrets?: () => ChannelPlugin["secrets"] | undefined;
loadChannelAccountInspector?: () => NonNullable<ChannelPlugin["config"]["inspectAccount"]>;
setChannelRuntime?: (runtime: PluginRuntime) => void;
};
@@ -332,6 +340,8 @@ export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
secrets,
configSchema,
runtime,
accountInspect,
features,
registerCliMetadata,
registerFull,
}: DefineBundledChannelEntryOptions<TPlugin>): BundledChannelEntryContract<TPlugin> {
@@ -343,6 +353,13 @@ export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
const loadChannelSecrets = secrets
? () => loadBundledEntryExportSync<ChannelPlugin["secrets"] | undefined>(importMetaUrl, secrets)
: undefined;
const loadChannelAccountInspector = accountInspect
? () =>
loadBundledEntryExportSync<NonNullable<ChannelPlugin["config"]["inspectAccount"]>>(
importMetaUrl,
accountInspect,
)
: undefined;
const setChannelRuntime = runtime
? (pluginRuntime: PluginRuntime) => {
const setter = loadBundledEntryExportSync<(runtime: PluginRuntime) => void>(
@@ -359,6 +376,9 @@ export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
name,
description,
configSchema: resolvedConfigSchema,
...(features || accountInspect
? { features: { ...features, ...(accountInspect ? { accountInspect: true } : {}) } }
: {}),
register(api: OpenClawPluginApi) {
if (api.registrationMode === "cli-metadata") {
registerCliMetadata?.(api);
@@ -374,6 +394,7 @@ export function defineBundledChannelEntry<TPlugin = ChannelPlugin>({
},
loadChannelPlugin,
...(loadChannelSecrets ? { loadChannelSecrets } : {}),
...(loadChannelAccountInspector ? { loadChannelAccountInspector } : {}),
...(setChannelRuntime ? { setChannelRuntime } : {}),
};
}

View File

@@ -1,7 +1,7 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { fileURLToPath, pathToFileURL } from "node:url";
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
import { resolveBundledPluginsDir } from "../plugins/bundled-dir.js";
import {
@@ -275,6 +275,54 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
});
}
export async function loadBundledPluginPublicSurfaceModule<T extends object>(params: {
dirName: string;
artifactBasename: string;
trackedPluginId?: string | (() => string);
env?: NodeJS.ProcessEnv;
}): Promise<T> {
const location = resolveFacadeModuleLocation(params);
if (!location) {
throw new Error(
`Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
);
}
const cached = loadedFacadeModules.get(location.modulePath);
if (cached) {
return cached as T;
}
const opened = openBoundaryFileSync({
absolutePath: location.modulePath,
rootPath: location.boundaryRoot,
boundaryLabel:
location.boundaryRoot === getOpenClawPackageRoot() ? "OpenClaw package root" : "plugin root",
rejectHardlinks: false,
});
if (!opened.ok) {
throw new Error(`Unable to open bundled plugin public surface ${location.modulePath}`, {
cause: opened.error,
});
}
fs.closeSync(opened.fd);
try {
const loaded = (await import(pathToFileURL(location.modulePath).href)) as T;
loadedFacadeModules.set(location.modulePath, loaded);
loadedFacadePluginIds.add(
typeof params.trackedPluginId === "function"
? params.trackedPluginId()
: (params.trackedPluginId ?? params.dirName),
);
return loaded;
} catch {
return loadFacadeModuleAtLocationSync({
location,
trackedPluginId: params.trackedPluginId ?? params.dirName,
});
}
}
export function listImportedBundledPluginFacadeIds(): string[] {
return [...loadedFacadePluginIds].toSorted((left, right) => left.localeCompare(right));
}

View File

@@ -3,6 +3,7 @@ type FacadeModule = typeof import("@openclaw/telegram/contract-api.js");
type SecurityAuditFacadeModule = typeof import("@openclaw/telegram/security-audit-contract-api.js");
import {
createLazyFacadeArrayValue,
loadBundledPluginPublicSurfaceModule,
loadBundledPluginPublicSurfaceModuleSync,
} from "./facade-loader.js";
@@ -13,8 +14,8 @@ function loadFacadeModule(): FacadeModule {
});
}
function loadSecurityAuditFacadeModule(): SecurityAuditFacadeModule {
return loadBundledPluginPublicSurfaceModuleSync<SecurityAuditFacadeModule>({
async function loadSecurityAuditFacadeModule(): Promise<SecurityAuditFacadeModule> {
return await loadBundledPluginPublicSurfaceModule<SecurityAuditFacadeModule>({
dirName: "telegram",
artifactBasename: "security-audit-contract-api.js",
});
@@ -31,8 +32,8 @@ export const singleAccountKeysToMove: FacadeModule["singleAccountKeysToMove"] =
createLazyFacadeArrayValue(() => loadFacadeModule().singleAccountKeysToMove);
export const collectTelegramSecurityAuditFindings: FacadeModule["collectTelegramSecurityAuditFindings"] =
((...args) =>
loadSecurityAuditFacadeModule().collectTelegramSecurityAuditFindings(
(async (...args) =>
(await loadSecurityAuditFacadeModule()).collectTelegramSecurityAuditFindings(
...args,
)) as FacadeModule["collectTelegramSecurityAuditFindings"];