





















@@ -3,28 +3,111 @@ import os from "node:os";
33import path from "node:path";
44import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
55import { afterEach, describe, expect, it, vi } from "vitest";
6+import {
7+clearBundledRuntimeDependencyNodePaths,
8+resolveBundledRuntimeDependencyInstallRoot,
9+} from "./bundled-runtime-deps.js";
610711const tempDirs: string[] = [];
812const originalBundledPluginsDir = process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
13+const originalPluginStageDir = process.env.OPENCLAW_PLUGIN_STAGE_DIR;
9141015function createTempDir(): string {
1116const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-public-surface-loader-"));
1217tempDirs.push(tempDir);
1318return tempDir;
1419}
152021+function createPackagedPublicArtifactWithStagedRuntimeDep(): {
22+bundledPluginsDir: string;
23+pluginRoot: string;
24+stageRoot: string;
25+} {
26+const packageRoot = createTempDir();
27+const pluginRoot = path.join(packageRoot, "dist", "extensions", "demo");
28+const stageRoot = path.join(packageRoot, "stage");
29+fs.mkdirSync(pluginRoot, { recursive: true });
30+fs.writeFileSync(
31+path.join(packageRoot, "package.json"),
32+JSON.stringify({ name: "openclaw", version: "0.0.0", type: "module" }, null, 2),
33+"utf8",
34+);
35+fs.writeFileSync(
36+path.join(pluginRoot, "package.json"),
37+JSON.stringify(
38+{
39+name: "@openclaw/plugin-demo",
40+version: "0.0.0",
41+type: "module",
42+dependencies: {
43+"public-artifact-runtime-dep": "1.0.0",
44+},
45+},
46+null,
47+2,
48+),
49+"utf8",
50+);
51+fs.writeFileSync(
52+path.join(pluginRoot, "provider-policy-api.js"),
53+[
54+'import { marker as depMarker } from "public-artifact-runtime-dep";',
55+"export const marker = `artifact:${depMarker}`;",
56+"",
57+].join("\n"),
58+"utf8",
59+);
60+61+const installRoot = resolveBundledRuntimeDependencyInstallRoot(pluginRoot, {
62+env: {
63+ ...process.env,
64+OPENCLAW_PLUGIN_STAGE_DIR: stageRoot,
65+},
66+});
67+const depRoot = path.join(installRoot, "node_modules", "public-artifact-runtime-dep");
68+fs.mkdirSync(depRoot, { recursive: true });
69+fs.writeFileSync(
70+path.join(depRoot, "package.json"),
71+JSON.stringify(
72+{
73+name: "public-artifact-runtime-dep",
74+version: "1.0.0",
75+type: "module",
76+exports: "./index.js",
77+},
78+null,
79+2,
80+),
81+"utf8",
82+);
83+fs.writeFileSync(path.join(depRoot, "index.js"), 'export const marker = "staged";\n', "utf8");
84+85+return {
86+bundledPluginsDir: path.join(packageRoot, "dist", "extensions"),
87+ pluginRoot,
88+ stageRoot,
89+};
90+}
91+1692afterEach(() => {
1793for (const tempDir of tempDirs.splice(0)) {
1894fs.rmSync(tempDir, { recursive: true, force: true });
1995}
2096vi.restoreAllMocks();
2197vi.resetModules();
2298vi.doUnmock("jiti");
99+vi.doUnmock("node:module");
100+clearBundledRuntimeDependencyNodePaths();
23101if (originalBundledPluginsDir === undefined) {
24102delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR;
25103} else {
26104process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = originalBundledPluginsDir;
27105}
106+if (originalPluginStageDir === undefined) {
107+delete process.env.OPENCLAW_PLUGIN_STAGE_DIR;
108+} else {
109+process.env.OPENCLAW_PLUGIN_STAGE_DIR = originalPluginStageDir;
110+}
28111});
2911230113describe("bundled plugin public surface loader", () => {
@@ -140,6 +223,25 @@ describe("bundled plugin public surface loader", () => {
140223expect(createJiti).toHaveBeenCalledTimes(1);
141224});
142225226+it("loads built public artifacts through staged runtime deps", async () => {
227+const publicSurfaceLoader = await importFreshModule<
228+typeof import("./public-surface-loader.js")
229+>(import.meta.url, "./public-surface-loader.js?scope=runtime-deps");
230+const fixture = createPackagedPublicArtifactWithStagedRuntimeDep();
231+process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = fixture.bundledPluginsDir;
232+process.env.OPENCLAW_PLUGIN_STAGE_DIR = fixture.stageRoot;
233+234+const loaded = publicSurfaceLoader.loadBundledPluginPublicArtifactModuleSync<{
235+marker: string;
236+}>({
237+dirName: "demo",
238+artifactBasename: "provider-policy-api.js",
239+});
240+241+expect(loaded.marker).toBe("artifact:staged");
242+expect(fs.existsSync(path.join(fixture.pluginRoot, "node_modules"))).toBe(false);
243+});
244+143245it("rejects public artifacts that change after boundary validation", async () => {
144246const createJiti = vi.fn(() => vi.fn(() => ({ marker: "should-not-load" })));
145247vi.doMock("jiti", () => ({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。