DeepSeek Harness plugin

dsh-drop-any-file

Extend the DeepSeek Harness (DSH) web chat drag-and-drop to accept every file type: dropping any file in the composer saves it into the current session's workspace so the agent can read and use it.

Jump to install

Source facts

Repository
Zenjibad/dsh-drop-any-file
Latest update
Aug 18, 2026
Category
Workflow & Automation
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/Zenjibad/dsh-drop-any-file
Plugin: dsh-drop-any-file
Author: Zenjibad

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-drop-any-file · Accept Any File Type in the DeepSeek Harness (DSH) Web Chat

> Extend the DeepSeek Harness (DSH) Web UI chat's drag-and-drop so that every file type is accepted — the built-in composer only lets images (PNG/JPG/WebP/GIF) attach inline and rejects everything else. Dropping a non-image file saves it into the active session's workspace so the agent can read and use it. 让 DSH 聊天支持拖拽任意类型文件:非图片文件将保存到当前会话工作区,供 Agent 读取使用。 > > 中文文档: README.zh.md · LLM index: [llms.txt](llms.txt) · Agent guide: [AGENTS.md](AGENTS.md)

!dsh-plugin !DeepSeek Harness !license !install

Keywords: dsh-plugin · deepseek-harness-plugin · drag-drop · file-upload · workspace · attachment · any-file · 拖拽 · 任意文件

---

