dsh-plugin-sync:把 DSH 插件清單搬進你的私有 GitHub 倉庫

前言

DSH 的理念是「一切皆插件」。實際用過幾個 profile 之後,問題會慢慢變成第二類問題:插件裝在哪臺機器上就跟着哪臺機器走。換新機器要重新找倉庫、重新 clone、重新填一份配置;從本地目錄(link: / file:)裝的插件甚至連「重新找」這一步都沒有,因爲本來就沒有 registry 版本可重裝。

dsh-plugin-sync 給出的辦法是:把插件清單、版本、啓用狀態和配置寫進你自己的 GitHub 私有倉庫,不經過任何第三方雲。插件由 Jfreey 維護,在社區目錄裏歸在「聯網工具」分類下,倉庫目前星標仍是 0,屬於早期項目。

下面介紹它做什麼、怎麼裝、怎麼用、以及有哪些必須知道的邊界。

這是什麼

一句話定位:它用你自己的私有 GitHub 倉庫當 DSH 插件的清單和源碼備份,跨機器同步靠「推送 / 拉取」兩端自己確認。

它不替代插件管理器。安裝、卸載、升級仍然在 DSH 的插件管理器裏做;本插件只負責把「當前裝了什麼、什麼版本、哪些配置、來源在哪」記錄下來,推送到倉庫,再在別的機器上按相同的記錄裝回來。

維護者是 Jfreey,許可證爲 MIT。源碼託管在 GitHub 的 Jfreey/dsh-plugin-sync,目錄頁是 skillhub.cn 上的插件索引頁(社區目錄,與 DeepSeek / 幻方無官方從屬關係)。

已覈實的核心能力

按 README 的「做什麼」一節整理,下面這些能力都能在倉庫裏覈對。

清單:記錄所有非安裝自帶的 bundle,包括名稱、版本、啓用狀態、安裝來源、聲明的 row id。

來源追溯:每個插件從哪來,優先級是本地 checkout 的 .git remote、其次包自身的 repository 字段,monorepo 會一直顯示到子目錄;查不到就標「來源未知」,不猜。

配置備份:備份 profile 裏這些 row 的 override 層。離開本機前,令牌形態的值統一替換成 <redacted>;apiKeyEnv 這類「指向密鑰的環境變量名」原樣保留,因爲它本身不是密鑰。

源碼備份:link: / file: 安裝的插件沒有 registry 版本,源碼會以真實文件提交到同一個私有倉庫,一次推送一個提交,可以在 GitHub 上直接讀、看 diff、用倉庫搜索。還原時下載、校驗 SHA-256、寫盤,再 link 安裝。

還原:缺失安裝、版本替換、寫入配置、切換啓用狀態,分組勾選。README 明確寫了「你沒點,它不動」,每條結果回報宿主真正做了什麼。

更新檢查:來源發佈了比本機新的版本時給提示;npm 裝的能一鍵更新,本地目錄的留給你自己處理。

清理:每行一個「同步」開關(隻影響這臺機器);「清理備份」能刪掉清單條目,也能連帶刪掉倉庫裏的源碼文件。

推送與拉取:推送在插件變更後自動觸發(30 秒防抖)加手動;拉取是定時加手動,只算差異,不會自己動手。

安裝與啓用

README 給出的安裝方式是把插件管理器指向倉庫目錄,本倉庫本身就是可安裝 bundle(package.json 聲明瞭 dsh.bundle):

dsh plugin --profile desktop add /絕對路徑/plugin-sync

注意命令裏的路徑是本地 checkout 或提取出來的目錄,不是 github: 形式。裝完刷新頁面,設置裏會出現「插件同步」入口。

要求 DeepSeek Harness 0.2.x,依賴 settings.section、ctx.locale、ctx.pluginManager、ctx.configEditor、ctx.credentials 這幾個契約。低於這個版本不會可用。

綁定倉庫與登錄

插件需要一個私有 GitHub 倉庫來承載數據。默認數據倉庫名是 dsh-plugin-sync-data,dsh-plugin-sync 這個名字留給插件自己的公開倉庫,兩者不撞名。

