Skip to content

Latest commit

 

History

History
329 lines (228 loc) · 15.3 KB

File metadata and controls

329 lines (228 loc) · 15.3 KB

AgentPulse 优化 Plan

目标:把 AgentPulse 从"私人工具"推进到"可分发、可信任、可维护"的产品形态,同时加固我们相对 Happy/HAPI 的差异化护城河(macOS 桌面 app observer + 自定义 agent + iOS 原生体验)。

一句话策略:先堵上安全/工程两个硬伤(P0),再夯实当前已有功能的可靠性(P1),最后做平台扩张和深度差异化(P2+)。不要在基础不稳的时候追求新功能。


阶段 0:不可推迟的基础(P0,2-3 周)

没有这些,项目无法分发给任何非本人的用户。

0.1 加 LICENSE

现状:无 LICENSE 文件,任何人法律上都不能使用我们的代码。 做什么:顶层加 LICENSE(推荐 MIT,和 Happy 一致;如果想学 HAPI 限制闭源商用则用 AGPL-3.0)。README 声明 license。 验证:GitHub repo page 自动识别 license。

0.2 WebSocket/REST 认证(配对机制)

现状:relay 监听 0.0.0.0:8350,WS 连上就能 sendInput 控制用户的 Claude/Codex。任何同网络的设备都能打。 做什么

  • relay 启动时生成 ~/.agentpulse/pairing-secret(UUID),CLI 同时输出 QR code
  • iOS 扫 QR → 解析为 agentpulse://pair?host=192.168.x.x&port=8350&secret=xxx
  • WS 握手 + REST 请求强制带 Authorization: Bearer <secret>;不通过的拒绝
  • mDNS 只广播 host/port,不广播 secret

验证

  • 删除 secret 后 iOS 显示"未配对"而非默默连不上
  • 旁路设备 curl http://host:8350/api/v1/status 返回 401

关键文件

  • relay-server/src/auth/pairing.ts(生成 + 校验 secret)
  • relay-server/src/auth/ws-auth.ts 插在 setupWebSocket
  • AgentPulse/Views/SettingsView.swift 加"扫码配对"按钮
  • ConnectionManager.swift 握手时发 Authorization header

0.3 最小 CI + Typecheck

现状:无 .github/workflows/,代码改了不知道编不编得过。 做什么

  • .github/workflows/ci.yml:pnpm install → npx tsc --noEmit(relay + bridge) → SwiftLint(iOS)
  • PR 必须过才能 merge(Branch Protection) 验证:故意提一个语法错误的 PR,CI 报红。

0.4 核心单测起步(先不追 100% 覆盖率)

现状:零测试。Happy 93、HAPI 102。 做什么:用 Vitest(和 tsx 兼容),对最易回归的 3 块先写测试:

  • relay-server/src/utils/session-reader.test.ts:解析 Claude JSONL(fixture 来自真实 session 片段),覆盖 Thinking / ToolUse / UserPrompt / EndTurn 四种
  • relay-server/src/utils/codex-session-reader.test.ts:Codex JSONL 解析
  • relay-server/src/utils/approval.test.ts:input delivery chain 的 fallback 顺序(mock sub-deps)

目标:30 个测试起步,CI 跑 < 30s。后面每修一个 bug 加一个对应的回归测试("fix 驱动测试")。

0.5 Hook 脚本输入 escaping 安全审计

现状approval.ts:248 把用户输入拼进 osascript 和 shell 命令。即使用 JSON.stringify/replace escape 了,走的路径多,易漏。 做什么

  • 列出所有走 execAsync 的用户输入入口,统一过一个 escapeForShell() / escapeForAppleScript() helper
  • 加一个 test fixture:输入 "; rm -rf ~/ "$(whoami)、``` ` `` 等常见 payload,确保不被命令注入 验证:renamed test 通过,加到 CI。

阶段 1:当前功能稳定性(P0/P1,3-4 周)

修现在还在犯的错、补之前遗留的坑。

1.1 远程 agent 同步(已规划未实现)

