
























@@ -107,6 +107,20 @@ function writeStandalonePlugin(filePath: string, source = "export default functi
107107fs.writeFileSync(filePath, source, "utf-8");
108108}
109109110+function mockLinuxMountInfo(mountPoints: readonly string[]) {
111+const originalReadFileSync = fs.readFileSync;
112+return vi.spyOn(fs, "readFileSync").mockImplementation((filePath, options) => {
113+if (filePath === "/proc/self/mountinfo") {
114+return mountPoints
115+.map(
116+(mountPoint, index) => `${100 + index} 99 0:${index} / ${mountPoint} rw - tmpfs tmpfs rw`,
117+)
118+.join("\n");
119+}
120+return originalReadFileSync(filePath, options as never) as never;
121+});
122+}
123+110124function createPackagePlugin(params: {
111125packageDir: string;
112126packageName: string;
@@ -453,6 +467,95 @@ describe("discoverOpenClawPlugins", () => {
453467]);
454468});
455469470+it("discovers bind-mounted bundled source overlays before packaged dist bundles", () => {
471+const stateDir = makeTempDir();
472+const packageRoot = path.join(stateDir, "node_modules", "openclaw");
473+const bundledRoot = path.join(packageRoot, "dist", "extensions");
474+const bundledPluginDir = path.join(bundledRoot, "synology-chat");
475+const sourcePluginDir = path.join(packageRoot, "extensions", "synology-chat");
476+createPackagePluginWithEntry({
477+packageDir: bundledPluginDir,
478+packageName: "@openclaw/synology-chat",
479+pluginId: "synology-chat",
480+entryPath: "index.js",
481+});
482+createPackagePluginWithEntry({
483+packageDir: sourcePluginDir,
484+packageName: "@openclaw/synology-chat",
485+pluginId: "synology-chat",
486+});
487+mockLinuxMountInfo([sourcePluginDir]);
488+const sourceEntryPath = path.join(sourcePluginDir, "src", "index.ts");
489+const bundledEntryPath = path.join(bundledPluginDir, "index.js");
490+491+const { candidates, diagnostics } = discoverOpenClawPlugins({
492+env: {
493+ ...buildDiscoveryEnv(stateDir),
494+OPENCLAW_BUNDLED_PLUGINS_DIR: bundledRoot,
495+},
496+});
497+498+const synologyCandidates = candidates.filter(
499+(candidate) => candidate.idHint === "synology-chat",
500+);
501+expect(synologyCandidates).toEqual([
502+expect.objectContaining({
503+origin: "bundled",
504+rootDir: fs.realpathSync(sourcePluginDir),
505+source: fs.realpathSync(sourceEntryPath),
506+}),
507+expect.objectContaining({
508+origin: "bundled",
509+rootDir: fs.realpathSync(bundledPluginDir),
510+source: fs.realpathSync(bundledEntryPath),
511+}),
512+]);
513+expect(diagnostics).toEqual([
514+expect.objectContaining({
515+level: "warn",
516+source: sourcePluginDir,
517+message: expect.stringContaining("bind-mounted bundled plugin source overlay"),
518+}),
519+]);
520+});
521+522+it("keeps copied source plugin dirs inert when they are not mounted overlays", () => {
523+const stateDir = makeTempDir();
524+const packageRoot = path.join(stateDir, "node_modules", "openclaw");
525+const bundledRoot = path.join(packageRoot, "dist", "extensions");
526+const bundledPluginDir = path.join(bundledRoot, "synology-chat");
527+const sourcePluginDir = path.join(packageRoot, "extensions", "synology-chat");
528+createPackagePluginWithEntry({
529+packageDir: bundledPluginDir,
530+packageName: "@openclaw/synology-chat",
531+pluginId: "synology-chat",
532+entryPath: "index.js",
533+});
534+createPackagePluginWithEntry({
535+packageDir: sourcePluginDir,
536+packageName: "@openclaw/synology-chat",
537+pluginId: "synology-chat",
538+});
539+mockLinuxMountInfo([]);
540+const bundledEntryPath = path.join(bundledPluginDir, "index.js");
541+542+const { candidates, diagnostics } = discoverOpenClawPlugins({
543+env: {
544+ ...buildDiscoveryEnv(stateDir),
545+OPENCLAW_BUNDLED_PLUGINS_DIR: bundledRoot,
546+},
547+});
548+549+expect(candidates.filter((candidate) => candidate.idHint === "synology-chat")).toEqual([
550+expect.objectContaining({
551+origin: "bundled",
552+rootDir: fs.realpathSync(bundledPluginDir),
553+source: fs.realpathSync(bundledEntryPath),
554+}),
555+]);
556+expect(diagnostics).toEqual([]);
557+});
558+456559it("loads package extension packs", async () => {
457560const stateDir = makeTempDir();
458561const globalExt = path.join(stateDir, "extensions", "pack");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。