前言¶
DSH Desktop 本身是交互式客戶端,”讓別的程序也能驅動它”通常要麼改客戶端源碼,要麼直接在宿主環境裏塞自寫腳本,維護成本都不低。WilShi/dsh-external-api 走另一條路:在 DSH 進程內掛一個可選的 HTTP 服務,默認關閉、默認只監聽 127.0.0.1,用 Bearer Token 鑑權,對外暴露會話、Run、SSE 事件這些能力。維護者是 WilShi。下面是介紹。
這是什麼¶
一句話定位:DSH Desktop 的可選外部 HTTP API,面向本機與可信局域網的集成場景。
插件包名爲 dsh-external-api,當前版本 0.2.4,MIT 許可證,Node 要求 ^22.19.0 || ^24.0.0。它兼容 DSH 0.2.1-rc.5(Host protocol v4)與 Cordis 4.0.5,插件形態是 Bundle(dsh.bundle.patch)加 Client inject(settings.section 槽位),也就是安裝後要在 DSH 設置頁裏點啓用,而不是改配置文件。
它不新開一套宿主環境。所有 @deepseek-ai/* 宿主包都聲明爲 peerDependencies,由 DSH 運行時提供;插件自身不安裝、也不會帶出第二套 cordis 單例。必需的宿主服務(inject)有 sessionController、workspaceController、agentPresets、sessionSkillCatalog、webServer。運行時依賴只有 @deepseek-ai/dsh-atomic-write(原子寫)和 @deepseek-ai/schemastery(配置 schema)。
核心功能¶
1、默認關閉。服務沒有啓用前不監聽任何端口。
2、默認只監聽本機。啓用後 host 默認是 127.0.0.1,只有本機能訪問;改成 0.0.0.0 或某個局域網地址纔對網絡開放,此時啓動日誌會打印一條網絡暴露告警。
3、Bearer Token 鑑權。除健康檢查和 CORS 預檢外,所有接口都要求 Authorization: Bearer <token>。
4、資源模型是 Session / Run / 事件流:列會話、建會話、發起 Run、查 Run 狀態、訂閱 SSE、取消 Run。
5、Token 級隔離。每個 Token 只能訪問自己創建的 Session、Run 和 SSE 流。
6、可訂閱的事件類型固定:run.queued、run.started、run.cancelling、message.delta、message.completed、tool.started、tool.completed、run.completed、run.cancelled、run.failed,SSE 支持 Last-Event-ID 斷線續傳。
7、能力探測接口。/api/v1/capabilities 返回當前 Token 能看到什麼,調用方可以先讀它再決定後續請求。
8、撤銷即失效。Token 撤銷會立即斷開它的 SSE 連接、嘗試取消活動 Run,並刪除其會話歸屬記錄。
安裝與啓用¶
安裝命令只有一條,來自 README:
dsh plugin --profile web add dsh-external-api
安裝後重啓 DSH,然後在「設置 → API 服務」裏啓用。之後按頁面提示填端口(默認 47831),爲調用方創建獨立 Token,並立即保存只顯示一次的明文。
開頭先驗證一下服務¶
端口按默認 47831,Token 已寫入環境變量 DSH_API_TOKEN。這一步的作用是確認 Listener 已起來、Token 有效,並看清這個 Token 具備哪些權限。
curl.exe http://127.0.0.1:47831/api/v1/capabilities `
-H "Authorization: Bearer $env:DSH_API_TOKEN"
創建一個會話併發起一次 Run¶
最省事的路徑是 POST /api/v1/responses:一次請求同時建會話、發 Run。下面用 Windows PowerShell 演示,局域網設備把 127.0.0.1 換成 DSH 主機的局域網 IPv4。
$body = @{ input = '請概括當前項目的結構' } | ConvertTo-Json -Compress
Invoke-RestMethod `
-Method Post `
-Uri 'http://127.0.0.1:47831/api/v1/responses' `
-Headers @{ Authorization = "Bearer $env:DSH_API_TOKEN" } `
-ContentType 'application/json' `
-Body $body
響應狀態是 202,裏面包含 session、run 和 eventsUrl。下一步拿這個 Run ID 去讀實時事件:
curl.exe -N http://127.0.0.1:47831/api/v1/runs/RUN_ID/events `
-H "Authorization: Bearer $env:DSH_API_TOKEN"
接口與 Scope¶
完整接口表(README 原文,Scope 名稱照抄):
| 方法 | 路徑 | 作用 | 所需 Scope |
|---|---|---|---|
GET / HEAD |
/api/v1/health |
Listener 健康檢查 | 無 |
GET |
/api/v1/capabilities |
能力、限制和當前 Token 權限 | capabilities:read |
GET |
/api/v1/workspaces |
Token 可使用的工作區 | capabilities:read |
GET |
/api/v1/agents |
Token 可使用的 Agent Preset | capabilities:read |
GET |
/api/v1/models?sessionId=... |
會話可用模型 | capabilities:read |
GET |
/api/v1/skills?sessionId=... |
會話可用技能 | capabilities:read |
GET |
/api/v1/sessions |
列出當前 Token 創建的會話 | sessions:read |
POST |
/api/v1/sessions |
創建會話 | sessions:write |
GET |
/api/v1/sessions/:id/messages |
獲取脫敏後的消息歷史 | sessions:read |
POST |
/api/v1/sessions/:id/messages |
發起 Run | runs:write |
POST |
/api/v1/sessions/:id/runs |
發起 Run(語義等同上一項) | runs:write |
POST |
/api/v1/responses |
創建會話併發起 Run | sessions:write、runs:write |
GET |
/api/v1/runs/:id |
查詢 Run 狀態 | runs:read |
GET |
/api/v1/runs/:id/events |
訂閱 Run 的 SSE 事件 | runs:read |
POST |
/api/v1/runs/:id/cancel |
取消 Run | runs:write |
創建會話時可以指定白名單裏的 workspaceId 和 agentPreset:
{
"workspaceId": "workspace-id",
"agentPreset": "agent-preset-id"
}
發起 Run 的請求體:
{
"input": "分析這個問題",
"clientTimeZone": "Asia/Shanghai"
}
瀏覽器端訂閱 SSE¶
瀏覽器原生的 EventSource 不能設置 Bearer Header,需要改用 fetch 流。注意:瀏覽器還必須位於服務允許的 Origin 白名單中,而當前設置頁尚未開放這項配置,普通網頁默認會被拒絕;下面代碼只適用於已經由發行版或受控配置啓用對應 Origin 的環境。
const response = await fetch(`${baseUrl}/api/v1/runs/${runId}/events`, {
headers: { Authorization: `Bearer ${token}` },
})
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`)
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()
for (;;) {
const { value, done } = await reader.read()
if (done) break
console.log(value) // 正式客戶端應按 SSE 幀解析,而不是假定一次讀取就是一個事件
}
錯誤格式¶
外部接口使用穩定的 JSON 錯誤結構,不回傳內部異常或宿主錯誤詳情。客戶端應以 HTTP 狀態碼和 error.code 作爲程序判斷依據。
{
"error": {
"code": "AUTH_INVALID",
"message": "The bearer token is invalid.",
"requestId": "..."
}
}
安全邊界¶
這一節建議先讀完再決定要不要改監聽地址。
1、默認只綁定 127.0.0.1。顯式把 host 設爲 0.0.0.0(或某個局域網地址)纔會對網絡開放。它不復用、也不轉發 DSH 主站的 /api 路由。
2、Token 明文只在創建時返回一次,持久化配置裏只保存 SHA-256 摘要。
3、工作區與 Agent Preset 默認不允許調用方自行指定,需要顯式加入 Token 白名單;白名單非空時,創建 Session 必須顯式指定被允許的 workspaceId / agentPreset,省略即 403。
4、對外開放範圍有明確上限:Slash Command、任意內部 RPC、工具參數、工具結果正文、系統提示和推理內容都不會對外開放。
5、Host 必須與客戶端實際訪問的 IPv4 和端口一致;Origin、Fetch Metadata、請求體大小、請求速率、併發 Run 和 SSE 連接數也都有限制。活動 Run 默認上限是每 Token 10 個、全局 20 個;舊版精確的 2/4 默認組合會在啓動時自動升級,其他自定義組合不變。
6、管理接口只掛在 DSH 自身的隨機迴環端口,並校驗同源瀏覽器請求,用於防止網頁跨站調用;它不把同一系統賬戶下的惡意本機進程當作可隔離的安全邊界。
7、管理接口寫入的配置保存在 $DSH_HOME/external-api/config-v1.json(目錄權限 0700、文件 0600),與 Token 歸屬記錄 ownership-v1.json 分開存放;加載器通過 cordis.patch.yml 下發的 config 作爲基礎層,管理頁的編輯覆蓋其上。
8、外部 API 是明文 HTTP。只在受信任的專用網絡中啓用,用系統防火牆把入站來源限制到所需私網;不要把端口直接映射或暴露到公網。跨不受信任網絡接入時,應使用 VPN 或受信任的 TLS 反向代理並限制來源網絡。
適用場景與注意¶
適合的場景:想在本機給 DSH Desktop 接一個外部編排腳本、Web 前端或局域網內的另一臺設備,並且希望按 Token 粒度控制誰能建會話、誰能跑 Run。
需要留意的地方:
1、插件以當前 DSH 進程的權限運行,能訪問會話、工作區和工具執行結果,等於擁有該進程的同等能力。安裝前先讀源碼,確認 MIT 許可證與你的分發方式兼容,再看 cordis.patch.yml 下發了哪些配置。
2、它不是官方產品,目錄頁是社區站點,與 DeepSeek / 幻方沒有官方從屬關係。
3、從 0.2.1-rc.5 起的 DSH Host protocol v4 纔在這個兼容範圍內,用在其他版本上需要先確認。
4、Token 明文只顯示一次,創建時丟失只能重新生成。
結尾¶
這套接口的價值在於把”驅動 DSH”變成一次普通的 HTTP 調用,同時用默認本機綁定、Token 級隔離和明確的對外暴露上限把風險壓在可控範圍裏。目錄頁在 https://www.skillhub.cn/plugins/WilShi/dsh-external-api,倉庫在 https://github.com/WilShi/dsh-external-api。