LI-Huaa/dsh-workspace-upload ↗★ 1
dsh-workspace-upload
A workspace file manager plugin for the dsh web GUI: browse, upload, download, rename, create and delete files in the session workspace.
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:LI-Huaa/dsh-workspace-upload说明文档
阅读完整 README ↗dsh-workspace-upload
English | 中文
A workspace file-manager plugin for the dsh Web profile — browse, upload, download, rename, create and delete files in the session's workspace, right from the chat UI.
Features
- A 文件 (Files) button in the composer's left controls opens a file-manager
dialog over the app:
- browse the workspace and its subdirectories (breadcrumb navigation, up / refresh);
- upload — multi-file, chunked, any size (640 KiB chunks, adaptive shrink under proxy body limits);
- download files;
- rename files and folders;
- create folders;
- delete files and folders (two-step confirm).
- Every operation is scoped to the workspace root: host-side path containment
rejects any
..or absolute-path escape. - The workspace resolves to the session's workspace (session → cwd → first registered workspace).
Directory structure
dsh-workspace-upload/
├── cordis.patch.yml # profile patch layer: inserts the plugin row
├── package.json # dsh.bundle / dsh.client declarations, exports
├── lib/
│ ├── index.js # host half: /api/workspace-upload file-manager route
│ └── client.js # client half: browser bundle (button + dialog)
├── test/
│ ├── protocol.mjs # host protocol tests (real handler, no dsh needed)
│ └── simulate.mjs # client kernel simulation (slot registration fake)
├── LICENSE # MIT
├── .gitignore
├── README.md # English
└── README-zh.md # 中文
Architecture
One package, one profile row, two halves:
| Half | File | Role |
|---|---|---|
| Host | lib/index.js | Registers GET/POST /api/workspace-upload on the dsh web server: list / rename / mkdir / delete / download / chunked upload, all with workspace containment. |
| Client | lib/client.js | Browser bundle (window.__ModuleLoader__.load): the trigger button in conversation.input.left; the dialog renders from the button itself (plain local state, position: fixed overlay). |