dsh-app-updater:檢測本機已裝應用並從官網一鍵靜默更新

前言

DSH 的插件體系裏,能跑的插件很多,能替你動本機環境的少。智能體經常需要知道「這臺機器上裝了哪些應用、有沒有新版」,但常見的做法要麼是讓模型自己拼 winget upgrade 命令,要麼是寫一段只針對某個軟件的腳本,裝完一個再補一個。這類做法的問題是:列不全、不認架構、不知道靜默參數,而且一旦執行就是以當前進程權限直接裝。

下面介紹 dsh-app-updater。它把「列出已裝應用 → 比對新版本 → 從發佈方官網下載全量安裝包 → 按官方靜默參數安裝」這條鏈路做進一個 DSH 插件,入口在設置界面裏,不需要命令行也能用。

這是什麼

dsh-app-updater,作者 ccgo26,MIT 協議,當前包版本 1.3.15,分類在社區插件目錄的「聯網工具」下,GitHub 倉庫 1 個星標。

一句話定位:它是 DSH 的應用更新插件,檢測本機所有已安裝應用是否有新版本,並從各自官網下載完整安裝包一鍵靜默更新。檢測過程不需要打開被檢測的軟件;插件自動識別 Windows / macOS 與 ARM64 / x64(Intel)架構,按當前系統與芯片挑選對應的安裝包。

已知邊界先說清楚:Windows 上它只驅動 winget 目錄收錄的應用,另用註冊表補齊未收錄的;macOS 上只對 Homebrew 管理的應用提供一鍵更新,拖 DMG 裝的應用能列出、能查到官網有沒有新版,但不能一鍵升級。

核心功能

四個步驟,README 裏寫得很明確:

  1. 列出已裝應用:Windows 走 winget list(覆蓋 winget 目錄收錄的軟件 + 商店/MSIX 包),再用 Windows 註冊表三處 Uninstall 鍵補齊 winget 未收錄的軟件。全程只讀文件與註冊表,不啓動任何應用。
  2. 判斷有沒有新版本:winget upgrade --include-unknown,由 winget 官方軟件目錄給出「已裝版本 → 可升級版本」,再與第 1 步的清單按 Id 合併。
  3. 一鍵更新:winget download 把發佈方官網的完整安裝包下載到指定目錄(下載時按清單核對 SHA-256);執行前插件會再在本地複覈一次哈希(1.3.5 起),不一致就不執行、改用兜底;接着解析隨包落盤的 winget 清單,取出發佈方自己聲明的靜默參數,執行靜默安裝;裝完用 winget list --id 複覈版本號。
  4. 失敗兜底:若官網安裝包被髮布方重新簽發導致哈希與目錄記錄不一致、或清單禁止直接下載,自動退回 winget upgrade --id <id> --exact --silent——同樣是從官網拉完整安裝包,只是下載與安裝過程交給 winget。

「從對應官網下載」這一點值得單獨講:安裝包地址來自 winget 清單裏的 InstallerUrl,例如 Git 指向 https://github.com/git-for-windows/git/releases/download/...,CPU-Z 指向 https://download.cpuid.com/cpu-z/...,嗶哩嗶哩指向 https://dl.hdslb.com/...,Chrome 指向 https://dl.google.com/dl/chrome/install/...。下載目錄(默認 %USERPROFILE%\Downloads\DSH App Updater)裏會同時留一份 .yaml 清單,裏面有發佈方聲明的 InstallerUrl / InstallerSha256 / InstallerSwitches.Silent,可以自行覈對來源。

macOS 側的邏輯不同,也更容易誤讀。應用從 /Applications 等目錄枚舉出來,再按 cask 目錄反查它是不是 Homebrew 裝的;拖 DMG 或手動裝的應用會如實列出並標註「非 Homebrew 管理」,不會被算成可更新。所以一臺全是拖裝應用的 Mac 上,界面顯示「0 個可更新」是正確結論,不是 bug。

不過從 1.3.9 起,非 Homebrew 應用也能查到「官網最新版本」,因爲 macOS 應用自己帶着更新源:

類型 位置 讀什麼
Sparkle Info.plist 的 SUFeedURL → appcast.xml <sparkle:shortVersionString>(不是構建號)
Electron Contents/Resources/app-update.yml url + channel → 抓對應 mac 安裝清單頂層的 version:

查到後,界面會在「可升級到」列顯示官網版本並打上橙色「需手動」標籤,狀態列顯示「官網有新版」,狀態欄還會補一句「另有 N 個官網有新版,需手動升級」。這條路徑只用於展示:hasUpdate / updateSupported 依然只由 Homebrew 決定,查到版本不等於能替你升級。查詢全程只讀,不下載、不安裝、不啓動被檢測的應用。

