面向模型的 display_file 工具,在网页 UI 内联渲染 36 种扩展名:图片(点击开灯箱)、可拖进度条的视频、音频、PDF、经 LibreOffice 在 Host 转为 PDF 的 Office 与 OpenDocument 文件,以及沙箱 iframe 中的本地 HTML;字节由带 Range 支持的签名 HTTP 路由流式传输,PNG/JPEG/WebP/GIF 在支持图像输入的路由上同时进入模型上下文。
安装
# Release 预构建包
dsh plugin --profile web add "https://github.com/Crosery/dsh-viewer/releases/latest/download/dsh-viewer.tgz"
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:Crosery/dsh-viewer
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
@crosery/dsh-viewer
万物皆可渲染。 一个 display_file 工具,把图片、视频、音频、PDF、Office 文档和本地网页直接渲染进 DeepSeek Harness 的对话流——是真的能看、能播、能拖进度条,不是一个文件名加一个字节数。
支持的格式:6 类 36 种扩展名
| 类别 | 渲染元素 | 扩展名 |
|---|---|---|
| 图片 | <img> + 点击开灯箱 |
png jpg jpeg webp gif svg avif bmp ico apng |
| 视频 | <video controls>,可拖进度条 |
mp4 m4v webm ogv mov |
| 音频 | <audio controls> |
mp3 m4a aac wav flac ogg oga opus |
| 内嵌阅读器 | pdf |
|
| 文档 | Host 转 PDF 后内嵌 | docx doc rtf odt xlsx xls ods pptx ppt odp |
| 网页 | sandbox <iframe> |
html htm |
其余格式仍然给一张带类型、大小和打开链接的卡片——没有任何一种类别是渲染不出东西的。
视觉路由上,png/jpg/jpeg/webp/gif 同时进入模型上下文:一次调用既给你看,也让模型看见。其余只给你看,卡片会如实标注。
截图
图片、视频、音频,一个回合内全部渲染:

Word / Excel / PowerPoint,Host 转换后内嵌:

PDF 与本地网页,实时渲染:

