Skip to content

TB-272 TB-293: 保存服务器列表并绑定连接与阅读来源 - #139

Closed
hehetoshang wants to merge 101 commits into
mainfrom
agent/moke/3fa392e4c2cd
Closed

hehetoshang wants to merge 101 commits into
mainfrom
agent/moke/3fa392e4c2cd

Conversation

@hehetoshang

@hehetoshang hehetoshang commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

启动页展示持久保存的服务器列表。加入 A/B 只校验和保存地址;点击条目才连接,失败、超时或取消保留原连接和列表。冷启动只加载列表,在线连接和离线入口由开发者显式选择。

  • v1 配置只保存稳定 ID、HTTP origin、名称和时间;Web 使用 localStorage,原生使用 AppData/books/.servers JSON 文件、临时文件 rename 和读回校验。迁移和恢复先备份可读取的原字节,损坏条目跳过并提示,外层损坏需显式恢复。
  • 候选连接与运行时连接分离,按请求、连接和会话编号隔离迟到响应。列表可读但不可写时仍能连接;外部配置冲突须重新读取后再连接,活动连接保留。切换连接或身份清理页面数据、请求和缓存。
  • Reader 开书、关闭与进度保留来源边界;单 WebView 返回使用一次性文档凭据并重新确认,普通冷启动不能从保存的 URL 恢复连接。Reader gitlink 与原生配置未改变。

历史评审处理(自动化启用前)

意见 最终行为与验证
Welcome 的 not_invited 兼容 成功 HTTP 的 not_invited 精确映射为需要访问码,未知错误仍拒绝连接;非成功 HTTP 的 ok/free 不会误激活。核对 Talebook master 固定 SHA 和 v3.8.4:官方 Welcome GET 返回 ok/free,invite gate 使用 not_invited。新增请求回归与 Welcome/Provider 两条真实浏览器访问码流程。
漫画尺寸重复校验与位置参数 parser 与 image adapter 共用 isUsablePageSize;保留 adapter 缺失/非法尺寸防御和一次诊断,调用改用 options 对象。共享尺寸边界、lazy/eviction/原始布局回归通过。
跨来源进度能力默认值不完整 fetch/save 共用完整 typed ServerCapabilities 默认值,跨来源不借用活动服务器能力;进度保存接受可选 AbortSignal,迟到 404 按 URL/connectionId/sessionId 隔离。20 项实际 request 回归覆盖跨来源、response/body 取消和同 URL 新身份。
Settings 阻塞式 alert 断开、退出登录和离线入口复用错误 toast/getErrorMessage,保留 Reader 中文错误和未知错误的业务提示。9 项生产 handler/Reader boundary 回归通过。
删除测试专用 setServer 从生产 store 删除该入口;测试 helper 仅初始化保存条目,再调用真实 beginConnection/activateCandidate,不绕过 Reader guard。相关 9 项回归通过。
8 秒读取超时打断 Reader 返回 原读取 busy 时 Provider 不自动替换 /library 文档,保留一次性凭据;显式“返回服务器列表”才清凭据、取消请求并同文档导航。旧产物真实复现故障,新生产产物覆盖模拟 iOS/OHOS 在线/离线延迟返回、显式放弃和无凭据冷入口;页面/凭据/HTTP/文档次数均校验。
Reader 进度 flush 永久挂起 两处 boundary 和每次 queue save 最多等待 8 秒,超时取消传输并释放等待者;同一挂起 callback 不重复等待,晚结果被观测且不重复诊断。保留开书/会话预约,截止后重验 session/candidate;新来源进度可继续保存。

历史状态与验证(自动化启用前)

固定 head:46b23a49696bdc408adb7716f7a3425ecf8bd0f6;包含 main 05f5d145096b6fa1c015a87333d71ae89e5e1a44。本轮 7 条意见已修复并 resolved;最后回读累计 78 条 resolved、未解决 0。保持草稿,无合并冲突,未合并或批准评审。

  • Node 24.21.0 完整单测 727/727;最低 Node 22.15.0 的本轮相关模块 116/116。
  • Web CI 对应浏览器组 32/32,包含两个新增 not_invited 访问码流程。
  • Tauri 静态生产前端 + synthetic IPC:52 项分组验证通过(既有 44 项和 timeout 组 8/8;iOS 冷入口断言按真实 location.assign 观察文档次数,不依赖 Android/OHOS 的 native navigation IPC)。
  • pnpm build、pnpm typecheck、ESLint 和差异检查通过;ESLint 0 error、21 既有 warning。构建前移走开发服务遗留的损坏生成类型,未修改源码规避类型检查。依赖、lockfile、Rust、原生配置及 Reader gitlink 均未改。
  • 4 个子代理分别负责 Welcome 兼容、漫画/能力/Settings、测试连接迁移、Reader deadline;主代理处理返回超时与最终验证。额外只读整合复核未发现阻断问题。

