跳到正文
dsh-market 浏览插件 GitHub EN

1jiegejiayouxuewangan1/dsh-desktop-uia

通过 UI Automation 控制 Windows 桌面:九个工具读取窗口控件树、按 id 走控件自身的 UIA 模式操作,每次写入都受审批策略、动作日志与设置面板约束。

Star 数 ★ 0 分类 工具与能力 收录于 2026-09-23

安装

在 DeepSeek Harness 里通过 dsh-market 安装

dsh plugin --profile web add dshmarket

或使用命令行

dsh plugin --profile web add "https://github.com/1jiegejiayouxuewangan1/dsh-desktop-uia/releases/latest/download/dsh-desktop-uia.tgz"

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。

截图

README

让 DeepSeek Harness 直接操作 Windows 桌面应用:读控件树 → 用元素 id 驱动 → 写操作过审批并留痕。

不截图、不猜坐标:控件树由 Windows UI Automation(UIA)直接给出,每个元素都有稳定 id、类型、名称、矩形和它支持的 UIA 模式;点击优先走元素的 Invoke/Value/Selection 模式,因此被遮挡的窗口也能操作,而且不会挪动你的鼠标。

只想拿来用? 看 使用说明.md(面向使用者,白话版:能做什么、怎么跟 AI 说、审批怎么设、常见问题)。 本文件是面向开发者/维护者的完整技术说明。 English: README.md(主文档)· GUIDE.md(英文使用说明)

面板:服务状态、窗口列表、控件树、元素详情、动作日志

设置 →「桌面控制」:服务状态、实时窗口列表、带元素详情的控件树、动作日志。

