新圖像在 macOS 26 上終於顯示正常了。 深色風格下,箭頭和圓環不再糊成一整塊,貓的頭和耳朵邊緣也不再有鋸齒。
關閉工作台後,發布說明佔用的記憶體會隨之釋放。 以前打開過的發布說明頁面會一直在背景執行,直到結束 DuoUpdater。
所有版本,最新的在前——和 App 自我更新時顯示的說明是同一份。下載請至發行頁面。較早的版本以英文顯示。
新圖像在 macOS 26 上終於顯示正常了。 深色風格下,箭頭和圓環不再糊成一整塊,貓的頭和耳朵邊緣也不再有鋸齒。
關閉工作台後,發布說明佔用的記憶體會隨之釋放。 以前打開過的發布說明頁面會一直在背景執行,直到結束 DuoUpdater。
全新的 App 圖像。 一隻蜷起來的暹羅貓,尾巴就是重新整理箭頭。在 macOS 26 及以後,它會跟隨你選擇的淺色、深色或著色圖像風格。
在低數據模式或按流量計費的網路(例如 iPhone 熱點)下,背景檢查會先等一等。 回到一般網路後立即補上。手動檢查和安裝更新不受影響。
因為網路不穩而失敗的檢查,會在兩分鐘後自動重試一次。 以前失敗的列和警告會一直留到下一次排程檢查,預設是六小時後。
底層改進。 版本說明裡的圖片佔用的磁碟空間小了很多,請求 面板不再把一次重新驗證的請求算成兩次,檢查 Muse 時也不再下載它的登入頁面。
透過自帶安裝腳本安裝的 Homebrew App(例如夸克網盤)現在可以一鍵更新。 以前每次嘗試都會出錯,說下載的檔案裡沒有安裝套件。
需要你親自執行安裝程式的 Homebrew App,現在會顯示更新並附上連結,而不是更新失敗。 以前 DuoUpdater 會把整個安裝程式下載完,然後出錯停下。
能辨識的 Homebrew App 更多了。 Homebrew 以不同名稱安裝的 App(例如 Visual Paradigm Community Edition)現在也會檢查更新,名稱相近的另一個 App 也不會再被誤認成它們。
Homebrew cask 已被下架的 App,不會再被推薦 Homebrew 拒絕安裝的更新。 如果 App 有自己的更新來源,會改用它來檢查。
VS Code 的版本說明又能顯示了。
底層改進。 Antigravity 和 OpenLens 的更新透過其他途徑找到之後,設定 › 診斷 裡不會再一直留著它們的警告。
從 blender.org 下載的 Blender 現在也能檢查更新,並且一鍵更新。 以前只有用 Homebrew 安裝的 Blender 才會被檢查。
Blender 的 alpha、beta 和候選版(RC)各走各的軌道。 DuoUpdater 會辨識你裝的是哪一種建置,同一種建置出了更新的版本時提醒你。alpha 不會被推薦正式版,反過來也一樣。
Muse 的更新又看得到了。 Muse 的下載連結開始要求登入之後,它那一列顯示的是錯誤,而不是新版本。
底層改進。 啟動後第一次打開選單列彈出視窗和工作台會快一點;duo check 現在會列出檢查失敗的 App,而不是說一切都是最新的。
DuoUpdater 現在支援義大利文、繁體中文、葡萄牙文(巴西)和土耳其文。 總共十一種語言。德文、西班牙文和日文中翻錯或顯示不完整的文字也已修正。「設定」→「資料夾」現在會像 Finder 一樣,用你的語言顯示資料夾名稱。
用 Homebrew 安裝的 App,現在會透過 Homebrew 更新。 這適用於由 brew 安裝、同時又內建更新程式的 App。以前 DuoUpdater 會直接就地更新,Homebrew 並不知情,於是下一次 brew upgrade 會把你已經裝好的版本再下載、再安裝一次。
視窗會在你目前所在的空間開啟。 以前關閉「設定」或其他 DuoUpdater 視窗後再打開,可能會把你切回它上次顯示時所在的空間。
一次更新,只通知一次。 有些 App 會透過兩個來源檢查,它們可能會把同一個新版本重複通知。
發行記錄不再出現日期在未來的版本。 如果開發者的更新來源給出的日期晚於 DuoUpdater 第一次看到這個版本的時間,記錄會改用看到它的時間。
備份磁碟沒接上時,工作台視窗又能完整放進螢幕了,「立即拷貝」也會告訴你為什麼無法拷貝。 以前視窗可能比螢幕還高,列表捲不到底;按下「立即拷貝」也毫無反應。
Xcode 的 Beta 版和候選版現在可以一鍵更新了。 只要在「設定」→「Xcode」登入一次 Apple Developer 帳號,DuoUpdater 就會像處理其他更新一樣,下載並安裝新的 Beta 版和 RC。以前它只能告訴你有新版本推出。
任何版本的 Xcode 都能與現有的並存安裝。 「設定」→「Xcode」會列出每一個 Xcode 版本,依版本分組,並預先展開你的 macOS 所對應的那一組。「安裝」會把你選的版本以獨立的一份放進「應用程式」(例如 Xcode-26.6.app),不會取代任何東西。你也可以只儲存封存檔(.xip)。登入後還會併入 Apple 自己的列表,新版本幾分鐘內就會出現。
Apple Developer 的登入狀態會自動延續。 Apple 大約八小時後就會結束開發者工作階段。現在只要 Apple 仍認得這台 Mac,DuoUpdater 就會在背景取得新的工作階段,不跳出視窗,也不必輸入密碼。你可以在「設定」→「Xcode」關閉這項功能。
新的 Xcode 版本會更快出現。 平日在 Apple 通常發布新版的時段,DuoUpdater 每五分鐘會找一次新的 Xcode。
幾款 App 在開發者做了變動後又能正常運作了。 CapCut beta 和 Superconductor nightly 又能被找到並安裝,Superconductor 也會保留目前的 App 身分。VLC 3.0.24 和 JetBrains Air 又能顯示版本說明了。
現在支援 Qoder CN IDE 了。 Qoder IDE 的中國大陸版可以檢查更新、一鍵安裝,版本說明也會顯示在視窗中。桌面 App Qoder CN 現在也會顯示版本說明了。
Qoder IDE 的最新版本不會再變來變去了。 Qoder 是分批推出更新的,DuoUpdater 現在用和你那份 IDE 相同的方式詢問,所以每次都會得到相同的答案。以前提供的版本可能在兩個版本之間來回切換,一鍵安裝也可能因為答案中途改變而停在一半。
透過 Sparkle 更新的 App,已下載的更新過時後現在也能再更新。 如果某個透過 Sparkle 自行更新的 App 已經下載了某個版本,之後又推出了更新的版本,DuoUpdater 現在會先清掉舊的下載,再安裝最新版。以前「更新」一直提示現在安裝會被它蓋掉,那一列也始終沒有變化。
duo install 會告訴你最新版本已經在等著安裝了。 如果 App 自己的更新程式已經下載好最新版本,它現在會直接說明,並建議你結束該 App 或執行 duo restart。以前它會誤稱這個 App 只能檢查、不能安裝。
搜狗输入法 現在可以一鍵更新了。 DuoUpdater 會依照這個輸入法自行更新的方式來套用更新,讓已安裝的套件留在原處,並且會先複製一份你的詞庫和設定,所以還原時它們也會一併回來。
存放在另一顆磁碟上的備份,現在會保留輸入法的詞庫和設定。 以前移到備份磁碟上的只有 App 本身,所以從那裡還原輸入法時,新版本的資料仍會留在原處。
不再提供永遠裝不上的更新。 JetBrains Air 現在顯示的是 Toolbox 實際提供的版本。開發者改名後仍以舊名稱留下的 App Store App,現在會告訴你 App Store 實際更新的是哪一份,而不是提供一個只會重新安裝另一份的更新。
更多 App 的版本說明現在會以格式化的方式顯示。 以 Markdown 發布版本說明的 App,現在也和其他 App 一樣顯示為原生列表。以前這些說明是以純文字顯示。
「重新開啟」不再和 App 自己的安裝程式搶時間。 當 App 已經自行下載好更新,而它的還原備份應該放在外接磁碟上時,這份複製現在會等安裝程式完成後才進行。以前它恰好在 macOS 留給安裝程式工作的那幾秒內執行。
還原備份現在可以存放在另一顆磁碟上。 一份備份就是整個 App 的完整副本,放在開機磁碟上會越積越多。「設定」→「備份」會列出已連接的磁碟和各自的可用空間,把你現有的備份搬過去,並在背景把新的備份寫到那裡。拔掉磁碟只會延後備份,不會讓你少一個還原點。duo backups 新增了 disks、sync、verify 和 probe。
現在支援千问输入法了。 有新版本時 DuoUpdater 會通知你,並依照這個輸入法自行更新的方式來更新它。
版本說明不會再卡在上一個版本。 如果開發者發布更新的時間比更新記錄頁面提到它早了幾分鐘,舊的說明就會被歸到新版本名下並永遠保留。CleanShot X 從 5.0.1 推出當天起,一直在 5.0.1 底下顯示 5.0 的說明。
刪除備份現在真的會釋放它所說的空間。 舊版 DuoUpdater 建立的備份是以 macOS 拒絕刪除的形式寫入的,所以「清理」會把大部分副本留在磁碟上,卻照樣算作已回收。
「清理」現在會立即打開。 以前每按一次,它都要把所有已儲存的備份從頭重新計算一遍大小。
WorkBuddy 又能顯示最新版本了。 它的更新服務回應的是升級鏈中的下一步,而不是最新的建置,因此 WorkBuddy 的四個版本全都落後了兩個版本。
底層改進。 從未宣告自己執行哪個二進位檔的 App(Meta 的 Muse 就是其中之一),現在也能讀出它的框架和 SDK。
透過「重新開啟」完成的更新現在也能還原了。 當 App 已經自行下載好更新、而你按下「重新開啟」時,DuoUpdater 現在會先儲存目前的版本,讓這次更新和其他更新一樣出現在「還原」中。以前只有 DuoUpdater 自己安裝的更新才能還原。
查看一次更新在 App 內部改了什麼。 對於已儲存上一個版本的 App,工作台在「版本說明」旁新增了「套件差異」檢視:簽署與權限、最低 macOS 版本、背景項目與登入項目、內附的程式庫,以及新增、移除或大小有變的檔案。在「終端機」中用 duo diff 也能對任意兩份 App 做同樣的比較。
App 自己的更新程式沒有套用更新時,「重新開啟」會告訴你。 如果 App 已經關閉,更新卻始終沒有完成,那一列現在會說明情況,並繼續提供「重新開啟」。以前轉圈停下後什麼也不說。
Cherry Studio 又能顯示最新版本了。 Cherry Studio 更改了 Mac 下載檔的名稱後,DuoUpdater 把一個較舊的版本當成了最新版,所以較新的副本顯示「已是最新」,較舊的副本則被提供那個舊版本。
App Store 更新不會再卡在沒有回應的輔助程式後面。 以前 DuoUpdater 的輔助程式停止回應時,更新會停在 0%,排在後面的每一個 App Store 更新都得等到你結束 DuoUpdater。
「全部更新」不會再重新開啟你已忽略的 App。 開啟「自動重新開啟已更新的 App」後,一批更新結束時可能會結束並重新開啟已忽略的 App,或是你已略過其下載版本的 App。
現在支援 Tinycast 和 SuperCmd 了。 Tinycast 或它的 Beta 版有新版本時,DuoUpdater 會通知你;除了 SuperCmd 2 之外,現在也能辨識最初的開源版 SuperCmd。Kimi Code 的版本說明現在也會顯示了。
版本說明與提供的版本一致了。 以前 VS Code 最新的版本說明頁面是空白的;而新版本剛出現時,說明面板也可能顯示上一個版本的內容。
「發行記錄」不會再於每次啟動時重複加入同一個版本。 以前 Claude、Raycast、Cline 等 App 每次啟動 DuoUpdater 都會多出一筆重複的項目。
工作台側邊欄分成了「App」「Brew」和「還原」三個標籤頁。 按一下標籤頁或在標籤頁上拖移即可切換;搜尋會同時篩選這三個列表。
底層改進。 對同一個 App 執行的「重新開啟」和「還原」不會再互相干擾;更新來源無法再讓 DuoUpdater 刪除下載資料夾以外的檔案;背景檢查不會再有讓 macOS 詢問是否允許取用其他 App 資料的風險;在「請求」篩選中輸入極大的數字不會再讓 App 結束;你在 duo install 中拒絕過的管理者授權提示,選單列現在也會遵照。
iStat Menus 的更新現在能正確偵測了。 iStat Menus 重新發布某個更新後,DuoUpdater 可能在安裝完成後仍繼續提供這個更新,再按一次「更新」則會出錯。
現在支援 Memoh Desktop 了。 有新版本時 DuoUpdater 會通知你,並能一鍵更新。
「全部更新」不會再要求你重新開啟已經重新開啟的 App Store App。 當 App Store 關閉某個 App 並以新版本重新開啟它時,那一列現在會立刻顯示為已更新,而不是一直顯示「立即重新開啟」直到整批更新完成。
已經自行下載好更新的 App,不會再被重複下載一次。 有些 App 會在背景悄悄下載更新,然後等你重新開啟它。現在在 DuoUpdater 中按「更新」,會直接安裝已經在你磁碟上的那一份:幾秒鐘就能完成更新,完全不必再下載。以前 DuoUpdater 會把同一個版本整個重新下載一遍。
現在支援 Cua Driver 和 Petex 了。 兩者有新版本時 DuoUpdater 都會通知你;Cua Driver 還能一鍵更新,並顯示它的版本說明。
同一個更新不會再一再通知你。 當開發者的伺服器在兩個版本之間來回切換時,每次檢查都看起來像有新的更新,於是又跳出一則通知。
剛安裝好的安裝套件不會再提議重新安裝。 以前它那一列會一直顯示將重新開啟已下載的安裝套件。
DuoUpdater 現在需要 macOS 15 Sequoia 或以上版本。 仍在使用 macOS 14 的 Mac 會保留目前的版本,但不會再收到更新;所有 Apple 晶片的 Mac 都能升級到受支援的 macOS。
底層改進。 例行檢查不再預先查詢下載連結;以前只要開發者的下載伺服器逾時,本來能用的「更新」按鈕就可能暫時消失。
現在支援 Aside 了。 Aside 瀏覽器有新的建置推出時,DuoUpdater 會通知你,並顯示它的版本說明。
你的 macOS 無法執行的更新不會再提供,而且那一列現在會說明原因。 當開發者註明某個版本支援哪些 macOS 時,DuoUpdater 會照著做:升級 macOS 後,Little Snitch 不會再提供上限低於你系統的建置;Xcode 不會再提供需要比你更新的 macOS 的建置;而 App 需要較新 macOS 的安裝套件會被拒絕,而不是裝上一個打不開的 App。以前只顯示一條空白橫線的列,現在會顯示「尚不支援此 macOS」或「需要較新的 macOS」,按一下就能看到詳細資訊。
查看每個 App 是用哪個 SDK 建置的。 按一下 App 名稱旁的標記,詳細資訊現在會寫著像是「以 macOS 27.0 SDK 建構。」這樣的內容,方便分辨哪些 App 已經針對最新的 macOS 重新建置。
分成兩個套件發布的 Homebrew App(一個給舊系統、一個給最新系統),現在會從正確的那一個讀取。 OnyX 在 macOS 27 上就是這樣拆分的,而 DuoUpdater 只看了給舊系統的那個套件:結果要麼完全不顯示 OnyX,要麼拿一個 Homebrew 在 27 上拒絕安裝的版本來判定它已是最新版本。現在你安裝的是哪一個,DuoUpdater 就追蹤哪一個。
TestFlight Beta 版不會再在 TestFlight 啟動時失去它的更新。 有新建置在等候的 Beta 版,以前可能會退回「已是最新」,並一直維持到你再次打開 TestFlight 為止。
Dropbox 和 ToDesk 的一鍵更新會下載正確的檔案。 在 Apple 晶片的 Mac 上,Dropbox 以前會下載僅支援 Intel 的建置,隨後被安全檢查拒絕;ToDesk 以前被提供的是只推送給部分使用者的搶先體驗版,而不是正式版。
「重新開啟」會立即完成 Spotify 的更新。 當 Spotify 已經自行下載好更新時,「重新開啟」以前要轉好幾分鐘,更新才會完成。
Beta 版會跟進到它們的正式版本。 Xcode Beta 版現在會收到它的候選版,而不是顯示已是最新版本;CotEditor 的 Beta 版能看到它的候選版;測試週期已結束的 Carbon Copy Cloner Beta 版,則會收到它最終成為的正式版,而不是檢查失敗。
版本說明與你安裝的版本一致。 以前 Blender 5.2 顯示的是 5.1 的說明,Raycast 1.x 顯示的是 2.x 的說明。Gemini 的「打開頁面」現在會打開 Google 目前的桌面版頁面,而不是錯誤頁面。
「設定」中的每個選項,說明就在它旁邊。 原本整個區塊下方的長篇說明,改成了每個控制項下方的一行簡短文字;選擇器也只說明你目前選取的那一項。
App 中的名稱現在統一寫作 DuoUpdater,與網站一致。
來自你尚未信任之 tap 的 Homebrew 套件,重新出現了。 從 Homebrew 6 開始,brew 在列出已安裝項目時會悄悄略過未信任 tap 中的套件,因此它們在沒有任何說明的情況下從 DuoUpdater 中消失了。現在它們會顯示為「未檢查 · tap 未受信任」,並附上可直接拷貝的 brew trust 指令;你在「終端機」中執行它(或執行升級)之後,切回視窗,畫面就會立刻跟上。
需要管理者密碼才能自行更新的 App,不會再被更新兩次。 當 Tailscale 這類 App 已經下載好自己的更新、正等著重新開啟時,DuoUpdater 看不出來,仍然提供「更新」,這可能會與 App 自己的安裝程式撞在一起。那一列現在會改為提供「重新開啟」,「全部更新」也不會動它。
Homebrew 區塊現在可以更新 Homebrew 本身。 Homebrew 有新版本時,頂端會出現一列,可以替你執行 brew update。如果你設定了 HOMEBREW_NO_AUTO_UPDATE,這一列就不會出現。
Homebrew 升級失敗時,現在會顯示 brew 實際的錯誤訊息。 以前那一列顯示的是 brew 在錯誤之後印出的建議,或者什麼都沒有,所以像「你的 Command Line Tools 太舊了」這樣的訊息始終沒有傳到你眼前。一次升級多個套件時,也不會再在還沒完成前就回報已完成。
選單列圖像可以數到 50 以上了。 以前待更新的項目超過 50 個時,它會一直停在 50。
版本說明:Claude 的說明會依「新增」「改進」「修正」分組,與 Claude 本身的顯示方式相同;Mac Mouse Fix 的說明改以文字顯示,而不是嵌入的網頁;Homebrew 自己的 App 現在也有版本說明了。
duo check 在實際上沒能檢查時,不會再說「Everything is up to date.」。 當它讀不到 TestFlight,或 App 掃描中途放棄時,現在會如實說明。
當 App 以多種語言發布版本說明時,現在會顯示你所用語言的那一份。 有些 App 會隨每次發布附上翻譯好的說明;DuoUpdater 以前只會拿 App 碰巧列在最前面或最後面的那份翻譯,結果有一款 App 的說明對每個人都顯示成德文,另一款的語言則每次發布都不一樣。
已推出 Mac 版本的 App,不會再顯示「這台 Mac 不支援」。 你在 Apple 晶片上執行的 iPhone 或 iPad App,偏偏在開發者推出真正的 Mac 建置那一刻被誤標為不支援;而那正是唯一一種讓更新更容易取得、而不是更難取得的變動。
你同時也在參與 Beta 測試的 App Store App,不會再被誤認為 TestFlight 建置。 當開發者把某個 Beta 版原封不動地轉為正式版時,兩者的建置編號相同,你購買的那一份就被交給了 TestFlight,於是 App Store 永遠無法為它提供更新。
需要比你目前系統更新的 macOS 才能安裝的 App Store 更新,現在會直接說明。 以前那一列照樣提議安裝,然後 App Store 在最後一步拒絕,畫面上沒有任何解釋。
「請求」視窗現在會顯示它的記錄實際能回溯到多久以前,並標示出它無法完整涵蓋的日期範圍。 以前在只記錄了幾個小時的日誌上選擇「過去 30 天」,看起來和選擇「過去 24 小時」一模一樣,畫面上也沒有任何解釋。
DuoUpdater 要替 TestFlight Beta 版做多少事,現在由你在「設定」→「一般」中決定。 選擇「我重新整理時」,它會讀取 TestFlight 已經知道的內容,並在你按下「重新整理」時請 TestFlight 給出最新答案;選擇「保持最新」,DuoUpdater 還會自行去詢問,因此 TestFlight 在背景安裝的 Beta 版不會再一直掛著問號等你重新整理,正在等你的建置也不會再被一個「已是最新」擋住而沒被注意到;選擇「關閉」則什麼都不讀取,並在那些列上如實說明,而不是用猜的。已經取得「完全取用磁碟」權限的 Mac 預設為「我重新整理時」,其他 Mac 則預設為「關閉」。
你自己打開 TestFlight,就足以讓 DuoUpdater 注意到它安裝了什麼。 以前透過 TestFlight 安裝的 Beta 版會一直掛著問號,直到下次重新整理;而在 macOS 27 上,TestFlight 對你已經安裝的 App 不再發出「可以測試」的通知,這一等可能就是好幾個小時。
Cline 現在能收到更新了,正式版和 Beta 版都可以,並會顯示它的版本說明。 在此之前,它那一列只有一個問號而沒有版本號碼:它沒有提供 DuoUpdater 讀得懂的那類更新來源,也沒有對應的 Homebrew 套件。DuoUpdater 現在會詢問 Cline 自己的更新程式所詢問的同一個位址,因此提供的更新正是 Cline 自己會安裝的那一個,Beta 版也會留在 Beta 路線上。
依「新增」「修正」這類標題分組的版本說明,現在會保留這些標題。 以前所有分組都會被合併成一份平鋪的列表,你無法分辨哪些是新功能、哪些是錯誤修正。
如果你在 Mac Mouse Fix 自己的一般設定中開啟了「Get Beta Versions」,它現在會提供 Beta 版。 以前 DuoUpdater 只看得到 Mac Mouse Fix 的正式版,因此 Beta 版會一直沒被注意到,直到下一個正式版推出。
建置編號只是單純遞增計數的 App,不會再把自己的修補版本藏起來。 對於回報版本為 12.10、建置編號為 282987 的 App,12.10.1 這個版本以前會被判讀為「已是最新」。
版本名稱不變的 App 推出新建置時,會再次通知你。 以前只要某個建置通知過一次,同一版本名稱下之後的每一個建置都會悄無聲息地到來:那一列亮了,圖像上的數字也算進去了,卻從來沒有跳出橫幅。
「全部更新」現在只計算真正更新完成的 App。 會打開 Apple 的「安裝程式」讓你自己完成的 App,以前在它的視窗還開著時就被算作完成,所以「已更新 2 個 App」可能代表其實什麼都還沒變。
已經安裝完成、只是留下一點殘餘的更新,現在會回報為已安裝,而不是「授予 App 管理權限」。 當時新版本其實已經在執行了,那一列卻還在要你去「系統設定」。
停止「全部更新」,現在會一併停止正在進行的下載。 以前好幾 GB 的傳輸會一直跑到結束,還會重試最多五次,之後才發現它早已被取消。
共用同一個版本說明頁面的兩個 App 不會再把說明弄混;說明分散在各版本頁面上的 App,更新後顯示的也會是最新的內容。 Antigravity 和 Antigravity IDE 以前可能會有十五分鐘互相顯示對方的說明;Thunderbird、WeChat、Opera 等幾款 App 更新後,則會有一段時間仍顯示上一個版本的說明。
搜尋 App 列表時會忽略變音符號,和「設定」中的搜尋一樣。 現在輸入「cafe」也能找到「Café」。
「診斷」頁面現在會針對每個發行通道各列出一行健康狀態。 以前損壞的 Beta 版或預覽版規則,會被同一個 App 狀態良好的正式版規則掩蓋。
GitHub 回應的「forbidden」不會再被回報為速率限制。 以前儲存庫改為私人、或權杖缺少某個權限範圍時,會引導你去加入一個其實幫不上忙的權杖。
對於會自行更新的 App,如果它等著安裝的建置比正在執行的還舊,就不會再提供「重新開啟」。
底層改進。 安裝、備份和套件檢查不會再佔用 App 其他部分所用的執行緒,因此進行這些作業時選單仍能即時反應;下載大型檔案時選單也保持流暢;「發行記錄」現在會把廠商在同一版本名稱下推出的每一個版本都算進去;缺少 App 本身執行檔的還原備份會被拒絕,而不是照樣存下;在全新的 Mac 上首次啟動時,不會再記錄莫名的資料庫錯誤;duo verify 和 duo reconcile 現在會回報條目塌縮在一起的更新記錄,以及已連續失敗好幾天的安裝程式位址;卡住的 duo 指令會在二十秒後放棄掃描,而不是一直懸著。
有些自行更新的 App,不會再在已有新版本時看起來仍是最新版本。 對於更新資訊放在更新緩慢的下載伺服器後面的 App,DuoUpdater 在新版本發布後,可能連續好幾天都只看到舊版本。
Kimi 的版本說明現在會顯示在 DuoUpdater 中。
CodeEdit 的更新現在會出現了。 以前有新版的 CodeEdit,那一列會停在一個問號上,而不是提供更新。
底層改進。 選單中的 Homebrew 列表會更快載入完成,duo 指令的啟動也更快了。
對 TestFlight Beta 版按「再檢查一次」,現在會給出真正的結果。 以前那一列會變成問號,直到下次重新整理為止。
重新整理按鈕向 TestFlight 查詢時,TestFlight Beta 版會保留原本的結果。 以前它們可能會在幾秒鐘內全部變成問號。
現在支援 super.engineering:檢查更新、版本說明和一鍵安裝。 新的 nightly 版本會連同其中的變更一起出現,按「更新」就會替你安裝好。
Microsoft Edge Beta 以前可能會提供正式版的 Edge 建置。 Microsoft 的 Beta 版更新來源曾短暫變成空的,那段時間那一列顯示的是一個正式版的版本號碼,如果更新下去,就會把它裝到你的 Beta 版上。
TestFlight Beta 版現在會顯示 TestFlight 為它們提供的更新,包括 iPhone 和 iPad App 的 Beta 版。 重新整理按鈕會在背景向 TestFlight 查詢,更新不會再在你看到幾分鐘後就消失,TestFlight 按鈕也會打開該 Beta 版的頁面。
當 DuoUpdater 無法判斷某個 TestFlight Beta 版是否為最新版本時,它會直接說明。 那一列會顯示問號,而不是說它已是最新版本,例如你已登出 TestFlight,或已不再測試該 Beta 版時。
「完全取用磁碟」權限現在有了說明,沒有它也不會一直打擾你。 沒有這項權限時,DuoUpdater 不會再觸發 macOS 關於讀取其他 App 資料的警告;如果某個 TestFlight Beta 版或 CotEditor 需要它,DuoUpdater 會告訴你一次:為什麼需要,以及要到哪裡授予。
已更新的 App 一旦結束,「重新開啟」就會消失。 有些 App 結束後仍留著輔助程式在執行,更新早已生效,卻還一直要求重新開啟。
沒有人回應的 macOS 隱私權提示,不會再讓更新檢查卡住。 即使拿不到那個 App 的設定,檢查也會繼續進行。
Rockxy 和 Ollama 的版本說明又完整了。 Rockxy 的說明會回溯到最近的幾次發布,而不是只有最新的那一個;Ollama 最新的一次發布也不會再被漏掉。
duo 這個選用的命令列工具,現在能更好地處理 TestFlight。 無論 TestFlight 是否開著,--refresh-testflight 都能正常運作,而且不會佔用你的螢幕;當 TestFlight 已經公佈了較新的建置時,duo check 也不會再說 Beta 版已是最新版本。
Telegram Desktop 又能檢查更新了。 Telegram 更改了它所發布檔案的名稱,那一列再也無法從中讀出版本號碼,因此顯示的是檢查失敗,而不是背後正等著的更新。
透過 TestFlight 安裝的 iPhone 或 iPad App,現在會被正確辨識為 TestFlight 建置。 DuoUpdater 以前把它當成 App Store 購買項目,因此那一列寫錯了是誰在管理它,同時還向商店查詢一個根本不存在的項目;每次檢查都會問一遍,只要這個 App 還裝著就一直問下去。
TestFlight Beta 版會以 TestFlight 自己的圖像標示。 由 App Store 管理的列早就帶著商店的圖像;由 TestFlight 管理的列卻是直接寫出名稱,於是同一類的列有兩種不同的標示方式。
切換標籤頁時,「網路」視窗的標題區不會再移動。 它的兩個標籤頁把標題放在略微不同的高度,所以在兩者之間切換時,視窗看起來像是抖了一下。
捲動整個 App 列表又變得流暢了。 快速捲動完整列表時會掉格;現在每一列不必先建構出來,就能回報自己的高度。
App Store App 的版本說明,現在一律來自 App Store。 當商店本身對某個 App 的查詢沒有找到或失敗時,視窗可能會改用該 App 另一種發行方式的說明;那是另一個建置,有自己的版本號碼,描述的是你這份副本永遠不會收到的版本。
Windscribe 在 Beta 或 Guinea Pig 通道上時,會收到該通道的建置。 DuoUpdater 會讀取你在 Windscribe 自己的設定中選擇的更新通道,因此跟隨發行前版本路線的副本,不會再在該路線上明明有更新的建置時被告知已是最新版本。視窗也會顯示這些發行前建置的說明,以前只會列出正式版的說明。
Windscribe 現在可以檢查更新,並附有版本說明。 執行舊建置的副本會列出它可以升級到的版本,以及其中有哪些變更;在此之前,DuoUpdater 完全看不到 Windscribe 的版本。更新仍要透過 Windscribe 自己的安裝程式進行,因為它會設定 App 本身以外的一些元件。
在你按下按鈕的過程中消失的 App,不會再被套用更新。 如果從你按下按鈕到安裝開始之間,這個 App 被解除安裝、被取代,或變得無法讀取,DuoUpdater 現在會停下來並說明原因,而不是照樣安裝到那個位置。
duo 這個選用的命令列工具,不會再在套件安裝完成之前就說已完成。 安裝以 .pkg 形式發布的 App 時,會打開 macOS 的安裝程式,剩下的交給你完成,但摘要卻把它算作已安裝:明明還什麼都沒取代,卻顯示「1 installed」。現在兩者會分開計算。它的 --json 輸出也會在每一行標明該 App 實際發生了什麼,因此腳本不必再去讀那段英文說明,就能分辨失敗和刻意略過。
底層改進。 保護一鍵「更新」的安裝前複查,現在也保護 duo install;而下載項目在取代 App 之前必須通過的檢查,現在集中在同一處,供兩條使用它們的路徑共用。
更新來源自相矛盾時,按「更新」不再毫無反應。 如果在你按下那一刻執行的檢查,傳回的版本比那一列原本提供的還舊,DuoUpdater 現在會說明情況,並繼續提供這個更新。以前它會把 App 判定為已是最新版本並從列表中移除,而同一個更新又會在下次檢查時重新出現。
Fork 設為 Develop 通道時,又會提供更新了。 DuoUpdater 把 Fork 的通道設定讀反了,追蹤的是落後許多的 Stable 更新來源,因此 Develop 的副本被列為已是最新版本,而 Fork 本身早已在提供更新的版本。
Mac Performance Monitor 現在會顯示版本說明了。 這個 App 把說明發布在自己的儲存庫中,而不是我們讀取的更新來源裡,所以視窗一直沒有東西可以顯示。
現在支援 CotEditor 了,正式版和 Beta 版兩條路線都涵蓋。 一份副本跟隨哪一條路線,取決於它正在執行的版本,以及 CotEditor 自己的「Update to prereleases when available」設定,因此 Beta 版副本會收到下一個 Beta 版,而不是一個會讓它倒退的正式版。
從 App Store 安裝的 App,永遠不會被提供來自其他地方的下載。 當商店本身對它的查詢失敗或傳回空結果時,那一列現在會說明由商店管理,並且不顯示版本號碼。以前檢查可能會轉而使用這個 App 的另一種發行方式(另一個建置,有自己的版本號碼),並提議把它安裝到你的商店副本上。
永遠不會為 App 提供會讓它退回舊版本的更新。 有些更新來源會把正式版排在實際上更新的發行前版本之上,採用它會讓 App 倒退。
列上的長錯誤訊息不再把列表其餘部分往下推。 它會限制在兩行內,完整文字可在游標停留時查看。
又多支援了四款 App:WhatCable、Qoder IDE、Qoder 和 Yaak。 每一款都能檢查更新和一鍵安裝,版本說明也會以文字讀入視窗,而不是嵌入網頁。
Qoder 的兩款 Mac App 現在能區分開來了。 IDE 和桌面 App 共用同一個名稱和下載頁面,但各自有獨立的版本路線,所以現在會分別追蹤。
WhatCable 和 Yaak 的 Beta 版現在會在自己的路線上追蹤。 執行 Beta 版的副本以前完全沒有來源,一直停在「失敗」;現在會收到下一個 Beta 版,版本說明也和正式版的分開。WhatCable 還包括 Beta 版最終轉成的正式版:採用它,這份副本就會移到正式版路線上。
檢查 App Store App 所用的網路流量,只剩以前的一小部分。 以前每次檢查都會重新抓取每個 App Store App 的產品頁面;現在這些頁面會保留一小時,並且用幾次請求就向商店問完你所有的 App,而不是每個 App 各問一次。檢查間隔設為五分鐘時,整體流量大約少了三分之一;在預設的六小時間隔下,頁面在兩次檢查之間仍會過期,因此省下的較少。
再次檢查單一 App,不會再重新抓取所有 App Store App。 以前按一次「再檢查一次」會丟掉所有快取的產品頁面,下一次排定的檢查就得全部重新抓取;現在只會重新整理你要求的那個 App。
檢查透過 GitHub 發布的 App,所用的網路流量也只剩以前的一小部分。 以前即使什麼都沒發布,每次檢查也會把每個版本的完整說明重新下載一遍;現在會先詢問 GitHub 該版本自上次以來是否有變更,沒有變更就什麼都不下載。每天會完整重讀每個版本一次,因此被撤回的版本在一天內就會被發現。
在 GitHub Beta 或 nightly 路線上追蹤的 App,現在只要求一個版本,而不是一整頁。 最新的版本幾乎每次都就是答案,只有在不是的那幾次才會抓取整頁。
Vorssaint 的更新檢查不再經過重新導向。 它的儲存庫改了名稱,沿用舊名稱會讓請求悄悄落入 GitHub 的匿名速率限制;現在檢查會直接使用新名稱。
底層改進。 發行產物現在在 GitHub 託管的 Mac 上建置、簽署和公證,並附有任何人都能驗證的建置來源證明;記錄的請求日誌也會區分回應是來自快取還是來自網路。
你匯出的請求日誌不會再帶有你的帳號名稱。 以前安裝在個人專屬資料夾中的 App,每一列都會寫出完整路徑;現在無論你用哪種方式匯出日誌,都會改以 ~ 顯示。
「請求」標籤頁現在會說明它涵蓋與不涵蓋的範圍。 它記錄的是 DuoUpdater 自己發出的請求。版本說明頁面會自行載入圖片和字型,而 App Store 和 Homebrew 的更新是由各自獨立的工具執行;這些都不會出現在裡面,視窗現在會直接說明,而不是讓你自己去猜。
「拷貝 URL」現在會先把位址轉義再交給你。 含有空格的路徑(例如 Firefox、Thunderbird 和 Bartender 的下載位址)以前是原封不動拷貝的;瀏覽器可以包容,命令列卻不行。
CapCut 的 Beta 版那一列不再回報檢查失敗。 在兩個 Beta 版之間,也就是一個轉為正式版之後、下一個開放之前,開發者在那條路線上什麼都不發布;以前這會顯示成一列紅字,外加一個按了也不可能成功的「重試」。現在那一列只會暫時沒有來自該來源的答案,直到下一個 Beta 版出現。
Audacity 現在會顯示說明它以什麼技術建置的標記。 它透過一個小型啟動程式啟動,再交給旁邊真正的程式,而標記一直是從那個啟動程式讀取的,但它根本沒有連結任何東西。
App Store 的版本說明現在會以你的語言顯示。 以前一律以商店本身的預設語言抓取,所以系統語言設為中文或日文的 Mac 讀到的仍然是英文。
DuoUpdater 本身的更新,現在會以它自己的名稱顯示在「網路」視窗中。 它的版本檢查、版本說明和下載,以前在 App 欄位中都是空白的。
底層改進。 夾帶在網址路徑中的憑證,現在會在請求被記錄之前移除,就像查詢字串中的憑證一向的處理方式。
「下載流量」視窗現在改名為「網路」,並多了第二個標籤頁。 「下載」就是你原本那份帳本:每次更新作為檔案花了多少流量。「請求」則是新的:DuoUpdater 代你發出的每一個請求、用途為何、屬於哪個 App、花了多少流量,並附有篩選欄位,讓你向日誌提問,而不必一路捲動翻找。頂端的數字回答的是你目前篩選出來的範圍,而不是累計總量。
檢查 Spotify 更新幾乎不再耗用網路流量。 Spotify 沒有在任何地方公佈版本檔案,所以檢查是從一個 2 MB 的精簡安裝程式中讀出版本號碼,而它以前每次都會把整個檔案下載一遍,全天候不停。現在會先詢問該檔案是否有變更,沒有變更就略過下載。
PDF Expert 現在找得到自己的更新,也會顯示變更內容。 它以前讀取的是一份開發者從 2022 年起就不再更新的版本列表,所以無論你裝的是哪一版,它都說自己已是最新版本。現在它改用 App 自己的更新程式所用的列表,也會附上過去版本的版本說明,而不只有最新的那一版。
UTM 預覽版建置現在會收到自己的更新、可以一鍵安裝,並顯示對應的版本說明。 預覽版副本以前會被拿去和正式版路線比較,然後悄悄被告知已是最新版本;現在它會沿著自己的路線走,一旦採用了該路線的正式版本,就會回到正式版路線。
底層改進。 記錄的請求已搬進和下載帳本相同的儲存空間,因此同一次下載的兩份記錄不會再對不上。你現有的帳本會原封不動地保留。
又有十五款 App 納入更新檢查,除了一款之外都能一鍵安裝。 AgentsView、AnythingLLM、Chatbox、ChatGPT Classic、DSH Desktop、FluidVoice、GitHub Copilot、Kun、Meetily、Microsoft 365 Copilot、OpenLogi、OpenSuperWhisper、Paseo、T3 Code(包含 alpha 和 nightly 兩條路線)以及 Vorssaint。唯一的例外是 ChatGPT Classic:它仍會替你檢查,但它附帶一個會自行更新 App 的安裝程式,所以這一款仍要由你自己執行。
八款 App 的版本說明現在直接顯示在 DuoUpdater 中,不再把你帶到網頁。 Xcode、Antigravity、Antigravity IDE、AnyDesk、AnythingLLM、Chatbox、Headlamp 和 Helium。其中三款原本就沒有網頁可以帶你去:AnyDesk 的頁面回應的是機器人驗證,Helium 的開發者根本沒有發布說明頁面,而 Antigravity IDE 則連任何連結都沒有。
Xcode Beta 版會說明每個 Beta 版改了什麼。 那一列以前只提供一個建置編號和一個連結;現在會列出 Apple 為你正在使用的 Beta 版,以及同一版本中之前每一個 Beta 版所寫的說明。
Word、Excel、PowerPoint、Outlook 和 OneNote 現在會發現自己的更新已經完成。 這五款 App 以前會一直提議重新打開你早已完成的安裝程式,永遠不會進展到提供重新開啟;無論等多久或重新檢查多少次都沒用。
等待重新開啟的列,又能說清楚自己的狀態了,兩個視窗都是。 當某個 App 既領先於廠商發布的版本、又在等待重新開啟時,按鈕上方那行文字說的完全是另一回事;對於等待「全部更新」後重新開啟的列,視窗和選單的說法也不一致。
在俄文介面中,列上的標籤不會再擠掉 App 名稱。 另外也修正了幾個標籤在所有翻譯語言中都借用了不相關字串用語的問題。
廠商只標了發布日期、沒標時間的 App,現在也會顯示版本說明。 以前這些版本在任何地方都不留痕跡。
我們拒絕打開的版本說明頁面,現在會說明原因。 以前會是一片空白,看起來就跟根本不發布說明的 App 一樣。
微信開發者工具的 nightly 版本說明不再是空白的了。
底層改進。 版本與發布日期的處理已整合到一處,建置編號再也不會被當成行銷版本號碼;發行時間軸也不會再為廠商沒提供的時刻捏造一個時間。
檢查失敗的項目,現在會在視窗中顯示出來。 以前對於檢查失敗的列,視窗什麼都不顯示;已忽略、已略過,或由 App Store、Toolbox、TestFlight 管理的列也一樣,看起來就和「已是最新」完全相同。現在兩個視窗對同一個 App 的說法一致,重試按鈕也兩邊都有。
在任何一列上按「再檢查一次」,都只會查詢那一個 App。 它也會重新確認哪些 App 正在執行,所以綠點看起來不對時,這是最快的修正方式。
略過和忽略現在可以在視窗的右鍵選單中操作。 這兩種列都會要你按右鍵來取消,但這個視窗的選單裡原本根本沒有這個選項。
背景檢查不會再把你正在閱讀的版本說明拿走。 每小時一次的檢查會丟棄所有已載入的說明,所以開著的「版本說明」面板會閃回載入中的轉圈圖像。現在只有你主動要求的重新整理才會重新載入。
中斷後的下載,會先經過核對才算完成。 伺服器重新傳送整個檔案或提早中斷,以前都被照單全收,問題要到下一步才浮現:安裝因封存檔損壞而失敗。以前透過代理伺服器下載時會一直失敗的情況,現在也能順利完成了。
更新一個 App,不會再重新掃描磁碟上的所有 App。 以前每按一次,都要把你所有的應用程式完整掃描兩遍,只為了查看你要求的那一個。
Chrome 的版本說明不會再卡住了。 Google 部落格一次例行的改版,就足以讓它停頓好幾分鐘;現在無論頁面長什麼樣子,都能迅速載入。
已下載的安裝程式不會再被丟棄後重新下載。 當廠商把同一個版本寫成兩種形式(v1.2.3 和 1.2.3)時,等待中的安裝套件會認不出來,而「重新開啟」可能一直在等一次其實早已完成的替換。
管理者安裝失敗,不會再被誤當成你按了「取消」。 以前不會顯示任何錯誤,而那個 App 還會悄悄停止提供一鍵更新,直到你從它那一列的選單重新要求管理者權限為止。
綠色的「執行中」圓點現在能注意到每一個 App。 有些 App 打開或結束時,macOS 從不發出通知,它們的圓點就會一直顯示錯誤的狀態,直到有其他不相關的操作把它更新為止。
「全部更新」在重新整理期間不會再忽隱忽現,在重新整理途中更新的 App,也不會被重設成再次提供它已安裝的更新。
在你按下「更新」時檢查失敗,現在會明確告訴你,而不是被歸類為「沒有要做的事」。
底層改進。 以特殊格式書寫的發布日期現在能正確讀取;版本說明頁面的位址檢查涵蓋了所有等效的寫法;例行的記錄作業也不再於你 Mac 上任何 App 打開或結束時都去讀寫磁碟。
Docker 的標記現在描述的是 Docker 的介面,而不是它的背景常駐程式。 那一列原本顯示「原生」,但 Docker Desktop 是一個 Electron App。這個標記是從 App 的套件中讀取的,而 Docker 的套件只是一層外殼:它指定的程式是一個用 Go 寫成的背景服務,本身不帶任何框架,真正繪製視窗的 App 則位於它內部的下一層。每一項都讀得正確,只是讀錯了檔案。現在 DuoUpdater 會去查看內層的 App,但只有在外層本身什麼都沒帶、而且裡面恰好只有一個能證明自己用什麼建置的 App 時才會這麼做;所以與真正介面一起附帶的輔助程式,仍然無法把自己的身分借給宿主。Docker 現在顯示為 Electron 42.5.0;在撰寫這段文字的電腦上,列表中的一百四十六個 App 裡,只有這一列有所改變。
CleanShot X 的版本說明又能正常閱讀了,包括 5.0 的。 CleanShot 推出了它多年來最大的一次更新,並同時重建了更新記錄頁面:日期移到了版本號碼上方,外圍多了兩層結構,功能版本還會在版本號碼和變更列表之間插入一段介紹和兩個影片連結。DuoUpdater 原本使用的讀取規則一條都對不上了。比面板空白更糟的是,它看起來並不空白:先前為上一個版本儲存的說明仍留在畫面上,頂著新版本的標題,所以面板寫著「5.0」,下面卻是 4.8.10 的變更,而任何地方都沒有顯示其他資訊。
在版本發布前就儲存的說明,現在會重新讀取,而不是永遠信以為真。 這是同一件事的另一半。DuoUpdater 在 CleanShot 公佈 5.0 的內容之前六分鐘就知道 5.0 的存在,於是把當時的頁面原樣存下,並歸到 5.0 名下;從此以後每次檢查都發現已經存過,再也沒有重新查看。現在已儲存的說明在被視為定稿之前,每個工作階段都會向廠商確認一次,所以說明較晚公佈的版本,會在你下次查看時自動補齊,而不會一直錯下去。
從選單列打開的任何項目,現在第一次按一下就會打開。 在某一列上選擇「更新記錄」(或是「設定」、發行記錄)時,第一次完全沒有反應,從第二次按一下起才正常。重新打開選單又會讓每一次嘗試都變回第一次,所以對於習慣打開選單、按一下、然後等視窗出現的人來說,它從來沒有成功過。選單中的第一次按一下,被用來把 DuoUpdater 帶到最前面,而不是執行它原本要做的事。打開的視窗現在也會保持在最前面:以前它會被帶到前面,又在同一瞬間被推回後面,這是它看起來像「按了沒反應」的另一個原因。
沒有填寫自己名稱的 App,現在也會有名稱。 欧路词典(Eudic)在列表中有圖像、有版本號碼,名稱的位置卻什麼都沒有。它的套件確實宣告了顯示名稱,但內容是空的,因為真正的名稱放在 App 的各語言翻譯中;而 DuoUpdater 把這個空答案當成了答案,沒有繼續往下找。現在它會改用 App 的另一個名稱,再不行就用 App 檔案本身的名稱,因此每一列都一定會有名稱。在這裡的一百五十個 App 中,只有一個受到影響;重點在於資訊本來就在,只是被略過了。
App 拒絕結束時,「重新開啟」現在會告訴你,而不是看起來像按了沒反應。 如果某個 App 有視窗正在等你處理(儲存提示、登入表單或某個對話框),macOS 就不會讓它結束,而 DuoUpdater 這邊對此無能為力:那是 App 自己的視窗。以前 DuoUpdater 會轉圈三十秒,然後不做任何解釋就把同一個按鈕放回來,看起來就像按鈕壞了,於是大家會再按一次。現在那一列會說明發生了什麼,並引導你到該 App 去處理那個視窗。什麼都沒有被更動,新版本也已經安裝好了,所以真的只剩重新開啟這一步;如果你在接下來十分鐘內自己結束該 App,DuoUpdater 會注意到,並以新版本將它重新開啟,你一下都不用按。
欧路词典的版本說明又變回單一版本,而不是十六年的全部歷史。 這個 App 會把它的完整歷史(一路回溯到 2.5.0 的每一個版本)全部放進最新那一版的說明中,所以詳細資訊面板把這些內容全都擺在「26.9.0」這個標題底下,你得捲過十年的記錄,才找得到剛剛改了什麼。現在每個版本在列表中都有自己的項目,和其他所有 App 一樣。
說明 App 用什麼技術建置的標記,現在判定 Tauri 需要證據,而不只是看起來像。 Tauri 在套件中不會留下任何可找的東西(沒有框架,也沒有自己的資料夾),所以這個標記原本只能從 App 的封裝方式,加上它連結了 Apple 的網頁檢視這一點推斷出來。Longbridge 完全符合這些條件,卻不是 Tauri:它用和 Zed 相同的繪圖引擎自行繪製視窗,只在介面的一角嵌入網頁檢視。現在 DuoUpdater 會先從二進位檔中讀出 Tauri 自己的特徵,才會下這個判斷,所以被標為 Tauri 的 App 確實是 Tauri;而 Longbridge 則顯示為它本來的身分:原生的 Mac App。
CapCut 不會再因為字節跳動的伺服器出了半秒差錯就變紅。 DuoUpdater 用來查詢 CapCut 版本的端點,會先回應成功的狀態碼,然後大約每五十次有一次,傳回的是錯誤物件而不是答案:那是字節跳動自家基礎架構內部的逾時,大約只有 390 位元組,而預期應該是 436 KB。DuoUpdater 這邊原本無從分辨這種情況和 CapCut 改變了回應格式有什麼不同,所以那一列會顯示檢查失敗,讓人讀起來像是「這壞了,得有人來修」,但它其實下一次重試就會自己好。現在這種特定的形式會被正確辨識出來:請求會立即重試,只有在連續五天都發生時,才會被回報為真正的問題。
列表中的每個 App 現在都會說明它是用什麼技術建置的。 App Store App 的那一列一直帶有商店的標記,其他每一列則看起來都一樣:Sparkle App、Electron App 和原生 App 完全分辨不出來。現在每個名稱旁都帶有該技術自己的標記,按一下可以看到名稱和一句說明,游標停留則會顯示提示:Electron、Tauri、Flutter、Qt、Java、Chromium、Mac Catalyst、在 Apple 晶片上執行的 iPhone App,或原生 Mac App;只要能當作事實讀出來,還會附上執行環境的版本(Electron 42.4.1、Qt 6.2、App 內嵌的 Chromium、建置它所用的 Tauri)。這些都是從套件本身讀出來的:封裝工具必須附帶的框架、啟動程式需要的執行環境、二進位檔連結的程式庫;所以它是關於實際安裝內容的事實,而不是從 App 名稱猜出來的。名稱太長放不下時,符號會讓出位置,而不是把名稱擠到第二行:這一列以名稱為主。整個功能可以在「設定」→「一般」中關閉。
LibreOffice 不會再宣稱可以「降級」到你已經安裝的版本。 它的下載索引列出的是三段式版本(26.8.0),而已安裝的副本回報的是四段式(26.8.0.3),把缺少的那一段補成零之後,已安裝的副本反而顯得比較新,所以那一列會對一個其實正好是最新的 App,顯示一句低調的「廠商版本落後於你」。現在,來源發布的版本段數比 App 回報的少時,會被視為描述同一個版本,而不是較舊的版本;真正的版本倒退仍會如實說明。
更新新增的設定,現在會主動提示自己一次。 一個出現在「設定」視窗中間的新偏好設定,是一個沒有人會發現的偏好設定。在新增了這類設定的更新之後,選單列的齒輪圖像上會出現一個藍點,它所在的頁面在側邊欄中也會有一個,控制項本身也會有一個,直到你看過為止。這只會出現在真正從舊版更新過來的人身上:全新安裝時,不會被一堆藍點迎接,因為那些功能本來就是這個 App 的一部分。
上一版新增支援的 Electron App,現在真的能更新了。 0.3.75 讓 DuoUpdater 學會讀取這些 App 內部附帶的檔案,它也確實讀到了:算出了新版本、適合你這台 Mac 的下載項目,以及用來驗證的校驗碼,然後卻沒有地方可以送出這些資訊。安裝按鈕從來沒有出現,而 duo install 給出的拒絕理由也不是真正的原因。這一版把安裝路徑接上了。實際上你可能完全看不出差別:這個讀取器排在每一條手寫規則之後,所以只會為沒有其他規則涵蓋的 App 發言;而以目前來說,在建置這一版的電腦上,這樣的 App 一個都沒有。它的意義在於你明天要安裝、還沒有人為它寫規則的那個 App。
會讓你離開原生建置的下載,現在會被拒絕。 Apple 晶片仍能透過轉譯執行 Intel App,這表示一個僅支援 Intel 的下載可以通過「這能在你的 Mac 上執行嗎?」這道檢查並順利安裝,結果把一個原本以原生方式執行的 App 悄悄換成轉譯執行的副本,而且之後的每次更新都會重蹈覆轍。現在每一次就地安裝都會比對你現有的版本和下載下來的版本,並拒絕這種替換。反方向的替換,或是從通用建置換成 Apple 晶片建置,都是正常的,仍然允許。
無法證明下載是哪種架構時,它現在會選擇不提供,而不是用猜的。 有些廠商會在預設下載之外另外發布 Apple 晶片版本,而要知道預設版本是 Intel 建置,唯一的方法就是注意到另一個版本的存在。如果第二次檢查沒有順利回來(廠商的伺服器拒絕、連線中斷,或兩者對自己是哪個版本說法不一),DuoUpdater 不再把這種沉默當成答案。它會告訴你版本號碼,但不提供安裝,這才是誠實的結果。
損壞的規則不會再從「診斷」中消失。 對於同時由手寫規則和新的描述檔讀取器涵蓋的 App(這是刻意的設計,大多數 App 都是如此),規則的失敗會被讀取器緊接著的成功抵消,因此一個規則其實已經損壞的 App,看起來仍然正常。現在兩者會分開記錄。描述檔讀取器也首次會回報自己的失敗:開發用電腦上有三個 App,原來指向的位址已經回應「找不到」好一段時間了,而以前沒有任何東西會把這件事說出來。
看得到更新但無法安裝的列,現在在兩個地方提供相同的選項。 選單列以前會顯示一個標示為「打開」的按鈕,按下去打開的是 Finder;而視窗對同一列則什麼都不顯示。
以 Electron 建置的 App,現在不需要有人事先登錄就能被辨識了。 許多 Mac App 內部都附帶一個小檔案,說明它的更新放在哪裡。在此之前,DuoUpdater 只認得有人手寫過規則的那些 App;其他的都躺在你的列表裡,旁邊沒有版本號碼,也沒辦法告訴你有新建置推出了。現在它會直接讀取那個檔案,就像它一直以來讀取 Sparkle 的方式一樣,因此這類 App 在你安裝的當天就會被涵蓋,而不是等到有人抽空處理的那一天。
已經有手寫規則的 App 不受影響。 新的讀取器排在它們之後,所以只能填補空缺,絕不會接管原本已經正常運作的東西。
有兩個細節決定它提供的下載是否正確,而這兩者都是實際檢查真實的 App 確認的,不是憑假設。 有些廠商即使同時發布了 Apple 晶片版本,仍把 Intel 建置標為「主要」下載;另一些則給 Apple 晶片建置取了一個和 Intel 版看不出差別的檔名,光看名稱完全無從分辨。DuoUpdater 會依架構挑選;只要無法確定某個下載能在你的 Mac 上執行,它就會告訴你版本號碼但不提供安裝,而不是交給你一個能順利安裝、之後卻打不開的東西。
如果你使用的是某個 App 的 Beta 版、候選版或 nightly 建置,DuoUpdater 以前一直悄悄盯著錯誤的路線。 它判斷你在哪條路線上的方式,是在廠商自己的版本列表中找到你的建置;但當發行前版本沿用它所來自的正式版的公開版本號碼時(這本來就是發行前版本的常態),它比對到的卻是正式版的那一筆。這個錯誤是無聲的:畫面上從未出現任何異常,你只是再也收不到自己路線上的下一個建置,而你看到的版本說明其實屬於正式版路線。這是實際安裝 Supacode 和 TypeWhisper 的發行前建置並觀察結果後才發現的;現在兩者都會跟隨它們真正所在的路線。
CapCut 的 Beta 版顯示了一個你並沒有的版本。 那一列寫著「9.3.4545 → 9.4.0-beta6」,而 CapCut 自己、Finder 和其他所有更新工具都把你這一份稱為 9.4.0-beta5。有些 App 把真正的版本放在和多數 App 不同的欄位中,而 DuoUpdater 在箭頭你這一側讀的是看起來比較整齊的那一個,在廠商那一側讀的卻是真正的那一個。它提供的更新一直都是對的,錯的只有標籤。現在箭頭兩側都取自同一處,CapCut 如此,另外七個以相同方式建置的 App 也是如此。
MacWhisper、GitHub Copilot for Xcode、TypeWhisper 和 OpenUsage 現在會顯示版本說明了。 這四款 App 的說明面板以前都是空的:它們的更新來源完全不帶說明,而這一點直到逐一對照廠商實際發布的內容才被發現。現在 DuoUpdater 會讀取 MacWhisper 自己的版本說明頁面、Copilot 的更新記錄檔案和 TypeWhisper 的更新記錄網站,並讓 OpenUsage 指向它的版本列表。
Helium 現在透過它自己的更新服務更新,因此帶來了 Beta 通道和小得多的下載量。 以前它是透過公開的版本列表追蹤的,而那裡只會出現正式版建置,所以使用 Helium Beta 版的人拿到的一直是正式版。它自己的服務還會發布修補檔,所以一次例行更新大約只要 40 MB,而不是重新下載 124 MB。這已經在正式版和 Beta 版建置上都驗證過,而且在取代任何東西之前,下載的檔案都會先用你已安裝那一份中的簽署金鑰進行驗證。
Firefox 和 Thunderbird 的 Beta 版、開發者版和 nightly 版本現在能正確追蹤了;在此之前,這五條路線從來沒有回報過任何一次更新。 以前它們是從 Mozilla 公開的版本檔案讀取的,那裡只發布顯示給你看的版本號碼,而安裝 Beta 版時會把「b5」去掉:一個 Firefox Beta 版在整個週期中都自稱 155.0,所以唯一被問到的問題是「155.0b5 比 155.0 新嗎?」,而答案是否定的。nightly 版更糟:Mozilla 每天推出一個,而每一個都叫做 157.0a1,所以整整四週的週期裡什麼都沒有回報。現在 DuoUpdater 改為詢問 Mozilla 自己的更新服務,也就是 Firefox 內建更新程式所用、App 本身內部就寫明的那個位址,並比對兩邊都帶有的建置識別碼。推出前已針對全部五條路線的真實下載檔案逐一核對過:該服務回報的識別碼,與你手上那份 App 內部的識別碼逐位元組相同;落後一個建置的 Beta 版現在會顯示它的更新,同一天稍早建置的 nightly 版也會。正式版和 ESR 版從未受到影響,這次也沒有更動。有一項限制值得直說:該服務不公佈發行日期,所以這五條路線的發行記錄,記下的仍是 DuoUpdater 第一次看到某個建置的時間,而不是 Mozilla 推出它的時間。
Wispr Flow、AionUi 和 Devin 現在可以一鍵更新了,不再只是告訴你有新版本。 這三個 App 原本都看得到更新,卻都無法套用,當時寫下的理由是:廠商分別發布 Intel 和 Apple 晶片兩種建置版本,而 DuoUpdater 無從選擇。實際量測而不是憑推測之後,這個理由並不成立:這三個 App 所讀取的端點本來就是 Apple 晶片版,而 DuoUpdater 本身也只在 Apple 晶片上執行——根本沒有需要選擇的時候。接上一鍵更新之前,每個下載檔都先抓下來檢查過:裡面是正確的 App、與你手上那份由同一位開發者簽署、經過 Apple 公證,AionUi 的還與它自己清單裡公佈的檢查碼核對過。接著每一個都在舊版上實際安裝了一遍,裝完確實停在新版本。
Grok Bot 現在也納入追蹤了——包括新版本偵測和一鍵更新。 xAI 這個桌面 App 是由 Anysphere(Cursor 背後的公司)建置和簽署的,更新也走 Cursor 自己的發布服務——這正是常用的途徑全都問不出答案的原因:沒有 Sparkle 更新來源、App Store 上沒有它、沒有公開的發布儲存庫,而 Homebrew 的 cask 又把更新交還給 App 自己。所以 DuoUpdater 改為讀取廠商自己的版本端點。另外還有兩個端點可用,都基於明確的理由沒有採用:x.ai 下載按鈕背後的那個完全不公佈版本號碼;App 自帶更新程式所用的那個,在你已經是最新版時會回傳空白內容——這種沉默與端點故障無法區分。安裝檔在接上之前也檢查過:磁碟映像檔裡是真正的 App,與你手上那份由同一位開發者簽署,並經過 Apple 公證。有一樣東西是刻意不提供的:版本說明。xAI 並未替這個 App 發布任何版本說明,它唯一公開的更新記錄屬於另一個產品。
Comet 和 Msty Studio 現在也能一鍵更新,另外新增一項檢查,專門找出那些本可一鍵更新、卻還沒提供的 App。 這兩個是那一批裡最後還只能看到更新、卻無法套用的,而結果兩者都不需要任何新機制——只需要更仔細地看清廠商實際送來的是什麼。Comet 的下載連結帶有簽章、一小時後就失效,所以更新按鈕改為指向 Perplexity 自己的閘道,連結在你按下的那一刻才產生,而不是在幾小時前執行檢查時就已產生。Msty 在同一個檔案裡公佈了四個下載項目,Intel 版排在第一個,而用來驗證下載內容的指紋,原本取的正是第一筆;現在它會綁定到實際下載的 Apple 晶片版本,這樣一來,在檢查與按下按鈕之間若有新版本發布,通常也會被攔下,而不是被悄悄裝上。兩者在推出前都實際下載過,並通過了真正的安裝把關。更大的教訓與這兩個 App 都無關:DuoUpdater 原本沒有任何機制能察覺某個 App 被設為「僅偵測」、而當初的理由早已不成立,因此每晚的規則巡檢現在會指出:某個只被觀察的 App,其實早已把安裝檔交到我們手上。
QQ音乐 現在也納入追蹤了——包括新版本偵測、一鍵更新,以及在視窗中顯示的版本說明。 常用的途徑沒有一條行得通。它不提供 Sparkle 更新來源,連 Sparkle 框架都沒有;它的 Homebrew cask 把更新交還給 App 自己;而它自己的二進位檔裡指名的更新端點,對我們能提出的任何請求都回傳 200 加上空白內容——這種沉默與端點故障無法區分,所以沒有採用。DuoUpdater 改為讀取廠商下載頁面所依據的資料檔:那個頁面本身只是個空殼,內容全靠這個檔案填入,而這個檔案也是版本說明唯一存在的地方——沒有部落格、沒有 appcast,也沒有逐版本的頁面。因此版本說明直接從這裡讀取,顯示在 DuoUpdater 自己的視窗中,更新則安裝同一份回應裡指名、經過公證的磁碟映像檔。有個陷阱值得記下來:這個檔案裡在目前那筆 Mac 記錄旁,還留著另一筆六年前的 Mac 記錄,所以兩條規則都以 Mac 下載檔名中的版本號碼來定位,而不是靠「Mac」這個字。還有一個限制值得直說:這個檔案只公佈你看到的版本號碼,從不公佈底下的建置編號,所以若廠商重新建置了某個版本、卻沒有更改版本名稱,在這裡是看不出來的。它不會憑空捏造不存在的更新——只是看不見這一種。
TimeMachineEditor 現在也納入追蹤了——包括新版本偵測和一鍵更新。 常用的途徑沒有一條行得通:沒有 Sparkle 更新來源(App 裡既沒有更新來源位址,也沒有會用到它的框架)、App Store 上沒有它、沒有公開的發布儲存庫,而 Homebrew 的 cask 又把更新交還給 App 自己。它有的是一個小小的廠商網站,上面唯一的下載連結在文字中就寫明了版本——Homebrew 自己的版本檢查依靠的也正是這個頁面、用同樣的方式讀取,所以這是廠商有意提供的途徑,而不是瞎猜。更新會以廠商自己的安裝套件進行,而不是單純替換 App,這是刻意的:這個下載檔還會在 App 之外安裝一個背景排程器、一個命令列工具和一個 launch daemon,若只替換 App 本身,就會留下一份新版 App 與一個舊版排程器並存,卻沒有任何東西會察覺兩者不一致。
Little Snitch 現在也納入追蹤了,正式版和 nightly 版本都包括在內。 Object Development 公佈了一個版本檔,它自己的更新程式會以此作為後備——Homebrew 讀的也是同一個——而 DuoUpdater 直接讀取它,因為 Little Snitch 不提供 Sparkle 更新來源,它的 cask 也把更新交還給 App。這兩種版本共用同一個身分,通常這會讓它們無從區分;但這個 App 在版本字串上露了餡:nightly 版本會把這個字完整寫出來(「6.5 nightly (7301)」),正式版則只是單純的「6.4.1」。值得說明這項支援刻意不做的事:它不會替你安裝更新。Little Snitch 在 App 之外還執行著一個系統延伸功能和一個具特權的背景服務,只替換 App 是否會讓它們保持完好,目前還沒有在真機上驗證過——所以 DuoUpdater 會告訴你新版本已推出,並帶你前往廠商自己的下載頁面,而不是拿一個網路過濾器來賭運氣。
Carbon Copy Cloner 現在也納入追蹤了,而且它不會試圖讓你跨越主要版本。 Bombich 同時維護著三代產品——CCC 5、6 和 7——三者向系統回報的都是同一個身分,這讓「有沒有更新的版本?」成了一個比表面上棘手的問題:7.1.6 確實排在 6.1.13 之上,但跨代需要另外購買,而且 CCC 7 需要的 macOS 版本,可能比一台 CCC 5 的 Mac 目前執行的還新。若只看版本號碼來回答,就會永遠告訴每一位 CCC 5 和 6 的使用者有免費更新在等著,而且每一次都是錯的。所以 DuoUpdater 會從各代各自的位址讀取,只提供你實際擁有的那一代裡的下一個版本。Beta 版也會一併偵測,供那些在 CCC 內開啟了 Beta 版的人使用。目前僅限偵測——CCC 會在 App 旁安裝一個具特權的輔助程式,這比目前已提供的一鍵更新涉及的範圍更大——另外,過去為什麼沒有任何工具看得到這些更新,原因也值得記下來:這個 App 自己的更新來源對每個請求都回應成功,內容卻是一整頁空白。
你的 Mac 實際上無法執行的更新,不會再被下載和安裝了。 每個 App 套件都會寫明它能啟動的最低 macOS 版本,有些廠商還會寫明最高版本——意思是「這個建置版本不適用於那麼新的作業系統」,還沒跟上 macOS 27 的 App 就是這樣表明的。DuoUpdater 過去兩者都不讀。透過 Sparkle 更新來源追蹤的 App 原本就會遵守最低版本,但它們只是少數:在一台典型的 Mac 上,從廠商自己的端點或 GitHub 發布版本讀取的 App 佔了一大半,而 GitHub 的發布版本在任何地方都不會寫明 macOS 需求。因此,一個已經要求比你更新的 macOS 的 App,可能會被提供更新、完整下載、替換上去——然後打不開。DuoUpdater 現在會從下載下來的 App 本身讀取這項需求,就放在既有的「是否為你的處理器所建置」檢查旁邊,並拒絕替換,而不是把一份能用的 App 換成一份打不開的。在更新來源有公佈上限的情況下,廠商寫明的上限也會被遵守,所以開發者已標示為不適用於你的 macOS 的建置版本,一開始就不會被提供。有三個限制值得直說。從下載檔中讀取需求,代表下載已經發生了——這能避免裝壞,但省不了流量,因為大多數 App 並沒有更早的地方可以詢問。如果某個 App 的最新版本你的 Mac 無法執行,它會一直顯示那個更新,也會一直拒絕安裝;拒絕時現在會說明它需要哪個 macOS 版本,但 DuoUpdater 還不會記住這個結果並停止提供。此外,這項檢查只涵蓋 DuoUpdater 自己替換 App 套件的途徑——安裝套件會把檔案交給 macOS,由 macOS 執行套件自己的需求;Homebrew 和 App Store 則各自挑選建置版本;來自 App Store 中 iPhone 與 iPad 區的 App 會被刻意略過,因為它們寫明的是 iOS 版本,拿它與 macOS 比對,比不檢查還糟。還有一件事它目前還不會明說:若開發者把某個建置版本標示為不適用於你的 macOS,那個更新就只是不會被提供,App 會顯示為已是最新版本,而不會解釋原因。對較舊的 Mac 來說,等你升級 macOS 的那天問題就自然解決了。但對於太新的 Mac 就不會,而那種情況理應得到比沉默更好的交代。
百度网盘 現在也納入追蹤了——包括新版本偵測、一鍵更新,以及在視窗中顯示的版本說明。 常用的途徑一條都不通。它不公佈 Sparkle 更新來源;Homebrew 的 cask 無法代表手動安裝的那份 App;而它自己的套件裡指名的更新清單已經失效——那個檔案、它的 arm64 版本,以及存放這一切的目錄,全都回應 404。所以 DuoUpdater 改為讀取廠商下載頁面所依據的端點,並安裝同一份回應裡指名、經過公證的 arm64 磁碟映像檔。它的版本說明也是用同樣的方式取得:「版本更新」頁面有一個 Mac版 分頁,但頁面本身是空的,內容由 API 填入,所以 DuoUpdater 直接讀取那個 API,把最近四十個版本顯示在自己的視窗中。有一個限制值得直說:這個來源只公佈你看到的版本號碼,從不公佈底下的建置編號,所以若廠商重新建置了某個版本、卻沒有更改版本名稱,在這裡是看不出來的。它不會憑空捏造不存在的更新——只是看不見這一種。
檢查失敗時,現在會明說失敗了,而不是顯示一片空白。 對於只有廠商自己的端點才能回報版本的 App,「沒有答案」原本只有一種表示方式:一條空白的橫線,而沒有任何支援的 App 看起來也是這樣。因此廠商的端點逾時或搬家,看起來就跟「DuoUpdater 不追蹤這個 App」一模一樣——一個看似永久的結論,沒有「重試」可按,也不會計入「N 個 App 無法檢查」的數量。現在這些會被視為檢查失敗,而它們本來就是:有「重試」按鈕、計入數量,並說明原因。刻意沒有涵蓋所有情況:你的 Mac 根本無能為力的狀況——你所在的發行路線沒有對應的規則、端點需要一個這台電腦沒有的身分——仍維持原本那條安靜的橫線;而連續三輪都失敗的檢查,也仍會從橫幅中退出,而不是永遠佔據那裡。由 JetBrains Toolbox 安裝的 App,即使背後的版本讀取失敗,也會保留「打開 Toolbox」按鈕,因為無論如何,打開 Toolbox 都是正解。
「重新開啟即可套用」的提醒,在你重新開啟之後就會消失了。 當某個 App 自己的更新程式在背景下載了一個建置版本時,DuoUpdater 會告訴你,並在通知上提供「重新開啟」按鈕。按下它是有效的——App 以新的建置版本回來,並顯示「目前執行的是 1.0。」——但被它取代的那則提醒卻還留在通知中心底下,仍在要你重新開啟一個你剛剛才重新開啟過的東西。DuoUpdater 原本是在等提醒不再需要的那一刻,而它檢查的時間點,偏偏是早已處理過的那一刻。現在只要沒有待處理的重新開啟,它就會把提醒撤下,這也會一併清除某個 App 在 DuoUpdater 沒有執行時自行套用更新後所留下的提醒。
在同一個版本號碼下推出多個建置版本的 App,現在各處都能正確處理了。 一個 Mac App 帶有兩個版本字串:一個是顯示給你看的(「1.0」),另一個是真正會遞增的建置編號。大多數 App 兩者都會更新,但有些只更新建置編號:Amp 一天之內推出了十個建置版本,每一個都叫 1.0;Surge 有四個不同的版本都以 6.9.0 發布;JetBrains 的預覽版建置也是如此。DuoUpdater 在大約十幾個地方都是比較顯示的版本來判斷「有沒有變化?」,而對這些 App 來說,無論實際發生了什麼,這種比較永遠只會回答「沒有」——或是在問「兩者相同嗎?」的地方,永遠回答「相同」。以下就是它造成的問題。這些全都源自同一個錯誤,是 Amp 讓它浮上了檯面。
「重新開啟」不會再看似卡住三分鐘,然後回報一個根本沒發生的失敗。 對一個自己的更新程式已備好建置版本的 App 按下「重新開啟」,會轉圈三分鐘,然後說失敗了——而實際上更新在一兩秒內就已套用,App 也早已以新的建置版本重新打開。DuoUpdater 一直在等顯示版本發生變化,但那個變化永遠不會來。在 Amp 上量到的是:一次不到一秒的替換,換來 189 秒的轉圈。現在不到一秒就能確定結果,真正的失敗也仍會如實回報。
「重新開啟」不再提供一個已經過時的建置版本。 當某個 App 的更新程式已下載了一個建置版本,而開發者後來又發布了另一個時,DuoUpdater 仍然提供「重新開啟」——於是你重新開啟後,立刻就落後了一個建置版本,而這正是那項檢查當初要防止的情況。這些列現在會改為提供更新,去抓取目前的建置版本。
略過某個版本,不會再讓一個 App 永遠沉默了。 這是最值得知道的一項。「略過此版本」本意是拒絕某一個版本,讓下一個版本照常出現。但它只記錄了顯示的版本,所以對於在多個建置版本間沿用同一個版本名稱的 App,略過一次就會拒絕之後所有的版本——而且是永久的,重新啟動後依然如此,螢幕上也沒有任何提示告訴你這個 App 已經不再出聲。現在「略過」會記錄它所拒絕的建置版本。這次修正帶來一個後果:先前版本的 DuoUpdater 略過的版本,會再次向你提供一次,因為舊的記錄無法說明它指的是哪一個建置版本。再略過一次,就會被正確記錄下來。
看起來什麼都沒改變的更新,現在又能還原了。 工作台會隱藏什麼都不會做的還原——而對這些 App 來說,每一次還原看起來都什麼都不會做,所以在一次真正的更新之後,那一列就消失了,而一份完整的備份明明就在磁碟上,卻沒有任何方法能用到它。
重新開啟的提醒又恢復成每個建置版本只通知一次,而且在開發者又推出新建置版本時也不會失效。 0.3.69 把每五分鐘重複一次的提醒,換成每個已備妥的建置版本只通知一次——但它用名稱來辨識建置版本,所以對這些 App,它只通知了第一個,之後的每一個都悄然無聲。這個問題已修正。另外:如果某個 App 因為有未儲存的內容而拒絕結束,DuoUpdater 會記下一筆,等你回應後再把它重新打開。過去如果在你猶豫的期間開發者又發布了另一個建置版本,這筆記錄就會被丟掉,導致替換完成後 App 一直關著,沒有任何東西會把它重新打開。現在這筆記錄會跟著新的建置版本走。
失敗的 App Store 更新,不會再被悄悄記錄為成功,已下載的安裝套件若是較舊的建置版本,也不會再被當成目前提供的那一個。兩者都源自同一個比較。發行記錄過去也把一個 App 的十個建置版本算成一次發布;從現在起會分開計算,不過已經記錄下來的歷史無法補回。
列本身就會說明重新開啟會套用哪個建置版本。 它原本寫著「1.0 → 1.0」——一行完全看不出任何差異的文字。現在會寫成「1.0 (129) → 1.0 (130)」,而且只在建置版本才是變化所在時才這樣顯示;如果版本名稱本來就不同,這一行維持不變。同樣的修正也套用到「重新開啟」的提示文字、通知、說明為何延後安裝的註記,以及 duo install 的拒絕訊息。
微信输入法 和 豆包输入法 又能一鍵更新了——而且更新方式與它們自我更新的方式相同。 輸入法的一鍵更新在 0.3.25 推出當天就撤回了,因為有人的輸入法設定不見了。當時的問題出在安裝的方式:它替換了整個 App,就像首次安裝程式那樣。輸入法是依照其 App 的位置向 macOS 註冊的,而這兩個 App 自我更新時都不會動到那個位置——它們保留 App 本身,只替換裡面的內容。DuoUpdater 現在也這麼做,所以已註冊的 App 在更新後仍是同一個 App,任何一個環節失敗,你原本在用的那份都會原封不動地留在原處。而且兩者都不會再向你要密碼了。
在更新它們之前,會先把它們學到的一切複製起來。 你的詞庫、設定和帳號狀態並不存放在 App 裡面,所以 DuoUpdater 原本保留的還原副本無法涵蓋它們。現在每次更新輸入法之前都會先為它們建立快照,並在還原時一併放回,而且這份副本幾乎不佔成本——其中一個的 578 MB 只需要十分之一秒,也幾乎不佔磁碟空間。這是一張安全網,而不是修復手段:App 下次啟動時要怎麼處理你的資料,是 App 自己的程式碼在執行,沒有任何東西能擋在它前面。但現在至少有了可以回頭的地方。
更新不會再悄悄剝奪 App 自我更新的能力。 當 DuoUpdater 需要管理者權限來替換 App 時,它會還原 App 的擁有者,卻沒有還原 App 安裝時的權限——而下載檔解開時的權限,通常比安裝程式設定的更窄。對這兩個輸入法來說,這就是它們自己的更新程式能不能完成下一次更新、還是連善後都做不到的差別。現在 App 安裝時的權限會一併帶到取代它的那一份上。
DuoUpdater 不會再每五分鐘就要你重新開啟同一個 App。 會自我更新的 App 會把新版本放在磁碟上,等你結束它。DuoUpdater 每次檢查都會注意到這一點,並且每次都發出一則提醒,所以一個你決定暫時不重新開啟的建置版本,會在你擱置的期間一直嘮叨下去。現在每個建置版本只會通知一次:重新開啟、略過它,或忽略這個 App,它就會安靜下來,下一個建置版本則照常通知。
等待重新開啟的 App 會算作更新,而被忽略的 App 則完全不再計入。 已經躺在磁碟上的新版本本身就是一個更新——只是提早下載好了——所以它現在會計入標記和「N 個可用更新」那一行,而不是另外放在旁邊單獨計數。被忽略的 App 則是反過來:一個你已經忽略的 App 可能會讓標記亮起,但它那一列只顯示一個淡色的「已忽略」標籤,沒有任何按鈕可按,所以那個數字指向的是你無法處理的東西。現在「忽略」和「略過」在提醒、標記和列表這三個地方的意思都一致了。
搜狗输入法 現在也納入追蹤了——方法是詢問它自己的更新程式,而不是讀取它的網站。 搜狗的更新記錄頁面公佈的是三段式版本號碼,而安裝的那份卻有四段,所以拿它來比較,就得先把真正的版本號碼刪短。它自己的更新檢查則是用套件自身的編號方式回答。DuoUpdater 以 App 本身的方式去詢問那個端點,並從中讀出版本,四段完整保留,所以只改了最後一段的重新建置也看得到。這一項僅限偵測:搜狗的更新程式做的遠不只是替換 App——它會重新註冊一個「快速查看」產生器、把你的資料搬到新位置,還會強制結束輸入法——所以更新本身就交給它處理。有一件事值得知道,而且是搜狗造成的、不是我們:它們的端點會依照發出詢問的 macOS 版本來回答,一台 macOS 28 的 Mac 以自己的身分去問,拿到的是 2023 年的建置版本。DuoUpdater 不會以自己的身分去問,所以在每一台 Mac 上看到的都是目前的版本。
若後來的修正能正確解析某些版本說明,這些說明現在會重新讀取,而不是永遠維持錯誤的樣子。 版本說明是按版本快取的,理由是已發布版本的說明永遠不會改變。這對說明本身來說沒錯,但對 DuoUpdater 能從中擷取出來的內容就不是這樣了,所以某個 App 的說明一旦被解析得亂七八糟,那個版本就一直卡在那個樣子,不管後來推出了多少解析修正都一樣。現在每一筆快取項目都會記錄是哪一代擷取邏輯寫入的,由較舊一代寫入的項目會重新抓取。兩個快取都是如此——App 的更新記錄,以及 Homebrew formula 的說明。
在某個 CDN 上失敗的檢查,會再詢問一次,才判定為故障。 502、503 和 504 都代表中間的某一層無法連到後面的伺服器:請求根本沒有送達,所以稍後再送同一個請求,通常就會成功。Headlamp 的檢查就正是死在這裡——GitHub 回應了 504,而且完全沒有速率限制的標頭。這三種現在會在十分之八秒後重試一次。刻意只限這三種:500 是伺服器本身出錯,重試多半只會重現同樣的錯誤;而對速率限制重試,只會耗掉它正在抱怨的那份額度。
Homebrew formula 的版本說明,會跟著它所屬的版本走。 版本說明原本是按 formula 記住的,而不是按版本,所以某個 formula 的說明一旦載入過,在這次工作階段剩下的時間裡,之後每次查看它都會拿到第一個版本的說明——即使已經出現了更新的版本也一樣。讀取說明過去還會佔用整個 formula 列表都在排隊等待的那個佇列,所以一個緩慢的 brew info,就可能讓它周圍的列表跟著停住。
在工作台的側邊欄按右鍵點一個 App,就能打開它。 就是選單列那些列一直以來都有提供的「打開」,現在也出現在你更可能想用它的這個視窗裡。
丟棄已下載的安裝套件時,它留下的錯誤也會一併清掉。 丟掉一個已備妥的套件,原本就會讓那一列回到「更新」,但你剛剛才取消的那次嘗試所留下的紅色失敗訊息,卻仍然留在那一列上——而且永遠不會有東西清掉它,因為錯誤只有在那一列變成最新版本之後才會撤下,而一列仍在提供更新的,永遠不會變成最新版本。現在它會跟著下載檔一起消失。
duo 這個選用的命令列工具,不再錯誤地描述自己。 --timeout 已移除——它會被接受,也寫在說明文件裡,卻沒有任何地方讀取它。--budget 首次寫進說明文件,而唯一提到它的那則訊息,現在會顯示真實的分鐘數,而不是永遠寫「15」。--max-calls 宣稱預設值是 20,實際上是 6。數字參數如果收到的不是數字,現在會視為錯誤:duo verify --max-concurrency 1x 過去會被忽略,然後以預設的 4 執行巡檢,對一個刻意想放慢巡檢速度的人來說,這恰好適得其反。另外,duo verify 現在會計算它實際發出的請求:一個先回應 502、重試後才成功的更新來源,不會再在悄悄問了兩次的情況下,回報一個乾淨的 ok。凡是能用 --help 的地方,-h 也都能用。
又有四個 App 納入追蹤、新增兩個發行通道,而且每一個都能就地更新。 CapCut、Canva,以及在兩個網站上各自發布的 WorkBuddy——國際版和中國版各自有獨立的發布週期,所以一份安裝只會被提供屬於它自己網站的版本和版本說明。Termius 和 VSCodium 除了已支援的正式版之外,也新增了它們的 Beta 和 Insiders 通道。CapCut 的第二條路線比較棘手:它是由 CapCut 內的一個開關選擇的,完全不會反映在版本號碼上,所以你在哪條路線上,是從磁碟上的那份 App 讀出來的,而不是從你剛好在執行的建置版本去推測。如果你勾選了那個選項,值得知道一件事:DuoUpdater 會在最新的 Beta 版一出來就提供給你,這可能會比 CapCut 自己分階段推出的時程更早送到你手上。
Android Studio 的預覽通道,不會再提供比你現有版本更舊的建置版本。 Google 是按發布順序而不是按版本排列它的版本清單,而 DuoUpdater 在這份清單中搜尋了三次——一次找版本、一次找日期、一次找下載檔——所以一個在較新的 Canary 之後才發布的候選版,可能讓版本號碼來自其中一筆、檔案卻來自另一筆。8 月 26 日,Canary 通道回報的是 2026.1.4 RC 2,而 2026.2.1 Canary 2 早已推出。現在這幾個答案都必須來自同一筆項目。
重新建置的版本,不會再安裝它本來要取代的那一份。 當一個專案在不更改版本的情況下重新建置某個版本時,兩個檔案都會留在同一個標籤下——KeePassXC 就在原本的 KeePassXC-2.7.11-arm64.dmg 旁邊放了 KeePassXC-2.7.11-1-arm64.dmg——而 DuoUpdater 過去會取排在第一個的那個。那個順序是按字母排列的,這次剛好把重新建置的那個排在前面,下次就可能排在後面。這件事完全看不出來:列上的版本是對的,只有背後的檔案錯了。現在會依照檔名本身寫的內容來挑出正確的那一個。
nightly 版本和快照版本,現在會被正確辨識為它們實際所是的發行前版本。 VLC 的 nightly 版本、KeePassXC 的快照版本以及 Freelens 的 nightly 版本,安裝時都使用正式版 App 的名稱和套件識別碼,所以唯一能看出它們身分的只有版本字串——而過去並沒有讀它。它們被當成正式版的安裝,這正是一個 nightly 版本會被提供一般版本、並拿來覆蓋自己的原因。DB Browser for SQLite 的 nightly 版本基於同樣的原因,改從 App 自己的檔名讀取,而那裡的問題是真實存在、只是被藏住了:它凍結的版本號碼只有在正式版追上之前才會排在目前正式版之上,一旦追上,每一個 nightly 版本的安裝都會被悄悄換成正式版。
重新開啟 App 後,會如實回報實際發生了什麼。 有少數 App 會在自己的套件裡再放一個 App——Surge 就把它的 Dashboard 放在那裡——而當只有那個內層 App 需要重新開啟時,DuoUpdater 會說外層 App 已重新開啟,但其實什麼都沒發生。現在它會回報內層 App 自己的結果,也不會再為一次根本沒發生的重新開啟發出通知。
已經下載好的更新會明白說出來,也可以撤回。 一列若持有已下載完成的安裝套件,過去只會顯示一個藍色的「安裝」按鈕,別無其他——沒有任何說明表示下載已經完成,也沒說這個按鈕會重新打開安裝程式、而不是從頭開始更新,更沒有辦法改變主意。現在這些資訊直接顯示在列上,而不是藏在提示文字裡;在列的右鍵選單中選擇丟棄已下載的安裝程式,就會丟掉下載檔,並讓那一列回到「更新」。過去若在同一個 App 的安裝正在進行時丟棄,也可能會丟掉一個剛剛才下載完成的套件;現在不會了。
指向已停用頁面的版本說明按鈕,又能打開有效的頁面了。 Microsoft 重新命名了 Edge 企業版的版本說明頁面,Termius 也搬移了它的更新記錄,所以 Edge 的三個通道和 Termius 一直把你帶到已經不存在的頁面。過去從來沒有東西檢查這些頁面是否還在,這就是為什麼它們可以一直失效下去、而其他一切看起來都很正常——現在它們會定期接受檢查。Edge 的 Dev 通道則完全不提供按鈕:Microsoft 已不再為它發布版本說明,而所有仍然存在的頁面,都屬於另一條發布週期。
Raycast 會跟到第 2 版,但只限能執行它的 Mac。 Raycast 2 需要 macOS Tahoe 和 Apple 晶片。不符合條件的 Mac 會留在第 1 版的發布週期,也不會再被告知一個它根本無法安裝的版本。兩條發布週期現在也各自保有自己的版本說明,所以第 1 版的安裝會讀到第 1 版的歷史,而不是第 2 版的。
更多 App 的版本說明,現在會顯示在 DuoUpdater 自己的視窗中。 包括 BetterDisplay 的全部三條路線、Shotbase,以及兩個網站的 WorkBuddy。BetterDisplay 的版本說明結尾不會再出現它下載按鈕的原始標記碼,也不會在每一個版本裡重複它的貢獻者名單。
duo 這個選用的命令列工具,會拒絕它看不懂的命令列。 打錯的參數過去會被忽略,而被忽略的參數看起來就像你根本沒傳——所以 duo verify --githubb 會悄悄檢查所有規則,而不是你要的那一個。未知的參數、缺少值的參數,以及多餘的引數,現在都會視為錯誤,並列出該命令實際接受哪些參數;而且任何命令後面都能加上 --help。
BetterDisplay 的發行前版本和內部建置版本,現在會提供給有要求的人了。 BetterDisplay 把它的三條發布週期全放在同一個更新來源裡,並用它自己設定中的兩個開關來選擇。DuoUpdater 過去只能從你剛好在執行的建置版本推測你所在的週期,而這完全反映不出你已經開啟、但還沒有實際動作的開關——所以一個兩個開關都已開啟、卻還停在正式版的人,會被告知已是最新版本,而 BetterDisplay 自己的更新程式卻已經在提供一個領先四個版本的更新。現在會直接從 BetterDisplay 讀取你實際的選擇,包括開啟內部建置版本時,一般發行前版本也會持續提供的情況。它那些僅支援 Apple 晶片的預覽建置版本則被刻意排除,所以 Intel Mac 永遠不會被提供一個它無法執行的版本。
連續兩次切換某個 App 的發行通道,不會再讓那一列卡在舊的結果上。 切換通道開關會讓 DuoUpdater 重新檢查那個 App,而過去如果在第一次檢查還在進行時又切換了一次,第二次就會被丟掉——那一列會繼續向一個剛剛才關掉 Beta 版的人提供 Beta 版,直到某件不相干的事剛好觸發了另一次檢查為止。現在較新的切換會接手較舊的那一次,而一次被中途打斷的檢查,會把它還沒處理到的 App 標記給下一輪,而不是把它們記成已處理。切換後,各列也會更快進入檢查中的狀態,因此螢幕上顯示的結果已被你的開關推翻、卻還沒更新的時間也縮短了。
住在另一個 App 裡面的 App,現在會跟著它一起結束和重新打開。 有少數 App 會在自己的套件裡再放一個完整的 App——Surge 就把它的 Dashboard 放在那裡。macOS 把它視為另一個獨立的 App,所以在外層 App 套用更新時,並沒有任何東西會把它關掉:它會繼續執行剛被取代的那個版本,而且是從一份已經不在原位的副本執行,從那時起它就無法再與它所屬的 App 溝通。現在這些 App 會跟著它們所在的 App 一起結束,並在其之後重新打開,而你當時實際在使用的那個視窗,會是回到最前面的那一個。只有你自己能打開的 App 才會這樣處理;App 自己在背後執行的隱形輔助處理程序則不會去動。
悄悄停止建立的還原點,現在又會建立了。 App 可能會鎖住自己的某個檔案,讓任何東西都無法刪除它,而為了還原所保留的副本也繼承了那個鎖——於是它永遠無法被下一份取代,只要一次嘗試被中斷,就可能留下一份沒有任何東西能清掉的副本。從那之後,那個 App 的每一次更新都在沒有退路的情況下進行,而且只用一行不說明原因的文字交代過去。開發者自己的 Mac 上就有一個 App 處於這種狀態達兩天之久。現在為還原所保留的副本不會再帶有那個鎖,已安裝的 App 則完全維持它開發者當初的設定,而一份卡住的副本也不會再擋住本該取代它的那一份。
成功的更新,不會再被回報為失敗。 macOS 可能會先把新版本放到位,然後在移除被替換下來的舊版本時失敗。過去這會被照單全收:DuoUpdater 會說某個 App 沒有更新,但其實已經更新了;或是說沒有還原點,但備份存放區裡明明有一份完整的——而在第一種情況下,它還會叫你去授予一項你早已授予、而且無論如何都幫不上忙的權限。現在在回報任何事之前,都會先檢查磁碟上實際發生了什麼。
版本那一行,不會再重複版本號碼裡已經包含的數字。 當某個 App 在等待重新開啟時,那一列會顯示你正在執行的版本,以及重新開啟後會換上的版本。兩邊都帶有建置編號,當開發者在同一個版本名稱下推出多個建置版本時,靠的就是它才看得出差異——但當版本本身已經不同時,它就純屬雜訊。Chrome 那一行把寬度都花在印「151.0.7922.174 (7922.17…」上,還沒印到真正改變的數字就沒地方了。現在只有在建置編號才是變化所在時,才會顯示它。
重新打開 App 以完成更新,現在統一用同一個詞。 自己的更新程式已經把新版本放到位的 App,在介面上寫的是「Restart」;把新版本先放在一旁、等你結束後才替換的 App,寫的則是「Relaunch」。這個差異在背後確實存在,但對你來說毫無差別:兩種情況都是按同一下,而且即使你從來不按,結果也一樣,因為下次你結束 App 時,更新無論如何都會完成。現在一律顯示為「重新開啟」——包括通知、提示文字,以及替你自動處理這件事的那個設定——這也是 Chrome、Claude 和大多數會自我更新的 App 擺在你面前的用詞。只有英文曾經為此用了兩個詞;德文、日文、俄文和中文一直都只用一個,西班牙文和法文則改用它們在其他地方早已使用的措辭。
需要管理者權限的更新,現在會向你要求權限,而不是失敗之後怪罪到某項權限上。 替換 App 時需不需要密碼,過去是看 App 所在的資料夾來決定的,而不是看 App 本身。凡是 macOS 以 root 身分安裝的東西——每一個 App Store App,以及任何由安裝套件放置的 App——都會通過那項檢查,走上不需要密碼的路徑,而且根本不可能完成:移除舊版本需要對它內部目錄的寫入權限。macOS 回報這種拒絕時,用的代碼和拒絕「App 管理」權限時相同,所以失敗出現時,變成了要你去授予「App 管理」權限,而那永遠幫不上忙,因為真正的障礙是檔案的擁有權。在一台一般的 Mac 上,這涵蓋了每一個 App Store App,外加少數其他 App,而且受影響的是管理者帳號——標準帳號本來就已經被導向正確的路徑。這些更新現在會走行得通的路徑,而且 App 會保留它原本的擁有者,而不是悄悄變成屬於你的。
App Store 的 App 現在也會像其他 App 一樣建立還原點。 備份過去會跳過它們,理由是商店隨時都能把先前的建置版本抓回來。但它不能——App Store 只提供 App 的目前版本——這讓商店成了唯一一條套用更新後無法撤銷的途徑。現在這些 App 在更新前會像其他 App 一樣備份,而且由於副本是以複製(clone)方式建立的,在更新真正取代原始檔之前,它幾乎不佔磁碟空間。還原時會說明商店特有的情況:更新會立刻重新出現在「更新項目」列表中,如果開啟了 App 自動更新,還會自行重新套用。而當某個商店更新本來就不可能被套用時——在 Mac 上執行的 iPhone App、你所在地區沒有販售的項目——也不會再建立還原點,所以一列不會再出現「還原到它目前正在執行的版本」這種選項。
Longbridge Desktop 現在也納入追蹤了,兩條發布週期都包括在內。 正式版和預覽版都各自支援版本偵測、一鍵安裝官方的 Apple 晶片建置版本,以及在 DuoUpdater 自己的視窗中顯示含插圖的版本說明。
WhatsApp 的版本說明,現在在它的 App Store 檢查還在進行時就會出現。 那個視窗中顯示的 App Store 頁面,過去被歸檔在一個查詢永遠比對不到的名稱之下,所以版本說明面板一直是空的,而不是顯示那個頁面。
選單頂端用更少的空間容納了更多內容。 DuoUpdater 自己的版本現在就顯示在名稱旁邊,按一下就能打開它的版本說明。「全部更新」移到了獨立的一行,讓翻譯後的標籤有空間完整顯示,底部的動作則改成圖像。過去宣告 DuoUpdater 正在自我更新的橫幅已經移除——改由版本旁邊的閃光圖像亮起,並一直亮著,直到你讀過有什麼改變為止。太長而放不進選單的狀態行,現在會以刪節號結尾,而不是把選單撐寬。
設定不再把它提供的更新選項截斷了。 決定如何套用更新的那兩個選單,在好幾種語言中都會截掉自己的標籤。現在措辭更精簡了,文字仍需要時也會換到第二行。
「新功能」會標出每個版本的推出日期。 側欄中的每個版本現在都附有發布日期。
依架構區分的更新,現在會選擇這台 Mac 實際能執行的建置版本。 有少數 App 會在同一個 Sparkle 更新來源中把同一個版本發布兩次——一次給 Apple 晶片、一次給 Intel——而 DuoUpdater 過去是看哪個下載位址剛好排在前面來決定。現在它會讀取更新來源中的硬體需求,若廠商沒有填寫,就看檔名中的架構。原生的建置版本會穩定勝出,這台 Mac 無法啟動的建置版本也不會被提供。GitHub 的發布版本也採用同樣的處理,而且不會只因為最新的檔案是針對另一種架構,就把一個完全正常的規則判定為故障。
ChatGPT 的更新檢查,現在會依照帳號所屬的推出路線進行。 OpenAI 有時會讓商業版和企業版帳號停留在較早的桌面建置版本,同時先把新版本推給個人帳號。DuoUpdater 過去在那項檢查中沒有帶上帳號的方案,結果等於悄悄替每個人選了保守的路線:它可能會說已安裝的那份不知為何比較新,而 ChatGPT 自己早已在下載更新的建置版本;或是提供一個 App 自己的更新程式又會再次取代的建置版本。現在它會從 ChatGPT/Codex 的登入狀態中取得方案標籤,並隨著與 App 相同的更新請求一起送出。如果無法取得這個標籤,就維持保守的行為;登入憑證本身永遠不會放進請求或診斷資訊中。
macOS 始終沒有回應的重新開啟,不會再卡住之後所有的更新。 Launch Services 偶爾會接受重新打開已更新 App 的請求,卻從此不再回呼。那一列會永遠停在「正在重新開啟…」,它的「重新開啟」按鈕一直無法使用,DuoUpdater 自己的更新也卡在它後面等著。現在一個超過一分鐘都沒有回應的啟動,會被判定為失敗並釋放,那一列就能恢復,更新程式的其餘部分也能繼續運作。
快速拖曳捲軸時,發行記錄不再會變成一片空白。 舊的延遲載入堆疊,在長距離跳躍時可能會跟不上,在新位置周圍的列建立完成之前,短暫留下一個空白的視窗。發行記錄現在改用可重複利用列的清單,能直接跳到目的地。重新整理的控制項在箭頭和轉圈圖像之間切換時,也會保持相同的大小,所以開始檢查時,最底下那一列不會再抖動。
下載流量現在會標出使用了二進位修補檔的更新。 新的下載會記錄實際完成的路徑——而不只是有沒有提供修補檔——並在歷史記錄中帶有「增量」標記。0.3.62 那些明顯較小的修補檔下載也會被辨識出來,即使它們是在流量帳本還沒有路徑欄位之前記錄的。
只要開發者以這種方式發布,更新現在只會下載有變化的部分。 有些 App 會在每個版本旁附上一個小修補檔——足以把你現有的版本變成新版本,而不必重新抓取整個 App。DuoUpdater 過去會忽略這些,每次都下載完整的套件。現在只要有修補檔與你目前的建置版本完全吻合,它就會採用修補檔。ChatGPT 上一次的更新只需 1.9 MB,而不是 605 MB;Docker 的則是 87 MB,而不是 582 MB。無論哪種方式,結果都是完全相同的 App——同樣的簽章、同樣的位元組,在推出前都已與完整下載比對過。當沒有吻合的修補檔,或修補檔套用失敗時,會照舊進行完整下載,所以不會有任何東西因為這項改動而裝不上。
DuoUpdater 不會再去下載一個 App 自己已經在下載的更新。 許多 App 也會自我更新,而我們兩邊同時去抓同一個 600 MB 的檔案,會讓你付出兩倍的流量。DuoUpdater 現在會察覺正在進行的下載並不去打擾,同時在那一列上說明,而不是悄無聲息地什麼都不做。如果那個下載最後被放棄了,十分鐘後它就不再算數,所以不會有任何東西一直被卡住。
App 已經準備好的更新,不會再被覆蓋。 會自我更新的 App 經常在背景下載,然後等你結束它們,才把新版本替換上去。在這種 App 上安裝,看起來是成功了,但一結束 App 就被還原——而如果 App 自己待套用的版本比 DuoUpdater 剛裝上的還舊,你最後反而比一開始更落後。現在這些更新會讓它們自己完成,無論它們帶的是哪個版本。
DuoUpdater 自己的更新也變小了。 它的版本現在也附有同樣的修補檔,所以從較近的版本更新時,只需抓取幾百 KB,而不是 11 MB。
更新留下的說明文字,現在會自行清除。 當 DuoUpdater 把某個 App 交給它自己的更新程式時,那一列會加以說明——「已將它移到最前面,讓它自己的更新程式套用更新」。這句話過去會永遠留在那裡:唯一能移除它的,就是對同一個 App 再開始另一次更新,所以更新早已完成好幾個小時之後,它還掛在那一列底下,描述一件早就結束的事。現在只要 App 變成最新版本,它就會消失。至於「更新套用時沒有還原點」的警告則刻意保留,因為它描述的是已經發生的那次更新,而且要等更新結束後才開始有意義。
被拒絕的更新,現在會用你的語言說明原因。 當有其他東西已經在安裝時——DuoUpdater 正在處理一批更新,或是終端機裡的 duo——那一列會告訴你。那則訊息過去只有英文,放在一個其他部分都已翻譯的視窗中,而且結尾還帶著一個處理程序編號:在終端機裡有用,在選單裡卻是你無從處理的東西。現在它已經翻譯,而且說明你該怎麼做,而不是誰持有鎖定。命令列工具仍會印出處理程序編號,在那裡你才能拿它做點什麼。
發行規律那一行和它的時鐘,現在會依照你的地區設定。 「發行記錄 → 規律」原本把一切總結為「Most often ships Friday, around 6 PM」。星期名稱有詞形變化的語言,沒辦法把星期直接塞進句子中間,結果用錯了詞形——俄文寫成「Чаще всего выходит пятница」,其實應該是「по пятницам」。現在它改成標籤形式:「高峰:星期五,約下午 6 點」,在任何語言中都正確。時間也跟著調整——如果你的 Mac 使用 24 小時制顯示時間,這裡也會一樣,圖表的座標軸也是。
設定搜尋現在看得懂你螢幕上的文字了。 設定側邊欄上方的搜尋欄位,會比對一份以英文撰寫、從未翻譯的額外詞彙清單,所以「rollback」能找到「一般」,「Zurücksetzen」和「回滚」卻什麼都找不到。現在這些詞彙在 DuoUpdater 支援的每一種語言中都有了。英文詞彙也仍然在每一種語言中都能用,因為它們所源自的說明文件是英文的。
開啟「顯示全部」不會再讓選單遲疑一下。 列出你所有 App 的完整清單,過去每次出現時都要整個重新量測一遍——在一台有 127 個 App 的 Mac 上,這大約要花一秒,而且幾乎全花在排版那些遠在可見範圍以下的列上。而且每次切換都要付出這個代價,不只是第一次。現在只會建立實際顯示在螢幕上的列。
失敗更新所留下的說明,不會再比失敗本身活得更久。 當某個更新無法套用時——比如說,因為有其他東西已經在安裝——那一列會用紅字說明原因。過去沒有任何東西會把那一行撤下:即使更新後來確實完成、那一列也回到打勾的狀態,舊的說明仍然掛在下面,歷經每一次重新檢查都還在,直到 DuoUpdater 重新啟動為止。現在只要 App 變成最新版本,它就會消失。仍在等待中的更新所屬的原因則不會動,所以一則你還沒讀到的說明,不會被背景執行的檢查清掉。
「全部更新」按鈕的大小,不會再隨列表長度改變。 只有少數幾個更新待處理時,它會被畫得比應有的尺寸小一號,旁邊空出八十點的空白,等列表變長時又跳回正確的大小。現在它會一直維持應有的大小。
下載流量現在會說明更新換到了哪個建置版本,而不只是哪個版本。 很多 App 會在同一個版本名稱下推出多個建置版本——Surge 就有四個不同的版本都以「6.9.0」發布——所以那些列顯示的是「6.9.0 → 6.9.0」,什麼都沒告訴你。現在會顯示「6.9.0 (12028) → 6.9.0 (12030)」,而且只在光看版本名稱不夠時才這樣顯示;如果版本本身已經改變,建置編號就只是雜訊,會被省略。
記錄下來的建置版本,是實際裝上的那一個,在更新到位後從 App 本身讀取。 而不是開發者的更新來源所宣稱的編號——更新來源確實會報錯,而且這種方式也適用於那些完全不公佈建置編號的來源:GitHub、Homebrew 和 App Store。仍在等待 macOS 安裝程式視窗的更新,永遠不會被拿來猜測;在它真正完成之前,不會為它記錄任何東西。
什麼都沒改變的下載,現在會被標示出來。 偶爾某次更新抓取並安裝的,其實是你 Mac 上原本就有的建置版本——開發者那邊的版本號碼不一致,或某個鏡像站提供的就是你已經有的東西。那是實實在在花掉卻毫無結果的頻寬,而流量視窗正是你會想看到它的地方。這些列現在會帶有「沒有變更」標籤。在這個版本之前記錄的下載沒有建置編號可以比較,所以會維持原狀,而不是加以猜測:未知並不等於沒有改變。
DuoUpdater 現在會讓你看到,讓 App 保持最新在下載上實際花了多少。 它一直精確到位元組地計算著替你抓取的每一個更新——但這個數字沒有地方顯示,只能躺在一個沒人能讀的檔案裡。現在有了一個「下載流量」視窗,從選單底部的圖表按鈕打開,本月的數字就印在那個按鈕旁邊,所以最常見的問題不必打開任何東西就能得到答案。視窗裡面有:總量、最近三個月的並排比較和它們之間的變化、位元組來源的細目,以及依花費多寡排序的每一個 App——點一下其中一個,就能看到它的每一次更新、從哪個版本更新到哪個版本,以及那次下載有多大。
這個總量對自己看不到的部分是坦白的。 Homebrew、App Store,以及透過內建更新程式自我更新的 App,都是自己抓取位元組,DuoUpdater 從來不經手——所以這個數字是下限,而不是完整的帳目。這一直都是事實;改變的是,視窗現在會一直說明這一點,而不是只在還沒有任何記錄時的空白畫面上才提到。
已經重新命名或刪除的 App,會保留它們的歷史記錄。 流量是依照 App 在磁碟上的位置來記錄的,這讓同一個 App 的兩個通道——比如 Android Studio 的 Canary 和 Beta——能分開計算,而不會被加在一起。代價是,重新命名一個 App,會讓它過去的記錄留在一個已經不存在的名稱下。當 OpenAI 把 Codex 改名為 ChatGPT 時,就把 30 GB 的下載量拆成了兩個看起來像重複的項目。這些項目現在會被歸為一組、以淡色顯示在清單底部,並放在一個說明它們是什麼的標題之下。沒有任何東西被丟掉,總量也仍然包括它們。
從 App Store 安裝的 App,不會再被提供開發者自己的下載版本。 很多 App 同時在兩個地方發布——商店裡,以及開發者自己網站上的直接下載——兩者身分相同,卻是真正不同的建置版本。直接下載的版本通常跑得比較前面,因為它不必等商店審查。對於從商店安裝的 App,DuoUpdater 會先檢查商店,但當那項檢查因為任何原因失敗時——連線中斷、某個地區的商店沒有回應——它就會悄悄轉向開發者的網站,把那裡有的東西提供給你。WhatsApp 就顯示成「26.32.75 → 26.33.19」:一個真實存在的版本,卻來自錯誤的地方。一旦接受,就會把你的商店版換成一份 App Store 永遠無法再更新的副本。現在從商店安裝的 App 只會與商店比對,不會去別的地方。
在 App 內切換它的更新通道,現在會立即生效。 有些 App 讓你在一般版本和 Beta 版本之間選擇——Surge、Tailscale、Fork、OrbStack、IINA、Alfred 等等——而 DuoUpdater 會跟隨你的選擇,絕不提供一個你沒要求的建置版本。過去它只有在你結束 App,或打開 DuoUpdater 的某個視窗時才會注意到變化。但這兩種情況都不符合大家實際的做法:關掉設定、讓 App 繼續執行,然後瞄一眼選單列。更糟的是,App 會在它方便的時候才把設定寫入磁碟,而不是在你按下的那一刻——Surge 在這裡花了五分鐘——所以就算結束 App,也可能讀得太早。DuoUpdater 現在會直接監看那個設定本身的變化,並在一兩秒內重新檢查那個 App。把 Surge 切回一般版本,現在會立即從它的列上清除 Beta 版本,而不是讓它留在那裡長達一小時。
什麼都連不上的檢查,不會再看起來像一切正常。 當所有來源都失敗時——沒有網路,或某個代理伺服器悄悄拒絕連線、而 Wi-Fi 圖像仍顯示一切正常——失敗的 App 會被隱藏,面板則顯示「127 個 App · 皆為最新」。這和一切真的都是最新時看到的畫面一模一樣,讓失敗的檢查與成功的檢查無從分辨。面板現在會說明有多少個 App 無法連線、顯示出了什麼問題,並提供重試這些 App 的選項——只重試它們,其他已成功檢查的都不會動。
A helper macOS wouldn't switch on no longer sends you to reset your whole Mac. App Store updates install through a small background item macOS asks you to approve once. When that approval was refused, DuoUpdater had exactly one explanation for it: the system's record of the item is damaged, and repairing it takes a Terminal command that clears the background-item approvals of every app on your Mac. That is one cause among several, and macOS never says which one applies — the ordinary one is that the switch is simply off. The message now leads with that instead: turn DuoUpdater on under "Allow in the Background" in Login Items & Extensions, and DuoUpdater opens the pane for you. The Terminal reset is still written down, as the fallback for when DuoUpdater isn't listed there or switching it on changes nothing. The message also stopped running off the edge of its card.
Two Settings options read on one line again. The two options under Install routing — how App Store updates install, and what happens to apps that ship their own updater — used to put their name to the left of the menu, the way options do everywhere else on macOS. Adding six languages moved both names above their menus instead, because a German or Russian option can be long enough to run off the end of the line, and the safe layout was applied to every language at once. Each row now decides for itself: the name stays beside the menu as long as the menu leaves room for it, and only moves above when the text genuinely needs the width. In English, Japanese and Chinese both rows are back on one line; in German and French they stay stacked, which is the only way they fit without being cut off.
DuoUpdater now speaks Russian, Simplified Chinese, Japanese, German, French and Spanish. It follows whatever language your Mac is set to, and switches with it — there is nothing to turn on. If you would rather read it in a language your Mac isn't set to, macOS can do that per app: System Settings ▸ General ▸ Language & Region ▸ Applications. Everything the app writes itself is translated: the menu-bar panel, every Settings page, the setup window, the notifications, the alerts, and the small print under each option. Counts are handled the way each language actually handles them rather than by bolting an "s" onto the end, which matters most in Russian, where "1 update", "2 updates" and "5 updates" take three different endings — and in Chinese and Japanese, where they take none.
Two things stay in English on purpose. Release notes are the vendor's own words, so they arrive in whatever language the vendor wrote them in — translating them would mean rewriting what a developer said about their own release. And the duo command-line tool stays English, the way command-line tools generally are.
An App Store app that was open when you updated it comes back again. Last release drew a line in the wrong place. It was fixing something real — an update that failed, or that you cancelled, could bring an app back to life minutes after you had closed it yourself — but it decided whether to reopen by asking whether someone had answered a quit prompt. Almost no App Store update shows one: the prompt only appears on a route DuoUpdater stopped using a while ago. So from 0.3.53, updating an App Store app that was running closed it and left it closed, with nothing on screen to say why. The question it asks now is whether the update actually landed, which is the thing that decides whether a quit is coming at all. This is verified against a real App Store update rather than reasoned about: the store terminates your app to replace its files and never brings it back — despite its own prompt promising that it will.
DuoUpdater now tells you when App Store is waiting on you. The store cannot replace an app while it is open, so it puts up a "cannot be open during installation" prompt and waits — with no time limit, holding the update and a download slot for as long as it takes. That prompt is a small panel inside App Store's own window, which may be sitting on some entirely unrelated page; App Store bounces its Dock icon a few times and gives up; and DuoUpdater lives in the menu bar with no Dock icon to bounce. An update could sit there indefinitely with nothing anywhere telling you a click was needed — one took four and a half minutes here, ending only because someone thought to look. The row now says what is being waited for, from the moment the update starts.
The dot that marks an app as open no longer lags behind. macOS does not reliably announce that an app has launched — some apps never trigger it at all, while quitting is always announced. DuoUpdater listened only for those announcements, so a row could insist an app was closed while its window sat in front of you, and only correct itself when some unrelated app happened to open or quit. It now re-derives which apps are running whenever it rescans, which includes the moment you open the menu.
Two things a review caught before you did. The warning that an update was applied without a rollback point was being erased before it could be read: restarting an app cleared the whole note, and restarting after an update is the default. It is now retracted only by whoever wrote it. And Update All could refuse to restart an app — telling you its own updater had a version staged and waiting — when nothing was staged at all: it compared what was on disk now against a build number recorded before the install, so an app whose version name had not changed looked like a conflict.
An app that already downloaded its own update is no longer asked to download it again. Plenty of apps fetch their next version quietly in the background and hold it until you next quit them — that's the "relaunch to update" state you see inside Claude, TablePlus and others. DuoUpdater has always recognised that state in Electron apps and offered you Relaunch instead of an Update, because the bytes are already on your disk. Apps built on Sparkle, which is most of the rest, were invisible to it: TablePlus sat there with a 133 MB download and an unpacked 382 MB copy of 26.9.11 in its cache, while its row offered to fetch 26.9.11 for you all over again. Those apps are now recognised too — the row offers Relaunch, and nothing is downloaded twice.
One thing it deliberately won't do is offer Relaunch for an older build. An app can be holding a version behind the one you have, which happens when a vendor releases to some people before others and DuoUpdater installed the newer one first. Relaunching there would quietly move you backwards, so the row doesn't offer it — and DuoUpdater won't restart the app for you either, because that restart is the exact signal the app's own installer is waiting for.
Three ways an update could go wrong that a review caught before you did. An App Store update that failed, or that you cancelled, could bring the app back to life minutes later — you would quit it yourself and it would reopen, because DuoUpdater had noted "this app may need reopening" before anything had actually closed it. It now only reopens an app when a quit was genuinely asked for. Separately, the check added last release to stop an app's own updater undoing ours compared only the version name: a vendor that ships several builds under one version number slipped straight past it, which is the same failure it was written to prevent. It now compares the build number too. And an app's own updater is no longer assumed to be waiting just because a downloaded copy is sitting in its cache — Sparkle leaves those behind for ten days after an interrupted install, which could have left a Restart button that did nothing but explain itself.
Same changes as 0.3.51, reissued so it can actually reach you. 0.3.51 went out carrying the same internal build number as 0.3.50. That number, not the one in the version name, is what an update check compares — so anyone already running 0.3.50 was told they were up to date and never offered it. This release carries the changes below under a build number that is properly newer. If you are reading this on 0.3.51, nothing about the app changed between the two.
An update that kept coming back. ChatGPT would offer a new version, install it, restart — and a minute later the same update was waiting again. The install was never the problem: the new version really did land on disk. What happened next is that ChatGPT's own updater put a different one back. There are two lists involved, and DuoUpdater was reading the wrong one. The first is everything the vendor has published; the second is what the vendor is actually handing out to your Mac today, which can be an earlier build while a release is still rolling out. DuoUpdater was reading the published list — the same address ChatGPT itself is configured with, which is what made it look right — and offering you a build the vendor was still holding back. ChatGPT had meanwhile downloaded the build it was being offered and parked it, waiting for the app to close. Restarting to apply our update is what closed it. DuoUpdater now asks the same question ChatGPT's own updater asks, so the two agree on what the current version is.
Restarting an app no longer applies somebody else's update. That collision isn't unique to ChatGPT. A lot of apps download their next version quietly and install it the instant you quit them — the "relaunch to update" state you've seen in Claude, TablePlus and others. They'll wait hours for that quit; one was observed holding on for nearly seven. When DuoUpdater restarts an app to put its own update into effect, that restart is exactly the signal they're waiting for, and theirs runs second. Before restarting, DuoUpdater now checks whether anything is waiting, and what version it holds. If it's the same version — which is the common case, and harmless — nothing changes. If it's a different one, the app isn't restarted: the row says what's waiting and leaves the choice to you, rather than quietly swapping in a build you didn't pick.
An App Store update no longer leaves your app closed. Updating an app you have open through the App Store means closing it — the store's own installer quits it to replace the bundle, and doesn't open it again afterwards. DuoUpdater knew to reopen it, but only if you'd answered its own "quit to finish updating" prompt. Answer the identical prompt in the App Store window instead, or take an update that raises no prompt at all, and nothing remembered that your app had been open. It updated correctly, said "Updated ✓", and your app was simply gone — with no restart offered, because by then there was no running app to restart. What decides now is whether the app was running when the update started, which is the thing that was actually true, rather than which window you happened to click in.
豆包输入法 now gets checked, and shows its release notes. It was in the list — DuoUpdater looks inside /Library/Input Methods — but nothing anywhere knew where to ask about it, so the row sat blank forever and read like "nothing to do". It now reads the same endpoint the vendor's own download button reads, and compares the same build number the vendor versions by — so even a re-release that keeps the version name unchanged shows up, rather than passing as the version you already have. The release notes come from the feed the input method's own updater polls, laid out as a proper list rather than a link to a page that doesn't exist. As with 微信输入法, DuoUpdater will tell you a new version is out but will not install it for you: an input method is registered with the system by its installer, not merely copied into place, and quietly swapping the bundle is how you lose your personal dictionary.
Zed's release notes come back instead of an error. DuoUpdater asks GitHub about a lot of apps, and GitHub lets an unidentified caller ask only sixty questions an hour from one network — a budget every GitHub-hosted app on your machine shares. Zed's notes were the ones losing that race, and the panel showed a failure rather than the notes. Those requests now carry the GitHub token you saved in Settings ▸ GitHub, or the one the gh command-line tool already holds if you use it, which lifts the limit far out of the way. Without a token nothing gets worse: Zed's notes now come from a single, smaller source that covers both its channels, where before each one fetched its own web page.
Notion's release notes are about the app you have. They were being read from Notion's product announcements — the page that introduces features as they launch. Those posts are titled by feature, not by version, so nothing on that page ever lined up with the version number on Notion's row, and the notes for the update you were being offered were never there to find. They now come from Notion's own "What's new" page for the Mac and Windows app, where the versions are the ones you can actually compare against.
Some release notes were quietly incomplete, and no longer are. A release's final line could go missing from ChatWise's notes. Two of Postman's releases were cut off mid-sentence — both at the exact point where the text contained a quotation mark. Six apps' notes are now read from the same data the vendor's own site is built from, rather than picked out of the finished page, which is both sturdier and how those two ended up whole. HBuilderX is the one trade-off: its notes now come from the official release document, which covers the HBuilderX editor itself and not the bundled uni-app and uniCloud module logs, so its entries are shorter than before — the same releases, in the vendor's own words, minus the parts about other products.
Release notes that arrive inside an app's update feed are laid out properly. A good number of apps ship their notes as a small piece of a web page tucked inside the feed their updater reads. DuoUpdater used to hand that straight to the system's HTML renderer, which produced a serif font nothing else in the app uses, bullets indented into the margin, and — the part that mattered — a long list could be cut in half, with everything after the cut silently absent. Those notes are now read into the same list layout the rest of the panel uses: right font, right indentation, and nothing dropped. Where the markup is too tangled to be sure of, DuoUpdater leaves it to the old renderer rather than risk showing you part of a list as if it were all of it. TablePro benefits most — its notes had stopped being found at all.
Four more apps show their release notes properly, instead of an embedded web page. Alcove, Docker, Kiro and Waku each publish their notes somewhere a program can read them — Alcove and Docker on their own sites, Kiro as a feed, Waku on GitHub — but DuoUpdater was showing you the web page instead, which meant the vendor's fonts, the vendor's navigation, and no way to move between versions. They now read as proper entries in the list, the same as everything else. Kiro's feed covers three separate products; only the notes for the app you have installed are shown.
A release whose notes are written as sentences no longer vanishes. Some releases don't have a bulleted list of changes — they have a line or two of prose, sometimes only "no public-facing changes in this release". DuoUpdater understood lists and nothing else, so those releases were skipped entirely: if you happened to be running exactly that version, the panel had no entry for it at all, as though your own build had never shipped. They're kept now. Where the notes are laid out in a way that still can't be read cleanly — a table, mostly — the page is shown as before rather than half-converted into something misleading.
Antigravity IDE gets checked. It's a separate app from Antigravity, with its own version, and it was in your list but nothing ever checked it — the row just sat there with no version to compare against and no way to find out. It's checked now. DuoUpdater won't install it for you; it will tell you when a new version is out and where to get it.
DuoUpdater tells you when it updated itself, and what changed. It installs its own updates quietly, on purpose — it waits until you're away from the machine and swaps itself without asking, because a tool that interrupts you to talk about itself is getting in the way of the work it's supposed to protect. The cost was that you'd end up on a new version you never agreed to and never saw the notes for: every other app in your list has a "what changed" panel, and the one app that changed under you in silence was this one. Now the menu says so once, and opens its release notes. It's shown until you read it rather than for a set time — the update this is for is the one that landed while you were asleep. A fresh install doesn't get told it was updated, because it wasn't, and going back to an older build on purpose doesn't either.
And you can read those notes any time. The ✨ button at the bottom of the menu opens every release DuoUpdater has ever shipped, with the one you're running marked in the list.
Switching an app's update channel in that app is noticed straight away. Some apps let you choose between their stable and pre-release builds in their own settings — Tailscale, Fork, Surge, OrbStack, TablePlus, CleanShot, IINA, Alfred, DuoPaste. That choice lives in the app's own preferences, where nothing tells DuoUpdater it has changed, so switching from a beta track back to stable left the row still comparing you against the beta — and still offering it — until the next scheduled check, up to an hour later. DuoUpdater now re-reads that choice when one of those apps opens or quits, and when you come back to its own window, and re-checks just the app that changed.
Tailscale's Release Candidate track is one of the choices DuoUpdater understands. Tailscale publishes three: stable, release candidate, and unstable. Only two were known here, so a Mac opted into release candidates was quietly checked against stable instead, and reported up to date whenever a candidate build was newer than the stable one. All three are now checked against the track you actually chose.
Confirming a quit late no longer leaves the app updated but closed. Some apps guard their own quit with a dialog — Claude, for one, asks about an active conversation — and that dialog could land in the middle of a Relaunch. DuoUpdater rightly refuses to sit there while you decide (and it still won't force the quit past your unsaved work), but once it stepped aside it also stopped listening. Answer the dialog a minute later and the quit went through, the app's own updater swapped in the new version — and then nothing happened: some updaters deliberately don't reopen the app after installing, DuoUpdater was no longer watching, and you were left staring at an app that had simply closed. It now leaves a note for itself when it steps aside: if you do confirm that quit within the next few minutes, it waits for the update to finish landing and then brings the app back, in front if that's where it was. The note expires after a few minutes, so a quit hours later is just you closing the app, and stays that way.
The same late answer now works for a plain Restart. A save prompt could block the Restart button (and the automatic restart that follows a one-click Update) in exactly the same way, with a worse ending: the new version was already on disk, so when you finally dismissed that prompt and the app closed, DuoUpdater's next look around decided there was nothing left to restart and quietly dropped the badge too — an app closed, an update half-applied, and no trace that anything had been asked for. Answering a save prompt within a few minutes now finishes the restart it belonged to, and the app comes back on the new version.
App Store updates tell you they're waiting, and don't leave the app closed if you answer late. When the App Store finishes downloading an update for an app you have open, it asks for that app to be closed before it can install — and DuoUpdater waits, indefinitely and on purpose, for you to say when. Until now that request lived only inside the menu: if you never opened it, an update sat downloaded-but-not-installed all night, and background checks waited with it. It now also arrives as a notification with a Relaunch button, so you can answer it without hunting for the row. And if your app puts up a save prompt of its own after you tap Relaunch, answering it minutes later no longer strands the app closed — the update lands and the app is reopened, the same as if it had quit right away.
The list holds still while you are clicking down it. Every finished update used to re-sort the list on the spot: the app that just landed left the pending group, or picked up a Restart badge and jumped to the top, and everything below it slid up a row — under the pointer of anyone working down a list of Update buttons. Click the top one, go for the next, and the next one had moved. Now the order is held from the first click until the whole round is done: an app that finishes shows its confirmation in place, nothing else moves, and the list settles once at the end, when the finished apps drop away as they always did.
One less version number on a row that needs a restart. An app with an update waiting and an earlier update still needing a restart tried to print all three versions on one line — installed, available, and the older one still running. On four-part versions like Chrome's it did not fit, and the line was cut off exactly where the digits started to differ, so the part left visible said nothing at all. The line now sticks to the comparison that matters — what you have and what is offered — and the running version moved to the row's tooltip, where it fits.
No more Dock icon. DuoUpdater is a menu-bar app — everything it does starts from the icon up there — but it also held a Dock slot, and the only thing that slot ever did was open the same window the menu bar opens. It now runs from the menu bar alone. If you would rather keep the Dock icon, Settings ▸ General ▸ "Hide the Dock icon" turns it back on, and with it the badge that shows the number of pending updates; hidden, that count lives on the menu-bar icon instead.
An app you ignored is no longer checked at all. Ignoring an app hid its row, but every check still asked its vendor after it — one network request per app per round, spent on an answer nothing would ever be said about. On an unauthenticated GitHub budget of sixty requests an hour, those were requests taken from the apps you do watch. Ignoring now means not asking, which is what "hide an app from update checks" always claimed. Naming an app on the command line still checks it, and so does asking for hidden rows, since both are you asking about that app specifically. Skipping a version is unchanged and still checked — whether the version on offer is still the one you skipped can only be known by asking. And un-ignoring re-checks that app on the spot, instead of leaving the row blank until the next round comes due.
An app you ignored stops notifying you. An app that updates itself leaves a "Relaunch to apply it" reminder in Notification Center and repeats it every few minutes until you act on it. That reminder never consulted the ignore list, so an app you had ignored went on sending it — hidden in DuoUpdater's own list, still arriving every five minutes, with nothing on screen to explain where it was coming from. Ignoring an app now silences those reminders and clears any already waiting in Notification Center. Skipping a version does the same for that version.
A download no longer squeezes the app's name or its version off the row. While an app was downloading, the progress bar and its percentage claimed enough of the row that a long name wrapped onto a second line, a long date-style version was clipped at both ends to something unreadable, and at 100% the percentage itself broke across two lines. The row now measures what the name and the version actually need and fits the progress readout into what's left: the bar gives way to a compact ring, and the percentage stays. Nothing is given up until there is genuinely no room for it.
An update no longer looks like it fired twice. With one update pending, installing it briefly emptied the list: the row dropped out the moment the new version reached the disk, the "Everything is up to date" placeholder took its place and jumped the window's height, and then the row reappeared saying "Relaunching...". Nothing was actually wrong underneath — the app still had to be restarted to run the new code — but it read as though something had happened twice. The row now stays where it is from the click through to the relaunch. The step that used to announce "Done" while the app was still being restarted says "Installed" instead; "finished" is left for the confirmation at the end, where it belongs.
The workbench sidebar follows the arrow keys again. Holding an arrow key walked the selection off the edge of the list and left it there, moving through apps that were never drawn and not catching up when the keys stopped. The selected app stays in view.
Moving through that sidebar is quicker. Every keypress was re-deriving, once per app per row, a fact about the whole list that had not changed — on a machine with 124 apps that came to about fifteen thousand redundant filesystem-path lookups per keystroke. It is derived once now. Fast key repeat can still outrun the list; there is more to do here.
An App Store update says "Update", not "Get". When the background helper has not been approved there is no way to install an App Store update in place, so the row hands off to the App Store app instead — but the button for that read "Get", which is what the store says about an app you do not own yet. Every row that reaches it is an app you already have, with an update waiting. It says "Update" now, and when the helper is what is missing the tooltip says so, since approving it in Settings is what turns those updates into one click.
The action column lines up. A checkmark or a small badge at the end of a row was centred in its slot while a wide button sat flush against the row's edge, so the right-hand column read as ragged — and visibly out of line with the Homebrew row pinned below it. Everything ends on the same edge now.
WeType (微信输入法) now reports the version you actually have. Its version was being read off the name of the vendor's installer file, which turns out to carry the installer's version rather than the app's — the installer is a small downloader that fetches the real app separately, and the two numbers drift apart. DuoUpdater now reads the same manifest the vendor's own installer reads, so the version matches your copy and new releases show up when they ship. WeType still has to be updated with the vendor's installer rather than in place: replacing the bundle skips the input-method registration step and was found to lose settings.
A beta build can never arrive on the stable channel. The fix in 0.3.44 looks further back through an app's releases when the newest one has no Mac build attached. That wider search could also see the developer's beta and release-candidate builds, which the normal check never shows you — so an app that happened to publish a release without its Mac download could have offered you a beta. Nothing had actually hit this, and now nothing can: the wider search only ever considers finished releases.
Notion's release notes are readable again. Notion restyled its releases page and DuoUpdater could no longer pick the posts out of it, so the notes fell back to showing the raw web page. They render as proper entries again.
PureMac's updates are visible again. The developer publishes a separate command-line tool from the same place as the app, and its release was being read as if it were the app — as version 1.0.0, which looks older than what you have installed, so the app reported itself up to date and every real update stayed hidden. It now reads only the app's own releases.
An update that only ever existed for phones no longer sits in your list. Some apps share one version number across Mac, Windows, Linux and mobile, and sometimes a release goes out to the phones alone — the version number moves, but no Mac build is ever made. DuoUpdater was reading that as a Mac update, which left an update you could never install and that never went away. It now looks for the Mac download itself rather than trusting the version number, so those releases are correctly ignored. LocalSend was the app affected; its row now reads as up to date, which it is.
LocalSend installed from its own website updates in one click. Now that the right release is identified, its Mac disk image can be installed in place like the rest.
An App Store copy is never replaced with a build from elsewhere. A few apps are published both on the Mac App Store and as a download from their developer, under the same identity — LocalSend is one. Those are genuinely different builds, and the App Store's copy has to keep updating through the App Store. DuoUpdater now leaves those copies to the store instead of ever offering the developer's build over them.
Updates that the wider ecosystem never picks up get flagged for us. Our nightly recipe sweep could only tell whether an app's version could still be read, not whether the answer made sense for a Mac — which is why the LocalSend problem had to be spotted by hand. It now also compares against Homebrew, and raises a flag when we are reporting a version that nobody else has packaged long after it was published. Nothing about this is visible on your Mac; it is how this class of mistake gets caught by machine next time.
Package updates are read more thoroughly before they are opened. The check added in 0.3.41 asks an installer package where it installs, and refuses one that will not say. It was reading only the summary the package publishes about itself; it now also reads the package's own file list, which is what the installer actually follows. That means a package that keeps quiet in its summary is still understood instead of turned away. Every app that updates this way was re-checked against its real installer, and none of them changed.
A download link that stays broken now gets noticed. When a vendor's server has a bad minute, DuoUpdater waits it out rather than crying wolf — but that was letting a download link that had been broken for good slip by unremarked, because it looked the same as a bad minute on any single check. It now tells the difference: brief trouble is still ignored, trouble that lasts is flagged and fixed. Nothing changes on your Mac; this is about broken apps getting repaired sooner instead of quietly staying broken.
Release notes can't be pulled down to an insecure page. A vendor's notes page is loaded over a secure connection, but nothing stopped that page from redirecting itself to an insecure one. Now it can't.
Updating OneNote no longer installs the whole of Microsoft Office. OneNote's update was being fetched from Microsoft's combined Office installer, which puts Word, Excel, PowerPoint, Outlook and OneDrive on your Mac along with it. If you keep OneNote on its own, that was five applications you never asked for. It now downloads Microsoft's standalone OneNote update, which installs OneNote and nothing else.
Package updates are checked more strictly before they are opened. An update that arrives as a macOS installer package now has to say where it installs, and has to name the app you are updating. A package that will not say is refused rather than let through. Every app that updates this way — Office, Edge, Teams, OneDrive, Tailscale, ToDesk, AweSun and the rest — was checked against its real installer first, so this should never fire on a normal update.
Telegram's one-click update stops vanishing when Telegram's servers hiccup. Working out where to download Telegram from needs one extra request to their servers, and those servers were intermittently refusing it — a few minutes at a time, then fine again. When that happened the update was still detected, but the Update button quietly disappeared for that check and you had to go to the website yourself. DuoUpdater now retries before giving up, so a brief hiccup no longer costs you the one-click install.
Package installers are checked against the app they claim to update. Some updates arrive as a macOS installer package, which runs with administrator rights the moment you confirm it. Until now the only check was that the package came from the same developer as the app being updated — which would have let any package from that developer through, not just the right one. DuoUpdater now also reads where the package says it will install and refuses it if that is not the app you are updating. Nothing changes for a normal update; this only ever fires on a package that does not belong.
Release notes only open over a secure connection. The notes pane loads a vendor's own page for some apps, and one of those pages was still being fetched over plain http. Those pages now have to be https, and a handful of other malformed addresses are refused outright rather than loaded.
Fewer apps can go silently missing after a vendor renumbers. Zotero's jump to 10.0 in 0.3.37 exposed a whole class of this: an app disappears from the update list, with no error, because the vendor changed how many numbers are in its version. Twenty-one apps — among them VS Code, Discord, Obsidian, Figma, WhatsApp and GIMP — no longer depend on that staying the same. The rest were checked and deliberately left alone, because for those a looser check would risk reading the wrong number off the page.
The download percentage no longer spills out of its row. While an update downloaded, the number next to the progress bar sat too far right — clipped by the edge of the list instead of lining up with everything else in the column — and there was more empty space between the bar and the number than there needed to be. Both are fixed; the percentage still holds a fixed width, so the row stays steady as it counts up to 100%.
Zotero 10 shows up as an update again. Zotero numbered its new major release 10.0 — two numbers where every previous release had three — and DuoUpdater's check for it quietly stopped recognising the version. Nothing looked wrong: no error, no failed check, Zotero simply never appeared in the list, so anyone still on 9.0.6 was never offered the upgrade. It is recognised again, and the one-click install is unchanged.
A package update that leaves the old copy running now offers to restart it. Some updates install as a package handed to macOS's own installer, and when that finished it left the previous copy still running the old version — with no prompt, so you had to notice and quit it yourself. DuoUpdater now spots that and offers a Restart, the same as it already does for other kinds of update. It only offers one when a copy that was open before the install is genuinely still running the old code — if the installer (or you) already relaunched the app, or it wasn't open, nothing is shown.
WeChat DevTools (微信开发者工具) is checked for updates now — Stable, RC and Nightly each on their own track. It used to sit there as "unknown": since version 2.02 the app reports Electron's stock identity on disk, calling itself version 36.6.0, and all three channels look identical from the outside. DuoUpdater now reads the real version and channel out of the app's own configuration, so whichever track you installed is the only one you are offered — a Nightly install is never handed a Stable build, or the other way round. Updates install in one click, and the release notes for the exact build show up in the window.
App Store updates stop breaking every time DuoUpdater updates itself. Replacing the app left the background helper from the previous copy running, and macOS then never started the new one — so the helper looked switched on while every App Store update failed talking to a copy that no longer existed. Only a restart cleared it. The helper now steps aside when it has been idle for a minute, which means a replaced app heals itself by the next update. For a Mac already in that state there is a Restart Helper button in Settings → Diagnostics and on the update that failed; it asks for an administrator password and takes effect immediately, no restart.
Diagnostics can tell you whether the helper actually works. "Enabled" only ever meant "switched on", and the difference between that and "answering" showed up as a failed update. A Check button now says which one you have, in those words.
Backups: choose what to delete. "Clean Up Now" only removed backups belonging to apps you had uninstalled, so on most Macs it deleted nothing and said nothing while the size stayed put. It now opens a list — every backup with its app icon, the update it would roll back, its size and date — with everything selected and anything you want to keep unselectable. The button says how much the selection frees. Backups whose records had gone missing were previously counted in the total but impossible to see or remove; they are listed too, marked unusable.
A silent self-update no longer leaves DuoUpdater sitting in front of you. When it applied its own update in the background, macOS brought back the windows that had been open — and bringing a window back also brings the app forward, so an update nobody asked for landed on top of whatever was being worked on and stayed there. DuoUpdater now notes which application was in front before it replaces itself, and gives the front back to it on the way in. Windows still return exactly as they were.
Docker updated to the version it said it would. Docker publishes its releases in an order that puts an older one first, and DuoUpdater read the newest version from that list while taking the download link from the top of it — so it fetched 574 MB, kept a backup, installed 4.86.0 over the 4.86.0 already there, and went on offering 4.87.0. Downloads are now matched to the version each entry declares for itself, whatever order a vendor lists them in.
An update that changed nothing is no longer reported as done. The check that runs immediately after installing already knew Docker hadn't moved; it was overruled by a success message and an "Updated ✓". When an update we applied ourselves leaves the app exactly as it was, that is now shown as the failure it is, naming what was installed and what is still on disk.
Silent self-updates no longer wait for an empty screen. With the switch turned on, DuoUpdater would hold its own update back while any of its windows were open — which, for a window people leave open, meant waiting until the app was quit. Open windows no longer delay it (macOS brings them back after the restart), while a DuoUpdater you are actually using still does: it waits for the keyboard and mouse to go quiet before restarting itself.
Installing DuoUpdater's own updates silently now actually is silent. The switch introduced in 0.3.30 downloaded the new version in the background and then still asked before applying it — the one thing it was meant to spare you. It now applies the update itself, at a moment when doing so interrupts nothing: no check or install running, nothing waiting to be relaunched, no DuoUpdater window open, and you working in another app. It restarts itself there, without a prompt. Until such a moment arrives it simply waits, and if none ever comes the update is still applied when you quit — so the wait can delay a version, never lose one. Leaving the switch off is unchanged: you are asked, as before.
An App Store update no longer blames you for a permission you already gave. When DuoUpdater is replaced while it is running — by its own update, or by a rebuild during development — the previous copy of its background helper keeps holding the slot the system reserves for it, while macOS still reports the helper as switched on. Every App Store update then failed with a red line telling you to go turn it on in Login Items, where you would find it already on, and the button offered beside that message quietly did nothing. That state is now recognised and named for what it is, along with the one thing that clears it. DuoUpdater no longer tries to repair it by re-registering the helper: that was measured to switch the background item off and leave it unable to be switched back on.
DuoUpdater can now update itself without asking. Settings → Updates has a new switch for installing DuoUpdater's own updates in the background, taking effect the next time it restarts. It stays off unless you turn it on, and the prompt-and-wait behaviour is unchanged for everyone who leaves it alone.
Its own updates are noticed within the hour. DuoUpdater checked for its own new versions once a day, so a release could sit unseen for most of a day. It now checks hourly.
Eight more AI desktop apps are tracked. OpenCode Desktop and OpenChamber follow their GitHub releases; Wispr Flow, Granola, Comet, Windsurf, AionUi and Msty are read from their vendors' own version endpoints.
Installer packages stay the same package from verification to macOS Installer. DuoUpdater now seals the selected installer before closing or replacing any existing Installer window, checks it again immediately before opening, and refuses the hand-off if another local process changed the file in between. This preserves Sparkle's signature guarantee all the way to the package you see in Installer without making the menu-bar UI pause while large packages are checked.
Multi-installer disk images handle more real-world package names without guessing. Versioned beta, release-candidate, Apple Silicon, and universal package names are recognized when they identify one unique product, while similarly named helpers and sibling products remain excluded. Older bundle-style macOS installer packages are supported by the same integrity checks.
Failed installer downloads are cleaned up immediately. A bad signature, unreadable disk image, cancelled download, or rejected package no longer leaves a full installer sitting in temporary storage until the next day's cleanup.
Signed Sparkle updates that arrive as installer packages now work. A few apps publish a perfectly valid, cryptographically signed .pkg instead of an app archive. DuoUpdater offered those updates, downloaded them, and then tried to unpack the package as though it were a zip — an update that could never finish. They now go to macOS's own Installer, after DuoUpdater verifies both the Sparkle signature on the download and the installer identity inside it.
A disk image containing several installers is no longer allowed to make a guess. Some vendors put a main installer, helpers, and sibling products in one image. Matching on a fragment of the filename could pick a helper simply because its name contained the app's name. DuoUpdater now opens a package only when it is the sole choice or can be identified uniquely; otherwise it stops and leaves the decision to you instead of presenting the wrong installer.
The App Store helper is more tightly scoped to your login session. The privileged helper now takes the account identity directly from macOS's authenticated XPC connection and refuses a request whose claimed user does not match. Normal App Store updates behave exactly as before; the change closes off a signed client from redirecting the helper into another user's session.
Release notes show their formatting instead of its punctuation. Notes that come from a project's GitHub release were rendered exactly as written — **bold** with the asterisks, links as [text](url). Bold is now bold and links are links. This affected every app whose updates come from GitHub, which is most of the open-source ones.
Arrow-keying down the app list no longer crawls. Holding an arrow key felt like moving one row at a time through mud, and long release notes made it worse. Three things were doing it: a permission check on every app in the list ran again for every row drawn; the notes for whichever app you passed through were re-parsed on each keypress; and a long set of notes — one project's runs to 54,000 characters — was laid out in a single pass, which froze the window for about two seconds. The check is now computed once per list, parsed notes are kept, and long notes are laid out only as far as you have scrolled. Worst measured stall went from ~2.1 s to under 0.6 s, and what remains is the deliberate pause before the detail pane catches up rather than a freeze.
Input methods are never updated by replacing the app, and 微信输入法 (WeType)'s one-click from 0.3.25 is withdrawn. Settings were lost on a Mac during the work that added it. What we can show is that the copy in the protected system folder was never actually replaced by DuoUpdater — but an older copy of the input method was installed and launched elsewhere on that machine while testing, inside the window where the settings were rewritten. Nothing here is proven, and an input method's dictionary is not something to test a theory on: WeType now reports its version and sends you to the vendor's installer, which registers the input source with the system — a step that replacing the app bundle skips, and the likely reason that Mac then appeared twice in WeType's own device list.
The refusal is not specific to WeType: DuoUpdater no longer offers a one-click for anything installed as an input method, whichever vendor it comes from. Those apps still report their versions and link out.
Three more apps now report their updates, and all three install with one click. Hidden Bar, XQuartz and EasyFind were sitting in the list as a grey "unknown".
Hidden Bar is the interesting one: it ships with an update feed configured, so from the outside it looked like it was already covered. The feed answers, and is well-formed, and contains no releases at all — which is indistinguishable from a healthy feed until you look inside it. Its version now comes from its release tags instead. EasyFind ships no updater at all, so a copy installed from the vendor's site had no way to learn about new versions.
XQuartz installs through the system installer rather than by replacing the app, because it is not just an app: it lays down a whole X11 stack, and swapping only the app bundle would leave the rest at the old version. macOS asks for the administrator password itself, as it does for any package.
Thirteen more apps now report their updates, twelve of them with one click. GIMP, MongoDB Compass, Meld, Emacs, Tor Browser, Zotero, GrandPerspective, TigerVNC, qBittorrent, Opera, LibreOffice, pgAdmin 4 and Telegram Desktop were all sitting in the list as a grey "unknown" — installed, with nothing to say about them. Each was worked out by downloading the vendor's actual build and reading its identity out of it, so a one-click only appears where the download is signed by the same developer as the copy you already have. The exception is qBittorrent: its own build isn't signed by an identified developer at all, so it reports its version and sends you to the project's page.
Opera, LibreOffice and pgAdmin 4 nearly joined that exception. All three publish nothing but a directory listing, and listings sort alphabetically — "100" comes before "99" — so the newest release is not the first one on the page. Reading the version was never the problem; building a download link was, because the obvious way to build one would have picked whichever release happened to be listed first. They now download the release that was actually compared. That is the one mistake a signature check cannot catch for you: an older build of the right app, signed perfectly.
1Password and Inkscape now install with one click too, and both were previously written off. 1Password's official download turns out to be a small installer program rather than the app — signed and notarised by 1Password, so every safety check passes it, and installing it would have replaced your password manager with its own installer. DuoUpdater now fetches the package that installer itself downloads. Inkscape's download page hands out its file through a one-time link that changes with every release; the same file also sits at a plain, predictable address, which is what gets used.
微信输入法 (WeType) now installs with one click. It lives in a folder only an administrator can write to, which is why it used to only report its version. It now goes through the same administrator prompt as any other app in a protected location.
Release notes for Opera, Inkscape and 1Password. Opera publishes one page per major version, Inkscape one wiki page per release, and 1Password a feed — all three now render as proper change lists in the app instead of a link out. 1Password's version and its notes both come from that feed now, which is a published interface, rather than from scraping the page beside it.
Discord's version check works again. Discord moved its downloads to a different server and the check was still looking at the old address, so DuoUpdater quietly reported "no version" for Discord Stable while everything else kept working. It now keys off the part of the address that names the release channel, which is the part that actually has to be right.
Fifteen more apps now report their updates, and all but one install with one click. Rancher Desktop, Cherry Studio, RedisInsight, Upscayl, WailBrew, Wave Terminal, Lens, Termius, Unity Hub, iStat Menus, Inkscape, Google Gemini, Antigravity, AnyDesk and Kiro were all showing as a grey "unknown" — installed, with nothing to say about them. Each one was worked out by reading the vendor's own build rather than trusting a download page: the version now comes from wherever that app's own updater looks, and a one-click only appears where the download is signed by the same developer as the copy you already have. Three of them (Google Gemini, Antigravity, Kiro) publish nothing a download page can be scraped for; their real update services answer the same questions their own updaters ask, so that is what DuoUpdater asks too.
AnyDesk in particular was written off and shouldn't have been. Its download and changelog pages both refuse anything that isn't a person with a browser, so an earlier sweep concluded the app was unreachable. The plain-text changelog on the same server answers fine — and it is what AnyDesk's own Homebrew entry has always read.
Updating an app in a location that needs an administrator password now asks, once. Most apps live in /Applications, which you can write to; a few — input methods, for one — live where only an administrator can. Those used to show an Update button that could never work. Now the button asks for the password, and if you dismiss that prompt DuoUpdater takes the hint: the row switches to Open and stops asking on every release. "Ask for administrator access again" in the row's right-click menu brings the button back. The choice is remembered for that copy of the app specifically, so declining for one install doesn't silence another.
An up-to-date Xcode beta no longer claims the vendor is behind it. Under "Show all", a row whose vendor has fallen behind what you have installed shows a muted note saying so — you're ahead, nothing to do. Xcode was getting that note while sitting on exactly the build Apple was offering: it publishes a build number plus a human label ("27.0 beta 5"), and comparing that label against the plain "27.0" the bundle reports made a release look newer than its own beta. The note now settles on the build whenever both sides have one, so the same release is recognised as the same release however it is labelled. A vendor that has genuinely fallen behind is still called out.
44 more apps now report their updates. Apps that publish on GitHub but ship no update feed of their own used to sit in the list as a grey "unknown" — DuoUpdater could see them installed and had nothing to say about them. Bruno, UTM, kitty, KeePassXC, Godot, Bitwarden, VSCodium, draw.io, Podman Desktop, Anki, Raspberry Pi Imager, LuLu, MarkEdit, Clash Verge, Freelens, Tabby, Espanso, Moonlight, SwiftBar, Sequel Ace, balenaEtcher, DB Browser for SQLite, OpenLens, Headlamp, OpenMTP, Goose, Caffeine, noTunes, KeepingYouAwake, MiddleClick and a dozen more now show a real version, and 36 of them install with one click like any other app. Which ones was decided by downloading each vendor's actual build and reading the identity out of it, so a one-click only appears where the download is signed by the same developer as the copy you already have. Seven — Alacritty, Flameshot, MarkText, darktable, OWASP ZAP, BlueBubbles and Wine — publish builds Apple hasn't notarised, so those report their version and send you to the vendor rather than installing anything. LocalSend is report-only for a different reason, corrected here after this version shipped: its builds are notarised, but its newest release attaches no macOS download at all, so there is nothing to install until the project starts publishing one again.
Apps that already carry a Sparkle feed needed nothing: they were checked as part of this sweep and were already working, which is why names like Rectangle, Maccy, iTerm2 and Telegram aren't in the list above.
An update that your Mac couldn't run is now refused rather than installed. Where a developer publishes one download per processor, DuoUpdater picks between them by the file's name — and names are not always honest: three of the apps above ship an Apple silicon build under a name that says nothing about it, or says the opposite. Before an app is replaced, its new version is now checked against the processor in your Mac, read out of the program itself instead of its name. If it can't run here, the update stops and your working copy is left exactly as it was. Nothing about a normal update changes; this is the case that used to end with an app that no longer opened.
An unanswered permission prompt no longer leaves the update check hanging. To tell a TestFlight build apart from an App Store one, DuoUpdater reads TestFlight's own database, and macOS keeps that behind the "access data from other apps" permission. Until that was answered the read didn't fail — it waited, indefinitely, for a prompt that might be sitting behind another window or might never be answered at all, and the scan behind it simply never finished. Nothing timed out and nothing said why. It now waits a few seconds and then carries on without TestFlight's side of the story; the only thing missing in the meantime is whether those particular apps came from TestFlight, and it sorts itself out on the next scan once the permission is granted.
Homebrew updates work behind a proxy. If your Mac reaches the internet through a proxy, upgrading brew packages failed with curl: (28) Failed to connect while every other update went through fine. Homebrew shells out to curl, which — unlike the rest of DuoUpdater's networking — doesn't read the proxy you configured in System Settings; it only reads proxy environment variables, and an app launched from the Dock or at login has none. DuoUpdater now passes your system proxy settings down to Homebrew itself. Nothing changes on a machine with no proxy configured, and a proxy you've already exported in your own shell still wins.
Claude's updates now show up while they're still rolling out. Anthropic releases Claude in stages: a build goes to a fraction of Macs at a time, and the public download page only catches up at the end. DuoUpdater was reading that public page, so for the whole of a rollout — most of a day, in the case of 1.30096.5 — it told you Claude was up to date while Claude itself had already quietly downloaded the new version and was waiting for a relaunch. It now also asks the same endpoint Claude's own updater asks, which answers for your Mac specifically, and offers whichever of the two is further ahead. Nothing about which build you're offered has changed: it is either the public release or the one your Mac was already allocated. Claude also gains real publication times, so its releases now appear in the Release Log with the moment Anthropic shipped them rather than an estimate.
A superseded package update no longer leaves its window sitting there. Updates that go through macOS's own installer — Microsoft Office, AweSun, ToDesk — open an Installer window and then wait for you. If you left one open and a newer release came along, installing that one opened a second window, and they stacked up. The older window is now closed once its replacement is ready, and its download cleaned up with it. A window that's mid-install, or asking for your password, is left strictly alone.
duo says which copy is which when two apps share a name. Naming an app that is installed twice — two Xcode betas, say — printed both candidates as a bare "Xcode" and told you to name one exactly, which matches both again. The listing now carries each copy's version, and when the matches genuinely share a name it asks for the path instead of repeating advice that cannot work.
"Open download page" no longer downloads a file. On apps whose page DuoUpdater knows — ToDesk and UU Remote among them — that button handed your browser the installer package instead of opening anything: the link it used was the same one the updater downloads from, so clicking it started a download you didn't ask for. The page and the package are now kept apart, and the button opens the vendor's actual download page. Where a source only ever publishes a package and no page at all — a bare Sparkle feed — there is now no button rather than one that downloads something.
The app list responds to the arrow keys again. Opening the workbench window left the keyboard focus nowhere in particular, so ↑ and ↓ did nothing until you clicked a row — and after clicking into the release notes on the right, or switching to another app and back, they stopped working again. The list now takes the keyboard when it opens and takes it back at the points it used to lose it. Typing in the search box is untouched: a search you've started keeps the caret.
The Brew section starts collapsed. Casks and command-line formulae are a side channel for most people, and having that tree open by default pushed your actual apps up the sidebar every time the window opened. It now starts closed and remembers however you leave it.
Xcode betas and release candidates are now detected, and two copies can be told apart. Xcode was a grey "v27.0" with no update information at all, and if you keep more than one build around — a current beta beside the previous one — they were indistinguishable: same name, same version, same icon. Each row now reads its real build, so an update shows as "27.0 beta 1 (27A5194q) → 27.0 beta 5 (27A5237l)". Which track a copy belongs to is worked out from Apple's published builds rather than guessed from what you named the folder, and you'll only ever be pointed at something at least as finished as what you have — a beta can be superseded by a beta, a release candidate or the finished release, never the other way round. Updating still means going to Apple: the downloads need you signed in with your Apple ID, so the row links to Apple's download page and its release notes.
Cursor's release notes are shown properly instead of an embedded web page. Cursor writes its changelog as dated posts rather than numbered releases, so the notes pane fell back to loading the website. Each post is now shown as its own entry — its date, its headline and its changes — the same as every other app with readable notes.
duo check now shows what changed when the version number doesn't. Updates that keep the same version and only move the build — Surge, the JetBrains previews — printed as "6.9.0 → 6.9.0" on the command line, which was accurate and told you nothing. It now shows the builds, matching what the menu bar has always shown.
Apps that ship their own updater are now updated directly by default. These are the ones like Chrome, VS Code, Cursor and the Electron apps — and because they are also the apps you tend to leave running all day, the old default of stepping aside while they were open meant they were almost never updated at all: the row offered to open the app and left the rest to you. DuoUpdater now downloads the vendor's own installer and applies it whether or not the app is running, then quits and relaunches it so the new version takes effect. Anything that installs background components alongside the app — Tailscale, Office — ships a package that macOS's own installer handles, so those pieces are still put in place properly. If you would rather nothing was touched while an app is open, Settings → General → Self-updating apps still has the old behaviour, and changing it back does not affect anything already installed.
An app whose developer changed its update signing key can be updated again. Apps that update through Sparkle sign each release with a key, and the copy you already have carries the matching public key to check it against. If a developer generates a new key and ships it without a hand-over release signed by the old one, that check fails for everybody — the app's own updater is just as stuck as DuoUpdater was, and the update sits there refusing to install with a signature error. DuoUpdater now recognises that specific situation: when the new download carries a different key of its own and the release was signed with it, it stops trusting the signature and falls back to the same checks it uses for apps that publish no signature at all — the download must be validly signed by Apple's developer certificates, and by the same developer as the app it is replacing, for the same app. A download that fails any of that is still refused, as is a bad signature that isn't explained by a key change. Mirage Beacon 1.3.0 was the first to hit this.
Update All no longer says a running app is finished before its restart. When an update had already replaced an app on disk but Update All was still busy with other installers, the row briefly showed a green checkmark and disappeared even though the old version was still running. The app now stays visible with its running and installed versions, explains that it is waiting for the batch restart, and offers Restart now. The completion checkmark appears only when the update is actually in effect.
Apps installed by a .pkg can now be rolled back. DuoUpdater keeps a copy of the previous version before it updates an app, so a bad update can be undone. Apps that install through macOS's own installer — Microsoft Office, AweSun, ToDesk and the like — never got that copy: the rollback was skipped for them entirely, so the one kind of update DuoUpdater can't watch land was also the one you couldn't back out of. They're now backed up like everything else.
Rollback no longer refuses apps that write inside their own bundle. Some apps keep working files in amongst their own program files — ToDesk stores its settings database and logs there, and doing so breaks the seal Apple puts on an app. DuoUpdater checked that seal before restoring a backup, so for those apps it declared a perfectly good backup damaged and refused to put it back. It now checks the copy against a fingerprint taken when the copy was made, which is the thing that actually matters: that what's being restored is exactly what was saved. Tampering with a stored backup is still caught, and still refused.
When a backup isn't possible, it says so instead of failing quietly. A few apps keep program files that your account simply can't read — EasyConnect is one — and no copy of those can be made. Rather than attempting it and reporting a failure part-way through an update, DuoUpdater now checks first, tells you which file is in the way, and updates anyway; you just don't get a rollback point for that one app.
The same app no longer appears several times over. Some apps leave a dated copy of themselves behind every time they update — DuoPaste, for one, parks a DuoPaste.backup-20260716-183428.app next to the real thing on each self-update. Those copies are complete, working app bundles as far as anything on disk can tell, so DuoUpdater listed each one as its own app: three identical DuoPaste rows, each offering the same update. Worse, taking one of those offers would have installed the new version into the backup, leaving the app you actually use untouched and creating another stray copy. Backup and duplicate bundles are now recognised for what they are and left out of the list, as are exact clones of an app found in two places. Genuinely separate installs that happen to share an identity — Firefox alongside Firefox Beta, two versions of Android Studio kept side by side — still each get their own row.
The list now reliably notices apps appearing and disappearing. DuoUpdater watches your Applications folders so that an app updating itself in the background, or one you drag to the Trash, is reflected within a few seconds. That watch could quietly stop working — nothing crashed, nothing was reported, it simply stopped hearing about changes, and the list then went stale until you reopened the window. It's now rebuilt periodically and after your Mac wakes from sleep, with a fresh scan each time, so a watch that dies recovers on its own instead of staying dead for the rest of the session.
An app that gets restarted after an update no longer jumps in front of what you're doing. When DuoUpdater updates an app that's currently running, it quits and reopens it so the new version actually takes effect. Reopening it also pulled it to the front — so an update to something sitting quietly in the background could drop a window on top of the thing you were typing into. The app's position now survives the restart: whatever was in front comes back in front, and whatever was in the background comes back in the background, still there and still updated, just not in your way. The same applies to apps DuoUpdater reopens after an App Store update. Apps that weren't running at all are, as before, updated on disk and left closed — updating an app never starts it up.
Fixes AweSun's update failing with "the server returned HTTP 404". DuoUpdater worked out where to download AweSun's installer by building the filename itself from the version number. Oray then renamed the file — the same 16.6.0 build, one letter's difference — and every attempt at the update hit a dead link. It now takes the filename straight from Oray's own download listing rather than guessing at it, so a future rename won't break it again.
An installer you've already downloaded no longer downloads again. Some apps update through an installer package that macOS opens for you to confirm. If you closed that window without finishing — or quit DuoUpdater and came back — the row went back to offering "Update", and taking it fetched the whole package a second time. ToDesk's is 375 MB. The download was on your disk the entire time; nothing was pointing at it. Those rows now offer "Install" instead, which just re-opens the file you already have. If the installer window is still open it comes forward rather than opening a second one, and the offer stands until either the download is gone or a newer version comes out — at which point the old package would be the wrong one, so the row goes back to a normal "Update".
Homebrew packages that aren't apps now show up. DuoUpdater tracked outdated Homebrew formulae, and left casks alone on the grounds that a cask installs an app, which already gets its own row. That holds right up until a cask installs no app — a command-line tool like codex, a font, a driver. Those had no row anywhere: nothing for the app list to find, and not a formula either. codex sat three versions behind without a word. They're now part of the Homebrew panel, which reads "packages" rather than "formulae" to match. Casks that do install an app are still managed per-app exactly as before, and apps that update themselves are still left to their own updater.
"Update All" now includes apps that install from a package. A handful of apps — ToDesk and AweSun among them — ship their update as an installer package rather than something DuoUpdater can swap into place on its own. Those were quietly left out of "Update All" and had to be updated one row at a time; if such an app was the only other update pending, the button disappeared altogether rather than acting on just one. They're now part of the batch, and they run at the very end: everything that updates unattended finishes first, so nothing opens a window or asks for your admin password until the rest is already done. One caveat worth knowing — DuoUpdater can't tell when macOS's installer has finished, so if two package updates come up in the same batch, both installer windows open one after the other rather than waiting in line.
Fixes updates going unnoticed for days at a time. DuoUpdater re-uses the answers it gets from each app's version feed so it isn't re-downloading the same file every few minutes. The problem was how long it trusted a stored answer: when a vendor's server doesn't say how long its reply stays valid, macOS guesses — and it guesses longer the longer that feed has gone unchanged. So the very feeds that had been quiet for a while were exactly the ones DuoUpdater stopped re-reading, and a new release could sit there for days with the app still reporting "up to date" and no error to show for it. Every version check now always asks the server whether anything changed, while still skipping the download when nothing has. OrbStack 2.2.2 is the release that surfaced this; the same blind spot applied to most apps checked directly against their vendor, including Chrome, Cursor, Claude, ChatGPT, Warp, Spotify, and Visual Studio Code.
Homebrew-managed apps no longer get stuck at the version they were on when DuoUpdater started. The catalog DuoUpdater reads to learn the latest version of a Homebrew app was loaded once per launch and never refreshed, which is invisible if you quit the app daily and wrong if you leave it running for weeks. It now refreshes periodically. As a bonus, machines with no Homebrew casks installed no longer download that 5 MB catalog at all.
Uses less memory and does less work in the background. This release is entirely under the hood — nothing about what DuoUpdater does has changed, only what it costs to leave running. Every update it downloaded used to leave a small amount of memory behind that was never reclaimed; harmless once, but it adds up over the weeks a menu-bar app tends to stay open. Separately, while the main window was open DuoUpdater re-read every installed app from disk every 15 seconds and started a system process each time to see what was running — that now happens every three minutes, since the filesystem watcher already notices a real change the moment it happens. Recording the release history after each check also used to save its file once per app rather than once per check, and release notes could be fetched more than once when the same page was already on its way in.
No more beachball while an app is relaunching. Clicking Update on another app while one was being quit and relaunched could freeze DuoUpdater for a moment — the spinning rainbow cursor, an unresponsive window, a click that seemingly did nothing. Relaunching an app now happens in the background instead of on the interface, so the rest of the list stays live and clickable throughout. The same freeze could show up when opening an app from a row's right-click menu, or when handing an update off to an app's own updater; both are fixed too.
Backups from uninstalled apps are now cleaned up automatically. DuoUpdater keeps one backup of an app's previous version so an update can be rolled back. Backups for apps you've since uninstalled or moved were never reclaimed and could quietly pile up gigabytes of disk space over time. They're now deleted automatically during the regular update check. Settings shows how much space backups are currently using, with a toggle to turn off the automatic cleanup and a "Clean Up Now" button to run it on demand.
JetBrains Toolbox apps no longer show a stuck or incorrect "update available." Version checks for Toolbox-managed apps (IntelliJ, Android Studio, Fleet, Air, and others) now always ask live rather than sometimes falling back to a local cache that could never actually report a new version — it fixes both a status that lingered after Toolbox had already installed the update, and one that never appeared in the first place.
Claude Desktop's release notes are now shown in DuoUpdater. Update entries for Claude Desktop now include Anthropic's own per-version changelog instead of a generic notice.
Update All now also relaunches apps that were only waiting on a restart. If an app had already downloaded its update and just needed a relaunch to finish — Claude, for instance — clicking Update All used to skip it, leaving a stray "Relaunch" button behind. It now relaunches those too, in the same pass, whenever automatic restart-after-update is on.
App Store updates recover from a receipt hiccup instead of just failing. Occasionally a Mac App Store update downloads in full but the very last install step trips over a "receipt" error — a transient App Store glitch that a second attempt usually clears. DuoUpdater now retries once automatically. If it still doesn't take, the row offers an "Open App Store" button to finish the update from the App Store's Updates page, instead of leaving a raw error on screen.
ToDesk update detection fixed. A change to ToDesk's download page stopped DuoUpdater from reading its latest Mac version, so ToDesk updates went unnoticed. Detection now reads the version reliably again.
App Store updates no longer show a scary error for an app that's already up to date. If the Mac App Store had quietly updated an app in the background — TestFlight, say — DuoUpdater's row could go stale and, on Update, try to reinstall the version that was already there. macOS's installer rejects that with an alarming red "The upgrade failed", even though nothing was actually wrong. DuoUpdater now confirms an App Store app really is behind before reinstalling, and treats a no-op reinstall as "already up to date" — settling the row quietly instead of showing an error.
App Store updates now ride out network hiccups. A brief connection drop mid-update — a flaky link, or a proxy resetting the connection — used to fail an App Store update outright with a "could not connect to the server" error. Those updates now retry automatically a few times before giving up, so a momentary blip no longer strands an update that a second attempt lands cleanly. Clicking Update again after a failure also clears the old error immediately, instead of leaving it on screen next to the spinner.
Apps that update themselves clear from the list faster. When an app like Chrome finishes updating itself in the background while an App Store update is running, its "update available" row now clears promptly — it no longer lingers until the rest of the queue finishes.
One-click updates for four more apps. HBuilderX, JetBrains Toolbox, and Microsoft Edge's Beta and Dev channels now update in place with a single click, instead of only telling you that an update exists. HBuilderX also now reads its version straight from DCloud's own release feed, so it picks up new builds sooner and more reliably.
Self-updating apps stay in their own lane. A running app that ships its own Sparkle updater is now handed off to that updater — the same courtesy DuoUpdater already gave other self-updating apps — instead of being replaced underneath it, unless you've chosen "Always replace" in Settings.
Fixes
Fixes
See when your apps actually ship. DuoUpdater now keeps a Release Log: a running timeline of every release the apps you track put out, each stamped with its publish time. Open it from the clock icon at the bottom of the popover.
Release-habit heatmap. A new Patterns view charts releases by weekday and hour, so you can see when an app tends to ship — pick any single app for its own pattern and version history, or view all of them together. History is backfilled from each app's update feed, so the heatmap is useful right away instead of starting empty.
Honest about what it can't time. Apps that publish an exact release date (Sparkle, GitHub, Alcove) are timed to the minute. Apps that only expose a version number get a clearly-marked "≈" estimated window — bounded by when DuoUpdater last saw the old version and first saw the new one — and never skew the heatmap.
Fixes
Passwordless App Store updates. Updating Mac App Store apps no longer interrupts you for your password every time. DuoUpdater now installs a small, signed privileged helper (one-time approval) and bundles mas, so App Store updates apply directly in the background.
Cleaner "Restart to finish" lines. When an app updates itself on disk while it's still running, the pending-restart line now shows the real marketing version on both sides — e.g. 1.8.x (build) → 1.9.0 (build) instead of a bare build number on the left.
Fixes
Apps that update themselves now clear correctly. If an app updated through its own updater (for example, Chrome via "About Chrome") while DuoUpdater was busy installing other updates, it could keep showing a stale "update available" row long after it was already current. DuoUpdater now re-checks the moment the installs finish, so the row clears right away instead of lingering.
"Update All" shows the whole queue. Every app in an "Update All" run now shows a "Queued" state immediately, instead of leaving the ones further down the list looking idle with a clickable Update button. Clicking Update on an app that's already queued can no longer start a second install of it.