现状:SSH 隧道通了,但 relay 不会去远端 fetch agent 列表。iOS 上点远程 session 直接弹回主页(因为 connectionManager.agents 里没有)。 做什么

  • relay 启动时读 ~/.agentpulse/remote-hosts.json,对每个 connected host 起一个轮询 task(3-5s)
  • 轮询 http://localhost:<tunnelPort>/api/v1/status,把 agents 加 remote-{hostId}- 前缀 merge 进本地 agentStore
  • host 断开/删除时清理对应 agents
  • iOS AgentListView 增加 "Remote" section 分组显示

验证

  • 开发机跑 relay → 本机 relay 自动同步 → iOS 看到 remote-xxx-claude-code-123
  • 远端 relay 挂掉 → iOS 5s 内看到 agent 消失
  • 点击远程 agent 能进入对话界面(不再 boomerang)

关键文件

  • relay-server/src/remote/remote-sync.ts
  • relay-server/src/index.ts 里在 autoConnectHosts() 后挂钩
  • ConnectionManager.swift 的 snapshot 处理不需要改——只要 agent 在 store 里就行

1.2 iOS 点击 session 不要静默 dismiss

现状AgentListView.swift:57-70 sheet binding 在 agents.first { $0.agentId == id } 返回 nil 时自动关闭,用户体验是"点了没反应"。 做什么

  • 绑定 fallback 到 placeholder AgentStatusstatus: .completed, agentName: "Session no longer available"),显示友好提示 + "Dismiss" 按钮
  • 保留最近 5 个已消失 agent 的 stub 30s,让进入聊天界面看历史消息

验证:关掉一个 Claude 的 Terminal → iOS 马上点它能看到"session 已结束,这是历史对话"而不是回主页。

1.3 输入失败要告诉 iOS"为什么"

现状websocket.ts:82 成功就广播 "Input delivered via X",失败只抛一个干巴巴的 error。iOS 绿对勾 ≠ 真送达,已经坑到用户两次。 做什么

  • sendInput 返回扩展到 { method, reliable: boolean, warning?: string }
  • reliable=false 的情况(osascript-paste 无法验证焦点、bridge 无 ACK)在 iOS 显示橙色叹号 + 悬浮提示"本次通过 osascript 发送,若未收到请手动激活对应窗口后重试"
  • 已经有 isUncertainDelivery() 在 AgentChatView.swift:334,扩展它

验证:关掉 Codex Desktop 发一条消息 → iOS 显示橙警告而不是绿对勾。

1.4 Bridge 多窗口问题彻底解决

现状:每个 VS Code 窗口一个 extension host,8348 只有第一个抢到;其他窗口的 session 通过 AXRaise 能绕过来,但如果目标 session 不在监听 bridge 那个窗口里,还是会失败。 做什么

  • 每个 bridge 端口递增尝试:8348, 8349, 8350...(最多 20 个)
  • bridge 启动时往 ~/.agentpulse/bridges.json{port, workspace, pid, windowTitle}
  • relay sendInput 时根据 session 的 cwd 匹配 bridges,POST 到正确端口
  • bridge 退出时 unregister

验证:开 3 个 VS Code 窗口,每个有 Claude session,iOS 分别发消息,每条都进对应 session。

1.5 Cursor 回复观测已接入的边界情况

现状:SQLite 扫 bubbleId 工作正常,但 Cursor 重启后 DB WAL 状态、用户切 workspace、多 Cursor 窗口都可能漏。 做什么

  • 测试 workspace 切换时的行为(希望:老 workspace 的 bubble 继续被读,新 workspace 启动后也开始读)
  • 多 Cursor 窗口场景下 pane 归属(iOS 上哪个 cursor-app-* 收到回复)

验证:操作上述两种场景 30 分钟,回复不丢、不串到错误 agent。

1.6 WebSocketClient Swift 6 兼容

现状:Xcode 提示 webSocketTask 不 Sendable。当前是 warning 但 Swift 6 会变 error。 做什么:把 WebSocketClient 改成 actor,或者 @unchecked Sendable 且显式加锁。前者更正确。 验证:切到 Swift 6 模式编译通过。


阶段 2:测试 + 观测基建(P1,2 周)

让改代码不再提心吊胆。