安装
dsh plugin --profile web add github:crosery/dsh-viewer
然后把包名加进 profile package.json 的 dsh.profile.bundles,并重启 profile——bundle 成员变更不走热重载。
仓库里直接提交了构建产物,所以这条命令不需要任何构建步骤,也不需要你在 profile 的 allowBuilds 里批准任何脚本。想要锁定版本安装的话,每次 release 也附带了同样的 tarball:
dsh plugin --profile web add https://github.com/Crosery/dsh-viewer/releases/latest/download/dsh-viewer.tgz
桌面版:打开插件 → 添加插件,粘贴上面那条 release 压缩包地址。dsh 命令行拒绝操作桌面 profile(profile "desktop" is managed exclusively by the Electron application),所以这是唯一的入口。在那里升级已装插件需要重启应用。
harness 0.1.7(包括桌面版)需要 0.2.0 或更新的版本。 从 0.1.7 起 harness 会自己检查插件的 peer 范围,而 v0.1.1 的范围止于 0.1.6 以下:安装会被拒绝,已经装好的副本在启动时被悄无声息地跳过——对话照常,但什么文件都不会显示。harness 0.0.1 与 0.1.0-rc.2 – rc.7 需要 0.2.1 或更新的版本:更早的范围从 0.1.0-rc.8 起算。那些版本自己不检查范围,但 npm 和 pnpm 会。
Office 渲染需要一个转换器。从 harness 0.1.6-alpha.2 起(包括桌面版),harness 自带的内置转换器就能处理 doc docx xls xlsx ppt pptx,什么都不用装。rtf 和三种 OpenDocument 格式,以及更早版本上的所有文档格式,仍需要 LibreOffice(PATH 上的 soffice,或 macOS 的应用包):
brew install --cask libreoffice
没有转换器也不影响其余格式,文档卡片会明确说明缺什么。
转好的 PDF 缓存在 $DSH_HOME/.dsh-viewer-cache/,所有 profile 共用,所以一份文档每改一次只转一次,而不是每显示一次转一次。缓存有上限:30 天没用过的产物会被删掉,超过 256 份或 512 MiB 时先删最久没用的。卡片加载它的 PDF 也算一次使用。
它解决什么
dsh 出厂只有 read_image:它存在的目的是把图片塞进模型上下文,所以
- 路由模型不声明
image输入时,它直接拒绝; - 只认 PNG/JPEG/WebP/GIF;
- 视频、音频、PDF、网页完全没有入口;
- 而且在 harness 0.1.2 及以前,内置 Web 客户端不给它画卡片——
read_image落在通用工具行上,人在屏幕前看到的只有一行Read image /path/to/a.png。(0.1.3 起 harness 自带了read_image视图,本插件在这个 key 上的卡片会主动让位。)
这个插件反过来:它存在的目的是把文件放到用户屏幕上。所以纯文本路由不是拒绝理由,非图片媒体也不是。
read_image(出厂) |
display_file(本插件) |
|
|---|---|---|
| 目的 | 进模型上下文 | 进用户视野 |
| 纯文本路由 | 拒绝 | 正常显示,只是模型看不见 |
| 图片 | PNG/JPEG/WebP/GIF | 上述 + SVG/AVIF/BMP/ICO/APNG |
| 视频 / 音频 | 无 | MP4/WebM/MOV/OGV,MP3/WAV/FLAC/OGG/M4A/Opus |
| PDF / 网页 | 无 | 内嵌 iframe |
| Office 全家桶 | 无 | docx/doc/rtf/odt、xlsx/xls/ods、pptx/ppt/odp(Host 转 PDF 后内嵌) |
| Web 卡片 | 0.1.2 及以前无(通用行);0.1.3 起有 | 有(在 harness 没有 read_image 视图的版本上,本插件也给它画卡片) |
两条字节通道
一个卡片可能从两个地方拿到字节,优先级如下。
签名资源路由(/crosery/dsh-viewer/asset)——Host 直接流式吐字节。这是唯一能承载视频/音频/PDF/网页的通道,也是唯一支持 Range 请求的通道,而 Range 正是 <video> 能出现进度条、Safari 肯开始播放的前提。
持久化附件——ctx.attachments 那条老通道。只有图片,但它在两种情况下不可替代:文件系统后端不暴露本地路径(远程 workspace)时,以及渲染出厂 read_image 结果时(那份结果只有附件,没有 URL)。
两条通道不是冗余:视觉路由上的 PNG 仍然要走附件,因为那是它进入模型上下文的唯一方式。0.1.7 起持久化图片改由对话自带的加载器读取,同一会话里每张图只读一次。
桌面版里,视频和音频从 Host 的回环地址(__DSH_TRANSPORT__.streamBaseUrl)加载,而不是窗口的 dsh-app: 源:应用的协议转发会去掉 Content-Length,Chromium 于是把媒体 URL 的第一次加载当成不可拖动的流。资源路由按签名授权,所以除了源以外什么都不变。
资源路由的安全边界
路由从不接受浏览器给的路径。工具执行时用一个每 harness 一份的密钥对已解析的绝对路径做 HMAC,路径和 MAC 一起放进 URL;路由只在 MAC 验过之后才认那个路径。
- 密钥:32 字节随机数,
$DSH_HOME/.dsh-viewer-asset-key,0600,首次使用时生成。自包含的签名而不是进程内 token 表,是因为卡片必须活过重启:从会话日志重放出来的卡片手上只有几个月前铸的那个 URL。唯一的例外是转换过的文档:它的 URL 指向缓存里的 PDF,那份 PDF 被淘汰之后,卡片里的 frame 会得到404,直到display_file重新转换这个文件。 - 篡改路径、换密钥、去掉签名、改用 padded base64 拼写——全部 404,且「没签名」和「签了但文件不存在」返回同一个状态码,探测者无法从状态码学到文件是否存在。
text/html和image/svg+xml带 CSP 响应头下发(前者sandbox,后者default-src 'none')。同源直接导航到资源 URL 时,这两种文档否则会以应用的 origin 执行脚本。所有响应带nosniff。- 字节走
createReadStream(processPath)而不是ctx.fs.readBytes——后者会把整个文件读进内存,而两小时的视频正是这插件存在的理由。
Office 全家桶怎么转
目标格式永远是 PDF。纯前端方案(docx-preview / exceljs + 表格网格)过不了本插件的约束:浏览器半边是 lazy-CJS bundle,模块表只答应 shell 基线,所有依赖都得内联,而 PPTX 根本没有可内联的免费渲染器。一个转换器产出一种格式,也意味着卡片只有一条文档代码路径。
- 转换器:LibreOffice headless。PATH 上的
soffice/libreoffice,或 macOS 的/Applications/LibreOffice.app/...;都没有时卡片如实说「需要安装 LibreOffice」并给出brew install --cask libreoffice,不失败整个调用。 - 每次调用私有 profile(
-env:UserInstallation=file://…)。LibreOffice 多个实例共用用户 profile 目录会互相破坏;更要命的是桌面上已经开着 LibreOffice 时,headless 调用会立刻退出且不产出任何文件——这是「明明成功了却没有 PDF」最常见的原因。 - 串行队列。私有 profile 解决了互相破坏,但没解决成本:几个 LibreOffice 冷启动同时跑会拖垮笔记本。重复由缓存吸收。
- 缓存键含 LibreOffice 版本 + 源文件 path/mtime/size。同样的字节经过新版 LibreOffice 是不同的产物,键里不带版本,升级后就会命中陈旧渲染而且看不出来。不对文件内容做摘要:几百 MB 的演示文稿不该为了「查一下转过没有」被读两遍。
- harness 内置转换器的缓存键是它的配置(字体、回退字体、图片分辨率等)加源文件的 path/版本/大小,而不是它的
generation:上游每个转换器实例都用randomUUID()生成generation,拿它做键等于每次重启都是冷缓存,缓存永远命中不了、只会越长越大。配置变了就是新产物,重启不是。它看不到的是配置不变、随 harness 升级换了引擎的情况;这时的产物仍是同一份字节的忠实渲染,会像其他产物一样按时淘汰。 - 产物写在
$DSH_HOME/.dsh-viewer-cache/,先以临时名写在缓存目录里,再 rename 就位——读者要么看不到产物,要么看到完整的,不会拿到半个 PDF 喂给阅读器。临时文件必须和产物在同一目录:rename不能跨文件系统,LibreOffice 的输出目录在系统临时目录下,而 Linux 上/tmp常是单独挂载的 tmpfs,直接从那里 rename 会报EXDEV。所以 LibreOffice 的 PDF 是先复制进缓存目录再 rename。 - 缓存有界,按最近使用淘汰:30 天没用、超过 256 份或超过 512 MiB 时,从最久没用的开始删;
display_file命中和资源路由下发 PDF 都算使用。插件启动时和每次写入后各在后台修剪一次,只动缓存目录里本插件写出的文件名的普通文件——别的文件、符号链接、子目录一概不碰。
实测冷启动 2.6–3.6 秒,之后命中缓存。
为什么把出厂 read_image 藏起来
read_image 和 display_file 只在一件事上重叠——把 raster 送进模型上下文——而同时看到这两个工具的模型会两个都用:先 read_image「我看一眼」,再 display_file「给你看」。真实会话里实测过:一张 1672×941 / 2.2 MB 的 PNG 在一个回合内被吃进上下文两次,对话流里出现两张长得一模一样的卡片。这不是提示词能可靠解决的问题,因为两个工具确实各自都在做模型以为它在做的事。
所以其中一个不再对模型可见。display_file 是严格超集:它处理所有媒体、在纯文本路由上照常工作(read_image 在那里直接拒绝),并且在视觉路由上仍然把可接纳的 raster 送进上下文。藏掉它是纯粹去重,不是去能力。
机制由注册表决定:tools.restrict() 明确拒绝 context-global 调用(「a context-global restriction would mask every agent」),所以限制是在每个 agent 的 agent.ctx 上施加的。
两个触发点,缺一不可:
agent/created—— 覆盖在工具已存在之后创建的 agent。这里的restrict必须被 try 包住:read_image只在挂了附件服务时存在,restrict对未知名字会抛错,而一个抛错的agent/created监听器会否决整个 agent 的发布。tools/change—— 覆盖反过来的竞态。read_image注册在dsh-tool-fs的异步attachments注入里,先创建的 agent 会终生看得见它。已经加上限制的 agent 会跳过,所以重试是幂等的;等待列表用WeakRef持有,不会把 agent 留活。
这条改动需要重启 profile 才生效——它在 Host 半边的 bundle 里,不走热重载。
卡片表头也不再一律叫「图片」:read_image 的卡片叫「读入图片」,这样万一有人关掉这个开关,两种来源在对话流里仍然一眼能分开。
read 打到媒体上:不报错,也不返回垃圾
出厂 read 是 UTF-8 解码。更关键的是:出厂的 dsh-fs-local 会采样文件头,遇到 NUL 字节直接抛 FS_NOT_TEXT(cannot read "…": binary file)。所以 read 指向 PNG 时,无论有没有这个插件,都会在对话流里画一条红色失败行——而那个文件明明存在、可读、离上屏只差一次工具调用。
两种「显而易见」的纠正都去不掉那条红行:tools/pre-execute 拒绝会自己造一个 isError;tools/post-execute 也不行——替换 value 在失败结果上被明确拒绝,替换 content 又保留 isError。
所以纠正放在 tools/execute(around-dispatch),并且不调用 next():注定失败的读取根本不发生,一次文件 I/O 都没有。返回的自造结果会经过 normalizeDispatchResult,对成功结果它会用本插件给的 value 重跑该工具自己的 output.render 和 output.presentationMeta——于是持久化的 read 元数据也被替换掉,出厂 read 卡片照常渲染,只不过内容是一行指向 display_file 的说明,而且是一次普通的成功读取。
配套还有一段系统提示词,紧跟出厂 tool:read 的指引:0.1.1 及以前是 order 101(出厂是 100),0.1.2 起取 getSectionOrder('TOOL_READ') + 1。当前 agent 调不到 display_file 时这段为空;只有 redirectRead 打开时它才提到 read 会被引回 display_file;在提供 present 的版本上,它把交付物让给 present、把答案里的配图让给 markdown 图片,避免同一个文件被展示、内嵌、交付三遍。.html 不在纠正范围内:读 HTML 源码是正当的文本读取,显示它是另一个意图。
完成的轮次仍把展示过的文件留在屏幕上
从 0.1.6 起,对话会把一个已完成轮次的工具行(包括本插件的卡片)收进一条「用时 N 秒」的折叠行,0.1.7 起这是默认行为。一个存在的意义就是把文件摆到用户眼前的插件,答案一出来卡片就被收起来了。所以插件同时往对话的轮次尾部(turn tail)贡献一项——就是轮次收尾回复和操作行之间那一栏,harness 自己的交付文件卡片也放在那里——在轮次完成后把这一轮展示过的文件显示在那里。从 run_code 里展示的文件也算:这类调用记成 tool/ptc-dispatch 事件,不带轮次号,所以在轮次结束时按对话引擎自己给它们定的位置收集。凡是工具行本来就看得见的地方,尾部都不出现:verbose 对话视图,仍在运行、被中止或出错的轮次,以及在 0.1.7 上用户在运行途中插话(steer)过的轮次——0.1.7 的对话不会折叠这种轮次。
配置
四个开关,默认值都是「插件该有的行为」:
| 字段 | 默认 | 关掉之后 |
|---|---|---|
tool |
true |
不注册 display_file(它会从模型看到的 schema 列表里消失,而不是留在那里拒绝调用) |
redirectRead |
true |
read 可以自由地把 raster 解码成替换字符 |
feedModel |
true |
图片只上屏,永不进模型上下文——图给人看而不是给模型看时更省 token |
supersedeReadImage |
true |
出厂 read_image 重新对模型可见(于是又可能出现同一张图进两次上下文) |
在哪里改取决于 harness 版本:
0.1.6 及以前:改
$DSH_HOME/settings.yaml的crosery-viewer段,热重载,不需要重启。0.1.7 起(web 与桌面版):本插件没有设置表单。设置页只为插件标成 volatile 的字段生成表单,而这几个字段都不是。请在 profile 自己的 patch 文件
$DSH_HOME/profiles/<profile>/cordis.patch.yml里给插件条目viewer写配置——dsh --profile web用~/.dsh/profiles/web/cordis.patch.yml,桌面版用~/.dsh/profiles/desktop/cordis.patch.yml——然后重启该 profile 或桌面应用:- id: viewer config: redirectRead: false这一行会整体替换条目的
config,而不是与之合并,所以没写的字段保持默认值。0.1.7 上settings.yaml已被移除,它的一次性导入把每一段写进同 id 的条目,所以crosery-viewer段不会被带过来——它只留在settings.yaml.imported里。
装到 profile
dsh plugin --profile web add github:crosery/dsh-viewer # 或本地开发:link:
然后在 profile package.json 的 dsh.profile.bundles 里加上包名。bundle 成员变更必须重启 profile(cordis.patch.yml 的编辑才走热重载)。
本地开发时客户端半边有个坑:装的 dsh-client-modules@0.1.1-rc.2 用 require.resolve(spec + "/package.json") 解析清单,只认包名不认文件绝对路径。所以 --patch 里写 name: /abs/path/lib/index.js 只会加载 Host 半边,客户端 bundle 静默不进 boot 图。让包能按名字解析(link: 或 symlink 进 profile 的 node_modules),patch 行写包名。
验证
npm run typecheck # 两个 program 分开检查
npm run build # 两份 .d.ts + 两个 bundle(产物提交进仓库,改动后要一并提交)
npm run check # 仓库不变式(README 计数、locale 键、peer 范围、安装路径)
npm run check:dist # 提交的 lib/ 与重新构建逐字节一致
npm test # 147 个用例
v0.1.1 时在真实环境跑通(dsh 0.1.1-rc.2,Node 26.7.0,claude-sonnet-5 路由,headless Chrome 驱动;0.2.0 在 0.1.7-rc.2 的 Web 与桌面版上的实测见 docs/acceptance.zh.md):
- 七种卡片全部渲染:图片(
<img>原尺寸,点击开灯箱、Esc 关闭)、视频(<video>有进度条,currentTime = 4跳转成功,seekable.end = 6)、音频(<audio>,duration 5)、PDF、文档(docx / xlsx / pptx)、网页、通用文件。 - Office 三件套真的转出来了:三个 iframe 的 src 全部返回
200 application/pdf且以%PDF-开头,frame 内部含 Chrome PDF 阅读器的<embed>;pptx 显示为 1/3 页并带幻灯片缩略图侧栏,正文是真实的幻灯片内容。 - 全部默认展开(
aria-expanded="true"),不再有「只有标题、看不到内容」的卡片。 - 徽标正确:视觉路由上的 PNG 显示「已进入模型上下文」,且模型确实描述出了「64×48 红色图片」;其余显示「仅在页面显示」。
- 重放安全:整页 reload 之后卡片全部从持久化的
tool/resultmeta 重建。 - 一次调用只进一次上下文:模型可见工具里已无
read_image(问它「你能调用 read_image 吗」答「不能」);一次display_file的会话日志里恰好一个 image content block。 已进入模型上下文是真的:给模型一个名字毫无提示的asset-0417.png(蓝底黄色大数字 7),它答出「数字 7、背景蓝色、图形黄色」——只有真看到才答得出。read打到图片上不再报错:会话日志里那条tool/result的isError不存在(成功),meta是本插件替换后的单行FsReadMeta;页面上是一条普通的Read · /tmp/…/red.png行,整页搜不到Error:。- 嵌套
run_code调用有内容:# Code · Display page.html via display_file tool的子行下面,iframe 内真的渲染出了页面正文,且该 frame 仍带sandbox="allow-scripts allow-forms allow-popups"。 - 资源路由:
Range: bytes=4-9→ 206 + 精确 6 字节;bytes=-4→ 尾部;bytes=999999-→ 416 +bytes */25515;伪造签名、把路径换到/etc/passwd、无签名 → 全部 404;POST → 405。
tests/asset-route.test.ts 把 handler 挂在真的 node:http 上跑真的请求:Range 的正确性无法靠解析器单测证明,必须看响应到底带了哪些字节和头。tests/convert.test.ts 用 LibreOffice 自己造 DOCX 再转回 PDF,断言产物头四字节是 %PDF-,并验证二次调用命中缓存不重写产物。
两份 tsconfig 是必需的,不是洁癖。 两个半边都在增强同一个 @deepseek-ai/cordis 的 Context,而 sessions 在 Host 侧是 SessionStore、在浏览器侧是 ISessions。一个同时看见两份增强的 program 会静默解析到错的那个(skipLibCheck 把冲突盖掉了),表现为 ctx.sessions.binding 不存在。
inject 里不要写 webServer。 未激活的条目是硬启动失败(dsh: 1 entry did not activate),不是优雅跳过——写进 inject 会让这个 bundle 无法与 dsh-headless/acp 组合,而不只是在那里失效。路由挂在 ctx.inject(['webServer'], …) 的嵌套作用域里,没有 HTTP 服务时工具照常注册,只是不铸 assetUrl。
两个只有真跑才会发现的坑
嵌套 run_code 调用拿不到 presentationMeta。 注册表只为顶层调用投影(exec.parent === undefined),所以模型在 run_code 里调 tools.display_file 时,卡片手上什么元数据都没有,只剩一行光秃秃的表头。修法是让模型可见的信封多带三个结构化字段(<media> / <bytes> / <asset>),meta 缺失时从 content 里的信封恢复——content 对嵌套调用同样会持久化。这是卡片解析自己写的结构化信封,不是从散文里抠 id;恢复出来的值仍然走和重放路径完全相同的校验,包括「assetUrl 必须是本插件路由」那道门。
PDF 的 iframe 不能加 sandbox。 不带 allow-same-origin 的 sandbox 给 frame 一个不透明 origin,而 Chrome 内置 PDF 阅读器拒绝在那里运行——frame 里显示的是「此页面已被 Chrome 屏蔽」而不是文档。PDF 本来也不需要 sandbox:它以 application/pdf + nosniff 下发,浏览器交给自己隔离的阅读器,不会在本 origin 执行任何东西。本地 HTML 正相反——那是 agent 可能刚写出来的任意脚本——所以它保留不透明 origin,并由 Host 随响应下发的 sandbox CSP 兜底。
Harness 版本兼容
| 在哪 | 版本 | 证据 |
|---|---|---|
Web(dsh --profile web) |
每一个已发布版本,从 0.0.1-rc.1 到 0.2.1-alpha.1,包括 npm 的 latest(0.2.0-rc.2)、next(0.2.0-rc.2)和 alpha(0.2.1-alpha.1)。0.0.1-rc.1 与 rc.2 也被接纳,但它们自己的 harness 谁都装不上 |
每个能装上的版本上两份类型检查和全部 174 个测试;打包后插件在每个能装上的版本(共 28 个)上的启动冒烟,并在无头 Chrome 里实际运行;0.0.1-rc.5、0.1.0-rc.2、0.1.0-rc.6、0.1.7-rc.2 与 0.2.1-alpha.1 上的浏览器实测(v0.1.1 时也在 0.1.1-rc.2 与 0.1.5-rc.2 上实测过) |
| 桌面版 | 0.1.7-rc.2——它唯一的频道 nightly |
在应用自带运行时上的启动冒烟;macOS 桌面窗口里的实测 |
peer 范围恰好接纳上面这些支持,每个元组一组比较器,无论按 npm 的 semver 规则,还是按 harness 从 0.1.7 起自己采用的「包含预发布」规则。0.1.0-rc.8 之前的版本前端更老,没有 dsh-client-ui-renderer;本插件只需要其中的 slots 和 locale 两个服务,每个版本都有。0.1.8、0.2.2 及以后等 CI 验证过才接纳。
CI 负责让这份声明保持真实。每个 pull request 都会在钉住的版本、0.1.1-rc.2 最低线和桌面版当天分发的版本上,跑类型检查、测试、peer 接纳检查,以及打包后插件的真实启动,并在无头 Chrome 里把它跑起来。每天的任务对桌面版和 npm 的 latest、next、alpha 重复这些检查,并在 macOS 上对桌面版压缩包本身再跑一次冒烟。每周的任务扫描每一个已发布的 harness 版本,在其中每个能装上的版本上启动并运行本插件,所以新版本不需要任何人手动添加就会被测到。失败会开一个 upstream-drift issue,第一次全绿的运行会把它关掉。发版也以同样的检查为门禁。详见 docs/harness-compatibility.zh.md。
已知限制
- 只接受本地文件路径,不接受 URL。规范值的形状留了扩展位,但 v1 没做。
- 视频/音频的时长和分辨率不在卡片头部——那需要 ffprobe。
<video>元素自己会显示。 - 插件自己读取只有附件的图片时(0.1.5 及以前),对象 URL 要到插件卸载才回收。上界是这期间显示过的不同附件数量,与出厂对话图库按会话持有的上界同量级。0.1.7 起这些图片由对话自带的加载器持有。
- 远程 workspace(后端不提供
processPath)上,非图片媒体没有可用通道,卡片如实显示「此文件系统后端不提供可预览的本地路径」。 - 未做视频转码:浏览器放不了的编码(例如
.mov里的 ProRes)会落到<video>的降级文案上。 - 桌面版里「在新标签打开」换成**「在侧边栏预览」**(harness 自带的预览,PDF 用 PDF.js):桌面窗口会静默拒绝指向应用 URL 的新标签。卡片里的内容——图片、播放器、PDF 与文档内嵌框——与 web 上完全一样。
- 轮次尾部需要 0.1.6 起的列表形态。 在 0.1.2–0.1.5 上,折叠(
compact)后的已完成轮次会把卡片留在折叠区里;想让它们保持展开,把对话视图切到normal。在 0.1.6 的normal视图下,卡片会同时出现在轮次里和尾部。 - 用户展开一个已折叠的轮次时,其中展示过的文件会出现两次——工具行里一次、尾部一次——与 harness 自己的交付文件卡片相同。
- 尾部靠对话自己的过程锚点(它的
turn-process轮次数据)识别插话过的轮次。如果将来的对话不再发布这份数据,插话过的轮次会把卡片显示两次——展开的工具行里一次、尾部一次——而不是丢掉它们。
链接
同类插件
zhu1090093659/dsh-web#packages/dsh-task-board★ 8801
侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
zhu1090093659/dsh-web#packages/dsh-web-all★ 8801
DSH Web UI 插件与皮肤合集:任务看板、git 图、右侧面板、远程移动端 UI、桌宠、实时 token 统计与皮肤中心。
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 4908
右下角常驻的小鲸鱼挂件:余额、今日已用与每轮对话消耗(含峰谷价),余额预警与今日预算的泡泡内容都可编辑;泡泡点击序列模块化自定义,支持并列加权 A/B、随机台词与随机图片;内置 30+ 厂商模板(OpenAI / OpenRouter / Kimi / 硅基流动 / 方舟 / 智谱 / MiniMax 等),按模型查余额与订阅额度;另有任务结束音效、导入音频、自定义角色与资源管理。数据全在本机,无遥测。
ccch1mneyyy/dsh-TUI★ 4640
Claude Code 风格全屏终端 UI:像素鲸鱼顶栏、实时工作状态行、思考流式展开。
omdsh-dev/DSH-better-sidebar★ 4198
侧边栏完整工作台:内置文件渲染编辑、终端、Git 与子代理,支持三方插件注册新 Tab。
Devin-AXIS/deepseek-design#deepseek-idesign★ 1463
可视化设计工作室,支持网站、App 原型、海报、信息卡、报告和杂志的模板创建、元素编辑、选区级 AI 草稿衔接与导出。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。