fix(updater): only toast 'Up to Date' on manual user-initiated checks

This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 22:52:16 -04:00
1 parent bea8aa9542
commit 7f2d028080
3 files changed
+52 -8

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@ export interface RegisterCoreHandlersDependencies {
checkConnector: (id: ConnectorId) => Promise<void>; checkConnector: (id: ConnectorId) => Promise<void>;
openConnectorAuthTerminal: (id: ConnectorId) => Promise<boolean>; openConnectorAuthTerminal: (id: ConnectorId) => Promise<boolean>;
openGenericTerminal: (input: { commandKey: string }) => Promise<boolean>; openGenericTerminal: (input: { commandKey: string }) => Promise<boolean>;
checkForUpdates: () => Promise<boolean>; checkForUpdates: (options?: { manual?: boolean }) => Promise<boolean>;
getWorkerProcess: () => { postMessage: (message: unknown) => void } | null; getWorkerProcess: () => { postMessage: (message: unknown) => void } | null;
resetAfterRestore: () => void; resetAfterRestore: () => void;
getApiPort: () => number; getApiPort: () => number;
+3 -1
View File
@@ -91,7 +91,9 @@ export function registerCoreReadHandlers({
}); });
registerHandle(IPC_CHANNELS.checkForUpdates, async () => { registerHandle(IPC_CHANNELS.checkForUpdates, async () => {
return ok(await checkForUpdates()); // Invocation via IPC only happens when the user opens Settings and clicks
// "Check for updates". Pass manual:true so the Up-to-Date toast fires.
return ok(await checkForUpdates({ manual: true }));
}); });
registerHandle(IPC_CHANNELS.getSidebarBadges, async (_event, companyId) => { registerHandle(IPC_CHANNELS.getSidebarBadges, async (_event, companyId) => {
+48 -6
View File
@@ -14,12 +14,38 @@ interface UpdaterServiceDependencies {
notify: (options: NotificationOptions) => void; notify: (options: NotificationOptions) => void;
} }
type SkipUpdateReason = "updates_disabled" | "insecure_url" | "dev_without_real_url";
function shouldSkipUpdateCheck(input: {
isPackaged: boolean;
updatesEnabled: boolean;
updateUrl: string | undefined;
updatesDisabledEnv: boolean;
}): SkipUpdateReason | null {
if (!input.updatesEnabled || input.updatesDisabledEnv) {
return "updates_disabled";
}
if (input.updateUrl && !input.updateUrl.startsWith("https://")) {
return "insecure_url";
}
if (!input.isPackaged && (!input.updateUrl || input.updateUrl.includes("example.invalid"))) {
return "dev_without_real_url";
}
return null;
}
export function createUpdaterService({ export function createUpdaterService({
db, db,
logger, logger,
notify, notify,
}: UpdaterServiceDependencies) { }: UpdaterServiceDependencies) {
let listenersAttached = false; let listenersAttached = false;
/**
* Set by checkForUpdates({ manual: true }) from the UI. Gates the
* "Up to Date" toast so the auto-every-24h check stays silent when
* nothing happened. Cleared inside the update-not-available handler.
*/
let manualCheckPending = false;
function attachListeners() { function attachListeners() {
if (listenersAttached) { if (listenersAttached) {
@@ -31,6 +57,7 @@ export function createUpdaterService({
}); });
autoUpdater.on("update-available", (info) => { autoUpdater.on("update-available", (info) => {
logger.info("[updater] update available", info.version); logger.info("[updater] update available", info.version);
manualCheckPending = false;
notify({ notify({
title: "Update Available", title: "Update Available",
body: `Version ${info.version} is downloading in the background`, body: `Version ${info.version} is downloading in the background`,
@@ -55,6 +82,10 @@ export function createUpdaterService({
}); });
}); });
autoUpdater.on("update-not-available", () => { autoUpdater.on("update-not-available", () => {
if (!manualCheckPending) {
return;
}
manualCheckPending = false;
notify({ notify({
title: "Up to Date", title: "Up to Date",
body: "You are running the latest version", body: "You are running the latest version",
@@ -64,6 +95,7 @@ export function createUpdaterService({
}); });
autoUpdater.on("error", (error) => { autoUpdater.on("error", (error) => {
logger.error("[updater] error", error); logger.error("[updater] error", error);
manualCheckPending = false;
notify({ notify({
title: "Update Check Failed", title: "Update Check Failed",
body: "Could not check for updates. Try again later.", body: "Could not check for updates. Try again later.",
@@ -73,20 +105,29 @@ export function createUpdaterService({
}); });
} }
async function checkForUpdates() { async function checkForUpdates(options: { manual?: boolean } = {}) {
const snapshot = db.listSnapshot(); const snapshot = db.listSnapshot();
if (!snapshot.updatesEnabled || process.env.AGENTCOMPANY_DISABLE_UPDATES === "true") {
return false;
}
const updateUrl = process.env.AGENTCOMPANY_UPDATE_URL?.trim(); const updateUrl = process.env.AGENTCOMPANY_UPDATE_URL?.trim();
if (updateUrl && !updateUrl.startsWith("https://")) { const skipReason = shouldSkipUpdateCheck({
isPackaged: app.isPackaged,
updatesEnabled: snapshot.updatesEnabled,
updateUrl,
updatesDisabledEnv: process.env.AGENTCOMPANY_DISABLE_UPDATES === "true",
});
if (skipReason === "insecure_url") {
logger.error("[updater] AGENTCOMPANY_UPDATE_URL must use HTTPS — refusing insecure update source."); logger.error("[updater] AGENTCOMPANY_UPDATE_URL must use HTTPS — refusing insecure update source.");
return false; return false;
} }
if (!app.isPackaged && (!updateUrl || updateUrl.includes("example.invalid"))) { if (skipReason === "dev_without_real_url") {
logger.info("[updater] Skipping update check because no real update URL is configured."); logger.info("[updater] Skipping update check because no real update URL is configured.");
return false; return false;
} }
if (skipReason) {
return false;
}
if (options.manual) {
manualCheckPending = true;
}
try { try {
autoUpdater.logger = { autoUpdater.logger = {
info: (...args: unknown[]) => logger.info(...args), info: (...args: unknown[]) => logger.info(...args),
@@ -100,6 +141,7 @@ export function createUpdaterService({
return Boolean(result?.updateInfo); return Boolean(result?.updateInfo);
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
manualCheckPending = false;
return false; return false;
} }
} }