gezi-wen/sage-guikit0

sage-guikit

Sage GUI Toolkit — Windows desktop control tools (screen layout / annotated screenshot / click / type / key / scroll / wait / window / UIA structured query) via PowerShell + SendInput + UIAutomation, DPI-aware and multi-monitor safe

包名
sage-guikit
版本
0.3.0
许可证
MIT
最近更新
2026年9月12日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:gezi-wen/sage-guikit

sage-guikit · Sage GUI Toolkit

Windows 桌面控制工具集,给 DeepSeek Harness(DSH)的 agent 装上眼睛和手:看屏幕、单窗口截图、点鼠标、拖动、打字、按快捷键、滚动、等生效、管窗口,并能对标准控件做 Windows UI Automation 结构化定位。

Windows desktop-control toolset for DeepSeek Harness (DSH): monitor layout, whole-screen and single-window capture, click / drag / type / key / scroll, pixel & window polling, window management, and UI Automation structured queries. Eleven model tools, zero external dependencies — no resident service, no Python, no API key.

  • 平台:Windows 10/11(不跨平台)
  • 依赖:PowerShell 7(pwsh)+ .NET 的 System.Drawing / UIAutomationClient,都是系统自带
  • 形态:DSH profile bundle(host 侧注册 11 个模型工具)

安装

# 1. 进 DSH profile 目录(例如 ~/.dsh/profiles/web)
cd 
pnpm add sage-guikit

# 2. 在该目录的 package.json 里把 bundle 挂上
#    "dsh": { "profile": { "bundles": [ ..., "sage-guikit" ] } }

# 3. 重启 dsh web

本地开发用 link: 也行:

"dependencies": { "sage-guikit": "link:E:/DSH-plugins/sage-guikit" }

⚠️ link 方式下裸导入从源位置向上解析,够不到 profile 的 node_modules——需要在插件目录自己跑一次 pnpm install 装 peer 依赖(@deepseek-ai/cordis@deepseek-ai/dsh-tools)。从 npm 安装没有这个问题。

工具(11)

工具参数用途
gui_screen显示器布局:每块屏的物理像素 bounds、虚拟桌面矩形、当前光标位置。多屏时副屏坐标可能为负。任何坐标操作之前先调它
gui_screenshotscreen / x,y,w,h / annotate / step截屏存 PNG 并返回路径(用 read_image 看)。annotate=true 把坐标网格标签画进图里(隔行隔列)+ 红色十字准星标光标位置——读标签报坐标,免去 DPI 换算
gui_window_shotwindow / handle / annotate / step单窗口截图:用 PrintWindow 抓某个窗口自己的内容,被别的窗口盖住也照样抓,且不激活、不移动任何东西。返回 x/y 原点与 scale,换算 屏幕坐标 = 原点 + 图坐标 × scale。比整屏截图省一个数量级(实测 DSH 窗口 298 KB vs 整屏 8.8 MB)
gui_clickx y (必填) / button / clicks移动真实光标到物理像素坐标并点击。1=单击 2=双击 3=三击。点击后回显焦点窗口,点错立刻可见
gui_dragfromX fromY toX toY (必填) / button / steps / stepDelayMs按住拖动:选文字、拖窗口标题栏、拉滑块/滚动条、拖放。移动是插值的(只调 SetCursorPos 不产生拖拽事件,多数程序不认)。steps 调大更稳更慢。无论成败都保证松开按键。回显焦点窗口
gui_typetext (必填) / mode / x y向当前焦点控件打字。unicode(默认)逐字符 SendInput,绕过 IME,中文无损;clipboard 走剪贴板 Ctrl+V(会覆盖用户剪贴板)。可选先点击 (x,y) 定位焦点
gui_keykeys (必填)真实键盘按键或组合键:enterctrl+salt+f4ctrl+shift+tabwinprintscreen。修饰键 ctrl/alt/shift/win,f1–f24,a–z,0–9,方向键等
gui_scrollx y (必填) / direction / notches在 (x,y) 处滚轮。up/down 纵滚,left/right 横滚,默认 3 格
gui_windowaction (必填):list/rect/activate/move窗口管理:列出可见窗口(标题/句柄/pid/进程/矩形/z 序)、取单个窗口 bounds、激活到前台、移动改尺寸
gui_waitmode (必填):pixel/window轮询验证。pixel 盯某个像素:compare=change 与调用时基线比变化,eq/neq 比指定 RGB;window 等某个标题的顶层窗口出现。点完先等生效再截图
gui_uiaaction (必填):tree/find/invoke/value + handle/title/name/elType/depth/maxWindows UI Automation 结构化查询,一次限定一个窗口。tree 列可交互元素(名称/类型/矩形/可用);find 按 Name 子串或控件类型定位;invoke 经 InvokePattern(回退 TogglePattern)直按按钮,不用坐标value 经 ValuePattern 直读控件文本,验证输入无需截图。返回的矩形是物理像素,可直接喂给 gui_click

精度栈:三层互补

