@windsland52/maafw-live 0.0.0-stage → 0.3.0

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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +278 -2
  3. package/bin/maafw-live.mjs +25 -0
  4. package/docs/daemon-protocol.md +116 -0
  5. package/docs/keyframe-evidence-contract-v0.md +61 -0
  6. package/docs/keyframe-retention-contract-v0.md +236 -0
  7. package/lib/client/daemon.js +234 -0
  8. package/lib/client/daemon.js.map +1 -0
  9. package/lib/commands/annotate.js +172 -0
  10. package/lib/commands/annotate.js.map +1 -0
  11. package/lib/commands/calibrate.js +61 -0
  12. package/lib/commands/calibrate.js.map +1 -0
  13. package/lib/commands/color.js +73 -0
  14. package/lib/commands/color.js.map +1 -0
  15. package/lib/commands/crop.js +228 -0
  16. package/lib/commands/crop.js.map +1 -0
  17. package/lib/commands/env.js +215 -0
  18. package/lib/commands/env.js.map +1 -0
  19. package/lib/commands/index.js +64 -0
  20. package/lib/commands/index.js.map +1 -0
  21. package/lib/commands/input.js +219 -0
  22. package/lib/commands/input.js.map +1 -0
  23. package/lib/commands/kf.js +131 -0
  24. package/lib/commands/kf.js.map +1 -0
  25. package/lib/commands/reco.js +211 -0
  26. package/lib/commands/reco.js.map +1 -0
  27. package/lib/commands/repl.js +698 -0
  28. package/lib/commands/repl.js.map +1 -0
  29. package/lib/commands/runtime.js +514 -0
  30. package/lib/commands/runtime.js.map +1 -0
  31. package/lib/commands/skill.js +169 -0
  32. package/lib/commands/skill.js.map +1 -0
  33. package/lib/commands/timing.js +229 -0
  34. package/lib/commands/timing.js.map +1 -0
  35. package/lib/commands/version.js +66 -0
  36. package/lib/commands/version.js.map +1 -0
  37. package/lib/daemon/framed.mjs +2850 -0
  38. package/lib/daemon/reco_child.mjs +146 -0
  39. package/lib/exec.js +96 -0
  40. package/lib/exec.js.map +1 -0
  41. package/lib/flags.js +42 -0
  42. package/lib/flags.js.map +1 -0
  43. package/lib/image.js +90 -0
  44. package/lib/image.js.map +1 -0
  45. package/lib/index.js +139 -0
  46. package/lib/index.js.map +1 -0
  47. package/lib/interface/index.js +12 -0
  48. package/lib/interface/index.js.map +1 -0
  49. package/lib/interface/jsonc.js +95 -0
  50. package/lib/interface/jsonc.js.map +1 -0
  51. package/lib/interface/load.js +307 -0
  52. package/lib/interface/load.js.map +1 -0
  53. package/lib/interface/override.js +103 -0
  54. package/lib/interface/override.js.map +1 -0
  55. package/lib/interface/plan.js +188 -0
  56. package/lib/interface/plan.js.map +1 -0
  57. package/lib/pkg.js +18 -0
  58. package/lib/pkg.js.map +1 -0
  59. package/lib/protocol.js +90 -0
  60. package/lib/protocol.js.map +1 -0
  61. package/lib/runtime/actions.js +110 -0
  62. package/lib/runtime/actions.js.map +1 -0
  63. package/lib/runtime/keyframes.js +73 -0
  64. package/lib/runtime/keyframes.js.map +1 -0
  65. package/lib/runtime/session.js +86 -0
  66. package/lib/runtime/session.js.map +1 -0
  67. package/lib/types/client/daemon.d.ts +86 -0
  68. package/lib/types/commands/annotate.d.ts +3 -0
  69. package/lib/types/commands/calibrate.d.ts +7 -0
  70. package/lib/types/commands/color.d.ts +9 -0
  71. package/lib/types/commands/crop.d.ts +3 -0
  72. package/lib/types/commands/env.d.ts +2 -0
  73. package/lib/types/commands/index.d.ts +13 -0
  74. package/lib/types/commands/input.d.ts +17 -0
  75. package/lib/types/commands/kf.d.ts +10 -0
  76. package/lib/types/commands/reco.d.ts +23 -0
  77. package/lib/types/commands/repl.d.ts +2 -0
  78. package/lib/types/commands/runtime.d.ts +48 -0
  79. package/lib/types/commands/skill.d.ts +17 -0
  80. package/lib/types/commands/timing.d.ts +64 -0
  81. package/lib/types/commands/version.d.ts +3 -0
  82. package/lib/types/exec.d.ts +31 -0
  83. package/lib/types/flags.d.ts +58 -0
  84. package/lib/types/image.d.ts +27 -0
  85. package/lib/types/index.d.ts +1 -0
  86. package/lib/types/interface/index.d.ts +11 -0
  87. package/lib/types/interface/jsonc.d.ts +9 -0
  88. package/lib/types/interface/load.d.ts +113 -0
  89. package/lib/types/interface/override.d.ts +33 -0
  90. package/lib/types/interface/plan.d.ts +47 -0
  91. package/lib/types/pkg.d.ts +6 -0
  92. package/lib/types/protocol.d.ts +108 -0
  93. package/lib/types/runtime/actions.d.ts +166 -0
  94. package/lib/types/runtime/keyframes.d.ts +43 -0
  95. package/lib/types/runtime/session.d.ts +53 -0
  96. package/lib/types/update-check.d.ts +20 -0
  97. package/lib/types/usage.d.ts +3 -0
  98. package/lib/update-check.js +97 -0
  99. package/lib/update-check.js.map +1 -0
  100. package/lib/usage.js +50 -0
  101. package/lib/usage.js.map +1 -0
  102. package/package.json +81 -4
  103. package/skills/README.md +51 -0
  104. package/skills/maafw-live/SKILL.md +263 -0
  105. package/skills/maafw-live/references/examples.md +238 -0
  106. package/skills/maafw-live/references/pitfalls.md +118 -0
  107. package/skills/maafw-live/references/workflows.md +159 -0
