a903067276-rgb/dsh-file-upload
一个上传按钮,外加将文件拖放到对话中作为本地路径:保存到项目uploads/目录,路径文本输入到输入框中,适用于任何视觉工具。
项目介绍Project Overview
DSH 文件上传插件,为 DeepSeek Harness 网页版提供上传按钮与拖放支持。支持多选文件、整文件夹递归读取,图片走官方附件通道(DeepSeek Files API,file_id 复用,7 天过期自动重传),其它文件归档后以路径文本插入。模型不支持图片时自动降级为路径文本,避免 400 错误。文件 64MB 硬限制,20–64MB 图片需在 cordis.patch.yml 调高 maxImageBytes。
dsh-file-upload is a DSH web plugin that adds an upload button and drag-and-drop to the composer. It supports multi-select files and folders, routes images through the official DeepSeek Files API attachment rail (with file_id reuse and 7-day auto-reupload), and archives other files as path text. Image routing falls back to path text when the model does not accept images, avoiding 400 errors. Files are capped at 64MB; images between 20–64MB require raising maxImageBytes in cordis.patch.yml.
请帮我了解并安装插件:【dsh-file-upload】【https://github.com/a903067276-rgb/dsh-file-upload】
把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.
或使用命令行安装(适合开发者)Or use CLI install (for developers)
命令行安装CLI Install
dsh plugin --profile web add "github:a903067276-rgb/dsh-file-upload#main"
把 a903067276-rgb/dsh-file-upload 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
dsh-file-upload ⬆️
One upload button + drag-and-drop files straight into the conversation — a plugin for DeepSeek Harness (dsh) web.
Unofficial project: independently developed and maintained by a community member, not an official DeepSeek product.
Screenshot

The upload icon button in the composer tool row (official DSH design tokens, follows dark/light theme); either the file's path is inserted into the input box, or — for supported images — the image lands in the official attachment rail (auto file_id reuse).
Features
| Action | Effect |
|---|---|
| Click upload icon | System file picker (multi-select) → smart routing → attachment rail and/or path text |
| Drag a file into the window | Images & any file type are taken over (no "unsupported" toast) → smart routing |
| Drag a folder into the window | Recursively reads the whole folder → rebuilds it under the attachment library with its original layout → only the folder path goes into the draft (contents not expanded) |
| Image (PNG/JPEG/WebP/GIF) | ① archived to the attachment directory (by-day folders) ② added to the official draft attachment rail → sent with automatic DeepSeek Files API file_id (same image reuses the id; 7-day expiry auto-reuploads) |
| Any other file | Archived to the library's files/ subfolder, <prefix> <path> text goes into the draft |
| Model without image support | Image falls back to archive + path text (never sent as an image block, no 400) |
- Single-file limit: 64 MB (DeepSeek Files API hard limit; the local attachment store defaults to 20 MB — see Large images below)
- Everything lands in the attachment library:
~/Documents/DSH/Attachments/images/<YYYY-MM-DD>/(images) or.../files/<YYYY-MM-DD>/(other files) — configurable in Settings - Button shows busy state while uploading; failures surface as Chinese notices
Settings card

