场景
agent 卡在「要不要继续?」上, 等了你 40 分钟。
完成通知在这个场景里毫无用处——任务根本没完成。你需要的不是「看到」,是「回复」。
00:00
你让 agent 跑一个大重构,然后去吃饭。
00:03
它停在 Do you want to proceed? (y/n) 上。
00:43
你回到电脑前。40 分钟,什么都没发生。
产品模型
两条通道
一条负责告诉你发生了什么,一条负责让你把话说回去。
emit
事件通知,零开销
以 hook 的形式挂在工具里,只在离散事件(需要确认、任务结束、出错)触发时推送一条通知。不常驻、不拦截输出。
run
PTY 包裹,实时双向
用伪终端包住你的命令,把整屏输出同步到手机;你在手机上打的字会原样写回本地终端,和坐在电脑前敲键盘没有区别。
joint-notify run --tool claude-code -- claude │ ├── PTY ──▶ @xterm/headless ──▶ 当前这一屏 ──▶ 手机 │ └────────────── 你在手机上输入的按键 ◀──────────────
兼容性
支持你在用的那一个
hook 会写进各自的配置文件;而 run 本来就能包裹任意命令行工具。
Cursor
~/.cursor/hooks.json
- 支持完成通知
- 支持实时会话
- 覆盖风险:追加写入,已有 hook 不受影响
Claude Code
~/.claude/settings.json
- 支持完成通知
- 支持实时会话
- 覆盖风险:会修改 settings.json,建议先备份
Codex CLI
~/.codex/config.toml
- 支持完成通知
- 支持实时会话
- 覆盖风险:可能覆盖已有的 notify 命令,安装前请确认
任意 CLI 命令
无需配置文件
- 不提供事件通知
- 支持实时会话
- 覆盖风险:无覆盖风险
技术细节
为什么手机上看着不乱
桌面端先用 @xterm/headless 把原始字节流解释成「当前这一屏」,再同步到手机。进度条、spinner、TUI 是刷新后的画面,不是一堆重复行。
朴素 ANSI 剥离
⠋ Building 12% ⠙ Building 27% ⠹ Building 48% ⠸ Building 61% ⠼ Building 88% ⠴ Building 100%
Joint
⠴ Building 100% ✔ 12 files changed Do you want to proceed? (y/n) ▍
安全模型
把权限边界摆在明面上
你装之前一定会想这个问题,所以我们不把它藏进文档深处。
我们做了什么
- 配对验证码只存 HMAC 摘要
- 验证码 5 次猜错即作废
- 按 IP 限流
- 设备令牌可随时吊销
- 会话之间按 sessionId 隔离
边界在哪
- 手机账号 = 你所有已配对机器上正在运行的 run 会话的键盘
- run 会话内可以执行任意输入
- 只有你主动 run 起来的会话会被同步
还没做的
- 生产环境必须自行配置 HTTPS/WSS
- 用户令牌 180 天有效期,暂无服务端吊销名单