@yoooclaw/phone-notifications 1.11.18 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +212 -29
  2. package/dist/bin/ntf.cjs +918 -113
  3. package/dist/bin/ntf.cjs.map +15 -10
  4. package/dist/cli/helpers.d.ts +27 -0
  5. package/dist/cli/helpers.d.ts.map +1 -1
  6. package/dist/cli/image-list.d.ts +3 -0
  7. package/dist/cli/image-list.d.ts.map +1 -0
  8. package/dist/cli/image-path.d.ts +3 -0
  9. package/dist/cli/image-path.d.ts.map +1 -0
  10. package/dist/cli/image-status.d.ts +3 -0
  11. package/dist/cli/image-status.d.ts.map +1 -0
  12. package/dist/cli/image-storage-path.d.ts +3 -0
  13. package/dist/cli/image-storage-path.d.ts.map +1 -0
  14. package/dist/cli/index.d.ts.map +1 -1
  15. package/dist/cli/ntf-query.d.ts +11 -0
  16. package/dist/cli/ntf-query.d.ts.map +1 -1
  17. package/dist/cli/ntf-summary-job.d.ts +3 -0
  18. package/dist/cli/ntf-summary-job.d.ts.map +1 -0
  19. package/dist/cli/ntf-summary.d.ts.map +1 -1
  20. package/dist/cli/ntf-sync.d.ts.map +1 -1
  21. package/dist/env.d.ts +3 -0
  22. package/dist/env.d.ts.map +1 -1
  23. package/dist/image/handler.d.ts +19 -0
  24. package/dist/image/handler.d.ts.map +1 -0
  25. package/dist/image/index.d.ts +3 -0
  26. package/dist/image/index.d.ts.map +1 -0
  27. package/dist/image/store.d.ts +76 -0
  28. package/dist/image/store.d.ts.map +1 -0
  29. package/dist/index.cjs +7787 -5679
  30. package/dist/index.cjs.map +42 -37
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/light-rules/client.d.ts +58 -0
  33. package/dist/light-rules/client.d.ts.map +1 -0
  34. package/dist/light-rules/gateway.d.ts.map +1 -1
  35. package/dist/light-rules/types.d.ts +13 -8
  36. package/dist/light-rules/types.d.ts.map +1 -1
  37. package/dist/notification/storage.d.ts +1 -0
  38. package/dist/notification/storage.d.ts.map +1 -1
  39. package/dist/notification/summary.d.ts +144 -0
  40. package/dist/notification/summary.d.ts.map +1 -0
  41. package/dist/plugin/images.d.ts +16 -0
  42. package/dist/plugin/images.d.ts.map +1 -0
  43. package/dist/plugin/lifecycle.d.ts +2 -0
  44. package/dist/plugin/lifecycle.d.ts.map +1 -1
  45. package/dist/plugin/light-rules-tools.d.ts +2 -2
  46. package/dist/plugin/light-rules-tools.d.ts.map +1 -1
  47. package/dist/plugin/notifications.d.ts +5 -3
  48. package/dist/plugin/notifications.d.ts.map +1 -1
  49. package/dist/plugin/recordings.d.ts.map +1 -1
  50. package/dist/profile/auth/jvsclaw.d.ts +1 -0
  51. package/dist/profile/auth/jvsclaw.d.ts.map +1 -1
  52. package/dist/recording/handler.d.ts +1 -0
  53. package/dist/recording/handler.d.ts.map +1 -1
  54. package/dist/recording/index.d.ts +1 -0
  55. package/dist/recording/index.d.ts.map +1 -1
  56. package/dist/recording/result-writer.d.ts +9 -0
  57. package/dist/recording/result-writer.d.ts.map +1 -0
  58. package/dist/recording/storage.d.ts +2 -0
  59. package/dist/recording/storage.d.ts.map +1 -1
  60. package/dist/recording/transcript-document.d.ts +1 -0
  61. package/dist/recording/transcript-document.d.ts.map +1 -1
  62. package/dist/tunnel/frame-slimmer.d.ts +37 -0
  63. package/dist/tunnel/frame-slimmer.d.ts.map +1 -0
  64. package/dist/tunnel/proxy.d.ts.map +1 -1
  65. package/dist/tunnel/relay-client.d.ts.map +1 -1
  66. package/dist/tunnel/service.d.ts +1 -0
  67. package/dist/tunnel/service.d.ts.map +1 -1
  68. package/dist/types.d.ts +120 -0
  69. package/dist/types.d.ts.map +1 -1
  70. package/dist/update/index.d.ts.map +1 -1
  71. package/openclaw.plugin.json +13 -1
  72. package/package.json +3 -2
  73. package/skills/notification-monitor/SKILL.md +14 -13
  74. package/skills/notification-query/SKILL.md +101 -7
  75. package/skills/notification-to-memory/SKILL.md +57 -22
  76. package/skills/notification-to-memory/references/step-0-preflight.md +0 -40
  77. package/skills/notification-to-memory/references/step-1-scan-pending.md +0 -137
  78. package/skills/notification-to-memory/references/step-2-process-dates.md +0 -98
  79. package/skills/notification-to-memory/references/step-3-check-cron-status.md +0 -38
  80. package/skills/notification-to-memory/references/step-4-final-reporting.md +0 -57
  81. package/skills/notification-to-memory/scripts/select-memory-prompt.sh +0 -18
