dshbase

Plugin directory / Developer / dsh-workspace-upload

dsh-workspace-upload

Verified · install-tested on dsh LI-Huaa

✓ Actively maintained

View on GitHub ↗ ← Back to plugin directory

1Stars
0Forks
0Open issues
—Language
2026-08-14Last push
Cross-platformPlatform

What it does

A simple plugin to address the pain point of inconvenient file management when deploying DeepSeek Harness in the cloud. 一个简单的插件,用于在云端部署deepseek harness时管理文件。

✅
Our take
Works — verified, early-stage project

A simple plugin to address the pain point of inconvenient file management when deploying DeepSeek Harness in the cloud. 一个简单的插件,用于在云端部署deepseek harness时管理文件。 It installs cleanly and boots without issues in our testing. It's early-stage but functional.

“Verified” means our automated CI actually ran dsh plugin add in a clean profile and it booted — nothing more. Feature descriptions and version compatibility are the author’s claims. This is not a security audit and not an endorsement of third-party code.

README

dsh-workspace-upload

English | 中文

A workspace file-manager plugin for the dsh
Web profile — browse, upload, download, rename, create and delete files in the
session's workspace, right from the chat UI.

Features

  • A 文件 (Files) button in the composer's left controls opens a file-manager
    dialog over the app:
    • browse the workspace and its subdirectories (breadcrumb navigation, up /
      refresh);
    • upload — multi-file, chunked, any size (640 KiB chunks, adaptive shrink
      under proxy body limits);
    • download files;
    • rename files and folders;
    • create folders;
    • delete files and folders (two-step confirm).
  • Every operation is scoped to the workspace root: host-side path containment
    rejects any .. or absolute-path escape.
  • The workspace resolves to the session's workspace (session → cwd → first
    registered workspace).

Directory structure

dsh-workspace-upload/
├── cordis.patch.yml      # profile patch layer: inserts the plugin row
├── package.json          # dsh.bundle / dsh.client declarations, exports
├── lib/
│   ├── index.js          # host half: /api/workspace-upload file-manager route
│   └── client.js         # client half: browser bundle (button + dialog)
├── test/
│   ├── protocol.mjs      # host protocol tests (real handler, no dsh needed)
│   └── simulate.mjs      # client kernel simulation (slot registration fake)
├── LICENSE               # MIT
├── .gitignore
├── README.md             # English
└── README-zh.md          # 中文

Architecture

One package, one profile row, two halves:

Half File Role
Host lib/index.js Registers GET/POST /api/workspace-upload on the dsh web server: list / rename / mkdir / delete / download / chunked upload, all with workspace containment.
Client lib/client.js Browser bundle (window.__ModuleLoader__.load): the trigger button in conversation.input.left; the dialog renders from the button itself (plain local state, position: fixed overlay).

Wiring:

  • package.json → dsh.bundle.patch points at cordis.patch.yml (the profile
    layer that inserts the row) and dsh.client.platform: "web" marks the
    package as a browser-roster entry whose bundle ships via exports["./client"].
  • cordis.patch.yml inserts one row, workspace-upload, into the web
    composition.

API

GET  /api/workspace-upload
       → { "workspace": "<resolved workspace dir>" }        (no params)
       → file download with attachment headers              (?sessionId&path&name)

File-manager modes (JSON POST; path is a workspace-relative directory
"" / "sub" / "sub/deep", name is always one path segment):

{ "mode": "list",   "sessionId"?, "path"? }                    → { workspace, path, entries:[{name,type,size,mtime}] }
{ "mode": "rename", "sessionId"?, "path"?, "name", "newName" } → { ok, from, to }
{ "mode": "mkdir",  "sessionId"?, "path"?, "name" }            → { ok, path }
{ "mode": "delete", "sessionId"?, "path"?, "name" }            → { ok, deleted }

Chunked upload (any file size; the GUI sends 640 KiB chunks — base64 bodies
~853 KiB, under the nginx default client_max_body_size of 1 MiB; on a bare
413 the client halves the chunk size down to 64 KiB and retries):

{ "mode": "chunk",  "sessionId"?, "path"?, "transferId", "name", "offset", "data", "total" } → { received }
{ "mode": "finish", "sessionId"?, "path"?, "transferId", "name", "total", "overwrite"? }     → { status, path, bytes }
{ "mode": "abort",  "transferId" }                                                            → { aborted }

Chunks are appended to a hidden .dsh-upload-<transferId> temp file inside the
target directory and renamed on finish. Chunks must arrive in order from
offset 0; a repeated chunk at an already-received offset is answered
idempotently (safe client retry). Orphaned transfers are swept after
30 minutes.

A legacy single-shot batch mode is kept for API/curl compatibility:

{ "sessionId"?, "files": [{ "name", "data": "<base64>", "overwrite"? }] }
  → { "workspace": string, "results": [{ name, path?, status, bytes?, error? }] }

Limits: 32 MiB per request (one chunk + overhead), 8 MiB decoded per chunk.
overwrite: true replaces an existing file; otherwise the file is skipped with
status: "skipped".

Install

The package installs as a dsh profile bundle. From the checkout that owns the
package directory:

dsh plugin --profile web add ./dsh-workspace-upload
# or from a clone:  dsh plugin --profile web add /path/to/dsh-workspace-upload

Then restart the web profile (dsh web ...) so the loader picks up the new
row, and refresh the browser. Removing the plugin:

dsh plugin --profile web remove dsh-workspace-upload

Development

Both tests run without a dsh instance (they exercise the real host handler and
client bundle factory directly):

node test/protocol.mjs   # host protocol: list/rename/mkdir/delete/download/chunked upload
node test/simulate.mjs   # client kernel: slot registration against a faithful slots fake

Security notes

  • All path / name values are sanitized and containment-checked on the host;
    uploads use hidden temp files that never escape the target directory.
  • The route inherits the dsh web server's bind (localhost by default); when
    exposing the GUI remotely, put it behind the same TLS/Basic-Auth reverse
    proxy as the rest of the app (and raise client_max_body_size if you want
    fewer, larger chunks).

Install

🧩 Let your agent install it (recommended)

Install the catalog once, then DeepSeek Harness can find and install any plugin from this site automatically:

dsh plugin add dshbase-catalog

Then say "install dsh-workspace-upload for me" — your agent finds it in the directory and installs it. Docs: dshbase-catalog · verified packs.

This plugin is GitHub source (not published to npm) — install it straight from the repo:

Web profile:

dsh plugin --profile web add github:LI-Huaa/dsh-workspace-upload

Headless (CLI) profile:

dsh plugin --profile headless add github:LI-Huaa/dsh-workspace-upload

Test report

Verified: L1 install + L2 load + L3 runtime from GitHub source on dsh 0.1.0-rc.6.

Security: not yet scanned — our daily static scan will cover it shortly.

Share this badge

More in Developer

Browse all 7819 plugins →