From 490d60bbc90a4f407d2d9111e0da70e7efe6995a Mon Sep 17 00:00:00 2001 From: Ariver <shanghai3168@gmail.com> Date: Mon, 13 Jul 2026 11:54:48 +0800 Subject: [PATCH] docs: define independent auto-insert architecture --- K2.项目管理/ID-01-AUTO-INSERT-架构设计.md | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 263 insertions(+), 0 deletions(-) diff --git "a/K2.\351\241\271\347\233\256\347\256\241\347\220\206/ID-01-AUTO-INSERT-\346\236\266\346\236\204\350\256\276\350\256\241.md" "b/K2.\351\241\271\347\233\256\347\256\241\347\220\206/ID-01-AUTO-INSERT-\346\236\266\346\236\204\350\256\276\350\256\241.md" new file mode 100644 index 0000000..5b5dab3 --- /dev/null +++ "b/K2.\351\241\271\347\233\256\347\256\241\347\220\206/ID-01-AUTO-INSERT-\346\236\266\346\236\204\350\256\276\350\256\241.md" @@ -0,0 +1,263 @@ +# ID-01-AUTO-INSERT 架构设计 + +任务:`ID-01-AUTO-INSERT-ARCH` +角色:架构师 +日期:2026-07-13 +结论:`ARCH_RESULT ID-01 PASS` + +## 1. 目标与边界 + +Owner 新硬约束已经明确:本路线不再追求 Mac App Store 审核兼容,目标改为 Developer ID / 非 MAS 分发下的真正跨 App 自动上屏。手动 `Cmd+V` 不算完成,正常主路径必须是:用户触发录音,ASR 识别完成后,文字自动进入当前目标 App 输入框。 + +本轮只做架构设计,不写生产源码、不构建、不发布、不上传、不修改 MAS 分支。架构证据文档只写入: + +`/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O/K2.项目管理/ID-01-AUTO-INSERT-架构设计.md` + +不写入旧项目 `/Users/ar/Projects/PrivateVoice/`、MAS 项目或旧 independent-developer 项目。 + +## 2. 当前起点核验 + +本轮只读核验到的新工作树状态: + +- Git 工作树:`/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O` +- 当前分支:`independent-developer-auto-insert` +- 远端跟踪:`boborobots/independent-developer-auto-insert` +- HEAD:`a075aec123e1f7118f138738f196c2995aa96f98` +- HEAD tag:`v2.2.2-build20260703.0335-real-user-test` +- 源码根:`/Users/ar/Projects/PrivateVoice-independent-developer-auto-insert/03-O/C1.source/privatevoice.src` + +关键源码入口核验: + +- `internal/hotkey/hotkey_darwin.go`:已存在 `CGEventTapCreate(kCGHIDEventTap, ..., kCGEventTapOptionListenOnly, ...)`、`CGEventSourceKeyState`、`CGEventSourceFlagsState`、Accessibility prompt、后台 event tap runloop。 +- `internal/input/paste_darwin.go`:已存在 `NSPasteboard` 写入 / 恢复、`CGEventPost(kCGHIDEventTap, Cmd+V)` 自动粘贴、`CGEventKeyboardSetUnicodeString` 打字 fallback、Accessibility 权限检查。 +- `internal/permissions/permissions_darwin.go`:已存在 Microphone、Accessibility、Input Monitoring 三类权限状态与请求路径,其中 Accessibility / Input Monitoring / Microphone 均为 required。 +- `CODEGRAPH.md`:当前基线声明 macOS Developer ID / notarization 脚本为 `C1.source/privatevoice.src/scripts/build-release-macos.sh`。 + +外部官方事实参考: + +- Apple `CGEventTapCreate` / Quartz Event Services:事件 tap 用于观察或影响低层输入事件流,相关路径受隐私权限约束。 +- Apple `AXIsProcessTrustedWithOptions`:用于判断当前进程是否是受信任 Accessibility client。 +- Apple `CGEventPost`:可向指定 event tap location post 事件。 +- Apple notarization / Hardened Runtime 文档:Developer ID 分发需要签名、公证与 hardened runtime 约束;notarization 不是 App Review,也不等于 MAS 审核通过。 + +## 3. 架构总决策 + +独立开发版应恢复并产品化“完整自动输入能力”,采用最小必要组合: + +1. 全局触发:`CGEventTap` + Input Monitoring,用于后台监听用户配置的全局热键 / 长按触发。 +2. 自动上屏权限:Accessibility,用于读取前台 App / focused element、判断可写性、在必要时 post 键盘事件。 +3. 首选输出:AX focused element direct insert。对可写的标准文本控件,直接通过 Accessibility 写入 focused text element,并用 AX readback 验证。 +4. 自动 fallback:受保护剪贴板 + `CGEventPost` 自动 `Cmd+V`。这不是手动 `Cmd+V`,而是 App 在安全 guard 通过后自动完成输入动作。 +5. 失败处理:失败时必须返回明确错误、可重试状态和原因;不得把“请用户手动 Cmd+V”作为正常主路径或验收成功。 + +第一阶段不建议把 `CGEventKeyboardSetUnicodeString` 逐字输入作为主路径。它对中文、组合字符、输入法状态、快捷键冲突和高延迟目标风险较高,可保留为后续实验项,默认不进入 ID-01 最小可验收切片。 + +## 4. 输出路由设计 + +建议新增或产品化一个 `AutoInsertRouter`,不要让 ASR 流程直接调用旧 `Paster.Paste`。 + +最小路由顺序: + +1. `PreflightTarget` + - 获取 frontmost app bundle id / pid / localized name。 + - 获取 focused element role、subrole、是否 secure、是否 Terminal / shell / console 类负向目标。 + - 记录目标指纹,不记录 transcript、窗口标题、文档标题或用户输入内容。 + +2. `AXDirectInsert` + - 只在 focused element 可写且不是 secure field 时启用。 + - 写入前读取 value / selected text range / insertion point 能力。 + - 写入后短轮询验证 value 变化、selected range 变化或目标可观测文本包含插入内容。 + - 成功时返回 `route=ax_direct verified=true`。 + +3. `GuardedAutoPaste` + - 再次确认 frontmost app / focused element 与 preflight 一致,或符合显式允许的 focus-switch 策略。 + - Terminal / shell / console、secure field、系统授权窗口、锁屏、未知目标一律禁止自动 paste。 + - 写入剪贴板前保存 snapshot;写入后验证剪贴板 token / text 已准备。 + - 调用 `CGEventPost` 自动发送 `Cmd+V`。 + - 如 AX 可读则验证目标文本变化;如目标不可读但目标在 QA 白名单中,可记录 `posted_unverified`,但正式验收仍要由 QA 通过屏幕/日志证据证明文字实际上屏。 + - 用户中途改剪贴板时不得覆盖用户新剪贴板;只在剪贴板仍是本轮 pending text 时恢复。 + +4. `Failure` + - 不静默降级。 + - 返回明确错误码,例如 `permission_missing`、`target_not_editable`、`focus_changed`、`terminal_blocked`、`paste_rejected`、`verification_failed`。 + - UI 可提供“重试”“复制到剪贴板”作为显式辅助动作,但这不是自动输入验收路径。 + +## 5. 支持边界与 NO-GO + +可支持目标: + +- TextEdit / Notes / Mail compose 等标准 AppKit 文本控件:优先 AX direct insert,可读回验证。 +- Safari / Arc / Codex / WebView / Electron 类 textarea 或 contenteditable:AX direct insert 可能不稳定,允许走 guarded auto paste;验收必须证明文字自动进入目标输入框。 +- WeChat 文本输入框:允许 guarded auto paste;如 AX 可读则做 readback,否则走 QA 可视证据。 + +条件支持目标: + +- 自绘编辑器、Electron、Monaco、复杂 Web editor:不承诺 AX 可写,必须通过 guarded auto paste 证明自动上屏。 +- 多窗口 / 多 Space / 焦点切换:默认要求 ASR 完成时 frontmost target 与录音开始时一致;若产品要允许中途切换,必须作为单独产品规则和 QA 项。 + +明确 NO-GO / 负向目标: + +- Terminal、shell、console、SSH session、REPL、数据库控制台等命令执行环境:默认不得自动 paste / type,避免把听写文本当命令执行。 +- Secure text field、密码框、系统授权弹窗、锁屏、登录窗口、远程桌面 / VM 控制窗口。 +- 无 frontmost editable target、焦点丢失、目标不可识别、目标禁止 paste 或目标切换无法确认。 +- 任何要求 AppleEvents、osascript、私有 API、IMK/TIS 或 App 专属注入的绕过方案。 + +## 6. 成功判定 + +ID-01 的“成功”不是剪贴板准备成功,也不是事件已 post,而是目标 App 中出现识别文本。 + +最小成功判定分级: + +- `verified_ax`:AX 写入后 readback 证明目标文本变化。 +- `verified_paste_readback`:自动 paste 后 AX readback 证明目标文本变化。 +- `verified_visual_qa`:目标无法 AX readback,但 QA 通过录屏 / 截图 / 目标可见文本证明自动上屏。 +- `posted_unverified`:只能作为开发诊断,不得作为最终 PASS。 +- `clipboard_only`:只表示 fallback 文本已准备,不算自动上屏成功。 + +日志要求: + +- 可记录 route、目标 bundle id / app name、role、错误码、耗时、权限状态。 +- 不得记录 transcript、用户输入内容、目标消息内容、窗口标题、文档标题、网页标题。 + +## 7. 测试矩阵 + +第一轮 Coder/QA 验收矩阵: + +| 场景 | 目标 | 验收信号 | +| --- | --- | --- | +| 标准可写控件 | TextEdit | 识别后自动上屏;优先 `verified_ax` 或 `verified_paste_readback` | +| Web 输入 | Arc textarea / contenteditable | 识别后自动上屏;允许 guarded auto paste;QA 可视证据必需 | +| Codex 输入框 | Codex 桌面 / Web 输入框 | 连续 5 轮自动上屏;中英文各覆盖 | +| WeChat | 微信消息输入框 | 中英文各 3 轮自动上屏;不得只停留在剪贴板 | +| 连续多轮 | TextEdit + Codex | 5 轮连续听写,文本顺序正确,无剪贴板污染 | +| 焦点切换 | 录音开始后切到另一个编辑目标 | 按产品策略:默认应报 `focus_changed` 并不自动输入;若允许切换则必须明确写入当前前台目标 | +| 无焦点 | Desktop / Finder / 无可写目标 | 明确失败,不写剪贴板、不自动 paste | +| Terminal 负向 | Terminal / shell | 明确 `terminal_blocked`,不得自动 paste / type | +| 权限缺失 | Accessibility / Input Monitoring / Microphone 任一缺失 | 明确提示缺失权限,不启动伪成功路径 | +| 剪贴板保护 | 用户中途改剪贴板 | 不覆盖用户新剪贴板;日志证明 skip restore | + +第二轮再扩展到 Slack/Discord/Notion/Google Docs/Microsoft Word 等复杂目标;不得在第一轮承诺所有 App 100% 成功。 + +## 8. 权限与用户引导 + +独立开发版的权限口径必须直接承认完整输入体验需要系统级能力: + +- Microphone:录音与本地 ASR。 +- Accessibility:读取当前 focused element、判断可写目标、必要时向系统发送自动输入事件。 +- Input Monitoring:后台全局热键 / 长按触发。 +- Background / Login Item:可选,仅用于后台常驻体验。 + +权限 onboarding 必须是分步 gate: + +1. 未授权时不启动录音伪流程。 +2. 每项权限显示用途和当前状态。 +3. 授权后必须有重新检测 / 重启热键监听能力。 +4. 权限撤销后必须降级为明确错误,而不是静默失效或只写剪贴板。 + +## 9. 签名、公证与分发门禁 + +独立开发版应走 Developer ID + notarized DMG,不走 MAS。 + +最小门禁: + +- 使用 Developer ID Application 证书签名 `.app` 与所有嵌入 dylib / helper / framework。 +- 开启 Hardened Runtime;只添加确实需要的 runtime exception。 +- 所有原生依赖、ASR dylib、Wails 相关二进制 Team ID / 签名链一致或满足 notarization 要求。 +- `notarytool` 公证通过,DMG stapled。 +- `spctl --assess --type execute` 对 `.app` 通过。 +- `codesign --verify --deep --strict --verbose=2` 对 `.app` 通过。 +- DMG / zip 有 SHA256 manifest、版本号、build 号、commit、分支、notarization 记录。 +- 首次发布前刷新 codebase-memory,并把 indexed commit / freeze commit 写入发布 manifest。 + +当前 `CODEGRAPH.md` 已声明 Developer ID / notarization 脚本为 `scripts/build-release-macos.sh`,但 ID-01 本轮不构建,后续 Coder 只能在实现切片后按发布门禁验证。 + +## 10. MAS 与 independent-developer 长期边界 + +MAS 路线和 independent-developer 路线必须长期分叉: + +- MAS 分支不得引入 Accessibility / Input Monitoring / CGEventTap / CGEventPost 自动输入作为核心路径。 +- independent-developer 分支可以保留完整全局热键与自动上屏能力,但不得反向合入 MAS。 +- 若需要复用代码,只允许复用无权限、无平台注入、无发布策略耦合的纯逻辑模块,例如 ASR、模型注册、设置 schema、通用 UI 组件。 +- 自动输入相关代码建议受 build tag 或目录边界保护,例如 `darwin_independent` / `!appstore`,并配静态扫描防止 MAS 构建误包含。 +- Review Notes、MAS 权限文案、MAS build scripts 不得从 independent-developer 路线拷贝。 + +## 11. 文件级实现计划 + +建议 Coder 下一棒只做“独立开发版自动上屏最小闭环”,不要顺手改模型、UI 大改、发布脚本或 MAS。 + +第一切片: + +- `internal/permissions/permissions_darwin.go` + - 保留 Microphone / Accessibility / Input Monitoring required。 + - 增加权限撤销后的明确错误码和重检入口。 + +- `internal/hotkey/hotkey_darwin.go` + - 保留 `CGEventTap` 全局监听。 + - 不恢复已证伪的“默认右 Command 单侧承诺”。 + - 热键以用户配置为准;默认值如需调整必须走产品口径。 + +- `internal/input/paste_darwin.go` + - 拆分当前 `Paste` 为可组合能力:clipboard snapshot/write/verify/restore、auto Cmd+V、可选 Unicode typing。 + - 在写剪贴板前加入 target guard;Terminal / secure field / no focus 一律不写剪贴板、不 post。 + - 保留用户中途改剪贴板不覆盖的保护。 + +- `internal/axinput/` 或 `internal/textoutput/` + - 新增 AX focused element 查询、可写性判断、direct insert、readback verification。 + - 新增 `AutoInsertRouter`,统一 AX direct insert 与 guarded auto paste。 + - 日志脱敏,不记录用户文本和目标标题。 + +- `app.go` / ASR 完成回调所在服务 + - 将识别结果送入 `AutoInsertRouter`。 + - 输出结果要能返回 route、verified 状态、错误码给前端。 + +- 前端权限 / 状态页 + - 显示权限缺失、目标不可写、Terminal blocked、focus changed、verification failed。 + - 可提供显式“复制到剪贴板”按钮,但不得替代自动上屏验收。 + +- `CODEGRAPH.md` + - 实现完成后由 Coder 更新新增模块、边界和验证命令。 + +## 12. 禁止事项 + +ID-01 及后续实现禁止: + +- 修改 MAS 分支或 MAS 项目。 +- 把手动 `Cmd+V` 定义为正常成功路径。 +- 对 Terminal / shell / console 自动 paste 或 type。 +- 在日志记录 transcript、目标消息、窗口标题、文档标题、网页标题。 +- 使用 AppleEvents / osascript / 私有 API / App 专属自动化绕过。 +- 重新押注 IMK/TIS。 +- 在未完成自动上屏 QA 前发布、打 tag、写 release manifest 或对外宣称稳定。 + +## 13. 风险 + +1. Accessibility / Input Monitoring 权限摩擦高,但这是非 MAS 自动上屏的必要代价。 +2. Electron / WebView / 自绘编辑器的 AX readback 不稳定,部分目标只能依赖 guarded auto paste + QA 可视证据。 +3. Terminal 自动输入风险高,必须默认阻断。 +4. 剪贴板 fallback 是自动路径的一部分,但仍有副作用风险,必须严控 restore 与用户中途修改。 +5. Developer ID notarization 可能被 dylib、hardened runtime、签名链、rpath 影响,需单独发布门禁验证。 +6. 不承诺所有 App 100% 成功;产品口径应承诺“主流可编辑输入框优先自动上屏,失败给明确错误与重试”,而不是静默降级。 + +## 14. 下一步归属 + +PMO 可派 Coder 进入 `independent-developer-auto-insert` 分支做第一切片,前提是继续遵守本文件边界。 + +Coder 最小验收: + +1. 权限缺失时明确失败。 +2. TextEdit 自动上屏 verified。 +3. Codex / WeChat / Arc 至少通过 guarded auto paste 自动上屏,不要求 AX direct insert 一定成功。 +4. Terminal 负向不自动 paste / type。 +5. 连续多轮和用户中途改剪贴板保护通过。 +6. 不改 MAS、不发布、不打 tag。 + +QA 最小验收: + +1. Codex、WeChat、TextEdit、Arc 四类目标真实自动上屏。 +2. 中文 / 英文、连续多轮、焦点切换、无焦点、Terminal 负向覆盖。 +3. 证据必须证明文字进入目标输入框,而不只是剪贴板存在内容。 + +最终架构结论: + +`ARCH_RESULT ID-01 PASS` + -- Gitblit v1.9.3