




















@@ -4,6 +4,7 @@ import path from "node:path";
44import { bundledDistPluginFile } from "openclaw/plugin-sdk/test-fixtures";
55import { afterEach, describe, expect, it, vi } from "vitest";
66import { discoverOpenClawPlugins } from "./discovery.js";
7+import { listBuiltRuntimeEntryCandidates } from "./package-entrypoints.js";
78import {
89cleanupTrackedTempDirs,
910makeTrackedTempDir,
@@ -198,6 +199,7 @@ function createPackagePluginWithEntry(params: {
198199packageName: string;
199200pluginId?: string;
200201entryPath?: string;
202+writeBuiltRuntime?: boolean;
201203}) {
202204const entryPath = params.entryPath ?? "src/index.ts";
203205mkdirSafe(path.dirname(path.join(params.packageDir, entryPath)));
@@ -208,6 +210,14 @@ function createPackagePluginWithEntry(params: {
208210 ...(params.pluginId ? { pluginId: params.pluginId } : {}),
209211});
210212writePluginEntry(path.join(params.packageDir, entryPath));
213+if (params.writeBuiltRuntime ?? listBuiltRuntimeEntryCandidates(entryPath).length > 0) {
214+const runtimeEntry = listBuiltRuntimeEntryCandidates(entryPath)[0];
215+if (runtimeEntry) {
216+const runtimePath = path.join(params.packageDir, runtimeEntry.replace(/^\.\//u, ""));
217+mkdirSafe(path.dirname(runtimePath));
218+writePluginEntry(runtimePath);
219+}
220+}
211221}
212222213223function createBundleRoot(bundleDir: string, markerPath: string, manifest?: unknown) {
@@ -303,7 +313,7 @@ function expectBundleCandidateMatch(params: {
303313async function expectRejectedPackageExtensionEntry(params: {
304314stateDir: string;
305315setup: (stateDir: string) => boolean | void;
306-expectedDiagnostic?: "escapes" | "none";
316+expectedDiagnostic?: "escapes" | "none" | "runtime";
307317expectedId?: string;
308318}) {
309319if (params.setup(params.stateDir) === false) {
@@ -320,6 +330,14 @@ async function expectRejectedPackageExtensionEntry(params: {
320330expectEscapesPackageDiagnostic(result.diagnostics);
321331return;
322332}
333+if (params.expectedDiagnostic === "runtime") {
334+expect(
335+result.diagnostics.some(
336+(entry) => entry.level === "error" && entry.message.includes("compiled runtime output"),
337+),
338+).toBe(true);
339+return;
340+}
323341expect(result.diagnostics).toEqual([]);
324342}
325343@@ -714,6 +732,7 @@ describe("discoverOpenClawPlugins", () => {
714732const stateDir = makeTempDir();
715733const globalExt = path.join(stateDir, "extensions", "pack");
716734mkdirSafe(path.join(globalExt, "src"));
735+mkdirSafe(path.join(globalExt, "dist"));
717736718737writePluginPackageManifest({
719738packageDir: globalExt,
@@ -722,15 +741,43 @@ describe("discoverOpenClawPlugins", () => {
722741});
723742writePluginEntry(path.join(globalExt, "src", "one.ts"));
724743writePluginEntry(path.join(globalExt, "src", "two.ts"));
744+writePluginEntry(path.join(globalExt, "dist", "one.js"));
745+writePluginEntry(path.join(globalExt, "dist", "two.js"));
725746726747const { candidates } = await discoverWithStateDir(stateDir, {});
727748expectCandidateIds(candidates, { includes: ["pack/one", "pack/two"] });
728749});
729750751+it("rejects source-only TypeScript entries for installed package plugins", async () => {
752+const stateDir = makeTempDir();
753+const pluginDir = path.join(stateDir, "extensions", "source-only-pack");
754+mkdirSafe(path.join(pluginDir, "src"));
755+756+writePluginPackageManifest({
757+packageDir: pluginDir,
758+packageName: "@openclaw/source-only-pack",
759+extensions: ["./src/index.ts"],
760+});
761+writePluginEntry(path.join(pluginDir, "src", "index.ts"));
762+763+const result = await discoverWithStateDir(stateDir, {});
764+765+expectCandidatePresence(result, { absent: ["source-only-pack"] });
766+expect(
767+result.diagnostics.some(
768+(entry) =>
769+entry.level === "error" &&
770+entry.message.includes("requires compiled runtime output") &&
771+entry.message.includes("./dist/index.js"),
772+),
773+).toBe(true);
774+});
775+730776it("reuses one filesystem realpath lookup per package root within a discovery run", () => {
731777const stateDir = makeTempDir();
732778const packageDir = path.join(stateDir, "extensions", "pack");
733779mkdirSafe(path.join(packageDir, "src"));
780+mkdirSafe(path.join(packageDir, "dist"));
734781735782writePluginPackageManifest({
736783 packageDir,
@@ -739,6 +786,8 @@ describe("discoverOpenClawPlugins", () => {
739786});
740787writePluginEntry(path.join(packageDir, "src", "one.ts"));
741788writePluginEntry(path.join(packageDir, "src", "two.ts"));
789+writePluginEntry(path.join(packageDir, "dist", "one.js"));
790+writePluginEntry(path.join(packageDir, "dist", "two.js"));
742791743792const realpathSync = vi.spyOn(fs, "realpathSync");
744793const { candidates } = discoverOpenClawPlugins({
@@ -1049,6 +1098,7 @@ describe("discoverOpenClawPlugins", () => {
10491098"diffs",
10501099);
10511100mkdirSafe(path.join(pluginDir, "src"));
1101+mkdirSafe(path.join(pluginDir, "dist"));
10521102mkdirSafe(nestedDiffsDir);
1053110310541104writePluginPackageManifest({
@@ -1062,6 +1112,11 @@ describe("discoverOpenClawPlugins", () => {
10621112"export default function () {}",
10631113"utf-8",
10641114);
1115+fs.writeFileSync(
1116+path.join(pluginDir, "dist", "index.js"),
1117+"export default function () {}",
1118+"utf-8",
1119+);
1065112010661121writePluginPackageManifest({
10671122packageDir: path.join(pluginDir, "node_modules", "openclaw"),
@@ -1322,8 +1377,8 @@ describe("discoverOpenClawPlugins", () => {
13221377},
13231378},
13241379{
1325-name: "skips missing package extension entries without escape diagnostics",
1326-expectedDiagnostic: "none" as const,
1380+name: "rejects missing TypeScript package runtime entries without escape diagnostics",
1381+expectedDiagnostic: "runtime" as const,
13271382setup: (stateDir: string) => {
13281383const globalExt = path.join(stateDir, "extensions", "missing-entry-pack");
13291384mkdirSafe(globalExt);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。