Files
app/tauri/README.md
T

155 lines
13 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.
# LineUp Tauri Reference Host
这是 LineUp Mini Runtime 的第一版 Tauri 2 Reference Host。它独立于当前 `upstream/tangsengdaodao-android/` 快速实验工程,不替换或修改现有 Android 验证路径。
## 已实现的最小闭环
- 可信 Host 前端:AppServer 地址、手机号/验证码登录、退出;
- AppServer 交互:`/login``/messages/send``/messages/sync`
- 用户消息本地即时回显与送达状态;
- HTTP 同步游标:首次 `0`、后续最后序号 `+ 1`
- `lineup.v1.text``agent.status``agent.progress``error` 的基础 Renderer
- `marked + DOMPurify` 的 GFM Markdown 安全渲染;
- Tauri 2 Rust Host、capability 文件与桌面窗口配置。
AppServer 已配置只允许 `tauri://localhost``https://tauri.localhost``http://tauri.localhost` 三个 Tauri Host origin 跨源调用。若产品使用新的 Tauri origin 或独立 Web 客户端,应先在 `lineup-app-server/configs/lineup.yaml``client.allowedOrigins` 显式加入,不能放开任意 Origin。
macOS 的 `src-tauri/Info.plist` 为当前 Tailscale / 私网 HTTP AppServer 配置了 `NSAllowsArbitraryLoadsInWebContent`。该例外仅作用于 Host 的 WKWebView 内容请求,使其能访问类似 `http://100.x.y.z:8090` 的地址,不放宽 Rust 原生网络层;待所有 AppServer 都迁移到 HTTPS 后应移除。
`ui.open / patch / close``app.call` 已被协议层识别,但目前安全降级为可见提示;它们是下一阶段 Mini Runtime Surface Host 与 Capability Registry 的实现范围,不能在当前 Host 页面中直接执行 Agent JavaScript 或直接授予设备能力。
## Milestone 0 当前行为基线
本节记录 Runtime 重构前已经由 Mac 开发态验证的行为。它是 M0 的回归基线,不代表最终模块结构;正式阶段任务和准入门槛见 [App Layer 架构方案 §9.1](../../设计/02.正式方案/lineup-app-layer-architecture.md#91-阶段任务清单与硬性准入门槛)。
| 范围 | 当前行为 | 当前实现位置 | 重构后必须保持 |
|---|---|---|---|
| 登录 | 用户输入 AppServer 地址、手机号、验证码;登录请求 10 秒超时;地址仅保存到 localStorage | `src/main.ts``src/runtime/transport-adapter.ts` | 成功登录进入会话;失败原因对用户可见 |
| 会话 | 当前只有一个 Agent channel;退出清空页面内消息并停止同步 | `src/main.ts` | 会话状态必须可由 Store 管理,不能依赖 DOM |
| 发送 | 用户消息先本地显示,再通过 Transport 调用 `/messages/send`;显示已发送或失败 | `src/main.ts``src/runtime/transport-adapter.ts` | 即时反馈、送达状态与失败可见性不退化 |
| 历史同步 | 首次从 cursor `0` 连续拉取到空页;之后以 `lastSeq + 1` 每 1.5 秒经 Transport 增量轮询 | `src/main.ts``src/runtime/transport-adapter.ts` | 保持包含式 cursor 语义,不能漏消息或无限热循环 |
| Agent 文本 | 将 `lineup.v1.text` 解码为 Markdown,使用 `marked + DOMPurify` 渲染 | `src/protocol.ts``src/runtime/trusted-dom-renderers.ts` | GFM、安全净化和外链防护不退化 |
| Agent 状态 | `thinking` 显示临时指示器;其他状态更新顶栏 presence | `src/protocol.ts``src/runtime/trusted-dom-renderers.ts` | 状态应更新已有状态项,而非无限追加气泡 |
| 进度和错误 | progress/error 目前以系统提示显示 | `src/protocol.ts``src/runtime/trusted-dom-renderers.ts` | 迁移后至少保持可见,M1 再升级为正式卡片 |
| 未实现扩展 | `ui.open / patch / close``app.call` 仅安全降级为提示,绝不执行不可信脚本或原生能力 | `src/protocol.ts``src/main.ts` | 在 M2/M3 完成前始终保持拒绝执行 |
### M0 回归场景
每次迁移或拆分模块后,至少执行以下场景;其中 `R01` 为构建级检查,其余为 Mac 浏览器访问 Linux 开发 Host 的人工验收,直至 M0 建立自动化测试。
- **M0-R01:构建。** `npm run build` 通过 TypeScript 检查与 Vite 打包。
- **M0-R01aRuntime 回归。** `npm test` 执行 Kernel 与 Conversation Store 的自动化回归;覆盖合法/非法/未知协议输入、重复 envelope ID、包含式 cursor、本地回显和 sync echo 去重。
- **M0-R02:登录。** 使用 Tailscale AppServer 登录;成功切换到聊天页,错误地址/超时给出可理解提示。
- **M0-R03:历史追平。** 登录后 presence 从“正在同步消息”变为“已同步,等待消息”,已有 Agent 历史可见。
- **M0-R04:本地回显。** 发送普通文本后,用户气泡立即出现,随后显示已发送或发送失败。
- **M0-R05Agent 回复。** Hermes 回复能够在未刷新页面时出现,并按 Markdown 安全渲染。
- **M0-R06:刷新恢复。** 刷新并重新登录后,历史仍可追平,不能重复无限渲染同一条 Agent 消息。
- **M0-R07:安全降级。** 收到未知 `lineup.v1.*` 类型、`ui.*``app.*` 请求时,页面不崩溃、不执行脚本/能力,并给出可见提示。
### M0 完成记录(2026-08-03
M0-01M0-08 已完成。Mac 经 Linux Tailscale Web Host `http://100.121.118.116:1420` 完成 M0-08 总验收:登录、历史追平、本地回显、Agent 回复、真实 Markdown(标题、加粗、行内代码、围栏 TypeScript 代码块)和刷新重新登录恢复均正常;用户与 Agent 消息不重复,presence 最终为“已同步,等待消息”。同时 `npm test` 的 2 个文件 / 8 个 Runtime 测试与 `npm run build` 均通过。
### M1-01 Tool Call 状态机(已完成)
`src/protocol.ts` 现可严格解码 `lineup.v1.tool.call``lineup.v1.tool.result``lineup.v1.tool.cancel`,只接受 `choice``confirm``input` 三种声明性请求。`src/runtime/tool-call-state.ts` 是独立于 UI、Storage、Transport、Tauri 与 Capability 的纯状态机;它以 `call_id` 为唯一关联键,只允许 `pending → submitted → completed / failed / cancelled / expired`,拒绝重复调用及非法状态跃迁。Interaction Kernel 仅投影这些状态,且只有已提交的 call 能接受 result。
这不是可用的交互组件:M1-01 不渲染选项、不提供按钮/表单、不发送 tool result,也不修改 Hermes Adapter。可信 ActionGroup 留给 M1-02Confirm/Input/Form 留给 M1-03。`npm test` 当前为 3 个文件 / 14 个测试通过,`npm run build` 通过。
### M1-02 ActionGroup(已完成)
`choice` tool call 必须在 `payload.data.action_group` 中使用 `single-choice``multi-choice``button` 模式,声明 112 个具有唯一 `id` 的 action。协议层会拒绝未知模式、空列表、重复 id 和超长文本。可信 Renderer 使用原生 DOM 按钮而不解释远端 HTML:单选和按钮动作点击后立即提交,多选在用户显式点击“提交选择”后一次性提交;卡片随即显示“已提交,等待 Agent 确认”并禁用所有控件。过期 call 从一开始即不可操作。
提交仅写入 Interaction Kernel 的 `{ action_ids: [...] }` submission 快照;此阶段没有网络请求、outbox、`tool.result` 回传、刷新恢复或 Hermes Adapter 改动。它们分别留给后续 M1 任务。`npm test` 当前为 3 个文件 / 15 个测试通过,`npm run build` 通过。
### 当前结构债务(M0 的改造对象)
`src/main.ts` 目前包含 App Shell、页面 DOM、会话 cursor、同步循环与 Runtime 编排;M0-05 已将登录、发送与同步 HTTP `fetch` 迁入 `src/runtime/transport-adapter.ts`M0-06 已将所有可信 DOM renderer 迁入 `src/runtime/trusted-dom-renderers.ts`。在 M0 准入前不新增 Surface 或 Capability 功能。
### M0-02 协议模型(已完成)
`src/protocol.ts` 是目前唯一允许把 Transport 原始 payload 转换成可信表现模型的入口。它定义了 `JsonValue``Actor`、严格 `Envelope``ConversationItem``UserAction``DeliveryState``ProtocolFallback`,并提供:
- `parseEnvelope(value)`:校验版本、消息类型、`id``conversation_id`、sender/target、timestamp 和 JSON payload
- `parseEnvelopeJSON(raw)`:供不接受历史纯文本的 Transport / Kernel 路径把非法 JSON 明确归为 `invalid_json`
- `decodeConversationItem(raw)`:保留历史纯文本作为 Markdown 的兼容行为;对于非法 LineUp envelope、未知类型和各类非法 payload,返回受控 fallback,绝不触发执行。
该文件不依赖 DOM、`fetch`、Tauri 或本地存储。Surface 与 Capability 在本阶段只校验其基础合约并降级显示;不加载 bundle,也不调用能力。下一项 M0-03 才会将此模型接入 Interaction Kernel 和 Renderer Registry。
### M0-03 Interaction Kernel 与 Renderer Registry(已完成)
`src/runtime/interaction-kernel.ts` 是 Transport 与表现层之间的唯一入口:它将原始同步/实时 payload 解码为 `ConversationItem`,对有有效 LineUp `id` 的消息实施有界去重,并将 Agent status 投影为可读取的 presence 快照。它不执行 `ui.*``app.*` 或用户动作。
`src/runtime/renderer-registry.ts` 只根据已验证的 `ConversationItem.kind` 分派已注册的可信 renderer;renderer 的输入只有表现项与 View context,不会获得 Transport 或 Capability 对象。当前聊天页已通过该 Registry 渲染同步进入的标准项。历史上 M0-03 完成时具体 DOM renderer 仍在 `main.ts`;该 M0-06 迁移现已完成,当前可信 DOM renderer 位于 `src/runtime/trusted-dom-renderers.ts`
## 本地命令
```bash
cd lineup-app/tauri
npm install
# 日常开发:启动 Vite 热更新 + 未打包的 Tauri 桌面壳
npm run desktop:dev
# 纯 Web 开发:Linux 开发机监听全部网卡,供 Mac 浏览器访问
npm run web:dev
# Mac 浏览器访问(经 Tailscale
# http://100.121.118.116:1420
# 仅检查并构建前端静态文件
npm run build
# Linux:仅构建 Debian 包,避免为不需要的 AppImage 下载额外运行器
npm run tauri -- build --debug --bundles deb
# macOSApple Silicon / Intel):生成 .app 与 .dmg
npm run tauri build -- --debug
```
`npm run desktop:dev` 不生成 `.app``.dmg` 或安装包。前端 TypeScript / CSS / HTML 保存后会由 Vite 自动热更新;改动 `src-tauri/` 中的 Rust Host 代码时,Tauri 会重新编译并重启开发窗口。只有需要交付可安装文件时才执行 `tauri build`
`npm run web:dev` 只运行同一份可信 Host Web UI,适合在浏览器中快速调试登录、消息和 Renderer。它监听 `0.0.0.0:1420`Mac 通过 Tailscale 打开 `http://100.121.118.116:1420`,直接验证 Linux 工作区的 Vite 热更新;不需要为每次验证将源码同步到 Mac。它不具备 Tauri 原生能力,也不应作为第三方 Mini Runtime Surface 的安全验收环境。AppServer 仅对固定开发 Origin `http://127.0.0.1:1420``http://100.121.118.116:1420` 配置 CORS;若改动 Vite 端口或 Host 地址,必须同步收紧更新 AppServer 的 `client.allowedOrigins`,不得改为 wildcard。
本项目的 `tauri``check:rust` npm 脚本会自动识别 Linux x86_64、macOS Apple Silicon、macOS Intel 的 Rust stable toolchain,并加入 `PATH`。因此不要直接执行裸 `tauri build`;统一使用 `npm run tauri build -- --debug`。如 Rust toolchain 安装在其他位置,可临时覆盖:
```bash
RUST_TOOLCHAIN_BIN=/path/to/rust/bin npm run tauri build -- --debug
```
## 本机构建状态(2026-08-03
- `npm run build`:已通过;
- Cargo/Tauri Rust crates:已下载并开始编译;
- Tauri Linux desktop build:已通过;debug 可执行文件和 Debian 包已生成;
- Android Spike:尚未开始。仍需要 Android NDK、Rust Android targets 与 Tauri Android 初始化。
Linux 桌面构建依赖以下系统开发库;本机已具备。新环境缺少时可安装:
```bash
sudo apt-get update
sudo apt-get install -y pkg-config libdbus-1-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev
```
本机已生成的产物:
```text
src-tauri/target/debug/lineup-tauri
src-tauri/target/debug/bundle/deb/LineUp_0.1.0_amd64.deb
```
Tauri 构建产物位于 `src-tauri/target/`,已被 Git 忽略。图标源文件是 `src-tauri/icons/icon.svg`,其余 PNG/ICO/ICNS 与 Android/iOS 图标由 `npm run tauri icon src-tauri/icons/icon.svg` 生成。
## 目录边界
```text
tauri/
├── src/ # 可信 LineUp Host 前端
│ ├── main.ts # 会话 UI、AppServer transport、Renderer 调度
│ └── protocol.ts # 平台无关的 LineUp v1 → ConversationItem 解码
├── src-tauri/ # Tauri Rust Host 与 capability 配置
└── package.json # Vite、Tauri CLI 与前端依赖
```
第三方 Agent Surface 未来必须运行于独立隔离执行域,只能经 LineUp Mini Runtime 事件桥与 Capability Gateway 通信;不得访问 Tauri `invoke`、Host DOM、登录态或本机特权。