Skip to content
dsh.fish
Bundle

dsh-screenshot-win

DSH 屏幕截图插件:聊天输入框的相机按钮 + Agent 可调用的 screenshot 工具(框选区域 / 指定窗口 / 整屏)。截图直接进入对话,并保存到工作区。

Source
lsnnn123
License
MIT
Updated
Updated 7 days ago

Readme

# dsh-screenshot-win

给 DeepSeek Harness 用的屏幕截图插件(Windows)。它同时提供两半:

- **GUI 按钮** —— 输入框左侧工具栏上的相机图标,点开后可选「框选区域 / 整个屏幕 / 指定某个窗口」。截图会作为一张普通的图片附件插入当前会话的输入框(你可以直接发送),同时 PNG 落盘到会话工作区。
- **Agent 工具** —— 名为 `screenshot` 的工具,模型可以自己截图,图片作为工具结果直接进入对话(模型能看到),文件同样落盘。

两半调用的是同一套宿主逻辑(同一个 `/dsh-screenshot-win/capture` 路由 → 同一批 PowerShell 采集脚本),不存在两套实现。

这是一个**标准的 DSH bundle 插件**:`package.json` 里声明 `dsh.bundle.patch` 与 `dsh.client`,可以像市场里的插件一样安装、在「插件」页面里启用/关闭/卸载,也可以直接发布到 npm 并提交到插件市场。

## 界面截图

两张实机截图(都是用本插件自己截的):

![输入框工具行的相机按钮与中文截图状态提示](assets/screenshot-1.png)

![整屏截图的结果(插件把图存到工作区,并在输入框提示状态)](assets/screenshot-2.png)

插件市场(如 dsh-market 的详情页)会读取仓库根目录的 `screenshots.json`(列出 1–8 张图,路径相对该文件)来展示这些截图。

## 目录结构

| 路径 | 作用 |
| --- | --- |
| `lib/index.js` | 宿主(Node)半:配置、`screenshot` 工具注册、HTTP 路由、与 `ctx.attachments` 对接 |
| `client/client.js` | 浏览器半:`conversation.input.left` 相机按钮 + `tool.call.toolview` 图片行 |
| `cordis.patch.yml` | bundle 补丁层:把自己这一行插进 profile |
| `locale/zh.json`、`locale/en.json` | 市场元数据(`meta.title` / `meta.description`) |
| `scripts/WinApi.ps1` | P/Invoke 与 GDI 封装(PrintWindow、DPI、显示器、窗口枚举、缩放、存图) |
| `scripts/capture-region.ps1` | 框选覆盖层(冻结画面、框选或点选窗口、Esc 取消、超时自动取消) |
| `scripts/capture-screen.ps1` | 整屏 / 指定显示器 / 指定矩形 |
| `scripts/capture-window.ps1` | 指定窗口(PrintWindow,可回退到屏幕拷贝) |
| `scripts/list-windows.ps1` | 可截取窗口列表(JSON) |
| `test/selftest.mjs` | 工具参数/输出 schema 与渲染内容自检 |
| `test/e2e.mjs` | 三种采集模式的端到端自检 |
| `test/client-sim.mjs` | 浏览器半的宿主侧模拟(模块注册、按钮流程、插图、语言、失败分支) |
| `tools/asar.mjs` | 只读读取 `app.asar`(安装环境排查用) |
| `tools/check-patch.mjs` | 用真实的 `dsh-app-boot` / `dsh-plugin-manager` 预演 bundle 解析与 patch 合成 |
| `screenshots.json`、`assets/` | 市场详情页展示的界面截图(1–8 张,路径相对 `screenshots.json`) |
| `LICENSE` | MIT |

## 配置

在 profile 或用户级 `cordis.patch.yml` 的插件行里配置:

```yaml
- insert:
    - id: dsh-screenshot-win
      name: dsh-screenshot-win
      config:
        saveDir: screenshots        # 相对会话工作区
        maxWidth: 1600              # 超过则等比缩小(0 表示不缩放)
        format: png                 # png | jpeg
        quality: 88                 # jpeg 质量
        timeoutMs: 300000           # 单次采集脚本超时
        regionTimeoutSec: 180       # 框选覆盖层的自动取消时间
        tool: true                  # 是否注册 screenshot 工具
        routes: true                # 是否挂载浏览器半用的 HTTP 路由
```

## 安装

### 方式 A:插件管理器 / 插件市场(标准做法)

```
plugin_manager action=install_bundle target="E:\GAME\Kingdom Rush6\dsh-screenshot-plugin"
```