package/README.md CHANGED
@@ -35,29 +35,46 @@ OpenClaw / QClaw 手机通知同步插件:接收手机通知并写入本地 JS
35
35
  - 同一天内按 `notification-id` 去重(使用内部索引文件)
36
36
  - 手机通知永久保存,不做过期清理
37
37
 
38
+ ## 图片同步
39
+
40
+ App 通过 `images.sync`(Gateway Native)或 `POST /images`(HTTP 备选)下发图片元数据,图片二进制走 OSS;插件在后台下载原图落盘。
41
+
42
+ 接入要点:
43
+ - 请求体为 `{ imageId, image }`;`image` 至少包含 `oss_image_url` 和 `created_at`,可附带 `mime_type / width / height / size_bytes / source_app / caption`
44
+ - 同步即时返回 `status=syncing`,下载在后台进行;同一 `imageId` + 同 URL 已 `synced` 时直接去重返回
45
+ - 单张下载大小上限默认 20MB,可通过配置 `image.maxBytes` 调整
46
+ - 存储结构(与录音同级,优先 `stateDir`,回退 `workspace`):
47
+
48
+ ```text
49
+ <storageDir>/images/
50
+ ├── files/ # 下载的原图,<imageId>.<ext>(扩展名由 mime_type 推断)
51
+ └── index.json # 元数据索引({ images: [] })
52
+ ```
53
+
54
+ Agent 侧通过 CLI 查询消费:
55
+ - `ntf image list [--status <s>] [--app <app>]` 列出图片
56
+ - `ntf image status <id>` 查看单张详情
57
+ - `ntf image path <id>` 打印本地文件绝对路径(仅 `synced` 可用)
58
+ - `ntf image storage-path` 打印图片存储目录
59
+
38
60
  ## 前置要求
39
61
 
40
62
  - **Node.js >= 22.12.0**
41
63
  - **OpenClaw >= 2026.3.28**,或兼容其插件 ABI 的 QClaw 宿主
42
64
 
43
- ### 可选:长录音本地转写(Local Whisper ASR)
65
+ ### 可选:长录音音频缓存 / 解码工具
44
66
 
45
- 如需使用**本地离线转写**(`asr.mode = "local"`),还需要安装:
67
+ 新链路中 ASR 和总结由 App 下发,插件不再推荐本地 Whisper 转写。若需要在本机播放、导出或处理硬件 OGG/Opus 音频,可按需安装:
46
68
 
47
69
  ```bash
48
- # whisper.cpp CLI(必须):本地 Whisper 模型推理引擎
49
- brew install whisper-cpp
50
-
51
70
  # opus-tools(推荐):解码硬件录音的 OGG/Opus 格式
52
71
  # 若已安装 ffmpeg 则可跳过
53
72
  brew install opus-tools
54
73
  ```
55
74
 
56
75
  > **说明**
57
- > - `whisper-cli` 是 whisper.cpp 的命令行工具,用于在本地 CPU/GPU 上运行 Whisper 模型。Apple Silicon 设备使用 Metal 加速,自动获得最佳性能。
58
- > - Whisper **模型文件**(`.bin`)由插件**首次转写时自动下载**(默认从国内 ModelScope 镜像),无需手动操作。
59
76
  > - 硬件录音以 Opus 编码、OGG 容器格式传输。`opusdec`(来自 opus-tools)或 `ffmpeg` 任意一个可用即可完成格式转换。
