dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
In-browser files pane for the DeepSeek Harness Web GUI: a Session View tab that browses the host's files and reads them in place. · DSH 网页「文件」视图标签:两栏目录导航 + 就地阅读文件
An in-browser files pane for the DeepSeek Harness Web GUI:
a Session View tab (对话 / 轨迹 / 文件) that browses the host's files and reads them in place —
instead of handing a path to a native desktop opener, which a headless host cannot honour.
在 DSH 网页面板里内置一个「文件」视图标签:左侧两栏目录导航,右侧阅读文件内容。不改动主窗体, 复用聊天窗自己的 Markdown 与代码渲染,点击会话里的任意文件即在其中打开。
dsh web runs on the machine that holds the files; the browser usually does not. Clicking a file
reference in a conversation calls remote.session.openWorkspacePath, which on a headless host
spawns xdg-open into nothing. This plugin intercepts that one call and renders the file in the
page instead.
@path mentions and produced-file chips open
here, and the tab is selected for you.MarkdownText, code
through the shipped shiki CodeBlock, so both look exactly like the conversation. is read from the host and rendered beside the
Markdown text (MarkdownText itself only renders http(s) images).~ is expanded;
a file path lands on its content, a directory path on its listing.~ and shortening only when the row would overflow), a collapse toggle that puts the listing away
entirely (remembered across reloads), and loading feedback that never blanks the pane it replaces.The package is plain ESM with a hand-written browser bundle: no build step, no dependencies.
From a checkout — clone it, then mount it as one row in your profile's patch layer
(~/.dsh/profiles/<profile>/cordis.patch.yml):
git clone https://github.com/liooil/dsh-files-pane ~/.dsh/plugins/dsh-files-pane
- insert:
- id: files-pane
name: '/home/<you>/.dsh/plugins/dsh-files-pane/lib/index.js'
The loader accepts a path-like entry name, and dsh-client-modules walks up from it to this
package's package.json for the dsh.client declaration and the ./client bundle. The bundle is
then served at /plugins/dsh-files-pane/client.js.
As a package — the repo declares a dsh.bundle manifest (cordis.patch.yml), so a published
copy mounts by name instead:
dsh plugin add dsh-files-pane
⚠️ Use one of the two. Mounting the same package from two sources at once fails the client
module scan (resolves from multiple active Loader sources), so remove the local-path row before
installing the package (or vice versa).
Changing the plugin set (including this first mount) takes effect on the next dsh start:
systemctl --user restart dsh # or however your host is supervised
| Path | Half | Owns |
|---|---|---|
lib/index.js | Host (Node) | Two same-origin POST routes, /dsh-files-pane/list and /dsh-files-pane/read, reading the filesystem through the deployment's own fs service. |
client/client.js | Browser | The View tab, the header action, the file-open interception, and all presentation. |
package.json | — | The dsh.bundle manifest (installable), the dsh.client declaration (platform: web), and the ./client export the module scanner reads. |
cordis.patch.yml | — | The bundle patch that mounts this package by name when it is installed as a package. |
The browser half is a hand-written bundle in the client module-table format
(window.__ModuleLoader__.load({id, factory})) — the same shape the shipped client packages ship —
so it can require("react") and require("@deepseek-ai/dsh-client-ui-primitives") directly.
Both are reached through Cordis services and both degrade safely, but both are internal enough to deserve a note:
conversation.session slot store (actions.setView). Selecting this tab
programmatically therefore goes uiSession.adapter.resolve(sessionId) →
slots.resolveStore(entry.store, binding) → store.actions.setView('files'), which is the same
cached per-session instance the header reads. If a future release renames either hop, the failure
is logged and the plugin falls back to manual tab switching.remote.session.openWorkspacePath is replaced on the Remote
namespace service (saved descriptor, restored on unload) and answers with the Remote envelope
{ok, value} the caller expects. This is what makes a click in the conversation land here.The two host routes sit outside /api (no gateway involved), so they carry their own guard: POST
only, plus an Origin/Host same-origin check.
There is no build step: edit client/client.js and reload.
Browser half only — ask the host to re-hash the bundle in place, then refresh the page:
ctx.get('clientModules').rebuilt('dsh-files-pane')
(A tiny Cordis plugin calling this is enough; a page refresh then fetches the new revision.)
Host half or package.json — restart dsh.
The client HMR driver only helps while pnpm run dev:web is rebuilding bundles from a checkout; this
package is served from its own directory, so the two steps above are the whole loop.
MIT — see LICENSE.