Files
makelore/.project-docs/20-architecture/data-flow.md
brother7 fba68e86d9
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled
merge: fix opencode model switching runtime refresh
2026-08-21 01:48:01 +08:00

74 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Flow
## Primary Flows
| Flow | Source | Destination | Notes |
|---|---|---|---|
| 桌面认证生命周期 | Renderer 登录、刷新与注销请求 | Host API → Main Works Session → Works Square `/api/auth/{login,mobile-login,refresh,logout}` → one-feel auth | Main 加密持有并先持久化轮换 token;客户端不携带 OAuth client secret;连续 7 天未使用才清除会话,终止性 `400`/`401` fail closed |
| 用户模块入口策略 | 会话恢复 / 登录 / 刷新 | Electron Main → Works `/api/auth/me` → 四布尔安全投影 → Renderer auth store → 卡片/路由/provider gate | 缺失对象或字段默认 `true`;`design` 映射 `painting`;终止性 `401` 清理 Main/Renderer 会话;全局 `/settings` 不受 Code gate |
| 项目创建 | 新建项目对话框 | Host API → Main 项目初始化 | 创建时固定 `ProjectType`;小游戏/小程序原子生成受控模板,自定义只生成项目空间 |
| 一键提交 | `ProjectPublishAction` | Renderer capability → Host API → Main 本地 npm/Vite build → built snapshot preflight → source+built+contract 上传 | 只对小游戏/小程序开放;首次 create 通过单一 multipart 合同原子提交文字资料与必选 PNG/JPEG/WebP 封面,创建失败或冲突不上传版本;已有 draft/published 只提交版本并沿用云端资料/封面,不做无条件 metadata PATCH |
| 构建产物预检 | Main-owned built snapshot | 一次性 loopback origin → fresh Electron WebContents/CDP(桌面、移动) | 检查错误、白屏和外域;不调用 Playwright,检查与上传归档相同字节,但不产生可信 receipt |
| 提交绑定 | 云端成功上传响应 | Main → submission binding v2 | 只持久化成功的 app/version/review/hash;落盘失败返回固定告警但不反转提交 |
| 运营发布 | Works Square 审核与交付 | 公共 `play_url` | 客户端只消费服务端发布结果;真实合同校验 → OSS/CDN 生产链仍待整链验收 |
| 可信发布门禁 | source+built+artifact contract | 服务端逐字节重算/合同校验 → 不可变 Release 固化 → 人工审核 | 服务端不运行项目 Vite;仍是不可绕过权威,未来 runtime 强门禁需可信 verifier |
| AI 编程 Session 执行提交 | ChatPanel 当前 Agent / Session | Renderer per-Session run state → deferred provider sync → Host API → Main manager FIFO → per-project Agent FIFO → runtime/config + Agent readiness → OpenCode message / command / summarize | 新建 session 已知为空时不等待空历史;普通 prompt、项目命令和压缩不重发模型 override;不同 Session 没有 reply-duration 全局锁。provider/runtime 或 Agent 未应用时以 typed `409`、`promptSent:false` 在执行前终止,且不自动重启、reload 或 dispose 共享 runtime |
| AI 编程 Session 模型切换 | 页面模型选择器或 `/models` / `/model` | Renderer Session model action → Host API → Main OpenCode client → `POST /api/session/{sessionID}/model` | Main client 将内部 `modelID` 映射为 OpenCode 1.18.9 wire `ModelRef.id`;只改变现有 Session 的后续 turn 模型并从 Session metadata 恢复,不写伙伴默认模型、provider 配置或 runtime freshness,也不要求 runtime restart |
| AI 编程启动确认 | Host POST、OpenCode HTTP polling 与 Session events | Renderer `{sessionId, runToken}` watchdog → `sessionRunStates[sessionId]` | Host 接受与 user transcript 不算 ACK;busy/retry、assistant、question、permission 或 typed terminal event 才确认/结束启动。10 秒无确认只终止该 Session 为 `SESSION_START_UNCONFIRMED`,清理其队列且不自动重放 |
| AI 编程上下文压缩 | OpenCode compaction Part / `session.compacted` / `/compact` | Renderer Store → per-session transcript state → Chat mixed timeline | 手动请求先创建 immutable UI event;native identity 负责归并与回放去重;完成只更新压缩项,真实 idle 才结束 run 和释放队列 |
| AI 编程模型代理错误 | OpenCode provider 请求 | Main Host AI proxy → Works 模型上游 | 配额耗尽保持独立终止态;只有明确的分组上游饱和才投影为 OpenCode 终止状态,通用限速仍保留 `429` |
| 客户端更新检查 | 设置页 | Renderer update store → IPC → Main AppUpdater → 目标 feed | Main 记录并重抛原始错误;Renderer 只显示去重、脱敏的单条提示,稳定源缺包不伪装为最新版 |
| 设计会话创建/切换 | AI 绘画页面或侧栏 | Renderer API → Main → Workspace Conversation API | 新会话属于现有 Workspace;读取独立消息、Brief、Quote 和 `turnRevision` |
| 设计消息与确认 | 当前 Conversation | Main ↔ 持久 Agent Gateway Session WebSocket → Conversation 快照 | `command.submit`、Run 与设计事件共用连接;传输失败才以同一幂等 ID 回退 REST;结构化业务错误不重试且未知文本由 Main 脱敏;请求和流式结果同时绑定 Workspace + Conversation |
| 设计有限请求生命周期 | Renderer Workspace JSON 调用 / shared Works token refresh | Main deadline → Electron transport → safe-read-only Node fallback → Host API timeout envelope | 包括取凭据、请求头和响应 body 的完整生命周期最多 30 秒;超时 abort transport 并稳定返回 `504 DESIGN_WORKSPACE_REQUEST_TIMEOUT`;透明 fallback 只允许 `GET`/`HEAD`/`OPTIONS`,mutation 不隐式重放 |
| 设计单图来源选择 | 当前 Workspace 已完成图片 / 本地图片 | 现有 Asset 上传或选择 → `attachmentAssetIds` → 当前 Conversation Turn | 图片 Brief 用作图生图参考图;视频 Brief 用作首帧;只提交一个真实 Workspace Asset ID |
| 设计任务同步 | 任一 Conversation 的事件流 / Quote REST 对账 | Renderer Workspace 任务列表 | Task 和 Asset 按 Workspace 归并;任务已落库但 Run 失败时恢复可见性,内部对账失败不覆盖新会话错误,切换 Conversation 后仍同步任务但不回写旧会话 |
| 设计 Quote 编辑与重报价 | 当前 Conversation 的 active Quote | Renderer 修改最终 Prompt/参数 → Main Host API → Works Square Quote update → 当前 Conversation | 服务端返回最新参数与设计点;报价完成前不能确认,确认提交最新原值,客户端不自行计价 |
| 设计项目删除 | Canvas 侧栏精确项目名确认 | Renderer → Main Host API → Works Square Workspace DELETE | 删除成功后清理当前 Workspace/Conversation/task stream 并选择最近更新的剩余项目;结算与软删除语义由服务端负责 |
| Prompt Museum 浏览与使用 | Canvas 侧栏“获取灵感” | Renderer → Main Host API → Works Square list/detail;选中 Prompt → 进程内 pending state → 当前 Canvas 输入框 | 只发送白名单筛选/游标;Works Token 留在 Main,Prompt 不自动发送,Museum 不包含客户端静态数据集 |
| Learning 项目浏览 | Learning 项目列表 / README 详情 | Renderer → typed Host API → Main fixed list/detail routes → Works Square published projects;README 图片节点 → 远程 HTTPS origin | 保留登录与 `module_access.learning`;Main 严格投影分页项目 DTO,README 禁用原始 HTML,仅图片节点可直连无凭据 HTTPS URL;封面和历史媒体仍使用受控媒体读取 |
| Learning 项目下载 | README 详情页“下载项目” | Renderer → Host API → Main 原生保存对话框 → Works archive stream → 临时文件 → 原子重命名 | 不校验 `Content-Length`、`archiveBytes`、实际流字节数或客户端大小上限;最多五跳同 Works origin 重定向且不向重定向请求携带 Bearer;SHA-256 与 ZIP 签名匹配后才保存,Renderer 只得到 `saved` 或 `cancelled` |
| Robot 引导式热点配网 V1(已实现、默认开启) | Robot Binding 页面 | 用户选择引导配网 → 进入固件配网模式 → Renderer 经 Host API 请求 Main 扫描 → 用户选择短效候选 → Windows/macOS Adapter 连接并核验当前 SSID → Main 打开固定 Portal → 用户在 Portal 配置 Wi-Fi → 电脑恢复互联网 → 现有六位 Binding | 精确环境值 `0` 或 capability 读取失败回退直接六位码;系统 Wi-Fi 保留兜底,Makelore 不收集 Wi-Fi 密码、不修改固件,热点发现/`bound` 都不等于可信身份或 online/ready |
## State Ownership
- Main 持有刷新凭据、发布 Token、固定 npm runtime、源码/构建归档、临时目录、幂等键和 submission binding v2;Renderer 不持有归档路径、构建 origin 或自动部署状态。
- 项目内 `.niancode/project.json` 保存 `ProjectType`;Main 在配置写入和目录复用时保持其不可变,并在打包时重新读取校验。
- Renderer 仅持有短效公开会话状态和提交展示状态。
- Renderer 可持久化当前账号的四布尔模块入口策略,但不持有原始 Works profile 或 Token。新账号不继承上一账号缓存;网络/暂时上游失败可保留同会话已知策略,终止性 `401` 不得回退到默认开启。
- 本地构建临时目录、HTTP origin 和预检 WebContents/partition 只属于一次调用;预检读取与 `built_archive` 相同的内存字节,但结果不写为可信上传 receipt,也不覆盖生产 opaque-origin。
- 旧 schema v1 `submitted` 记录迁移并保留;旧 `armed`、`waiting_for_package`、`waiting_for_login`、`uploading`、`failed` 归一为 `legacy_retired`,不再启动 watcher 或上传任务。
- AI 绘画 Conversation 持有消息、Brief、Quote、`turnRevision` 和服务端 Session 绑定;Workspace 持有 Conversation 列表、生成任务和资产。
- AI 绘画 Main 持有 Workspace 请求 deadline、底层 transport fallback 和共享 Works token refresh flight。共同等待者必须在同一有限期限内 settle,refresh flight 结束后必须释放,后续刷新可重新发起;底层 mutation transport failure 不得自动产生第二次上游请求。
- Prompt Museum pending Prompt 是 Renderer 进程内一次性导航状态;Canvas 消费后立即清除,不进入 Workspace/Conversation 直到用户主动发送。
- Learning 不再维护客户端课程库、生成任务、player registration、进度或 runtime 状态。Main 在一次下载调用内持有当前账号快照、Works 凭据、保存路径和临时文件;账号变化或校验失败会中止并清理临时文件。历史课程数据不会被新逻辑读取,也不会自动删除。
- 图生图参考图与视频首帧都先归一为当前 Workspace 的 Asset;从作品选择时复用生成结果 Asset,本地选择时先走既有上传接口,再把唯一 Asset ID 随 Turn 提交。选择或上传成功后关闭选择器。
- 本地开发适配器将旧单会话 schema v2 原子迁移为带默认 Conversation 的 schema v3;打包应用不使用该本地适配器作为云端失败回退。
- 注销和退出会关闭本地事件流并清除本机 Conversation Session-id 缓存;服务端持久 Session 保留,下一次访问从 Conversation API 重新读取。
- AI 编程 Store 的 `sessionMessagesBySessionId` own-key 是加载状态契约:键缺失表示历史未知,存在且值为 `[]` 表示已知为空。只有后者可使用不读取历史的快速选择;普通历史会话选择继续刷新消息。
- AI 编程 Store 的运行态、错误、startup deadline 和内部 pending queue 按 Session 持有;top-level error 只表示真实全局错误。任何不确定的远程失败只清理对应 Session 的旧队列,手动重试不得复活失败前已排队的 prompt。
- OpenCode Session 持有现有会话的 active provider/model;伙伴 `model` 只在创建新 Session 时提供默认值。页面与 slash command 通过同一 native Session endpoint 切换,后续执行和压缩不得用伙伴默认值覆盖该选择。
- Main 以 manager identity + runtime generation provenance 持有 runtime-config latch,并按 Agent id 持有 desired/applied 内容状态与本 generation 已知 id。owned `fresh` generation 中首次出现的唯一 id 可在 live `/agent` 暴露后应用,且不会阻断已有未变 Agent;同 id 编辑、删除后重建、live id 缺失以及 attached/unknown generation 均不能越过门禁。托管本地 AI proxy 的 fresh runtime config 直接使用当前进程 Host API token;把该已生效 token 写回 provider storage 不构成 runtime freshness 变化,attached/unknown generation 或 timeout/partial persistence 仍保持 sticky pending。
- AI 编程压缩状态由 transcript 中的 `compactionsById` / `compactionOrder` 单一持有。运行中 hydration 保留 manual pending identity 并用 native Part 合并;completed 不得回退为 running。`session.compacted` 不是 run idle,不能据此释放 queued prompt;失败、中止或 runtime generation 变化只清理对应未完成事件。
- Main Host AI proxy 可为固定 OpenCode 重试契约做窄化的内部状态投影:配额耗尽投影为 `402`,明确上游分组饱和的 `429` 投影为终止 `400`,其他 `429` 原样保留;升级 OpenCode 时必须重新验证该契约。
- Main AppUpdater 持有 feed、原始异常、下载和安装状态;设置页只消费状态投影。一次 electron-updater `error` 事件覆盖的并发检查不会在 Renderer 重复发错,但独立的后续检查仍有自己的报告生命周期。
- Robot V1 引导状态只在 Renderer 当前进程内保存,不持久化 Wi-Fi 凭据、activation code 或 Binding operation ID。相同进程内的模糊 Binding 重试复用 operation ID;无效码或重启后必须取得新码并使用新 operation ID。
- Robot Hotspot Module 只在 Main 内保存最近一次扫描的短效、不透明候选快照。新的扫描、clear、60 秒过期或进程重启使旧 candidate ID 失效;Renderer 关闭/重开向导以 generation 防止旧扫描/连接结果回写。
## External Interfaces
- Works Square 项目创建、版本上传、构建状态与 Release 状态 API。
- Works Square `/api/auth/me` 模块权限 API;Electron Main 持有 Bearer 并只向 Renderer 投影 `programming`/`design`/`learning`/`robot` 对应的四个布尔值。
- 本机 Host API 的发布路由;发布路由要求 Renderer capability。
- 本机 Host API 的 OpenCode session、history 与 prompt 路由,以及 Main-owned AI 模型代理。
- Main-owned electron-updater IPC 与 Works Square 平台/架构稳定 feed;正式安装产物发布不由 Renderer 控制。
- 服务端安全投影后的公共 `play_url`;只接受同源 HTTPS、精确 App 路径和可信版本状态。
- Works Square Workspace/Conversation API、每个 Conversation 的持久 Agent Gateway Session、单次 WebSocket ticket、双向命令/事件帧与幂等 REST 传输回退。
- Works Square Prompt Museum list/detail API;Main 添加当前账号 Bearer Token,Renderer 只使用 Host API 投影。
- Works Square Learning project list/detail/media/archive API;Main 添加当前账号 Bearer Token,并限制固定路径、DTO、封面/历史媒体 MIME/大小、归档重定向、摘要和 ZIP 签名;归档大小字段只作展示,不参与下载门禁。README 中经服务端校验的无凭据 HTTPS 图片由 Renderer 图片节点直接请求。
- 已实现的本机 Robot provisioning capability、固定 portal-open 与 hotspot scan/connect Host API。它们是本地 Main 操作,不读取 Works access token、不调用上游,也不接受任意 URL/SSID/BSSID/interface/profile。
## Last Updated
2026-08-21