From a5f66b5c48eb658e6dbdb4da3444d021d67e5edb Mon Sep 17 00:00:00 2001 From: Vignesh Natarajan Date: Fri, 3 Apr 2026 21:31:18 -0700 Subject: [PATCH] fix(plugins): constrain workspace discovery to .openclaw/extensions --- src/plugins/discovery.test.ts | 30 ++++++++++++++++++++++++++++++ src/plugins/discovery.ts | 15 +++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/plugins/discovery.test.ts b/src/plugins/discovery.test.ts index 497f7954129..9777764d73b 100644 --- a/src/plugins/discovery.test.ts +++ b/src/plugins/discovery.test.ts @@ -279,6 +279,36 @@ describe("discoverOpenClawPlugins", () => { expectCandidateIds(candidates, { includes: ["alpha", "beta"] }); }); + it("does not recurse arbitrary workspace directories for plugin auto-discovery", () => { + const stateDir = makeTempDir(); + const workspaceDir = path.join(stateDir, "workspace"); + const workspaceExt = path.join(workspaceDir, ".openclaw", "extensions"); + + const expectedWorkspacePluginDir = path.join(workspaceExt, "workspace-plugin"); + createPackagePluginWithEntry({ + packageDir: expectedWorkspacePluginDir, + packageName: "@openclaw/workspace-plugin", + pluginId: "workspace-plugin", + }); + + const unrelatedWorkspaceDir = path.join(workspaceDir, "lobster-integrations", "bin"); + createPackagePluginWithEntry({ + packageDir: unrelatedWorkspaceDir, + packageName: "@openclaw/stray-workspace-plugin", + }); + + const result = discoverOpenClawPlugins({ + workspaceDir, + env: buildDiscoveryEnv(stateDir), + }); + + expectCandidatePresence(result, { + present: ["workspace-plugin"], + absent: ["stray-workspace-plugin"], + }); + expect(result.diagnostics).toEqual([]); + }); + it("resolves tilde workspace dirs against the provided env", () => { const stateDir = makeTempDir(); const homeDir = makeTempDir(); diff --git a/src/plugins/discovery.ts b/src/plugins/discovery.ts index 11e6cf688ea..3a2a7295e2c 100644 --- a/src/plugins/discovery.ts +++ b/src/plugins/discovery.ts @@ -925,18 +925,9 @@ export function discoverOpenClawPlugins(params: { const workspaceMatchesBundledRoot = resolvesToSameDirectory(workspaceRoot, roots.stock); if (roots.workspace && workspaceRoot && !workspaceMatchesBundledRoot) { - discoverInDirectory({ - dir: workspaceRoot, - origin: "workspace", - ownershipUid: params.ownershipUid, - workspaceDir: workspaceRoot, - candidates, - diagnostics, - seen, - recurseDirectories: true, - skipDirectories: new Set([".openclaw"]), - visitedDirectories: new Set(), - }); + // Keep workspace auto-discovery constrained to the OpenClaw extensions root. + // Recursively scanning the full workspace treats arbitrary project folders as + // plugin candidates and causes noisy "plugin manifest not found" validation failures. discoverInDirectory({ dir: roots.workspace, origin: "workspace",