60
- > - 若只使用云端 API 转写(`asr.mode = "api"`),以上依赖均**不需要**安装。
77
+ > - `asr.mode = "local"` 已停用,仅保留枚举兼容旧客户端。
61
78
 
62
79
  ## 宿主兼容性
63
80
 
@@ -243,16 +260,67 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
243
260
 
244
261
  ## 长录音 & ASR 配置
245
262
 
246
- 插件支持接收来自硬件的录音文件,自动下载音频并触发语音转写,生成带时间戳和关键点的 Markdown 笔记。
263
+ 插件保持原有录音同步入口不变:App 仍通过 `recordings.sync` 下发录音元数据和 `oss_audio_url`,音频二进制仍走 OSS。新版 App 如果已经在 App / 云端完成 ASR 和总结,可额外调用 `recordings.result.write` 写入转录文本和总结;旧版插件没有这个方法时,`recordings.sync` 仍可正常工作。
264
+
265
+ ### 写入转录文本和总结(可选)
266
+
267
+ 新版客户端推荐调用顺序:
268
+
269
+ 1. 录音完成后上传音频到 OSS。
270
+ 2. 调用原有 `recordings.sync`,只同步 `recordingId`、录音元数据和 `oss_audio_url`;为了兼容旧插件,这个 WS 方法保持不变。
271
+ 3. App 调云端转写 / 总结接口,拿到 ASR 结构化结果和总结 Markdown。
272
+ 4. 如果插件支持 `recordings.result.write`,调用该方法写入转录文本和总结。
273
+ 5. 如果旧插件返回 method-not-found 或连接层不支持该方法,App 保留本地转录/总结即可;音频同步不受影响。
247
274
 
248
- ### ASR 模式
275
+ `recordings.result.write` 主要写文本结果,不新增 HTTP route。它写入的 `transcript-data/`、`transcripts/`、`summaries/` 会覆盖旧 HTTP / 插件 ASR 链路生成的文件;反过来,后续 HTTP / 插件 ASR 重新生成结果时,也可以覆盖它写入的文件。
276
+
277
+ 可选参数 `ossUrl`:App 侧如果产出了最终音频(如剪辑、降噪后的版本),可在 `params.ossUrl` 传入音频 OSS 地址。插件会按 `recordings.sync` 相同的下载逻辑在后台拉取,并覆盖之前 `recordings.sync` 下载的本地音频文件;下载完成或失败都会推送 `recording.status` 事件。该字段不影响文本结果写入,响应不等待下载完成。
278
+
279
+ ```json
280
+ {
281
+ "method": "recordings.result.write",
282
+ "params": {
283
+ "recordingId": "ios-rec-20260609-203000",
284
+ "ossUrl": "https://bucket.oss.example.com/recordings/audio/final.mp3",
285
+ "transcript": {
286
+ "generatedAt": "2026-06-09T20:25:00+08:00",
287
+ "source": {
288
+ "provider": "model-proxy",
289
+ "taskId": "asr-task-xxx",
290
+ "requestId": "req-xxx",
291
+ "status": "SUCCEEDED"
292
+ },
293
+ "title": "产品方案讨论",
294
+ "category": "meeting",
295
+ "brief": "讨论 App 写入转录和总结的新链路。",
296
+ "text": "完整转写正文...",
297
+ "segments": [
298
+ {
299
+ "text": "这一段是转写内容。",
300
+ "startMs": 0,
301
+ "endMs": 4200,
302
+ "speakerId": 1
303
+ }
304
+ ]
305
+ },
306
+ "summary": {
307
+ "format": "markdown",
308
+ "markdown": "# 总结\n\n- ASR 和总结由 App 通过 result.write 写入。\n"
309
+ }
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### Legacy:插件侧 ASR
315
+
316
+ 旧客户端仍可使用 `recordings.sync` 同步 OSS 音频,并通过请求级 `asr` 触发插件侧转写。该链路仅用于兼容,不推荐新客户端继续接入。
249
317
 
250
318
  录音转写不再依赖插件侧 `openclaw.json` 持久配置,而是由客户端在请求里显式传入 `asr` 参数,插件保持无状态执行。
251
319
 
252
320
  | `asr.mode` | 说明 | 额外依赖 |
253
321
  |---|---|---|
254
- | `"local"` | 本地离线 Whisper(推荐,隐私安全) | `whisper-cli`(brew install whisper-cpp) |
255
322
  | `"api"` | 云端 model-proxy 长录音转写 | 请求级 `asr.api.apiKey`,未传则回退插件本地 API Key |
323
+ | `"local"` | 已停用,保留枚举兼容旧客户端 | — |
256
324
  | 未传 `asr` | 关闭转写,仅下载保存音频 | — |
257
325
 
258
326
  ### 初始化接口
@@ -260,7 +328,7 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
260
328
  客户端在正式转写前可先调用 `recordings.asr.init`:
261
329
 
262
330
  - `mode = "api"`:校验请求级或插件本地 API Key 是否可用,并返回当前环境的长录音 submit-task 端点
263
- - `mode = "local"`:探测本地环境并预下载所需 Whisper 模型
331
+ - `mode = "local"`:已停用,调用会返回错误
264
332
 
265
333
  示例:
266
334
 
@@ -269,11 +337,10 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
269
337
  "method": "recordings.asr.init",
270
338
  "params": {
271
339
  "asr": {
272
- "mode": "local",
273
- "local": {
274
- "model": "small",
340
+ "mode": "api",
341
+ "api": {
275
342
  "language": "zh",
276
- "modelSource": "domestic"
343
+ "enableNormalization": true
277
344
  }
278
345
  }
279
346
  }
@@ -282,7 +349,7 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
282
349
 
283
350
  ### 在 recordings.sync 中指定转写方式
284
351
 
285
- 本地 Whisper:
352
+ 仅同步录音,不触发插件侧转写:
286
353
 
287
354
  ```json