2.1 测试分层

  • 单元(Vitest):utility 函数、parser、delivery chain 逻辑。目标 200+ 测试。
  • 集成(新):用 supertest 启动整个 express + WS,mock 掉 execAsync/fs,跑端到端的 "iOS → relay → response" 场景。
  • Swift 单元:XCTest 跑 ChatMessage 过滤、ConnectionManager 的消息路由逻辑(不需要真连 WS)。

2.2 结构化日志

现状console.log 散落 300+ 处,字符串拼接。调试全靠 grep。 做什么

  • pino(快),LOG_LEVEL 环境变量控制
  • 关键事件(agent 发现、输入投递、WS 连断)走结构化 log:logger.info({ event: 'input-delivered', agentId, method, latencyMs })
  • console.log 保留但标注废弃

2.3 开发者 simulator 完善

现状tools/simulate-ios-app.ts 存在但用法不明。 做什么:加一个 npm run simulate 打开 CLI,可以交互式:

  • 列出 relay 检测到的 agents
  • 向某 agent 发消息
  • 模拟 approve/deny 这让我们不用装 Xcode 就能回归测试 relay。

阶段 3:安全深化(P1,3-4 周)

阶段 0 的 token 认证是及格线,这里做到"可信"。

3.1 应用层 E2EE(学 Happy)

场景:用户通过 cloudflared 把 relay 暴露公网,或者在不信任的 WiFi 用 SSH 隧道。 做什么

  • 配对时除了 secret,再生成一个 ECDH 共享密钥(Curve25519)
  • 所有 input/approve/focus payload 用 AES-256-GCM 加密
  • relay 在本机解密,但如果走中转隧道,隧道看不到明文
  • 参考 Happy 的 docs/encryption.md 实现

非目标:不做 zero-knowledge server(我们没有 server)。

3.2 Hook socket 权限收紧

现状/tmp/agentpulse-{uid}.sock 用 uid 隔离,但 tmp 路径本身是广开放的。 做什么:move 到 ~/.agentpulse/run/hook.sock,显式 chmod 600

3.3 审计日志

做什么:所有 iOS 发起的写操作(sendInput、approve、interrupt)写到 ~/.agentpulse/audit.log(append-only),包含时间戳 + agentId + 操作类型 + 来源 IP。便于事后溯源。


阶段 4:平台扩张(P2,4-6 周)

4.1 PWA / Web 客户端(优先级最高)

为什么:一次开发覆盖 Android、macOS 浏览器、Windows、Linux。Cost < Native Android。 技术栈:React 19 + Vite + Tailwind(和 HAPI 的 web/ 类似),Service Worker 做离线缓存。 做什么

  • 新增 web/ workspace
  • 复用 relay 现有的 REST/WS API,不改 protocol
  • 核心页面:Agent List、Chat、Settings(配对通过手动输入 host + secret,没有 QR 扫描也 OK)
  • 打包为 PWA(manifest.json + SW),可"Add to Home Screen"
  • Relay 增加 GET / 托管 web bundle(可选)

验证:在 Chrome 开 localhost:8350 能用,Android 上能 Install to Home Screen 当原生 app 用。

4.2 推送通知(iOS 已有,扩展到 PWA)

做什么

  • Web Push(VAPID keys + service worker subscription),接入 relay
  • 触发场景:agent 进入 waitingForApproval / waitingForInput / 完成一个长任务

4.3 TestFlight 分发 iOS

做什么

  • Apple Developer Program 账号($99/yr)
  • GitHub Actions 自动 build + upload
  • 避免要求用户本机 Xcode

阶段 5:差异化加深(P2,4-6 周)

我们已经在独占跑道上,跑得更远。

5.1 更多桌面 AI app 支持

现状:已有 Claude Code、Codex Desktop、ChatGPT Atlas、ChatGPT Desktop、Claude Desktop、Cursor。 做什么

  • Windsurf(Codeium 的 IDE)—— Electron-based,大概率和 Cursor 类似套路
  • Zed —— 有 AI 助手,SQLite 存,查一下
  • Jupyter + Copilot Chat —— 想都别想,可能需要 iframe AX 魔法
  • 每个新 app 开一个 adapter 文件 + detection 规则

5.2 Custom Agent Scripting(用户写插件)

现状custom-cli-adapter.ts 只能正则匹配进程名。 做什么:用户能写 JS 插件注册 agent,暴露钩子:

