From 1ac22971f71bd9e2ee120feb2a19e2bd9348515d Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 12 Jul 2026 06:36:57 -0700 Subject: [PATCH] fix(update): skip plugins switched through ClawHub --- src/cli/update-cli/update-command.test.ts | 13 +++++++++++++ src/cli/update-cli/update-command.ts | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/cli/update-cli/update-command.test.ts b/src/cli/update-cli/update-command.test.ts index 6d58a71f7435..d64b40c9fc18 100644 --- a/src/cli/update-cli/update-command.test.ts +++ b/src/cli/update-cli/update-command.test.ts @@ -18,6 +18,7 @@ import { resolvePostCoreUpdateChildStdio, resolvePostUpdateServiceStateReadEnv, resolvePostInstallDoctorEnv, + resolvePostSyncPluginUpdateSkipIds, shouldPrepareUpdatedInstallRestart, resolveUpdatedGatewayRestartPort, shouldUseLegacyProcessRestartAfterUpdate, @@ -541,6 +542,18 @@ describe("collectMissingPluginInstallPayloads", () => { }); }); +describe("resolvePostSyncPluginUpdateSkipIds", () => { + it("skips plugins already switched through ClawHub or npm and repaired payloads", () => { + expect( + resolvePostSyncPluginUpdateSkipIds({ + switchedToClawHub: ["whatsapp"], + switchedToNpm: ["voice-call"], + repairedMissingPayloadIds: new Set(["telegram"]), + }), + ).toStrictEqual(new Set(["whatsapp", "voice-call", "telegram"])); + }); +}); + describe("shouldUseLegacyProcessRestartAfterUpdate", () => { it("never restarts package updates through the pre-update process", () => { expect(shouldUseLegacyProcessRestartAfterUpdate({ updateMode: "npm" })).toBe(false); diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index 3add9cef8ce2..c201358da833 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -264,6 +264,18 @@ type MissingPluginInstallPayload = { type PostUpdatePluginWarning = NonNullable[number]; +export function resolvePostSyncPluginUpdateSkipIds(params: { + switchedToClawHub: readonly string[]; + switchedToNpm: readonly string[]; + repairedMissingPayloadIds: ReadonlySet; +}): Set { + return new Set([ + ...params.switchedToClawHub, + ...params.switchedToNpm, + ...params.repairedMissingPayloadIds, + ]); +} + function isClawHubTrustNotice(message: string): boolean { const trimmed = stripAnsi(message).trimStart(); return ( @@ -2324,7 +2336,11 @@ export async function updatePluginsAfterCoreUpdate(params: { timeoutMs: params.timeoutMs, updateChannel: pluginUpdateChannel, coreVersion: coreVersion ?? undefined, - skipIds: new Set([...syncResult.summary.switchedToNpm, ...missingPayloadIdSet]), + skipIds: resolvePostSyncPluginUpdateSkipIds({ + switchedToClawHub: syncResult.summary.switchedToClawHub, + switchedToNpm: syncResult.summary.switchedToNpm, + repairedMissingPayloadIds: missingPayloadIdSet, + }), skipDisabledPlugins: true, syncOfficialPluginInstalls: true, disableOnFailure: true,