原读取永久挂起时可继续离线书库或当前连接;列表修改和重新读取仍需等待原操作结束或完全退出重启。读取与写入不是 CAS,末次检查后的跨进程竞争仍有边界。忽略 AbortSignal 的自定义 callback 无法强制终止;取消也不能撤回服务器已接受的 POST。新推送触发 CI,交付不等待外部 CI 完成。

原生文件与 Provider 部分验证使用 synthetic IPC/effects。Tauri/ArkWeb 实机重启迁移、Reader 真窗口/EPUB、Cookie/CORS、自签名仍未验收;浏览器和模拟 IPC 通过不能作为实机证据。

后续 review 自动化

开发者已确认:剩余 review 全部有证据证明是误报、没有需要修改的内容时才删除自动化。已启用 Multica 自动化“ Moke PR #139:每10分钟检查并修复新review”(cd2985c6-e4ec-40c0-8510-d60f7807096c),每 10 分钟检查,Asia/Shanghai,run_only,指派 Moke 开发工程师。没有新意见时继续监测;有效问题修复、验证后非 force 推送原 PR 并 resolve 对应线程。误报结论须独立复核并记录证据,不能因没有新 review 或未解决为 0 而删除。PR 关闭/合并时暂停,不继续改代码。未手动触发试跑;首次调度为 2026-10-11 22:10(Asia/Shanghai),执行情况以自动化运行历史为准。

自动维护记录:2026-10-11(run ee74798e)

最终提交:2e5ce49c5c536184abed3a4d6904649cfd878926,已非 force 推送至原 head agent/moke/3fa392e4c2cd;保持草稿与 Reader gitlink 4b3db91bd1d94e5e9063639964f95a407334e81f。本轮读取全部 84 个 reviewThreads、84 个 reviews(含其全部 comments)与 2 个普通 comments,并再次核对 head 和评审更新时间。普通评论中的历史要求按现有代码/文档核对,不重复归类为误报。

评论 ID / 意见 分类、最终处理与证据
4241885088:单 WebView 进度来源 核心缺陷为误报,保留未解决线程。 单 WebView 完整替换 Moke 文档,Provider 已卸载;detail 的在线/下载路径与 open-offline-book 携带来源 mokeServerUrl。固定 Reader 4b3db91 的 HTTP 契约 与实际 mokeLaunchContext/mokeBridge 负责直接 POST 来源 /api/book/{id}/progress、debounce、关闭/隐藏 flush。独立只读代理复核;新增 13 项执行实际 Reader bootstrap/bridge 的行为回归,覆盖 OHOS/Android/iOS、A/B 来源与桌面不重复直存。补齐 docs 解释。IPC/HTTP/DOM 模拟不等于设备验收。
4241885293:离线返回落点 有效文档缺口,按评审明确允许的文档方案修复并 resolved。 README/docs 明确超时出口进入 /library 查看本地下载(包括未入架书籍),关闭单 WebView Reader 后按既有离线契约返回 /shelf;示例书的首次 offline-library intent 到达即消费,后续新 Reader 凭据不继承。保留入口与现有返回行为,reader-return、sample-offline-navigation 和生产 handler 回归通过。
4241885559:HTTP 状态集合与重试说明 有效传输判定缺陷及描述缺口,已修复并 resolved。 共享明确状态集合,保留既有 401+need_login、403+not_invited 配对;5xx 的 ok/认证状态全部拒绝,失败探测不进入 welcome。200+not_installed 仅识别 Talebook,随后 welcome 仍拒绝未安装服务器。协议依据:master 固定 d5d21dda 与 v3.8.4 固定 028383d4。本次点击只探测一次,网络失败直接提示;不再等待 1.5 秒自动重试,可再次点击条目重试。新增状态矩阵和真实两道检查回归。
4241885782:beginConnection 拒绝无反馈 有效,已修复并 resolved。 Welcome 按现场条目/保存/读取/错误状态明确提示,读取预约未释放时禁用条目并显示等待说明;被拒绝的新点击不会取消原在途请求。6 项新增生产 handler 回归在旧实现全部失败,修复后通过。
4241885983:备份不可查看 有效可验证性缺口,已修复并 resolved。 提供“查看本次会话最新备份”,真实存储重读并核对已验证原字节,显示原生 JSON 路径或 Web 当前站点 localStorage 键及可复制只读内容。仅注册表备份;不枚举历史或兼容镜像独立备份,不自动恢复/删除。读与迁移/保存/恢复串行,主配置后续写失败仍可查看已验证备份;篡改/删除/权限失败与 UI 迟到结果不假报成功。12 项 repository/store、7 项 UI 新增回归通过。README 说明历史维护与保留策略;多次损坏/恢复仍可能累积。
4241886120:CI 就绪无失败出口 有效,已修复并 resolved。 30 次失败后打印预览日志并 exit 1,成功仅结束就绪子 shell,继续 Playwright;保留 EXIT 清理。提取生产 step 的 bash 语法及立即就绪/第 3 次就绪/耗尽/Playwright 失败码 4 项隔离检查通过。