288
355
  {
@@ -297,13 +364,6 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
297
364
  "oss_audio_url": "https://bucket.oss.example.com/recordings/audio/2026-03-23_14-32.mp3",
298
365
  "markers": [],
299
366
  "transfer_status": "syncing_openclaw"
300
- },
301
- "asr": {
302
- "mode": "local",
303
- "local": {
304
- "model": "small",
305
- "language": "zh"
306
- }
307
367
  }
308
368
  }
309
369
  }
@@ -365,8 +425,12 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
365
425
  <stateDir>/plugins/phone-notifications/recordings/
366
426
  ├── audio/
367
427
  │ └── 2026-03-31_14-32.ogg # 原始录音(OGG/Opus)
428
+ ├── transcript-data/
429
+ │ └── 2026-03-31_14-32.json # 结构化转写 JSON
368
430
  ├── transcripts/
369
431
  │ └── 2026-03-31_14-32_与王总沟通产品方向.md # 转写文本 + 关键点
432
+ ├── summaries/
433
+ │ └── 2026-03-31_14-32.md # App 下发或 ASR 结果中的总结 Markdown
370
434
  └── index.json # 录音状态索引
371
435
  ```
372
436
 
@@ -399,6 +463,66 @@ openclaw config set plugins.entries.phone-notifications.config.ignoredApps '["co
399
463
  - `id` 用于同一天内去重(写入 `.ids` 索引),不会作为通知正文字段落盘。
400
464
  - `app` 是手机端上报字段;插件落盘时会标准化为 `appName`。
401
465
 
466
+ ### Gateway Native:通知总结进度与结果
467
+
468
+ 用户在 App 里通过普通对话说“总结未读通知”时,Agent 会调用插件工具 `notifications_summary`。插件在工具执行过程中会主动广播 `notification.summary.status`,完成后广播 `notification.summary.result`,App 不需要把入口改成直接 Gateway RPC 也能展示进度与最终总结。
469
+
470
+ `notification.summary.status` 示例:
471
+
472
+ ```json
473
+ {
474
+ "jobId": "sum_20260614031000_ab12cd34",
475
+ "phase": "summarizing",
476
+ "status": "running",
477
+ "message": "正在整理通知摘要:2/5 个分片",
478
+ "doneChunks": 2,
479
+ "totalChunks": 5,
480
+ "percent": 40
481
+ }
482
+ ```
483
+
484
+ `notification.summary.result` 示例:
485
+
486
+ ```json
487
+ {
488
+ "jobId": "sum_20260614031000_ab12cd34",
489
+ "status": "complete",
490
+ "ok": true,
491
+ "message": "通知总结已完成",
492
+ "title": "未读通知总结",
493
+ "markdown": "# 未读通知总结\n\n共整理 700 条通知..."
494
+ }
495
+ ```
496
+
497
+ 如果 App 需要绕过 Agent、直接发起通知总结并展示进度,也可以调用:
498
+
499
+ ```json
500
+ {
501
+ "type": "req",
502
+ "id": "sum_abc",
503
+ "method": "notifications.summary.start",
504
+ "params": {
505
+ "limit": 1000,
506
+ "chunkSize": 150,
507
+ "from": "2026-06-13T09:00:00+08:00"
508
+ }
509
+ }
510
+ ```
511
+
512
+ 接口会立即返回:
513
+
514
+ ```json
515
+ {
516
+ "ok": true,
517
+ "jobId": "sum_20260614031000_ab12cd34",
518
+ "status": "queued",
519
+ "event": "notification.summary.status",
520
+ "resultEvent": "notification.summary.result"
521
+ }
522
+ ```
523
+
524
+ 随后插件通过 `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` 查询当前状态和结果。
525
+
402
526
  ### HTTP 备选:`POST /notifications`
403
527
 
404
528
  ```http
