dsh-workspace-upload:把工作區文件管理搬進 dsh 聊天界面

前言

用 dsh 的 Web profile 跑智能體時,會話的工作區是智能體讀寫文件的地方。但 GUI 本身沒有文件入口:想把本地一份 CSV 交給智能體,得先登到服務器上拷進工作區;想確認智能體剛生成了哪些產物,也只能切回終端 ls 一遍。dsh-workspace-upload 補的就是這個缺口——在聊天界面裏直接瀏覽、上傳、下載、重命名、新建和刪除會話工作區中的文件。

下面按功能、架構、安裝、API 的順序介紹這個插件。

這是什麼

dsh-workspace-upload(版本 0.1.0,MIT 許可證)由 LI-Huaa 維護,是一個面向 dsh Web GUI 的工作區文件管理插件。

安裝後,聊天輸入區左側會出現「文件 (Files)」按鈕,點開一個覆蓋在應用上的文件管理對話框。插件的所有操作都限定在工作區根目錄內,工作區按「當前會話 → cwd → 第一個註冊的工作區」的順序解析。

核心功能

對話框裏能做的事:

1、瀏覽工作區及其子目錄,帶麪包屑導航和上級/刷新按鈕;
2、多文件分塊上傳,支持任意大小的文件(默認 640 KiB 一塊,遇代理體積限制自適應縮小,最低降到 64 KiB 後重試);
3、下載文件;
4、重命名文件和文件夾;
5、創建文件夾;
6、刪除文件和文件夾,刪除有兩步確認。

安全邊界寫在宿主側:所有 path / name 值都會淨化並做遏制檢查,.. 和絕對路徑逃逸一律拒絕;上傳寫入的是目標目錄內一個不會逃逸的隱藏臨時文件。

架構:一個包,兩半

插件是一個包、一條 profile 行、兩個半體:

半體 文件 職責
宿主半 lib/index.js 在 dsh web server 上註冊 GET/POST /api/workspace-upload,覆蓋 list / rename / mkdir / delete / download / 分塊上傳,全部帶工作區遏制檢查
客戶端半 lib/client.js 瀏覽器 bundle(經 window.__ModuleLoader__.load 加載):觸發按鈕掛在 conversation.input.left,對話框由按鈕自身渲染(position: fixed 覆蓋層)

接線方式:package.jsondsh.bundle.patch 指向 cordis.patch.yml,由它在 web 組合裏插入名爲 workspace-upload 的一行;dsh.client.platform: "web" 把這個包標記爲瀏覽器 roster 條目,bundle 經 exports["./client"] 提供。

安裝與啓用

插件以 dsh profile bundle 的形式安裝。在持有該包目錄的檢出裏執行:

dsh plugin --profile web add ./dsh-workspace-upload
# 或從克隆目錄:dsh plugin --profile web add /path/to/dsh-workspace-upload

然後重啓 web profile(dsh web ...)讓加載器讀到新行,再刷新瀏覽器即可。卸載:

dsh plugin --profile web remove dsh-workspace-upload

API 用法

宿主半隻暴露一個路由 /api/workspace-upload,GET 和 POST 各有分工。

GET:探測與下載

不帶參數時返回解析後的工作區目錄;帶 ?sessionId&path&name 時以附件頭下載對應文件:

GET /api/workspace-upload
# 無參數 → { "workspace": "<resolved workspace dir>" }
# 帶 ?sessionId&path&name → 以附件頭返回文件內容

JSON POST:文件管理操作

path 是工作區相對目錄("""sub""sub/deep"),name 始終是單段路徑:

{ "mode": "list",   "sessionId"?, "path"? }                     { workspace, path, entries:[{name,type,size,mtime}] }
{ "mode": "rename", "sessionId"?, "path"?, "name", "newName" }  { ok, from, to }
{ "mode": "mkdir",  "sessionId"?, "path"?, "name" }             { ok, path }
{ "mode": "delete", "sessionId"?, "path"?, "name" }             { ok, deleted }

分塊上傳

大文件走三步協議:先 chunk、再 finish,中途放棄用 abort

{ "mode": "chunk",  "sessionId"?, "path"?, "transferId", "name", "offset", "data", "total" } → { received }
{ "mode": "finish", "sessionId"?, "path"?, "transferId", "name", "total", "overwrite"? }     → { status, path, bytes }
{ "mode": "abort",  "transferId" }                                                            → { aborted }

幾個值得知道的細節:

  • GUI 默認發 640 KiB 一塊,base64 後約 853 KiB,壓在 nginx 默認 client_max_body_size 1 MiB 之下;如果裸收到 413,客戶端會把塊體積減半重試,最低降到 64 KiB。
  • 服務端限制:單請求 32 MiB(一個分塊加開銷),每分塊解碼後 8 MiB。
  • 分塊追加到目標目錄內的隱藏臨時文件 .dsh-upload-<transferId>finish 時重命名爲目標文件;分塊必須從 offset 0 起按序到達。
  • 已接收 offset 上的重複分塊會得到冪等應答,客戶端可以安全重試;孤兒傳輸 30 分鐘後清理。
  • overwrite: true 覆蓋已有文件,否則跳過並返回 status: "skipped"

舊版批量上傳

爲兼容 API/curl 調用,保留了單次批量 base64 模式:

{ "sessionId"?, "files": [{ "name", "data": "<base64>", "overwrite"? }] }
  → { "workspace": string, "results": [{ name, path?, status, bytes?, error? }] }

適合在腳本里一次性推幾個小文件,不必走分塊流程。

開發與測試

兩個測試都不需要 dsh 實例,直接驅動真實的宿主處理器和客戶端 bundle 工廠:

node test/protocol.mjs   # 宿主協議:list/rename/mkdir/delete/download/分塊上傳
node test/simulate.mjs   # 客戶端內核:對 slots fake 做槽位註冊

適用場景與注意

適合的人羣很明確:用 dsh Web profile、且經常要在本地和會話工作區之間搬文件,或者想在 GUI 裏查看智能體產物的人。如果你的操作全部發生在終端裏,這個插件幫不上什麼。

幾點注意:

1、插件以當前 dsh 進程的權限運行,能觸達該進程能觸達的工作區文件。安裝前建議檢查源碼與許可證(MIT),確認無誤再用。
2、路由繼承 dsh web server 的綁定,默認是 localhost。要把 GUI 暴露到遠端,需放在與主應用相同的 TLS/Basic-Auth 反向代理之後;想減少分塊次數,可以調高代理的 client_max_body_size
3、分塊必須按序到達。GUI 客戶端自己會處理重試與降塊,如果繞過它直接寫調用方,重試和順序要自己保證。

結尾

回顧一下:dsh-workspace-upload 把「文件怎麼進出工作區」這件 Web profile 下原本要切終端的事,收進了聊天界面的一個按鈕裏;宿主側的路徑遏制和分塊冪等設計,讓它在有代理體積限制的環境下也能穩定工作。

  • GitHub:https://github.com/LI-Huaa/dsh-workspace-upload
  • 社區目錄頁:https://www.skillhub.cn/plugins/LI-Huaa/dsh-workspace-upload

目錄爲社區維護的獨立站點,與 DeepSeek / 幻方無官方從屬關係。

羽毛球分组比赛记分
小程序二维码

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

小夜