验证:Node 24.21.0 全量单测 767/767;pnpm lint 0 error、21 既有 warning;pnpm typecheck、pnpm build、git diff --check 通过。生产 Tauri 静态前端使用系统 Chromium 与 synthetic IPC,server-storage-timeout / offline-document / connection-reader-guard 三组 14/14 通过;预览进程已收齐停止。构建生成的 next-env 变化已还原,无依赖/lockfile/Rust/原生配置/gitlink 修改。

分工:4 个子代理分别核实并实现 API、Welcome/备份 UI、Repository/store 备份支持、Reader 协议回归与 CI;另 1 个独立只读代理复核整合,未发现阻断。主代理核实普通评论、完成文档、审阅整合并执行最终检查/推送。

本轮有有效修复,不满足“当前尚未解决意见全部误报”的自动删除条件。自动化 cd2985c6-e4ec-40c0-8510-d60f7807096c 保持 active;不 merge/approve、不发送 review 讨论消息。仍保留跨进程非原子存储竞争及原生 WebView/EPUB/Cookie/CORS 实机验收边界。

推送后一次 CI 快照

快照 head 2e5ce49c5c536184abed3a4d6904649cfd878926;状态计数:{'IN_PROGRESS': 17, 'SUCCESS': 3, 'NEUTRAL': 1}。只记录现场状态,不等待外部 CI;新推送的后续结果由下一次调度核对。

自动维护记录:2026-10-11(run 4909a0a2)

提交 abd69959b0e97c63e3180f22b9f5ab907a655156 已非 force 推送至原 head agent/moke/3fa392e4c2cd,保持 Draft、Reader gitlink 4b3db91bd1d94e5e9063639964f95a407334e81f。开始写入与推送前均重读实时自动化条件、runs、活动 agent/tasks 和 PR head;没有其他修复占用。完整分页读取 88 个 threads、88 个 reviews(含全部线程 comments/回复)及 2 个普通 comments;旧已处理要求不重复实现。推送后回读:86 条 resolved,2 条保留未解决。

评论 ID / URL 分类、处理与证据
4241972135:离线格式查找跨来源 有效,已修复并 resolved。 offline-books.ts 格式回退与无格式查询一致,按 serverUrl、bookId、format 匹配;空来源不能读取或删除其他服务器记录,跨服可见的示例书也不能误命中。7 项新增 Web/Tauri 行为回归覆盖 A/B 同 ID、读取/删除、规范化 origin、示例碰撞与显式本地记录;旧实现复现 6 项失败,修复后通过。
4241972338:候选访问码字段不约束激活 有效,已修复并 resolved。 activateCandidate 默认未确认,读取 needsAccessCode,并在 Reader 关闭/进度等待后回读;仅 Access 的当前请求收到服务端 err=ok 后传确认。新 store 回归及生产 handler 回归验证普通/陈旧/错误/验证码响应不能授权。此 boolean 约束内部调用,服务端访问认证仍由接口和会话负责。
4241972536:断开/退出登录文案 有效,已修复并 resolved。 Settings 按 reader.windows_open 错误码分别提示断开、退出登录或进入离线模式;保留未知错误兜底与其他 Reader 错误。9 项生产 handler 回归通过,旧文案在 3 个对应动作断言中复现失败。
4241971909:Welcome 403 not_invited 核心缺陷假设不适用于官方协议,保留未同意线程。 固定 Talebook d5d21dda 的 base.py:206 邀请门禁明确 HTTP 200;user.py:467 Welcome 绕过该门禁并返回 HTTP 200 ok/free。user/info 的 401/403 配对是兼容探测,不能推定 Welcome 有同一状态。补充协议注释和 403+not_invited 拒绝回归,不放宽失败 HTTP;真实浏览器验证 HTTP 200 访问码入口与确认。
4241885088:单 WebView 进度 独立再次复核核心缺陷为误报,线程保留。 Moke 整文档导航卸载 Provider;实际来源传入 Reader URL。固定 Reader 4b3db91 的契约、reader.moke bootstrap、mokeLaunchContext、mokeBridge 直接向来源保存并在关闭/隐藏时 flush,无需给 main 加桌面窗口绑定。实际 bootstrap/bridge 回归 13/13,加 Reader URL/事件回归合计 34/34 通过。

