DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

roojay /

roojay/dsh-stream-upload

Verified

DeepSeek Harness hybrid attachment plugin: native images plus bounded-memory workspace uploads

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: master@6c4a46b0

dsh-stream-upload

English | 中文

A dual-face DeepSeek Harness plugin for hybrid attachments from the Web composer. PNG/JPEG/WebP/GIF files are handed to the official durable image-attachment pipeline. Everything else — code, text, PDFs, DOCX/XLSX, archives — is streamed as raw bytes into the session workspace where the agent's own file tools already operate.

How it works

browser composer                          dsh host
┌─────────────────────┐                  ┌──────────────────────────────────────┐
│ 📎 button           │ image files       │ native draft image + attachment store│
│ (conversation.input │──────────────────▶│ stock preview / validation / model   │
│  .left slot entry)  │ other raw bytes   │ trust + quota → exclusive temp file  │
│ draft ← path insert │──────────────────▶│ sha256 → atomic publish in workspace │
└─────────────────────┘                  └──────────────────────────────────────┘
  • Native images: raster images use DSH's createDraftImages → addImages path and stock attachment rail; this plugin never stores a duplicate image object.
  • Bounded-memory files: non-images stream socket→exclusive 0600 temp file while a full SHA-256 is calculated. Quotas pass before an atomic hard-link publish; aborted and rejected bodies leave no partial file.
  • Trust fence: /api/upload is a longer prefix than /api, so it wins dispatch and must re-implement the official browser fence itself — Host ∈ loopback ∪ connection.trustedHosts, sec-fetch-site ≠ cross-site, Origin authority match. Reverse-proxy deployments behind --trusted-host keep working unchanged; everything else gets 403.
  • Per-session isolation: a live session resolves its own workspace, storage roots and session directories are realpath-contained and cannot be symlinks, directories are 0700, and the API returns only workspace-relative references.
  • Lifecycle and quotas: full-SHA-256 content dedup, per-file/session/workspace caps, file count and free-space admission, bounded concurrency, request timeout and per-file TTL sweep.
  • Composer-native queue: a compact horizontal strip lives in the official conversation.input.dock immediately above the composer. It shows every selected file, byte-accurate progress for workspace uploads, native-image attachment state, size, completion or the retained failure reason. Active batches can be cancelled; successful/cancelled batches collapse after five seconds while failures remain until dismissed. Visual progress uses ARIA progress bars; state transitions use a polite live region. One borderless strip fills the composer's 780 px maximum width: a single attachment expands across the available row and the cancel/dismiss action stays inside the same visual surface. Multiple attachments share the row and scroll horizontally only after reaching their compact minimum width.
  • Composer integration: the button is a conversation.input.left slot entry. Every successful non-image upload appends one CommonMark resource link, [display name](<workspace/relative/path>), through the official inputActions.setDraft provide-channel face — no DOM scraping anywhere.

Install

This package uses ESM and has no prepare script, so installing it from Git does not require allowBuilds. Pin DSH to @deepseek-ai/dsh@0.1.2-rc.1.

dsh plugin --profile web add github:roojay/dsh-stream-upload#v0.4.0

Install from npm (once published):

dsh plugin --profile web add dsh-stream-upload@0.4.0

Install from a GitHub Release tarball:

dsh plugin --profile web add https://github.com/roojay/dsh-stream-upload/releases/download/v0.4.0/dsh-stream-upload-0.4.0.tgz

Install from a local directory:

cd /absolute/path/to/dsh-stream-upload
pnpm install --ignore-scripts
dsh plugin --profile web add "$PWD"

Restart the Web process after installation:

systemctl restart dsh   # or however your deployment restarts `dsh web`

Remove the plugin:

dsh plugin --profile web remove dsh-stream-upload

Config (cordis.patch.yml row)

key default meaning
maxBytes 99614720 (95 MiB) hard cap for one non-image upload body; stays below Cloudflare's 100 MB request ceiling
allowedExtensions [] lowercase allowlist; empty = all allowed
uploadDirName .dsh-uploads workspace-relative storage directory
uploadTtlMs 604800000 (7 d) stale upload lifetime
sweepIntervalMs 3600000 (1 h) sweep interval; 0 disables
maxConcurrentUploads 2 concurrent upload bodies
maxSessionBytes 1073741824 (1 GiB) retained bytes per session
maxTotalBytes 2147483648 (2 GiB) retained bytes below one workspace upload root
maxFilesPerSession 100 retained files per session
minFreeBytes 536870912 (512 MiB) filesystem free-space reserve
requestTimeoutMs 120000 idle request timeout

Upload feedback

The UI follows the established status-bar/file-card pattern: queued → uploading/attaching → complete, failed or cancelled. The paperclip remains in conversation.input.left; the status strip is a separate conversation.input.dock entry at order 30, sharing one isolated observable store per session. Ordinary files use XMLHttpRequestUpload.progress because the browser Fetch API does not expose portable upload-byte progress; the host route and raw streaming protocol are unchanged. Success is not inferred from reaching 100% of sent bytes: the chip becomes complete only after the server returns a validated workspace-relative reference.

Limits / roadmap

  • v1 stores binaries as-is: reading PDF/DOCX content needs an agent-side tool or conversion step (see ecosystem plugins like markitdown converters if you want automatic document→Markdown).
  • No folder upload or global drop handler; use the paperclip. The smaller surface avoids directory traversal, recursive quota and conflict problems.
  • Uploads are whole-request transfers, not resumable chunks. Cancelling removes the host .part file; retrying a failed file currently means selecting it again.

Tests

npm test   # node --test test/*.test.js — fence scenarios, transport, sweeper
—/ 5

No ratings yet

Verified DSH bundle

Commit 6c4a46b04255

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout