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. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
File-message plugin for DeepSeek Harness (dsh). Claude-desktop-style drag-and-drop and paperclip uploads, content sniffing, fully bundled document → Markdown conversion (MarkItDown engine, 20+ formats, image OCR), text inlining into the composer, voice-to-text input, and a read_document tool for agents.
English | 中文
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.
- Text inlining (Claude style) — small text files (code, JSON, CSV, logs, config) are inserted directly into the composer via the official
slash/input-insert-textevent, so the model sees the content immediately; larger text files insert a path reference with a preview. - 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 OCR by default — uploaded images are readable through
read_document(Tesseract, 110+ languages); no vision plugin required. - Voice input — record from the mic and transcribe straight into the composer (browser Web Speech API, zero dependencies); audio files upload as file attachments.
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;
- Small text files land directly in the composer; documents appear as attachment cards and their path is sent with the message;
- 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: 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
| Scenario | Path |
|---|---|
| Model accepts image input (or a vision bridge like dsh-vision-router is installed) | agent uses the official read_image tool |
| Default (no vision route) | read_document <image path> → bundled engine runs OCR (Tesseract) and returns a text description |
| Official MarkItDown CLI with LLM credentials | read_document <image path> → CLI describes the image |
The injected systemPrompt covers this guidance; the agent picks the right path automatically.
Voice input
- Record — the mic button in the composer records up to
maxRecordSec(default 60 s); the transcript (browser Web Speech API) lands in the composer as editable text, so you can review it before sending. - Audio files — uploaded audio is stored like any file attachment; with a configured ASR endpoint (OpenAI-compatible
/audio/transcriptions) the plugin transcribes it automatically and sends the transcript with the message.
- id: file-upload
config:
asrEndpoint: '' # e.g. https://api.openai.com/v1/audio/transcriptions
asrApiKeyEnv: OPENAI_API_KEY # env var holding the ASR key
asrModel: whisper-1
Configuration
| 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 |
inlineTextLimit |
8192 (8 KB) | Text inlined into the composer up to this size |
previewTextLimit |
2048 (2 KB) | Preview length for larger text files |
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 |
maxRecordSec |
60 | Max voice recording length (seconds) |
asrEndpoint |
'' |
Optional OpenAI-compatible ASR endpoint for audio files |
asrApiKeyEnv |
OPENAI_API_KEY |
Env var holding the ASR API key |
asrModel |
whisper-1 |
ASR model name |
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
├── upload.ts # upload route: loopback/session/size/dedup/TTL
├── asr.ts # audio transcription (OpenAI-compatible endpoint)
├── tool.ts # read_document: ctx.fs reads + paging + LRU cache
└── client/
└── index.tsx # paperclip + drag overlay + mic + attachment 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-ui#packages/dsh-web-ui-all★ 2314
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.
ccch1mneyyy/dsh-TUI★ 1071
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 945
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
omdsh-dev/dsh-at-file★ 175
Codex-style `@file` mentions: search workspace files in the composer and attach their contents to prompts.
huiliyi37/dsh-tianshu-tui★ 143
A terminal UI (TUI) for DeepSeek Harness.
Nagi-ovo/dsh-visualize★ 95
In-conversation generative UI: the model renders interactive HTML cards into the chat stream, with streaming preview and sandboxed rendering.