前言¶
DSH 桌面版的端口每次啓動都是隨機分配的(--port 0),所以每次重啓都等於換了一個 origin:舊頁面失聯、殼自動開新頁面、你被扔回首頁。想「重啓一下看效果」,原本要關窗口、找圖標、等啓動、手動點回會話。客戶端插件的改動(slot 註冊、界面代碼)本來只需要重新加載頁面就能生效,但手按 F5 會讓當前會話掉回首頁——因爲 DSH 的當前會話只活在頁面裏、不在 URL 上。
下面介紹的 @ray1270/dsh-restart 就是解決這兩件事的客戶端插件。
這是什麼¶
維護者 Ray1270(package.json 裏 author 爲 Ray_lv)發佈在 npm 上的客戶端插件(dsh.client.platform = web),當前版本 0.3.7,MIT 協議。它給 DeepSeek Harness 桌面版的窗口頂欄加兩個常駐按鈕:
應用 編輯 刷新 重啓
刷新只重裝瀏覽器這一半,Host、會話記錄、正在跑的 agent 都不動;重啓會重啓整個應用(Electron 殼 + Host),但回來時停在你剛纔那個會話。
兩個按鈕分別做什麼¶
刷新:重新加載當前頁面。Host 不動、會話記錄不動、正在跑的 agent 也不動,只把 Web 客戶端重新裝一遍。加載完仍然停在剛纔那個會話(會話標記 2 分鐘內有效)。代價是輸入框裏沒發出去的草稿會丟,已發送的對話、正在生成的回覆、後臺 job 都不受影響。由於不跨 origin,刷新這條鏈路上沒有任何一次網絡請求。
重啓:重啓整個應用。第一點是預檢,按鈕顯示「確認重啓 · N 項」,N 是此刻會被中斷的 agent / job / 終端數量,4 秒不點自動撤銷;第二點是執行,1–3 秒後應用退出並自動拉起,回來時停在同一會話(30 分鐘內有效)。確認過就是真中斷:會話記錄是追加寫,丟的是進行中那一輪和 job 輸出。
之所以刷新一下、重啓兩下:刷新不殺服務端任何東西,代價最大隻是草稿;重啓會真的中斷活動任務,必須讓人看清數字再決定。
點失敗(重啓失敗 / 刷新失敗)時按鈕 6 秒後復位,原因在 tooltip 裏。
安裝與啓用¶
已發佈到 npm,一行命令:
dsh plugin --profile desktop add @ray1270/dsh-restart
裝完重啓一次應用纔會加載(這一次只能手動重啓,雞生蛋問題),之後就靠這兩個按鈕了。
dsh plugin add 走的是 pnpm add 到 profile,再把包名追加進 dsh.profile.bundles,不用手改任何文件。應用裏的「插件 → 添加插件」走的是同一條路,只是多一個填路徑的輸入框。
不走 npm 的話,到 Releases 下載最新的 ray1270-dsh-restart-<版本>.tgz,在「插件 → 添加插件」裏填它的絕對路徑。兩條路裝的是同一個文件——release 附件與 npm 上那份 tgz 的 sha256 逐字節相同。想自己覈對:
Get-FileHash .\ray1270-dsh-restart-<版本>.tgz -Algorithm SHA256 # 與同名 .sha256 文件比對
從源碼裝(開發):
dsh plugin --profile desktop add "<這個目錄的絕對路徑>"
裝完同樣要重啓一次應用。
配置¶
寫在 profile 的加載行 config: 裏(也可以直接改 cordis.patch.yml):
| 鍵 | 默認 | 含義 |
|---|---|---|
enabled |
true |
重啓總開關;false 時重啓報「不可用」並拒絕一切重啓請求(刷新不受影響) |
delayMs |
1200 |
動手前的等待,用來保證 HTTP 回執先到瀏覽器 |
hostGraceMs |
8000 |
等 Host 自己優雅退出的上限,超時才強殺 |
killTimeoutMs |
15000 |
等舊進程徹底消失的硬上限 |
pendingTtlMinutes |
30 |
「重啓後回哪個會話」標記的有效期 |
刷新沒有配置項:它不碰 Host,也就沒有可配置的危險動作。
刷新是怎麼做的¶
純客戶端,Host 不參與:
- 把當前
sessionId寫進 localStorage(同源,重載不丟); location.reload();- 頁面重新加載時 client 插件重新拉取、重新掛載,讀到標記後
uiWorkspace.openSession(sessionId),再刪掉標記。
刷新不跨 origin,所以標記放瀏覽器存儲就夠了,不必走 Host 文件。
重啓是怎麼做的¶
由 Host 側排程,helper 由 WMI 服務啓動,繞過應用自身的進程樹。整體流程:
- 瀏覽器
POST /api/dsh-restart/restart { sessionId, force }; - Host 做活動工作檢查(
force才放行),寫pending-restore.json(重啓後要回的會話); - 起一個短命的 powershell,調用
Win32_Process.Create→ 由WmiPrvSE.exe創建helper.ps1(用Win32_ProcessStartup的ShowWindow = 0讓它生來沒有窗口); - 等 helper 寫出 boot 狀態後纔回 200;
- helper 複覈 ShellPid 的鏡像路徑仍等於目標 exe(防 PID 複用),
taskkill /F只殺 Electron 殼(不加/T),等 Host 自己優雅退出(最多 8 秒),再Start-Process重新拉起 exe(失敗重試一次並校驗新進程還活着),結果寫restart-state.json; - 殼一死,Host 的 IPC 管道斷開,Host 走自己的
application.shutdown.shutdown(0)優雅退出(5 秒上限)。
幾個關鍵點:
- 只殺殼,不殺樹。 Host 子進程註冊了
process.once("disconnect", () => stop()),殼一死它就自己走完整 dispose 流程,所以「硬殺」只硬在殼這一下。 - helper 必須活着跨過殼死掉那一刻。 這個應用的子進程樹不活過殼,普通子進程做不到(表現就是「只有關、沒有開」)。
Win32_Process.Create由WmiPrvSE.exe執行,helper 的父進程是 WMI 服務,殼死時它毫髮無損;helper.log的 boot 行會記下parent=…(WmiPrvSE)。 - 不用
detached: true。 Windows 上 Node 用detached啓動powershell.exe會讓它立刻以 0 退出,一行都不執行;真正讓 helper 活下來的是「由誰創建」。 - WMI 啓動失敗就放棄重啓。 短命 launcher 把結果寫進
launch-result.json;Host 等不到 helper 的boot或 WMI 明確拒絕,就回 500 並寫cancel-restart標記,還在延遲中的 helper 看到會立刻放棄。 - 創建的進程要生來隱藏。
-WindowStyle Hidden救不了(那是窗口存在之後纔去隱藏),必須構造Win32_ProcessStartup交給Create。
生態裏現成的安全重啓是 ctx.appExit(0),但它在桌面版等於讓 Host 自殺,殼會判定異常終止並彈出「應用意外停止」恢復框,多一次點擊、日誌裏留一次假崩潰。這個插件選的是「殺殼 + 重新拉起」,所以能一次點完。
發生問題時按可能性排序自查:
- 點了沒反應、應用還在——helper 沒起來(WMI 被禁用/攔、腳本被刪)。此時應用完全沒受影響。先看
~/.dsh/dsh-restart/launch-result.json,再看helper.log。 - 應用退出了但沒回來——helper 在殺之前就死了,或
Start-Process被攔。手動雙擊D:\dsh\DeepSeek Harness.exe;restart-state.json寫着停在哪一步。 - 點「刷新」後回到首頁——會話標記沒能寫下(無痕模式、存儲被策略關閉)或會話已被刪,刷新鏈路失敗一律靜默退回首頁。
- 想徹底繞過這一切——托盤右鍵退出,再雙擊圖標。
狀態文件都在 ~/.dsh/dsh-restart/:restart-state.json(最近一次重啓階段)、helper.log(帶時間戳的過程日誌)、launch-result.json(WMI 創建 helper 的結果)、pending-restore.json(待恢復會話標記,被消費後刪除)、cancel-restart(僅在 Host 放棄啓動時短暫出現)。
適用場景與注意¶
適合在 DeepSeek Harness 桌面版裏調試客戶端插件、或常需要重啓應用看效果的人。刷新在任何環境都可用;重啓只支持桌面版,CLI / headless 的 Host 沒有殼可重啓,按鈕會明確報「不支持」。
注意幾點:
- 插件以當前 dsh 進程的權限運行,安裝前建議先看源碼與許可證(MIT)。
- 頂欄入口依賴桌面殼自己畫的菜單條(
preload用data-windows-menu標記 + open shadow root),插件只能在它後面追加同級按鈕;探測不到就靜默不掛載,回落到會話標題欄最右側。兩條入口共用一套邏輯,頂欄那份優先。 - 同一時刻只有一個入口在屏幕上:頂欄掛載成功時標題欄那對渲染成空,但仍掛着(它是唯一拿得到
sessionId的地方,用於刷新/重啓後的會話回填)。 - 重啓依賴 WMI 服務。WMI 被策略關掉、或
Win32_Process.Create被安全軟件攔住時,重啓會明確拒絕(應用不受影響),錯誤和返回碼寫在launch-result.json。 - 殺軟可能好奇:「隱藏窗口的 PowerShell 殺掉應用再拉起」是 EDR 的經典啓發式。helper 是明文、帶完整註釋、固定路徑、日誌可查,沒有做任何編碼混淆。
- 極端情況下仍可能看到一個控制台窗口:某臺機器上
Win32_ProcessStartup構造失敗時 helper 會退回可見窗口啓動(重啓本身照常),launch-result.json裏windowHidden會是false。
小結¶
刷新解決「客戶端改動只需重裝頁面、又不想丟當前會話」,重啓解決「重啓後被扔回首頁」。兩者都以會話爲中心,代價提示和失敗原因都寫在界面上和日誌裏。
目錄頁:https://www.skillhub.cn/plugins/Ray1270/dsh-restart
倉庫:https://github.com/Ray1270/dsh-restart
npm:https://www.npmjs.com/package/@ray1270/dsh-restart