dsh-plugin
DayuanJiang/next-ai-draw-iodsh 的 draw.io 绘图插件:通过 next-ai-drawio MCP 服务器作图,点回复里的链接在右侧栏打开实时预览,可以手动修改图,AI 的每次修改都是可以恢复的版本。需 Node.js 18+,首次使用由 npx 下载 MCP 服务器。
DeepSeek Harness 的文档预览 / 编辑 / 管理插件。在 DSH Web GUI 中提供一个 VSCode 风格的「文档中心」工作台:左侧文件树,点击即预览,代码与 Markdown 支持编辑与 Ctrl/Cmd+S 保存;同时为 Agent 暴露 doc_read / doc_edit / doc_create 三个文档工具,让模型可以通过自然语言读写工作区文档。
🌐 中文 | English
A document preview / edit / management plugin for DeepSeek Harness. It provides a VSCode-style "Document Center" workbench inside the DSH Web GUI: a file tree on the left, click-to-preview, editable code & Markdown with
Ctrl/Cmd+Ssave; and exposes three document tools (doc_read/doc_edit/doc_create) to agents, letting models read and write workspace documents through natural language.
| Category | Formats | Implementation |
|---|---|---|
| Office documents (read-only) | .docx .xlsx .pptx | Metadata + friendly "preview not supported yet" notice card (Office preview kernel not loaded) |
| Code & config | .py .java .go .rs .cpp .c .js .ts .jsx .tsx .json .yaml .yml .toml .xml .ini .conf etc. | Lightweight syntax highlighting (keywords / strings / comments / numbers) + editing + Ctrl/Cmd+S save + Tab indentation + bracket auto-pairing |
| Markup & rich text | .md .html | Markdown: edit/preview dual mode — preview renders headings, lists, code blocks, tables and images, with support for relative images and relative links; HTML: sandboxed preview (CSP disables scripts + iframe sandbox attribute, double isolation) + source view + open in new tab (unidoc.openExternal) |
| Static assets & layout | .png .jpg .jpeg .gif .svg .webp .pdf | Images scale to fit; PDF embedded browser viewer (paging / zoom provided natively by the browser) |
| Data science (exploratory) | .ipynb | Read-only notebook preview: Markdown cell rendering + code cell highlighting + text output |
| Plain text fallback | .log .csv .txt and any unclassified text | CSV rendered as a table; everything else opens as read-only plain text — unknown extensions never crash |
| Explicitly unsupported | Audio/video (.mp4 .mp3 etc.), iWork (.pages .numbers .key), CAD (.dwg), OpenPencil (.op) | Friendly "preview not supported" notice with file info |
sidebar.footer.action, Font Awesome fa-file-pen) — toggles the workbench;shell.overlay):
hintCwd, so any workspace — including an old historical session — is hit precisely with no stale root; after a switch the tree is fully reset: cache cleared, expanded state, selected path and scroll position reset to the root, preview closed, so the top path and the tree always match);.git, .github, .vscode, node_modules …), loading asynchronously in batches without freezing the page; "Collapse All" collapses everything and frees the cache; refresh / expand-all / collapse-all / options / close buttons sit below the tree (bottom-left);unidoc.openExternal);tool.view.cordis): shows the plugin's activation state with a one-click open button;| Tool | Description |
|---|---|
doc_read | Read a document/code file by path (supports offset/limit for line-based reads of large files; binary files return file info) |
doc_edit | Replace the unique occurrence of old_string with new_string in a file and save atomically (0 or multiple matches both fail with a clear message) |
doc_create | Create a new file in the workspace (no overwrite by default; overwrite=true allows overwriting) |
All paths are relative to the Document Center root (the current session workspace) and
are validated with fs.contains to prevent directory traversal.
src/host.js, runs in the DSH Node process)
inject: ['fs', 'webServer', 'sandboxPolicy']hintCwd (authoritative) — the client reads the currently selected session's workspace cwd from the DSH client runtime sessions service (sessions.manager.selected → sessions.list.getSnapshot().byId[id].cwd) and sends it with every unidoc.root(hintCwd) call; the host uses it directly (after verifying it is a directory) — no matter whether you switched to a brand-new session or an old historical one, the current workspace is hit precisely, so the tree can never keep showing a stale workspace;
cwd (agents.currentInitiator() → session.header.cwd) — only valid in agent tool-call contexts; browser RPCs run outside the initiator boundary and yield undefined;cwd from the online agent list (agents.list(), registration order — old first, new last) — iterated newest-registered first; the just-activated session is most likely the current workspace;sessionQuery.listSessions() ordered by createdAt desc — excludes persisted "ghost" sessions (a historical session's createdAt can be the largest while it is no longer the current workspace);sessionQuery.listSessions() records (including persisted) ordered by createdAt desc;sandboxPolicy.workspaceRoot (re-read dynamically each time).
Tool execution additionally honors the caller agent's (exec.agent) session cwd to precisely target the current workspace;
unidoc.root accepts refresh: true to drop the cache and re-resolve, letting the client sense workspace switches.SandboxExecutionPolicy (workspaceRoot = resolved workspace); tool calls respect session-mode overrides, e.g. read-only sessions reject writes;unidoc.root / unidoc.list / unidoc.read / unidoc.save / unidoc.create
/ unidoc.openExternal (returns a raw-route URL for opening in a new tab) / unidoc.openWithEditor
(child_process.spawn for the external editor: editorCmd strictly validated, path guarded by fs.contains,
detached + stdio: ignore + unref so the host is never blocked)ctx.effect): GET <rawPrefix>?p=<relative path> serves raw bytes for images / PDF / HTML, attaching Content-Security-Policy (no scripts / no connections) and X-Content-Type-Options: nosniff to HTML responsesharness.defineTool + harness.registerTool, mounted on the plugin Fiber (ctx.effect) and auto-unregistered on stop / updatesrc/client.js, runs in the browser page)
inject: ['slots', 'timer']React.createElement (no JSX, no bundler); styles injected via styles.insert using --dsw-alias-* theme tokens (auto-adapts to light/dark themes)fa-file-penunidoc.root(refresh) on open and every 5s, fully resetting the tree (cache, expanded state, selected path, scroll position) and reloading the current workspace's files on switches; every call carries the currently selected session's workspace cwd as hintCwd (from the runtime sessions service), so the host hits the current workspace precisely even when you switch to an old historical session — no stale root can survive; without a hint the host falls back to candidates (online agents newest-first → live sessions → persisted sessions → fallback root); "Expand All" loads recursively in async batches (hidden directories included) without freezing on huge reposhost.call to the host side; never touches page globals directlyThis repository is the source & documentation repo for dsh-unidoc; the plugin is published as a DSH
static Cordis plugin package (lib/ build artifacts are committed with the repo), and can also be
installed directly as a DSH profile dependency:
# Install as a DSH profile dependency (lib/ ships in the package; prepare also builds automatically)
npm install git+https://github.com/Che-Year/dsh-unidoc
Development (source → artifacts):
# 1. Install build deps (esbuild)
npm install
# 2. Syntax smoke check (isomorphic with DSH define-time preflight)
npm run check
# 3. Build artifacts into lib/ (esbuild bundles the host + custom bundler for the client)
npm run build
# 4. Deploy into a session: submit both sides' source with cordis_define (code.host / code.client),
# then activate with cordis_run (client-side activation requires approval on first run)
After activation:
doc_read / doc_edit / doc_create tools appear on the agent side.Persistent deployment: to auto-load with Harness startup, migrate both sides' source into a static plugin package (dsh-web-ui family style), or place it into the corresponding preset under
~/.dsh/.agent-presets.
External editors are configured as a list (session-level in-memory state, cleared when the plugin unloads):
code), Sublime Text (subl), Atom (atom), Notepad++ (notepad++), Vim (vim), Neovim (nvim), Typora (typora);unidoc.openWithEditor and remembers your last choice as the default for next time;PATH (e.g. VSCode's code requires "Install 'code' command" first); target file paths are always guarded by fs.contains against directory traversal.| Version | Highlights |
|---|---|
| v0.3.6 | Fixed: tree / root not refreshing after a workspace switch (stuck on an old workspace) — the client's authoritative signal (sessions.list.getSnapshot().current) is now retried after a delayed startup so it always reaches the host; the no-hint fallback no longer always hits the session with the largest createdAt when multiple existing sessions coexist; added a hintCwd receipt log and a 42-assertion automated suite (tests/root-resolution.test.mjs) |
| v0.3.5 | Fixed v0.3.4 regression: sidebar plugin icon disappeared (client-half crash) — the DSH client runtime has no timer service, so v0.3.4's blanket ctx[name] forwarding made the Cordis proxy throw (cannot get property "timer" without inject) and crashed the whole client apply; the timer bridge is restored (checked first) and the remaining services are forwarded safely (try/catch, undefined on absence) while keeping the v0.3.4 hintCwd workspace-isolation capability |
| v0.3.4 | Fixed: "always showing the old workspace A" (authoritative-signal fix) — the client reads the currently selected session's workspace cwd from the runtime sessions service and sends it as unidoc.root(hintCwd); the host prefers it, and the no-hint fallback now prioritizes live sessions over persisted "ghost" records, so switching to an old historical session no longer leaves a stale root |
| v0.3.3 | Fixed: tree still showing the old workspace after a switch (root cause) — browser RPCs run outside the agent initiator boundary, so agents.currentInitiator() was unavailable and agents.list() hit a stale online agent from the workspace you just left; the host root resolution was reworked to "recent session first" (newest session → online agents newest-first → dynamic fallback root), and the tree / path state is fully reset on refresh and workspace switches |
| v0.3.2 | Faster workspace-switch sensing + full tree reset — runtime polling shortened to 5s; after a workspace switch the tree is fully reset (cache cleared, expanded state, selected path and scroll position reset to the root, preview closed) with a "workspace switched, tree refreshed" toast |
| v0.3.1 | Fixed: file tree not refreshing after a workspace switch — reopening the Document Center after switching agents/sessions now resets and reloads the tree with the new workspace's files, with no stale data left behind; the top path and the tree stay consistent |
| v0.3.0 | Workspace detection & display; file-tree "Expand All / Collapse All" (hidden dirs included); external editor picker menu with an editable editor list; icon-only sidebar entry with the Font Awesome fa-file-pen icon |
| v0.2.0 | Open HTML preview in a new tab; external editor integration (RPC + command config); Font Awesome file icons by extension in the tree; fixed missing lib/ on git install that broke startup |
| v0.1.0 | Initial release: Document Center workbench (file tree + multi-format preview/edit + save), agent tools doc_read / doc_edit / doc_create |
Full details in CHANGELOG.md.
node scripts/check.js: syntax smoke test for both sides' source;node tests/root-resolution.test.mjs: automated suite (42 assertions) — root resolution & workspace isolation (hintCwd authoritative signal / candidate order / path safety / agent tools);tests/verification.md: manual E2E verification checklist (mounting, file tree, per-format preview, saving, Toasts, tool calls, edge cases);~/.dsh/source/current/; mount capabilities only through the official dynamic-plugin mechanism; reuse official Service/Slot capabilities (fs, webServer, slots, timer).This plugin builds on / reuses the architecture of dsh-better-sidebar; thanks to the original author.
LICENSE file retains the full copyright notices and license terms of the upstream projects (dsh-better-sidebar and the DSH core framework, both MIT-licensed);~/.dsh/source/current/; capabilities are only mounted at runtime through the official DSH dynamic-plugin mechanism, avoiding derivative-work confusion and compliance risks.