DeepSeek Harness Hub
← 返回列表

屏幕视觉插件davidekingsss/dsh-screen-eye

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

让模型自主截屏并直接看到画面内容

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/16 · 已提供中文文档

macOS 上 DeepSeek Harness 的自主屏幕视觉:智能体捕获屏幕并在一次调用中接收图像本身。

综合分
30
GitHub 分
30
用户评分
★ Stars
0
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add davidekingsss/dsh-screen-eye
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-attachment@deepseek-ai/dsh-tools@deepseek-ai/dsh-util-values@deepseek-ai/schemastery
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-screen-eye

给 macOS 与 Windows 上的 DeepSeek Harness 装一只自主的眼睛:agent 截屏后在同一次工具调用里直接拿到图,于是它能自己去看正在运行的应用、弹窗、报错,或者自己刚写的界面——不必再让你手动截图。

无原生构建步骤、不带预编译二进制、零依赖。

它提供什么

两个模型可调用的工具:

| 工具 | 作用 |
|---|---|
| screenshot | 截屏,并把图片本身作为 image 内容块返回给模型——模型是真的看得见。 |
| screen_permission | 在 macOS 上报告本进程当前是否允许截屏;用 action: "guide" 可直接打开对应的系统设置面板并给出需要授权的路径。Windows 没有这项权限可报告,因此该工具在 Windows 上不注册。 |

mode 决定截什么:screen(默认,主显示器)、display(按序号指定某一块
屏幕)、region(指定矩形,原点是主屏左上角,因此在主屏左侧或上方的显示器
取负坐标)、displays(不截图,只列出已连接的屏幕,以及 display 需要的序号
和每块屏幕在 Windows 上的起点坐标),以及需要人交互的 window / select
(等待用户点选窗口或拖拽出选区)。Windows 没有系统级选区工具,所以在那里
select 会被明确拒绝并说明原因,而 window 指的是当前在最前面的窗口。

把 frames 设为大于 1,一次调用就会按 interval_ms 的间隔连拍那么多张并全部返回,
这是"看清随时间变化的过程"的方式。它刻意不是 GIF:harness 以单帧存储图片,
动画 GIF 到达模型时只剩第一帧。

这两个旋钮都交由你调,而"有用的方向"不一定是调细。采集速度取决于面积——
整块 4K 屏要 155ms,而 1200×800 的区域只要 56ms——所以要看清一个几百毫秒的
组件动画,正确做法是截它所在的那一小块,而不是要求整屏用更细的间隔。
返回里会报告实际达成的间隔,达不到时明确说明。
docs/motion.md 有实测数据与推理。

决定成本与分辨率的那个旋钮是 interval_ms:它决定运动被采样得多细,而帧数
就是一次连拍的全部花费。这里没有任何东西强加一段固定时长——连拍的长度是
(frames - 1) × interval_ms,而调用若先等待画面变化,结束时机就由画面决定——
所以唯一的封顶是 timeout_ms(整次调用的预算,默认五分钟,可按次调高)。
帧数上限是 provider 自己的"单请求 600 张图",不是本插件拍的数字:每帧是一张图、
最多 384 个视觉 token(实测约 380),因此帧数是一个成本决策,交给看得见上下文的
调用方比交给插件更合理,工具描述里也把这道算术直接写了出来。

其余情况下每次调用只返回一张图。这是刻意的约束,而不是系统的限制:screencapture
是「每块屏幕写一个文件」,所以在多显示器 Mac 上不加限定的截图会产出多个文件,
而本插件的管线只解析并读取一个路径——其余的会以本插件没有选择过的文件名留在
盘上。因此默认被钉在单块显示器上,要看另一块就用 display。

为什么需要这个插件

多数截图工具假设难点在于「取像素」。在 macOS 上难点是权限,而且它失败
的样子像 bug:

screencapture: could not create image from display

macOS 把截屏能力锁在「屏幕录制」权限后面,而该权限挂在责任进程上——也就是
macOS 认为要对整棵进程树负责的那个应用。而 DeepSeek Harness 的宿主往往不是
一个正常的 GUI 应用:应用内插件市场通过一个 detached 的辅助进程重启宿主,
宿主因此被挂到 launchd 之下,其上方不存在任何应用身份。这样的进程去申请
屏幕时,macOS 无法把请求归属到任何用户可以授权的对象上,于是*直接拒绝,且
连授权弹窗都不会出现。

这个权限无法用程序授予:TCC 数据库受 SIP 保护,tccutil 只能重置,
CGRequestScreenCaptureAccess 对非 bundle 进程拒绝弹窗。所以本插件只做真正
做得到的事:

