diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index 650fccfd25f..cb76c0da133 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -837,6 +837,24 @@ afterAll(() => { }); describe("loadOpenClawPlugins", () => { + it("refreshes bundled plugin-sdk aliases without deleting the shared alias directory", () => { + const distRoot = makeTempDir(); + const pluginSdkDir = path.join(distRoot, "plugin-sdk"); + const aliasDir = path.join(distRoot, "extensions", "node_modules", "openclaw", "plugin-sdk"); + mkdirSafe(pluginSdkDir); + mkdirSafe(aliasDir); + fs.writeFileSync(path.join(pluginSdkDir, "index.js"), "export const value = 1;\n", "utf8"); + fs.writeFileSync(path.join(pluginSdkDir, "core.js"), "export const core = 1;\n", "utf8"); + fs.writeFileSync(path.join(aliasDir, "sentinel.txt"), "keep\n", "utf8"); + + __testing.ensureOpenClawPluginSdkAlias(distRoot); + fs.writeFileSync(path.join(pluginSdkDir, "core.js"), "export const core = 2;\n", "utf8"); + __testing.ensureOpenClawPluginSdkAlias(distRoot); + + expect(fs.existsSync(path.join(aliasDir, "sentinel.txt"))).toBe(true); + expect(fs.readFileSync(path.join(aliasDir, "core.js"), "utf8")).toContain("core.js"); + }); + it("disables bundled plugins by default", () => { const bundledDir = makeTempDir(); writePlugin({ diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 58be2de0014..a7c13ece67f 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -690,7 +690,14 @@ function ensureOpenClawPluginSdkAlias(distRoot: string): void { "./plugin-sdk/*": "./plugin-sdk/*.js", }, }); - fs.rmSync(pluginSdkAliasDir, { recursive: true, force: true }); + try { + if (fs.existsSync(pluginSdkAliasDir) && !fs.lstatSync(pluginSdkAliasDir).isDirectory()) { + fs.rmSync(pluginSdkAliasDir, { recursive: true, force: true }); + } + } catch { + // Another process may be creating the alias at the same time; mkdir/write + // below will either converge or surface the real filesystem error. + } fs.mkdirSync(pluginSdkAliasDir, { recursive: true }); for (const entry of fs.readdirSync(pluginSdkDir, { withFileTypes: true })) { if (!entry.isFile() || path.extname(entry.name) !== ".js") { @@ -728,6 +735,7 @@ export const __testing = { resolvePluginSdkAliasCandidateOrder, resolvePluginSdkAliasFile, resolvePluginRuntimeModulePath, + ensureOpenClawPluginSdkAlias, shouldLoadChannelPluginInSetupRuntime, shouldPreferNativeJiti, toSafeImportPath,