dsh-tu4-inline-images
对话内联图片 DSH 插件 — 在 DeepSeek Harness (DSH) Web GUI 的对话中,出现本地图片路径即直接渲染为图片。
A DSH plugin that renders local image paths as inline images in DeepSeek Harness (DSH) web conversations. Security-first: loopback-only route, strong per-process token, multi-root realpath whitelist.
What it does / 功能
When a local image path appears in any of these three surfaces, it is displayed as an image (display size capped by a user-selectable tier — no thumbnail files are generated):
当以下三个位置出现本地图片路径时,直接显示图片(展示尺寸受用户可选档位上限约束,不生成缩略图文件):
- Input box / 文本输入框 — paste or type a path in the composer and a preview row appears above it (the path text stays in your draft)
- User message / 发送框(发送后用户消息气泡) — send a message containing a path and the image renders inside your bubble
- LLM output / LLM 输出框 — when the assistant's reply references a path, the image renders inline in the reply
Supported formats: png / jpg / jpeg / webp / gif / svg / avif / bmp / ico (9). Both bare paths (D:\pics\photo.png, /home/me/pics/photo.png, \\server\share\photo.png) and markdown () are recognized.
展示尺寸三档(CSS 上限,保宽高比):小 320×240 / 中 640×420(默认)/ 大 960×600。
How it works / 工作原理
- Host half (Node) registers a loopback route
GET /plugins/dsh-tu4-inline-images/image?t=<token>&p=<path> on the DSH web server, plus a same-origin GET .../state route that serves the token to the client half. The host rewrites local image paths in LLM stream output to that route URL (display-only; the route handler remains the sole file-read enforcement point).
- Client half (browser, pure display) provides the input-box preview row (standard DSH slot
conversation.input.dock), the user-bubble DOM post-processing (conservative: single text node + user row + outside code blocks), and a settings card (size tiers + extra whitelist roots) in the DSH settings UI.
- Security chain (every image request must pass): token → extension whitelist → multi-root realpath containment (default roots: session workspace + DSH paste root; plus user-added roots from settings) → regular-file stat → byte-size limit (aligned with the host's
attachments.imageLimits).
- Error responses are fixed short strings — paths are never echoed.
Requirements / 前置条件
- DSH (DeepSeek Harness) with the web profile (Web GUI), e.g.
dsh web
- The GUI should run on loopback (default). The plugin only serves images over the loopback route; see Known boundaries.
Install / 安装
Option A — prebuilt tgz from Releases:
# bash / zsh
dsh plugin --profile web add file:$(pwd)/dsh-tu4-inline-images-1.1.0.tgz
# PowerShell
dsh plugin --profile web add file:D:\path\to\dsh-tu4-inline-images-1.1.0.tgz
Option B — build from source:
git clone https://github.com/zehenk/dsh-tu4-inline-images.git
cd dsh-tu4-inline-images
pnpm install
pnpm run check # build + security gate + tests (74 tests)
pnpm pack # → dsh-tu4-inline-images-1.1.0.tgz
dsh plugin --profile web add file:$(pwd)/dsh-tu4-inline-images-1.1.0.tgz
Host plugins load at web-server boot: restart dsh web after install (and after uninstall). 安装/卸载后需重启 dsh web 生效。
The install follows the standard DSH profile install (~/.dsh/profiles/web/): the plugin is recorded in the profile's dependencies and dsh.profile.bundles, and its node_modules artifacts are resolved by pnpm. No environment variables, no custom directories, no workspace coupling — it keeps working when you switch conversation workspaces.
安装遵循 DSH 标准 profile 安装,不依赖任何环境变量或自定义目录,切换会话工作区不失效。
Uninstall / 卸载
dsh plugin --profile web remove dsh-tu4-inline-images
# then restart dsh web
Removes the dependency, the bundle registration, and the node_modules artifacts — verified clean.
Usage / 使用
- Input preview: paste or type an image path in the composer — a preview row appears above the input box. The path text stays in your draft (pure display, no delete button); when you send, the image appears in your message bubble.
- LLM output: ask the assistant to reference an image path (or let it report one) and it renders inline in the reply.
- Settings card: GUI → Settings → plugin settings → 对话内联图片 (inline images):
- 展示尺寸 / size: 小 / 中(默认)/ 大 — applied immediately to all three surfaces
- 追加图片目录 / extra roots: add any number of absolute directory paths whose images should be renderable; remove any time. Default roots (session workspace + DSH paste root) are built-in and not removable.
Smoke test with the bundled sample: docs/test.png — put it under a whitelisted root (or add its directory) and paste the path.
Security / 安全
Security is the premise; functionality is the goal. Design invariants:
| 项 | 设计 |
|---|
| 网络端点 | 仅回环;URL 前缀运行时取自 webServer.host/port,源码零 URL 字面量(test/gate.mjs 构建门禁强制);host 为 0.0.0.0 时 URL 强制 127.0.0.1 |
| 文件读取 | 扩展名白名单 → 多根 realpath 包含校验(拦截 .. 穿越与符号链接/junction 逃逸)→ 常规文件 stat → 字节限流(对齐宿主 attachments.imageLimits.maxImageBytes) |
| token | crypto.randomBytes(32).toString('hex')(256 bit),进程级单 token;响应 Cache-Control: private, no-store + X-Content-Type-Options: nosniff;SVG 另附 Content-Security-Policy: sandbox |
| 流改写 | 仅针对本地图片路径模式介入;跳过带 purpose(compaction/session-title)的辅助调用;路由 handler 是唯一执行点,改写纯展示,任何异常原样透传 |
| 客户端 | 零文件系统访问;无 innerHTML 拼用户数据;无全局监听;网络仅同源 |
| 错误响应 | 统一短文案,绝不回显路径 |
- Build gate (
test/gate.mjs, runs on every build): zero URL literals, zero Math.random, zero install-type lifecycle scripts in shipped code.
- 4-stage security self-audit record: docs/SELF-AUDIT.md (zero high-risk findings; 7 documented residual risks, all assessed acceptable).
- Reporting vulnerabilities: see SECURITY.md.
Known boundaries / 已知边界
<img> requests carry no Origin header — the token is the sole gate; the token appears in the message DOM (<img src>) and is same-origin scoped.
- Deploying DSH with
--host 0.0.0.0 exposes the route to the LAN (token still gates it); loopback deployment is recommended.
- Surface ② (user bubble) is conservative DOM post-processing: a path split across multiple DOM text nodes is not replaced there (the path text remains; surface ③ is unaffected). Repeated occurrences of the same path in one message: the first renders, the rest stay as text.
- Surface ② relies on a stable product DOM marker (
data-time-hover-root on user rows); after a major DSH GUI upgrade, re-verify (see docs/PLAN-v3.md §3.2).
- Planned (v2): click-to-zoom lightbox.
Development / 开发
pnpm install
pnpm run check # = node build.mjs && node test/gate.mjs && node --test "test/*.test.mjs"
src/ — host half (index.ts, path-guard.ts, scan.ts, rewrite.ts, index-style.ts, settings.ts, state-route.ts) + client half (client.ts) + shared constants (paths.ts). TypeScript; tests run on Node ≥ 22 native TS type-stripping (.mjs tests import .ts directly), so src relative imports use explicit .ts extensions.
build.mjs — esbuild dual bundle: lib/index.js (node ESM, host) and lib/client.js (browser CJS wrapped in the DSH window.__ModuleLoader__.load({id, factory}) module-table contract — see docs/PLAN-v3.md §3.5).
test/ — 74 unit/integration tests + gate.mjs security gate.
- Invariants to preserve when contributing: route handler as sole read point, zero URL literals, no
Math.random, no lifecycle scripts, multi-root containment before any read, error responses never echo paths. See CONTRIBUTING.md.
Repository layout / 目录结构
├── src/ 插件源码(host 半 + 客户端 half + 共享模块)
├── test/ 单测/集成测试 + 安全门禁(gate.mjs)
├── docs/ 设计与安全文档(见 docs/README.md)
├── build.mjs esbuild 双 bundle 构建
├── cordis.patch.yml DSH bundle 挂载(host 插件,仅 insert)
├── dsh.plugin.json 社区兼容清单(DSH 实际读取 package.json)
├── package.json DSH 权威清单(dsh.bundle.patch + dsh.client + exports)
├── CHANGELOG.md
├── SECURITY.md
└── CONTRIBUTING.md
Credits / 致谢
The image-path scanning algorithm is inspired by the community plugin dsh-inline-images (Asher-2000). This project is a complete rewrite that fixes every security finding from its audit (hardcoded external endpoint, no path whitelist, weak token, install-type lifecycle script) and adds the three-surface display, multi-root whitelist, and settings.
路径扫描算法参考社区插件 dsh-inline-images;本项目为完全重写,修复其审计发现的全部安全问题,并新增三位置展示、多根白名单与设置。
License / 许可证
MIT
English summary (for quick scan)
DSH web-profile plugin. Renders local image paths as inline images in (1) the input box preview, (2) sent user-message bubbles, and (3) LLM replies. Loopback-only image route with a 256-bit per-process token; multi-root realpath whitelist (workspace + paste root + user-added roots via settings); display size tiers small/medium/large via a settings card; 9 image formats; zero runtime dependencies; 74 tests + build-time security gate; MIT. Install: dsh plugin --profile web add file:<tgz> (see above). Uninstall: dsh plugin --profile web remove dsh-tu4-inline-images. Restart dsh web after changes.