装好后在 DSH 的「插件」页面里会出现这一项:卡片上的开关就是**启用/关闭**,进入详情页右侧的垃圾桶是**卸载**(卸载会把它从 profile 的 `dependencies` 里摘掉)。这两件事都由插件管理器改 profile 清单完成,不需要手工编辑。

### 方式 B:手工把它接成 profile bundle(本机当前采用)

本机 `desktop` profile 的 pnpm 安装被 profile 既有的供应链策略拦住
(`ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`,涉及 `@openviking/dsh-memory-plugin@0.5.19`,与本插件无关),
且 CLI 拒绝操作 Electron 托管的 profile(`profile "desktop" is managed exclusively by the Electron application`)。
所以本机用与插件管理器**同样的数据结构**手工接上,三步:

1. 目录联接:`C:\Users\lsnnn\.dsh\profiles\desktop\node_modules\dsh-screenshot-win` → 本目录;
2. `C:\Users\lsnnn\.dsh\profiles\desktop\package.json`:
   在 `dependencies` 里加 `"dsh-screenshot-win": "link:E:/GAME/Kingdom Rush6/dsh-screenshot-plugin"`,
   在 `dsh.profile.bundles` 里加 `"dsh-screenshot-win"`(**bundle 出现在这个数组里才等于「已启用」**);
3. 用户级补丁 `C:\Users\lsnnn\.dsh\cordis.patch.yml` 保持为空(`[]`)——插件这一行由本 bundle 自带的
   `cordis.patch.yml` 负责插入,两处都插会挂载两次。

自检:

```powershell
node tools\check-patch.mjs
```

它会用真实的 `dsh-plugin-manager`/`dsh-app-boot` 断言:清单里有依赖、`bundles` 里已选中、
包能被解析出 `dsh.bundle.patch`、补丁层里**只有一处**插入该行、合成后的 170 行里该行只出现一次。

关闭 / 卸载在方式 B 下等价于:从 `dsh.profile.bundles` 移除(关闭)或再从 `dependencies` 移除并删掉联接(卸载)。
等 profile 能正常安装后,删掉这三处、改用方式 A 即可。

宿主半的 JavaScript 只在启动时导入一次,**改动 `lib/index.js` 需要重启应用**;浏览器半每次加载页面都会从磁盘重新读取,**改动 `client/client.js` 只要刷新页面**。

## 使用

工具参数(`screenshot`):

| 参数 | 说明 |
| --- | --- |
| `mode` | `region`(默认,屏幕上框选)/ `screen`(整屏)/ `window`(指定窗口) |
| `out` | 输出文件;默认 `<会话工作区>/screenshots/shot-<时间戳>.png` |
| `hwnd` / `window` / `process` / `active` | 指定窗口(句柄 / 标题正则 / 进程名 / 当前前台窗口) |
| `focus` | 截图前先前置该窗口 |
| `screen_fallback` | PrintWindow 失败时回退到屏幕拷贝 |
| `monitor` / `x` / `y` / `width` / `height` | 整屏模式的显示器与矩形范围 |
| `scale` / `max_width` / `format` / `quality` | 缩放与编码 |
| `list_windows` | 只列出可截取窗口(结果通过错误信息返回,方便模型阅读) |
| `timeout_sec` / `no_window_pick` | 框选模式超时 / 禁用「点选窗口」 |

窗口采集用 `PrintWindow` + `PW_RENDERFULLCONTENT`,因此被遮挡的窗口(含 Electron 窗口)也能正确截取;对少数 GPU 直绘窗口失败时会自动回退到屏幕拷贝(`screen_fallback`)。

界面语言跟着 DSH 走:`ctx.locale.active` 是 `zh*` 显示中文,其它显示英文;拿不到 locale 服务时按浏览器语言判断。

## 仓库与发布

包名(也是仓库名)是 **`dsh-screenshot-win`**。原本想用的 `dsh-screenshot` 在 npm 上已被别人占用(`0.0.1`);
`dsh-screenshot-win` 经镜像查询确认未被占用(`https://registry.npmmirror.com/dsh-screenshot-win` → 404)。

仓库已发布:**<https://github.com/lsnnn123/dsh-screenshot-win>**(public,默认分支 `main`,已打 `dsh-plugin` topic)。
别人这样安装:

```powershell
plugin_manager action=install_bundle target="github:lsnnn123/dsh-screenshot-win"
```

### 本机怎么往上推

这台机器上 `git push` 到 GitHub **不可用**,两个原因叠在一起:

- `~/.gitconfig` 里有 `url.https://gh.ddlc.top/https://github.com/.insteadOf = https://github.com/`,把 GitHub 重写到镜像,镜像返回 **429**;
- 绕过镜像直连时,schannel 报 `CRYPT_E_NO_REVOCATION_CHECK`,换 OpenSSL 报 `unable to get local issuer certificate`(证书环境有中间人代理)。

