mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-17 04:01:05 +00:00
perf(test): trim bundled facade hot paths
This commit is contained in:
@@ -2,22 +2,21 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
|
||||
import { createPluginActivationSource, normalizePluginsConfig } from "../plugins/config-state.js";
|
||||
import { clearPluginDiscoveryCache } from "../plugins/discovery.js";
|
||||
import { clearPluginManifestRegistryCache } from "../plugins/manifest-registry.js";
|
||||
import {
|
||||
__testing,
|
||||
canLoadActivatedBundledPluginPublicSurface,
|
||||
listImportedBundledPluginFacadeIds,
|
||||
loadActivatedBundledPluginPublicSurfaceModuleSync,
|
||||
loadBundledPluginPublicSurfaceModuleSync,
|
||||
resetFacadeRuntimeStateForTest,
|
||||
tryLoadActivatedBundledPluginPublicSurfaceModuleSync,
|
||||
} from "./facade-runtime.js";
|
||||
import { createPluginSdkTestHarness } from "./test-helpers.js";
|
||||
|
||||
const { createTempDirSync } = createPluginSdkTestHarness();
|
||||
const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
const originalStateDir = process.env.OPENCLAW_STATE_DIR;
|
||||
const FACADE_RUNTIME_GLOBAL = "__openclawTestLoadBundledPluginPublicSurfaceModuleSync";
|
||||
|
||||
function createBundledPluginDir(prefix: string, marker: string): string {
|
||||
const rootDir = createTempDirSync(prefix);
|
||||
@@ -41,36 +40,6 @@ function createThrowingPluginDir(prefix: string): string {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
function createCircularPluginDir(prefix: string): string {
|
||||
const rootDir = createTempDirSync(prefix);
|
||||
fs.mkdirSync(path.join(rootDir, "demo"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "facade.mjs"),
|
||||
[
|
||||
`const loadBundledPluginPublicSurfaceModuleSync = globalThis.${FACADE_RUNTIME_GLOBAL};`,
|
||||
`if (typeof loadBundledPluginPublicSurfaceModuleSync !== "function") {`,
|
||||
' throw new Error("missing facade runtime test loader");',
|
||||
"}",
|
||||
`export const marker = loadBundledPluginPublicSurfaceModuleSync({ dirName: "demo", artifactBasename: "api.js" }).marker;`,
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "demo", "helper.js"),
|
||||
['import { marker } from "../facade.mjs";', "export const circularMarker = marker;", ""].join(
|
||||
"\n",
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "demo", "api.js"),
|
||||
['import "./helper.js";', 'export const marker = "circular-ok";', ""].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
clearRuntimeConfigSnapshot();
|
||||
@@ -78,7 +47,6 @@ afterEach(() => {
|
||||
clearPluginDiscoveryCache();
|
||||
clearPluginManifestRegistryCache();
|
||||
vi.doUnmock("../plugins/manifest-registry.js");
|
||||
delete (globalThis as typeof globalThis & Record<string, unknown>)[FACADE_RUNTIME_GLOBAL];
|
||||
if (originalBundledPluginsDir === undefined) {
|
||||
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
|
||||
} else {
|
||||
@@ -97,106 +65,107 @@ describe("plugin-sdk facade runtime", () => {
|
||||
const overrideB = createBundledPluginDir("openclaw-facade-runtime-b-", "override-b");
|
||||
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = overrideA;
|
||||
const fromA = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
const fromA = __testing.resolveFacadeModuleLocation({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
expect(fromA.marker).toBe("override-a");
|
||||
expect(fromA).toEqual({
|
||||
modulePath: path.join(overrideA, "demo", "api.js"),
|
||||
boundaryRoot: overrideA,
|
||||
});
|
||||
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = overrideB;
|
||||
const fromB = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
const fromB = __testing.resolveFacadeModuleLocation({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
expect(fromB.marker).toBe("override-b");
|
||||
expect(fromB).toEqual({
|
||||
modulePath: path.join(overrideB, "demo", "api.js"),
|
||||
boundaryRoot: overrideB,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns the same object identity on repeated calls (sentinel consistency)", () => {
|
||||
const dir = createBundledPluginDir("openclaw-facade-identity-", "identity-check");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = dir;
|
||||
const location = {
|
||||
modulePath: path.join(dir, "demo", "api.js"),
|
||||
boundaryRoot: dir,
|
||||
};
|
||||
const loader = vi.fn(() => ({ marker: "identity-check" }));
|
||||
|
||||
const first = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
const first = __testing.loadFacadeModuleAtLocationSync<{ marker: string }>({
|
||||
location,
|
||||
trackedPluginId: "demo",
|
||||
loadModule: loader,
|
||||
});
|
||||
const second = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
const second = __testing.loadFacadeModuleAtLocationSync<{ marker: string }>({
|
||||
location,
|
||||
trackedPluginId: "demo",
|
||||
loadModule: loader,
|
||||
});
|
||||
expect(first).toBe(second);
|
||||
expect(first.marker).toBe("identity-check");
|
||||
expect(listImportedBundledPluginFacadeIds()).toEqual(["demo"]);
|
||||
expect(loader).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("breaks circular facade re-entry during module evaluation", () => {
|
||||
const dir = createCircularPluginDir("openclaw-facade-circular-");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = dir;
|
||||
(globalThis as typeof globalThis & Record<string, unknown>)[FACADE_RUNTIME_GLOBAL] =
|
||||
loadBundledPluginPublicSurfaceModuleSync;
|
||||
const dir = createBundledPluginDir("openclaw-facade-circular-", "circular-ok");
|
||||
const location = {
|
||||
modulePath: path.join(dir, "demo", "api.js"),
|
||||
boundaryRoot: dir,
|
||||
};
|
||||
let reentered: { marker?: string } | undefined;
|
||||
const loader = vi.fn(() => {
|
||||
reentered = __testing.loadFacadeModuleAtLocationSync<{ marker?: string }>({
|
||||
location,
|
||||
trackedPluginId: "demo",
|
||||
loadModule: loader,
|
||||
});
|
||||
return { marker: "circular-ok" };
|
||||
});
|
||||
|
||||
const loaded = loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
const loaded = __testing.loadFacadeModuleAtLocationSync<{ marker: string }>({
|
||||
location,
|
||||
trackedPluginId: "demo",
|
||||
loadModule: loader,
|
||||
});
|
||||
|
||||
expect(loaded.marker).toBe("circular-ok");
|
||||
expect(reentered).toBe(loaded);
|
||||
expect(reentered?.marker).toBe("circular-ok");
|
||||
expect(loader).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("back-fills the sentinel before post-load facade tracking re-enters", async () => {
|
||||
it("back-fills the sentinel before post-load facade tracking re-enters", () => {
|
||||
const dir = createBundledPluginDir("openclaw-facade-post-load-", "post-load-ok");
|
||||
const location = {
|
||||
modulePath: path.join(dir, "demo", "api.js"),
|
||||
boundaryRoot: dir,
|
||||
};
|
||||
const reentryMarkers: Array<string | undefined> = [];
|
||||
const loader = vi.fn(() => ({ marker: "post-load-ok" }));
|
||||
|
||||
vi.resetModules();
|
||||
vi.doMock("../plugins/manifest-registry.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../plugins/manifest-registry.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadPluginManifestRegistry: vi.fn(() => {
|
||||
const load = (
|
||||
globalThis as typeof globalThis & {
|
||||
[FACADE_RUNTIME_GLOBAL]?: typeof loadBundledPluginPublicSurfaceModuleSync;
|
||||
}
|
||||
)[FACADE_RUNTIME_GLOBAL];
|
||||
if (typeof load !== "function") {
|
||||
throw new Error("missing facade runtime test loader");
|
||||
}
|
||||
const reentered = load<{ marker?: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
});
|
||||
reentryMarkers.push(reentered.marker);
|
||||
return {
|
||||
plugins: [
|
||||
{
|
||||
id: "demo",
|
||||
rootDir: path.join(dir, "demo"),
|
||||
origin: "bundled",
|
||||
channels: ["demo"],
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const facadeRuntime = await import("./facade-runtime.js");
|
||||
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = dir;
|
||||
(globalThis as typeof globalThis & Record<string, unknown>)[FACADE_RUNTIME_GLOBAL] =
|
||||
facadeRuntime.loadBundledPluginPublicSurfaceModuleSync;
|
||||
|
||||
const loaded = facadeRuntime.loadBundledPluginPublicSurfaceModuleSync<{ marker: string }>({
|
||||
dirName: "demo",
|
||||
artifactBasename: "api.js",
|
||||
const loaded = __testing.loadFacadeModuleAtLocationSync<{ marker: string }>({
|
||||
location,
|
||||
trackedPluginId: () => {
|
||||
const reentered = __testing.loadFacadeModuleAtLocationSync<{ marker?: string }>({
|
||||
location,
|
||||
trackedPluginId: "demo",
|
||||
loadModule: loader,
|
||||
});
|
||||
reentryMarkers.push(reentered.marker);
|
||||
return "demo";
|
||||
},
|
||||
loadModule: loader,
|
||||
});
|
||||
|
||||
expect(loaded.marker).toBe("post-load-ok");
|
||||
expect(reentryMarkers.length).toBeGreaterThan(0);
|
||||
expect(reentryMarkers.every((marker) => marker === "post-load-ok")).toBe(true);
|
||||
expect(facadeRuntime.listImportedBundledPluginFacadeIds()).toEqual(["demo"]);
|
||||
facadeRuntime.resetFacadeRuntimeStateForTest();
|
||||
vi.doUnmock("../plugins/manifest-registry.js");
|
||||
vi.resetModules();
|
||||
expect(listImportedBundledPluginFacadeIds()).toEqual(["demo"]);
|
||||
expect(loader).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it("clears the cache on load failure so retries re-execute", () => {
|
||||
const dir = createThrowingPluginDir("openclaw-facade-throw-");
|
||||
@@ -221,30 +190,48 @@ describe("plugin-sdk facade runtime", () => {
|
||||
});
|
||||
|
||||
it("blocks runtime-api facade loads for bundled plugins that are not activated", () => {
|
||||
setRuntimeConfigSnapshot({});
|
||||
const access = __testing.evaluateBundledPluginPublicSurfaceAccess({
|
||||
params: {
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
},
|
||||
manifestRecord: {
|
||||
id: "discord",
|
||||
origin: "bundled",
|
||||
enabledByDefault: false,
|
||||
rootDir: "/tmp/discord",
|
||||
channels: ["discord"],
|
||||
},
|
||||
config: {},
|
||||
normalizedPluginsConfig: normalizePluginsConfig(),
|
||||
activationSource: createPluginActivationSource({ config: {} }),
|
||||
autoEnabledReasons: {},
|
||||
});
|
||||
|
||||
expect(
|
||||
canLoadActivatedBundledPluginPublicSurface({
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(access.allowed).toBe(false);
|
||||
expect(access.pluginId).toBe("discord");
|
||||
expect(access.reason).toBeTruthy();
|
||||
expect(() =>
|
||||
loadActivatedBundledPluginPublicSurfaceModuleSync({
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
__testing.throwForBundledPluginPublicSurfaceAccess({
|
||||
access,
|
||||
request: {
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
},
|
||||
}),
|
||||
).toThrow(/Bundled plugin public surface access blocked/);
|
||||
expect(
|
||||
tryLoadActivatedBundledPluginPublicSurfaceModuleSync({
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
}),
|
||||
).toBeNull();
|
||||
expect(access.allowed).toBe(false);
|
||||
});
|
||||
|
||||
it("allows runtime-api facade loads when the bundled plugin is explicitly enabled", () => {
|
||||
setRuntimeConfigSnapshot({
|
||||
const dir = createTempDirSync("openclaw-facade-runtime-enabled-");
|
||||
fs.mkdirSync(path.join(dir, "discord"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "discord", "runtime-api.js"),
|
||||
'export const marker = "runtime-api-enabled";\n',
|
||||
"utf8",
|
||||
);
|
||||
const config = {
|
||||
plugins: {
|
||||
entries: {
|
||||
discord: {
|
||||
@@ -252,14 +239,38 @@ describe("plugin-sdk facade runtime", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
canLoadActivatedBundledPluginPublicSurface({
|
||||
} as const;
|
||||
const access = __testing.evaluateBundledPluginPublicSurfaceAccess({
|
||||
params: {
|
||||
dirName: "discord",
|
||||
artifactBasename: "runtime-api.js",
|
||||
}),
|
||||
).toBe(true);
|
||||
},
|
||||
manifestRecord: {
|
||||
id: "discord",
|
||||
origin: "bundled",
|
||||
enabledByDefault: false,
|
||||
rootDir: "/tmp/discord",
|
||||
channels: ["discord"],
|
||||
},
|
||||
config,
|
||||
normalizedPluginsConfig: normalizePluginsConfig(config.plugins),
|
||||
activationSource: createPluginActivationSource({ config }),
|
||||
autoEnabledReasons: {},
|
||||
});
|
||||
const loader = vi.fn(() => ({ marker: "runtime-api-enabled" }));
|
||||
const location = {
|
||||
modulePath: path.join(dir, "discord", "runtime-api.js"),
|
||||
boundaryRoot: dir,
|
||||
};
|
||||
|
||||
expect(access.allowed).toBe(true);
|
||||
const loaded = __testing.loadFacadeModuleAtLocationSync<{ marker: string }>({
|
||||
location,
|
||||
trackedPluginId: "discord",
|
||||
loadModule: loader,
|
||||
});
|
||||
expect(loaded.marker).toBe("runtime-api-enabled");
|
||||
expect(loader).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("resolves a globally-installed plugin whose rootDir basename matches the dirName", () => {
|
||||
|
||||
@@ -463,33 +463,59 @@ function resolveBundledPluginPublicSurfaceAccess(params: {
|
||||
}
|
||||
const { config, normalizedPluginsConfig, activationSource, autoEnabledReasons } =
|
||||
getFacadeBoundaryResolvedConfig();
|
||||
const activationState = resolveEffectivePluginActivationState({
|
||||
id: manifestRecord.id,
|
||||
origin: manifestRecord.origin,
|
||||
config: normalizedPluginsConfig,
|
||||
rootConfig: config,
|
||||
enabledByDefault: manifestRecord.enabledByDefault,
|
||||
const resolved = evaluateBundledPluginPublicSurfaceAccess({
|
||||
params,
|
||||
manifestRecord,
|
||||
config,
|
||||
normalizedPluginsConfig,
|
||||
activationSource,
|
||||
autoEnabledReason: autoEnabledReasons[manifestRecord.id]?.[0],
|
||||
autoEnabledReasons,
|
||||
});
|
||||
if (activationState.enabled) {
|
||||
const resolved = {
|
||||
allowed: true,
|
||||
pluginId: manifestRecord.id,
|
||||
};
|
||||
cachedFacadePublicSurfaceAccessByKey.set(key, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const resolved = {
|
||||
allowed: false,
|
||||
pluginId: manifestRecord.id,
|
||||
reason: activationState.reason ?? "plugin runtime is not activated",
|
||||
};
|
||||
cachedFacadePublicSurfaceAccessByKey.set(key, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function evaluateBundledPluginPublicSurfaceAccess(params: {
|
||||
params: BundledPluginPublicSurfaceParams;
|
||||
manifestRecord: FacadePluginManifestLike;
|
||||
config: OpenClawConfig;
|
||||
normalizedPluginsConfig: ReturnType<typeof normalizePluginsConfig>;
|
||||
activationSource: ReturnType<typeof createPluginActivationSource>;
|
||||
autoEnabledReasons: Record<string, string[]>;
|
||||
}): { allowed: boolean; pluginId?: string; reason?: string } {
|
||||
const activationState = resolveEffectivePluginActivationState({
|
||||
id: params.manifestRecord.id,
|
||||
origin: params.manifestRecord.origin,
|
||||
config: params.normalizedPluginsConfig,
|
||||
rootConfig: params.config,
|
||||
enabledByDefault: params.manifestRecord.enabledByDefault,
|
||||
activationSource: params.activationSource,
|
||||
autoEnabledReason: params.autoEnabledReasons[params.manifestRecord.id]?.[0],
|
||||
});
|
||||
if (activationState.enabled) {
|
||||
return {
|
||||
allowed: true,
|
||||
pluginId: params.manifestRecord.id,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
pluginId: params.manifestRecord.id,
|
||||
reason: activationState.reason ?? "plugin runtime is not activated",
|
||||
};
|
||||
}
|
||||
|
||||
function throwForBundledPluginPublicSurfaceAccess(params: {
|
||||
access: { allowed: boolean; pluginId?: string; reason?: string };
|
||||
request: BundledPluginPublicSurfaceParams;
|
||||
}): never {
|
||||
const pluginLabel = params.access.pluginId ?? params.request.dirName;
|
||||
throw new Error(
|
||||
`Bundled plugin public surface access blocked for "${pluginLabel}" via ${params.request.dirName}/${params.request.artifactBasename}: ${params.access.reason ?? "plugin runtime is not activated"}`,
|
||||
);
|
||||
}
|
||||
|
||||
function createLazyFacadeValueLoader<T>(load: () => T): () => T {
|
||||
let loaded = false;
|
||||
let value: T;
|
||||
@@ -552,16 +578,22 @@ export function createLazyFacadeArrayValue<T extends readonly unknown[]>(load: (
|
||||
return createLazyFacadeProxyValue({ load, target: [] });
|
||||
}
|
||||
|
||||
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
|
||||
type FacadeModuleLocation = {
|
||||
modulePath: string;
|
||||
boundaryRoot: string;
|
||||
};
|
||||
|
||||
type BundledPluginPublicSurfaceParams = {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
};
|
||||
|
||||
function loadFacadeModuleAtLocationSync<T extends object>(params: {
|
||||
location: FacadeModuleLocation;
|
||||
trackedPluginId: string | (() => string);
|
||||
loadModule?: (modulePath: string) => T;
|
||||
}): T {
|
||||
const location = resolveFacadeModuleLocation(params);
|
||||
if (!location) {
|
||||
throw new Error(
|
||||
`Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
|
||||
);
|
||||
}
|
||||
const { location } = params;
|
||||
const cached = loadedFacadeModules.get(location.modulePath);
|
||||
if (cached) {
|
||||
return cached as T;
|
||||
@@ -582,10 +614,9 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
|
||||
rejectHardlinks: false,
|
||||
});
|
||||
if (!opened.ok) {
|
||||
throw new Error(
|
||||
`Unable to open bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
|
||||
{ cause: opened.error },
|
||||
);
|
||||
throw new Error(`Unable to open bundled plugin public surface ${location.modulePath}`, {
|
||||
cause: opened.error,
|
||||
});
|
||||
}
|
||||
fs.closeSync(opened.fd);
|
||||
|
||||
@@ -600,14 +631,20 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
|
||||
|
||||
let loaded: T;
|
||||
try {
|
||||
loaded = getJiti(location.modulePath)(location.modulePath) as T;
|
||||
loaded =
|
||||
params.loadModule?.(location.modulePath) ??
|
||||
(getJiti(location.modulePath)(location.modulePath) as T);
|
||||
// Back-fill the sentinel before resolving plugin ownership. That lookup can
|
||||
// trigger config loading, plugin auto-enable, and other facade reads that
|
||||
// re-enter this loader for the same module path.
|
||||
Object.assign(sentinel, loaded);
|
||||
// Track the owning plugin after the module exports are visible through the
|
||||
// sentinel, so re-entrant callers never observe an empty facade object.
|
||||
loadedFacadePluginIds.add(resolveTrackedFacadePluginId(params));
|
||||
loadedFacadePluginIds.add(
|
||||
typeof params.trackedPluginId === "function"
|
||||
? params.trackedPluginId()
|
||||
: params.trackedPluginId,
|
||||
);
|
||||
} catch (err) {
|
||||
loadedFacadeModules.delete(location.modulePath);
|
||||
throw err;
|
||||
@@ -616,6 +653,34 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
function resolveActivatedBundledPluginPublicSurfaceAccessOrThrow(
|
||||
params: BundledPluginPublicSurfaceParams,
|
||||
) {
|
||||
const access = resolveBundledPluginPublicSurfaceAccess(params);
|
||||
if (!access.allowed) {
|
||||
throwForBundledPluginPublicSurfaceAccess({
|
||||
access,
|
||||
request: params,
|
||||
});
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(
|
||||
params: BundledPluginPublicSurfaceParams,
|
||||
): T {
|
||||
const location = resolveFacadeModuleLocation(params);
|
||||
if (!location) {
|
||||
throw new Error(
|
||||
`Unable to resolve bundled plugin public surface ${params.dirName}/${params.artifactBasename}`,
|
||||
);
|
||||
}
|
||||
return loadFacadeModuleAtLocationSync<T>({
|
||||
location,
|
||||
trackedPluginId: () => resolveTrackedFacadePluginId(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function canLoadActivatedBundledPluginPublicSurface(params: {
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
@@ -627,13 +692,7 @@ export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends obje
|
||||
dirName: string;
|
||||
artifactBasename: string;
|
||||
}): T {
|
||||
const access = resolveBundledPluginPublicSurfaceAccess(params);
|
||||
if (!access.allowed) {
|
||||
const pluginLabel = access.pluginId ?? params.dirName;
|
||||
throw new Error(
|
||||
`Bundled plugin public surface access blocked for "${pluginLabel}" via ${params.dirName}/${params.artifactBasename}: ${access.reason ?? "plugin runtime is not activated"}`,
|
||||
);
|
||||
}
|
||||
resolveActivatedBundledPluginPublicSurfaceAccessOrThrow(params);
|
||||
return loadBundledPluginPublicSurfaceModuleSync<T>(params);
|
||||
}
|
||||
|
||||
@@ -666,3 +725,13 @@ export function resetFacadeRuntimeStateForTest(): void {
|
||||
cachedFacadeManifestRecordsByKey.clear();
|
||||
cachedFacadePublicSurfaceAccessByKey.clear();
|
||||
}
|
||||
|
||||
export const __testing = {
|
||||
evaluateBundledPluginPublicSurfaceAccess,
|
||||
loadFacadeModuleAtLocationSync,
|
||||
throwForBundledPluginPublicSurfaceAccess,
|
||||
resolveActivatedBundledPluginPublicSurfaceAccessOrThrow,
|
||||
resolveFacadeModuleLocation,
|
||||
resolveBundledPluginPublicSurfaceAccess,
|
||||
resolveTrackedFacadePluginId,
|
||||
};
|
||||
|
||||
@@ -2814,38 +2814,6 @@ module.exports = {
|
||||
expectSetupLoaded: true,
|
||||
expectedChannels: 1,
|
||||
},
|
||||
{
|
||||
name: "can prefer setupEntry for configured channel loads during startup",
|
||||
fixture: {
|
||||
id: "setup-runtime-preferred-test",
|
||||
label: "Setup Runtime Preferred Test",
|
||||
packageName: "@openclaw/setup-runtime-preferred-test",
|
||||
fullBlurb: "full entry should be deferred while startup is still cold",
|
||||
setupBlurb: "setup runtime preferred",
|
||||
configured: true,
|
||||
startupDeferConfiguredChannelFullLoadUntilAfterListen: true,
|
||||
},
|
||||
load: ({ pluginDir }: { pluginDir: string }) =>
|
||||
loadOpenClawPlugins({
|
||||
cache: false,
|
||||
preferSetupRuntimeForChannelPlugins: true,
|
||||
config: {
|
||||
channels: {
|
||||
"setup-runtime-preferred-test": {
|
||||
enabled: true,
|
||||
token: "configured",
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
load: { paths: [pluginDir] },
|
||||
allow: ["setup-runtime-preferred-test"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectFullLoaded: false,
|
||||
expectSetupLoaded: true,
|
||||
expectedChannels: 1,
|
||||
},
|
||||
{
|
||||
name: "does not prefer setupEntry for configured channel loads without startup opt-in",
|
||||
fixture: {
|
||||
@@ -2887,6 +2855,26 @@ module.exports = {
|
||||
expect(registry.channels).toHaveLength(expectedChannels);
|
||||
});
|
||||
|
||||
it("prefers setupEntry for configured channel loads during startup when opted in", () => {
|
||||
expect(
|
||||
__testing.shouldLoadChannelPluginInSetupRuntime({
|
||||
manifestChannels: ["setup-runtime-preferred-test"],
|
||||
setupSource: "./setup-entry.cjs",
|
||||
startupDeferConfiguredChannelFullLoadUntilAfterListen: true,
|
||||
cfg: {
|
||||
channels: {
|
||||
"setup-runtime-preferred-test": {
|
||||
enabled: true,
|
||||
token: "configured",
|
||||
},
|
||||
},
|
||||
},
|
||||
env: {},
|
||||
preferSetupRuntimeForChannelPlugins: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("blocks before_prompt_build but preserves legacy model overrides when prompt injection is disabled", async () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
|
||||
@@ -248,6 +248,7 @@ export const __testing = {
|
||||
resolvePluginSdkAliasCandidateOrder,
|
||||
resolvePluginSdkAliasFile,
|
||||
resolvePluginRuntimeModulePath,
|
||||
shouldLoadChannelPluginInSetupRuntime,
|
||||
shouldPreferNativeJiti,
|
||||
toSafeImportPath,
|
||||
getCompatibleActivePluginRegistry,
|
||||
|
||||
Reference in New Issue
Block a user