dsh-workspace-upload
English | 中文
面向 dsh Web profile 的工作区文件管理器插件——直接在聊天界面中浏览、上传、下载、重命名、新建和删除会话工作区中的文件。
功能特性
- 输入框左侧的 文件按钮(位于模式/命令选择器右侧)点击后打开文件管理弹窗:
浏览工作区及其子目录(面包屑导航、上级、刷新); 上传——多文件、分块、不限大小(640 KiB 分块,遇到代理体积限制时自适应缩小); 下载文件; 重命名文件与文件夹; 新建文件夹; 删除文件与文件夹(两步确认)。
- 所有操作都被限制在工作区根目录内:host 端路径包含校验会拒绝任何
..或绝对路径逃逸。 - 工作区按会话解析(会话工作区 → cwd → 第一个已注册工作区)。
目录结构
dsh-workspace-upload/
├── cordis.patch.yml # profile 补丁层:插入插件行
├── package.json # dsh.bundle / dsh.client 声明、exports
├── lib/
│ ├── index.js # host 半区:/api/workspace-upload 文件管理路由
│ └── client.js # client 半区:浏览器 bundle(按钮 + 弹窗)
├── test/
│ ├── protocol.mjs # host 协议测试(真实 handler,无需 dsh 实例)
│ └── simulate.mjs # client 内核模拟(插槽注册假服务)
├── LICENSE # MIT
├── .gitignore
├── README.md # English
└── README-zh.md # 中文架构
一个包、一行 profile 配置、两个半区:
| 半区 | 文件 | 作用 |
|---|---|---|
| Host | lib/index.js | 在 dsh web 服务器上注册 GET/POST /api/workspace-upload:list / rename / mkdir / delete / download / 分块上传,全部带工作区路径包含校验。 |
| Client | lib/client.js | 浏览器 bundle(window.__ModuleLoader__.load):触发按钮注册在 conversation.input.left;弹窗由按钮组件自身渲染(纯本地状态,position: fixed 遮罩)。 |
接线方式:
package.json中dsh.bundle.patch指向cordis.patch.yml(插入插件行的 profile 层),dsh.client.platform: "web"将该包标记为浏览器注册表条目,其 bundle 通过exports["./client"]提供。cordis.patch.yml向 web 组合配置插入一行workspace-upload。
API
GET /api/workspace-upload
→ { "workspace": "<解析出的工作区目录>" } (无参数)
→ 带附件头的文件下载 (?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 }分块上传(不限大小;GUI 每块 640 KiB——base64 请求体约 853 KiB,低于 nginx 默认的 client_max_body_size 1 MiB;遇到裸 413 时客户端把块大小减半重试,最低 64 KiB):
{ "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 }分块追加写入目标目录内的隐藏临时文件 .dsh-upload-<transferId>,finish 时重命名为最终文件名。分块必须从 offset 0 按序到达;已接收偏移的重复分块会幂等返回当前偏移(客户端可安全重试)。超过 30 分钟无活动的孤儿传输会被自动清扫。
为兼容 API/curl 保留旧的单次批量模式:
{ "sessionId"?, "files": [{ "name", "data": "<base64>", "overwrite"? }] }
→ { "workspace": string, "results": [{ name, path?, status, bytes?, error? }] }限制:单请求 32 MiB(一个分块 + 开销),单分块解码后 8 MiB。overwrite: true 覆盖已存在文件,否则以 status: "skipped" 跳过。
安装
该包作为 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开发
两个测试都不需要 dsh 实例(直接驱动真实的 host handler 与 client bundle 工厂):
node test/protocol.mjs # host 协议:list/rename/mkdir/delete/download/分块上传
node test/simulate.mjs # client 内核:插槽注册(忠实模拟的 slots 假服务)安全说明
- 所有
path/name都会在 host 端进行净化与包含性校验;上传使用隐藏临时文件,绝不会逃逸出目标目录。 - 路由继承 dsh web 服务器的绑定(默认 localhost);远程暴露 GUI 时,应与应用其余部分一样置于 TLS + Basic Auth 反向代理之后(如需更少、更大的分块,可调大
client_max_body_size)。