feat(plugins): surface imported runtime state in status tooling (#59659)

* feat(plugins): surface imported runtime state

* fix(plugins): keep status imports snapshot-only

* fix(plugins): keep status snapshots manifest-only

* fix(plugins): restore doctor load checks

* refactor(plugins): split snapshot and diagnostics reports

* fix(plugins): track imported erroring modules

* fix(plugins): keep hot metadata where required

* fix(plugins): keep hot doctor and write targeting

* fix(plugins): track throwing module imports
This commit is contained in:
Vincent Koc
2026-04-02 22:50:17 +09:00
committed by GitHub
parent 1ecd92af89
commit def5b954a8
18 changed files with 684 additions and 51 deletions

View File

@@ -5,8 +5,10 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
import {
canLoadActivatedBundledPluginPublicSurface,
listImportedBundledPluginFacadeIds,
loadActivatedBundledPluginPublicSurfaceModuleSync,
loadBundledPluginPublicSurfaceModuleSync,
resetFacadeRuntimeStateForTest,
tryLoadActivatedBundledPluginPublicSurfaceModuleSync,
} from "./facade-runtime.js";
@@ -70,6 +72,7 @@ function createCircularPluginDir(prefix: string): string {
afterEach(() => {
vi.restoreAllMocks();
clearRuntimeConfigSnapshot();
resetFacadeRuntimeStateForTest();
if (originalBundledPluginsDir === undefined) {
delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
} else {
@@ -114,6 +117,7 @@ describe("plugin-sdk facade runtime", () => {
});
expect(first).toBe(second);
expect(first.marker).toBe("identity-check");
expect(listImportedBundledPluginFacadeIds()).toEqual(["demo"]);
});
it("breaks circular facade re-entry during module evaluation", () => {

View File

@@ -37,6 +37,7 @@ const ALWAYS_ALLOWED_RUNTIME_DIR_NAMES = new Set([
const EMPTY_FACADE_BOUNDARY_CONFIG: OpenClawConfig = {};
const jitiLoaders = new Map<string, ReturnType<typeof createJiti>>();
const loadedFacadeModules = new Map<string, unknown>();
const loadedFacadePluginIds = new Set<string>();
let cachedBoundaryRawConfig: OpenClawConfig | undefined;
let cachedBoundaryResolvedConfig:
| {
@@ -175,6 +176,10 @@ function resolveBundledPluginManifestRecordByDirName(dirName: string): PluginMan
);
}
function resolveTrackedFacadePluginId(dirName: string): string {
return resolveBundledPluginManifestRecordByDirName(dirName)?.id ?? dirName;
}
function resolveBundledPluginPublicSurfaceAccess(params: {
dirName: string;
artifactBasename: string;
@@ -334,6 +339,7 @@ export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(param
try {
loaded = getJiti(location.modulePath)(location.modulePath) as T;
Object.assign(sentinel, loaded);
loadedFacadePluginIds.add(resolveTrackedFacadePluginId(params.dirName));
} catch (err) {
loadedFacadeModules.delete(location.modulePath);
throw err;
@@ -373,3 +379,15 @@ export function tryLoadActivatedBundledPluginPublicSurfaceModuleSync<T extends o
}
return loadBundledPluginPublicSurfaceModuleSync<T>(params);
}
export function listImportedBundledPluginFacadeIds(): string[] {
return [...loadedFacadePluginIds].toSorted((left, right) => left.localeCompare(right));
}
export function resetFacadeRuntimeStateForTest(): void {
loadedFacadeModules.clear();
loadedFacadePluginIds.clear();
jitiLoaders.clear();
cachedBoundaryRawConfig = undefined;
cachedBoundaryResolvedConfig = undefined;
}