像素比对 dingchenhui0618-arch/dsh-vision-diff
DeepSeek Harness比对两张截图,输出差异结论与对照页
29.5/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/15 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dingchenhui0618-arch/dsh-vision-diff该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · vision
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 24 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 17 处,展示前 14 条)
- 读取本地文件lib/index.js:111
const parsed = JSON.parse(readFileSync(statePath, 'utf8') || '{}') - 读取本地文件lib/index.js:132
if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true }) - 写入本地文件lib/index.js:132
if (!existsSync(dataDir)) mkdirSync(dataDir, { recursive: true }) - 写入本地文件lib/index.js:133
writeFileSync(statePath, JSON.stringify({ history: history, latest: latest }, null, 2)) - 读取本地文件lib/index.js:146
} while (existsSync(join(runsDir, id))) - 读取本地文件lib/index.js:173
info = statSync(path) - 读取本地文件lib/index.js:180
return new Uint8Array(readFileSync(path)) - 写入本地文件lib/index.js:210
mkdirSync(dir, { recursive: true }) - 写入本地文件lib/index.js:212
if (item.bytes !== undefined) writeFileSync(join(dir, item.name), Buffer.from(item.bytes)) - 写入本地文件lib/index.js:213
else writeFileSync(join(dir, item.name), item.text, 'utf8') - 删除本地文件递归lib/index.js:316
rmSync(dropped.dir, { recursive: true, force: true }) - 读取本地文件lib/index.js:464
if (file === undefined || !existsSync(file)) { - 发起网络请求lib/client.js:40
fetch(API, { headers: { accept: 'application/json' } }) - 发起网络请求lib/client.js:149
fetch(API + '/clear', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 的像素级截图比对:输入两张本地 PNG,输出 PIXEL_CHANGED 判定、差异区域和一份自包含的 HTML 对比页。零依赖,全本地。
README
由 DeepSeek 最新模型翻译生成dsh-vision-diff
两张截图进去,出来一句机器可读的结论和一张能给人看的对照页:同一屏两次渲染到底变没变、变在哪。
Pixel-level screenshot diff for DeepSeek Harness。输入两张本地 PNG;输出 PIXEL_CHANGED=true|false 结论、差异区域,以及一份自包含的 HTML 对照页。全部在本地跑——不调模型、不联网、不上传。
它解决什么
截图类插件已经会「看懂内容」:识图、OCR、元素定位、多模态问答。但视觉回归最刚需的那一步没人做——回答「这两张图一样吗」,而且要能用脚本判断,不是让人再看一眼。
所以这个插件的输出以一行字符串开头:
PIXEL_CHANGED=false
一致就是 false,不一致就是 true。grep 一下就能当断言用,后面才是差异比例、差异区域、证据数字和对照页。
它不做内容识别。 它不做「这个按钮是什么」这种判断。它只回答变化,因为那一步可以做成确定性的,而「看懂」那一步不行。
展开完整文档(README 剩余 1 节)
dsh-vision-diff
两张截图进去,出来一句机器可读的结论和一张能给人看的对照页:同一屏两次渲染到底变没变、变在哪。
Pixel-level screenshot diff for DeepSeek Harness。输入两张本地 PNG;输出 PIXEL_CHANGED=true|false 结论、差异区域,以及一份自包含的 HTML 对照页。全部在本地跑——不调模型、不联网、不上传。
它解决什么
截图类插件已经会「看懂内容」:识图、OCR、元素定位、多模态问答。但视觉回归最刚需的那一步没人做——回答「这两张图一样吗」,而且要能用脚本判断,不是让人再看一眼。
所以这个插件的输出以一行字符串开头:
PIXEL_CHANGED=false
一致就是 false,不一致就是 true。grep 一下就能当断言用,后面才是差异比例、差异区域、证据数字和对照页。
它不做内容识别。 它不做「这个按钮是什么」这种判断。它只回答变化,因为那一步可以做成确定性的,而「看懂」那一步不行。
装
npm 版 CLI 管理的 profile
dsh plugin --profile <name> add <插件目录>
桌面版:desktop profile 由 Electron app 独占,npm 版 CLI 会被拒绝—— error: profile "desktop" is managed exclusively by the Electron application 用它自带的那个 CLI 才行(在 app 的 resources/runtime/cli/bin/dsh 下): "<app>/resources/runtime/cli/bin/dsh" plugin --profile desktop add <插件目录>
装完重启一次 app(host 半区的改动必须重启进程才会加载;Node 按 URL 缓存 ES 模块,关开插件换不掉已经加载的代码)。重启后对话视图里会出现「视觉差异」Tab,agent 也多了一个 vision_diff 工具。
用
让 agent 调
vision_diff(before: "shots/home-dark.png", after: "shots/home-light.png")
命令行直接看产物
每次比对会在 $DSH_HOME/vision-diff/runs/<id>/ 留一份完整证据,双击 index.html 就能看,不需要服务器。
参数
| 参数 | 必填 | 说明 |
| before | ✅ | 第一张 PNG 的路径(绝对路径,或相对 cwd) |
| after | ✅ | 第二张 PNG 的路径 |
| ignore | | 忽略区,见下 |
| threshold | | 通道差必须大于它才算变化。默认 0。截图有压缩噪声时用 2~8 |
| blockSize | | 判断两个差异像素是否相连的粒度,默认 16。只影响区域如何分块,不影响区域边界 |
| regionLimit | | 最多返回几个差异区域,默认 20,按差异像素数从大到小取 |
| cwd | | 解析相对路径用的目录。不填则用插件配置的工作目录 |
| label | | 这次比对的说明,会写进报告和对照页 |
忽略区
每次都会变的部分——时钟、光标、随机广告、动画帧——用忽略区排除掉,否则日报式的比对会天天报「有变化」。
以下写法都接受:
{ "x": 0, "y": 0, "w": 100, "h": 40 }
{ "x1": 0, "y1": 0, "x2": 100, "y2": 40 }
{ "left": 0, "top": 0, "right": 100, "bottom": 40 }
[0, 0, 100, 40]
{ "regions": [ { "x": 0, "y": 0, "w": 100, "h": 40 } ] }
{ "x": 0, "y": 0, "w": 0.1, "h": 0.05, "unit": "fraction" }
unit: "fraction" 时按图片宽高的比例解释,所以同一份配置能跨分辨率复用。
宽度或高度取负值表示反方向(人拖框拖反了就是这么写的)。超出图像的部分会被裁剪而不是报错。
解析不了的忽略区不会被静默忽略——它进 ignoreRejected,并在结论、对照页和 Tab 上都标出来,因为一个失效的忽略区会让差异数字虚高,看起来像被测页面出了问题。
输出
{
"pixelChanged": true,
"verdict": "PIXEL_CHANGED=true",
"width": 1920, "height": 1080,
"afterWidth": 1920, "afterHeight": 1080,
"dimensionChanged": false,
"totalPixels": 2073600,
"comparedPixels": 2073600,
"ignoredPixels": 0,
"diffPixels": 1483,
"diffPercent": 0.071527,
"maxChannelDelta": 255,
"meanAbsError": 0.4123,
"boundingBox": { "x": 240, "y": 96, "w": 120, "h": 18 },
"regions": [ { "x": 240, "y": 96, "w": 120, "h": 18, "pixels": 1483, "blocks": 9 } ],
"regionsTruncated": false,
"threshold": 0,
"blockSize": 16,
"ignore": [],
"ignoreRejected": []
}
几个刻意的取舍:
- diffPercent 保留 6 位小数。 1920×1080 里一个像素是 0.0000482%,四舍五入到两位就成了 0.00,紧挨着 diffPixels: 1 读起来像「没变化」。
- 比对范围是 before 的完整面积。 尺寸不一样时,没有对应像素的部分计为已变化,同时 dimensionChanged 置真。反过来——只比重叠区——会让「侧边栏没了」这种渲染事故报出 PIXEL_CHANGED=false。
- regions 的边界是精确像素包围盒。 网格只用来判断连通性(一个视觉变化算一个区域),不用来决定矩形——否则一个像素的变化会被描述成 16×16 的「区域」。
- meanAbsError 是每通道平均绝对误差。 四个通道都计入。用「每像素最大通道差」去平均会让单通道的变化显得大四倍。
支持的图片
刻意划定的范围,而不是碰巧能跑:非交错的 PNG,颜色类型 0/2/3/4/6(灰度、RGB、调色板、灰度+alpha、RGBA),位深 8 或 16(16 位取高字节)。
不支持的会明确报出来,不会近似解码:
- Adam7 交错 → 报错并提示另存为非交错
- 位深 1/2/4(每字节多像素)→ 报错并提示另存为 8 位
- 非 PNG 文件 → 报错说明文件头不匹配
- CRC 校验失败 → 报错说明文件已损坏
这一条比看上去重要:这个插件的全部价值就是它的数字可信,所以一个把不支持的布局猜着解出来、给出一个看似合理但错误的像素数的解码器,比一个直接拒绝的解码器糟得多。
隐私与成本
全部本地。 不调用模型、不联网、不上传图片。截图里可能是别人的私有页面、客户后台、未发布的产品——这也是它做成插件而不是服务的理由。
零依赖。 host 半区只 import node: 内置模块和同目录兄弟模块。PNG 编解码是自己写的(src/png.js),因为它必须存在,而引一个解码器库就破坏了这条铁律。
数据落在哪
| 路径 | 内容 |
| $DSH_HOME/vision-diff/runs/<id>/index.html | 自包含对照页,可归档、可转发 |
| $DSH_HOME/vision-diff/runs/<id>/report.json | 原始结论 |
| $DSH_HOME/vision-diff/runs/<id>/diff.png | 差异图:红色=变化,蓝色=已忽略 |
| $DSH_HOME/vision-diff/runs/<id>/before.png、after.png | 输入副本(keepInputs: false 可不留) |
| $DSH_HOME/vision-diff/state.json | 历史索引与最近一次结论 |
HTTP 面(挂在 webServer 的 /dsh-vision-diff 前缀下):
GET /dsh-vision-diff 统计、最近一次、历史、配置 GET /dsh-vision-diff/view/<id> 自包含对照页 GET /dsh-vision-diff/asset/<id>/<file> 原始 PNG POST /dsh-vision-diff/clear 清空历史索引
容错姿态
这个插件任何一处坏掉都不该拖垮 harness,所以:
- 读文件优先走 ctx.fs,让读取经过 harness 自己的文件系统抽象、遵守它的策略。只有宿主根本没有 fs 服务时才退回 node:fs——出错不会静默退回,因为那等于绕过一个安全边界。
- 像素上限取 ctx.attachments.imageLimits.maxImagePixels(宿主给的话)与配置的较小值。超限的图在解码前就被拒绝,不是解完再发现占了 4GB。
- webServer.register 失败只是没有对照页和 Tab,vision_diff 工具照常工作——一个变了形状的 web 契约不该让用户连比对都做不了。
- 落盘是尽力而为:state.json 写不进去不影响这次比对的结果。
- 历史滚动:超出 historyKeep(默认 30)的旧记录,连同它的产物目录一起清掉,不会无声堆积。
- 每次注册都可撤销(ctx.effect),因为 dsh 能在运行期卸载插件。
配置
apply(ctx, config) 的 config 全部可覆盖:
| key | 默认 | 说明 |
| dataDir | $DSH_HOME/vision-diff | 产物与索引落在哪 |
| baseDir | process.cwd() | 解析相对路径的基准目录 |
| maxBytes | 64 MB | 单个输入文件上限 |
| maxPixels | 4000 万(取与宿主图像策略的较小值) | 单张图上限 |
| threshold / blockSize / regionLimit | 0 / 16 / 20 | 这三个的默认值,可被单次调用覆盖 |
| historyKeep | 30 | 保留多少次记录 |
| keepInputs | true | 是否在产物目录里留下输入副本(对照页永远内含两边,不受此项影响) |
开发
node --test # 60 个测试
node scripts/build.mjs
测试用的 PNG 是手工构造的,不是编码器自己产出的。 encodePng 只写 filter 0,所以「编码再解码」这种往返测试永远测不到 filter 1–4。测试里有一份独立的 PNG 构造器(自己的分块、自己的 CRC32、自己的正向 filter),解码器的所有滤镜都是对着这份独立实现验证的。
test/host.test.mjs 用桩 Context 驱动真实的 apply():真的把文件写进临时目录、真的走工具执行路径、真的调 HTTP handler,不是这些逻辑的复刻。
和现有实现的关系
没有参考任何现有实现的代码。 这条设想的证据来源是 ankye/dsh-client-vision——它做的是内容识别(识图、多模态问答),回答的是「这屏是什么」;本插件回答的是「这两屏一样吗」。两者不重叠,我也没有去看它的实现。
- PNG 编解码是按 PNG 规范手写的(node:zlib 提供 DEFLATE,其余是分块解析、五种行过滤器、CRC32)。
- 差异算法是自己写的逐像素比较 + 连通块聚类,没有用 pixelmatch 之类的库——引依赖会破坏零依赖构建。
- 工程骨架(零依赖构建、host/client 双半区、cordis.patch.yml、README、测试)沿用 dsh-warden 的既有结构,那是同一个作者自己的脚手架。
本插件是独立仓库,不是任何仓库的 fork。
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
dingchenhui0618-arch/dsh-vision-diff
比对两张截图,输出差异结论与对照页
相关插件 · 视觉能力
智能体工作台Devin-AXIS/iPolloWork
企业级、本地优先的智能体工作台,面向个人和智能体团队——一个工作区,容纳多个智能体引擎;一套统一体系,管理插件和技能;支…
★ 7,364已汉化DSH
72
开源设计工作台nexu-io/open-design
OpenDesign:首个协作式 AI 设计智能体工作区
★ 105,709已汉化DSH
71.8
GPT 图像提示词库freestylefly/awesome-gpt-image-2
Prompt as Code | GPT Image 2 / 2.5 提示词与案例库,530+ 个案例、20+…
★ 34,534已汉化DSH
71.5
多智能体编码工作区spacering-net/codeg
多智能体协作的 AI 编程工作区:汇总 Claude Code、Codex、OpenCode、Pi、Grok Build 等来源的会话。支持桌面应用、自托管服务器或 Docker。
★ 4,319已汉化DSH
71.5
AI 用量桌面小部件Javis603/token-monitor
EN | 简 | 繁 | KO | JA | PT
★ 3,533已汉化DSH
71.1
架构图生成器tt-a1i/archify
把你想理解、规划或分享的事,变成可以互动的可视化作品。
★ 93,105已汉化DSH
71.1
这是你的仓库?认领条目