MB-X Bilibili Pipeline
5 days ago 8b94574583bb5d33faf4d3cec465e3fbcdcf40d3
dev-doc/project-doc/B站博主动态采集辅助工具.md
@@ -262,8 +262,26 @@
半小时页面刷新不再由 Codex 自动化通过持久 `node_repl`、`globalThis` 标签页对象或交互 Chrome 工具执行。独立 cookie-free MV3 扩展使用 `chrome.alarms` 在半小时边界触发,并通过短生命周期 Native Messaging port 完成握手、唯一刷新、观察和 Host 提交;Codex 自动化仅处理源控协调器和下游 handoff,不拥有浏览器 transport。
每个槽位只创建一个 inactive 的任务自有标签页。标签页以 slot/lease 页面标记和 `chrome.storage.session` 记录双重绑定;Host commit 完成或失败/finally 后,扩展重新核对 tab/window/URL/标记,先退休持久所有权,再唯一执行一次 `tabs.remove`。已关闭是幂等成功;对象漂移、关闭失败只记录固定脱敏状态,禁止导航到 `about:blank`、禁止第二次 remove/update,也绝不关闭用户已有标签页或共享窗口。
每个槽位只创建一个 inactive 的任务自有标签页。标签页以 slot/lease 页面标记和 `chrome.storage.session` 记录双重绑定;跨 dynamic/opus 导航时,document-start content script 只能向本扩展 service worker 请求经 tab/window/URL/lease 重绑的标记,不能直接读取 session storage。Host commit 完成或失败/finally 后,扩展重新核对 tab/window/URL/标记,先退休持久所有权,再唯一执行一次 `tabs.remove`。已关闭是幂等成功;对象漂移、关闭失败只记录固定脱敏状态,禁止导航到 `about:blank`、禁止第二次 remove/update,也绝不关闭用户已有标签页或共享窗口。
Native port 在首次消息前安装 message/disconnect handler,写入按序执行;peer 在写前、写中、写后关闭统一为 `E_NATIVE_PEER_CLOSED`。Python Host 将单个 frame 合并为一次受互斥保护的写和一次 flush,只吞并确定的 pipe peer-close,其他 I/O 错误继续失败。连续 100 个槽位、service-worker 重启、同 ID 用户替换、关闭失败和 peer-close 竞态均由生产形状测试覆盖。
本节只消除本项目制造的重复 stdio/tab 触发,不能修改已签名 Codex 桌面客户端的主进程写竞态。自动化 `q` 必须保持暂停,直到源审 `PASS/0`、可验证部署以及受控客户端重启清除旧管道;若可信扩展/Host 尚未可验证,返回固定 `E_TRUSTED_ADAPTER_UNAVAILABLE`,不得回退到旧交互循环。脱敏复现和上游边界见 `Codex桌面Stdio写EOF脱敏复现与项目侧缓解.md`。
## 16. 功能 Dynamic Native Host 1.1(待同链复审和部署)
`bili_dynamic_refresh_extension` 1.1 与 `com.project_info.bili_dynamic_refresh` 不再使用固定 `E_TRUSTED_ADAPTER_UNAVAILABLE` stub。扩展在已登录的 allowlisted dynamic/opus 页面只读取可见 DOM:creator UID/name、规范页面身份、完整正文、公开原图候选、BVID、发布时间和页面可见时长;它不读取或传输 Cookie、token、signed URL、localStorage、Chrome Profile、验证码内容或原始网络记录。访问控制、owner 不匹配、未解析卡片、正文/原图未稳定、聚合 bundle 超过 900000 bytes 或字段含秘密形状时均 fail-closed,不能形成 no-new。
Host 通过 Native Messaging exact origin、reviewed local-unpacked source identity、slot/lease 和 HMAC 序列绑定一次 capture。公开原图仅允许无 query/fragment 的 `i0/i1/i2.hdslb.com/bfs/` 原图 URL,下载器禁用代理继承、限制重定向/字节/魔数,以 CreateNew 临时 intake 交给既有 `bili_article_image_collector.py`;正文/原图由该收集器按 formal append-only 保存,视频只追加规范 BVID metadata,再由 `bili_half_hour_pipeline.py` 投影现有 exact-once outbox。capture bundle 和原图 URL不进入 durable terminal;终端只保留 slot、run、capture SHA、数量和 outbox ID。相同 slot 的 durable terminal replay 在 Native/tab/page action 前返回。
构建/安装入口为 `bili_dynamic_refresh_native_host/build_host.ps1` 与 `install_native_host.ps1`。构建只接受固定离线 Python/PyInstaller 6.15.0/provision identity 和原 reviewer 一次合并 PASS receipt,精确绑定 extension、Host、article collector、half-hour pipeline 四套 source manifest;安装复用同一 PASS receipt,不再制造 build-hash 二次复审门。安装默认仅 ValidateOnly,`-Install` 只向缺失的版本目录 CreateNew 写 EXE、runtime config、native manifest、install receipt 与由可见 unpacked-load 事实机械生成的 identity facts,并在缺失的 `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.project_info.bili_dynamic_refresh` 注册默认 String。扩展继续采用 Chrome 可见界面的本机 unpacked 加载,不使用 CWS、policy、Raw CDP 或 Profile 写入。
扩展安装/重载本身只恢复 durable slot 并建立 30 分钟 alarm,不会在 Host 身份材料尚未完成时抢先采集。一次真实部署回填通过扩展 action 的 `kickoff.html` 明确触发 `RUN_CURRENT_SLOT_REQUEST`;消息只能来自同扩展的精确页面 URL,并仍进入同一个 FIFO、durable STARTED/terminal 与 exact-once 协调器。它不是逐轮许可入口,日常后续轮次仍完全由 alarm 自动执行。
当前 source tests 已覆盖非空图文+原图+视频、零新增重放、malformed/secret/owner mismatch、Host 已许可重启拒绝、peer-close、100 槽位生命周期、受控当前时槽入口和任务自有 tab 关闭。此节记录的是待复审 source 状态;在原 reviewer `PASS/0`、一次受控 build/install/reload、真实从最后 accepted cursor 回补并得到当前 `RUN_COMPLETE` 前,`q` 仍保持暂停。项目侧部署只能移除高频触发,不能永久修复已签名 Codex Desktop 的主进程 pending WriteWrap/EOF 竞态。
### 16.1 Native Host 预终态脱敏诊断
Host 在正式 run/outbox/terminal journal 之前另有固定 schema 的 append-only phase ledger,用于区分进程入口、运行配置、本地身份、Chrome launcher、首帧、maintenance、slot HELLO、durable replay/resume、session 与 durable handoff 边界。每行只包含 UTC 100ns 时间、build/session nonce 的 SHA-256、固定 phase、固定错误码,以及 PID/进程创建 FILETIME;禁止写入页面 URL/标题/正文/capture payload、Cookie/header/token/signed URL、localStorage/Profile、命令行、原始异常或任意调用方字符串。未知错误一律收敛为 `E_HOST_INTERNAL`。
ledger 位于已审核 Host 的唯一版本安装根、与 `runtime-config.json` 同目录,因而在配置解析失败前仍可用。该版本稳定态只拥有一个固定 `preterminal-phase-ledger-v1.jsonl` 和一个固定 lock;JSONL 最多 131072 bytes、512 条诊断事件,达到任一上限后停止追加并固定返回 `CAPACITY_REACHED`,已有字节 mutation0,业务 run 继续执行;永不轮转为多段,也不删除、替换、覆盖正式 ledger。首次初始化使用唯一固定 `.initializing` CreateNew 暂存对象:Windows 内核在 OWNER 首记录 durable retain 前负责 crash-close 自动回收;OWNER 首记录严格绑定当前 build/session hash 与该 held 文件的 volume/file identity,fsync 后才取消自动回收,再以 no-replace 原子发布为正式 ledger。若崩溃发生在 retain 后、发布前,重启只恢复 OWNER 记录与当前暂存对象身份完全一致的对象;若发生在发布后、lock owner receipt 完成前,重启只接受正式 ledger 的同一 OWNER 身份,并只修复空或其 canonical receipt 前缀。未知暂存、目标、receipt 或身份漂移保持原字节并失败关闭。初始化成功后 `.initializing` 必须不存在。两个受信 Chrome parent 通过同一 held lock 串行,Host 生命周期内持续持有根目录、lock 与 JSONL 句柄;每行 append 后立即 flush/fsync,并从同一 held handle 精确读回完整前缀和新行,不能只比较路径或大小。锁内先验证 OWNER 首记录和全部 canonical JSONL;仅允许截除 OWNER 后没有终止换行的撕裂事件尾部,完整但非法的行一律失败关闭,随后才可 append。任何 trace、回读、对象身份、`PROCESS_EXIT` 或 close 失败均由最外层非递归边界固定收敛为 `HOST_FATAL/E_PRETERMINAL_LEDGER`,不输出原始异常;maintenance/replay/session 的写端 peer-close 和 ACK 前 EOF则保留 `PROCESS_EXIT/E_NATIVE_PEER_CLOSED`,不得被误写为 `OK` 或 `E_PROTOCOL`。该 ledger 只用于诊断早退,不替代 formal terminal,也不能把失败解释为 no-new。