VS Code-style file manager: browse the conversation workspace in a sidebar tree and edit files in the center column with Monaco, with theme import/export and Markdown preview.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:chengzhi43/dsh-file
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
dsh-file
A VS Code-style file manager plugin for DeepSeek Harness Web: browse the current conversation's workspace from the sidebar and edit files in the center column.
DeepSeek Harness 的 VS Code 风格文件管理器插件:在 Web 侧边栏浏览当前对话工作区的文件,在中间主区域编辑。
Screenshot
Browse the workspace in the sidebar tree; clicking a file opens it in the center-column "Files" view (Monaco editor with syntax highlighting).
Installation
# Run from inside the cloned dsh-file directory (not its parent)
cd /path/to/dsh-file
dsh plugin --profile web add .
dsh plugin add pnpm-links the package into the profile and appends it to dsh.profile.bundles. Restart dsh web to take effect (client plugin metadata is cached by name; it is rescanned after a restart).
DSH Desktop install
The desktop client is the deepseek-harness-desktop project (package dsh-plugin-desktop). It uses a separate profile (desktop) from dsh web (web), and plugins are not shared between them — a plugin installed only into the web profile will not appear in the desktop client, nor in its Settings → Plugins list:
# Run from inside the dsh-file directory as well
cd /path/to/dsh-file
dsh plugin --profile desktop add .
After installing, fully quit and relaunch the desktop app (quit the application, not just close the window); dsh-file will then show up in the sidebar footer "Files" button and in Settings → Plugins.
Note: do not add plugins to
~/.dsh/profiles/desktop/cordis.yml— the desktop client rewrites it to an empty list[]on every startup. The correct entry point isdsh.profile.bundles+dependenciesin the profile'spackage.json(which is exactly whatdsh plugin adddoes).
Install from npm
dsh plugin --profile web add dsh-file
Or download the tarball from Releases and install it locally (use --profile desktop for the desktop app):
dsh plugin --profile web add ./dsh-file-0.1.1.tgz
Configuration
The root in cordis.patch.yml is only the fallback root when there is no session (defaults to process.cwd()). When the file manager opens, the browser resolves the current conversation's workspace directory and re-pins the root via setRoot, so usually nothing needs to change:
- insert:
- id: dsh-file
name: 'dsh-file'
config:
root: !!js process.cwd() # fallback root only, before the file manager pins the session workspace
Features
- "Files" button at the sidebar footer: toggles the sidebar body into the file manager (file tree) and back to the workspace/session list
- Workspace follows the active conversation: opening the file manager resolves the current session's workspace directory (
SessionHeader.cwd) and re-pins the gateway root viasetRoot— no longer the directorydsh webwas launched from - Center-column editor (view tab): the editor is registered as a
conversation.viewview ("Files" tab, alongside Chat/Trajectory). Clicking a file opens it inside the session scroll area of the page (not a popup): Monaco Editor (the same kernel VS Code uses, loaded from CDN) with extension-based syntax highlighting; falls back to a plain textarea when the CDN is unreachable - Markdown preview:
.mdfiles open as a read-only rendered preview by default (marked + GFM: headings, lists, tables, task lists, code blocks). A VS Code-style preview/source toggle button sits next to the "Theme" button in the toolbar (shown only for Markdown files); the chosen mode is remembered (localStorage) and reused on the next open - Theme settings (VS Code style): the "Theme" button in the editor toolbar opens a settings panel — light by default, presets selected via a dropdown (Light/Dark/One Dark/GitHub), plus custom background / foreground colors and font size (10–28px), applied live to Monaco and the editor chrome (toolbar/status/tabs follow the background), persisted to localStorage
- Theme import/export: export the current theme to a JSON file and import it back, just like VS Code, to migrate your colors between environments (see Theme import/export)
- Edit & save: Ctrl+S or the "Save" button in the editor, dirty marker (●); multiple open files switch via the top tab strip, each tab has a ✕ close button
- File operations: create file, create directory, rename, delete (delete requires confirmation; non-empty directories are rejected)
- Workspace boundary: every path resolves against the currently pinned
root; escaping paths are rejected by the host (including symlink-escape protection)
Theme import/export
The theme panel (the "Theme" button in the editor toolbar) can export the current theme to a JSON file or import one back — the same idea as VS Code theme files, handy for moving your colors across machines or environments.
Export a theme
- Open the file editor (the "Files" view in the center column).
- Click the Theme button in the toolbar to open the settings panel.
- Click Export theme — the browser downloads a
dsh-file-theme-YYYY-MM-DD.jsonfile.
The exported JSON carries both the plugin's flat fields and VS Code workbench colors:
{
"name": "dsh-file · One Dark",
"type": "dsh-file-theme",
"version": 1,
"background": "#282c34",
"foreground": "#abb2bf",
"fontSize": 13,
"colors": {
"editor.background": "#282c34",
"editor.foreground": "#abb2bf"
}
}
Import a theme
- Open the theme settings panel.
- Click Import theme and pick a JSON file.
Accepted formats:
- This plugin's export format (
background/foreground/fontSize); - VS Code theme JSON: reads
colors["editor.background"]andcolors["editor.foreground"](tokenColorsare not applied yet — syntax highlighting keeps Monaco's built-in colors).
On success the colors apply immediately and are persisted to localStorage; invalid JSON or missing valid colors shows an error in the panel.
Architecture
The plugin has two halves sharing the package name dsh-file:
| Host half (Node process) | Client half (browser React) | |
|---|---|---|
| Source | src/index.ts |
src/client/ |
| Build output | dist/index.js (tsc, keeps standard decorators) |
dist/client.js (esbuild, ModuleLoader bundle) |
| Responsibility | Filesystem RPC | Sidebar file tree + center-column editor view |
| Key API | class FileManagerGateway extends TypertRemoteService + @Remote() |
ctx.slots.register(), ctx.remote.$mount() |
Host ↔ Client communication (Typert Remote)
Browsers cannot touch the filesystem directly, so the host half exposes file operations as RPC endpoints (namespace fileManager: listDir / readText / writeText / createFile / createDirectory / rename / delete / stat / resolve / getRoot / setRoot). The client mounts the call surface with ctx.remote.$mount(TYPERT_REMOTE) and resolves the service via ctx.get('remote.fileManager'). setRoot re-pins the gateway root to the current session's workspace directory.
Key constraint (SRC descriptor contract): the Typert gateway derives wire parameter names from method signatures via Function.prototype.toString — host methods must use flat parameters (listDir(path: string), not listDir(input: {...})); the parameter names are the wire fields the client sends. Both halves must use identical names.
Panel toggle mechanism
The sidebar main area is the single-seat sidebar.workspaces slot (occupied by the workspace browser at priority 0). The plugin registers its own shadow entry at priority: -1 when the button is clicked — a single-seat slot renders the lowest-priority live entry, so the file manager wins the cell; closing disposes the entry and the workspace browser returns. After clicking a file in the tree, the editor renders in the "Files" view registered in conversation.view — the session scroll area of the center column (alongside chat / trajectory), entered via the "Files" tab in the session header, never a popup.
Dependency resolution (important)
The @deepseek-ai/* packages must not be installed as copies inside the plugin's own node_modules: @Remote decorator markers live in a module-level WeakMap, and if the plugin and the api-gateway each hold a separate dsh-typert-protocol instance the markers are invisible to each other (RPC returns 404). Node must resolve to the same instance as the dsh installation:
# Local development (when dsh is installed locally via npx):
ln -s ~/.dsh/profiles/node_modules/@deepseek-ai node_modules/@deepseek-ai
At startup dsh maintains a flat symlink fallback at $DSH_HOME/profiles/node_modules (healProfilesModuleFallback) pointing at every package in the dsh installation. For production releases the plugin declares @deepseek-ai/* as peerDependencies, provided by the profile.
Desktop app (deepseek-harness-desktop) notes:
- On startup the desktop app re-points
~/.dsh/profiles/node_modules/@deepseek-aiat the packaged Desktop.app directory (/Applications/DSH Desktop.app/.../app.asar.unpacked/node_modules), which strips.d.tsfiles — keeping the symlink above pointing atprofilesguarantees the plugin loads the same runtime instance as the desktop api-gateway (RPC works). - The stripped types break
tsc.tsconfig.jsonusespathsto map the compile-time lookup of@deepseek-ai/*to the global dsh install (which ships full.d.ts); runtime resolution is unaffected (Node still walks the node_modules symlink → profiles → desktop instance). Adjust the path per the comment intsconfig.jsonif your global dsh lives elsewhere. - Do not run
npm installinside the plugin directory: npm dereferences thenode_modules/@deepseek-aisymlink into a real directory and corrupts the profiles symlink structure, makingdshfail with "exists and is not a symlink". If you must add dependencies, re-run theln -sabove afterwards.
Development
npm install # esbuild + typescript + types
node build.mjs # build host (tsc) + client bundle (esbuild)
node build.mjs --watch # watch client only (rerun for host changes)
Build outputs:
dist/index.js— host half (Node ESM; compiled with tsc to keep the standard stage-3 decorators; esbuild would lower@Remoteto the legacy form and crash at runtime)dist/client.js— client half (window.__ModuleLoader__.load({id, factory})format;reactand other seed words stay external)
Debugging
dsh --profile web --dump-config | grep -A4 dsh-file # confirm the plugin layer is composed
# Test RPC (requires a running dsh web)
curl -X POST http://127.0.0.1:3080/api/fileManager/getRoot \
-H 'Content-Type: application/json' \
-d '{"type":"client-request","rpcId":"t","method":"fileManager/getRoot","payload":{"args":{}}}'
FAQ
- RPC returns not found: almost always the
@deepseek-ai/dsh-typert-protocoldual-instance problem — check whether the plugin'snode_modules/@deepseek-aiis a symlink (ls -la node_modules/@deepseek-ai); if not, create the link as described above and restart. - Blank editor: Monaco loads from the jsdelivr CDN; in intranet environments configure a local mirror or wait for the textarea fallback.
- Wrong directory opened: verify the current session's workspace directory (the sidebar title shows the directory name). The file manager auto-runs
setRootto the current session'scwd; without a session it falls back tocordis.patch.yml'sroot. - Plugin changes have no effect: host-half changes require restarting
dsh web; client-half bundle changes only need a page refresh (a rev change triggers a reload).
License
Links
More in this category
zhu1090093659/dsh-web-ui#packages/dsh-web-ui-all★ 4430
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.
omdsh-dev/DSH-better-sidebar★ 2101
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
ccch1mneyyy/dsh-TUI★ 1924
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/dsh-at-file★ 362
Codex-style `@file` mentions: search workspace files in the composer and attach their contents to prompts.
omdsh-dev/dsh-genui★ 214
Interactive UI components rendered inline in replies: layout, charts, forms, quizzes, mermaid, 3D scenes, and an action event loop back to the model.
huiliyi37/dsh-tianshu-tui★ 211
A terminal UI (TUI) for DeepSeek Harness.