HTTP API:注意力推送(Web Push)
基于 main@306d253 的文档在 GitHub 上查看编辑此页
注意力推送(Web Push)
Section titled “注意力推送(Web Push)”| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/push | {enabled, available, reason?, publicKey?, subscriptions};publicKey 为 base64url 未压缩 P-256 VAPID 公钥,仅可用时出现 |
| POST | /api/push/subscriptions | {endpoint, expirationTime?, keys: {p256dh, auth}}(即 PushSubscription.toJSON(),expirationTime 接受但不保存)→ 200 {id};同一 endpoint 幂等返回原 ID;400 非法订阅,409 {error, reason} 或已满 32 台 |
| DELETE | /api/push/subscriptions/{id} | 204,ID 不存在同样 204 |
| POST | /api/push/test | 无请求体;同时向全部已登记设备发送测试通知并等待推送服务应答 → 200 {devices, accepted, removed: [订阅 ID], errors: [信息]};removed 为推送服务报告失效并已删除的订阅,errors 只含推送服务主机名;409 不可用 |
reason 取值:disabled([push] enabled = false)、storage(<state_dir>/push.json 不可读写)、quick_tunnel(地址每次启动都会变化,请改用 Cloudflare Named Tunnel 或 Tailscale Serve/Funnel 等稳定 HTTPS 入口)、unsupported_origin。
来源按请求 Origin 判定(没有时由 Host 推断,经隧道或 HTTPS 反向代理到达的视为 https):通过 Host/Origin 校验的 HTTPS 入口(例如托管 Tailscale Serve/Funnel、当前有效 Named 设置、allowed_origins 中的 HTTPS 条目)与 127.0.0.1 / ::1 / localhost 回环来源可以订阅。
来源规范化与不可订阅的来源:
- 非回环 HTTPS 来源规范化后作为通知
navigate锚点:省略:443,保留8443/10000等非默认端口,IPv6 字面量保留方括号。回环来源保留浏览器发送的 scheme 与 authority。 - 当前 Quick Tunnel 公网来源为
quick_tunnel(即使同时配置了 Named 主机名也不放行)。 - 非回环
http来源(VPN、局域网)与本机套接字为unsupported_origin。
订阅来源资格不等于公网 Host/Origin 授权:
- 手工
tailscale serve或其他 TLS 反向代理仍需把 HTTPS origin 加入allowed_origins,否则写请求本身会 403。 - 托管 Tunnel/Tailscale 的来源仍只在 ready 后授予,停止/失败或重配时撤销。
- 更改 Named 主机名、切换 Quick 或恢复配置默认后,后续请求立即使用新的有效来源判定,不继续使用启动时 TOML 的旧值。
服务端订阅会话列表并按以下规则推送:
- 以
(会话 ID, kind, since)去重。启动或重新订阅时快照中已有的 attention 只作为基线;同一信号的 source 变化、输出、spinner 与重连都不再推送;清除后再次置位或种类升级才推送。 - 只推送
[push] kinds中的种类;启发式waiting、attention 清除与进程退出不推送。 - 内部订阅积压断开后,按保留的基线比较新快照,补推断开期间新出现的信号一次;不重放断线期间已清除的信号。
- 500 ms 内置位又清除的信号不推送。
发送方式:
- 消息按 RFC 8291
aes128gcm加密、RFC 8292 VAPID 认证。 TTL为[push] ttl_seconds;question / permission / error 的Urgency为high,其余normal。- 单次请求 10 秒超时,不跟随重定向。推送服务返回 404 / 410 时删除订阅;其他失败只记日志(订阅 ID 与推送服务主机名)且不重试。
- 发送经 64 条有界队列与 2 个 worker,队满丢弃,不阻塞 PTY、会话列表或 HTTP。送达是尽力而为。
负载(≤ 4096 字节):
{ "web_push": 8030, "notification": { "title": "deploy", "body": "等待授权", "navigate": "https://term.example.com/s/abcdefgh", "tag": "webmux-abcdefgh", "silent": false }, "webmux": { "v": 1, "sessionId": "abcdefgh", "kind": "permission", "source": "agent", "since": "2026-01-01T00:00:00Z" }}title:去除控制字符、截断到 80 字符的会话标题;为空或include_title = false时为WebMux。body:按种类固定,形如“等待授权 · Agent 请求执行工具操作”。前半是与会话列表一致的短标签(需要回答、等待授权、本轮完成、出错停止、有通知、响铃提醒),后半是可执行的下一步。navigate:登记时的来源加/s/<ID>。- 负载不含 cwd、命令、预览、OSC 正文或终端输出。
- 测试通知:标题为
WebMux 测试通知,正文为若看到此消息,说明推送已生效,webmux为{"v": 1, "test": true}。
前端 Service Worker /sw.js 只处理 push 与 notificationclick,不拦截请求、不持有 Token;点击时只用 webmux.sessionId(须匹配 ^[a-z2-7]{8}$)与自身来源拼出 /s/<ID>,否则打开 /。