- Attachment directory (
~/Documents/DSH/Attachmentsdefault,~supported) — only used for image archives - Path prefix (
[上传文件]default) — text prepended to paths in the draft; clear it to send bare paths - Images via official attachment (default on) — off = images follow the old path-text logic
- Archive images to the attachment directory (default on) — off = official attachment only (saves disk; if the official channel is unavailable, the image is still force-archived)
- Allow public uploads (default off) — off = same-origin check keeps allowing localhost only (CSRF-safe); on = any origin may upload, for public/port-forwarding access (e.g. ddnsto). Enable only if you trust everyone who can reach your DSH.
- Read-only display: current image size limit from the host
Large images (20–64 MB)
DeepSeek accepts up to 64 MB per image. DSH's local attachment store defaults to 20 MB; images above that (and ≤64 MB) are archived + path-text referenced (the model can still read them via read_image, which follows the same store limit).
To let large images through the official attachment path too, add this to ~/.dsh/profiles/web/cordis.patch.yml and restart dsh web:
- id: attachment-local
config:
maxImageBytes: 67108864 # 20 MiB → 64 MiB (DeepSeek hard limit)
Note: the whole config row is replaced, so keep every key you need; re-check against the DSH version after upgrades. The model always sees the harness-normalized version (≤2048px / ≤4 MiB, ≤384 tokens per image) regardless of the original size.
Install
Official bundle install (one line):
dsh plugin --profile web add "github:a903067276-rgb/dsh-file-upload#main"
Restart dsh web (bundle layers are composed at startup). Requires pnpm on PATH (dsh plugin forwards to pnpm).
Manual mount (fallback): see docs/install.md — symlink into ~/.dsh/profiles/web/node_modules/ plus a single entry in ~/.dsh/cordis.patch.yml (a double entry makes the plugin apply twice and crash on duplicate route registration), then restart.
Usage
- Click the upload icon and pick files (multi-select), or drag files/folders anywhere into the window.
- Images (when the current model accepts images): archived to the attachment directory and shown in the official attachment rail — send, and the model sees the image (DeepSeek Files API
file_id, auto-reused). - Images when the model does not accept images (or you turned the official path off): archived to
images/, then<prefix> <absolute path>lines go into the draft — e.g.[上传文件] /path/to/Attachments/images/xxx.png— and your existing draft text is kept. - Other files: archived to
files/, path text goes into the draft. Press send; the model reads the file by path. - Folders: drag one in and it is rebuilt under the attachment library with its original layout; only the folder path (one line) goes into the draft.
Platform support
| Platform | Status |
|---|---|
| macOS | ✅ fully tested (development environment) |
| Linux | ✅ expected to work (pure Node implementation), untested |
| Windows | ⚠️ expected to work (pure Node implementation, Windows-safe filename sanitization, platform separator paths), untested |
Requirements
- DSH web >= 0.1.0-rc.7 (run with
dsh web) - Version compatibility (best effort — new features verified locally on 0.1.1-rc.2; on 0.1.0-rc.7/rc.8 the official image rail can't be fully checked, not guaranteed):
- Maintenance policy: this plugin keeps evolving with the latest DSH releases; compatibility with older DSH versions is best-effort only and not guaranteed going forward.
| Your DSH version | Install this | Note |
|---|---|---|
| 0.1.1-rc.1 and newer | main (v0.1.5+) |
Full features (including the official image rail) |
| 0.1.0-rc.7 – 0.1.0-rc.8 | main (v0.1.5+) |
Works fine; the official image rail auto-degrades to path text unless the session model accepts images. Conservative fallback: v0.1.4 — dsh plugin add github:a903067276-rgb/dsh-file-upload#v0.1.4 |
| 0.1.0-rc.6 and older | v0.1.2 — dsh plugin add github:a903067276-rgb/dsh-file-upload#v0.1.2 |
Last release without the settings card (the card uses the rc.7+ keyed slot contract) |
- No extra shell needed: the host half is pure Node (
node:fs), no system commands required on any platform.
How it works
- Host (
lib/index.js):POST /api/file-upload/save— validates the session and size, writes the base64 payload to<library>/images/<YYYY-MM-DD>/(mode=image) or<library>/files/<YYYY-MM-DD>/(mode=file) with pure Node;POST /api/file-upload/save-folder— takes a relative-path + base64 list and rebuilds the tree under<library>/files/<date>/<timestamp>-<folder>/(each segment sanitized,..rejected to prevent traversal);GET/POST /api/file-upload/configreads/writes the settings (officialsettingsservice) and exposes the host image limit plus whether the current session's model accepts images (llm.resolveModelinputModalities— same source the adapter uses). - Client (
lib/client.js): registers the upload icon in theconversation.input.leftseat; capture-phase document listener takes over file drags and reads dragged folders viawebkitGetAsEntry; routing: supported image + official on + model supports + within host limit → archive +conversation.createDraftImages+inputActions.addImages(the official InputBar's own mechanism) → official attachment rail (no path text); anything else degrades to archive + path text; >64 MB is refused with a notice. - Error boundary: a render crash degrades to a small "⚠ upload component error" chip instead of unmounting the whole composer.
Notes
- The attachment library only grows; it is never cleaned automatically (we don't delete your files) — remove files manually when needed.
- After modifying the plugin, restart
dsh webfor changes to take effect (client-side edits apply on a page refresh; host edits need a restart).
Why this plugin exists
DSH natively rejects dragged-in images when the current model doesn't support them. This plugin routes images through the official attachment path when the model can see them (so they ride the DeepSeek Files API with file_id reuse), keeps a user-accessible archive copy you can find later, and falls back to plain path text otherwise — a plain-text message that passes the model's image check and works with any model or vision plugin (it bypasses DSH's native image rejection because no image block is ever submitted on the fallback path).
nexu-io/open-design
ruvnet/ruflo
amruthpillai/reactive-resume
esengine/DeepSeek-Reasonix
volcengine/OpenViking
Molunerfinn/PicGo
titanwings/colleague-skill
nocobase/nocobase