前言¶
如果你用 systemctl --user 跑 dsh-web,大概經歷過這套動作:打開終端,敲 systemctl --user restart dsh-web,然後回到瀏覽器手動刷新頁面。如果有 agent 任務正在跑,那一輪 turn 就此中斷,還得手動補一句話讓它繼續。
dsh-restart-systemd 把這件事搬進了 WebUI:點一下側欄的重啓按鈕(或者輸入 /restart 命令),服務在約 3 秒後重啓,瀏覽器自動重連,被中斷的會話自動續接。下面介紹它的實現思路、安裝方法與使用邊界。
這是什麼¶
dsh-restart-systemd 是一個 DSH 插件,定位是「DSH WebUI 重啓按鈕(systemd 版)」,由 RoyougiShiki 維護,許可證 MIT,當前版本 0.1.0。
它只做一件事:調度 systemctl --user restart dsh-web,並把善後處理乾淨——瀏覽器自動重連、被中斷的 agent 會話自動續接。主場景是 WSL/Linux 下的 systemd 用戶態(已實測),Windows 原生分支爲佔位。
核心實現¶
觸發與調度¶
插件提供兩個觸發入口,行爲一致:
- 側邊欄 footer 的重啓按鈕,位於 remote-web-ui 電話圖標旁,點擊後彈出二次確認:「進行中的 agent 任務將中斷並自動續接,頁面約幾秒後自動重連。」
- 聊天輸入
/restart命令,返回確認文本。
確認後前端 POST /api/restart-dsh。host 側先把當前 running 的 agent id 快照到續接列表,寫入 flag 文件,隨即返回 202 {scheduled:true, delayMs:3000},3 秒後再 spawn systemctl --user restart dsh-web。spawn 用白名單 argv,沒有 shell 拼串。延遲 3 秒的目的,是讓瀏覽器先收到 202。
防濫用保護¶
重啓是破壞性操作,插件做了幾層防護:
- 單飛鎖:已有一發在途時,重複 POST 返回
409 already-scheduled。 - 殘留窗口:插件驅動的 boot(flag 被消費)後 15 秒內再次點擊返回
429,防重試乒乓。 - loopback fence:socket/Host 非
127.0.0.1/::1、sec-fetch-site爲cross-site或 Origin 不同源,返回403;X-Forwarded-For永不被信任。
flag 文件 $DSH_HOME/dsh-restart.flag 充當一次性 boot token:spawn 前寫入,boot 時消費一次即刪,絕不自行觸發重啓。殘留 flag 因此無法造成二次重啓循環。
自動重連與會話續接¶
服務重啓後,瀏覽器經 ConnectionController 指數退避(500ms→10s)自動重連,無需手動刷新。
新 boot 上,插件消費 flag、裝上 agent/created 監聽、讀取續接列表 $DSH_HOME/dsh-restart-resume.json:對最後 turn 被中斷(存在未閉合 turn/start,或最近一次 turn/end.reason === 'interrupted')的會話,執行 agent.followup("Continue.") 自動續接。正常結束的會話絕不打擾。恢復是盡力而爲:60 秒 RECOVERY_TIMEOUT_MS 窗口內未重建的 agent 會被丟棄。
雙面插件¶
這是一個雙面插件,經 cordis.patch.yml 注入一行,安裝後 host 與 client 兩半同時掛載:
| 半邊 | 運行環境 | 職責 |
|---|---|---|
| host | node(宿主) | /api/restart-dsh 路由、/restart 命令、flag/resume 狀態 |
| client | 瀏覽器 | 側欄按鈕 UI、二次確認、狀態反饋 |
路由只註冊 /api/restart-dsh,避開 /plugins 前綴,無 bundle 供數衝突。
安裝與啓用¶
先構建,需要 devDependencies:
cd dsh-restart-systemd
npm install
npm run build # tsc -b → lib/
裝入 web profile:
dsh plugin --profile web add /tmp/dsh-restart-systemd
安裝後無熱重載,需重啓服務生效:
systemctl --user restart dsh-web
驗證插件已加載:新開一個會話,journalctl --user -u dsh-web 出現 dsh-restart-systemd: 開頭的日誌,且側邊欄 footer 行出現重啓圖標。
典型用法¶
完整流程是四步:
- 點擊側欄重啓按鈕(或輸入
/restart),完成二次確認。 - 前端 POST
/api/restart-dsh,收到202。 - 約 3 秒後服務重啓,頁面短暫顯示「正在重啓…」,隨後自動重連。
- 若重啓前有 running agent,journald 出現
recovery armed for N session(s)與resuming interrupted agent <id>,對應會話自動繼續。
整個過程可以在 journald 裏按順序看到三條日誌:
restart scheduled … delayMs=3000
spawning restart for dsh-web …
consumed leftover restart flag
驗證 loopback fence,僞造 Host 發跨站請求,預期返回 403:
curl -H 'Host: evil.example' -X POST http://127.0.0.1:3080/api/restart-dsh
# 403 forbidden: loopback-only
單飛可以快速連點兩次按鈕驗證,第二次返回 409 already-scheduled。
回滾:
dsh plugin --profile web remove ui-dsh-restart-systemd
systemctl --user restart dsh-web
rm -f ~/.dsh/dsh-restart.flag ~/.dsh/dsh-restart-resume.json
重啓後按鈕與命令即消失,不改動其它配置;兩個狀態文件隨時可刪。
適用場景與注意¶
這個插件適合主環境爲 WSL/Linux、用 systemctl --user 啓動 dsh-web、且頻繁需要重啓服務加載插件或改配置的開發者。
使用前瞭解幾個邊界:
- 單一服務單元:systemctl 命令固定爲
dsh-web,沒有配置項可改。 - 非 systemctl 啓動則不生效:若以
node …手跑 dsh-web,spawn systemctl 會失敗並記日誌,不產生重啓。 - 恢復是盡力而爲:60 秒窗口內未重建的 agent 被丟棄;clean 會話永不被主動續接。
- Windows 爲佔位:原生 Windows(非 WSL)走 detached helper 分支,但 helper 本體未隨包附帶,機器相關;WSL 主場景不進入此分支。
- 版本 0.1.0:package.json 的 homepage/bugs 字段仍是
<your-org>佔位。
最後是一條通用提醒:插件以當前 dsh 進程的權限運行,而這個插件能觸發服務重啓。雖然它自帶 loopback fence,但插件機制本身意味着相當的信任——安裝前應檢查源碼與許可證(本項目爲 MIT)。
結尾¶
dsh-restart-systemd 做的事情小而剋制:讓「重啓服務」不再打斷你的工作流。點一下按鈕,瀏覽器自動重連,被中斷的會話自動續接,防重啓循環的保護做在調度層裏。如果你的 dsh-web 跑在 systemd 上,值得一試。
- 目錄頁(社區維護的獨立站點,與 DeepSeek / 幻方無官方從屬關係):https://www.skillhub.cn/plugins/RoyougiShiki/dsh-restart-systemd
- 源碼:https://github.com/RoyougiShiki/dsh-restart-systemd