1、建私有倉庫:打開 github.com/new?name=dsh-plugin-sync-data&visibility=private,點擊 Create。README 特別提示要建空倉庫,不要勾 README。

2、生成細粒度令牌:頁面提供了預填鏈接,令牌的名稱、描述、90 天有效期、Contents: write 都已設置好。Repository access 選 Only select repositories,勾上剛建的倉庫,Generate,複製 github_pat_…。

3、登錄:把令牌粘進第一個輸入框,點「從我的倉庫裏選」——這一步令牌只用於列倉庫,不落盤——挑一個倉庫,再點「校驗並保存」,這時才真正登錄並存令牌。

README 裏有兩條容易被忽略的行爲。第一,登錄後先拉取、不會把本機狀態推上去,所以新機器用空清單或舊版本覆蓋共享 manifest 的風險被規避了;要讓倉庫第一次有內容,登錄後點一次「推送」。第二,倉庫必須是私有的,配置會離開本機,公開倉庫等於公佈;公開倉庫在登錄時和每次推送前都會被拒,除非顯式勾選允許公開倉庫的確認項。

選到一個不是同步倉庫的倉庫會被攔住:非空且沒有 manifest 的倉庫是別人的項目,頁面會列出其中幾個文件名,你確認後才寫;同名文件但格式不對則直接拒絕。經典令牌(repo scope)也能用,但權限更寬,README 不推薦。插件不讀 git 憑據助手,也不讀鑰匙串。

倉庫裏實際有什麼

根目錄是一個 dsh-plugin-sync.json,記錄設備和插件條目。結構如下,字段名和示例都來自 README:

{
  "schema": 1,
  "updatedAt": "2026-11-05T05:10:00.000Z",
  "devices": {
    "3f2a91c0d4e8": { "name": "workstation", "updatedAt": "…" }
  },
  "plugins": {
    "dsh-searxng-search": {
      "version": "0.5.0",
      "enabled": true,
      "source": "local:dsh-searxng-search",
      "repository": "https://github.com/octocat/dsh-searxng-search",
      "repositoryFrom": "git-remote",
      "config": { "searxng-search": { "proxy": "http://127.0.0.1:7890" } },
      "snapshot": {
        "format": "tree",
        "path": "snapshots/dsh-searxng-search/0.5.0",
        "commit": "9c33e2c…",
        "files": 18,
        "bytes": 292546,
        "sha256": "…"
      },
      "redacted": 1,
      "updatedAt": "…",
      "by": "3f2a91c0d4e8"
    }
  }
}

README 的截圖用的也是 octocat / workstation 這類虛構示例數據,不含真實賬號或機器信息。

源碼備份放在 snapshots/<名字>/<版本>/ 下,通過 Git Data API 提交,所以一次推送就是一個提交,倉庫保持可讀。文件按內容尋址,沒變的文件不重複上傳;被刪掉的文件在同一個提交裏刪除;可執行位保留。收哪些文件看 package.json 的 files 白名單(沒寫就全收),外加 package.json、cordis.patch.yml、locale/、assets/、scripts/、圖標和 README。node_modules、.git、.env*、日誌、軟鏈永遠不收。單文件超過 1 MiB,或整份快照超過 maxSnapshotBytes,整份拒絕,不會上傳半份。

兩臺機器寫同一個文件時,每次推送先讀當前版本、把本機條目合進去、再用讀到的版本號寫回;返回 409 說明別人先寫了,就重讀再合併,最多三次。本機沒有的遠端條目原樣保留,文件是並集,空機器推上去不會刪掉任何東西。

主要配置項

全部字段和默認值都寫在 README 的配置表裏,下面挑出最常用的幾項:

字段 默認值 含義
repo 空 私有倉庫的 owner/name。
apiBase https://api.github.com GitHub Enterprise 時修改。
manifestPath dsh-plugin-sync.json 清單文件的位置。
snapshotsPath snapshots 源碼備份的位置。
snapshotFormat tree tree 存真實文件;json 退回單文件 base64 快照。
deviceName 主機名 寫進清單的設備名。
deviceId 自動派生 穩定身份,爲空時按主機、平臺、home 派生。
autoPush true 插件變更後自動推送,30 秒防抖。
autoPull true 定時拉取清單。
intervalMinutes 15 定時週期,限制在 1–1440。
includeSnapshots true 上傳本地插件的源碼。
maxSnapshotBytes 4194304 單個插件的源碼總量上限。
maxManifestBytes 2097152 清單上限,超過直接拒絕。
excludedRows [] 這臺機器永不發佈的 row id。
excludedPlugins [] 這臺機器不推送、也不參與差異的插件。
allowPublicRepo false 允許公開的同步倉庫。

頁面裏改的設置存在 ~/.dsh-plugin-sync/settings.json,在 profile 之外,不重寫任何 profile 文件;cordis.patch.yml 裏的 row 配置提供默認值。

安全邊界

README 有一節「安全說明」,有四條值得在動手前讀完。

一是監聽範圍:插件只接受迴環瀏覽器的請求,/state 和 /api 要求自定義頭,且要求對端是 127.0.0.1 或 ::1。README 直接提示「請讓 Harness 只監聽迴環」。

二是令牌綁定主機:登錄時調 GET /user,並把 base 和令牌一起寫進憑據記錄;之後所有請求都用這個 base,改設置不可能把令牌送去別處,換 API 主機必須重貼令牌。

三是脫敏分兩層:敏感形態的鍵名和敏感形態的值(令牌形態、私鑰等)都變成 <redacted>,apiKeyEnv 這類引用保留。源碼裏出現令牌形態的值,整份拒絕並點名文件。清單不含本機絕對路徑,本地插件的路徑被替換成 local:<name>,還原靠源碼備份。

四是令牌存儲:存在憑據記錄 dsh-plugin-sync/github(存儲文件權限 0600),插件自己的設置文件同樣是 0600,沒有路由會返回令牌。

典型流程

結合上面的能力,換機器可以按這個順序走。

1、新機器上裝好本插件,按上面的命令指向 checkout 目錄,刷新頁面,進入設置 → 插件同步。

2、登錄:建好或選好私有倉庫,粘貼細粒度令牌,選倉庫,校驗並保存。這一步只有拉取,不會把新機器的空狀態推出去。

3、點「推送」給倉庫播種;之後這臺機器上的插件變更會自動推送(30 秒防抖),也可以隨時手動推。

4、回到原來的機器或任意新機器,插件同步頁會顯示差異,勾選要還原的條目(缺失安裝、版本替換、配置寫入、啓用狀態),點一次應用。每條結果都回報宿主實際做了什麼。

5、從本地目錄裝的插件會連帶還原源碼:下載、校驗 SHA-256、寫盤、link 安裝。不需要手動再 clone 一次。

適用場景與注意

適合這樣的場景:DSH 有多個 profile 或多臺機器,插件主要從 GitHub 倉庫裝、希望自己掌控備份位置,並且能接受「私有倉庫 + 手動確認」這層約束。兩個獨立存儲等於兩個 DSH profile,適合 profile 級隔離。

需要注意的地方也明確:插件以當前 dsh 進程的權限運行,能讀寫憑據記錄和本地文件,安裝前應檢查源碼與許可證(本項目爲 MIT)。它要求 Harness 0.2.x 的幾項契約,版本不符就用不了。同步倉庫必須保持私有,配置會離開本機。另外它是個人備份工具,不是發佈通道——作者的調研結論裏寫明,發佈分發是下一版要做的事,市場 PR 仍需手動。

結尾

dsh-plugin-sync 解決的是一個很具體的問題:把「DSH 裝了哪些插件、什麼版本、什麼配置、源碼在哪」放進你自己的私有 GitHub 倉庫,換機器時不用重新找一遍。清單、版本、配置、源碼備份、還原、更新提示、清理都在一個設置頁裏完成,密鑰不外發,公開倉庫和不可識別的 manifest 都會被拒絕。

目錄頁:https://www.skillhub.cn/plugins/Jfreey/dsh-plugin-sync
GitHub:https://github.com/Jfreey/dsh-plugin-sync

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

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

小夜