前言¶
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