npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yoooclaw/phone-notifications

v1.12.7

Published

后台同步手机通知数据到本地存储,供 Agent 查询消费

Readme

@yoooclaw/phone-notifications

OpenClaw / QClaw 手机通知同步插件:接收手机通知并写入本地 JSON 文件系统,供 Agent 查询和消费。

工作方式

插件支持两种接入模式:

  1. Gateway Native(默认)
  • 手机端通过 gateway 已有连接调用 notifications.push
  • 插件通过 registerGatewayMethod 接收 params.items
  1. HTTP 备选模式
  • 开启 httpEnabled=true 后,注册 POST /notifications
  • 通过 Authorization: Bearer <apiKey> 鉴权

存储设计

通知按以下目录结构写入(JSON):

<workspace>/notifications/
├── 2026-03-02.json
├── 2026-03-03.json
└── 2026-03-04.json

补充规则:

  • workspaceDir 可用时,根目录是 <workspace>/notifications
  • workspaceDir 不可用或不可写时,回退到 <stateDir>/plugins/phone-notifications/notifications
  • 通知写入 YYYY-MM-DD.json 的 JSON 数组(append-only)
  • 通知条目基础字段为 appNametitlecontenttimestamp(ISO 8601 含时区)
  • 飞书消息落库时会对齐微信语义:群聊写成 title=群名content=发送人: 正文;私聊保持 title=发送人
  • 部分应用会补充结构化字段;例如飞书消息会额外写入 senderNameconversationTypegroup/private)、conversationName
  • 同一天内按 notification-id 去重(使用内部索引文件)
  • 手机通知永久保存,不做过期清理

图片同步

App 通过 images.sync(Gateway Native)或 POST /images(HTTP 备选)下发图片元数据,图片二进制走 OSS;插件在后台下载原图落盘。

接入要点:

  • 请求体为 { imageId, image }image 至少包含 oss_image_urlcreated_at,可附带 mime_type / width / height / size_bytes / source_app / caption
  • 同步即时返回 status=syncing,下载在后台进行;同一 imageId + 同 URL 已 synced 时直接去重返回
  • 单张下载大小上限默认 20MB,可通过配置 image.maxBytes 调整
  • 存储结构(与录音同级,优先 stateDir,回退 workspace):
<storageDir>/images/
├── files/            # 下载的原图,<imageId>.<ext>(扩展名由 mime_type 推断)
└── index.json        # 元数据索引({ images: [] })

Agent 侧通过 CLI 查询消费:

  • ntf image list [--status <s>] [--app <app>] 列出图片
  • ntf image status <id> 查看单张详情
  • ntf image path <id> 打印本地文件绝对路径(仅 synced 可用)
  • ntf image storage-path 打印图片存储目录

前置要求

  • Node.js >= 22.12.0
  • OpenClaw >= 2026.3.28,或兼容其插件 ABI 的 QClaw 宿主

可选:长录音音频缓存 / 解码工具

新链路中 ASR 和总结由 App 下发,插件不再推荐本地 Whisper 转写。若需要在本机播放、导出或处理硬件 OGG/Opus 音频,可按需安装:

# opus-tools(推荐):解码硬件录音的 OGG/Opus 格式
# 若已安装 ffmpeg 则可跳过
brew install opus-tools

说明

  • 硬件录音以 Opus 编码、OGG 容器格式传输。opusdec(来自 opus-tools)或 ffmpeg 任意一个可用即可完成格式转换。
  • asr.mode = "local" 已停用,仅保留枚举兼容旧客户端。

宿主兼容性

  • OpenClaw:可直接使用全局 openclaw CLI 安装、更新、验证和执行插件命令。
  • QClaw:插件 ABI 与 OpenClaw 兼容,但宿主目录、配置入口和 CLI 管理方式不同。QClaw 桌面应用通常不提供全局 openclaw 命令,而是由应用内置 wrapper 调用 OpenClaw runtime。
  • 一键安装脚本会自动探测宿主状态目录,优先读取 ~/.qclaw/qclaw.json;如果你的变体产品使用 ~/.qclow/qclaw.json,脚本和插件也会兼容。
  • QClaw 下不支持本脚本自动执行 gateway restart / Tailscale 配置。安装完成后如插件未自动生效,请重启 QClaw 桌面应用。

安装

通过 OpenClaw CLI 安装(OpenClaw)

openclaw plugins install @yoooclaw/phone-notifications

更新

openclaw plugins update @yoooclaw/phone-notifications

通过一键脚本安装(推荐给 QClaw,备选给 OpenClaw)

适用于无法使用 npm 源的环境,或宿主不是标准 OpenClaw CLI 的场景。脚本从 CDN 获取插件包并自动注册。

curl -fsSL https://artifact.yoooclaw.com/plugin/install.sh | bash
& ([scriptblock]::Create((irm https://artifact.yoooclaw.com/plugin/install.ps1)))

PowerShell 入口用于 OpenClaw 与 EClaw;ArkClaw / JvsClaw 只提供 macOS / Linux shell 入口。

EClaw Windows 入口:

& ([scriptblock]::Create((irm https://artifact.yoooclaw.com/plugin/install-eclaw.ps1)))

脚本会自动探测宿主:

  • OpenClaw:默认使用 ~/.openclaw
  • ArkClaw:暂与 OpenClaw 使用相同安装路径和重启策略,入口会写入 PHONE_NOTIFICATIONS_CLAW_PROFILE=arkclaw
  • QClaw:优先读取 ~/.qclaw/qclaw.json
  • JvsClaw:使用专用入口,默认写入可写目录 /home/admin/.openclaw
  • EClaw(Ecloud AI Assist):使用专用入口,默认写入 ~/.eclaw
  • 兼容变体:如存在 ~/.qclow/qclaw.json 也会识别

显式渠道入口:

curl -fsSL https://artifact.yoooclaw.com/plugin/install-openclaw.sh | bash
curl -fsSL https://artifact.yoooclaw.com/plugin/install-arkclaw.sh | bash
curl -fsSL https://artifact.yoooclaw.com/plugin/install-jvsclaw.sh | bash
curl -fsSL https://artifact.yoooclaw.com/plugin/install-eclaw.sh | bash

JvsClaw 环境下不要写只读的 ~/.openclaw 基线目录,使用:

curl -fsSL https://artifact.yoooclaw.com/plugin/install-jvsclaw.sh | bash

JvsClaw 宿主可能把 JVSCLAW_STATE_DIR 指向只读目录(例如 /opt/jvs-claw)。 插件状态默认统一写入 /home/admin/.openclaw;如需覆盖可写目录,使用 PHONE_NOTIFICATIONS_STATE_DIR;如需单独覆盖配置文件,使用 PHONE_NOTIFICATIONS_CONFIG_PATH。不要改写宿主路径变量:

curl -fsSL https://artifact.yoooclaw.com/plugin/install-jvsclaw.sh |
  PHONE_NOTIFICATIONS_STATE_DIR=/custom/writable/openclaw bash

指定版本安装

curl -fsSL https://artifact.yoooclaw.com/plugin/install.sh | bash -s -- --version 1.2.3
& ([scriptblock]::Create((irm https://artifact.yoooclaw.com/plugin/install.ps1))) --version 1.2.3

带 API Key 安装

安装时同时写入灯控鉴权 API Key(写入 <stateDir>/credentials.json):

curl -fsSL https://artifact.yoooclaw.com/plugin/install.sh | bash -s -- --api-key ock_xxx
& ([scriptblock]::Create((irm https://artifact.yoooclaw.com/plugin/install.ps1))) --api-key ock_xxx

使用本地包安装

bash install.sh --tgz-url ./yoooclaw-phone-notifications-1.2.3.tgz

全部选项

| 选项 | 说明 | 默认值 | |---|---|---| | --version <ver> | 指定版本号 | 自动获取最新版本 | | --tgz-url <url\|path> | 直接指定插件包地址(跳过版本检测) | - | | --target-dir <path> | 安装目录 | <stateDir>/extensions/phone-notifications | | --state-dir <path> | 宿主状态目录 | 自动探测 | | --config-path <path> | 宿主配置文件路径 | <stateDir>/openclaw.json | | --channel <openclaw\|arkclaw\|jvsclaw> | 写入运行渠道 | 渠道脚本自动传入;直接运行核心时不写入,默认 openclaw | | --host-cli <cmd> | 宿主 CLI 命令名 | openclaw | | --api-key <apiKey> | 灯控鉴权 API Key | - | | --token <token> | 兼容旧参数,等价于 --api-key | - |

脚本做了什么

  1. 自动探测宿主状态目录和配置文件路径,并提前检查安装目录、配置文件、凭据文件的写入权限
  2. 从 CDN 下载最新版本的插件 tgz 包(或使用你指定的版本/地址)
  3. 解压并验证包完整性(检查 package.json、入口文件可加载)
  4. 备份旧版本(如有),原子替换到 <stateDir>/extensions/phone-notifications
  5. 写入 <configPath> 配置:
    • plugins.entries — 启用插件,合并用户配置
    • tools.allow / tools.alsoAllowagents.main.tools.* — 根据插件 contracts.tools 自动放行工具
    • 若宿主已启用 plugins.allow 白名单,会自动补入 phone-notifications,确保插件能被宿主加载
    • 清理该插件旧的 plugins.load.paths / 旧的 plugins.installs 安装记录(该字段已被宿主废弃,插件不再读写),避免继续命中之前通过 openclaw plugins install--link 保留的副本
  6. 如传入 --api-key / --token,写入 <stateDir>/credentials.json
  7. OpenClaw 下尝试执行 gateway restart,并验证 ntf / phone-notifications 命令是否可用;JvsClaw 下触发 gateway restart 后跳过即时 CLI 自检,避免平台 RPC 恢复期间误报;QClaw 下不会主动重启宿主进程
  8. 安装失败时自动回滚到旧版本

如果你的宿主显式配置了 plugins.deny,且其中包含 phone-notifications,安装脚本会中止并提示先移除 denylist 项,不会写入一个无法加载的安装结果。

验证安装

OpenClaw

以下命令适用于带全局 openclaw CLI 的标准 OpenClaw 宿主:

ls ~/.openclaw/extensions/phone-notifications

~/.openclaw/plugins 主要是运行时数据目录或旧插件目录;这里没有 phone-notifications 不代表安装缺失。安装脚本的目标目录是 ~/.openclaw/extensions/phone-notifications

openclaw ntf --help
openclaw ntf --version

如果环境里已有其他插件占用了 ntf,可改用不冲突的别名:

openclaw phone-notifications --help
openclaw phone-notifications -v

QClaw

  • QClaw 通常没有全局 openclaw 命令。
  • 插件安装完成后,如未自动生效,请先重启 QClaw 桌面应用。
  • 如需执行 OpenClaw CLI 子命令,请使用 QClaw 自带的 wrapper;命令语义与本文档中的 openclaw ... 子命令保持一致,但入口由 QClaw 提供。

配置

以下命令以 OpenClaw 为例。QClaw 下请通过其自带 wrapper 执行同样的 OpenClaw 子命令。

安装脚本会自动写入必要的插件注册配置。如需手动调整:

# 可选:忽略特定 app
openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["com.android.systemui"]'

配置项说明:

  • ignoredApps?: string[]
  • relay.heartbeatSec?: number:Relay 心跳发送间隔,默认 10
  • relay.heartbeatTimeoutSec?: number:多久没有收到 Relay 入站数据后判定断线,默认 90
  • relay.reconnectBackoffMs?: number:断线后的基础重连退避,默认 2000 毫秒

长录音 & ASR 配置

插件保持原有录音同步入口不变:App 仍通过 recordings.sync 下发录音元数据和 oss_audio_url,音频二进制仍走 OSS。新版 App 如果已经在 App / 云端完成 ASR 和总结,可额外调用 recordings.result.write 写入转录文本和总结;旧版插件没有这个方法时,recordings.sync 仍可正常工作。

写入转录文本和总结(可选)

新版客户端推荐调用顺序:

  1. 录音完成后上传音频到 OSS。
  2. 调用原有 recordings.sync,只同步 recordingId、录音元数据和 oss_audio_url;为了兼容旧插件,这个 WS 方法保持不变。
  3. App 调云端转写 / 总结接口,拿到 ASR 结构化结果和总结 Markdown。
  4. 如果插件支持 recordings.result.write,调用该方法写入转录文本和总结。
  5. 如果旧插件返回 method-not-found 或连接层不支持该方法,App 保留本地转录/总结即可;音频同步不受影响。

recordings.result.write 主要写文本结果,不新增 HTTP route。它写入的 transcript-data/transcripts/summaries/ 会覆盖旧 HTTP / 插件 ASR 链路生成的文件;反过来,后续 HTTP / 插件 ASR 重新生成结果时,也可以覆盖它写入的文件。

可选参数 ossUrl:App 侧如果产出了最终音频(如剪辑、降噪后的版本),可在 params.ossUrl 传入音频 OSS 地址。插件会按 recordings.sync 相同的下载逻辑在后台拉取,并覆盖之前 recordings.sync 下载的本地音频文件;下载完成或失败都会推送 recording.status 事件。该字段不影响文本结果写入,响应不等待下载完成。

{
  "method": "recordings.result.write",
  "params": {
    "recordingId": "ios-rec-20260609-203000",
    "ossUrl": "https://bucket.oss.example.com/recordings/audio/final.mp3",
    "transcript": {
      "generatedAt": "2026-06-09T20:25:00+08:00",
      "source": {
        "provider": "model-proxy",
        "taskId": "asr-task-xxx",
        "requestId": "req-xxx",
        "status": "SUCCEEDED"
      },
      "title": "产品方案讨论",
      "category": "meeting",
      "brief": "讨论 App 写入转录和总结的新链路。",
      "text": "完整转写正文...",
      "segments": [
        {
          "text": "这一段是转写内容。",
          "startMs": 0,
          "endMs": 4200,
          "speakerId": 1
        }
      ]
    },
    "summary": {
      "format": "markdown",
      "markdown": "# 总结\n\n- ASR 和总结由 App 通过 result.write 写入。\n"
    }
  }
}

Legacy:插件侧 ASR

旧客户端仍可使用 recordings.sync 同步 OSS 音频,并通过请求级 asr 触发插件侧转写。该链路仅用于兼容,不推荐新客户端继续接入。

录音转写不再依赖插件侧 openclaw.json 持久配置,而是由客户端在请求里显式传入 asr 参数,插件保持无状态执行。

| asr.mode | 说明 | 额外依赖 | |---|---|---| | "api" | 云端 model-proxy 长录音转写 | 请求级 asr.api.apiKey,未传则回退插件本地 API Key | | "local" | 已停用,保留枚举兼容旧客户端 | — | | 未传 asr | 关闭转写,仅下载保存音频 | — |

初始化接口

客户端在正式转写前可先调用 recordings.asr.init

  • mode = "api":校验请求级或插件本地 API Key 是否可用,并返回当前环境的长录音 submit-task 端点
  • mode = "local":已停用,调用会返回错误

示例:

{
  "method": "recordings.asr.init",
  "params": {
    "asr": {
      "mode": "api",
      "api": {
        "language": "zh",
        "enableNormalization": true
      }
    }
  }
}

在 recordings.sync 中指定转写方式

仅同步录音,不触发插件侧转写:

{
  "method": "recordings.sync",
  "params": {
    "recordingId": "2026-03-23_14-32",
    "recording": {
      "name": "会议录音",
      "duration_sec": 120,
      "file_size_bytes": 1024000,
      "created_at": "2026-03-23T14:32:00+08:00",
      "oss_audio_url": "https://bucket.oss.example.com/recordings/audio/2026-03-23_14-32.mp3",
      "markers": [],
      "transfer_status": "syncing_openclaw"
    }
  }
}

云端 API(model-proxy 长录音):

{
  "method": "recordings.sync",
  "params": {
    "recordingId": "2026-03-23_14-32",
    "recording": {
      "name": "会议录音",
      "duration_sec": 120,
      "file_size_bytes": 1024000,
      "created_at": "2026-03-23T14:32:00+08:00",
      "oss_audio_url": "https://bucket.oss.example.com/recordings/audio/2026-03-23_14-32.mp3",
      "markers": [],
      "transfer_status": "syncing_openclaw"
    },
    "asr": {
      "mode": "api",
      "api": {
        "language": "zh",
        "enableNormalization": true
      }
    }
  }
}

手动重新触发转写

当录音状态为 syncedtranscribe_failedtranscribed 时,客户端可通过 Gateway Native 主动调用 recordings.retranscribe

  • 当前接口必须再次传入 asr
  • HTTP POST /recordings 仅支持同步录音,不支持重新转写
  • 接口会立即返回,实际转写进度应结合 recording.status 广播或 recordings.status 查询确认
{
  "method": "recordings.retranscribe",
  "params": {
    "recordingId": "2026-03-23_14-32",
    "asr": {
      "mode": "api",
      "api": {
        "language": "zh"
      }
    }
  }
}

转写结果存储

<stateDir>/plugins/phone-notifications/recordings/
├── audio/
│   └── 2026-03-31_14-32.ogg       # 原始录音(OGG/Opus)
├── transcript-data/
│   └── 2026-03-31_14-32.json      # 结构化转写 JSON
├── transcripts/
│   └── 2026-03-31_14-32_与王总沟通产品方向.md   # 转写文本 + 关键点
├── summaries/
│   └── 2026-03-31_14-32.md        # App 下发或 ASR 结果中的总结 Markdown
└── index.json                      # 录音状态索引

数据协议

Gateway Native:notifications.push

{
  "type": "req",
  "id": "n_abc",
  "method": "notifications.push",
  "params": {
    "items": [
      {
        "id": "n_001",
        "app": "WeChat",
        "title": "张三",
        "body": "在吗?",
        "timestamp": "2026-03-02T08:30:00+08:00",
        "category": "message",
        "metadata": { "chatId": "c1" }
      }
    ]
  }
}

说明:

  • id 用于同一天内去重(写入 .ids 索引),不会作为通知正文字段落盘。
  • app 是手机端上报字段;插件落盘时会标准化为 appName

Gateway Native:通知总结进度与结果

用户在 App 里通过普通对话说“总结未读通知”时,Agent 会调用插件工具 notifications_summary。插件在工具执行过程中会主动广播 notification.summary.status,完成后广播 notification.summary.result,App 不需要把入口改成直接 Gateway RPC 也能展示进度与最终总结。

notification.summary.status 示例:

{
  "jobId": "sum_20260614031000_ab12cd34",
  "phase": "summarizing",
  "status": "running",
  "message": "正在整理通知摘要:2/5 个分片",
  "doneChunks": 2,
  "totalChunks": 5,
  "percent": 40
}

notification.summary.result 示例:

{
  "jobId": "sum_20260614031000_ab12cd34",
  "status": "complete",
  "ok": true,
  "message": "通知总结已完成",
  "title": "未读通知总结",
  "markdown": "# 未读通知总结\n\n共整理 700 条通知..."
}

如果 App 需要绕过 Agent、直接发起通知总结并展示进度,也可以调用:

{
  "type": "req",
  "id": "sum_abc",
  "method": "notifications.summary.start",
  "params": {
    "limit": 1000,
    "chunkSize": 150,
    "from": "2026-06-13T09:00:00+08:00"
  }
}

接口会立即返回:

{
  "ok": true,
  "jobId": "sum_20260614031000_ab12cd34",
  "status": "queued",
  "event": "notification.summary.status",
  "resultEvent": "notification.summary.result"
}

随后插件通过 notification.summary.status 广播阶段事件,phase 可能为 queuedscanningsnapshot_createdsummarizingmergingcompletefailed。App 可用 message 直接展示进度,也可用 doneChunks/totalChunks/percent 做进度条。总结完成后,插件会通过 notification.summary.result 广播可直接展示的 markdown。若错过事件,可用 notifications.summary.statusnotifications.summary.result 传入 jobId 查询当前状态和结果。

HTTP 备选:POST /notifications

POST /notifications
Authorization: Bearer ntf-xxxxxxxxxxxx
Content-Type: application/json
{
  "notifications": [
    {
      "id": "n_001",
      "app": "SMS",
      "title": "京东快递",
      "body": "包裹已发出",
      "timestamp": "2026-03-02T08:30:00+08:00"
    }
  ]
}

响应示例:

{ "ok": true, "ingested": 1 }

CLI 命令

以下示例均以 OpenClaw 全局 CLI 为例。QClaw 下请将 openclaw 替换为 QClaw 提供的 OpenClaw wrapper。

插件注册了 openclaw ntf 命令组,并提供 openclaw phone-notifications 作为无冲突别名,供 Agent 直接消费。所有命令输出 JSON。

认证管理

# 设置用户 API Key(灯效下发等接口需要)
openclaw ntf auth set-api-key "ock_xxx"

# 查看当前认证状态
openclaw ntf auth show

# 清除认证信息
openclaw ntf auth clear

兼容旧命令:openclaw ntf auth set-token <token>,效果等同于 set-api-key

通知查询

# 查询最近通知(默认按时间倒序)
openclaw ntf search

# 查询指定应用的通知
openclaw ntf search --app wechat --from 2026-03-01T00:00:00+08:00 --to 2026-03-09T23:59:59+08:00

# 按关键词搜索
openclaw ntf search --keyword "开会" --limit 20

# 按发送人过滤
openclaw ntf search --sender "张三" --from 2026-03-01T00:00:00+08:00

# 仅看飞书群聊消息
openclaw ntf search --app Feishu --conversation-type group

# 生成轻量通知摘要输入,适合 Agent 总结大量通知
openclaw ntf summary --limit 700

# 创建并运行分片总结任务,适合 1000+ 条通知
openclaw ntf summary-job create --limit 3000 --chunk-size 150 --max-content 120
openclaw ntf summary-job run <jobId> --max-chunks 30 --include-result
openclaw ntf summary-job status <jobId>
openclaw ntf summary-job result <jobId>

通知统计

# 全维度统计
openclaw ntf stats

# 按应用维度统计
openclaw ntf stats --dim app --from 2026-03-01

# 按时段维度统计
openclaw ntf stats --dim hour --from 2026-03-01 --to 2026-03-09

通知同步(记忆系统)

# 扫描当天未同步通知(默认只扫描本地当天)
openclaw ntf sync scan

# 扫描指定日期或 checkpoint 之后的所有未同步通知
openclaw ntf sync scan --date 2026-03-09
openclaw ntf sync scan --from-date 2026-03-02 --to-date 2026-03-09
openclaw ntf sync scan --all

# 获取指定日期未处理通知(单次最多 300 条)
openclaw ntf sync fetch --date 2026-03-09

# 标记当前批次同步完成;若 fetch 返回 hasMore=true,继续重复 fetch/commit
openclaw ntf sync commit --date 2026-03-09

通知监控任务

# 创建监控任务
openclaw ntf monitor create boss-alert \
  --description "监控老板微信消息" \
  --match-rules '{"appName":"wechat","senderKeywords":["张总"]}' \
  --schedule "*/30 * * * *"

# 列出监控任务
openclaw ntf monitor list

# 查看监控任务详情
openclaw ntf monitor show boss-alert

# 启用/暂停监控任务
openclaw ntf monitor enable boss-alert
openclaw ntf monitor disable boss-alert

# 删除监控任务
openclaw ntf monitor delete boss-alert --yes

灯效规则

灯效规则工具调用独立 Notification Intelligence Agent 的插件侧 API: /api/plugin/notification-intelligence/light-rules。鉴权只使用 X-Api-Key-Id,不调用 App 端 JWT 接口,也不调用 preview / evaluate / import


# 启用一次性灯效及云端灯效规则工具
openclaw ntf light setup

# 调试命令:直接发送灯效指令到硬件设备
openclaw ntf light send \
  --segments '[{"mode":"wave","duration_s":4,"brightness":192,"color":{"r":255,"g":0,"b":0}}]'

# 呼吸灯效
openclaw ntf light send \
  --segments '[{"mode":"breath","duration_s":6,"brightness":128,"color":{"r":0,"g":255,"b":0}}]'

开发

pnpm install
pnpm run build    # tsc 编译
pnpm test         # vitest 测试

目录结构

src/
├── types.ts            # 类型定义
├── index.ts            # 插件入口:service + gateway method + http route + tool + cli
├── logger.ts           # 日志模块
├── recording/
│   ├── handler.ts      # recordings.sync 核心处理流程
│   ├── result-writer.ts # recordings.result.write 写入转录/总结
│   ├── asr.ts          # Legacy ASR 调度器(api;local 已停用)
│   ├── whisper-local.ts# Legacy 本地 Whisper 兼容模块
│   ├── downloader.ts   # 从 OSS 下载音频文件
│   ├── storage.ts      # 录音本地存储管理(index.json)
│   └── state-machine.ts# 录音状态机
├── notification/
│   ├── storage.ts      # 通知文件系统存储引擎
│   └── ...
├── light/              # 灯效控制
├── light-protocol.ts   # 灯效 ANCS 协议编码
└── cli/
    ├── index.ts        # CLI 注册汇总入口
    ├── helpers.ts      # 共享工具(路径解析、日期处理、JSON 输出)
    ├── auth.ts         # auth 子命令
    ├── ntf-search.ts   # search 子命令
    ├── ntf-stats.ts    # stats 子命令
    ├── ntf-sync.ts     # sync 子命令
    ├── ntf-monitor.ts  # monitor 子命令
    └── light-send.ts   # light send 子命令

发布与运维(CI/CD)

发布采用 release branch flow:master 只保存已发布的稳定基线,开发/测试/beta/正式发布都在 release/** 分支完成。

版本发布

release/** 分支上执行版本命令(要求工作区干净、本地与 origin/<当前 release 分支> 完全一致):

bun run release:patch   # 预发布转正 或 稳定版 patch+1
bun run release:minor
bun run release:beta     # 递增 beta.N

命令会提交 package 版本号、打附注 tag 并推送,触发 release.yml

发布流水线(Artifact Promotion 制品晋级)

按「一次构建、同字节晋级」分三个 job 串行执行(当前无人工审批关卡,全自动):

  1. build:校验 tag 在 release/** 上、tag 与 package.json 版本一致,构建一次并 bun pm pack,算出 SHA256SUMS,通过 actions/upload-artifact 传给后续 job。此 job 不挂 environment。
  2. deploy-test(environment test):不重新构建,校验 sha256 后把同一份 tgz 上传到测试 OSS 稳定渠道,重新生成测试安装脚本,发飞书「RC 已上测试 OSS」。
  3. deploy-prod(environment production):deploy-test 成功后自动继续,下载同一份产物、再次 sha256 校验,原样上传线上 OSS(含 latest/beta 渠道标记),重新生成线上安装脚本,npm publish 现成 tarball(仅稳定版,不重新构建),创建 GitHub Release(直接挂这份 tgz),发飞书「已上线」。

environment: test / production 仅用于部署轨迹追踪与未来按需加保护(当前两者 protection_rules 均为空,不形成关卡)。beta 走同一条流水线、跳过 npm publish。若日后想恢复上线审批,在 production environment 开启 Required reviewers 即可,无需改 workflow。

安装脚本(install*.sh / install.ps1 / install-core.mjs)是环境相关的薄胶水层,不参与晋级,每个环境各自重新生成;只有 tgz 走同字节晋级。部署时安装脚本会同时归档到 v{版本}/installer/,作为回滚恢复源。

回滚

回滚是把渠道指针(latest/beta)重指到已存在、不可变的 v{版本}/ 目录,不重新构建。GitHub → Actions → Rollback OSS Channel → Run workflow,填入 version(含 - 自动识别 beta 渠道)与 environmenttest/production):

  • 脚本先 HEAD 校验目标 tgz 存在,再重指渠道标记,并从 v{版本}/installer/ 恢复安装脚本。归档缺失(早于本机制上线的历史版本)时只回退 tgz 指针并显式告警,不静默跳过。
  • 当前 test / production 回滚均免审批(环境无保护规则)。environment 声明保留,便于日后需要时单独给生产回滚加 Required reviewers。

不自动回滚的部分(手动 SOP):

  • npm:无法安全 unpublish,对问题版本 npm deprecate '@yoooclaw/phone-notifications@<bad>' '请安装上一版'。OSS 是主要分发路径。
  • GitHub Release:不自动删除,手动编辑标注「已回滚」,保留追溯。

CI/CD 配置(GitHub 仓库设置)

  • Secrets 单一来源(build-once 前提):构建期烤入 dist 的 secrets(LIGHT_APP_KEY / LIGHT_TEMPLATE_ID / APP_NAME_MAP_URL / OPENCLAW_HOST_DEVELOPMENT / OPENCLAW_HOST_TEST / OPENCLAW_HOST_PRODUCTION)配置为 repo-level secrets(已就绪:当前全部 secret 均在 repo 级,test / production environment 无任何 environment 级覆盖)。不要在 environment 级重复配置这些键,否则 build-once 会静默失效(构建字节取决于 build job 挂的 environment)。
  • 无审批关卡test / production environment 当前 protection_rules 为空,不形成审批。如需恢复上线审批,给 production environment 加 Required reviewers 即可,无需改 workflow。
  • OSS / npm 凭据(OSS_* / NPM_TOKEN)走 repo 级 secrets;测试 OSS 共享 OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET,测试 bucket / region / 域名在 workflow 内以明文常量内置。
  • 备注:APP_NAME_MAP_URL 目前未配置为 secret,构建时解析为空字符串(与改造前一致);如需启用应用名映射,补上该 repo 级 secret。

License

MIT