1.3.10 起放寬了一點:能對上 cask 的非 Homebrew 應用可以直接一鍵更新,執行的是

brew install --cask --force <token>

它會移除現有 .app、裝上 cask 版本,之後該應用自動變成「Homebrew 管理」,後續走常規 brew upgrade --cask。README 解釋了爲什麼不是 --adopt:Homebrew 的 --adopt 只在已有 app 與要安裝的版本完全一致時才接受,版本不同會直接報錯,所以它只能「認領一個已經是最新的 app」,不能用來升級。對不上 cask 的應用仍然無法更新,界面顯示「需手動」並提示去應用內或官網升級;1.3.8 起這類行的提示會直接把對應命令寫出來,例如 brew install --cask --adopt iina。

安裝與啓用

安裝走 dsh plugin,推薦從 npm 裝,插件中心同樣可一鍵安裝:

dsh plugin --profile web add dsh-app-updater

剛發佈的新版本可能被 pnpm 的 minimumReleaseAge 策略暫緩一段時間;想立刻裝到指定版本就顯式寫版本號:

dsh plugin --profile web add dsh-app-updater@1.2.0

不想走 npm,也可以用源碼倉庫或預構建包:

# 源碼倉庫
dsh plugin --profile web add github:ccgo26/dsh-app-updater

# 預構建 tgz(見 Releases)
dsh plugin --profile web add https://github.com/ccgo26/dsh-app-updater/releases/latest/download/dsh-app-updater.tgz

依賴要求:Node ^22.19.0 || >=24.0.0,DSH >=0.1.5-rc.1 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0。

桌面端(Electron 託管的 desktop profile)不允許 dsh plugin 直接改寫,需在 profile 的 node_modules 下建 junction 並手動補 bundles。裝好後重啓 DSH,入口在 設置 → 應用更新。

典型用法

整個交互流程都在設置頁裏,沒有後臺任務:

  1. 打開 設置 → 應用更新。
  2. 點 刷新檢測。插件不會自動掃,只有你點了才掃。
  3. 列表默認按「有新版本優先」排序,可用搜索框和 全部 / 僅可更新 / 未收錄 / 需手動 分段篩選。
  4. 勾選要更新的應用,或點 全部更新 一次處理當前篩選下所有可更新項;點行尾 更新 可單個更新。
  5. 更新期間有進度條、當前應用名與逐條日誌,取消 會停止尚未開始的項目。
  6. 打開下載目錄 可以在資源管理器中查看已下載的官網安裝包。
  7. 同一頁的 插件設置 面板裏可以改下載目錄、保留安裝包、演練模式等開關。
  8. 表格列寬默認給足以完整顯示應用名,拖動表頭右側分隔條可調寬度,雙擊恢復默認,偏好記在瀏覽器本地。

狀態列的含義需要分清:有新版本 表示軟件目錄給出了更高版本號;已是最新 表示目錄裏沒有更高版本;未收錄 表示目錄裏找到了更新但本插件無法驅動(原因可能是軟件目錄未收錄、由應用商店管理、版本未知,或屬於系統組件)。紅色警告橫幅出現時說明這次掃描的判斷依據本身失效(Homebrew 命令失敗,或 cask 目錄抓不下來),此時「0 個可更新」不代表應用都是最新。

用 API 改設置

設置面板能做的,也能走 HTTP 接口,適合腳本化:

curl -X POST -H "Content-Type: application/json" \
  -d '{"confirm":true,"settings":{"dryRun":true}}' \
  http://127.0.0.1:<port>/dsh-app-updater/settings

downloadDir 會被校驗:必須留空(用默認目錄)或是絕對路徑(D:\Apps)/ UNC 路徑(\\server\share),否則返回 400,面板裏顯示紅字提示,不會把安裝包寫到意料之外的位置。

設置項

狀態文件:%USERPROFILE%\.dsh\dsh-app-updater\state.json

鍵 默認 說明
includeStoreApps true 是否把商店應用列進來(Windows 是 MSIX、macOS 是 App Store 應用;只展示,不會被更新)。
includeRegistryOnly true Windows:是否用註冊表補齊 winget 未收錄的應用(只展示,不能更新)。
includeUnmanagedApps true macOS:是否把非 Homebrew 管理的應用也列出來(只展示,不能更新)。
checkOfficialSources true macOS:是否讀取非 Homebrew 應用自帶的更新源判斷官網有沒有新版。只讀;關閉後這些應用一律顯示「不可判斷」,且掃描不再發起對這些域名的請求。
downloadDir 空 自定義安裝包下載目錄;空 = %USERPROFILE%\Downloads\DSH App Updater(macOS 爲 ~/Downloads/DSH App Updater)。
keepInstallers true 更新後保留安裝包;false 則在安裝完成後刪掉該應用剛下載的包。
hashFallbackUpgrade true 下載失敗時退回 winget upgrade 兜底。
dryRun false 演練模式:只下載官網全量安裝包、不執行安裝。用來先看會裝什麼。
matchSystemArch true 按識別到的系統與芯片架構挑選安裝包(Windows 傳 --architecture;macOS 由 Homebrew 決定)。
autoElevate true 自動提權安裝:需要管理員的安裝包改由管理員上下文執行;UAC 開啓時每次安裝會彈一次確認。