@@ -468,6 +592,12 @@ openclaw ntf search --app Feishu --conversation-type group
468
592
 
469
593
  # 生成轻量通知摘要输入,适合 Agent 总结大量通知
470
594
  openclaw ntf summary --limit 700
595
+
596
+ # 创建并运行分片总结任务,适合 1000+ 条通知
597
+ openclaw ntf summary-job create --limit 3000 --chunk-size 150 --max-content 120
598
+ openclaw ntf summary-job run <jobId> --max-chunks 30 --include-result
599
+ openclaw ntf summary-job status <jobId>
600
+ openclaw ntf summary-job result <jobId>
471
601
  ```
472
602
 
473
603
  ### 通知统计
@@ -524,14 +654,19 @@ openclaw ntf monitor disable boss-alert
524
654
  openclaw ntf monitor delete boss-alert --yes
525
655
  ```
526
656
 
527
- ### 灯效控制
657
+ ### 灯效规则
658
+
659
+ 灯效规则工具调用独立 Notification Intelligence Agent 的插件侧 API:
660
+ `/api/plugin/notification-intelligence/light-rules`。鉴权只使用
661
+ `X-Api-Key-Id`,不调用 App 端 JWT 接口,也不调用
662
+ `preview` / `evaluate` / `import`。
528
663
 
529
664
  ```bash
530
665
 
531
- # 启用灯效控制工具
666
+ # 启用云端灯效规则工具(lightrules_list/create/update/delete)
532
667
  openclaw ntf light setup
533
668
 
534
- # 直接发送灯效指令到硬件设备(需要先 set-api-key
669
+ # 调试遗留命令:直接发送灯效指令到硬件设备(OpenClaw 对话不再注册 light_control
535
670
  openclaw ntf light send \
536
671
  --segments '[{"mode":"wave","duration_s":4,"brightness":192,"color":{"r":255,"g":0,"b":0}}]'
537
672
 
@@ -557,8 +692,9 @@ src/
557
692
  ├── logger.ts # 日志模块
558
693
  ├── recording/
559
694
  │ ├── handler.ts # recordings.sync 核心处理流程
560
- │ ├── asr.ts # ASR 调度器(local / api / yoooclaw)
561
- │ ├── whisper-local.ts# 本地 Whisper 转写(环境检测、模型下载、格式转换、推理)
695
+ │ ├── result-writer.ts # recordings.result.write 写入转录/总结
696
+ │ ├── asr.ts # Legacy ASR 调度器(api;local 已停用)
697
+ │ ├── whisper-local.ts# Legacy 本地 Whisper 兼容模块
562
698
  │ ├── downloader.ts # 从 OSS 下载音频文件
563
699
  │ ├── storage.ts # 录音本地存储管理(index.json)
564
700
  │ └── state-machine.ts# 录音状态机
@@ -578,6 +714,53 @@ src/
578
714
  └── light-send.ts # light send 子命令
579
715
  ```
580
716
 
717
+ ## 发布与运维(CI/CD)
718
+
719
+ 发布采用 release branch flow:`master` 只保存已发布的稳定基线,开发/测试/beta/正式发布都在 `release/**` 分支完成。
720
+
721
+ ### 版本发布
722
+
723
+ 在 `release/**` 分支上执行版本命令(要求工作区干净、本地与 `origin/<当前 release 分支>` 完全一致):
724
+
725
+ ```bash
726
+ bun run release:patch # 预发布转正 或 稳定版 patch+1
727
+ bun run release:minor
728
+ bun run release:beta # 递增 beta.N
729
+ ```
730
+
731
+ 命令会提交 package 版本号、打附注 tag 并推送,触发 `release.yml`。
732
+
733
+ ### 发布流水线(Artifact Promotion 制品晋级)
734
+
735
+ 按「一次构建、同字节晋级」分三个 job 串行执行(当前无人工审批关卡,全自动):
736
+
737
+ 1. **build**:校验 tag 在 `release/**` 上、tag 与 package.json 版本一致,构建一次并 `bun pm pack`,算出 `SHA256SUMS`,通过 `actions/upload-artifact` 传给后续 job。此 job 不挂 environment。
738
+ 2. **deploy-test**(environment `test`):**不重新构建**,校验 sha256 后把同一份 tgz 上传到测试 OSS 稳定渠道,重新生成测试安装脚本,发飞书「RC 已上测试 OSS」。
739
+ 3. **deploy-prod**(environment `production`):deploy-test 成功后自动继续,下载同一份产物、再次 sha256 校验,原样上传线上 OSS(含 `latest`/`beta` 渠道标记),重新生成线上安装脚本,`npm publish` 现成 tarball(仅稳定版,不重新构建),创建 GitHub Release(直接挂这份 tgz),发飞书「已上线」。
740
+
741
+ `environment: test` / `production` 仅用于部署轨迹追踪与未来按需加保护(当前两者 `protection_rules` 均为空,不形成关卡)。beta 走同一条流水线、跳过 npm publish。若日后想恢复上线审批,在 `production` environment 开启 Required reviewers 即可,无需改 workflow。
742
+
743
+ > 安装脚本(`install*.sh` / `install.ps1` / `install-core.mjs`)是环境相关的薄胶水层,不参与晋级,每个环境各自重新生成;只有 tgz 走同字节晋级。部署时安装脚本会同时归档到 `v{版本}/installer/`,作为回滚恢复源。
744
+
745
+ ### 回滚
746
+
747
+ 回滚是把渠道指针(`latest`/`beta`)重指到已存在、不可变的 `v{版本}/` 目录,不重新构建。GitHub → Actions → **Rollback OSS Channel** → Run workflow,填入 `version`(含 `-` 自动识别 beta 渠道)与 `environment`(`test`/`production`):
748
+
749
+ - 脚本先 HEAD 校验目标 tgz 存在,再重指渠道标记,并从 `v{版本}/installer/` 恢复安装脚本。归档缺失(早于本机制上线的历史版本)时只回退 tgz 指针并显式告警,不静默跳过。
750
+ - 当前 `test` / `production` 回滚均免审批(环境无保护规则)。`environment` 声明保留,便于日后需要时单独给生产回滚加 Required reviewers。
751
+
752
+ 不自动回滚的部分(手动 SOP):
753
+
754
+ - **npm**:无法安全 unpublish,对问题版本 `npm deprecate '@yoooclaw/phone-notifications@<bad>' '请安装上一版'`。OSS 是主要分发路径。
755
+ - **GitHub Release**:不自动删除,手动编辑标注「已回滚」,保留追溯。
756
+
757
+ ### CI/CD 配置(GitHub 仓库设置)
758
+
759
+ - **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)。
760
+ - **无审批关卡**:`test` / `production` environment 当前 `protection_rules` 为空,不形成审批。如需恢复上线审批,给 `production` environment 加 Required reviewers 即可,无需改 workflow。
761
+ - OSS / npm 凭据(`OSS_*` / `NPM_TOKEN`)走 repo 级 secrets;测试 OSS 共享 `OSS_ACCESS_KEY_ID` / `OSS_ACCESS_KEY_SECRET`,测试 bucket / region / 域名在 workflow 内以明文常量内置。
762
+ - 备注:`APP_NAME_MAP_URL` 目前未配置为 secret,构建时解析为空字符串(与改造前一致);如需启用应用名映射,补上该 repo 级 secret。
763
+
581
764
  ## License
582
765
 
583
766
  MIT