Claude-style drag-and-drop/paperclip file upload with content sniffing, document-to-Markdown via Microsoft MarkItDown (built-in JS fallback), text inlining, and a read_document tool for agents.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-file-upload
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:HongMing-Huang/dsh-file-upload
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
File-message plugin for DeepSeek Harness (dsh). Claude/Codex-style uploads — drag-and-drop (files and folders), paperclip picker, paste-to-attach, multi-file support; content sniffing; fully bundled document → Markdown conversion (MarkItDown engine, 20+ formats, image OCR); Codex-style @relative/path references; automatic image explanations for text-only models; and a read_document tool for agents.
English | 中文
Zero-config, install-and-use. Every feature works out of the box with sensible defaults — no Python, no downloads, no picking backends. Image explanations auto-discover a vision endpoint (local Ollama → OpenAI-compatible key from the dsh credentials seam).
Features
- Upload — composer paperclip button plus a global drag-and-drop overlay ("release to attach"), multi-file support.
- Attachment cards — color-coded type badges (PDF red / DOC blue / XLS green / TXT gray / ZIP purple / JSON gold) with name and size; removable.
- Codex-style file references — uploaded files appear in the message as
@relative/pathreferences (like OpenAI Codex), never as raw content dumped into the composer; the agent reads the file withread_document(converted to Markdown on demand). - Codex-style
@mentions — type@in the composer to pick any uploaded file by its relative path; the reference inserts as a mention. - Document → Markdown, fully bundled — the MarkItDown engine ships inside the plugin (Microsoft MarkItDown TypeScript port,
markitdown-node): PDF / DOCX / PPTX / XLSX / HTML / CSV / JSON / XML / RSS / Atom / ZIP / Jupyter / image OCR / audio transcription. No Python, no downloads, no setup. - Image explanation for text-only models — upload an image and the plugin automatically generates a description ("讲解图片") through a vision discovery chain, so the DeepSeek API (text-only) can reason about the image: explicit
visionEndpoint→ local Ollama with a VL model (e.g. DeepSeek-VL2, zero-config) → OpenAI-compatible endpoint with a dsh-credentials key. Multimodal routes / vision bridges keep the officialread_imagepath. read_documenttool for agents — line-numbered paging (offset/limit), byte-budgeted LRU cache (invalidated on file change), size pre-checks, reads throughctx.fs(inherits sandbox and fs-observation policy).- Security — loopback-only uploads, sanitized file names, session-isolated storage (
.dsh-uploads/<sessionId>), sha256 content dedup, bounded concurrency, TTL sweep.
Install
dsh plugin --profile web add dsh-file-upload
# restart dsh web
Usage
- Click the paperclip in the composer toolbar, or drag files anywhere over the window;
- Files appear as attachment cards and their
@relative/pathreference is inserted into the composer — the raw content is never dumped into the chat; the agent reads the file withread_documentwhen it needs the content; - The agent reads documents with
read_document <path>— converted to Markdown on demand, pageable withoffset/limit.
MarkItDown (fully bundled — no downloads, no setup)
The MarkItDown capability ships inside the plugin. Works out of the box: no Python, no pip, no downloads, no build-script approval.
- Bundled engine — the Microsoft MarkItDown TypeScript port (
markitdown-node) is a regular dependency covering 20+ formats: PDF, DOCX, PPTX, XLSX, HTML, CSV, JSON, XML, RSS, Atom, ZIP, Jupyter notebooks, images (OCR via Tesseract, 110+ languages), and audio transcription (via LLM, needs model credentials). - Images — OCR to text by default through the bundled engine.
- Offline — all parsing runs locally, no network calls.
Optional upgrade: if an official MarkItDown CLI already exists on your machine (or is set via
markitdownBin), the plugin prefers it (adds EPUB and more); without one the bundled engine is always available.
- id: dsh-file-upload
config:
markitdownBin: /path/to/your/markitdown # optional; empty = bundled engine only
Startup log (bundled mode):
[dsh-file-upload] Document → Markdown ready: bundled MarkItDown engine (20+ formats, image OCR) — fully packaged, no downloads, no Python.
How images are handled (Codex-style reference + auto-explained)
Every uploaded file — images included — lands in the composer as a clean
Codex-style @relative/path reference (the raw content and absolute host
paths never appear in the chat). Images additionally get content support
based on what your session's model can do, detected at upload time:
| Detected route | What happens |
|---|---|
Multimodal model (declares image input, e.g. GPT-4o / Qwen-VL / Claude / Gemini) |
the @reference is inserted; the agent calls the read_image tool and the image enters model context directly |
A read_image tool is registered (official tool or a vision bridge such as dsh-vision-toolkit) |
detected automatically — same native path (the model fetches the image content itself) |
| Text-only model (the DeepSeek API is text-only) | if a description can be generated, the message carries [图片: name] 图片讲解: <description> right before the @reference, so the text-only model reasons about the image content immediately; if no vision endpoint is configured, only the clean @reference is inserted (the agent can still OCR via read_document) |
Vision discovery chain (zero-config, in order): ① explicit visionEndpoint/visionModel → ② local Ollama at http://localhost:11434 (picks a VL model such as DeepSeek-VL2 — images never leave the machine) → ③ DeepSeek official vision API (deepseek-v4-flash-vision-exp, uses the DEEPSEEK_API_KEY already configured in your DSH credentials — no extra setup) → ④ OpenAI standard endpoint using a key from the dsh credentials seam. Without any of these, images upload as plain references (no fallback text).
DeepSeek now has an official multimodal model:
deepseek-v4-flash-vision-expaccepts JPEG/PNG/GIF/WebP via the standard OpenAI-compatible format. The plugin's vision chain picks it up automatically through your existing DeepSeek key, so uploading an image immediately produces a high-quality[图片: name] 图片讲解: …block for text-only models. You can also make DSH itself route images natively: adddeepseek-v4-flash-vision-expas a custom model of the deepseek-official provider withinputModalities: ["text", "image"](settings → Models, or thellm-deepseek.modelssettings section) and switch the session to it — the plugin then detects native image input and the agent reads images directly.
Route detection mirrors the official read_image gate (ctx.llm.resolveModelInfo + inputModalities), plus a live check for a registered read_image tool.
Configuration
All fields have sensible defaults — you can install and use the plugin without touching any of them. Tune only what you need.
| Field | Default | Description |
|---|---|---|
uploadMaxBytes |
25165824 (24 MB) | Max bytes per uploaded file |
allowedExtensions |
[] |
Extension allowlist; empty = all allowed |
uploadTtlMs |
604800000 (7 days) | Unreferenced upload lifetime |
sweepIntervalMs |
3600000 (1 h) | Sweep period; 0 = disabled |
maxConcurrentUploads |
4 | Concurrent upload limit |
maxFileBytes |
25165824 | Byte cap for one document read |
readLimit |
2000 | Max lines returned by one read_document call |
sheetRowLimit |
200 | Rows kept per XLSX sheet |
maxSheets |
5 | Sheets read per workbook |
cacheEntries |
16 | Parse-cache entry count |
cacheMaxBytes |
67108864 (64 MB) | Parse-cache byte budget |
markitdownBin |
'' |
Optional MarkItDown CLI path; empty = auto-detect PATH |
markitdownTimeoutMs |
120000 | Timeout for one CLI invocation |
visionEndpoint |
'' |
Vision endpoint for image explanations; empty = auto (local Ollama → OpenAI standard) |
visionModel |
'' |
Vision model id; empty = auto |
visionApiKeyEnv |
OPENAI_API_KEY |
Credential reference for the vision key (dsh credentials seam) |
visionMaxBytes |
10485760 (10 MB) | Max image bytes sent to the vision endpoint |
Development
pnpm install
pnpm build # tsc (host) + esbuild (client bundle)
pnpm test # node --test
Architecture
src/
├── index.ts # entry: apply + Config schema + assembly
├── detect.ts # content sniffing (never trusts extensions)
├── convert.ts # MarkItDown engine + optional CLI backend
├── vision.ts # image explanations (vision discovery chain)
├── upload.ts # upload route: loopback/session/size/dedup/TTL
├── tool.ts # read_document: ctx.fs reads + paging + LRU cache
└── client/
└── index.tsx # paperclip + drag (files/folders) + paste + cards
Dual-face plugin: dsh.bundle (host) + dsh.client (web UI). No official patches — everything uses official seams (ctx.webServer, ctx.tools, ctx.systemPrompt, ctx.sessions, slash/input-insert-text, slash/input-insert-reference).
Security
- Uploads are loopback-only and same-origin checked.
- File names are sanitized (control chars, path separators, dot segments, leading dots stripped).
- Storage is session-isolated under the session's own workspace; unknown sessions get 403.
- sha256 content dedup, bounded concurrency (429 on overload), TTL sweep.
- Text extraction parses bytes, never trusts extensions; binaries are handed to the agent by path only.
License
MIT
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 8567
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 8567
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 4363
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
ccch1mneyyy/dsh-TUI★ 4235
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 4081
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1441
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.