- 检测:真的去截一次并按结果分类,而不是靠启发式猜测;
- 算准目标:从正在运行的宿主推导出必须被授权的那一个可执行文件,而不是
泛泛描述;
- 打开面板:按需直接跳转到系统设置里对应的那一页;
- 把步骤当作工具结果返回:于是 agent 交给你的是一份修复指引,而不是一段
报错。

Windows 没有这道闸门,也就没有这段故事:那里的难点是截图可能成功但没用——
进程若不感知 DPI,拿到的就是屏幕的降采样副本;若没挂在交互式桌面上,拿到的
就是一张黑图。两者都在下面的 Windows 一节里被解决。

安装

dsh plugin --profile web add github:davidekingsss/dsh-screen-eye
然后重启 dsh

dsh plugin --profile web add github:davidekingsss/dsh-screen-eye
然后重启 dsh

macOS 与 Windows 是同一条安装命令,平台层自己挑引擎。

若用本地检出目录,而不是已发布的源:

dsh plugin --profile web add -w link:/path/to/dsh-screen-eye

本插件没有构建步骤、没有依赖,安装期不编译任何东西。

授予屏幕录制权限(macOS,一次性)

调用一次 screenshot。如果缺权限,返回结果会明确告诉你该怎么做;
screen_permission 配合 action: "guide" 会走完整个引导:先检查,只在授权确实缺失时
才打开面板,并返回需要添加的路径。简言之:

1. 打开 系统设置 → 隐私与安全性 → 屏幕与系统音频录制。
2. 点 +,按 ⌘⇧G,粘贴工具报告的那个路径(通常是运行 harness 的
node 二进制),选中它。
3. 把它的开关打开。

不需要重启——授权对下一次截图即生效。

若你是从终端启动 harness,改为给那个终端 App 授权,效果相同。

macOS 可能定期要求重新确认此权限,把同一个开关重新打开即可。

Windows 完全不需要这些,见 Windows。

配置

所有键都是可选的——而且它们全都可以在 harness 自己的设置页里改:设置 →
Screen Eye(左侧独立一项,眼睛图标),或者直接手改 ~/.dsh/settings.yaml 里的
screen-eye: 一节。两种方式都是下一次调用即生效、不需要重启,并且插件的挂载
条目始终是"清空某个字段后回落到的那一层"。三层关系、这个页面的形态、以及它背后的
两条平台约束见 docs/settings.md。

| 键 | 默认值 | 含义 |
|---|---|---|
| outputDir | /Screen Eye | 截图 PNG 的落盘目录——系统图片文件夹,并单独放一个子目录,免得五十张截图混进你自己的照片里。 |
| locale | en | 引导文案语言:en 或 zh。 |
| timeoutMs | 300000 | 一整次调用的预算(含 wait_for_change 的等待)。等待没用掉的时间才归后面的采集。 |
| frames / interval_ms | 1 / 200 | 每次调用的帧数与帧间目标间隔。上限 600,这是 provider 对单请求图片数的限制,不是本插件的策略——超过的部分会被截下来然后替换成占位文本。每帧是一张图、最多 384 个视觉 token(实测约 380),所以帧数是一个成本决策,由你来做。 |
| duration_ms | — | 直接说明运动持续多久,由工具自己定帧数与间隔,而不必你手算。 |

frames、interval_ms、duration_ms 描述的是同一次连拍,任意两个确定第三个:
帧数 + 间隔给出跨度,帧数 + 时长给出等分该窗口的间隔,间隔 + 时长给出能放几帧。
三个同时给出会被拒绝而不是替你裁决。

这也正是成本杠杆:每帧都是一张图,而图就是成本。窗口不变而调大
interval_ms,就是用更少的图看同一段运动——分辨率换成本;调小则相反。
只给时长时,按常规帧数采样而非上限,因为上限是花钱最多的答案,而你没要它;
但如果这次调用先等待画面变化,结束时机就由画面决定,帧数这时是余量而不是计划。

wait_for_change: true 同时确定了连拍何时结束:画面不再变化时它自己收尾,
于是 frames 变成上限,返回值会说明是哪种结束方式。wait_timeout_ms(默认
30000)是等待运动开始的时间上限。until_still: false 可以退出这一行为,改为拍满
整个窗口——那是"要一段跨度"而不是"要一个事件"时该做的选择。
| maxDimension | 4096 | 截图允许的最大单边像素。4096 是 provider 在"单请求含 15 张及以上图片"时对单边的限制,而一次连拍可以带上百张。超限的截图会被拒绝并报出真实尺寸,而不是缩放到上限——所以要整屏截取更大的显示器,得把这里调高。 |
| keepRecent | 50 | outputDir 里保留的最新截图数量。一张截图是几 MB 的 PNG,而用眼睛的 agent 会截很多张,所以这个目录默认是有上限的。设为 0 表示全部保留。 |
| requireImageCapableModel | true | 当调用方模型未声明图片输入时直接拒绝,而不是返回一张它看不见的图。 |
| deleteAfterCommit | false | 提交到附件存储后删除 PNG。默认关闭,以便返回的路径可再次读取。 |