安全邊界

插件要下載並安裝可執行文件,安全約束寫得比較細,值得單獨看一節。

  • 路由默認拒絕:每個請求先過 ctx.connection.admit(request),即 DSH 用來做 Host/Origin 信任校驗與瀏覽器會話 / API 認證的那套邊界(宿主自己的 /api 路由用的就是它)。未通過的請求直接得到 401 / 403 / 409,響應頭帶 X-DSH-Admission: denied。
  • 兼容模式默認關閉(1.3.1 起):宿主沒有 connection 服務時不再「僅憑迴環地址放行」,一律 403。確實需要時必須顯式開啓,二選一:在 cordis.patch.yml 裏覆蓋 allowLegacyLoopback: true,或啓動 DSH 前設置環境變量 DSH_APP_UPDATER_ALLOW_LEGACY_LOOPBACK=1。開啓後放行的迴環請求帶 X-DSH-Admission: loopback-legacy-optin 與警告頭,設置頁頂部出現紅色提示條,日誌記一條 warn。
  • 來源圍欄(1.3.5 起):兼容模式下拒絕跨站瀏覽器請求,Origin / Referer 存在且與本次請求的 Host 不同源、或帶 Sec-Fetch-Site: cross-site 的一律 403。瀏覽器禁止腳本僞造這幾個頭,所以第三方網頁裏的無 Cookie fetch(…/update) 無法驅動「下載 + 提權安裝」;本機腳本與宿主 IPC 橋不受影響。
  • 寫操作顯式確認:scan / update / cancel / settings / open-downloads 必須帶 confirm:true。
  • 不猜測靜默參數:優先用發佈方在清單裏聲明的 Silent 參數;清單缺失時才按安裝包類型用保守的默認開關。
  • 執行前本地複覈 SHA-256(1.3.5 起):winget download 完成後,按隨包清單的 InstallerSha256 對流盤的安裝包重新計算一次哈希再執行——這個文件緊接着可能以管理員身份運行,中途被替換或損壞必須攔住;不一致就不執行、改用兜底。清單裏配對不出本次架構的哈希時跳過並記錄,絕不拿別的架構的哈希誤判。
  • 需要管理員時自動提權:機器級安裝包(MSI、寫入 Program Files)交給 Start-Process -Verb RunAs 的提權副本執行,可在設置裏關掉。

適用場景與注意

適合這類開發者:常常需要確認一臺開發機或跳板機上的軟件版本是否滯後,或者想把「檢查更新 + 裝更新」做成可復現的一步,又不想引入額外的更新守護進程。Windows 側依賴 winget 可用、macOS 側依賴 Homebrew 可用,插件本身不替代這兩個包管理器。

注意三點:

  1. 插件以當前 dsh 進程的權限運行。用它批量更新系統組件或機器級應用包時,實際執行的是提權副本,UAC 會逐個彈確認;不想看到確認就把 autoElevate 關掉。
  2. 安裝前應自己檢查源碼與許可證。本插件是 MIT 協議,倉庫在 github.com/ccgo26/dsh-app-updater;它下載的是發佈方官網的安裝包,安全邊界依賴 winget 目錄裏的清單是否可信。默認不開兼容模式、寫操作要 confirm:true,這兩個默認值建議保持。
  3. 非 Homebrew 應用的官網版本查詢只讀且可整開關(checkOfficialSources)。關掉後這些應用回到「不可判斷」,掃描不再發起對相關域名的請求——對在意外聯的開發環境可以直接關。

結尾

dsh-app-updater 解決的是一個具體問題:把「檢測本機應用版本 → 從官網取全量安裝包 → 按官方靜默參數安裝 → 複覈版本號」串成一條可審計的鏈路,並在下載後、執行前加了一道本地哈希複覈。它不是包管理器的替代品,而是把 winget 與 Homebrew 的能力整理成一個帶界面、帶狀態、帶安全邊界的 DSH 插件。功能集中在 Windows 的 winget 與 macOS 的 Homebrew 兩條路徑上,非 Homebrew 應用能列出和查到版本,但升級要麼收編進 Homebrew,要麼手動處理。

目錄頁:https://www.skillhub.cn/plugins/ccgo26/dsh-app-updater
倉庫:https://github.com/ccgo26/dsh-app-updater

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