让智能体看视频听音频并观察实时桌面
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/24 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ck99998888/dsh-video-reader该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · desktop
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 85 处,展示前 69 条)
- 读取本地文件index.js:281
const data = new Uint8Array(await readFile(entry.path)) - 写入本地文件index.js:329
await mkdir(framesDir, { recursive: true }) - 写入本地文件index.js:478
await writeFile(srtPath, srtText, 'utf8') - 写入本地文件index.js:479
await writeFile(path.join(dir, 'transcript.txt'), speakerLines.join('\n'), 'utf8') - 写入本地文件index.js:549
await mkdir(framesDir, { recursive: true }) - 写入本地文件index.js:609
await mkdir(dir, { recursive: true }) - 写入本地文件index.js:624
await writeFile(manifestPath, `${JSON.stringify({ createdAt: Date.now(), fps, seconds, region: plan.label, width, frames: framePaths }, null, 2)}\n`, - 读取本地文件index.js:669
const raw = await readFile(manifestPath, 'utf8').catch(() => undefined) - 读取本地文件index.js:1378
const data = await readFile(target) - 发起网络请求client.js:314
fetch(`${API}/settings`, { headers: { accept: 'application/json' } }) - 发起网络请求client.js:327
fetch(`${API}/deps/status`, { cache: 'no-store' }) - 发起网络请求client.js:340
const response = await fetch(`${API}/deps/install`, { - 发起网络请求client.js:376
const response = await fetch(`${API}/settings`, { - 发起网络请求client.js:397
const response = await fetch(`${API}/preview?t=${Date.now()}`, { cache: 'no-store' }) - 发起网络请求client.js:415
fetch(`${API}/watch/status`).then((response) => response.json()) - 读取本地文件lib/ffmpeg.js:37
await access(candidate, constants.X_OK) - 执行外部命令来自配置/变量lib/ffmpeg.js:78
child = spawn(command, args, { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 读取本地文件lib/video.js:64
const info = await stat(absolute).catch(() => undefined) - 读取本地文件lib/video.js:232
const info = await stat(outPath).catch(() => undefined) - 写入本地文件lib/video.js:280
await copyFile(source, pattern.replace('%03d', String(index + 1).padStart(3, '0'))) - 读取本地文件lib/video.js:292
const info = await stat(outPath).catch(() => undefined) - 读取本地文件lib/video.js:360
const decoded = decodeText(await readFile(outPath)) - 读取本地文件lib/video.js:378
entries = await readdir(dir, { withFileTypes: true }) - 读取本地文件lib/video.js:395
const decoded = decodeText(await readFile(full)) - 写入本地文件lib/video.js:432
await mkdir(dir, { recursive: true }) - 删除本地文件递归lib/video.js:458
await rm(stale.full, { recursive: true, force: true }).catch(() => {}) - 读取本地文件lib/asr.js:34
await access(candidate, constants.R_OK) - 读取本地文件lib/asr.js:69
stat(source).catch(() => undefined), - 读取本地文件lib/asr.js:70
stat(destination).catch(() => undefined), - 写入本地文件lib/asr.js:73
await mkdir(path.dirname(destination), { recursive: true }) - 写入本地文件lib/asr.js:74
await copyFile(source, destination) - 读取本地文件lib/asr.js:102
const entries = (await readdir(dir)).filter((name) => /^ggml-.*\.bin$/i.test(name)) - 读取本地文件lib/asr.js:164
.update(`${wavPath}|${(await stat(wavPath).catch(() => ({ size: 0 }))).size}`) - 删除本地文件lib/asr.js:184
await rm(srtPath, { force: true }).catch(() => {}) - 读取本地文件lib/asr.js:193
const srt = await readFile(srtPath, 'utf8').catch(() => undefined) - 写入本地文件lib/asr.js:200
await writeFile(saveSrt, srt, 'utf8').catch(() => undefined) - 删除本地文件lib/asr.js:210
if (stagedAudio) await rm(stagedAudio, { force: true }).catch(() => {}) - 读取本地文件lib/diarize.js:35
await access(candidate, constants.R_OK) - 读取本地文件lib/diarize.js:54
const entries = (await readdir(dir)).filter((name) => /\.onnx$/i.test(name)) - 读取本地文件lib/settings.js:87
const raw = await readFile(settingsPath(), 'utf8') - 写入本地文件lib/settings.js:102
await mkdir(path.dirname(settingsPath()), { recursive: true }) - 写入本地文件lib/settings.js:103
await writeFile(settingsPath(), `${JSON.stringify(next, null, 2)}\n`, 'utf8') - 读取本地文件lib/capture.js:108
const info = await stat(outPath).catch(() => undefined) - 写入本地文件lib/capture.js:125
await mkdir(outDir, { recursive: true }) - 读取本地文件lib/capture.js:131
const entries = await readdir(outDir).catch(() => []) - 执行外部命令固定命令lib/windows.js:69
const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { - 执行外部命令来自配置/变量lib/record.js:33
const child = spawn(ffmpeg, args, { windowsHide: true, stdio: ['pipe', 'ignore', 'pipe'] }) - 读取本地文件lib/record.js:85
const info = await stat(session.outPath).catch(() => undefined) - 读取本地文件lib/record.js:106
const info = await stat(session.outPath).catch(() => undefined) - 读取本地文件lib/deps.js:204
const input = createReadStream(file) - 读取本地文件lib/deps.js:212
try { return (await stat(file)).size } catch { return -1 } - 读取本地文件lib/deps.js:216
try { await access(file); return true } catch { return false } - 读取本地文件lib/deps.js:222
const entries = await readdir(dir, { recursive: true, withFileTypes: true }) - 执行外部命令固定命令lib/deps.js:234
const child = spawn('tar', ['-xf', archive, '-C', destination], { windowsHide: true }) - 执行外部命令固定命令lib/deps.js:249
const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { windowsHide: true }) - 写入本地文件lib/deps.js:261
await mkdir(destination, { recursive: true }) - 发起网络请求lib/deps.js:276
const response = await fetch(url, { - 写入本地文件lib/deps.js:283
await mkdir(path.dirname(destination), { recursive: true }) - 写入本地文件lib/deps.js:284
const stream = createWriteStream(destination) - 写入本地文件lib/deps.js:329
await mkdir(path.dirname(finalPath), { recursive: true }) - 写入本地文件lib/deps.js:331
await copyFile(source, temp) - 删除本地文件lib/deps.js:339
await rm(finalPath, { force: true }) - 写入本地文件lib/deps.js:340
await rename(temp, finalPath) - 删除本地文件lib/deps.js:342
await rm(temp, { force: true }).catch(() => {}) - 删除本地文件lib/deps.js:384
return async () => { await unlink(lockPath).catch(() => {}) } - 读取本地文件lib/deps.js:387
const info = await readFile(lockPath, 'utf8').then((text) => JSON.parse(text)).catch(() => ({})) - 删除本地文件lib/deps.js:391
await unlink(lockPath).catch(() => {}) - 删除本地文件lib/deps.js:448
await rm(target, { force: true }).catch(() => {}) - 删除本地文件递归lib/deps.js:481
await rm(staging, { recursive: true, force: true }).catch(() => {})
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 插件:观看、收听并查看视频/音频文件和实时桌面,全程本地运行(whisper.cpp + sherpa-onnx + ffmpeg)。
README
由 DeepSeek 最新模型翻译生成Video Reader
English · 中文
一个 DeepSeek Harness 插件,让智能体看、听、观察这些东西:磁盘上的视频/音频文件,以及实时桌面。全部在本地运行——不上传,不调用云端 API。
- video_read —— 元数据、帧、字幕、多语种带说话人标注的转写
- video_frames —— 在精确时刻截取全宽静帧
- screen_capture —— 观察屏幕;每一帧捕获结果都会返回,分页给出
- screen_frames_read —— 继续读取一次捕获,直到没有未读帧
- screen_watch —— 持续采样,只在画面变化时唤醒智能体
- screen_record —— 长时间捕获录成视频(30 fps 录 1 小时约 350 MB)
- video_reader_setup —— 检查或安装外部工具链(首次使用时自动安装)
安装
git clone https://github.com/ck99998888/dsh-video-reader $env:USERPROFILE\dsh-video-reader
pwsh -File $env:USERPROFILE\dsh-video-reader\scripts\setup.ps1 # optional; -Mirror if GitHub is slow
展开完整文档(README 剩余 1 节)
Video Reader
English · 中文
一个 DeepSeek Harness 插件,让智能体看、听、观察这些东西:磁盘上的视频/音频文件,以及实时桌面。全部在本地运行——不上传,不调用云端 API。
- video_read —— 元数据、帧、字幕、多语种带说话人标注的转写
- video_frames —— 在精确时刻截取全宽静帧
- screen_capture —— 观察屏幕;每一帧捕获结果都会返回,分页给出
- screen_frames_read —— 继续读取一次捕获,直到没有未读帧
- screen_watch —— 持续采样,只在画面变化时唤醒智能体
- screen_record —— 长时间捕获录成视频(30 fps 录 1 小时约 350 MB)
- video_reader_setup —— 检查或安装外部工具链(首次使用时自动安装)
安装
git clone https://github.com/ck99998888/dsh-video-reader $env:USERPROFILE\dsh-video-reader
pwsh -File $env:USERPROFILE\dsh-video-reader\scripts\setup.ps1 # optional; -Mirror if GitHub is slow
或者从仓库把它作为 DSH bundle 安装:
dsh plugin --profile web add github:ck99998888/dsh-video-reader
然后在 DSH 里把这个文件夹作为 bundle 安装(插件管理器 → 从文件夹安装,或用该路径执行 plugin_manager install_bundle),并重启 DSH 一次:bundle 的 Client 部分在页面启动时组装。
依赖会自动安装。 首次调用 video_read/video_frames/screen_capture 时,会把缺失的东西下载到 %LOCALAPPDATA%\dsh-video-reader\bin 和 …\models(先下 ffmpeg),然后重试该调用。每个下载都锁定版本,并按 lib/deps.js 里的清单做 sha256 校验;校验不通过的文件会被删除,绝不会安装。scripts/setup.ps1 现在只是提前预置全部依赖的一种方式。
用插件 Config(autoSetup、autoSetupModels、 dependencyMirror)或从 设置 → Video Reader → Dependencies 控制这一行为。
| 组件 | 大小 | 为什么不提交进仓库 |
| ffmpeg + ffprobe(essentials 9.0.2) | ~200 MB | GitHub 单文件上限 100 MB |
| whisper.cpp CLI + DLLs(b5454) | ~10 MB | 随其余依赖一起下载 |
| sherpa-onnx 说话人分离运行时(v2.0.0) | ~17 MB | 随其余依赖一起下载 |
| ggml-medium-q5_0.bin 语音模型 | 514 MB | 远超上限 |
| pyannote 分割 + 3D-Speaker 嵌入 | ~33 MB | 随其余依赖一起下载 |
值得了解的设计取舍
- 长时间捕获是录像,不是一堆帧:30 fps 录一小时就是 108,000 帧,只有当成视频、让读取方事后采样才说得通。
- screen_capture 绝不做降采样。它保留每一帧,写出 manifest.json,附带一个批次,并明确告诉你还有多少帧未读。
- 窗口捕获按窗口边界裁剪合成后的桌面:GDI 读不了硬件合成窗口的 D3D 表面,所以 gdigrab -i title= 返回的是黑帧。
- screen_watch 对比 64×40 灰度采样(2,560 字节),只有变化超过阈值时,才消耗一张全尺寸帧并唤醒。
License MIT。
让智能体真正「看」和「听」视频:一次调用就能拿到容器与流信息、可直观看清的关键帧拼版、逐帧大图路径、内嵌或同名字幕,以及带说话人标签的多语种转写(S1/S2/… 谁在第几秒说了什么)。
全部在本机处理:视频不上传、不联网、不依赖云服务。插件 = Harness Host 插件 + ffmpeg / whisper.cpp / sherpa-onnx 与模型(由插件自动下载,不入库)。
依赖:首次使用自动补齐(1.8 起)
tools/、models/ 不入库,插件会自己补齐:
- 首次需要时(video_read / video_frames / screen_capture)自动把缺的组件下到 %LOCALAPPDATA%\dsh-video-reader\{bin,models},装好再继续本次调用;
- 每个文件都按 lib/deps.js 里钉死的版本与 sha256 校验,校验失败的文件直接丢弃、不会安装;
- GitHub 域名不可达时默认走 gh-proxy.com 镜像(dependencyMirror);
- 设置页「依赖组件」可看状态、手动安装、关掉自动安装;
- 也可以用工具 video_reader_setup 显式检查/安装,或提前跑 scripts/setup.ps1。
工具
video_reader_setup
| 参数 | 说明 |
| action | status(只读报告)或 install(下载缺失组件) |
| components | ffmpeg whisper whisper-model sherpa diarization-models,或简写 models / binaries / all(默认) |
| mirror | GitHub 下载是否优先走 gh-proxy 镜像;默认取设置页 |
返回每一项的就绪状态、安装位置、版本,以及安装耗时/体积。视频类工具在缺依赖时会先自动安装再继续,所以这个工具主要用于提前体检或重试失败的下载。
video_read
| 参数 | 说明 |
| path | 视频/音频路径,绝对或相对会话工作目录 |
| frames | 抽帧数量,默认 12,0–40,0 只要元数据/转写 |
| mode | even(默认)或 scenes(按镜头切换) |
| start / end | 只看某段,支持 90、1:30、00:01:30 |
| transcript | 是否转写,默认 true |
| language | 强制语种,如 zh en de ja ko fr ru;默认 auto 自动识别 |
| speakers | 已知说话人数(0 = 自动推断);给出时聚类更稳 |
| diarize | 是否标说话人,默认 true |
| subtitles | 是否提取内嵌/同名字幕,默认 true |
| width | 单帧宽度,默认 960,0 = 原尺寸 |
输出现场:
Speech transcript (auto-detected, 6 turns, 2 speakers: S1/S2)
talk time: S1 0:10 · S2 0:07 [S1] 0:00–0:05 大家好,今天我们讨论视频阅读器的测试结果。
[S2] 0:06–0:09 我觉得识别准确率比之前好多了。
...
full timestamped SRT: …\transcript.srt
拼版图片会作为图片内容直接进模型上下文(每格左上角烧入时间码),同时给出每帧全尺寸图路径。
video_frames
按时间点抓全宽大图,一次最多 8 张,用于复查细节。
screen_capture
看一眼当前桌面(实时屏幕),不是磁盘上的文件。
| 参数 | 说明 |
| seconds | 抓取窗口秒数,0 = 单张截图,最大 30 |
| fps | 采样帧率 1–30(默认取自设置页)。实测 2560×1600 下 gdigrab 能稳定跑到 ~28 fps、上限 ~56 fps;单次抓取总帧数封顶 240 |
| region | full / window:<标题片段> / x,y,w,h;不传则用设置页的默认值 |
| frames | 拼版最多保留几帧(默认 12) |
| width | 每帧宽度 |
| mouse | 是否把鼠标指针画进去 |
返回同样是一张可直接看的拼版 + 每帧路径。抓取是采样:模型看到的是这 N 秒内实际存在的若干帧,不是持续视频流——所谓「实时看视频」,就是「按 2–5 fps 采样一段时间」。
屏幕捕获设置页
Web UI 里 设置 → 视频阅读器 提供图形化配置:允许/禁止抓屏、整屏 / 指定窗口 / 自定义区域、帧率、默认时长、每帧宽度、是否包含鼠标指针,并带「抓一帧预览」按钮直接确认范围。
设置在保存后立即生效(工具每次调用都会重新读取),也可以直接改 %LOCALAPPDATA%\dsh-video-reader\screen-settings.json。
抓取方式的实现要点:
- 整屏:gdigrab -i desktop,对 GPU 合成的浏览器、播放器同样有效
- 窗口:先枚举窗口拿到坐标,再按该矩形裁剪桌面。不能直接用 gdigrab -i title=,因为 GDI 读不到 D3D 合成窗口,会得到黑帧
- 区域:-offset_x/-offset_y/-video_size
- 窗口标题是子串匹配,填稳定片段(如 Microsoft Edge),因为播放页的完整标题会随页面变化
实测帧率(2560×1600 桌面,JPEG 落盘)
| 目标 fps | 实际产出 | 说明 |
| 10 | 9.1 fps | |
| 20 | 18.8 fps | |
| 30 | 28.5 fps | 插件上限,够覆盖 24–30fps 的视频 |
| 40 | 37.9 fps | |
| 60 | 56.6 fps | 受 JPEG 编码/写盘限制,不是采集 API 的瓶颈 |
单张全屏截图(含进程启动)约 173 ms。ddagrab(Desktop Duplication)同分辨率下 60 目标也只有 55.5 fps,与 gdigrab 基本持平,因此插件统一用 gdigrab,避免给窗口/区域模式再分一套实现。
看视频场景建议 15–24 fps;看游戏或快速动画再上 30。总帧数封顶 240(seconds × fps ≤ 240),超了会自动降 fps 并在结果里注明。
语种与识别
单个多语种模型(whisper medium-q5_0,99 种语言),不需要按语种换模型。
本机实测:
| 语种 | 素材 | 结果 |
| 中文 | Windows 原生 TTS 双人对话 | 逐句全对,两名说话人分对 |
| 英文 | 原生 TTS + espeak | 全对 |
| 德文 | espeak | “Das Wetter ist heute schön. Wir gehen im Park spazieren.” 全对 |
| 法文 | espeak | “Le temps est bon aujourd'hui, nous allons nous promener dans le parc.” 仅一处冠词差异 |
| 俄/日/韩 | — | 模型原生支持(99 语种),本机没有可用素材,未实测 |
说话人分离
sherpa-onnx 离线分离:pyannote 分段模型(5.7MB)+ 3D-Speaker 声纹嵌入(27MB)+ 聚类,不联网、CPU 即可。57 秒 4 人素材实测分离正确(S1–S4),速度约为实时 20 倍。
- 每句转写按时间重叠度归到说话人,连续同人语句合并成一 "turn"
- 输出附带各说话人发言总时长
- 已知人数时传 speakers 会显著更稳(尤其 3 人以上)
配置
- insert:
- id: video-reader
name: '@local/dsh-video-reader'
config:
frames: 12
columns: 4
frameWidth: 960
tileWidth: 320
maxTextChars: 12000
maxTranscriptSeconds: 900
languages: auto
cacheDir: .dsh-video-cache
diarize: true
speakers: 0 # 0 = 自动 diarizationThreshold: 0.5 依赖自动安装(1.8 起) autoSetup: true # 首次使用时自动下载缺失的 ffmpeg / 运行时 autoSetupModels: true # 连语音与分离模型(~547 MB)也自动下载 dependencyMirror: true # GitHub 域名不通时走 gh-proxy.com 镜像路径覆盖(留空则用随包二进制) ffmpegPath: '' ffprobePath: '' whisperPath: '' whisperModel: '' diarizationPath: '' segmentationModel: '' embeddingModel: ''
体积
| 组成 | 大小 |
| ffmpeg + ffprobe(essentials 构建) | 201 MB |
| whisper.cpp + sherpa-onnx 运行时 | 27 MB |
| 识别模型 medium-q5_0 | 514 MB |
| 分离模型(分段 + 声纹) | 33 MB |
| 代码/资源 | < 0.1 MB |
| 合计 | 约 778 MB |
想更准可以换 ggml-large-v3-q5_0.bin(1031 MB,俄/日/韩明显更好)放进 models/ 即可,代码会自动优先选它。想更小可以换 ggml-small.bin(465 MB),代价是德/俄/日/韩准确率下降。
ggml-large-v3-turbo-q5_0.bin 经实测在本机两个 whisper.cpp 构建上都无法加载(unknown tensor ''),文件 sha256 与官方一致,属模型与构建的格式兼容问题,故默认不采用。
screen_record
长时采集走录制而不是堆截图:1 小时 × 30fps = 108,000 帧,只有视频形态才合理。
| 参数 | 说明 |
| action | start / stop / status |
| id | stop 时指定要停的录制 id |
| seconds | 最长时长,默认 3600(1 小时),上限 7200 |
| fps | 1–30,默认 30 |
| region | 同 screen_capture,默认用设置页的选择 |
| maxMB | 文件大小上限,默认 2000MB,到顶自动停 |
| width | 编码前缩放宽度,默认 1280 |
录制在后台跑(不阻塞对话),返回 id 和 .mkv 路径;stop 后直接用 video_read 采样任意片段。
实测码率(2560×1600 桌面,30fps):
| 编码参数 | 码率 | 1 小时 |
| 原生尺寸 · crf26 · ultrafast | 7.7 GB/h | 太大 |
| 原生尺寸 · crf30 · veryfast | 1.05 GB/h | 偏大 |
| 1280 宽 · crf30 · veryfast(默认) | 354 MB/h | 1 小时约 0.35 GB |
选择要抓的软件界面
设置页的「指定窗口」是一个自动列出当前所有可见窗口的下拉框(进程名 + 标题 + 分辨率),带「刷新窗口列表」按钮,不用手打标题。
选中的窗口会同时保存 windowTitle 与 windowProcess;抓取时按 精确标题 → 标题子串 → 进程名 的顺序解析,所以浏览器换页面、标题变了也能命中同一个进程。窗口不在了会直接报错,并列出当前可选的窗口,而不是给一张黑图。
抓窗口的实现是「先取窗口坐标,再裁剪合成后的桌面」——因为 GDI 读不到 GPU 合成窗口的 D3D 表面,gdigrab -i title= 对浏览器/播放器只会得到黑帧。
缓存
帧、拼版、字幕、wav、SRT 写在 <会话工作目录>/.dsh-video-cache/<文件名>-<内容哈希>/,保留最近 20 个媒体目录,可整删。
边界
- 位图字幕(PGS/VobSub)无法转文字,会标注跳过
- 转写同步执行:turbo/medium 在 CPU 上约实时 1–3 倍;超过 maxTranscriptSeconds 需自己传 start/end 分段
- 说话人分离是聚类结果,人数未知且音色接近时可能多分/少分;给 speakers 可缓解
- whisper 系模型对纯合成音(如 espeak)不代表真实口语表现
- 抓屏会包含屏幕上一切可见内容(聊天、验证码、密码框),默认整屏;需要隔离就用窗口/区域模式
- DRM、受保护视频、部分全屏独占的游戏在 GDI 下可能黑屏;独显直连/多显示器时坐标要按实际桌面
- 抓屏是采样而非录制:seconds × fps 决定能看清多少变化,动作太快(如 60fps 游戏)需要调高 fps 或缩短窗口
两条读取路径(1.6 起)
1. 全帧读取:不抽样
screen_capture 保留每一帧、写入 manifest.json,并把帧按批(默认 20 张/次)作为图片返回:
- 返回里带 totalFrames / readFrames / remainingFrames
- remainingFrames > 0 时结果明确写出下一步:screen_frames_read {manifest, offset, count}
- 拼版只在 sheet: true 时生成,且仅作总览,不再替代逐帧阅读
算一下就知道为何要分批:30 秒 × 30fps = 900 帧,每帧约 800 token,全读约 72 万 token,单轮装不下。插件保证“一帧不少地落盘、可按批读完”,同时保留 240 帧采集预算(fps 会被压到 240/seconds)。
2. 实时通道:screen_watch
| 参数 | 说明 |
| action | start / stop / status |
| intervalMs | 采样间隔,默认 2000ms |
| threshold | 判定“变了”的归一化差异,默认 0.02 |
| maxMinutes | 自动停止,默认 30 分钟 |
每次抓一张 64×40 灰度小图(2560 字节)算平均绝对差;超过阈值才抓全尺寸帧存盘,并主动唤醒模型(agent.inject + agent.followup)。唤醒失败时事件留在队列,用 action:"status" 抽取,不丢观察。
实测:3 秒内壁纸变化一次,检测 1 次、唤醒 1 次、误报 0。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
ck99998888/dsh-video-reader
让智能体看视频听音频并观察实时桌面
相关插件 · 桌面端
Codex 任务面板chuspeeism/dashi-taskboard
现代化可灵活嵌入的任务面板,支持 Codex、DeepSeek Harness
★ 3,409已汉化DSH
71.7
多智能体自进化开发平台Prism-Shadow/penguin-harness
统一、稳定的 Agent 自进化框架全自动优化:任意模型 · 任意算法 · 任意 Benchmark
★ 3,125已汉化DSH
71.1
桌面宠物PC2005-cloud/dsh-pet
DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI…
★ 5,465已汉化DSH
71
多端 Agent 工作台GCWing/OpenBitFun
OpenBitFun 是一个高度可定制的开源 Agent 工作台,让任务按你的方式持续推进。
★ 3,457已汉化DSH
70.8
DSH Desktopanywhere-labs/dsh-desktop
为 DeepSeek Harness DSH 插件生态打造的现代化桌面端解决方案。万物皆「插件」,桌面本身也是「插件」。
★ 34,710已汉化DSH
70.7
桌面宠物伴侣OpenPetsHQ/openpets
桌面伴侣平台,带宠物、插件,还能选配本地智能体集成。
★ 1,378已汉化DSH
70.2
这是你的仓库?认领条目