提交到存储时会去掉那些会让它无法无损保存的记录。在 macOS 上就是去掉
screencapture 给每张截图附带的 ICC 描述文件、EXIF 与 iTXt 记录,而磁盘上的
文件保留它们:这几个记录正是附件存储"保留原字节还是重编码"的判定依据——带
元数据的图片不允许直通——所以去掉它们,截图才能按原字节存下,而不是被编码
成质量 85 的 WebP。在 Windows 上则没有东西可去:GDI+ 这几个记录一个都不写,
截图本来就已满足存储的直通条件。存储无论如何都会转成 sRGB,因此没有任何存活
下来的信息被丢掉;而你能打开的那个文件仍然带着它的色彩描述。

清理只会删除本插件自己写出的文件:outputDir 的直接子项、且文件名严格匹配
本插件生成的形状(shot--.png)的普通文件。它绝不递归、绝不动
其他命名规则的文件,也绝不会删掉刚刚返回给你的那一张。

cordis.patch.yml
- insert:
- id: screen-eye
name: dsh-screen-eye
config:
locale: zh
outputDir: /Users/me/Pictures/agent-shots
keepRecent: 200

实现

screenshot 工具 ──▶ lib/capture.mjs ──▶ 平台引擎 ──▶ PNG
│                 macOS:screencapture
│                 Windows:PowerShell + System.Drawing
└──▶ attachments.saveImage() ──▶ image 内容块 ──▶ 模型

截图不会被缩放到 maxDimension 以内,这个上限也刻意没有设得更低,两者出自同一个实测:
harness 在模型看到图片之前,会先按路由的像素预算投影——默认 640,000 像素,16:9 屏幕约
1066×600——所以 4K、5K、6K、8K 显示器截出来的图,到达模型时是同一张。在这个预算之下,
把上限调小省不下一个 token;而在这里做缩放,会在"模型在图上量到的位置"与"region 需要的
屏幕坐标"之间再多插一级缩放,而那正是放大工作流所依赖的映射。

两个引擎都不自带二进制。macOS 走系统自带的 screencapture(1):它不需要任何
编译产物、由 Apple 签名,并且在当前 macOS 上内部已经使用 ScreenCaptureKit;
自带辅助二进制则意味着要产出多架构构建和一个 ad-hoc 签名——而它的哈希每次
重新构建都会变,哈希一变,用户的屏幕录制授权就静默失效。Windows 走每一台
机器都有的 Windows PowerShell 5.1,通过 Add-Type 在内存里临时编译一层垫片
来驱动 System.Drawing,一次调用结束即消失。

图片通过与内置 read_image 完全相同的附件通道抵达模型,因此其校验、降采样与
会话回放行为与任何其他图片一致。

平台支持

macOS 与 Windows,且在两处强制:bundle patch 带
disabled: !!js process.platform !== 'darwin' && process.platform !== 'win32',
其他平台连模块都不会被 import;apply() 再检查一次,使得绕过 patch 的直接
挂载也无法注册没有引擎的截图工具。

Windows

Windows 不需要授权也不需要引导——任何挂在交互式桌面上的进程都能截屏——所以
那里根本不注册 screen_permission,screenshot 就是全部工具面。与 macOS 真正
不同的有四点,每一点都是被解决的,而不是被写进文档绕过的;
docs/windows.md 里有实测数据。

