# dsh-plugin-midscene [English](README.md) | 中文 [![CI](https://github.com/ciky20171114/dsh-plugin-midscene/actions/workflows/ci.yml/badge.svg)](https://github.com/ciky20171114/dsh-plugin-midscene/actions/workflows/ci.yml) 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)提供基于 [Midscene](https://midscenejs.com) 的 AI 驱动 UI 自动化。模型看得到屏幕、用自然语言描述定位元素、对真实目标执行操作——一台真实的 Android 设备或一个真实的 Chrome 浏览器。 一个能力 seam(`ctx.midscene`)、两个 Provider、两个工具: | | Provider 入口 | 工具 | 目标 | |---|---|---|---| | Android | `dsh-plugin-midscene/android` | `android_ui` | 一台 ADB 连接的设备 | | Web | `dsh-plugin-midscene/web` | `web_ui` | 一台已经在运行的 Chrome 的活动页面 | 每个工具都是**带 `action` 参数的单工具**(与 `str_replace_editor` 同风格):模型选择一个动作(`tap` / `act` / `input` / `query` / `assert` / `boolean` / `back`),工具在内部自行分发——不撑大工具面。 ## 前置条件 - DSH(`dsh` CLI)及一个 profile - Android:`adb devices` 能看到设备 - Web:Chrome 以 `--remote-debugging-port=9222 --user-data-dir=<目录>` 启动;Provider **只连接、绝不启动**浏览器 - 一个 Midscene 兼容的视觉模型,通过环境变量配置(见[模型配置](#模型配置)) ## 安装 ```sh dsh plugin --profile mysetup add dsh-plugin-midscene ``` bundle 的默认层注册两个工具。工具以机会主义方式读取 `ctx.midscene`,所以即使还没配置 provider 工具也已出现——此时调用会以一条指明缺失 provider 行的错误失败。 然后向 profile 的 `cordis.patch.yml`(`~/.dsh/profiles/mysetup/cordis.patch.yml`)加**恰好一个** provider 行(两个 provider 不能在同一个 context 里同时拥有 `ctx.midscene`): ### Android ```yaml - insert: - id: midscene-android name: dsh-plugin-midscene/android config: deviceId: '' # 留空:选择 getConnectedDevices() 的第一台 aiActionContext: '' # 供 aiAct 规划使用的自由文本上下文,例如应用约定 ``` ### Web ```yaml - insert: - id: midscene-web name: dsh-plugin-midscene/web config: browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser/' aiActionContext: '' ``` 可直接粘贴的 provider 行见 [`examples/`](examples/)。 从 `http://127.0.0.1:9222/json/version` 的 `webSocketDebuggerUrl` 取端点。注意该 Chrome 每次重启后 id 都会变——更新此行并重启 dsh。 销毁时 provider 会销毁自己的 agent,然后对浏览器执行 **`disconnect()` —— 绝不 `close()`**:Chrome 进程属于你的部署,继续运行。 启动: ```sh dsh --profile mysetup # 若 3080 被占用,加 --port 3081 ``` ## 安装排错 **`dsh plugin add` 报 `ERR_PNPM_IGNORED_BUILDS`**,点名 `sharp` / `@ffmpeg-installer/linux-x64`:pnpm ≥ 10 会拦截这些传递依赖的安装脚本(来自 `@midscene/*`),直到显式声明。处理:打开 `~/.dsh/profiles//pnpm-workspace.yaml`,把 pnpm 列在 `allowBuilds` 下的键设为 `false`(插件没有它们也能工作——仅当你要 sharp/ffmpeg 二进制做真机截图/录屏时才设 `true`),然后重新执行 `add`。每个 profile 只需一次。 ## 工具参考 `android_ui` 与 `web_ui` 共享同一形状: | `action` | 其余参数 | 结果 | |---|---|---| | `tap` | `prompt`(元素描述) | ack | | `act` | `prompt`(目标描述) | ack + agent 自身的结果文本(若有) | | `input` | `prompt`(元素)+ `value`(要输入的文本) | ack | | `query` | `demand`(要提取什么) | 提取出的 JSON | | `assert` | `prompt`(断言)+ 可选 `msg` | pass/fail + 可选思考过程 | | `boolean` | `prompt`(是/否问题) | true/false | | `back` | — | ack(Android:系统返回;Web:历史返回) | schema 无法表达的跨字段规则(如 `input` 必须有 `value`、`query` 必须有 `demand`)在 `execute` 中以指名道姓的错误消息强制执行。**断言失败是一次成功的 `pass: false` 结果**——错误路径只留给基础设施故障(设备消失、websocket 拒绝)。 ## 模型配置 Midscene 的视觉模型通过 `@midscene/*` 自身的约定配置——环境变量,而非 DSH 的 `ctx.llm`: ```sh export MIDSCENE_MODEL_NAME=glm-4.6v export MIDSCENE_MODEL_BASE_URL=https://open.bigmodel.cn/api/paas/v4/ export MIDSCENE_MODEL_API_KEY=<你的key> export MIDSCENE_MODEL_FAMILY=glm-v ``` (任何 OpenAI 兼容的多模态端点都可以——设置对应变量即可。) ## 设计边界:不含策略、不含恢复 Provider 是刻意的薄传输层:无重试、无前置条件检查、无对意外 UI 状态的自动恢复(意外弹窗、非预期跳转、重新登录)。有这类需求的调用方在其上自行构建——例如每次写操作前检查应用状态的约束/harness 层。 ## 已知限制 - **每个 provider 实例一个目标** —— 每个 context 一台设备或一台浏览器;扇出需要隔离的组合。 - **不重连** —— 会话中途断开表现为一次被拒绝的调用。 - **锁定 SDK 版本** —— `@midscene/android` / `@midscene/web` 精确锁定在 `1.11.0`;升级是一次刻意的版本 bump。 - **`puppeteer` 是 peer**(web)—— 由部署方的 pnpm 解析;Chrome 本身由部署方提供,本插件绝不下载。 ## 开发 ```sh git clone https://github.com/ciky20171114/dsh-plugin-midscene cd dsh-plugin-midscene pnpm install # 原生/浏览器安装脚本默认被拒绝;测试 mock 掉 SDK pnpm test # 只测接线:mock 的 @midscene/*、puppeteer,真实工具注册表背后的 stub seam pnpm build # tsc 输出到 lib/(git 安装时作为 `prepare` 运行) ``` 目录结构: ``` src/service.ts MidsceneService 定义 —— ctx.midscene seam(7 个操作) src/android.ts Android provider(AndroidDevice + AndroidAgent,ADB) src/web.ts Web provider(puppeteer.connect + PuppeteerBrowserAgent,只连接) src/tool.ts android_ui + web_ui 工具(一份共享定义,action 分支) tests/ 31 个接线测试 —— 绝不碰真机或真浏览器 ``` 开发时把本地 checkout 装进 profile: ```sh dsh plugin --profile dev add /path/to/dsh-plugin-midscene ``` ## 社区与支持 欢迎通过 [GitHub Discussions](https://github.com/ciky20171114/dsh-plugin-midscene/discussions) 提交反馈或 bug 报告。本仓库携带 `dsh-plugin` topic 以便被检索到。 ## 许可证 MIT