📑 Table of Contents

  • [✨ Features](#-features)
  • [🏗️ How it works](#️-how-it-works)
  • [🚀 Quick start](#-quick-start)
  • [⚙️ Configuration](#️-configuration)
  • [❓ FAQ](#-faq)
  • [⚠️ Security notes](#️-security-notes)
  • [📦 Project structure](#-project-structure)
  • [🙏 Credits](#-credits)

---

✨ Features

FeatureDescription
📥 Any file typeDrop .pdf, .zip, .txt, .csv, .bin, … — nothing is rejected with an "only PNG/JPG" error
💾 Saves into the workspaceEach dropped non-image file is written into the active session's workspace directory, where the agent can read/search/use it with its normal file tools
🖼️ Images untouchedDrops that contain only images keep the built-in inline-attach behaviour — nothing changes for what already worked
🚫 Composer rejection bypassedCapture-phase window listeners intercept the drop before the composer's image-only handler runs, so its rejection toast never fires
🫳 Drop overlay + toastA themed full-window overlay appears while dragging ("Drop to save to workspace"); a toast confirms what was saved (or why it failed)
🛡️ Name sanitisation + size capsFile names are reduced to a basename and stripped of illegal path characters; payload/file sizes are capped (80 MB body / 60 MB file)
🌗 Theme-awareOverlay and toast use --dsw-alias-* design tokens; follows light/dark automatically
♨️ Survives restartsReal profile-bundled plugin: install once with dsh plugin add, auto-loads on every DSH boot — no per-session define, no cordis_define

🏗️ How it works

User drags a non-image file over the chat window
                                  │
Client bundle (browser)           ▼
  └─ capture-phase listeners on window: dragenter / dragover / dragleave / drop
       (registered from the conversation.input.dock seat, which carries sessionId)
  └─ drop contains a non-image file? → preventDefault + stopPropagation
       (the composer's image-only handler never sees the event)
  └─ FileReader → base64 → fetch POST /drop-any-file/api
       body: { sessionId, fileName, base64 }
                                  │
Host half (DSH process)           ▼
  └─ webServer route POST /drop-any-file/api
  └─ resolve target dir: sessions.get(sessionId).header.cwd  (the workspace)
  └─ sanitise basename → decode base64 → node:fs writeFile
  └─ returns { ok, saved: { name, path, bytes } }
                                  │
Client bundle (browser)           ▼
  └─ toast: "Saved to workspace: <name>" (or the error)
  • Pure pull from the browser: no events, no push — the client posts only when the user drops a file; the host answers with JSON ({ok:false,error} on any failure, never a non-JSON 500).
  • Image-only drops pass through: the handler returns early without stopPropagation, so the composer's own inline-attach path still runs for images.
  • Persistence: ships dsh.bundle (cordis.patch.yml) + dsh.client (exports["./client"], bundled) so it installs as a real profile plugin that the DSH client-modules scanner loads on every boot.

🚀 Quick start

Standard install: dsh plugin add (persists across restarts)

Install the package from this GitHub repo:

# local directory (from the parent of this repo):
dsh plugin --profile web add ./dsh-drop-any-file

# or directly from GitHub (any DSH machine):
dsh plugin --profile web add github:Zenjibad/dsh-drop-any-file
# or:
dsh plugin --profile web add git+https://github.com/Zenjibad/dsh-drop-any-file.git

dsh plugin add is a pnpm add into the profile plus a dsh.profile.bundles reconcile: seeing this package's dsh.bundle declaration, it appends dsh-drop-any-file to the bundle stack. Restart DSH, then hard-refresh the browser tab (Ctrl+F5). On boot the client-modules scanner resolves exports["./client"] and the drop handler activates. No per-session define, survives restarts.

> ⚠️ Important: after installing (or updating) a client plugin, a hard page refresh (Ctrl+F5) is required — the DSH client HMR only hot-swaps already-loaded bundles and does not pull in new bundles into an open tab.

Manual profile mount (alternative)

1. git clone https://github.com/Zenjibad/dsh-drop-any-file.git (any location). 2. Add to ~/.dsh/profiles/web/package.json dependencies: "dsh-drop-any-file": "link:<repo-path>", then pnpm install in the profile dir. 3. Restart DSH.

Requirements

  • Any DSH web session with an active workspace (the drop target is the current session's cwd).
  • No session open → nothing to save into → the plugin is inert (the composer keeps its default behaviour).

⚙️ Configuration

No config file, no persisted settings. Behaviour is fixed by constants in the source:

KnobLocationDefault
Max POST body (base64)MAX_BODY_BYTES in src/index.ts80 MB (≈ 60 MB file)
Max saved fileMAX_FILE_BYTES in src/index.ts60 MB
Save targetresolved per dropsessions.get(sessionId).header.cwd (the session's workspace)
Drop interceptionsrc/client/index.tsxcapture-phase dragenter/dragover/dragleave/drop on window
Toast durationsrc/client/index.tsx6 s

❓ FAQ

Q: I dropped a file and it still says "Only PNG, JPG, WebP, and GIF images are supported"? A: The plugin isn't loaded in your current page. Restart DSH (if the host half isn't mounted yet), then hard-refresh the browser tab (Ctrl+F5). New client bundles only appear on a full page reload — the HMR client does not add new bundles to an already-open tab.

Q: I dropped a file but nothing happened / no toast? A: The drop must happen over the chat window with a session open, and must contain at least one non-image file. If the drop is image-only, the built-in attach path runs instead (by design). Check the browser console for drop-any-file save error entries.

Q: Where does the file go? A: Into the active session's workspace directory (sessions.get(sessionId).header.cwd) — the same directory the agent works in, so the agent can read it with read/glob/grep etc. The response includes the absolute path in the success toast context.

Q: Does it handle multiple files at once? A: Yes — every non-image file in the drop is saved in order; the toast lists each saved name and counts failures.

Q: Can I drop a file larger than the cap? A: No — the host rejects files over 60 MB with 413 and the client shows a failure toast. Raise MAX_FILE_BYTES (and MAX_BODY_BYTES) in src/index.ts and rebuild if you need bigger files.

Q: My old dynamic plugin is still active after installing this? A: This plugin is packaged, not dynamic — there is no dynamic twin. If you previously installed a dynamic drop-handler, stop it (cordis_stop / cordis_undefine) to avoid double interception.

Q: How do I remove it? A: dsh plugin --profile web rm dsh-drop-any-file (or delete the profile dependency + bundle entry) and restart DSH.

⚠️ Security notes

  • Path traversal blocked: the file name is reduced to path.basename() and every illegal path character (<>:"/\\|?* and control chars) is replaced, so a hostile name can never escape the workspace.
  • Scoped writes: files are written only into the resolved session's cwd — never to arbitrary paths.
  • Size caps: the request body is capped at 80 MB and the decoded file at 60 MB, so the host never buffers unbounded payloads.
  • No extra I/O: the plugin performs no network calls, no shell execution, and no disk scanning — only the requested file write.
  • Base64 over same-origin HTTP: the file bytes travel as base64 JSON to a same-origin route (the DSH webServer); no third-party endpoint is involved.

📦 Project structure

dsh-drop-any-file/
├── src/
│   ├── index.ts            # host half: body read, session-cwd resolve, sanitise, writeFile, /drop-any-file/api route
│   └── client/index.tsx    # client bundle: capture-phase drop listeners, overlay, toast, POST to the host route
├── cordis.patch.yml        # dsh.bundle patch (inserts the plugin row on boot)
├── tsdown.config.ts        # bundles host (node ESM) + client (CJS ModuleLoader)
├── package.json            # name, exports["./client"], dsh.client + dsh.bundle
├── lib/                    # build output (index.js, client.js)
├── AGENTS.md               # repository guide for AI agents
├── llms.txt / llms-full.txt
├── README.md / README.zh.md
└── LICENSE

🙏 Credits

  • DeepSeek Harness — the DSH plugin/dynamic runtime, Slots, theme, webServer, client-modules.
  • headroom-stats-plugin — reference for the packaged client-plugin build pattern (tsdown host/client split, cordis.patch.yml, dsh.client).
  • DSH-better-sidebar — another reference for the packaged client-plugin build pattern.

📄 License

[MIT](LICENSE)