























@@ -962,6 +962,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
962962},
963963env: {
964964OPENCLAW_UPDATE_IN_PROGRESS: "1",
965+OPENCLAW_UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR: "1",
965966},
966967});
967968@@ -1061,6 +1062,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
10611062},
10621063env: {
10631064OPENCLAW_UPDATE_IN_PROGRESS: "1",
1065+OPENCLAW_UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR: "1",
10641066},
10651067});
10661068@@ -1077,7 +1079,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
10771079});
10781080});
107910811080-it("does not install channel-selected external plugins during the package update doctor pass", async () => {
1082+it("does not install channel-selected external plugins during an opted-in package update doctor pass", async () => {
10811083mocks.listChannelPluginCatalogEntries.mockReturnValue([
10821084{
10831085id: "discord",
@@ -1099,6 +1101,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
10991101},
11001102env: {
11011103OPENCLAW_UPDATE_IN_PROGRESS: "1",
1104+OPENCLAW_UPDATE_DEFER_CONFIGURED_PLUGIN_INSTALL_REPAIR: "1",
11021105},
11031106});
11041107@@ -1109,6 +1112,144 @@ describe("repairMissingConfiguredPluginInstalls", () => {
11091112expect(result).toEqual({ changes: [], warnings: [], records: {} });
11101113});
111111141115+it("installs channel-selected external plugins during a legacy package update doctor pass", async () => {
1116+mocks.installPluginFromNpmSpec.mockResolvedValueOnce({
1117+ok: true,
1118+pluginId: "discord",
1119+targetDir: "/tmp/openclaw-plugins/discord",
1120+version: "2026.5.17",
1121+npmResolution: {
1122+name: "@openclaw/discord",
1123+version: "2026.5.17",
1124+resolvedSpec: "@openclaw/discord@2026.5.17",
1125+integrity: "sha512-discord",
1126+resolvedAt: "2026-05-17T00:00:00.000Z",
1127+},
1128+});
1129+mocks.listChannelPluginCatalogEntries.mockReturnValue([
1130+{
1131+id: "discord",
1132+pluginId: "discord",
1133+meta: { label: "Discord" },
1134+install: {
1135+npmSpec: "@openclaw/discord",
1136+},
1137+},
1138+]);
1139+1140+const { repairMissingConfiguredPluginInstalls } =
1141+await import("./missing-configured-plugin-install.js");
1142+const result = await repairMissingConfiguredPluginInstalls({
1143+cfg: {
1144+channels: {
1145+discord: { enabled: true, token: "secret" },
1146+},
1147+},
1148+env: {
1149+OPENCLAW_UPDATE_IN_PROGRESS: "1",
1150+},
1151+});
1152+1153+expect(mocks.installPluginFromNpmSpec).toHaveBeenCalledTimes(1);
1154+expect(result.changes).toEqual([
1155+'Installed missing configured plugin "discord" from @openclaw/discord.',
1156+]);
1157+expectRecordFields(result.records.discord, {
1158+source: "npm",
1159+installPath: "/tmp/openclaw-plugins/discord",
1160+});
1161+});
1162+1163+it("prefers npm over ClawHub during a legacy package update doctor pass", async () => {
1164+mocks.installPluginFromNpmSpec.mockResolvedValueOnce({
1165+ok: true,
1166+pluginId: "whatsapp",
1167+targetDir: "/tmp/openclaw-plugins/whatsapp",
1168+version: "2026.5.17",
1169+npmResolution: {
1170+name: "@openclaw/whatsapp",
1171+version: "2026.5.17",
1172+resolvedSpec: "@openclaw/whatsapp@2026.5.17",
1173+integrity: "sha512-whatsapp",
1174+resolvedAt: "2026-05-17T00:00:00.000Z",
1175+},
1176+});
1177+mocks.listChannelPluginCatalogEntries.mockReturnValue([
1178+{
1179+id: "whatsapp",
1180+pluginId: "whatsapp",
1181+meta: { label: "WhatsApp" },
1182+install: {
1183+clawhubSpec: "clawhub:@openclaw/whatsapp",
1184+npmSpec: "@openclaw/whatsapp",
1185+defaultChoice: "clawhub",
1186+},
1187+},
1188+]);
1189+1190+const { repairMissingConfiguredPluginInstalls } =
1191+await import("./missing-configured-plugin-install.js");
1192+const result = await repairMissingConfiguredPluginInstalls({
1193+cfg: {
1194+channels: {
1195+whatsapp: { enabled: true, allowFrom: ["+15555550123"] },
1196+},
1197+},
1198+env: {
1199+OPENCLAW_UPDATE_IN_PROGRESS: "1",
1200+},
1201+});
1202+1203+expect(mocks.installPluginFromClawHub).not.toHaveBeenCalled();
1204+expectRecordFields(mockCallArg(mocks.installPluginFromNpmSpec), {
1205+spec: "@openclaw/whatsapp",
1206+expectedPluginId: "whatsapp",
1207+});
1208+expect(result.changes).toEqual([
1209+'Installed missing configured plugin "whatsapp" from @openclaw/whatsapp.',
1210+]);
1211+expectRecordFields(result.records.whatsapp, {
1212+source: "npm",
1213+installPath: "/tmp/openclaw-plugins/whatsapp",
1214+});
1215+});
1216+1217+it("keeps ClawHub-only candidates available during a legacy package update doctor pass", async () => {
1218+mocks.listChannelPluginCatalogEntries.mockReturnValue([
1219+{
1220+id: "matrix",
1221+pluginId: "matrix",
1222+meta: { label: "Matrix" },
1223+install: {
1224+clawhubSpec: "clawhub:@openclaw/plugin-matrix@stable",
1225+defaultChoice: "clawhub",
1226+},
1227+},
1228+]);
1229+1230+const { repairMissingConfiguredPluginInstalls } =
1231+await import("./missing-configured-plugin-install.js");
1232+const result = await repairMissingConfiguredPluginInstalls({
1233+cfg: {
1234+channels: {
1235+matrix: { enabled: true, homeserver: "https://matrix.example.org" },
1236+},
1237+},
1238+env: {
1239+OPENCLAW_UPDATE_IN_PROGRESS: "1",
1240+},
1241+});
1242+1243+expectRecordFields(mockCallArg(mocks.installPluginFromClawHub), {
1244+spec: "clawhub:@openclaw/plugin-matrix@stable",
1245+expectedPluginId: "matrix",
1246+});
1247+expect(mocks.installPluginFromNpmSpec).not.toHaveBeenCalled();
1248+expect(result.changes).toEqual([
1249+'Installed missing configured plugin "matrix" from clawhub:@openclaw/plugin-matrix@stable.',
1250+]);
1251+});
1252+11121253it("does not install configured plugins when plugins are globally disabled", async () => {
11131254mocks.listChannelPluginCatalogEntries.mockReturnValue([
11141255{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。