deepseek-computer-use
社区插件,非 DeepSeek 官方出品。官方包在 @deepseek-ai/* scope 下。
给 DeepSeek Harness 的 Windows 桌面控制(Computer Use) 插件。
工具面对齐 Codex 的 window2 API(list_apps / list_windows / get_window /
launch_app / get_window_state / click / press_key / type_text / scroll /
set_value / drag / perform_secondary_action / activate_window /
start_audio_recording / stop_audio_recording / computer_use_health),底层是
Windows 原生三件套:UI Automation 观测 + SendInput 注入 + Windows.Graphics.Capture 抓屏。
零 npm 原生依赖、零 .NET 运行时依赖:helper 和覆盖层用系统自带的 csc.exe 现编,
抓屏(WGC)和录音(WASAPI)是 net48 程序,Windows 10/11 自带运行时。
安装
要求:Windows 10/11、Node ≥ 18.17。其余都是系统组件(.NET Framework 4.8、
UI Automation、WGC)。
npm install --save deepseek-computer-use
npm install --save github:STARDUSTLC666/deepseek-computer-use
然后在本机 profile 的 cordis.patch.yml 里启用(详见本文件末尾的配置示例):
- insert:
- id: deepseek-computer-use
name: 'deepseek-computer-use'
首次调用任意 Computer Use 工具时会自动编译 helper 与覆盖层(约 1 秒)。
想要抓屏/录音这两项可选能力,再跑一次:
npm run build
没有 .NET SDK 也能用:抓屏自动退回 PrintWindow,录音会明确报 audio_missing。
自检
npm test
npm run test:all
它解决什么问题
DSH 本体没有鼠标键盘工具,只能跑命令、读写文件。需要"操作图形界面"时(点按钮、
填表单、操作没有 API 的桌面软件),这个插件补上这一层,并且把每一步都变成可观测、
可审批、可验证的动作。
架构
DSH 宿主
└─ lib/index.js apply(ctx) → 注册 16 个工具;图片走 attachments;审批走 approval
├─ lib/tools.js 工具定义(Codex window2 语义)
├─ lib/safety.js 白名单/黑名单 + 危险动作审批门
└─ lib/bridge.js 常驻进程 + 串行 JSON-lines RPC + 超时/取消
└─ native/bin/dsh-computer-use-helper.exe (用系统 csc.exe 现编)
├─ native/Driver.cs IPlatformDriver 契约 + WindowsDriver(UIA / SendInput / PrintWindow)
├─ native/Json.cs 零依赖 JSON 解析与序列化
└─ native/Program.cs stdio 分发
驱动层是接口(IPlatformDriver),今天是 windows 一个实现;macOS(AX + CGEvent)
和 Linux(AT-SPI)可以照着补,插件层不用改。
helper 常驻的原因:元素编号必须和"那一次观测"绑在同一个进程里,否则
click(element_index) 无从解析。因此插件串行发请求、按 id 配对响应。
工具
list_apps | 列出当前有可操作窗口的应用及其窗口 |
list_windows | 列出可见窗口:id / 标题 / 所属应用 / z 序 / 边框 |
get_window | 按 id 重新读取窗口(换绑、刷新边框) |
launch_app | 启动应用(可带参数),默认需要审批 |
get_window_state | 观测:带编号的无障碍树 +(可选)窗口截图 |
click | 按元素编号(优先走 Invoke,抗遮挡)或窗口相对坐标点击 |
press_key | 单键或组合键,X keysym 风格:Control_L+s、Return |
type_text | 注入 Unicode 文本(与键盘布局无关,中文安全) |
scroll | 在坐标或元素处滚动 |
drag | 窗口内拖拽 |
set_value | 用 Value 模式直接改值,比"点击+全选+输入"更稳更省 |
perform_secondary_action | Invoke / Expand / Collapse / Toggle / Select / Scroll Up… |
activate_window | 把窗口提到前台 |
start_audio_recording | 开始录制系统音频(WASAPI 环回),需 audio: true |
stop_audio_recording | 停止录音并返回 WAV 路径与字节数 |
computer_use_health | 自检:helper、编译器、策略、窗口数 |
使用中指示器(对齐 Codex 的两个 overlay)
Codex 用两层覆盖层告诉用户"AI 正在动你的电脑",这里做的是同一套:
Display Overlay — 呼吸细边
- 一圈 1.4px 的强调色细线 + 3 层递减光环,圆角 18px;只占屏幕最外圈,不压内容
- 亮度按约 3.2 秒周期呼吸(内线 alpha 170→240、光环 26→46)
- 4 条边 + 4 个圆角 = 8 个细长分层窗口,每帧位图几 KB
- 底部居中一枚自动宽度玻璃胶囊:脉冲圆点 +
DSH 正在使用您的电脑 +
Esc 键帽 + 取消,停在任务栏上方留白
Cursor Overlay — 虚拟光标 + 系统光标抑制
- 覆盖层存在期间常驻一个带描边和投影的白色指针,启动时落在真实指针位置,
之后 0.35 缓动滑向每个动作点;背后一层极淡的强调色光晕
- 默认抑制真实系统光标,退出/Esc/超时用
SPI_SETCURSORS 恢复;
helper 强杀覆盖层时也会兜底恢复一次
DPI 关键点:覆盖层进程必须 Per-Monitor-V2 感知,否则在 150% 缩放屏上
系统会把 1707×1067 的逻辑画面放大到 2560×1600,描边变糊、位置偏移 1.5 倍。
Esc 是按 turn 生效的:首次触发报 cancelled_by_user,同一 turn 内后续调用直接
turn_stopped,新 turn 才恢复。
安全模型
- 可达性:
allowApps / denyApps 决定这个插件能不能碰某个应用;空数组 = 不限制。
- 确认:
approval 三档
never:从不询问(自担风险)
sensitive(默认):目标元素标签命中危险词(delete/删除/卸载/支付/提交/安装…)才问;
launch_app 总是问
always:每次动作都问
审批走 DSH 自己的 approval seam;没有审批服务时直接拒绝,绝不静默降级。
- 前台证明:打字、按键、坐标点击、滚动必须先证明目标窗口真的拿到了前台,
否则抛
focus_failed 并告诉你当前是谁占着前台。SendInput 会打到有焦点的窗口,
这一步省不得。
- 快照 TTL:元素编号默认 120 秒过期,过期必须重新观测,避免"拿旧编号点新界面"。
dryRun:只回放动作不注入,用于集成演练。
安装
# 1. 放进 DSH 的 profile
cd "E:\path\to\dsh-computer-use"
# 方式 A:本地目录挂载(把本目录链进 profile 的 node_modules)
# 方式 B:npm pack 后用宿主插件管理器安装
插件通过 package.json 的 dsh.bundle.patch 声明自己:
{ "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } }
cordis.patch.yml 负责往宿主配置里插插件行。装好后重启宿主,让模型调
computer_use_health 自检;第一次调用会自动编译 helper(约 1-2 秒)。
配置
- id: deepseek-computer-use
config:
approval: sensitive
allowApps: ["notepad", "chrome"]
denyApps: []
defaultIncludeScreenshot: false
maxElements: 260
snapshotTtlMs: 120000
dryRun: false
toolPrefix: ''
timeoutMs: 20000
契约对齐(对着 Codex 逐条核过)
工具名与参数按 @oai/sky 的 window2 契约实现,不是"看着像":
get_window_state 默认 | 截图 ON、accessibility null | 一致(include_text:true 才出树) |
| 返回形态 | { window, screenshots[], accessibility } | 一致;screenshots 是数组,含窗口 + 关联瞬态 UI(菜单/tooltip),每项带 id/zIndex/originX/originY/尺寸 |
| 坐标寻址 | click/scroll/drag 收 screenshotId | 一致;坐标系绑定那张截图,过期报 stale_screenshot |
scroll | x,y 必填,无 element_index | 一致(缺 x/y 直接拒) |
| accessibility 结构化字段 | tree/focused_element/selected_text/selected_elements/document_text | 全部实现 |
| 观测节奏 | 动作后自动等约 1s 再抓 | 一致(settleMs,默认 1000) |
| 元素失效 | 编号只对产生它的观测有效 | 一致(TTL + helper 进程内缓存) |
| 最小化窗口 | 不该给出无意义画面 | 本插件直接报 window_minimized(实测旧版会吐出 334×48 的假图) |
| 键名 | X keysym + Numpad_* 别名 | 一致(KP_0/Numpad_Add/…) |
list_apps | lastUsedDate、useCount | lastUsedDate 有;useCount 无数据源,不编造 |
launch_app | app id / .exe 路径(含 UWP) | exe 路径、AUMID(shell:AppsFolder\\…)、shell URI |
| Win/Meta 键 | 硬拒绝 | 硬拒绝(插件层 + helper 层双保险) |
| 终端/Run/密码管理器/安全软件/认证对话框/宿主自身 | 硬拒绝 | 硬拒绝(不可被审批覆盖) |
| 按应用授权 | "Computer Use was not approved to use " | 每会话每应用一次审批(appConsent: session),批准后下发 approve_app,由 helper 强制:未批准的应用直接 app_not_approved |
| 回合边界 | 新 turn 时向 helper 发 end_turn,清元素快照 | 一致:token 变化即 end_turn(清快照/截图区/Esc 阻断,保留应用授权) |
| 请求预算 | 普通 10s、launch_app 15s | 一致(timeoutMs / launchTimeoutMs) |
| 回合结束停止输入 | turn_ended 钩子 | 读会话日志判断 open turn,turn_ended 拒绝注入 |
明确没做的(不属于 Windows window2 面)
paste / select_text:属于 macOS 的 window 客户端(target: "mac"),Windows 上不存在。
- 播客/音频录制(
start_audio_recording):契约里是可选方法,未实现。
- full-desktop 面(
clipboard_read/write、key_down/up、move、drag_handle、get_screenshot):那是 target: "linux" 的 API。
- 区域裁剪截图:Codex 是"bounded screenshot",我们截整窗(长边封顶 2560)。token 成本略高,语义一致。
- Windows.Graphics.Capture:Codex 用 WGC;我们用
PrintWindow(PW_RENDERFULLCONTENT),遮挡可用、最小化拒绝,不需要 D3D 互操作。
抓屏链路(对齐 Codex 的 WGC)
按顺序尝试,前一级成功就不走下一级:
- Windows.Graphics.Capture(
native/bin/capture/dsh-cu-capture.exe,.NET Framework 4.8 + SharpDX)
—— 抓窗口的合成层,遮挡可用、GPU/直合成界面正常、无 DPI 黑边。
不依赖 .NET 8 运行时(Framework 4.8 是 Windows 自带组件),载荷仅 0.68MB
- PrintWindow(PW_RENDERFULLCONTENT) —— 无 .NET 8 运行时的机器上的兜底
- 屏幕 BitBlt —— 前两者失败或返回黑图时
WGC 走 .NET Framework 4.8(Windows 10/11 自带),用户不需要安装任何运行时。
万一抓取失败仍会自动降级到第 2 级。
覆盖层是独立窗口,WGC 按窗口抓取时天然不会被拍进去——和 Codex 的
"exclude display overlay from captures" 等效。
已知限制
- 只有 Windows 驱动。macOS / Linux 只有接口(
IPlatformDriver),没有实现。
- 前台焦点是硬约束。前台被置顶工具(如 Process Lasso)或提权窗口占住时,
type_text/press_key/坐标点击/scroll 会被拒绝并报出占用者。
语义动作(set_value、Invoke 点击、Expand)不需要前台,照常工作。
- Electron/Chromium 无障碍树很薄,网页内容要走截图 + 坐标。
- 不绕过 UIPI:目标窗口权限更高时注入会被系统拦。
- DPI 虚拟化已处理:老式/无 DPI 感知的程序按 96 DPI 渲染,窗口物理尺寸被 DWM 放大,
PrintWindow 返回的是未缩放画面(此前会带黑边)。现在抓屏会裁掉黑边,并记录
「物理像素 / 截图像素」比例(实测某 WinForms 程序为 1.46×),坐标动作按该比例换算,
所以截图坐标点哪就点哪。
useCount、应用使用历史没有可靠本机数据源,宁缺不编。
测试
node test/integration.mjs
固定装置是一个独立的 WinForms 可执行文件(test/bin/dsh-cu-fixture.exe,一个文本框 +
一个名为 "Delete all" 的按钮;特意不用 shell 托管——shell 窗口本身被硬拒绝)。覆盖:
14 个工具注册、截图数组与 origin/zIndex、accessibility:null 语义、set_value、
type_text、危险按钮触发审批、dryRun 不落地、denyApps 拒绝、Win 键硬拒绝、
scroll 契约。
许可
MIT。第三方商用前请自行做安全评审:这个插件能让模型操作你的桌面。
原生载荷(native/bin)
dsh-computer-use-helper.exe | UIA / SendInput / 抓屏调度 / 音频调度 | ~50KB |
dsh-cu-overlay.exe | 鲸鱼娘 + 虚拟光标覆盖层 | ~30KB |
capture/ | WGC 抓屏(.NET Framework 4.8 + SharpDX) | 0.68MB |
audio/ | WASAPI 环回录音(NAudio) | 0.60MB |
| 合计 | | ~1.4MB |
全部基于 Windows 自带的 .NET Framework 4.8,用户零安装。
与 Codex window2 的细节对齐(第二轮)
| 协议握手 | helper 提供 hello → { protocolVersion: 1, capabilities: [...] };bridge 首次连接校验,不匹配直接报 protocol_mismatch |
| 每请求预算 | 客户端把预算随请求下发(__budgetMs,对应 Codex 的 x-oai-cua-request-budget-ms),helper 用它收缩抓屏等待 |
| 坐标越界 | point_outside_screenshot,文案同 Codex("is outside screenshot bounds … Take a fresh screenshot before retrying") |
| 截图失效 | stale_screenshot 带同一句 "Take a fresh screenshot before retrying" |
| 优雅关闭 | close RPC → helper 落盘后退出 0;插件 dispose 时先发 close 再兜底 kill |
| Esc 语义 | 覆盖层退出码 130 → helper 写入 %LOCALAPPDATA%\dsh-computer-use\interrupts\<会话>-<turn>.flag 并自身退出 130;同 turn 后续调用 turn_stopped,新 turn 放行 |
| 音频授权 | 独立于应用授权的一次性确认(Codex: computer-audio / "Allow Computer Use to record computer audio?") |
| 音频返回 | { filepath, bytes: base64 WAV, data_url },统一 24 kHz 立体声 16-bit |
| 音频上限 | 单次录音 ≤ 300 000 ms(Codex 同值),不足 500 ms 自动抬到 500 |
| App 字段 | isRunning + lastUsedDate(进程启动时间);useCount 无数据来源,按 Codex 的可选字段处理,不伪造 |
marker 文件按 会话 + turn 命名,避免旧会话的 Esc 标记误伤新会话。
配置示例
- insert:
- id: deepseek-computer-use
name: 'deepseek-computer-use'
config:
approval: sensitive
appConsent: session
audio: false
redactApps: []
defaultIncludeScreenshot: true
toolPrefix: ''
已知边界
- 只支持 Windows;驱动层是接口(
IPlatformDriver),macOS / Linux 可照着补。
- Codex window2 没有的接口(
paste / clipboard_* / key_down / move)这里也没有。
App.useCount 没有数据来源,按可选字段留空,不伪造。
License
MIT