Skip to content

feat: 实现 system.notify 主动推送通知全链路#11

Merged
1186258278 merged 1 commit intoqingchencloud:mainfrom
youyli03:feature/system-notify
Mar 5, 2026
Merged

feat: 实现 system.notify 主动推送通知全链路#11
1186258278 merged 1 commit intoqingchencloud:mainfrom
youyli03:feature/system-notify

Conversation

@youyli03
Copy link
Contributor

@youyli03 youyli03 commented Mar 5, 2026

本次改动实现了 OpenClaw gateway 通过 node.invoke 向 ClawApp 推送系统通知的完整链路, 包含服务端 node 客户端注册、通知历史持久化、前端通知展示与页面刷新后恢复等功能。

================================================================================

Node 设备密钥

  • 新增 NODE_DEVICE_KEY_PATH = server/.node-device-key.json
  • 启动时自动生成或加载 Ed25519 node 设备密钥对(nodeDeviceKey / nodeDevicePrivateKey)
  • deviceId = SHA-256(pubkey raw bytes),持久化到文件避免重启后 deviceId 变化

通知历史持久化

  • NOTIFY_HISTORY_PATH = server/.notify-history.json
  • 启动时读取文件,过滤 24h 以前的记录
  • broadcastSystemNotify(payload) 每次写入前:push → 清 TTL 超期 → 限 50 条 → _saveNotifyHistory()
  • _saveNotifyHistory() 同步写文件,保证跨进程重启依然可命中

新增 HTTP 接口

  • GET /api/node-id → { ok, nodeId, nodeReady },供 AI 工具调用时查询 node 设备 ID
  • GET /api/notify-history?sid=... → { ok, items: [{...payload, _sentAt}] } · 校验 sid 有效性(401) · 过滤 24h 内的历史条目,按时序返回

SSE 事件处理

  • handleEvent() 新增分支: event === "system.notify" → handleSystemNotify(payload)
  • handleSystemNotify(payload): · 解析 title / body(兼容 message/text 字段)/ _sentAt · 调用 appendSystemNotifyItem(title, body, sentAt) · 调用 showNotification(title, {body, tag, renotify}) 弹出系统通知

实时通知 DOM 插入

  • appendSystemNotifyItem(title, body, sentAt): · 按 data-sent-at 去重 · 在 _typingEl 前插入 .msg.system-notify-item 元素(居中通知条样式) · HTML 结构:.sni-pill > .sni-title / .sni-body / .sni-time

历史加载重构 loadHistory()

  • 并行拉取:Promise.all([wsClient.chatHistory(), wsClient.notifyHistory()])
  • 统一在 clearMessages() 后渲染,避免多路径竞争
  • 渲染完所有聊天消息后调用 insertNotifyItemsInOrder(notifyItems)

通知权限 UI

  • import { requestPermission, isSupported as isNotifySupported } from './notify.js'
  • showSettings() 注入通知权限区块(id="notify-section")
  • renderNotifySection() 根据 Notification.permission 返回对应 HTML: · granted → ✓ 已开启(绿色)
    · denied → 已被拒绝(红色)
    · default → "开启通知权限" 按钮
    · 不支持 → 灰色提示文字
  • 点击按钮 → requestPermission() → 刷新区块 innerHTML

新增 i18n 键值(zh-CN + en 各 7 条):
settings.notify 通知 / Notifications
settings.notify.enable 开启通知权限 / Enable Notifications
settings.notify.granted 已开启提示 / Enabled — ...
settings.notify.denied 拒绝提示 / Blocked by browser — ...
settings.notify.unsupported 不支持提示 / Not supported in this browser
notify.ai.reply AI 助手 / AI Assistant
notify.media [媒体消息] / [Media message]

新增系统通知条样式:
.msg.system-notify-item 居中对齐,margin: 4px 0
.sni-pill 圆角胶囊,semi-transparent background,max-width:90%,flex-wrap
.sni-title font-weight:600,主色
.sni-body 次要色
.sni-time 11px,opacity:0.6,不换行

新增通知权限状态样式:
.settings-notify-status 基础文字样式
.settings-notify-status.ok 绿色(--primary)
.settings-notify-status.warn 红色(--danger)
.settings-notify-status.muted 灰色

新增忽略规则:
server/.node-device-key.json — 私钥文件,不应提交
server/.notify-history.json — 运行时状态,不应提交

b95d451ed0f226b34bf6b0331f502df3
JUYY$T K@((WN_$)Q50LNQ

本次改动实现了 OpenClaw gateway 通过 node.invoke 向 ClawApp 推送系统通知的完整链路,
包含服务端 node 客户端注册、通知历史持久化、前端通知展示与页面刷新后恢复等功能。

================================================================================

Node 设备密钥
- 新增 NODE_DEVICE_KEY_PATH = server/.node-device-key.json
- 启动时自动生成或加载 Ed25519 node 设备密钥对(nodeDeviceKey / nodeDevicePrivateKey)
- deviceId = SHA-256(pubkey raw bytes),持久化到文件避免重启后 deviceId 变化

通知历史持久化
- NOTIFY_HISTORY_PATH = server/.notify-history.json
- 启动时读取文件,过滤 24h 以前的记录
- broadcastSystemNotify(payload) 每次写入前:push → 清 TTL 超期 → 限 50 条 → _saveNotifyHistory()
- _saveNotifyHistory() 同步写文件,保证跨进程重启依然可命中

新增 HTTP 接口
- GET /api/node-id → { ok, nodeId, nodeReady },供 AI 工具调用时查询 node 设备 ID
- GET /api/notify-history?sid=... → { ok, items: [{...payload, _sentAt}] }
  · 校验 sid 有效性(401)
  · 过滤 24h 内的历史条目,按时序返回

SSE 事件处理
- handleEvent() 新增分支: event === "system.notify" → handleSystemNotify(payload)
- handleSystemNotify(payload):
  · 解析 title / body(兼容 message/text 字段)/ _sentAt
  · 调用 appendSystemNotifyItem(title, body, sentAt)
  · 调用 showNotification(title, {body, tag, renotify}) 弹出系统通知

实时通知 DOM 插入
- appendSystemNotifyItem(title, body, sentAt):
  · 按 data-sent-at 去重
  · 在 _typingEl 前插入 .msg.system-notify-item 元素(居中通知条样式)
  · HTML 结构:.sni-pill > .sni-title / .sni-body / .sni-time

历史加载重构 loadHistory()
- 并行拉取:Promise.all([wsClient.chatHistory(), wsClient.notifyHistory()])
- 统一在 clearMessages() 后渲染,避免多路径竞争
- 渲染完所有聊天消息后调用 insertNotifyItemsInOrder(notifyItems)

通知权限 UI
- import { requestPermission, isSupported as isNotifySupported } from './notify.js'
- showSettings() 注入通知权限区块(id="notify-section")
- renderNotifySection() 根据 Notification.permission 返回对应 HTML:
  · granted  → ✓ 已开启(绿色)
  · denied   → 已被拒绝(红色)
  · default  → "开启通知权限" 按钮
  · 不支持   → 灰色提示文字
- 点击按钮 → requestPermission() → 刷新区块 innerHTML

新增 i18n 键值(zh-CN + en 各 7 条):
  settings.notify               通知 / Notifications
  settings.notify.enable        开启通知权限 / Enable Notifications
  settings.notify.granted       已开启提示 / Enabled — ...
  settings.notify.denied        拒绝提示 / Blocked by browser — ...
  settings.notify.unsupported   不支持提示 / Not supported in this browser
  notify.ai.reply               AI 助手 / AI Assistant
  notify.media                  [媒体消息] / [Media message]

新增系统通知条样式:
  .msg.system-notify-item  居中对齐,margin: 4px 0
  .sni-pill                圆角胶囊,semi-transparent background,max-width:90%,flex-wrap
  .sni-title               font-weight:600,主色
  .sni-body                次要色
  .sni-time                11px,opacity:0.6,不换行

新增通知权限状态样式:
  .settings-notify-status        基础文字样式
  .settings-notify-status.ok     绿色(--primary)
  .settings-notify-status.warn   红色(--danger)
  .settings-notify-status.muted  灰色(--text-secondary)

新增忽略规则:
  server/.node-device-key.json  — 私钥文件,不应提交
  server/.notify-history.json   — 运行时状态,不应提交
@1186258278 1186258278 merged commit 83318cd into qingchencloud:main Mar 5, 2026
@1186258278
Copy link
Contributor

感谢 @youyli03 的精彩贡献!🎉 system.notify 全链路实现得非常完整,从服务端 node 注册、通知持久化到前端展示和权限管理,代码质量很高。连续 6 个 PR 为 ClawApp 带来了大量修复和新功能,非常感谢你的付出!已合并 ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants