dsh-restart-systemd:給 DSH WebUI 加一個會自動續接會話的重啓按鈕

前言

如果你用 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 原生分支爲佔位。

核心實現

觸發與調度

插件提供兩個觸發入口,行爲一致:

  1. 側邊欄 footer 的重啓按鈕,位於 remote-web-ui 電話圖標旁,點擊後彈出二次確認:「進行中的 agent 任務將中斷並自動續接,頁面約幾秒後自動重連。」
  2. 聊天輸入 /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 行出現重啓圖標。

典型用法

完整流程是四步:

  1. 點擊側欄重啓按鈕(或輸入 /restart),完成二次確認。
  2. 前端 POST /api/restart-dsh,收到 202。
  3. 約 3 秒後服務重啓,頁面短暫顯示「正在重啓…」,隨後自動重連。
  4. 若重啓前有 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
羽毛球分组比赛记分
小程序二维码

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

小夜