// ~/.agentpulse/plugins/my-agent.js
module.exports = {
  name: 'my-agent',
  detectProcess: (proc) => proc.command.includes('my-cli'),
  readOutput: async (pid) => { /* read some file */ return messages },
  sendInput: async (pid, text) => { /* write to some pipe */ }
}

风险:代码执行不受 sandbox,需警告用户。

5.3 统一 session envelope(学 Happy)

现状AgentStatus 是扁平结构,不同 agent 的工具调用/thinking/approval 复用度低。 做什么:定义 v2 protocol agent:tool-call-start / agent:stream-delta / agent:turn-end 等事件类型,向后兼容 v1。 收益:Codex、Cursor、Gemini 的回复 UI 可以共用;将来支持更多 agent 时添加简单。

5.4 Web 终端(xterm.js)

做什么:当 agent 运行在 cmux/tmux 中,web 客户端可以直接嵌入 pty 流,用户能看到终端完整输出。 复用:HAPI 的 hub/src/terminal/ 是好参考。

5.5 语音输入(ElevenLabs 或 Whisper.cpp)

做什么:iOS/Web 按住麦克风 → 转文字 → 作为 input 发送。ElevenLabs 质量高但付费,Whisper.cpp 本地但模型大。 优先级:在用户明确表达需求前先放一边。


阶段 6:可选的 Wrapper 模式(P3,探索性)

一些场景 observer 做不到,提供可选 wrapper 增强。

6.1 agentpulse claude 包装命令

做什么:类似 happy/hapi 的做法,但是可选。用户可以继续裸跑 claude(observer 模式),或者 agentpulse claude(wrapper 模式获得 SDK 级可靠发送)。 收益

  • osascript 失败场景的硬退路
  • Accessibility 权限不需要了
  • pty stream 可以直接广播给 web 终端

6.2 跨 session 搜索

做什么:把每个 session 的 JSONL 索引到本地 SQLite FTS5,iOS/Web 可以全文搜"上次改 auth 相关是哪个 session"。 成本:中,但 Happy 都没做好,是差异化机会。


优先级总结(6 个月视角)

阶段 时间 核心产出 可分发?
0 基础 2-3w LICENSE、配对认证、CI、初始测试 ⚠️ 可给少数人 beta
1 稳定性 3-4w 远程同步、错误透传、bridge 多窗口 ✓ alpha 用户
2 测试/观测 2w 测试完善、日志、simulator ✓ 内部闭合
3 安全 3-4w E2EE、audit log ✓ 公开可用
4 平台扩张 4-6w PWA、TestFlight ✓ 广泛分发
5 差异化 4-6w 更多桌面 app、custom plugin、v2 protocol ✓ 有护城河
6 可选 wrapper 模式、搜索、语音 按需

总计:~5-6 个月到"公开可分发 + 差异化"。


不做 / 暂不做的事

  • 不做中央 server(Happy 的路):违背"本机优先"定位,维护成本高
  • 不抢 CLI wrapper 赛道(HAPI 的路):他们先发,我们的 observer 是互补品而非替代
  • 不移植 Electron / Tauri 桌面端:用户已有 iOS+PWA 就够覆盖,成本不值
  • 不做社交/分享功能:和产品定位无关

每周节奏建议

  • 每周一:从 plan 挑 2-3 个 issue,开 GitHub issue 追踪
  • 每个 merge 必带:(a) 测试 (b) 日志 (c) 文档更新
  • 每月一次:回顾 plan,调整优先级

附:当前已完成的项目(本次 session 截止)

  • ✅ Codex Desktop UI scraper(AX tree walker 带错误恢复)
  • ✅ Cursor 回复读取(SQLite bubble tail + 去重)
  • ✅ iOS chat view 自动激活 native app(focus WS 消息)
  • ✅ Bridge 多窗口初步修复(AXRaise workspace 匹配)
  • ✅ Budget UI(手动设置 + 24h/7d 图表修复)
  • ✅ Tmux session 名打标([tmux:wjy] 前缀)
  • ✅ Detached tmux 消息投递(tmux send-keys 代替 TTY write)

这些都是 plan 要巩固的基础,下一步应该给每一项加上自动化测试,固化回归能力。