

























@@ -2,8 +2,15 @@ import fs from "node:fs";
22import path from "node:path";
33import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
44import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js";
5+import type { OpenClawConfig } from "../config/types.openclaw.js";
56import { setBundledPluginsDirOverrideForTest } from "../plugins/bundled-dir.js";
67import { createPluginActivationSource, normalizePluginsConfig } from "../plugins/config-state.js";
8+import {
9+clearCurrentPluginMetadataSnapshot,
10+setCurrentPluginMetadataSnapshot,
11+} from "../plugins/current-plugin-metadata-snapshot.js";
12+import { resolveInstalledPluginIndexPolicyHash } from "../plugins/installed-plugin-index-policy.js";
13+import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.types.js";
714import {
815evaluateBundledPluginPublicSurfaceAccess,
916resolveBundledPluginPublicSurfaceAccess as resolveActivationCheckBundledPluginPublicSurfaceAccess,
@@ -23,6 +30,7 @@ const originalDisableBundledPlugins = process.env.OPENCLAW_DISABLE_BUNDLED_PLUGI
2330const originalStateDir = process.env.OPENCLAW_STATE_DIR;
2431const trustedBundledFixturesRoot = path.resolve("dist-runtime", "extensions");
2532const trustedBundledFixtureDirs: string[] = [];
33+type SnapshotPluginRecord = PluginMetadataSnapshot["manifestRegistry"]["plugins"][number];
26342735function writeJsonFile(filePath: string, value: unknown): void {
2836fs.mkdirSync(path.dirname(filePath), { recursive: true });
@@ -91,6 +99,7 @@ afterEach(() => {
9199fs.rmSync(dir, { recursive: true, force: true });
92100}
93101clearRuntimeConfigSnapshot();
102+clearCurrentPluginMetadataSnapshot();
94103resetFacadeRuntimeStateForTest();
95104setBundledPluginsDirOverrideForTest(undefined);
96105vi.doUnmock("../plugins/manifest-registry.js");
@@ -148,7 +157,7 @@ describe("plugin-sdk facade runtime", () => {
148157149158expect(resolved?.boundaryRoot).not.toBe(overrideDir);
150159expect(resolved?.modulePath).toMatch(
151-/(?:^|\/)(?:extensions|dist-runtime\/extensions)\/browser\/browser-maintenance\.(?:ts|js)$/u,
160+/(?:^|[\\/])(?:extensions|dist-runtime[\\/]extensions)[\\/]browser[\\/]browser-maintenance\.(?:ts|js)$/u,
152161);
153162});
154163@@ -568,4 +577,141 @@ describe("plugin-sdk facade runtime", () => {
568577pluginId: "demo",
569578});
570579});
580+581+it("validates current snapshot against facade boundary config and ignores on mismatch", () => {
582+const dir = createTempDirSync("openclaw-facade-snapshot-validate-");
583+fs.mkdirSync(path.join(dir, "demo"), { recursive: true });
584+fs.writeFileSync(
585+path.join(dir, "demo", "runtime-api.js"),
586+'export const marker = "snapshot-validate";\n',
587+"utf8",
588+);
589+// Do NOT write openclaw.plugin.json on disk to force fallback to registry scan
590+useBundledPluginDirOverrideForTest(dir);
591+592+function createTestSnapshot(
593+params: {
594+config?: OpenClawConfig;
595+plugins?: SnapshotPluginRecord[];
596+} = {},
597+): PluginMetadataSnapshot {
598+const policyHash = resolveInstalledPluginIndexPolicyHash(params.config);
599+return {
600+ policyHash,
601+index: {
602+version: 1,
603+hostContractVersion: "test",
604+compatRegistryVersion: "test",
605+migrationVersion: 1,
606+ policyHash,
607+generatedAtMs: 1,
608+installRecords: {},
609+plugins: [],
610+diagnostics: [],
611+},
612+registryDiagnostics: [],
613+manifestRegistry: { plugins: params.plugins ?? [], diagnostics: [] },
614+plugins: [],
615+diagnostics: [],
616+byPluginId: new Map(),
617+normalizePluginId: (pluginId) => pluginId,
618+owners: {
619+channels: new Map(),
620+channelConfigs: new Map(),
621+providers: new Map(),
622+modelCatalogProviders: new Map(),
623+cliBackends: new Map(),
624+setupProviders: new Map(),
625+commandAliases: new Map(),
626+contracts: new Map(),
627+},
628+metrics: {
629+registrySnapshotMs: 0,
630+manifestRegistryMs: 0,
631+ownerMapsMs: 0,
632+totalMs: 0,
633+indexPluginCount: 0,
634+manifestPluginCount: 0,
635+},
636+};
637+}
638+639+const configWithPaths = {
640+plugins: {
641+load: { paths: ["/path/one"] },
642+entries: {
643+"demo-snapshot": { enabled: true },
644+demo: { enabled: true },
645+},
646+},
647+} satisfies OpenClawConfig;
648+const matchedSnapshot = createTestSnapshot({
649+config: configWithPaths,
650+plugins: [
651+{
652+id: "demo-snapshot",
653+rootDir: path.join(dir, "demo"),
654+source: path.join(dir, "demo", "runtime-api.js"),
655+manifestPath: path.join(dir, "demo", "openclaw.plugin.json"),
656+channels: ["demo"],
657+providers: [],
658+cliBackends: [],
659+skills: [],
660+hooks: [],
661+origin: "bundled" as const,
662+},
663+],
664+});
665+666+setCurrentPluginMetadataSnapshot(matchedSnapshot, { config: configWithPaths });
667+668+setRuntimeConfigSnapshot(
669+{
670+plugins: {
671+load: { paths: ["/path/two"] },
672+entries: {
673+"demo-snapshot": { enabled: true },
674+demo: { enabled: true },
675+},
676+},
677+},
678+{
679+plugins: {
680+load: { paths: ["/path/two"] },
681+entries: {
682+"demo-snapshot": { enabled: true },
683+demo: { enabled: true },
684+},
685+},
686+},
687+);
688+689+expect(
690+resolveActivationCheckBundledPluginPublicSurfaceAccess({
691+dirName: "demo",
692+artifactBasename: "runtime-api.js",
693+location: null,
694+sourceExtensionsRoot: dir,
695+resolutionKey: "snapshot-validate-demo",
696+}),
697+).toEqual({
698+allowed: false,
699+reason: "no bundled plugin manifest found for demo",
700+});
701+702+setRuntimeConfigSnapshot(configWithPaths, configWithPaths);
703+704+expect(
705+resolveActivationCheckBundledPluginPublicSurfaceAccess({
706+dirName: "demo",
707+artifactBasename: "runtime-api.js",
708+location: null,
709+sourceExtensionsRoot: dir,
710+resolutionKey: "snapshot-validate-demo",
711+}),
712+).toEqual({
713+allowed: true,
714+pluginId: "demo-snapshot",
715+});
716+});
571717});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。