






























@@ -791,7 +791,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
791791expect(result.changes).toEqual(['Repaired missing configured plugin "demo".']);
792792});
793793794-it("reinstalls a known configured plugin when its recorded install path is missing", async () => {
794+it("reinstalls a known configured plugin from the catalog when its recorded install path is missing", async () => {
795795const records = {
796796discord: {
797797source: "npm",
@@ -809,6 +809,109 @@ describe("repairMissingConfiguredPluginInstalls", () => {
809809],
810810diagnostics: [],
811811});
812+mocks.listChannelPluginCatalogEntries.mockReturnValue([
813+{
814+id: "discord",
815+pluginId: "discord",
816+meta: { label: "Discord" },
817+install: {
818+npmSpec: "@openclaw/discord",
819+},
820+},
821+]);
822+mocks.installPluginFromNpmSpec.mockResolvedValueOnce({
823+ok: true,
824+pluginId: "discord",
825+targetDir: "/tmp/openclaw-plugins/discord",
826+version: "1.2.3",
827+npmResolution: {
828+name: "@openclaw/discord",
829+version: "1.2.3",
830+resolvedSpec: "@openclaw/discord@1.2.3",
831+integrity: "sha512-discord",
832+resolvedAt: "2026-05-01T00:00:00.000Z",
833+},
834+});
835+mocks.updateNpmInstalledPlugins.mockResolvedValue({
836+changed: false,
837+config: {
838+plugins: {
839+installs: records,
840+},
841+},
842+outcomes: [
843+{
844+pluginId: "discord",
845+status: "skipped",
846+message: "No update applied.",
847+},
848+],
849+});
850+851+const { repairMissingConfiguredPluginInstalls } =
852+await import("./missing-configured-plugin-install.js");
853+const result = await repairMissingConfiguredPluginInstalls({
854+cfg: {
855+plugins: {
856+entries: {
857+discord: { enabled: true },
858+},
859+},
860+channels: {
861+discord: { enabled: true },
862+},
863+},
864+env: {},
865+});
866+867+expect(mocks.updateNpmInstalledPlugins).toHaveBeenCalledWith(
868+expect.objectContaining({
869+pluginIds: ["discord"],
870+config: expect.objectContaining({
871+plugins: expect.objectContaining({ installs: records }),
872+}),
873+}),
874+);
875+expect(mocks.installPluginFromNpmSpec).toHaveBeenCalledWith(
876+expect.objectContaining({
877+spec: "@openclaw/discord",
878+expectedPluginId: "discord",
879+}),
880+);
881+expect(mocks.writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith(
882+expect.objectContaining({
883+discord: expect.objectContaining({ installPath: "/tmp/openclaw-plugins/discord" }),
884+}),
885+{ env: {} },
886+);
887+expect(result.changes).toEqual([
888+'Installed missing configured plugin "discord" from @openclaw/discord.',
889+]);
890+});
891+892+it("updates a known configured plugin when its installed manifest path still exists", async () => {
893+const records = {
894+discord: {
895+source: "npm",
896+spec: "@openclaw/discord",
897+installPath: process.cwd(),
898+},
899+};
900+mocks.loadInstalledPluginIndexInstallRecords.mockResolvedValue(records);
901+mocks.loadPluginMetadataSnapshot.mockReturnValue({
902+plugins: [
903+{
904+id: "discord",
905+channels: ["discord"],
906+},
907+],
908+diagnostics: [
909+{
910+pluginId: "discord",
911+message: "manifest without channelConfigs metadata",
912+},
913+],
914+});
812915mocks.updateNpmInstalledPlugins.mockResolvedValue({
813916changed: true,
814917config: {
@@ -817,7 +920,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
817920discord: {
818921source: "npm",
819922spec: "@openclaw/discord",
820-installPath: "/tmp/openclaw-plugins/discord",
923+installPath: process.cwd(),
821924},
822925},
823926},
@@ -857,7 +960,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
857960);
858961expect(mocks.writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith(
859962expect.objectContaining({
860-discord: expect.objectContaining({ installPath: "/tmp/openclaw-plugins/discord" }),
963+discord: expect.objectContaining({ installPath: process.cwd() }),
861964}),
862965{ env: {} },
863966);
@@ -907,7 +1010,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
9071010discord: {
9081011source: "npm",
9091012spec: "@openclaw/discord",
910-installPath: "/tmp/openclaw-plugins/discord",
1013+installPath: process.cwd(),
9111014},
9121015},
9131016},
@@ -949,7 +1052,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
9491052);
9501053expect(mocks.writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith(
9511054expect.objectContaining({
952-discord: expect.objectContaining({ installPath: "/tmp/openclaw-plugins/discord" }),
1055+discord: expect.objectContaining({ installPath: process.cwd() }),
9531056}),
9541057{ env: {} },
9551058);
@@ -999,7 +1102,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
9991102brave: {
10001103source: "npm",
10011104spec: "@openclaw/brave-plugin@beta",
1002-installPath: "/tmp/openclaw-plugins/brave",
1105+installPath: process.cwd(),
10031106},
10041107},
10051108},
@@ -1038,7 +1141,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
10381141);
10391142expect(mocks.writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith(
10401143expect.objectContaining({
1041-brave: expect.objectContaining({ installPath: "/tmp/openclaw-plugins/brave" }),
1144+brave: expect.objectContaining({ installPath: process.cwd() }),
10421145}),
10431146{ env: {} },
10441147);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。