工具适用
结构层gui_uia标准控件(Win32 / WPF / WinForms):按名称或控件类型定位,直读直按,最省 token 也最可靠
视觉层gui_window_shot / gui_screenshot annotate=true自绘界面、游戏、CEF/Electron 这类 UIA 盲区:网格标签画进图里,agent 读标签报坐标。单看一个窗口用 gui_window_shot(被遮挡也抓得到,还省 token)
验证层gui_wait像素变化 / 窗口出现轮询,确认动作生效再继续

日常顺序:gui_screen 拿布局 → gui_window_shot 看目标 → 标注坐标 → gui_click / gui_draggui_wait 验证 → 必要时 gui_uia 直读控件文本核对。

实现

每次工具调用 = 一个 PowerShell 子进程,注入内联 C#:

  • U32..dll 编译缓存(原子写入 tmp + Move、>4KB 完整性校验、失败自动回退内存编译),冷启动一次编译,之后只是 Add-Type -Path文件名取自 C# 源码的内容哈希,源码一改缓存自动失效——固定版本号那种写法会加载到缺新方法的旧 DLL,报「U32 不包含名为 X 的方法」
  • user32 SendInput / SetCursorPos / EnumWindows / SetForegroundWindow / PrintWindow;UIA 走 UIAutomationClient。拖拽用 SendInput 绝对坐标 + MOUSEEVENTF_VIRTUALDESK 插值移动
  • SetProcessDPIAware:所有坐标一律物理像素,高 DPI 与多屏负坐标都安全
  • 结果以 JSON 从 stdout 回传,转成 text block。stdout 在 PRELUDE 第一句显式钉死为 UTF-8——子进程没有控制台时 .NET 会回退到系统 ANSI 代码页(中文 Windows = gb2312),不钉死则回传的中文界面文本(UIA 元素名、窗口标题)会乱码

截图与编译缓存的落盘目录默认是 %TEMP%\sage-guikit,可用环境变量 SAGE_GUIKIT_DIR 覆盖。

一个非显然的约束:别把脚本写胖

Windows Defender 会经 AMSI 扫描传给 pwsh -Command 的脚本。把「按标题找窗口 + GetWindowRect + PrintWindow + 画网格」全塞进一个脚本时,会被判为恶意脚本直接拒绝执行(报「此脚本包含恶意内容,已被防病毒软件阻止」)——枚举窗口标题 + 捕获窗口正是窥屏软件的特征。实测把窗口截图拆成独立工具、脚本压瘦后就不再触发。加功能时请留意脚本体积与 API 组合,改动后跑一遍 smoke.mjs

已知边界

  • UIPI:点不进管理员权限窗口;锁屏 / UAC 安全桌面完全不可达
  • Windows 前台锁:程序化抢焦点会被系统静默拒绝——注入前先 gui_window activate 或点击目标窗口
  • z 序陷阱:前台窗口切换后原坐标可能落到别的窗口上——靠 click/type 的焦点窗口回显发现
  • DPI-unaware 目标(点/拖/截都会受影响):DPI-unaware 的应用(WinForms 默认、部分 CEF 壳)在系统缩放 ≠ 100% 时受 Windows DPI 虚拟化影响:SetCursorPos 坐标被缩放甚至返回 false;拖拽的横向位移可能被吃掉(实测 175% 缩放下拖一个 unaware 窗口,纵向走 90px、横向 0px);gui_window_shot 抓出来的图内容只铺在左上角一小块。判据:gui_window_shot 返回的 scale > 1(= 系统 DPI ÷ 窗口 DPI),此时按 原点 + 图坐标 × scale 换算,或先激活一个 DPI-aware 窗口再操作。Obsidian / Electron / Edge 这类 DPI-aware 应用全部正常
  • 最小化窗口gui_window_shot 对最小化窗口会出黑图,先 gui_window activate
  • 锁屏:锁屏时 SetCursorPos 静默返回 false、光标冻住、前台 Idle。这是环境阻挡不是插件 bug——唯一判据是光标真能移动
  • UIA 盲区:Electron/CEF 类应用在没装屏幕阅读器时基本只暴露空 Pane,这类必须走视觉标注坐标
  • gui_type 的 clipboard 模式会覆盖用户剪贴板;unicode 模式更干净
  • 工具操作的是真实鼠标键盘——跑自动化期间这块屏幕就是它的实验台,同机其它 agent 的输入会互相污染

开发

pnpm install          # peer 由 DSH 闭包提供;这里装的是 devDependencies
node smoke.mjs        # 被动冒烟:布局 / 区域截图 / 窗口截图 / 窗口列表 / UIA / 等待
node smoke-drag.mjs   # 拖拽端到端:起一个 DPI-aware 测试窗口,拖它标题栏并核对位移

smoke-drag.mjs 会接管真实鼠标 1–2 秒,同屏有别的 agent 在跑 GUI 自动化时不要跑。

定位

同类方案(如基于 MCP 的 Windows computer-use server)通常走无障碍树 + 视觉模型 + 常驻进程,能力更全,代价是额外运行时(Python/uvx)、常驻内存和 API key。本插件的取舍相反:零依赖、无常驻、坐标直控,会话级即插即忘,代价是没有视觉理解,坐标要靠 gui_screen / 标注截图自己算。

License

MIT