Agent ──▶ desktop_* 工具(宿主插件,含审批与审计)──▶ UiaSidecar.exe(C#/.NET,UI Automation + 输入注入)──▶ 目标应用
                     │
                     └─▶ 浏览器面板(设置 → 桌面控制):窗口列表 / 控件树 / 动作日志 / 策略配置

1. 工具

工具 作用 审批
desktop_windows 列出顶层窗口(hwnd/pid/进程/标题/矩形/状态),并管理窗口:focus / minimize / maximize / restore / show / hide / move / resize / 置顶 / close 只有 list、focus 免审批
desktop_snapshot 读某个窗口的控件树;带 query 时按名称/类型/自动化 id 直接找元素;patterns 可选 auto(默认,限时探测)/ all(全量,慢窗口更慢)/ none(最快) 只读
desktop_inspect 读单个元素的全部属性:框架、类名、焦点/可用状态、支持的模式,以及值、开关状态、选中项、表格内容、文本 只读
desktop_act 对元素执行:click / rightClick / doubleClick / hover / focus / invoke / setValue / select / addToSelection / toggle / expand / collapse / scrollIntoView / scroll / drag 需要
desktop_input 输入文字(Unicode,中文可用)或发送按键组合(ctrl+s、alt+f4) 需要
desktop_wait 等窗口出现、等元素出现、等值变成某个内容、等元素消失、固定延时 只读
desktop_launch 启动程序、打开文档或 URI(notepad、ms-settings:、路径) 需要
desktop_clipboard 读 / 写剪贴板文本 写需要
desktop_screenshot 截窗口或整屏为 PNG;当前模型支持图片输入时直接返回图片,否则返回文件路径 只读

结果里最重要的两部分:

  • 动作方式:via InvokePattern 表示走模式、没动鼠标;via mouse 表示走了坐标点击。
  • 变化对比:每个写操作后会自动重新快照并列出新增 / 消失 / 变化的元素。若显示 no structural change,通常意味着这次点击没生效——这时应该重新快照看状态,而不是原地重试。

元素 id(el_12)按 UIA 运行期标识生成,窗口重建后会失效;此时工具会明确报 STALE_ELEMENT 并提示重新快照,而不是静默失败。

让模型"看得见后果"的四条反馈(1.1)

看不见屏幕的模型会犯固定几类错,所以这四种情况直接写进工具返回里,模型在需要的那一刻就会读到:

  • 动作弹出的新窗口:每个写操作前后都比对一次桌面窗口集合,新出现的窗口会被点名——new window appeared: "另存为" (notepad pid=111) — snapshot hwnd 0x22 to work there。点击弹出的对话框、启动产生的第二个窗口都不会再被忽略;desktop_launch 额外等待最多 5 秒,把新窗口句柄直接报出来。
  • 读取很慢的窗口:按进程记录读取耗时。Chromium/Java 这类枚举本身就慢的窗口会被点明,建议改用 desktop_snapshot 的 query {name|type|aid} 只取一个元素;从下一次读取起自动收紧(maxNodes 300、maxDepth 5),除非调用方显式要求更大。
  • 重复的无效点击:同一元素连续两次 click/doubleClick/invoke 都以 no structural change 结束时,第二次会标注(this is no-op click number 2…),第三次相同点击直接拒绝,并提示先 desktop_inspect 看清状态、必要时滚动到可见,或改用 setValue/select/expand/toggle。带状态的控件(复选框、下拉框、滑块、滚动条——toggle、expandCollapse、selectItem、rangeValue、scroll、value)不受此限,因为它们的"变化"本来就未必体现在结构差异里。重新 desktop_snapshot 会清零计数,desktop_act {force: true} 可强制放行。
  • 被上限截断的读取:结果里直接说明被截断,以及该调大哪个上限。

2. 审批与安全

一轮写操作要过两层:

  1. 插件自己的策略(面板可改):denyActions 动作黑名单 → denyProcesses 进程黑名单 → allowProcesses 只允许名单 → trustedProcesses 信任名单。
  2. DSH 的审批通道(ctx.approval),按模式决定:
模式 行为
ask(默认) 跟随 DSH:DSH 会弹审批就弹(弹窗与 pwsh 提权同一套 UI);DSH 已关闭审批(例如 danger-full-access 预设)就直接执行。
always 不依赖 DSH 预设:没有审批渠道(或没人应答)一律拒绝。
never 完全不询问,全部直接执行。

为什么默认是「跟随 DSH」:本机 permission.defaultPreset 是 danger-full-access 时,DSH 会把会话审批策略置为 never,此时若强行「没人应答就拒绝」,每一次点击都会失败。想更严格就把模式改成 always。

无论哪种模式,每个动作都会写进动作日志(<DSH_HOME>\storages\dsh-desktop-uia\audit.jsonl;读操作也记,便于回溯"模型看过什么"),面板里能看到时间、工具、目标、结果和拒绝原因。被拒绝是正常结果:工具会返回 refused 和原因,并明确要求不要原样重试。

安全边界(做不到的事情,插件会明确报错而不是假装成功):

  • 非管理员运行的 DSH 无法读取或操作以管理员身份运行的程序(UIPI),工具会返回明确提示;需要时以管理员身份启动 DSH。
  • 不绕过 UAC / 安全桌面,不注入进程,不修改目标程序。
  • 坐标点击(point)无法预先知道属于哪个进程,因此进程名单对它不生效——优先用元素 id。

3. 面板(设置 → 桌面控制)

  • 状态:旁车进程状态、PID、DPI 感知、是否管理员、元素缓存、存储目录;刷新 / 自检 / 重启服务。
  • 窗口:当前所有可见顶层窗口,点一行载入它的控件树,可一键切到前台。
  • 控件树:带缩进的元素列表(id、类型、名称、可用模式、禁用/离屏/焦点标记),点元素看详情。
  • 动作日志:每次写操作的时间、工具、目标、结果。
  • 设置:审批模式、信任/禁止/只允许进程名单(可一键把当前前台进程加进信任)、动作后自动对比开关、快照上限。

面板只做「观察 + 配置」,不能代替模型点击或输入——所有会改变桌面的动作都必须经过工具调用和审批。


4. 安装

最省事的方式(不用命令行):到 Releases 下载 zip,解压到任意目录,双击 install.cmd。压缩包里已经带了编译好的旁车,所以不会触发任何编译;脚本会把插件装进你的 DSH profile 并跑一次自检。装完重启 DSH Desktop。

从源码目录安装:

# 工作区里的插件目录下
powershell -ExecutionPolicy Bypass -File install.ps1            # 默认装到 web profile
powershell -ExecutionPolicy Bypass -File install.ps1 -Profile web -DshHome "D:\dsh-home" -AppRoot "D:\DSH\resources\app"

安装脚本会:

  1. 用系统自带的 .NET Framework C# 编译器(csc.exe)编译旁车(已有编译好的 exe 就跳过,-Rebuild 强制重编),不需要 .NET SDK、不需要 NuGet、不需要联网;
  2. 在 profile 目录里执行 pnpm add file:<插件目录>;
  3. 把插件名加进 profile 的 dsh.profile.bundles 分层列表(用 dsh plugin add 时由它自己完成);
  4. 校验 profile 清单仍是合法 JSON、依赖、分层、安装目录都在,然后跑一次 doctor。

必须重启 DSH Desktop(或在应用里重载 profile)后宿主半才会挂载,desktop_* 工具和面板才会出现。

更新:改完源码后重跑 install.ps1 -Rebuild(file: 安装是 profile 内的真实副本,需要重新拷一次)。 卸载:

powershell -ExecutionPolicy Bypass -File uninstall.ps1          # 加 -Purge 连设置和日志一起删

为什么不用 link::link: 会把模块留在原目录,Node 从那里往上找 @deepseek-ai/dsh-tools 找不到(harness 包只存在于 profile 的 node_modules 链里),DSH 会启动失败并报 ERR_MODULE_NOT_FOUND。file: 会把插件放进 profile 自己的 node_modules,解析链正确。 为什么不让 dsh plugin 代劳:它把参数经 shell 转发,路径里的空格(例如 ds harness)会被拆成多个参数。


5. 验证

node --test "test/*.test.mjs"          # 84 个用例:格式、策略、存储、运行时、9 个工具、面板渲染 + 真实 DOM 交互
node scripts/doctor.mjs                # 端到端自检:编译/机器/旁车/真实桌面/工具层全链路
$env:DSH_UIA_LIVE=1; node --test "test/sidecar.live.test.mjs"   # 真机旁车集成测试
sidecar\build.ps1 -SelfTest            # 只测旁车

面板跑两遍:一遍用 hook 替身只检查渲染结果,一遍用真 React 挂载进 jsdom,额外验证首次加载、点窗口读控件树、点元素看详情、改设置后发出的请求体(react / react-dom / jsdom 是开发依赖,缺了会自动跳过)。

scripts/doctor.mjs 会真读你当前桌面:列窗口、快照前台窗口、按 query 找元素、点坐标反查窗口、校验坐标不变量(元素矩形中心反查窗口必须落回它自己的窗口,DPI/虚拟屏错位只有这一项能发现)、截图、读剪贴板,最后把 9 个工具注册起来跑 desktop_windows / desktop_snapshot / desktop_screenshot。它还会比对 profile 里那份已安装副本与当前源码是否一致,不一致会直接报 stale 并提示跑 scripts/dev-sync.ps1。任一项失败都会给出原因。


6. 故障排查

现象 原因与处理
工具返回 elevated/访问被拒 目标程序以管理员运行。以管理员身份启动 DSH 后重试。
STALE_ELEMENT / UNKNOWN_ELEMENT 窗口重建,id 过期。重新 desktop_snapshot。
终端/游戏/画图类窗口控件很少 这类应用是自绘的,UIA 只能看到外壳。用 desktop_screenshot 兜底,或对该窗口用坐标点击(point)。
Electron/Chromium 类窗口读得慢 这类提供程序本身慢(基础读取约 1~2s,与元素数不成比例)。工具会点名并自动收紧下一次读取;也可用 patterns:"none" 取最快读取,或用 query 只找需要的元素。
改了源码但行为没变 DSH 加载的是 <DSH_HOME>\profiles\<profile>\node_modules\dsh-desktop-uia 里的副本。跑 scripts/dev-sync.ps1 后重启 DSH;doctor 会检查该项。
同一个按钮连点三次被拒 这是重复无效点击守卫(第 1 节第 3 条)。重新快照 / desktop_inspect 看清状态,或加 force: true。
截图报 WINDOW_MINIMIZED / WINDOW_OCCLUDED 最小化的窗口没有像素;被完全遮挡且窗口自身不支持 PrintWindow 时先切前台。
坐标点击落在错误位置 多显示器混合缩放。旁车已声明 PerMonitorV2(应用清单),若仍异常,请用元素 id 而不是坐标。
旁车崩溃或响应超时 旁车按请求隔离线程,超时会返回 TIMEOUT 并保持可用;连续 5 次卡死才自重启(宿主下次调用时自动拉起)。
面板 404 宿主半没挂载:profile 的 bundle 列表里没有 dsh-desktop-uia,或 DSH 没重启。
面板 401/403 正常鉴权(需要 DSH 会话 Cookie);直接访问 URL 时会出现。

7. 目录结构

dsh-desktop-uia/
├── package.json           # dsh.bundle.patch + dsh.client(platform: web)
├── cordis.patch.yml       # 把插件行插入 profile 的组合
├── lib/
│   ├── index.js           # 宿主入口:配置、系统提示段、生命周期
│   ├── tools.js           # 9 个 desktop_* 工具(schema / 渲染 / 审批接线)
│   ├── service.js         # 运行时:旁车调用、快照缓存、元素→窗口归属、结果渲染
│   ├── approval.js        # 策略判定(黑白名单 + 三种审批模式)
│   ├── format.js          # 控件树文本、结构 diff、元素/窗口描述
│   ├── store.js           # 设置与审计(原子写 + JSONL 追加)
│   ├── routes.js          # 面板 HTTP 路由(走 DSH 连接鉴权)
│   ├── sidecar.js         # 旁车进程管理:JSON-RPC 行协议、超时、自愈
│   └── client.js          # 浏览器半(module-loader 经典脚本,无构建步骤)
├── sidecar/
│   ├── UiaSidecar.cs      # UIA 读树/操作/输入/GDI 截图(C# 5)
│   ├── Program.cs         # 行协议、看门狗、错误码、--selftest
│   ├── UiaSidecar.manifest# PerMonitorV2 + asInvoker
│   └── build.ps1          # 用系统 csc.exe 编译
├── scripts/
│   ├── doctor.mjs         # 端到端自检(含"已安装副本是否陈旧"与坐标不变量校验)
│   └── dev-sync.ps1       # 把当前源码同步到 DSH 实际加载的 profile 副本
├── test/                  # node:test 套件 + 假 ctx / 假旁车 / React 垫片 / 真实 DOM 面板套件
├── install.cmd / uninstall.cmd    # 双击即用(Release 压缩包里的入口)
├── install.ps1 / uninstall.ps1
└── README.md · GUIDE.md · README.zh-CN.md · 使用说明.md

8. 设计取舍

  • 单个 C# 旁车,而非原生 Node 绑定:UIA 客户端在 .NET Framework 里开箱可用,配合系统自带 csc.exe 实现零依赖分发;同时进程隔离让 UIA 调用卡死不会拖垮 DSH 主进程。
  • 一次缓存扫描读整棵子树:激活 CacheRequest 后一次 FindAll 取回全部后代,属性读取走缓存,整棵树只需一次跨进程往返。实测:终端窗口 23 个元素 30150ms、explorer 1020ms、VMware 77 个元素约 1s。因此没有实现计划里的事件增量刷新——这个量级不需要,而事件注册会带来额外的不稳定性。 两个必须踩到的坑(代码注释里也记着):缓存请求的 TreeScope 只能用 Subtree——用 Descendants 会取回集合但每个 Cached 读取都抛异常,而 TreeScope.Parent 会被直接拒绝,一旦加进去整条缓存路径就失败并静默退化成逐元素遍历(这正是 Electron 窗口慢到 20s 的根因)。
  • 按预算探测 UIA 模式:模式探测放在视图过滤和截断之后,只为真正返回给模型的元素探测,并有 2000ms 预算。原生窗口全额覆盖;Electron/Chromium 这类提供程序极慢的窗口拿预算内的部分,并附一条说明(需要更完整时用 desktop_inspect 单元素补,或显式 patterns:"all")。实测该窗口从约 20s 降到约 2s。
  • 元素 id 用运行期标识做键:同一窗口反复快照时 id 稳定,模型可以在多轮之间记住 el_57。
  • 模式优先、鼠标兜底:可靠性远高于坐标点击,而且不打扰用户。
  • 审批跟随 DSH 而不是自建:复用原生弹窗和会话策略,避免出现「插件说没问题、DSH 说不行」的两套真相;插件只用白/黑名单做更细的约束。
  • 面板只用 8 个 seed 模块里的 react:不引入构建步骤,浏览器半是手写的经典脚本,改完刷新页面即可(带 .map 不是必需项)。

内容来自项目 README(GitHub)↗

评论

评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。