- 缩放。 Windows PowerShell 默认是 DPI 不感知的,而 DPI 不感知的进程拿到的
是桌面的降采样副本:在 125% 缩放的 3840×2160 面板上,它报告并截取的是
3072×1728。引擎在读取任何东西之前先声明 per-monitor 感知,取到的是真实像素。
- 看不见的会话。 没有挂在交互式窗口站上的进程不会失败——CopyFromScreen
返回黑图,朴素实现会把它当成"成功截到一张黑屏"。引擎先检查窗口站与会话并
点名拒绝,而整帧全黑的照片会带着说明返回,说明这通常意味着什么。
- 连拍。 Windows 上单次截图冷启动约 380ms,且几乎与面积无关,因为每次调用都要付
一次 PowerShell 启动(其中 148ms 是它什么都不做时的启动成本)。现在引擎只启动一次
并常驻:C# 垫片每台机器只编译一次,之后单次截图只需 16-22ms。六帧连拍若拆成
六次冷调用,会把一段 400ms 的动画采样成两秒半,所以 Windows 把整段连拍放进同一个
引擎进程,计划里的间隔才成为可达的。
- 只播一次的动画。 组件过渡只播一次,而模型不可能在用户点击的那一瞬间发出调用
——实测:在一段 300ms 过渡刚开始时发出的连拍,两个平台都是 0 帧可用。
wait_for_change: true 就是答案:"我盯着这块区域,画面一动就开始拍。"在一段已知的
300ms 过渡上实测:帧序列在动画开始后 103ms 起拍,8 帧中有 4-5 帧落在动画
区间内。macOS 有同样的选项,并且自 2026-09-16 起也有同类的常驻助手进程——它的一次
变化检测耗时 22.6ms,而走 screencapture 要 55.8ms;这决定了同一段 300ms 过渡是
只录到后三分之一,还是完整录下来(见 docs/macos-findings.md)。
结束端同样交给画面判定:等待过变化的连拍,会在画面静止时自己收尾,所以 frames
是上限而不再是"猜动画有多长"。同一段过渡上实测:7 帧覆盖完整段动画后自行停止,
10 帧的额度还剩 3 帧没用。返回值会说明是哪种结束方式;想要"固定一段窗口"而不是
"一个事件"的调用方,用 until_still: false 明确要求。
- 双屏。 显示器按主屏优先列出并带各自的原点,所以第二块屏上的 region
——包括位于主屏左侧、坐标为负的那块——都落在正确像素上。已在 3840×2160 主屏
加一块 x = -2560 的 2560×1600 屏上逐像素比对验证。
- select。 Windows 没有系统级选区工具,所以该模式被明确拒绝并给出原因、
建议改用 region;window 截的是当前在最前面的窗口,因为同样没有可点的东西。

环境要求

- macOS 或 Windows,以及 harness 自身的 Node 运行时。两条截图链路都不需要额外
安装任何包,安装时也不编译任何东西。
- macOS 上需要给运行 harness 的进程授予「屏幕录制」权限(见上文)。Windows 不需要。
- 一个声明了图片输入的模型路由。保持 requireImageCapableModel 默认值时,
被声明为纯文本的路由会在事前被拒绝,并在消息里点名该模型,而不是悄悄截一张
它看不见的图。注意这个声明在 harness 的模型元数据里,而不在模型身上:
settings.yaml 可以覆盖内置的模型条目,一条只写了 text 的覆盖会让一个
明明看得见的模型被拒绝。拒绝消息会点名模型和那个设置项,因此修好它只需要
一行,而且不重启即可生效。

开发

npm install
node test/selftest.mjs

该测试套件无需启动 harness:逻辑模块被直接导入,工具定义经由桩上下文执行,
因此在一台从未装过 harness 的机器上同样能跑——CI 走的就是这条路,macOS 与
Windows 上各跑一遍。真正实拍的用例只在本机确实看得见自己的屏幕时运行——
macOS 上要有屏幕录制权限,Windows 上要有可见桌面——所以在授权之前套件依然是绿的。

针对某个平台自身模型的用例,直接问那个平台的模型,而不是问当前宿主机:于是
Windows 引擎生成的 PowerShell 在 macOS CI 上也会被断言,macOS 的权限文案在
Windows 上也会被断言。这是刻意的:只有当另一侧也被检查时,这条接缝才有意义。

插件导入的三个 @deepseek-ai/ 包在 devDependencies 里精确钉版本,这是
刻意的:这几个包把当前版本线发在 next dist-tag 下,而 latest 仍指向一个
老得多的版本,不钉版本就会装到旧的那一个、import 直接失败。

docs/verification.md 记录了实际跑过的内容——
两个平台上的自测、隔离 profile 中的加载器验收、以及端到端 agent 回合——并说明
每一项证明了什么、没证明什么。

docs/macos-debugging.md 是一份自包含的 Mac 端验证手册,
专门写成"可以直接交给一个没有任何上下文的 AI 去执行"的形式:按优先级排列的八个步骤,
每一步都写明怎么跑、期望看到什么、出现偏差意味着什么、要回报哪些数字。
tools/motion-fixture.html 是它要用的动画素材:每 4 秒自动播放一次的 300ms 过渡,
不需要有人在连拍等待时去点击。

docs/macos-findings.md 是 2026-09-16 在 Mac 上跑完这份手册的
实测记录。在信任别处任何 Mac 数字之前都值得先读它:它确认了 region 是像素级精确裁剪、
静止判定的前提成立;同时推翻了"macOS 的检测足够留下 300ms 过渡里的四五帧"这一说法
——实测只留下一到两帧,因为每次检测都要付一次完整的 screencapture 进程启动开销。
正是这个测量结果让 macOS 也补上了常驻引擎,也让本插件不再像以前说的那样"完全不含编译
产物":该助手进程在使用者的机器上从源码编译,继承屏幕录制授权而不是另外申请,
并且任何失败都会回退到 screencapture。

许可证

MIT

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群