@@ -0,0 +1,236 @@
1
+ # 关键帧留存与引用契约 — v0
2
+
3
+ > **状态(2026-10-07 更新):核心条款已实现并完成首轮验收**——捕获身份(session/daemon/代次/seq)、
4
+ > per-frame 尺寸(`fw/fh`,ROI 换算不再用全局尺寸)、L0 双区缓存 + 字节预算、显式升格(tmp+rename、
5
+ > 幂等、冲突拒绝)、manifest v1 与离线解析、库磁盘配额、**库帧 → L2 裁剪与出处登记(离线,不需要设备)**。
6
+ > 验收记录见 §9;接口对已落地部分视为冻结,
7
+ > 未落地项(跨库副本冲突的读侧检测等)仍标 v0 边界。缓存数值为实测环境(MuMu v5 + maa-node 5.14.2)结论。
8
+ >
9
+ > 主契约以本文为准。[源码核查与对齐备注](keyframe-evidence-contract-v0.md) 只记录实现基线和补充裁决,不定义第二套接口。技能侧文档在 MaaTutorial 仓(`skills/maafw-pipeline/references/state-plan.md`、`skills/docs/state-plan-review-brief.md`),同步消费本契约。
10
+ >
11
+ > 文中源码行号对应重构前基线 commit `85dae0e`(2026-10-03 核查),仅用于定位;结论不依赖行号,重构后需重定位。
12
+
13
+ > 需求来源:state-plan v0 的 evidence 引用(`kf:xxxx`)此前在工具侧没有落点。运行工具(maafw-live)有实时观察底座——常驻 daemon:连续采样 + 变化检测 + 环形缓冲——但环内只保留降采样小图,原图不留存;**当事后决定保留某帧时,那次捕获的原图往往已经不存在**(现调 screencap 拿到的是另一个时刻,对消失的一次性状态没有帮助)。
14
+ >
15
+ > 本契约只定一件事:**如何把短期实时信息,选择性地留存为可跨工作会话复用的开发证据(以 L0 观测帧为载体)**。沿用既有架构裁决("持续观察、同源关联";独立轨迹记录层已被否决——"agent 边探索边写,写下的 pipeline 就是记录"),因此这是**在现有观测流上补关联和留存**,不造第二套采集链,不造资产平台,不重建轨迹系统。
16
+
17
+ ## 0. 责任边界
18
+
19
+ | 层 | 负责什么 | 不负责什么 |
20
+ | --- | --- | --- |
21
+ | maafw-live(生产端) | 采集事实、执行动作、输出节点事件、导出/留存观测帧 | 不判断业务上完成了哪个计划状态 |
22
+ | harness 对接层(关键帧工具) | 进程生命周期、升格决策与登记、观测与计划的关联 | 不建立第二套设备采集链 |
23
+ | state-plan | 需求、状态与转移、实现映射、进度、证据引用 | 不复制帧流、运行日志、设备会话 |
24
+
25
+ > **与 MaaEvidenceKit(MEK)的边界**:MEK 管**诊断证据**——从日志、项目文件与仓库文档提取确定性事实(`maa-evidence/v1`,evidence ID 由来源定位决定),服务 issue 分析;本契约管**观测帧**——设备原图的捕获与留存(`kf:` ID 为关键帧库序号,携带会话 / 分辨率 / 帧序身份),服务 pipeline 编写。两者不共享 schema、不互相索引;将来若需关联(如诊断报告引用某帧),按完整 ID 单向引用,不合并。本契约内"证据"一词除引用 state-plan 的 `evidence` 语义外,一律指留存帧。
26
+
27
+ ## 1. 三项保证
28
+
29
+ **采得到**(覆盖,不承诺完备)
30
+
31
+ - 现状:daemon 连续采样(活跃 8fps / 静默约 1Hz,`framed.mjs:195,440`);输入动作不触发即时抓帧或提频(`framed.mjs:630-653`)。
32
+ - 契约要求:输入动作边界(前、后)各保留一张原图进 L0 缓存(§2)——窄增强,落在现有 input / 节点事件出口;可选增强(不门禁):变化后短时连拍、输入后短时提频。
33
+ - 明确不承诺:所有瞬态必被捕获。低频阶段的一次性弹窗仍可能漏采;单纯增大缓存不解决漏采。漏采的状态就是没有证据——state-plan 侧按 guessed 处理,这是既有的诚实语义。
34
+
35
+ **留得住**(升格)
36
+
37
+ - 现状:环形缓冲只存降采样小图(默认长边 480、120 张,`framed.mjs:195,383-390`);流式路径上原图解码后即弃(`framed.mjs:429-432`);`screencap` 能拿原图但那是"另一个时刻"。
38
+ - 契约要求(工具侧新增的窄能力):
39
+ 1. 与预览环并行的 **L0 原图有界缓存**,分两区:**滚动区**——最近 K 张原图(默认建议 16),保证"刚看到就能留";**锚区**——change/stable 事件锚点帧、动作边界帧、run 起止帧(默认建议 32)。各自 FIFO,总容量有界可配。
40
+ 2. **升格**:从 L0 缓存取原图落盘进本地关键帧库 + 登记(§4),返回 id / path / sha256 / 保留状态。
41
+ 3. **保留状态必须显式**:元数据还在 ≠ 像素可读。L0 已淘汰时升格如实报失败,允许降级导出小图作参考帧(§6)。
42
+ - 不做:全量永久保存、自动判定"哪些帧重要"(升格决策在 harness/agent/人)、独立资产平台。
43
+
44
+ **对得上**(引用与坐标)
45
+
46
+ - 留存记录必须携带(§4):会话/连接代次与设备来源、捕获时间与捕获时帧序、**捕获时控制器分辨率**、小图尺寸与缩放关系、持久文件定位、内容完整性标识。
47
+ - 契约动机即当时的现状缺陷:历史帧 ROI 换算用当前全局控制器尺寸而非该帧捕获时尺寸,重连/改分辨率后旧帧会被错误裁剪(**已修**:每帧携带 `fw/fh`,换算用捕获时尺寸,R1 验收通过)。坐标元数据是留存帧可用(可复核)的前提,不是锦上添花。
48
+
49
+ ## 2. 图像层级(保留哪种图像)
50
+
51
+ | 层 | 是什么 | 用途 | 可否作 state 证据本体 |
52
+ | --- | --- | --- | --- |
53
+ | L0 原图 | 控制器分辨率、捕获时刻的整帧 | 唯一证据本体;模板/测试图的升格源 | ✅ |
54
+ | L1 预览小图 | 环内降采样帧(默认长边 480) | 人看、变化检测、粗对齐;识别单测可接受其作输入(结果须标注来源尺寸) | ❌ 只作参考帧 |
55
+ | L2 派生图 | 从 L0 脱敏裁剪、语义命名后的模板图、测试图 | 提交进仓库(state-plan §5 升格路径) | 本身即交付物 |
56
+
57
+ 规则:
58
+
59
+ - **升格仅限 L0**。L0 已淘汰 → 只能导 L1 参考帧,state 证据按 unverified 计。
60
+ - L0 原图永不修改;脱敏裁剪只产生 L2 派生物。
61
+ - 库内 L0 可直接作 L2 的裁剪源(`crop --from-kf <完整 ID>`):**不需要设备,也不需要捕获它的那次会话还活着**
62
+ ——这正是"不可复现状态"要模板时的用法。裁剪只读库帧(字节与库目录都不变),产物落在 `--out` 处。
63
+ - 关键帧库内 L0 以"文件 + 登记记录"存在,不进 git(沿用 state-plan 存储分层:体积无控 + 账号隐私 + 过程资产)。
64
+
65
+ ## 3. 什么情况下留存
66
+
67
+ 自动候选(生产端/对接层,进 L0 缓存即可,**不等于升格**):
68
+
69
+ - change / stable 事件锚点帧;
70
+ - 输入动作边界帧(前、后);
71
+ - run 起止帧、节点 ok/fail 事件时刻帧。
72
+
73
+ 显式升格(决策在 agent / 人):
74
+
75
+ - agent 在动线中认定某帧是状态证据(观测到新 state、异常弹窗、不可复现状态)→ 指定 seq 或"最新帧"升格;
76
+ - 升格后引用写进 state-plan 的 evidence。
77
+
78
+ 容量淘汰:L0 缓存 FIFO,淘汰即不可升格——动线纪律是**"看到即升格,不攒到最后"**。
79
+
80
+ ## 4. 如何唯一引用
81
+
82
+ - **kf ID 由关键帧库 manifest 分配**:建议 `kf:<库UUID>:<零填充序号>`。库内序号单调递增、永不复用;库 UUID 创建后持久保存,复制库保留身份,新建库生成新身份。调用方把完整 ID 当不透明字符串,不解析其中序号。
83
+ - 裸 `kf:0142` 只可作展示简称,不能作为跨库持久引用:两个库都可能有 0142,哈希若只存各自 manifest 内也不能防止误指向。库副本在 v0 按单写者管理,不支持副本独立写入后自动合并;发现同 ID 对应冲突内容时必须报错,不能任选一份。
84
+ - manifest 每条记录(关键帧库本地,如 `frames/manifest.json`,与 L0 文件同库):
85
+
86
+ | 字段 | 说明 |
87
+ | --- | --- |
88
+ | schema | manifest 版本;未知版本返回 unsupported,不默认为当前版 |
89
+ | id | 完整 `kf:<库UUID>:<序号>`;示例值不代表实际回执 |
90
+ | file | L0 文件的库内相对路径,不以本机绝对路径决定身份 |
91
+ | sha256 | PNG 文件字节的完整性摘要;不是感知 hash,也不独自证明来源真实性 |
92
+ | session | daemon 实例 UUID、连接代次与目标来源(kind / target);地址 / hwnd 只是当时定位,不是永久设备身份 |
93
+ | capturedAt | 捕获时间,ISO 8601 带时区;同进程排序另用 captureSeq |
94
+ | captureSeq | 捕获时帧序,与 session / 连接代次联合定位,不单独作为帧身份 |
95
+ | ctrlW / ctrlH | **捕获时**控制器图像实际尺寸;控制器基准配置同时留档 |
96
+ | smallW / smallH / scale | 当时小图尺寸与缩放关系;无小图则明确为空,不借用最新帧数据 |
97
+ | source | change / stable / action-before / action-after / run-start / run-end / node-ok / node-fail / explicit |
98
+ | note | 脱敏语义说明,不含账号信息 |
99
+
100
+ - **关联强度(建议术语)**:
101
+ - `bound`:该留存帧就是引擎识别所用的那张图——**当前节点事件接口不提供,不许冒称**;
102
+ - `adjacent`:时间邻近关联——节点事件记录的是回调时最新帧序(`framed.mjs:550-565`),属于此类。
103
+ - 关联强度属于“事件与图像的关系”,不是图片文件的属性,也不等于业务验证结果。当前节点事件关联按 adjacent 处理;转移级引用若只有邻近帧,只能作为线索,不能证明动作导致了目标状态。
104
+
105
+ ### 4.1 最小操作语义(名称及传输格式待定)
106
+
107
+ - **留存选定捕获**:输入 session / 连接代次 / captureSeq;若选择“最新”,必须在接受请求时固定具体捕获身份。已淘汰则失败,绝不重新截图或改取更新帧冒充指定帧。同一捕获与图像规格的重试可以返回原对象,不得改写旧 ID 的含义。
108
+ - **发布留存结果**:先固定像素并写 L0 文件,再发布 manifest 记录;半写文件、磁盘失败、配额不足不能返回成功引用。路径由本地库管理,不能覆盖 preview 或既有留存。
109
+ - **按完整 ID 帧解析**:不依赖 daemon 存活。至少返回 available / missing / corrupt / unsupported;仅 available 提供经过完整性检查的图像路径。missing 表示本机不可复核,不表示历史从未观测。
110
+ - **保留与清理**:已升格对象不随 L0 热缓存淘汰,不被后台 TTL/LRU 静默删除。配置本地库配额;满额时拒绝新增留存并报告,由用户处理清理或扩容。显式删除后保留缺失诊断,不自动重用 ID。
111
+ - **隐私**:设备地址、完整来源和图像留在本地;计划只登记完整 ID 与脱敏说明。复制本地库、把 L2 提交仓库、向外部服务上传是不同操作,不自动串联。
112
+
113
+
114
+ ## 5. 坐标如何解释
115
+
116
+ - 坐标 / ROI 必须声明所属图像空间。控制器图像指框架截图链路交付的图像,不一定是设备物理屏幕原始分辨率;L1 的识别 box 属于小图空间,不能直接抄成 pipeline ROI。
117
+ - 工具侧要求:历史帧 ROI 换算必须用 per-frame 尺寸(**已实现并验收**:`frame_get` 与变化区域换算均用该帧捕获时尺寸)。
118
+ - 与 state-plan 的 `baseline: 720` 的关系:baseline 从项目 controller 配置获取,不替代每帧实际尺寸。换算是待补能力,不能说当前识别测试已经自动完成;缺少转换依据时拒绝用于坐标生成。
119
+ - L2 记录来源及裁剪 / 缩放变换。脱敏或裁剪可能去掉识别上下文,必须重核 hits / box 断言;不原地修改 L0,不继承原图的全部验证结论。
120
+ **已实现**:`crop` 写 `<out>.prov.json`(schema 1,回执里同时给 `provenance`),记
121
+ `derivedFrom`(来源帧身份:完整 `kf:` ID 或热缓存 seq + sha256 + **捕获时**尺寸)、`transform`(宽松框 →
122
+ 裁剪框、无缩放无重采样)、`selfMatch`(同帧自匹配得分与位置判据)、`cross`(跨帧验证,未做即 null)。
123
+ 落点缺省**在模板旁**(复制 / 改名时出处跟着走);`--prov-out <dir>` 可把它写到指定目录下(同名),
124
+ 此时不变量是**同一相对路径**而不是同一目录——模板要进资源包(框架会整目录打包给用户)而出处属于过程资产时,
125
+ 这样资源包里才只有框架要加载的东西。约定的目录根由项目自定(例如与 `image/` 同构的 `state-plan/provenance/`)。
126
+ **来源帧也留档**:`--keep-source` 在热缓存路径下把**本次裁剪用的那一帧**(按 seq,不取最新帧)升格进库,
127
+ 出处随即从"只有一个随进程消失的 seq"升级为可复核的 `kf:` 身份;没用它而出处是热缓存来源时,回执**如实警告**
128
+ (出处无从复核)——契约的模型是 L0 缓存 → 选择性升格 → 从库帧裁 L2,这一步以前只能靠人手补。
129
+ 字段是白名单:**设备地址 / 目标 / daemon 身份不落进 L2**——L2 要提交进仓库,设备来源留在本地 manifest(§4.1)。
130
+ 模板空间必须与识别空间一致:源帧捕获时尺寸 ≠ 当前控制器尺寸时如实警告(§5 的坐标卫生要求)。
131
+
132
+ ## 6. 何时不可复核(降级阶梯)
133
+
134
+ | 情形 | 判定 |
135
+ | --- | --- |
136
+ | 引用无对应 manifest 记录 / 文件丢失 | unverified |
137
+ | sha256 不匹配(文件损坏或被改) | unverified |
138
+ | 元数据不全(缺 ctrlW/ctrlH 或 session) | unverified(用于 ROI / 识别测试时;可作一般参考) |
139
+ | 只有 L1 小图(L0 已淘汰) | 参考帧,不可作 state 证据本体 |
140
+ | 节点事件关联未标注强度 | 一律按 adjacent 计 |
141
+
142
+ 本文的“可复核”只表示**文件在、哈希对、元数据全**,不表示 state 已确认、识别通过或任务成功。统一用 available 表示解析可用,避免与 state-plan 的 tested 混淆;missing / corrupt / unsupported 在计划投影中可统一提示“当前不可复核”,但保留具体原因。
143
+
144
+ 留存帧不可复核不抹去历史观测和实现进度;只是不能继续以它为当前落笔依据。需补观测或恢复像素后核对。L1-only 按本文较严格的策略只作为参考,不得上采样冒充 L0。
145
+
146
+ ## 7. 工具侧现状与待修(2026-10-03 源码核查快照)
147
+
148
+ > **现状(2026-10-08 复核)**:下表 #1、#2 两条**已修**,别再当现行缺陷读——`reco_test` 的参数用例改由父进程
149
+ > 折叠好整包下发(`reco_child.mjs` 只透传、不再自行折叠),`run` 的退出码按任务级 `record.ok` 定。
150
+ > 表内的位置与影响列是**当时的核查记录**,行号已随之后的重构漂移。
151
+ > 另:#4 的"原图留存"已由 L0 缓存(滚动区/锚区,输入边界帧自动入锚区)落地;#5 的跨会话帧身份已由
152
+ > 关键帧库 ID 补上("节点事件 seq 为回调时最新帧"这一半仍成立)。
153
+
154
+ | # | 问题 | 位置 | 对契约的影响 |
155
+ | --- | --- | --- | --- |
156
+ | 1 | **(已修)reco 参数静默丢失**:父进程构造 cases 下发,子进程只读 param/sweep → 一切识别参数退化为 {} 单例、阈值扫描失效;不报错、结果照出 | `framed.mjs:697-712` / `reco_child.mjs:25-37` | 修复前识别单测结果**不可作为状态证据**(测的参数不是你以为的参数) |
157
+ | 2 | **(已修)CLI 退出码不反映任务结果**:任务结果在 `record.ok`,daemon 返回外层 `ok:true`,CLI 按外层定退出码 → 任务失败仍退出 0,human 摘要还打"完成" | `framed.mjs:604,618` / `runtime.ts:325,334` | tested 判定输入必须是任务级 `record.ok`(JSON 信封 `data.record.ok`),不是退出码;EXIT.FINDINGS(3) 只用于调用级失败(资源未解析、缓冲为空等) |
158
+ | 3 | **历史帧 ROI 用当前全局尺寸换算** | (已修;原基线 `framed.mjs:480-481`) | §5 的契约要求(per-frame 尺寸),R1 验收通过 |
159
+ | 4 | 环内只存小图、原图不留存;输入不触发抓帧/提频 | `framed.mjs:195,383-390,429-432,630-653` | §1"留得住/采得到"的缺口本体,即本契约的新增能力诉求 |
160
+ | 5 | seq 为进程内序号,无跨会话帧身份;节点事件 seq 为回调时最新帧 | `framed.mjs:387,550-565` | §4 关联强度术语;captureSeq 仅考古用 |
161
+
162
+ 动线纪律(非缺陷,写入防误用):
163
+
164
+ - **单会话单控制器**:一次性命令每次 spawn 自己的 daemon,与面板 daemon 并存 = 同设备两控制器互扰(定案红线)。交互探索用 repl,验证用一次性 `run --project`,不并发。
165
+ - **一次性 run 不起流,seq 恒为 0**:帧序对齐只在 repl / client 订阅路径有效。state-plan 证据引用不依赖 seq(§4 captureSeq 仅考古)。
166
+ - **长流程分段跑**:`run --timeout` 缺省 30s、`0` 表示不自动停(300s 硬上限已移除,停止权交调用方);
167
+ 长流程仍按 subflow 分段——与滚动开发动线天然一致。
168
+
169
+ **消费注意(现有接口,核查基线)**
170
+
171
+ 新能力落地前,消费现有接口注意:
172
+
173
+ - 优先接 `@windsland52/maafw-live/client` 的 `spawnDaemon`、`call`、`subscribe`,不要解析会截断长结果的 REPL 文本。
174
+ - frame 订阅回调拿到 `{seq, t, hash, diff, w, h}`;hash 是感知哈希,不是文件 SHA-256。
175
+ - frame_get 返回 `{ok, path, bytes, w, h, seq, t, diff}`,导出的是小图或其裁剪。
176
+ - reco_test 返回结果列表,每项有 param / ms / ok / detail;输入小图时 box 属于小图空间,不自动等于 pipeline 坐标。
177
+ - run 返回 `{ok, record}`,业务成功在 `record.ok`;外层调用完成不等于任务成功。
178
+ - 节点 seq 可被后续回调更新;startSeq 不是严格的开跑前截图,framesCaptured 是进程累计值,不据此计算本次无损覆盖。
179
+ - 传输回复与 data 内有不同层级的 ok;业务失败不一定让客户端 Promise reject。
180
+
181
+ ## 8. 对 state-plan v0 的落点
182
+
183
+ 1. `frames:` 定位为证据引用登记,完整 ID 与本契约对齐;不复制本地 manifest,不把原图缓存描述为已实现。
184
+ 2. checkpoint 仅表示开发断点,由 agent 维护。设备最后确认位置另存本地,至少含目标 / session、state、证据与确认时间;**record.nodes 是执行记录,不是设备位置断言**。新动作、画面变化、重连、重启或人工接管后,操作前重新观测;歧义允许 unknown。
185
+ 3. tested 必须结合验证对象、范围、实际结果及被测实现指纹;任务级 `record.ok` 是流程结果的一项输入,不自动升级沿途所有 state。静态识别、节点、局部流程和端到端分别说明;实现变化后提示待复验。
186
+ 4. transitions 可选 `evidence` 在 v0 仅辅助人工核对,不自动门禁;既有“观测源状态、动作目标、动作后实际出口才落笔”的护栏不放松。只有 adjacent 帧不能独自证明边,多出口须逐项核对。选定动作回执及转移观测记录 schema 待后续定义,不重建全量轨迹层。
187
+
188
+ ## 9. 实施与验收边界
189
+
190
+ 建议先完成捕获身份、per-frame 尺寸、L0 缓存和显式升格 / 离线解析,再接 state-plan。自动候选仍由生产端及时入锚区,升格决策保留在 harness / agent / 人;16+32 的容量只是起点,还需字节上限和实测吞吐,不能据此承诺历史保留时长。
191
+
192
+ ### 验收记录(2026-10-05,MuMu v5 模拟器 + maa-node 5.14.2 win32)
193
+
194
+ 可重跑脚本:`node scripts/accept-kf-offline.mjs`(离线,无设备)、
195
+ `node scripts/accept-emulator.mjs [--target <adb地址>]`(需模拟器在线)。
196
+
197
+ - ✅ **同序号跨库不误解析**:完整 ID 含库 UUID,A 库 ID 在 B 库 → missing(O1);裸序号拒绝(O2)。
198
+ ⚠️ **库副本同 ID 双记录**:读侧解析到第一条、不报错——v0 单写者边界(O8,见 §4),
199
+ 写侧同捕获内容不符已明确冲突拒绝(R2d)。
200
+ - ✅ **旧帧捕获后改控制器尺寸,ROI 仍用旧帧依据**(R1,10/10):重连改 `short_side`
201
+ 720→1080 后,旧 seq 同 ROI 裁剪逐字节一致、`ctrlW` 仍报捕获时尺寸、跨空间 ROI 报越界而非静默错剪。
202
+ 实测备注:`adb shell wm size` 改物理分辨率**不传导**——MuMu 上框架所选截图方法按初始化尺寸交付
203
+ (原生 `adb exec-out screencap` 已变 1920x1080,控制器仍交 1280x720),且短边缩放会归一化掉变化;
204
+ 故验收以重连改 shortSide 为触发,这也覆盖契约点名的"重连"场景。
205
+ - ✅ **升格幂等 / 热缓存淘汰 / daemon 重启离线解析**(R2a-c、R2g、R3a)。
206
+ - ✅ **原图已淘汰拒绝升格**(R2g,不取最新帧冒充);**磁盘失败**(manifest 只读注入 rename 失败):
207
+ 明确报错、L0 文件回收、manifest 不发布半成品、恢复后重试成功(R2e-R2f);**库配额耗尽**拒绝新增并报告
208
+ (`test/unit-kf-quota.mjs`;契约 §4.1 语义:不后台静默删除,清理由用户决定)。
209
+ - ✅ **文件 / manifest 损坏、版本未知可分别诊断**(O3-O7):损坏 JSON、未知 schema、缺 frames、
210
+ tmp 残留、文件丢失、sha 不匹配各自独立可诊断,不默认重建、不取最新图补位。
211
+ - ⬜ 动作前后抓帧与动作结果的分别报告、CLI 退出 0 不映射未经验证的转移——属 state-plan 消费侧验收,未在本轮。
212
+
213
+ ### 验收记录(2026-10-07:库帧 → L2 裁剪与出处)
214
+
215
+ 可重跑脚本:`node scripts/accept-crop-fromkf.mjs`(**离线,不需要设备**;需先 `npm run build`,26 条)。
216
+
217
+ - ✅ **库帧即裁剪源**:`crop --from-kf <完整 ID>` 从库内 L0 裁出 L2;回执 `source.kind=kf`、`source.id` 是完整
218
+ `kf:` ID、`seq` 为 `null`(不是热缓存帧序,不冒充);snap 收紧与同帧自匹配照跑(K1/K2)。
219
+ - ✅ **全程无设备**:未连接时 `spaceCheck` 如实为 `{current:null,match:null}`,不冒称已核对当前控制器尺寸;
220
+ 项目只作资源来源(离线解析 interface.json 的 `resource[]`)(K5、C1)。
221
+ - ✅ **身份可复核**:解析之后文件被替换 → sha256 层拒绝;库记录尺寸与像素不一致 → 拒绝(K7/K8)。
222
+ - ✅ **L0 不可变**:裁剪前后库帧字节一致、库目录无新增文件(L2 与出处落在 `--out` 处)(K4)。
223
+ - ✅ **L2 带出处**:`<out>.prov.json` 与回执 `provenance` 逐字段一致(`--prov-out` 时落在指定目录、同名),
224
+ `transform` 记真实裁剪框(无缩放),出处不含设备来源(K3)。
225
+ - ✅ **跨帧验证缺省关**(留存帧对应的状态通常已不在画面上),`--cross` 显式开(K6)。
226
+ - ✅ **分辨率空间守卫**(真机,MuMu v5 + maa-node 5.14.2,Reverse: 1999 / M9A 项目):以 `shortSide 1080`
227
+ 连接(控制器 1920×1080)后从 1280×720 库帧裁剪 → 空间核对报 `≠`、跨帧比对判定为"该比对不成立"
228
+ (不误归因为动态区域)、退出 `FINDINGS(3)`,不静默产出可被误用的模板。
229
+ - ✅ **真机动线(一次性状态)**:Reverse: 1999 大师一区大厅的每日签到弹窗(当日仅此一次)当场升格为库帧;
230
+ 关掉弹窗后**离线**从该库帧裁出「获得物品 / OBTAINED」状态标记(snap 140×58 → 101×23,自匹配 1.0)
231
+ 与物品卡片(120×120,1.0);复核:模板在**留存帧**上命中 `[589,124,101,23]` 得分 1.0,在**当前画面**上
232
+ 未命中——该状态的唯一证据就是这张留存帧。这正是 §1"留得住"与 §2"库内 L0 即原图本体"要支撑的用法。
233
+
234
+ **未定项**(原列表更新):manifest schema 已冻结为 v1(`schema:1`);L0 字节预算与库磁盘配额已实现
235
+ (默认 192MB 内存 / 1GiB 磁盘,`init kfQuotaBytes` 或 `MAAFW_KF_QUOTA_BYTES` 可调);**仍开放**:
236
+ L0 时间维度保留时长、跨库副本冲突的读侧检测、动作回执格式、验证记录 schema。
@@ -0,0 +1,234 @@
1
+ /**
2
+ * MaaFramework 设备 daemon 的客户端(本仓库唯一实现)。
3
+ *
4
+ * daemon 是 `daemon/framed.mjs`:一个独立子进程,经 stdin/stdout 说 JSON 行协议,持有
5
+ * Controller / Resource / Tasker、帧流与环形缓冲。本模块负责 spawn、请求应答配对、超时硬杀
6
+ * 自愈、原生 stderr 日志环、以及帧/事件汇聚——CLI 命令、`maafw-live repl`、宿主插件与任何 headless
7
+ * harness 都用它,避免每个消费者各写一遍同一套管道。
8
+ *
9
+ * 两条不变量:
10
+ * - 一个 DaemonClient = 一个 daemon 子进程 = 一个设备会话(同一台设备同一时刻只能有一个)。
11
+ * - 调用超时即认为 daemon 卡死(maa-node 的 wait() 可能同步阻塞 worker),kill 后下次调用自动重生。
12
+ */
13
+ import { spawn } from 'node:child_process';
14
+ import { existsSync } from 'node:fs';
15
+ import { homedir } from 'node:os';
16
+ import { join } from 'node:path';
17
+ import readline from 'node:readline';
18
+ import { fileURLToPath } from 'node:url';
19
+ const CAP = { frames: 2000, events: 2000, logs: 400 };
20
+ const DEFAULT_TIMEOUT = 30000;
21
+ export function defaultRunDir() {
22
+ return join(homedir(), '.maafw-live');
23
+ }
24
+ /** 定位 daemon 脚本:显式参数 → MAA_DAEMON → 包内 lib/daemon/framed.mjs。 */
25
+ export function resolveDaemonPath(explicit) {
26
+ const candidates = [explicit, process.env.MAA_DAEMON, fileURLToPath(new URL('../daemon/framed.mjs', import.meta.url))];
27
+ for (const c of candidates) {
28
+ if (c && existsSync(c))
29
+ return c;
30
+ }
31
+ throw new Error('找不到 daemon 脚本,候选:' + candidates.filter(Boolean).join(' | '));
32
+ }
33
+ function push(list, item, cap) {
34
+ list.push(item);
35
+ if (list.length > cap)
36
+ list.splice(0, list.length - cap);
37
+ }
38
+ /** 错误对象 → 一行文本(抛出来的形态不可控:Error、字符串、别的什么都有可能) */
39
+ const message = (e) => String(e?.message ?? e);
40
+ export function spawnDaemon(options = {}) {
41
+ const daemonPath = resolveDaemonPath(options.daemonPath);
42
+ const runDir = options.runDir ?? defaultRunDir();
43
+ const defaultTimeout = options.timeoutMs ?? DEFAULT_TIMEOUT;
44
+ const kfQuotaBytes = options.kfQuotaBytes;
45
+ const events = { frames: [], events: [], errors: [], logs: [], preview: null };
46
+ const listeners = new Map();
47
+ const pending = new Map();
48
+ let child = null;
49
+ let exited = false;
50
+ let reqId = 0;
51
+ let calls = 0;
52
+ let restarts = 0;
53
+ /** 当前这次 spawn 的 init 握手(daemon 重启会换新的一条) */
54
+ let initResult = null;
55
+ const emit = (kind, message) => {
56
+ for (const cb of listeners.get(kind) ?? []) {
57
+ try {
58
+ cb(message);
59
+ }
60
+ catch { /* 订阅者的异常不能拖垮客户端 */ }
61
+ }
62
+ };
63
+ const wire = (c) => {
64
+ const rl = readline.createInterface({ input: c.stdout, crlfDelay: Infinity });
65
+ rl.on('line', (line) => {
66
+ let m;
67
+ try {
68
+ m = JSON.parse(line);
69
+ }
70
+ catch {
71
+ /* 非 JSON 行 = maa 原生日志 */
72
+ push(events.logs, line, CAP.logs);
73
+ return;
74
+ }
75
+ const kind = String(m.kind ?? '');
76
+ if (kind === 'reply') {
77
+ const id = Number(m.id);
78
+ const p = pending.get(id);
79
+ if (!p)
80
+ return;
81
+ pending.delete(id);
82
+ clearTimeout(p.timer);
83
+ if (m.ok === false)
84
+ p.reject(new Error(String(m.error ?? 'daemon error')));
85
+ else
86
+ p.resolve(m.data);
87
+ }
88
+ else if (kind === 'frame') {
89
+ const meta = m.meta;
90
+ push(events.frames, meta, CAP.frames);
91
+ if (typeof m.preview === 'string')
92
+ events.preview = m.preview;
93
+ else if (meta && typeof meta.preview === 'string')
94
+ events.preview = meta.preview;
95
+ emit('frame', meta);
96
+ }
97
+ else if (kind === 'event') {
98
+ const ev = m.ev;
99
+ push(events.events, ev, CAP.events);
100
+ emit('event', ev);
101
+ }
102
+ else if (kind === 'stream_error') {
103
+ push(events.errors, String(m.error ?? ''), CAP.logs);
104
+ emit('stream_error', m.error);
105
+ }
106
+ else if (kind === 'stream_stopped') {
107
+ /* daemon 在控制器被销毁(connect 会重建)时主动停流并推这条:它是"你盯着的流没了"的通知,
108
+ * 必须进错误环 + 推给订阅者。曾经只发了不收,调用方只能从 connect 回执间接推断。 */
109
+ push(events.errors, '帧流已停止:' + String(m.reason ?? ''), CAP.logs);
110
+ emit('stream_stopped', m.reason);
111
+ }
112
+ });
113
+ c.stderr?.on('data', (d) => push(events.logs, String(d), CAP.logs));
114
+ c.on('error', (e) => push(events.errors, 'child error: ' + e.message, CAP.logs));
115
+ c.on('exit', (code) => {
116
+ exited = true;
117
+ for (const [, p] of pending) {
118
+ clearTimeout(p.timer);
119
+ p.reject(new Error('daemon 已退出 (code=' + code + '),会话失效,请重新 connect'));
120
+ }
121
+ pending.clear();
122
+ });
123
+ };
124
+ /**
125
+ * spawn 后立刻发 init 并等回执:daemon 身份与落点是宿主真需要的东西(面板要 framesDir、
126
+ * 配额要 kfQuota 才能提前提示),而失败也必须可见——旧实现用 id=-1 发了就不管,
127
+ * 回执被分发链当"未知 id"丢弃,init 失败(比如 runDir 建不出来)完全无从得知。
128
+ * 与 `call` 共用同一条 stdin 管道,所以 init 一定排在后续请求之前。
129
+ */
130
+ const handshake = (c) => new Promise((resolve) => {
131
+ const id = ++reqId;
132
+ const timer = setTimeout(() => {
133
+ pending.delete(id);
134
+ resolve({ ok: false, error: 'init 无应答(' + defaultTimeout + 'ms):对方可能不是本协议的实现' });
135
+ }, defaultTimeout);
136
+ pending.set(id, {
137
+ resolve: (d) => resolve({ ok: true, data: d }),
138
+ reject: (e) => resolve({ ok: false, error: e.message }),
139
+ timer,
140
+ });
141
+ try {
142
+ c.stdin.write(JSON.stringify({ id, cmd: 'init', runDir, ...(kfQuotaBytes ? { kfQuotaBytes } : {}) }) + '\n');
143
+ }
144
+ catch (e) {
145
+ /* spawn 到写入之间子进程就死了 → write 同步抛(EPIPE)。这条也要落到 {ok:false},
146
+ * 否则 init() 会 reject,而它的契约是"失败如实回报,不抛"。 */
147
+ clearTimeout(timer);
148
+ pending.delete(id);
149
+ resolve({ ok: false, error: 'init 发送失败:' + message(e) });
150
+ }
151
+ });
152
+ const ensure = () => {
153
+ if (child && !exited)
154
+ return child;
155
+ if (child)
156
+ restarts += 1;
157
+ child = spawn(process.execPath, [daemonPath, '--child'], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
158
+ exited = false;
159
+ wire(child);
160
+ initResult = handshake(child).then((r) => {
161
+ if (!r.ok)
162
+ push(events.errors, 'init 失败:' + r.error, CAP.logs);
163
+ return r;
164
+ });
165
+ return child;
166
+ };
167
+ const kill = () => {
168
+ if (!child)
169
+ return;
170
+ try {
171
+ child.kill();
172
+ }
173
+ catch { /* ignore */ }
174
+ };
175
+ const call = (cmd, args = {}, timeoutMs = defaultTimeout) => new Promise((resolve, reject) => {
176
+ let c;
177
+ try {
178
+ c = ensure();
179
+ }
180
+ catch (e) {
181
+ reject(e);
182
+ return;
183
+ }
184
+ const id = ++reqId;
185
+ calls += 1;
186
+ const timer = setTimeout(() => {
187
+ pending.delete(id);
188
+ /* 卡死自愈:maa-node 的 wait() 可能同步阻塞 daemon,超时即硬杀,下次调用自动重生 */
189
+ kill();
190
+ reject(new Error('daemon 调用超时:' + cmd + '(' + timeoutMs + 'ms 无应答,已重启 daemon,会话需重新 connect)'));
191
+ }, timeoutMs);
192
+ pending.set(id, { resolve: resolve, reject, timer });
193
+ try {
194
+ c.stdin.write(JSON.stringify({ id, cmd, ...args }) + '\n');
195
+ }
196
+ catch (e) {
197
+ /* 子进程在 spawn 与写入之间就死了 → write 同步抛(EPIPE)。报成可读的失败,
198
+ * 别把 ERR_STREAM_DESTROYED 直接甩给调用方。 */
199
+ clearTimeout(timer);
200
+ pending.delete(id);
201
+ reject(new Error('daemon 请求发送失败(' + cmd + '):' + message(e)));
202
+ }
203
+ });
204
+ return {
205
+ events,
206
+ call,
207
+ init() {
208
+ ensure();
209
+ return initResult ?? Promise.resolve({ ok: false, error: '尚未 spawn daemon' });
210
+ },
211
+ logTail(n = 60) { return events.logs.slice(-n); },
212
+ subscribe(kind, cb) {
213
+ const set = listeners.get(kind) ?? new Set();
214
+ set.add(cb);
215
+ listeners.set(kind, set);
216
+ return () => { set.delete(cb); };
217
+ },
218
+ stats: () => ({ calls, restarts, alive: !exited && child !== null, pid: child?.pid ?? null, daemonPath }),
219
+ close() {
220
+ if (!child)
221
+ return;
222
+ try {
223
+ child.stdin.write(JSON.stringify({ id: -1, cmd: 'shutdown' }) + '\n');
224
+ }
225
+ catch { /* ignore */ }
226
+ const dying = child;
227
+ setTimeout(() => { try {
228
+ dying.kill();
229
+ }
230
+ catch { /* ignore */ } }, 1500).unref();
231
+ },
232
+ };
233
+ }
234
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/client/daemon.ts"],"names":[],"mappings":"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"}