验证:Node 24.21.0 全量单测 781/781;pnpm lint 0 error、21 既有 warning;pnpm typecheck、pnpm build、git diff --check 通过。生产 Tauri 静态前端的 offline-document 与 connection-reader-guard 6/6;Web 的桌面/移动 A/B 连接与认证、Access 重载和 3 个访问码流程 6/6。浏览器预览进程全部停止,4 个子代理结果全部收齐;构建生成的 next-env 差异已还原,运行时自动注入的 AGENTS 内容及测试产物未纳入提交。依赖/lockfile/Rust/原生配置/gitlink 未改。

分工:3 个实现代理分别负责 Welcome HTTP 协议、离线来源、候选访问码;1 个只读代理独立复核 Reader 意见及最终整合,未发现阻断问题。主代理修复 Settings、复核整合并完成全量检查、浏览器验证、推送与线程回读。

边界:DOM、IPC、HTTP/计时器部分使用模拟,不等于原生设备、真实 Cookie/CORS 或 Talebook 部署验收。离线列表到详情仅传 bookId 的原有初始选书歧义(src/app/detail/page.tsx:199)未扩展修改;后续开书/删除使用所选记录的实际来源,本轮修复的是函数直接查询/格式回退的跨来源错误。

自动化保持 active。开发者实时条件允许 A(剩余意见全部证实误报且独立复核)、B(当前 head 的 Claude 实际完成且无任何评审输出)或 C(当前 head 明确因额度耗尽停止)删除。上一 head 2e5ce49 对应 Claude run 38147841177 attempt 1 的 DeepSeek 审查正常完成(result success、is_error=false),但确实发布了本轮 4 条新评审,因此不满足 B/C;本轮有有效修复,不能刚推送就用 A 删除。新 head 的 Claude 仍执行中,后续调度继续核实。不 merge/approve,不发送讨论回复。

本轮推送后一次 CI 快照

head abd69959b0e97c63e3180f22b9f5ab907a655156:{'IN_PROGRESS': 18, 'SUCCESS': 2, 'NEUTRAL': 1},无失败状态;记录现场,不等待外部 CI。