而 PowerShell 的 `Invoke-RestMethod -SkipCertificateCheck` 访问 `api.github.com` 是通的,所以改用 Git Data API 发布:

```powershell
pwsh -File tools\publish-github.ps1              # 把本地 HEAD 同步到 GitHub(只传变化过的文件)
pwsh -File tools\publish-github.ps1 -WhatIf      # 只看差异
```

凭据按 `-Token` → `$env:GITHUB_TOKEN` → Windows 凭据管理器里 `GitHub - https://api.github.com/lsnnn123` 的顺序取,token 不会打印。
(将来若要恢复 git 正常工作:`git config --global --unset url.https://gh.ddlc.top/https://github.com/.insteadOf`,并给 git 配好能通过代理的 CA。)

### npm 发布(可选)

不被市场收录所必需;要发就这样:

```powershell
npm publish --access public --registry=https://registry.npmjs.org/
```

发布前确认:

- `package.json` 的 `private` 必须不存在(本包已移除);
- `files` 覆盖 `lib`、`client`、`scripts`、`locale`、`cordis.patch.yml`、`README.md`、`LICENSE`——
  少了 `scripts` 装出来的包会在采集时报找不到脚本;
- `main` / `exports["."]` 指向的文件必须真的存在(插件管理器安装时要求入口产物就位);
- `author` / `repository` / `homepage` 与 LICENSE 的版权署名目前**留空**(若要发 npm,`repository` 需指回本仓库)。

「插件」页面卡片直接显示 `package.json` 的 `description`(本包为中文);市场条目文案读
`locale/zh.json`、`locale/en.json` 的 `meta.title` / `meta.description`。

## 上传到插件市场

⚠️ **`dsh-market/dsh-market` 是市场应用本身,不是插件目录**(它自己的 README 明确写了"请不要往本仓库提插件条目")。
市场的插件列表来自精选目录 **[awesome-dsh-plugin/awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)**,
站点是 <https://awesome-dsh-plugin.com>;市场每次打开都实时抓 `awesome-dsh-plugin.com/plugins.json`。

**上架 = 给精选目录提一个 PR,只加一个文件**:`data/plugins/lsnnn123__dsh-screenshot-win.yml`
(本仓库 `market/` 下已经放好了这个文件,照抄即可)。`description.en` 必填,含 `: ` 必须加引号;分类用 `tools`。

收录条件(CI 自动查):

| 条件 | 本仓库 |
| --- | --- |
| `package.json` 声明 `dsh.bundle`(**最常见的被拒原因是只写 `dsh.client`**) | ✅ `./cordis.patch.yml` |
| 仓库有真实可用代码 | ✅ |
| 仓库创建满 **1 天** | 2026-10-09 创建 → 次日之后才能提 |
| 打了 `dsh-plugin` topic | ✅ |
| npm 包 | 可选,没有也能收录(`install` 走 `github:owner/repo`) |
| `screenshots.json` | 可选,有的话市场详情页会展示截图 |

社区里已有别的截图插件(`paicat1/dsh-screenshot`、`wangzhanchao883/dsh-screenshot-capture`、
`deepseekbluefish/dsh-screenshot-plugin`、`ntesicn/dsh-screenshot-xn`),名字与功能侧重都不同,不冲突。

## 验证

```powershell
node test\selftest.mjs     # 工具 schema
node test\e2e.mjs          # 三种采集模式(会真的截一张图)
node test\client-sim.mjs   # 浏览器半的宿主侧模拟
node tools\check-patch.mjs # bundle 接线与补丁合成
```

运行中还可以直接查询宿主半:

```powershell
Invoke-WebRequest http://127.0.0.1:19387/dsh-screenshot-win/status
```

返回插件版本、默认保存目录,以及浏览器半的状态(`client.bundle` 是否已登记、`client.advertised` 是否已进入页面启动名单)。

## 已知限制

- 仅 Windows(依赖 GDI / `PrintWindow` 与 PowerShell 7)。
- 框选模式需要真人拖拽;纯自动化环境可传 `-TimeoutSec` 观察取消路径。
- 浏览器半的槽位注册只能刷新页面后在真实 GUI 中确认(本插件无法控制浏览器)。
- 浏览器半若在页面加载时拿不到 locale 服务,会先按浏览器语言显示,并在下一次渲染时自动换成 DSH 的语言。

Install

dsh plugin --profile web add github:lsnnn123/dsh-screenshot-win

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source