|
| 1 | +# 给新 Electron 应用生成 CLI |
| 2 | + |
| 3 | +这篇文档是把一个新的 Electron 桌面应用接入 OpenCLI 的**中文入口指南**。 |
| 4 | + |
| 5 | +如果你需要更完整的背景和标准流程,继续看: |
| 6 | +- [Chrome DevTools Protocol(中文)](/zh/advanced/cdp) |
| 7 | +- [CLI-ifying Electron Applications(英文深度版)](/advanced/electron) |
| 8 | +- [TypeScript 适配器开发指南(英文)](/developer/ts-adapter) |
| 9 | + |
| 10 | +## 这篇文档适合什么场景 |
| 11 | + |
| 12 | +当目标应用满足下面条件时,用这套流程: |
| 13 | +- 应用是 **Electron**,或者至少能暴露可用的 **CDP(Chrome DevTools Protocol)** 端口 |
| 14 | +- 可以通过 `--remote-debugging-port=<port>` 启动 |
| 15 | +- 你希望控制的是桌面应用本身,而不是它背后的公开 HTTP API |
| 16 | + |
| 17 | +如果应用**不是** Electron,或者不暴露 CDP,就不要硬套这套方案。那种情况应改用原生桌面自动化方案。可参考 [英文版说明](/advanced/electron#non-electron-pattern-applescript)。 |
| 18 | + |
| 19 | +## 最短落地路径 |
| 20 | + |
| 21 | +### 1. 先确认它是不是 Electron |
| 22 | + |
| 23 | +macOS 下常见检查方式: |
| 24 | + |
| 25 | +```bash |
| 26 | +ls /Applications/AppName.app/Contents/Frameworks/Electron\ Framework.framework |
| 27 | +``` |
| 28 | + |
| 29 | +如果存在,通常就可以继续尝试 CDP。 |
| 30 | + |
| 31 | +### 2. 带 CDP 端口启动应用 |
| 32 | + |
| 33 | +```bash |
| 34 | +/Applications/AppName.app/Contents/MacOS/AppName --remote-debugging-port=9222 |
| 35 | +``` |
| 36 | + |
| 37 | +然后把 OpenCLI 指到这个端口: |
| 38 | + |
| 39 | +```bash |
| 40 | +export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9222" |
| 41 | +``` |
| 42 | + |
| 43 | +### 3. 先做 5 个基础命令 |
| 44 | + |
| 45 | +建议一个新 Electron 适配器先实现这 5 个命令: |
| 46 | + |
| 47 | +- `status.ts` —— 确认 CDP 连通 |
| 48 | +- `dump.ts` —— 导出 DOM / snapshot,先做逆向再写逻辑 |
| 49 | +- `read.ts` —— 读取当前上下文 |
| 50 | +- `send.ts` —— 往真实编辑器里输入并发送 |
| 51 | +- `new.ts` —— 新建会话 / 标签页 / 文档 |
| 52 | + |
| 53 | +这是最稳妥的基线,因为它先把“能连上、能看见、能读、能写、能重置状态”这 5 件核心事情打通了。 |
| 54 | + |
| 55 | +## 推荐开发顺序 |
| 56 | + |
| 57 | +### 第一步:先做 `status` |
| 58 | + |
| 59 | +目标不是功能,而是先证明: |
| 60 | +- CDP 真的连上了 |
| 61 | +- 你连到的是对的窗口/标签页 |
| 62 | +- 应用当前页面确实可读 |
| 63 | + |
| 64 | +如果 `status` 都不稳定,先不要继续往下做。 |
| 65 | + |
| 66 | +### 第二步:做 `dump` |
| 67 | + |
| 68 | +**不要猜 selector。** |
| 69 | + |
| 70 | +先把这些导出来: |
| 71 | +- `document.body.innerHTML` |
| 72 | +- accessibility snapshot |
| 73 | +- 稳定属性:`data-testid`、`role`、`aria-*` 等 |
| 74 | + |
| 75 | +然后再决定: |
| 76 | +- 消息列表在哪 |
| 77 | +- 输入框在哪 |
| 78 | +- 按钮在哪 |
| 79 | +- 当前会话容器在哪 |
| 80 | + |
| 81 | +### 第三步:做 `read` |
| 82 | + |
| 83 | +只读真正需要的区域,不要把整个页面文本都塞出来。 |
| 84 | + |
| 85 | +常见目标: |
| 86 | +- 对话消息区 |
| 87 | +- 当前线程内容 |
| 88 | +- 当前编辑器历史 |
| 89 | +- 当前文档主区域 |
| 90 | + |
| 91 | +### 第四步:做 `send` |
| 92 | + |
| 93 | +很多 Electron 应用的输入框是 React 控制组件,直接改 `.value` 往往没用。 |
| 94 | + |
| 95 | +更稳妥的方式通常是: |
| 96 | +- 先 focus 到可编辑区域 |
| 97 | +- 能用时优先 `document.execCommand('insertText', false, text)` |
| 98 | +- 最后用真实按键提交,比如 `Enter`、`Meta+Enter` |
| 99 | + |
| 100 | +### 第五步:做 `new` |
| 101 | + |
| 102 | +很多桌面应用的新建动作其实更适合走快捷键,而不是点按钮。 |
| 103 | + |
| 104 | +典型模式: |
| 105 | + |
| 106 | +```ts |
| 107 | +const isMac = process.platform === 'darwin'; |
| 108 | +await page.pressKey(isMac ? 'Meta+N' : 'Control+N'); |
| 109 | +await page.wait(1); |
| 110 | +``` |
| 111 | + |
| 112 | +## 文件一般怎么放 |
| 113 | + |
| 114 | +一个 TypeScript 桌面适配器,通常结构是: |
| 115 | + |
| 116 | +```text |
| 117 | +src/clis/<app>/status.ts |
| 118 | +src/clis/<app>/dump.ts |
| 119 | +src/clis/<app>/read.ts |
| 120 | +src/clis/<app>/send.ts |
| 121 | +src/clis/<app>/new.ts |
| 122 | +src/clis/<app>/utils.ts |
| 123 | +``` |
| 124 | + |
| 125 | +当基础能力稳定后,再继续加: |
| 126 | +- `ask` |
| 127 | +- `history` |
| 128 | +- `model` |
| 129 | +- `screenshot` |
| 130 | +- `export` |
| 131 | + |
| 132 | +## 加完适配器后,还应该补什么文档 |
| 133 | + |
| 134 | +至少补这几项: |
| 135 | +- `docs/adapters/desktop/` 下的适配器说明页 |
| 136 | +- 命令列表和示例 |
| 137 | +- 如何带 `--remote-debugging-port` 启动 |
| 138 | +- 需要哪些环境变量 |
| 139 | +- 平台限制和注意事项 |
| 140 | + |
| 141 | +可以参考这些现成文档: |
| 142 | +- `docs/adapters/desktop/codex.md` |
| 143 | +- `docs/adapters/desktop/chatwise.md` |
| 144 | +- `docs/adapters/desktop/notion.md` |
| 145 | +- `docs/adapters/desktop/discord.md` |
| 146 | + |
| 147 | +## 常见问题 |
| 148 | + |
| 149 | +### CDP 能连,但命令不稳定 |
| 150 | + |
| 151 | +常见原因: |
| 152 | +- 连错窗口或标签页 |
| 153 | +- 页面还没渲染完 |
| 154 | +- selector 是猜的,不是从 `dump` 里找出来的 |
| 155 | +- 输入框是受控组件,直接赋值不生效 |
| 156 | + |
| 157 | +### 应用看起来像 Chromium,但就是不好控 |
| 158 | + |
| 159 | +有些桌面应用虽然嵌了 Chromium,但并不真正暴露可用的 CDP 接口。 |
| 160 | +这种情况不要强行走 Electron 方案,应该换到非 Electron 的桌面自动化方案。 |
| 161 | + |
| 162 | +### 这个应用其实也有网页版本,还要不要做 Electron 适配器 |
| 163 | + |
| 164 | +如果网页版本已经足够稳定,浏览器适配器通常更简单。 |
| 165 | +只有当**桌面应用才是真正的集成面**时,再优先做 Electron 适配器。 |
| 166 | + |
| 167 | +## 推荐阅读顺序 |
| 168 | + |
| 169 | +如果你从零开始: |
| 170 | + |
| 171 | +1. 先看这篇 |
| 172 | +2. 再看 [CLI-ifying Electron Applications(英文深度版)](/advanced/electron) |
| 173 | +3. 再看 [Chrome DevTools Protocol(中文)](/zh/advanced/cdp) |
| 174 | +4. 再看 [TypeScript Adapter Guide(英文)](/developer/ts-adapter) |
| 175 | +5. 最后找一个现成桌面适配器文档照着做 |
| 176 | + |
| 177 | +## 最后一个实践建议 |
| 178 | + |
| 179 | +不要一上来就做很大的命令面。 |
| 180 | + |
| 181 | +先把下面 5 个做稳: |
| 182 | +- `status` |
| 183 | +- `dump` |
| 184 | +- `read` |
| 185 | +- `send` |
| 186 | +- `new` |
| 187 | + |
| 188 | +这 5 个稳定了,再往外扩,成本最低,返工也最少。 |
0 commit comments