hehetoshang and others added 30 commits September 18, 2026 07:58
feat: add Preview device entitlement client
fix: harden Preview service URL and window resizing
feat: establish and harden Preview distribution
Comment thread src/lib/server-storage.ts Outdated
Comment thread src/lib/store/server.ts Outdated
Comment thread src/app/welcome/page.tsx
Comment thread src/components/providers/ServerProvider.tsx Outdated
Comment thread tests/sample-book.spec.mjs
Comment thread src/lib/api.ts
Comment thread README.md Outdated
Comment thread src/lib/server-connection.ts
Comment thread src/lib/comic-images.ts Outdated
Comment thread src/lib/reading-progress.ts Outdated
Comment thread src/app/settings/page.tsx Outdated
Comment thread src/lib/store/server.ts Outdated
Comment thread src/lib/store/server.ts
Comment thread src/lib/reader-source.ts Outdated
Comment thread src/lib/reader-source.ts
Comment on lines +113 to +119
function sourceForReaderEvent(windowLabel: string, bookId: string): ReaderSource | null {
const known = windows.get(windowLabel);
if (known) return known.bookId === bookId ? known : null;
if (retired.has(windowLabel) || pending?.ambiguous || pending?.source.bookId !== bookId || pending.before.has(windowLabel)) return null;
observeWindow(windowLabel);
return windows.get(windowLabel) ?? null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(高)单 WebView 平台(OHOS / Android / iOS)的阅读进度会被这里静默丢弃,属于行为回归。

src-tauri/src/extensions/api_server.rs:271-275 明确写着这些平台「阅读器运行在唯一 WebView(label 为 main)里,而不是独立的 reader-* 窗口」,因此 is_reader_window_label 把 main 也算作阅读器窗口(label.starts_with("reader-") || (is_single_webview_runtime() && label == "main"))。但前端的来源绑定只接受 reader-*:

  • 单 WebView 平台走 openEmbeddedReaderBook(src/lib/moke-reader.ts:460),它既不设置 pending,也不绑定任何窗口;
  • 于是 sourceForReaderEvent('main', bookId) 在本行短路:pending 为 null ⇒ pending?.source.bookId !== bookId 恒为真 ⇒ 返回 null;
  • 即使 pending 存在,第 117 行的 observeWindow('main') 也会被第 73 行的 !label.startsWith('reader-') 拒绝。

结果 ReaderProgressProvider.tsx:38-46 永远拿不到 source → saveReadingProgress 不会被调用,book:closed 的收尾 flush 也一并失效。改动前监听全局 reader:page:changed + store 里的 serverUrl,这些平台是可用的。

注意 isReaderWindow(第 193-198 行)不把 main 计入是正确的(否则移动端 requireClosedReaders 会因为主窗口始终存在而永远无法关闭),所以修复不能简单地把 main 加进 isReaderWindow;可行方向是让嵌入式打开这条路径显式登记来源(例如 openEmbeddedReaderBook 在 single-WebView 运行时绑定当前 source,返回/切换会话时清除),或按 docs 的约定与 Reader 仓库协调一个显式来源契约(事件回带启动实例 id)。

另外:docs/reader-development.md 记录了 moke-home-*「没有 Moke 来源绑定」这个已知缺口,但没有覆盖 main;新增的 reader-progress 测试也只用 reader-* label,建议补一个 single-WebView 用例。

Comment thread src/lib/store/server.ts
Comment thread src/lib/api.ts Outdated
Comment thread src/app/welcome/page.tsx Outdated
Comment thread src/lib/server-registry.ts
Comment thread .github/workflows/ci.yml Outdated
Comment thread src/lib/api.ts
err: data.err || `http.${response.status}`,
// A success-shaped body cannot turn a failed HTTP request into an open
// connection. Invite-state compatibility applies only after HTTP success.
err: typeof data.err === 'string' && data.err !== 'ok' && data.err !== 'free' ? data.err : `http.${response.status}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not_invited 的语义在这里取决于 HTTP 状态码,两处判定互相矛盾,建议确认后再定稿。

  • readConnectionWelcome(src/lib/server-connection.ts:8)把 not_invited 视为「需要访问码」的成功状态;
  • 但本分支在 !response.ok 时把它当作错误返回(err: 'not_invited'),checkSavedServer 会据此抛出「访问码状态检查失败」,用户永远到不了 /access。

问题在于同一文件第 282-285 行已经显式记录 ['not_invited', 403]——即该状态在 /api/user/info 上就是 403。如果 Talebook 的 invite gate 对 /api/welcome 同样返回非 2xx(403),那么「需要访问码」的服务器会直接变成不可连接;而如果返回 200,本分支永远不生效。

另外 tests/helpers/server-list-fixture.mjs:31 的 route.fulfill({json}) 固定是 200,tests/server-validation.test.mjs:87,105 只断言 503 的情况,所以现有测试无法覆盖 403 这一真实分支。

建议:明确 /api/welcome 在 invite gate 下的真实状态码,并让两个入口对同一个 not_invited 给出一致结论(要么都拒绝,要么都路由到访问码页),同时补一条 403 的回归。

Comment thread src/lib/offline-books.ts Outdated
Comment thread src/lib/store/server.ts
Comment thread src/app/settings/page.tsx Outdated
Merge PR head abd6995 onto
hehetoshang/moke main f770eae.

Integrate saved-server connection and Reader source/session guards with
personal Preview entitlement, OHOS vendor support, and offline reconnect.
Preserve personal version 1.1.5-10 and all four pinned submodules.

Resolve integration boundaries around Chrome 96 abort compatibility,
access confirmation after registry replacement, stale continue-reading
results, and userinfo deadline cancellation. Adapt browser fixtures to
explicit connection, responsive navigation, IPC, and real BFCache restore.

Validation: 955 Node tests; 33 web, 95 normal static, and 91 Preview
browser cases passed in groups; Rust transport 13 tests; lint, typecheck,
normal/Preview frontend builds, and locked Rust Preview cargo check.
Browser coverage uses Chromium and mocked native IPC; no device validation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant