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.19.1

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)
  • 通知条目基础字段为 appName、title、content、timestamp(ISO 8601 含时区)
  • 飞书消息落库时会对齐微信语义:群聊写成 title=群名、content=发送人: 正文;私聊保持 title=发送人
  • 部分应用会补充结构化字段;例如飞书消息会额外写入 senderName、conversationType(group/private)、conversationName
  • 同一天内按 notification-id 去重(使用内部索引文件)
  • 手机通知永久保存,不做过期清理

上下文来源清单(磁盘读取契约)

常驻应用(如 ClawPilot Desktop)需要对通知/录音/网页做增量索引。让它们周期性起 ntf 子进程既贵又脆(Node 运行时、构建产物、PATH 任一环出问题,本地已有数据就 索引不到),而这三类数据本来就已经原子落盘了。因此插件在三个 store 全部 init 成功之后、发布 storage ready 之前,原子写出一份版本化清单,把最终实际采用的 目录发布出去:

<stateDir>/plugins/phone-notifications/context-source.json
{
  "schemaVersion": 1,
  "pluginVersion": "1.13.2",
  "generatedAt": "2026-08-13T10:00:00.000Z",
  "sources": {
    "notifications": { "formatVersion": 1, "root": "/abs/path/notifications", "commit": "daily-json" },
    "recordings":    { "formatVersion": 1, "root": "/abs/path/recordings",    "commit": "index.json" },
    "webPages":      { "formatVersion": 1, "root": "/abs/path/web-pages",     "commit": "index.json" }
  }
}

约定:

  • schemaVersion 管清单结构;每个来源的 formatVersion 独立管对应磁盘数据格式, 改一类数据的格式不牵连另外两类。消费方遇到不认识的高版本必须报「不兼容」, 不能按旧格式猜着解析。
  • root 一律是规范化后的绝对路径,且是插件运行时最终采用的那一个——某个 store 因为 stateDir 不可写回退到 workspace 时,写进清单的就是回退后的路径。 消费方只消费,不自行推导目录。
  • commit 是提交点语义:
    • daily-json——通知以单日 YYYY-MM-DD.json 整文件为提交单元,新增通知时整份 原子重写,所以不能把新增等同于「追加的最后一行」。
    • index.json——录音/网页先写产物(音频、转写、摘要、正文),再原子替换 index.json。消费方只有读到新的 index.json 才算这组文件已提交;直接盯着 正文文件的 mtime 会读到写了一半的文档。
  • 某个来源不可用时,清单里写 { "unavailableReason": "..." },绝不写一个不存在 的路径冒充可用。
  • 清单只含定位与版本信息,不含通知正文、转录或网页正文。
  • 插件停止时不删清单:它描述的是仍然躺在磁盘上的数据,插件没在跑的时候消费方 照样要能据此完成索引。存储目录变化时覆盖清单。
  • 清单写失败只记告警,不影响通知/录音/网页服务本身。

ntf CLI 继续保留,服务 Agent/Skill 的按需查询和用户手动排障——它和这条常驻索引 链路是两条独立的轨道。

图片同步

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

接入要点:

  • 请求体为 { imageId, image };image 至少包含 oss_image_url 和 created_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 打印图片存储目录

网页同步数据查询

浏览器扩展通过 webPages.save(Gateway Native)或 POST /web-pages 下发已经清洗为 Markdown 的网页正文;插件将正文、可选的 HTML 存档和索引保存到本地。

存储结构(与录音、图片同级,优先 stateDir,回退 workspace):

<storageDir>/web-pages/
├── files/
│   ├── <urlHash前8位>-<slug>.md    # YAML frontmatter + 清洗后的正文
│   └── <urlHash前8位>-<slug>.html  # 可选的原始 HTML 存档
└── index.json                       # 元数据索引({ pages: [] })

Agent 侧通过 CLI 查询消费:

  • ntf synced-web-page list [--from <ISO_TIME>] [--to <ISO_TIME>] 按收藏时间倒序列出本地已同步网页,可按 capturedAt 时间范围过滤
  • ntf synced-web-page path <urlHash> 打印 Markdown 文件绝对路径,支持至少 8 位的唯一哈希前缀
  • ntf synced-web-page search <keyword> [--limit <n>] 搜索本地已同步网页的标题、URL 和 Markdown 正文,不访问互联网
  • ntf synced-web-page storage-path 打印网页存储目录

list 的 --from 包含起点,--to 不包含终点;时间参数使用带明确时区的 ISO 8601 格式,过滤字段为 capturedAt。

统一上下文查询 Skill

Agent 侧所有只读查询统一由 skills/context-query 路由。主 SKILL.md 只负责识别数据源和查询意图,通知、录音、网页、图片的具体执行规则放在 references/ 中按需加载。新增上下文来源时增加 reference 和 CLI 适配器即可, 不再为每种来源增加独立 Query Skill。

录音深加工统一由 skills/recordings-process 路由。会议纪要、录音翻译、 思维导图、采访整理和实体提取共享录音定位流程,再按用户需要加载对应的 references/ 产物规则,不再各占一个 Skill。通知写入记忆等写入流程仍保留独立 Skill。

前置要求

  • 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

指定安装版本时,--version <版本号> 优先于安装脚本内置的下载地址;需要自定义包地址时使用 --tgz-url(优先级最高)。同时指定版本和包地址时,包内版本必须匹配,否则在替换旧插件前终止。此规则适用于 Bash 与 PowerShell 安装器。

只检查插件更新(不下载、安装或重启网关):

openclaw ntf update --check

返回 current(运行版本)、latest(候选版本)、hasUpdate 和 updated: false。macOS/Linux 按当前环境和账号查询灰度服务,未命中时 latest 为 null;Windows 沿用 OSS 渠道检查。--check 与 --force 同用也不会安装。不加 --check 则保持原有检查并安装行为。

插件内的 plugin.update / plugin-update 和 ntf update 复用仓库已有的 scripts/install-core.mjs,不再由内置 updater 直接替换目录:

  • 标准 OpenClaw:探测 openclaw plugins install --help,通过 plugins info --json 校验当前安装归属,再调用原生安装器。默认归档使用 tgz;新版托管 npm 安装使用 npm-pack:<绝对路径>,并跟随宿主返回的新版本目录,不再假设都在 extensions 下。
  • 检测到 5.28 的旧扫描安装协议、旧 CLI 不支持覆盖安装、未找到 CLI,或使用其他宿主:沿用已有安装脚本,先准备生产依赖,再备份并替换插件。不添加危险代码放行参数;已废弃为 no-op 的旧扫描参数不触发旧版分流。但检测到新版 SQLite 状态/托管 npm 目录时不允许此回退,须先恢复宿主 CLI;原生安装下的链接或未明确归属的自定义来源也不自动迁移。
  • 原生能力探测异常或原生安装失败:报告失败,不会绕过宿主策略再次尝试直接覆盖;Nix 模式拒绝自更新。
  • 原生成功后不再自行写配置、manifest、依赖或执行 npm link;安装登记(含新版 SQLite)、$include 及启用状态由宿主管理。不直接授予 hooks/工具权限。旧脚本回退路径仍沿用既有兼容配置。
  • 新版更新同一已启用插件时,核对完整 manifest 契约、包入口及 bin 不变后,自动衔接既有能力授权;兼容旧包补声明已存在的 ntf / phone-notifications CLI 入口。新增能力或无法核实则返回 CAPABILITY_CONSENT_REQUIRED,用户审阅宿主报告后可明确执行 ntf update --accept-capabilities。不绕过宿主安装策略。
  • 走原生路径的旧宿主覆盖安装可能重新启用已禁用插件;此时在安装前停止,要求升级宿主或由用户明确启用后重试。

Gateway 内触发更新时,独立进程执行安装并保存结果,不阻塞 Relay 心跳。 安装日志位于 <stateDir>/extensions/.phone-notifications-update/<updateId>/install.log, 结果位于该根目录的 last-update.json,不会随 npm 版本目录切换而丢失;子目录的 install-result.json 保存实际安装目录和原生错误。原生安装使用的 tgz 保存在 <stateDir>/plugins/phone-notifications/install-artifacts/,避免宿主登记的来源路径随临时文件清理而失效。 Windows 沿用手动停止/启动模式:先停止 Gateway,等待 last-update.json 中 status=succeeded,再启动,避免安装尚未完成就加载插件。

升级完成后会校验 systemd gateway 是否真正激活并展示实时连接状态(1.16.2 起),及早发现「升级已完成但服务未启动」。每次自动 / 手动更新的开始、成功、失败及运行中版本心跳会持久化上报到服务端(1.17.0 起),网络异常时本地排队并自动重试。

注意:此策略只在运行包含本改动的版本后生效,旧版 updater 不会被远程自动改写。 首次部署应使用带 --update 的新版安装脚本或宿主原生安装命令;不要把旧版“已解包”当成新版完整安装流程已经执行。

隔离联调脚本:node scripts/installer/smoke-native-update.mjs /绝对路径/openclaw。它创建无网络行为的测试插件和独立 state/config,只验证安装链路,不启动 Gateway,也不代表真实业务运行验收。新版覆盖归档、npm 版本目录、授权拒绝/接受、禁用状态和 $include;旧版覆盖原生归档更新。

通过一键脚本安装(推荐给 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\|eclaw> | 写入运行渠道 | 渠道脚本自动传入;直接运行核心时不写入,默认 openclaw | | --host-cli <cmd> | 宿主 CLI 命令名 | openclaw | | --api-key <apiKey> | 灯控鉴权 API Key | - | | --token <token> | 兼容旧参数,等价于 --api-key | - | | --lang <zh-CN\|zh-TW\|en-US> | 安装提示与插件运行时输出(CLI 输出、错误信息、skill 回复)使用的语言;等价环境变量 PHONE_NOTIFICATIONS_LANG | zh-CN |

脚本做了什么

  1. 自动探测宿主状态目录和配置文件路径,并提前检查安装目录、配置文件、凭据文件的写入权限
  2. 从 CDN 下载最新版本的插件 tgz 包(或使用你指定的版本/地址)
  3. 解压并验证包完整性(检查 package.json、入口文件可加载)
  4. 备份旧版本(如有),原子替换到 <stateDir>/extensions/phone-notifications,并通过 npm 安装 package.json 中声明的生产依赖(继承当前终端的 npm/代理配置)
  5. 写入 <configPath> 配置:
    • plugins.entries — 启用插件、合并用户配置。根据实际宿主的配置 schema 处理 hooks.allowConversationAccess:支持时为提示词 Hook 补充授权(保留显式 false),不支持时清理遗留字段;无法确认时保留现有值、不新增该字段。App 会话管理使用独立的 Gateway RPC 权限。
    • tools.allow / tools.alsoAllow 与 agents.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 毫秒

录音结果同步

当前版本由 App / 云端完成 ASR 和总结,再通过 recordings.result.write 把结果与音频 OSS 地址写入插件。旧的 recordings.sync、recordings.asr.init、recordings.retranscribe 和插件侧 ASR 已移除。

Gateway WebSocket:recordings.result.write

WebSocket 方法会同步写入请求中提供的转写和总结文件,然后立即响应;OSS 音频在后台下载,进度和最终结果通过 recording.status 事件推送。

可选参数 ossUrl 是最终音频的 OSS 地址。传入时会覆盖同一录音先前的音频来源;不传时只写入转写/总结。

{
  "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"
    }
  }
}

私有上传任务字段:ossTaskId

recordings.result.write 在 params 顶层接受可选字符串 ossTaskId,值为上传服务返回的 taskId。新私有上传链路应传入;旧请求可省略。传入时必须为非空字符串(最多 256 字符),并同时提供音频 URL;不使用 ASR taskId 或 OSS Multipart uploadId。链接刷新和下述可选回显已实现。

{
  "recordingId": "ios-rec-20260609-203000",
  "ossTaskId": "<上传服务返回的 taskId>",
  "ossUrl": "https://bucket.oss.example.com/recordings/audio/final.mp3",
  "transcript": {
    "source": { "provider": "model-proxy", "taskId": "asr-task-xxx" },
    "text": "完整转写正文..."
  }
}
  • ossTaskId 是上传业务任务 ID;transcript.source.taskId 仍是 ASR 任务 ID,二者不可混用。
  • ossTaskId 用于刷新下载链接,并作为现有同步结果的附加字段;不参与去重或音频替换。
  • 保留原有复用规则:同一 recordingId 存在非空本地音频时直接复用,URL或任务 ID变化也不重下;本地音频缺失时才下载。
  • 现有响应、recording.status 事件和 recordings.status/list 查询附带可选 ossTaskId,无值时省略。App 仍通过 recordingId 关联录音,旧 App 可忽略字段;不新增独立回执接口。回显不代表下载成功,也不证明复用的本地音频来自该上传任务。
  • 下载返回 403 时,携带插件 API Key 请求 /api/device/file/plugin/download-url,请求体为 { "taskId": "<ossTaskId>" },最多刷新一次并重试。404 不刷新,刷新失败或再次 403 时结束。不向 OSS 发送插件凭证。
  • 当前 URL与配套 ossTaskId 保存到索引,重启恢复下载时仍可刷新;旧请求未传任务 ID时不自动刷新。保留本次调用内 Range 续传,不增加跨进程断点。
  • 自动刷新已实现,但真实 App RECORDING 任务的服务端访问尚待联调验证。当前已验证测试环境插件接口鉴权,以及模拟请求的链接刷新和断点续传。
  • ossUrl 使用纯 URL 字符串。现有 transcript / summary 校验与受理、音频落盘状态的区别不变。

HTTP:POST /recordings(已停用)

该路由保留并返回 410 Gone,不写入结果、不下载音频。包括 Tailscale 在内的直连客户端统一使用 Gateway WebSocket 方法 recordings.result.write。

写入响应表示已受理;客户端通过 recording.status 事件或 recordings.status 查询确认 audio_status=downloaded 且 audioFile 非空后,再标记同步完成。部分宿主会过滤直连事件,因此必须保留状态查询兜底。

历史接口(当前版本已移除)

下面的内容只用于理解旧客户端协议;当前版本不再注册这些 Gateway 方法。

录音转写不再依赖插件侧 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,
      "duration_display": "2m 0s",
      "file_size_display": "1 MB",
      "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,
      "duration_display": "2m 0s",
      "file_size_display": "1 MB",
      "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
      }
    }
  }
}

手动重新触发转写

当录音状态为 synced、transcribe_failed 或 transcribed 时,客户端可通过 Gateway Native 主动调用 recordings.retranscribe。

  • 当前接口必须再次传入 asr
  • HTTP POST /recordings 已停用,返回 410
  • 接口会立即返回,实际转写进度应结合 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/
│   └── 2026033114_与王总沟通产品方向_2026-03-31_14-32.md   # 总结 Markdown
│                                   # 名字是「本地时间 YYYYMMDDHH + 标题 + 录音 ID」,
│                                   # 按文件名排序即按时间排序;权威路径始终看
│                                   # index.json 的 summaryFile,不要自行拼接
└── 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 可能为 queued、scanning、snapshot_created、summarizing、merging、complete、failed。App 可用 message 直接展示进度,也可用 doneChunks/totalChunks/percent 做进度条。总结完成后,插件会通过 notification.summary.result 广播可直接展示的 markdown。若错过事件,可用 notifications.summary.status 或 notifications.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

# 迭代器写法:自动定位下一个未处理的日期/批次,直接返回可执行的 commitCommand
openclaw ntf sync next
openclaw ntf sync next --all
openclaw ntf sync next --from-date 2026-03-02 --to-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}}]'

# 只在带屏幕的二代硬件上显示文字(不传 --segments)
openclaw ntf light send --title "会议提醒" --reason "15:00 产品评审"

只传 --title / --reason 时按纯文字屏幕展示处理:下发一段 8 秒显式关灯段、不循环(可能关掉当前灯效)。段内省略的 brightness 默认 128、color 默认白色,显式写出的非法值仍按严格校验处理。

数据迁移(1.16.0 本地包 / 1.18.0 云端中转)

使用 openclaw-data-transfer Skill 或 ntf transfer,在两个环境间搬运通知、录音转写、摘要和收藏网页。源数据保留,目标冲突不覆盖;音频默认不带,记忆、配置和凭据不迁移。

# 目标插件运行后,可先导出能力文件带回源端预检
ntf transfer capabilities --out ./target-capabilities.json
# 源端预览并创建明文本地包;输出路径必须不存在
ntf transfer export --dry-run
ntf transfer export --out ./transfer-package.tar.gz --target-capabilities ./target-capabilities.json
# 用户自行携带这一个文件到目标
ntf transfer import --file ./transfer-package.tar.gz --dry-run
# 使用预览返回的两个 ID
ntf transfer import --local <localTransferId> --plan <planId>
# 失败或部分完成后,本地继续
ntf transfer import --local <localTransferId> --plan <planId> --resume

--out 以 .tar.gz/.tgz 结尾时生成单个 gzip 压缩包(1.18.0 起),其他路径仍生成包目录;import --file 两种都接受。目标端低于 1.18.0 时只能导入包目录,带 --target-capabilities 导出 tar.gz 会提前报 ARCHIVE_UNSUPPORTED_BY_TARGET。压缩包只是打包,内容仍是明文。与 yoooclaw CLI 的 yoooclaw transfer 包格式互通。

--with-audio、--with-images、--with-html 选择附件;--include 选择数据类型,--from/--to 使用含时区时间戳(左闭右开)。

1.18.0 起支持 OSS 云端中转(迁移 Skill 默认使用),使用插件 channel API Key 和 /file/plugin 接口:

# 导出并上传 tar.gz,返回 taskId 和可复制到目标 Agent 的导入话术
ntf transfer export --via oss
# 接收端使用同账号凭证,自动获取地址、下载并导入
ntf transfer import --task <taskId>
# 只预览,不导入
ntf transfer import --task <taskId> --dry-run

云端迁移包由服务端统一维护,在上传完成 24 小时后自动删除,与导入结果无关。插件不调用云端删除接口,也不提供删除命令。导入成功仍会清理本地 staging 副本;部分成功或失败保留本地副本和报告供重试。迁移回复会醒目提醒:⚠️ 迁移数据包含大量隐私信息,请妥善保管任务 ID 和数据包,避免向他人分享或泄露。

上传复用阿里云 SDK 分片续传,下载复用插件 Range 下载器;仅支持本次命令内恢复,不支持重启后续传。客户端限制单包 5 GiB,默认不保留临时压缩包(用 --out package.tar.gz 可保留)。创建任务每天最多 3 次,不自动重试创建;STS 过期时停止,不自动重建任务。上传确认失败可用错误中的 taskId/objectKey 执行 ntf transfer complete --task <taskId> --object-key <objectKey>。导出只交付任务 ID 和导入话术,下载地址在命令内部使用,不进入用户消息或日志;默认云端文件保留 24 小时。

导入经本机鉴权的插件进程执行。目标插件必须运行;不直接修改在线存储。历史通知默认跳过自动记忆消费,本地待消费通知不受影响;所有使用该存储的 ntf sync 消费者需升级到 1.16.0。暂存包与报告位于目标状态目录的 plugins/phone-notifications/transfers/,部分结果可从本地重试,处理完成后用户可删除对应任务目录。该目录含明文数据,勿当作可公开分享的日志。

AI TODO 待办管理

提供 ai_todo_list、ai_todo_get、ai_todo_create、ai_todo_update、ai_todo_delete 五个原生 Agent 工具,复用 App 待办服务和插件 API Key。完成和恢复通过 ai_todo_update 的 isDone 字段操作。

同时提供 ntf todo list/get/create/update/delete 命令,与原生工具共用同一个 Client。完成和恢复使用 ntf todo update <id> --is-done true|false。

接口、CLI 用法、时间语义、组合更新的部分成功处理和待联调前提见 AI TODO Agent 接入说明。

媒体生成与结果交付(1.18.0)

media-generate Skill 支持文生图、图生图 / 图片编辑、文生视频、参考图生视频、首尾帧视频,以及积分估算和视频任务查询。参考素材、尺寸与时长校验与 CLI 的 yoooclaw-media-generate 对齐:每次新请求都先确认规格、估价并取得本次授权才生成;视频为 2–30 秒的整数时长,任务自动轮询最长 20 分钟,沿用原进程等待结果;生成结果按批次直接交付并显示标题编号,回复中的媒体链接会按工具真实输出校正。

图片、视频与迁移话术统一使用原生工具 result_deliver 交付(替代 media_deliver)。网页会话中,ntf transfer export --via oss --delivery-reference 保存真实任务 ID,仅返回交付编号;工具校验会话并在本轮结束后发送隐私提醒与可复制话术。导入仍使用原命令。未新增迁移原生工具;旧工具白名单需将 media_deliver 更新为 result_deliver。

迁移交付编号从 [[transfer:1]] 开始,在当前插件状态目录内持续递增;重启不重置,导出失败可能跳号。编号仅用于本机 result_deliver,不能替代发给目标 Agent 的真实任务 ID。

开发

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 渠道)与 environment(test/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