外观
滑动窗口与延时播放
buffered 将观察、落盘、云端上传与首页展示分别调度。应用与 Music 的每次已观测语义变化立即进入有序内存窗口,硬件采用较慢采样和变化阈值;每 5 分钟上传最近 15 分钟的完整窗口。网页通常落后 7 分钟,在本地按时间回放,再将短时间内的多次普通应用/歌曲切换合并展示,减少闪动。首页的合并不会删除时间线中的事件,也不增加 KV 写入。
默认参数
| 参数 | 默认值 | 作用 |
|---|---|---|
| 应用与 Music 变化 | 系统通知优先 | 尽早观察切换,不把通知当作可靠消息队列 |
| Music 兜底采样 | 2 秒 | 异步读取播放状态与元数据,弥补未收到的通知 |
| 电池、系统负载采样 | 30 秒 | 电量按整数百分比/供电变化记;负载按阈值记录;小幅真变化在 120 秒门槛后的成功采样合并记录 |
| 普通本机检查点 | 10 秒 | 将期间的内存变化合并成一次原子保存,安全边界立即保存 |
| 无变化覆盖心跳 | 30 秒 | 更新磁盘中的观测范围,不添加重复状态事件 |
| 上传间隔 | 300 秒 | 每包一次 Worker 请求、一次 KV 写入 |
| 滑动窗口 | 最多 900 秒 | 含起点基线、之后的变化与明确缺口;相邻正常包重叠 600 秒 |
| 正常播放延迟 | 420 秒 | 300 秒分批 + 120 秒传播与拉取余量;不是全球准时保证 |
| 网页拉取 | 60 秒 | 一次获取整个窗口,其余播放在页面内存中完成 |
| 首页普通状态切换 | 通常最多每 2 秒一次 | 短时间连续变化只显示最后候选;隐私遮蔽、清空等立即处理 |
| 云端截止时间 | window_end + 600 秒 | 绝对过期,不因旧包晚到或读取而延长 |
| 包大小/条目上限 | 512 KiB/2048 个事件/128 个缺口 | 内存、磁盘、上传和解析有界;溢出明确记缺口 |
上述采集、落盘与展示值均为当前实现的固定策略,不新增可分别调整的配置键;v2 的上传、窗口和播放策略字段保持不变。新安装默认 buffered,已有 eco/realtime 安装保留原模式,须显式迁移。运行只依赖 macOS 自带工具和 osascript 执行的 JXA,不需要 Node.js、Python 或第三方守护进程。
变化记录、落盘与展示
| 层次 | 行为 | 保留的内容 |
|---|---|---|
| 内存事件 | 应用/Music 每次已观测语义变化立即入队,保留顺序和时间 | 不因 10 秒落盘或 2 秒展示节奏删除这些事件;仍受 900 秒窗口及容量限制 |
| 私有检查点 | 普通变化最多每 10 秒合并写入;无变化时每 30 秒保存覆盖心跳 | 一次写入包含期间所有仍在窗口内的事件 |
| 上传包 | 每 300 秒一次完整窗口 | 与磁盘写入次数、首页画面切换次数无关 |
| API 回放 | 分类接口按服务器时间减 420 秒投影,时间线接口返回原窗口 | 没有首页的 2 秒视觉合并 |
| 首页展示 | 前台应用、Music 和运行列表的普通切换通常最多每 2 秒一次 | 短时间连续变化只显示最后候选,不保证每个原始事件都出现在首页 |
检查点周期从上次成功保存计算;重复的应用/Music 状态不触发新的待保存变化。启动、睡眠、隐私配置变化、上传前后等安全边界立即保存,不等待普通检查点周期。普通事件先存在内存中,意外崩溃可能损失最近约 10 秒尚未保存的变化;这不是掉电无损日志。没有事件变化时只按 30 秒心跳推进已观测覆盖,不制造重复事件。
硬件仍每 30 秒采样。电量按整数百分比记录,或在充电/供电状态变化时记录。系统负载与最后已记录值比较:任一负载的差值达到 max(0.20, |旧值| × 5%) 时记录整个 system;较小但真实的变化在距上次记录达到 120 秒后,于随后一次成功采样时记录最新值,通常约 2 分钟,也可能多一个 30 秒采样周期及任务延迟。数值与 null 之间的转换立即记录。阈值以下的每个微小波动不会全部保留,这项策略减少重复写入和近似相同的事件,不改变 API 的数字字段类型。
首页以原始回放状态作为展示候选,合并后的画面仅供渲染,并不代表精确单一时刻的 API 快照;敏感应用遮蔽、字段清空、音乐停止/不可用、运行列表移除条目、缺口、离线和新会话会立即清除旧显示,不等待 2 秒。相同曲目的封面补充也可以立即显示。这里的“立即”指页面已经取得并回放到该状态之后,不绕过 7 分钟延时或 KV 传播。需要精确消费窗口事件时使用 /api/timeline,或复用未经视觉合并的 WindowPlayback。接入示例
一次窗口如何播放
假设 10:00 开始一个新会话:
| 本机时间 | 本机观察/上传 | 网页行为 |
|---|---|---|
| 10:00 | 记录当前状态作为基线 | 尚无有效上传窗口时显示离线/等待窗口 |
| 10:01 | Code → Safari | 记录应用字段变化,暂不上传 |
| 10:02 | 歌曲 A → 歌曲 B | 记录整段 music 替换,包含曲名和可用封面 URL |
| 10:05 | 上传 10:00–10:05 窗口 | 拉取后保存到页面内存,显示暖机倒计时 |
| 10:07 | 持续采集 | 开始播放 10:00 的基线 |
| 10:08 | 持续采集 | 展示 10:01 的 Safari 切换 |
| 10:09 | 持续采集 | 展示 10:02 的歌曲 B |
| 10:20 | 上传 10:05–10:20 窗口 | 以重叠内容接续播放,不重复播放旧事件 |
首次正常播放约在启动 7 分钟后;首包上传前浏览器尚不知道本机已经启动,可能显示离线/等待窗口,取得首包后才有暖机倒计时。网络与平台延迟可能使等待更久。网页不会把窗口末尾的最新状态直接跳到播放头;展示层只合并当前播放头附近的短时间普通变化,也不会为了显得在线而延续未观察到的状态。简单分类 API 仍可按需读取,它们返回服务器时间减 420 秒对应的切片;窗口尚未覆盖该时刻或该时刻落在缺口内时返回 offline。
数据结构
每个 schema_version: 2 包都能独立重建自身窗口,不依赖 Worker 保存上一包。baseline 是窗口起点完整 v1 快照;events 只携带发生变化的顶层字段,字段对象整体替换,不做深层 JSON Patch。事件具有会话内序号和实际观测时间;序号严格递增,时间不倒退。同一时间可以有多个有序变化。
例如下面片段表示一次换歌;这不是可单独提交的完整包:
json
{
"seq": 27,
"at": "2026-09-09T10:02:00.000Z",
"changes": {
"music": {
"state": "playing",
"track": "Example Song B",
"artist": "Example Artist"
}
}
}窗口滑动时,过旧变化折叠到新的起点基线,过期事件退出队列。敏感应用在进入队列之前已过滤。关闭采集项或更改屏蔽名单会清空本机队列并建立新会话,防止下次批次重新发出旧隐私设置下的数据。完整字段与边界 · OpenAPI
缓存、故障与恢复
| 情况 | 处理 |
|---|---|
| 无状态变化 | 不重复添加事件,每 30 秒保存本机覆盖心跳,仍按 5 分钟上传窗口 |
| 意外崩溃 | 恢复最近私有检查点;普通变化最多约 10 秒尚在内存,未保存部分无法保证恢复 |
| 短暂断网或一次上传失败 | 保留本机有界窗口;下次发送最新完整窗口,利用重叠恢复尚未淘汰的变化 |
| 休眠、采集失败、进程重启、时钟异常 | 标记 sleep/collection/restart/clock 缺口或重建会话;时钟回拨不能形成负长度缺口,未知时段不补造事件 |
| 窗口容量耗尽 | 增加 dropped_events 并推进窗口起点;仍有可表示的正长度缺口时记录 overflow,不把丢弃部分当作连续历史 |
| 网页一次拉取失败或读到较旧 KV | 保留已经验证且未过期的内存窗口;不回退到旧批次,不重播已消费事件 |
| 播放到尚未取得的窗口末尾 | 显示缓冲中并停播,后续窗口覆盖播放头时按顺序续播;实际延迟可超过 7 分钟 |
| 断网超过可恢复范围 | 明确展示不可恢复的缺口,再从仍保留的最早位置恢复;不无限补传历史 |
| 隐私配置或会话变化 | 清理旧队列;网页接到新会话后丢弃旧窗口并重新暖机 |
本机 window-cache.json 位于权限 700 的安装目录,文件权限 600,保存的单个窗口时间跨度最多 15 分钟。磁盘检查点通常落后内存至多约 10 秒的普通变化、或 30 秒无变化覆盖,加上阻塞/调度延迟;最早记录可能因此早于当前时间减 15 分钟,不能理解成文件内容恰在 15 分钟时立即物理删除。网页仅在内存保存窗口,不写 localStorage。Worker 覆盖同一个 KV 键,不给每个事件建立单独键。API 的精确截止为窗口末尾之后 10 分钟,KV 物理绝对过期按秒向上取整(最多多不足 1 秒),因此一个最早的事件在云端最多可被当前窗口覆盖约 25 分钟。这是短期活动缓冲,不能称为“完全没有历史”。本机休眠或程序停止时无法主动执行磁盘清理,重新运行会裁剪已过期窗口。隐私与删除边界
这不是无损操作录像。采集仅包括允许公开的应用、Music 和硬件状态,不包括按键、点击、文档、屏幕或浏览历史。非常快的切换、未送达的通知、采样间隔内的短暂状态或设备休眠都可能无法观测。Apple 说明分布式通知可能延迟或丢弃,不能作为可靠事件日志;MacFlare 的通知加轮询仍不能保证捕捉每次真实操作。Apple 通知机制
KV 为最终一致存储,旧值或不存在的结果也可能被缓存,更新传播可能需要 60 秒或更久,没有固定最长传播时间。420 秒是正常运行的播放目标,不能保证所有地区无停顿或绝不漏片。Cloudflare 一致性说明
额度与迁移
全天定时写入估算为 86400 / 300 = 288 次,比 eco 的 720 次减少 60%,比 realtime 的 2880 次减少 90%。变化数量主要影响包大小,不增加该包的 KV 写入次数;启动、手动发送、失败后重试和同账户其他项目仍需另计。免费 KV 当前每日 1000 写入,多个命名空间共享账户额度;同一键最高每秒一次写入。Cloudflare 定价 · 平台限制
先发布新版 Worker,再迁移当前用户的 Agent:
sh
npm ci
npm run deploy
/bin/bash scripts/install.sh --profile bufferedSTATUS_TTL_SECONDS 继续控制旧快照模式,不改变 v2 窗口策略。新 Worker 同时接受旧 /api/update,便于逐步升级;旧 Worker 没有 /api/batch,因此不可反过来先启动新版批量 Agent。首次安装和回退步骤见 配置 与 部署。