# AgentMail 鸿蒙客户端(ArkUI)构筑计划 > 邮件驱动·多智能体协作平台 — HarmonyOS 客户端 > 构筑者:dsh | 版本:v0.1 | 日期:2026-09-07 > 上游接口契约:[docs/API.md](API.md) · 平台规划:[docs/PLAN.md](PLAN.md) --- ## 0. 定位与分工 - jianf(发起人)→ 收件方 pi 负责 **ele 客户端**(Win/Linux),并作为长期维护者给我方提供 API 解读与支持; - dsh(我)→ 负责 **鸿蒙客户端**(ArkUI),本计划即鸿蒙侧的构筑蓝图。 - 平台后端是 **同一个 Gateway**(Go + SQLite,HTTP REST + SSE),鸿蒙客户端调用其**人类接口** (`/api/v1/me/*` 等),与 WebUI 完全等价,没有私有通道。 - 交付形态:一套 Stage 模型 ArkTS 工程,以 HAP 形式运行于 HarmonyOS 手机/平板/2in1, 覆盖 WebUI 的「邮件收发 + 会话 + 联系人 + 权限决策」核心功能面。 **本机工具链(已勘察确认)**: | 组件 | 版本/位置 | |---|---| | DevEco CLI | 26.0.0 Beta2,`/opt/huawei/command-line-tools`(`devecocli` 1.3.0 已包装在 `/usr/local/bin`) | | HarmonyOS SDK | 6.1.0(API 23,Version 6.1.0.105 Release),`/opt/huawei/harmonyos/ohos-sdk/linux` | | 包管理 | `ohpm`(command-line-tools/ohpm) | | 模拟器 | `HarmonyPhone`(phone / HarmonyOS 6.1.1(24),KVM 加速,按需 `harmony-emu start`) | | 真机 | 可选:hdc 无线调试(动态 IP,签名 hap 走 `devecocli signature generate`,材料在 `~/.ohos/config`) | **鸿蒙生态约束(真机安装必须签名)**:模拟器可装 unsigned hap;真机一律要签名。 签名材料已就绪,`devecocli signature generate --force` 可随时重新生成。 --- ## 1. 目标功能面(与 WebUI 对齐的裁剪集) 首版(M1-M3)做「个人邮件客户端」核心闭环,管理员面(用户管理/Agent 密钥/默认预算)不在鸿蒙侧首版 范围内 —— 那是 Web 管理台的职责,鸿蒙端聚焦日常使用。 | 模块 | 功能 | 对应 API | |---|---|---| | 登录/凭证 | 账号密码登录;用户密钥登录(跨设备场景,Bearer) | `POST /auth/login`、`GET /auth/me`、`POST /auth/logout`、`POST /me/keys` | | 收件箱/发件箱 | 未读/全部列表、未读标记、发件箱 | `GET /me/mail/inbox?s=unread\|all`、`GET /me/mail/sent` | | 会话列表 | 我参与的会话(卡片/列表视图)、未读数、预算徽标 | `GET /me/sessions`、`GET /contacts`、`GET /contacts/suggest` | | 邮件详情 | Markdown 正文渲染、附件清单、抄送显示、对话树 | `GET /mail/{id}`、`GET /mail/{id}/thread` | | 写信 | 收件人三段式补全、抄送、正文、往返预算、附件上传、会话别名(`.new` 时) | `POST /me/mail/send`、`POST /me/attachments` | | 回复/转发 | 回复落回原会话、转发开新线索(附件随行) | `POST /me/mail/{id}/forward` | | 权限决策 | 待决请求列表、同意/拒绝/备注 | `GET /permission/pending`、`POST /permission/decide` | | 实时推送 | 新邮件、权限决策、会话更新(SSE) | `GET /events/stream?access_token=` | | 会话管理 | 改名、预算调整、**权限档位调整(plan / workspace / full)**、改名提议展示/接受/驳回、归档 | `PUT /sessions/{id}/alias`、`GET\|PUT /sessions/{id}/budget`、`PUT /sessions/{id}/permission`、`rename-proposal` 组、`POST /contacts/archive` | | 权限档位 | 会话头部展示当前档位与强制力(`permission_mode` / `permission_enforcement`),可随时调整 | 见会话管理行;档位三值 `plan / workspace / full` | | 附件 | 下载(保存到本地/分享) | `GET /me/attachments/{id}` | 明确**不做**(首版):管理员后台、Agent 侧接口、日历(`/calendar/*`,Web 端新功能,待 api 解读后二版评估)。 --- ## 2. 技术方案 ### 2.1 工程形态 - Stage 模型 + `@ohos/http`(默认网络库,无需三方依赖),`entry` 单模块起步; 若 UI 面膨胀再拆 `common`/`feature` 多模块,首版不预先拆。 - 语言:**纯 ArkTS**(严格模式,`arkts-*` lint 全开),不用 JS/TS 混写。 - 状态管理:首版用组件内 `@State`/`@Link`/`@Provide` 即可;跨页共享(登录态、token、未读数) 提炼为**单例 Service 层**(`@ohos.data.preferences` 持久化凭证),不引第三方状态库。 ### 2.2 API 层(核心设计) 对齐 WebUI 的 `src/api/` 思路,鸿蒙侧实现一份同构的客户端: ``` entry/src/main/ets/ ├── ability/ EntryAbility.ets ├── pages/ 登录 / 主界面 / 会话 / 邮件详情 / 写信 / 权限 ├── api/ │ ├── ApiClient.ets # 统一 request():base + headers + 错误归一化(400/401/403/404/409/429) │ ├── AuthApi.ets # login / logout / me / keys │ ├── MailApi.ets # inbox / sent / detail / thread / send / forward / read │ ├── SessionApi.ets # sessions / alias / budget / rename-proposal │ ├── ContactApi.ets # contacts / suggest / archive │ ├── PermissionApi.ets # pending / decide │ ├── AttachmentApi.ets # upload / download │ └── SseClient.ets # SSE 流式解析 + 指数退避重连 ├── model/ # 领域模型(Mail/Session/Contact/…,与 API.md 字段一一对应) ├── store/ # 单例:SessionStore / MailStore / UnreadStore(跨页共享) ├── components/ # MailListItem / SessionCard / AddressInput / PermissionCard / MarkdownView … └── common/ # 常量(BASE_URL、事件名)、工具(相对时间、rune 截断、Markdown 渲染) ``` 要点: - **基地址与凭证**集中在 `common/config`:`apiBase` 可运行时配置(设置页填写网关地址), 与 WebUI 的 `window.__AGENTMAIL_API_BASE__` 同思路 —— 一个构建产物可指向任意后端。 - **认证双通道**:Cookie(账号密码登录,`@ohos.http` 手动管理 Cookie)与 `Authorization: Bearer `(密钥登录)。SSE 与附件下载这两个浏览器侧只能走 query 令牌的端点, 鸿蒙侧可以带请求头,优先 Bearer,避免令牌进 URL 日志。 - **错误归一化**:统一把服务端 `{"error": "中文可操作描述"}` 与 HTTP 状态码翻译成 `ApiError { code, message }`;401 统一触发「回登录页」;429/403 文案原样上屏。 ### 2.3 SSE 实时(关键难点) `@ohos.http` 的流式响应(`on('dataReceive')`)用于解析 SSE 帧: - 手工按 `\n\n` 分帧,解析 `event:` / `data:` 行;事件表与 API.md 一致: `new_mail` / `permission_decision` / `session_update` / `session_archived` / `agent_online`。 (pi 梳理补充:`new_mail` 与 `session_update` 事件还带 `session_alias` / `permission_mode` / `permission_enforcement` 字段 —— 鸿蒙端模型层按此定义, 会话卡片同时显示档位徽标。) - **断线重连**:指数退避 1s→15s 上限(对齐 Web 端 `api/sse.ts`),`AbortController` 优雅停止。 - `new_mail` 到达 → 通知 MailStore 刷新收件箱与未读数;`permission_decision` → 刷新权限卡片。 - 生命周期:App 前台订阅、后台/退到 `onBackground` 暂停,回到前台重连(省电 + 避免无谓重连风暴)。 ### 2.4 Markdown 渲染 鸿蒙没有现成 react-markdown。选择(按优先级): 1. **首版:轻量自研解析子集** —— 标题/粗体/斜体/行内代码/代码块/列表/引用/链接/分割线, 用 `Span`/`RichText` 逐段渲染;**raw HTML 一律不渲染**、链接协议白名单(http/https), 守住 Web 端 Markdown XSS 回归测试立下的同一条纪律(见 PLAN.md 6.4)。 2. 若正文复杂度超预期,评估 `@ohos/webview` 加载本地渲染页(需白名单校验后再注入,引入 XSS 面,慎用)。 ### 2.5 附件 - 上传:`@ohos.request` / `http` multipart 到 `POST /me/attachments`(字段名 file), 返回 `attachment_id` 后随 `send` 的 `attachment_ids` 使用 —— 两步流程与 Web 端一致。 - 下载:`GET /me/attachments/{id}` → 写入应用沙箱文件目录,用 `@ohos.file.picker` / `@ohos.share` 交出去(保存到相册/分享)。注意服务端**强制 octet-stream + attachment**, 按响应头 `Content-Disposition` 解析文件名(API.md 七、已暴露该响应头)。 ### 2.6 UI 布局(ArkUI) | 界面 | 结构 | |---|---| | 登录页 | 服务器地址 + 账号/密码 或 用户密钥;「保存地址」入 preferences | | 主界面(手机) | 底部 Tab:收件 / 会话 / 联系人 / 权限 / 我的;顶栏未读徽标 | | 主界面(平板/2in1) | 两栏/三栏自适应:左列表 + 右详情;`@ohos.mediaquery` 断点切换 | | 会话列表 | 列表/卡片视图切换;卡片含主题、最新一封发件人+摘要、预算徽标(剩 1 橙 / 用尽红 / 不限不显示) | | 邮件详情 | Markdown 正文 + 附件行 + 抄送行 + 权限卡片 + 底部「回复 / 转发」+ 对话树入口 | | 对话树 | 缩进 + 连接线(不引图形库),分块加载:`dir=around` 首屏 + `up/down` 增量 | | 写信页 | 收件人补全(三段式 suggest)+ 抄送 + 主题 + 正文 + 预算档位 + 附件 + 会话别名(仅 `.new`) | | 设置 | 网关地址、退出登录、密钥管理(`/me/keys` 创建/吊销,展示 token_hint) | 窄屏三维地址补全:`/contacts/suggest` 无参 → name;带 name → path;带 name+path → 别名+`new`, 逐级展开,方向键选择(对齐 Web 端 AddressInput 交互)。 --- ## 3. ArkTS 工程注意点(本机实操约束) 在写第一行 `.ets` 前先加载 `arkts-grammar-standards` 技能,按规范落地: - 严格模式:禁 `any`、禁未声明类型、`undefined` 处理显式化(`??` / optional chain 白名单内)。 - 组件用 `struct` + `@Component`,状态用 `@State`/`@Prop`/`@Provide`;列表用 `LazyForEach` (邮件量可能上百,`ForEach` 全量渲染会卡)。 - 网络模型用 `interface` 描述(与 API.md 字段对齐),服务端多余字段不枚举。 - Sendable / Actor 需求确认:首版无跨线程共享大对象,先不进;若 SSE 解析放 worker 再评估。 - `@ohos.http` 需在 `module.json5` 声明 `ohos.permission.INTERNET`(示例项目已带,照抄)。 - 构建:`devecocli build` / `hvigorw assembleHap` 产出 `entry-default-signed.hap`; 模拟器 `hdc install`(unsigned 亦可),真机走签名 hap + `hdc tconn`。 --- ## 4. 里程碑与验收 ### M0 脚手架(0.5 天) - `devecocli create` 建 Stage 工程(包名 `com.agentmail.harmony`,API 23); - 空壳 App 在 HarmonyPhone 模拟器跑通(`harmony-emu start` → build → install → 截图); - 签名配置写入 `build-profile.json5`(复用 `~/.ohos/config` 材料)。 **验收**:启动图标进桌面,`devecocli ui screenshot` 可见主页面。 ### M1 API 层 + 登录(1-2 天) - `ApiClient`(Base + 错误归一化 + 双认证通道)+ `AuthApi`; - 登录页(账号密码 / 用户密钥)+ token/凭证持久化 + 401 全局回落登录。 **验收**:连本机 Gateway(`http://localhost:8180`)用真实账号登录成功, `GET /auth/me` 返回当前用户;错密码显示服务端中文文案。 ### M2 邮件闭环(3-5 天) - 收件箱/发件箱列表(未读态、分页)、邮件详情(Markdown 渲染 + 附件行 + 抄送)、 回复/转发、写信页(三段式补全 + 预算档位 + 附件上传两步流 + `.new` 会话别名)。 **验收**:人 → Agent → 人 端到端:给 pi/opencode 发一封带抄送与附件的任务邮件, Agent 回信后在本端看到详情并可继续回复;新信未读计数正确。 ### M3 实时 + 会话 + 权限(2-3 天) - `SseClient`(解析 + 退避重连 + 生命周期);未读徽标与列表随事件刷新; - 会话列表(列表/卡片)、改名提议条(接受/驳回)、预算调整、**权限档位调整与档位徽标**; - 权限待决列表 + 决策卡片。 **验收**:另一浏览器标签给本账号发信,鸿蒙端 ≤3s 内未读 +1(SSE 路径); Agent 权限请求出现在待决列表,决策后 Agent 侧收到(对照 Web 端行为一致)。 ### M4 对话树 + 附件落盘 + 打磨(2-3 天) - 对话树分块加载(`around/up/down` + 滚动位置补偿); - 附件下载到沙箱 → picker/share 导出;平板/2in1 自适应布局、深色模式(如有余力)。 **验收**:251 封长链线索在鸿蒙端分页取完、depth 连续;真机(若可用)安装签名 hap 全流程跑通。 ### M5 联调 + 文档(1-2 天) - 与 ele 端(pi)在**同一 Gateway** 上双端并行联调:双端同账号互斥/一致性问题暴露; - 对照 `PLUGIN-CONTRACT.md` 的验收清单做客户端视角补测; - 更新本计划为「已落地」,同步 README 接入说明。 **预计总工时**:约 2-3 周(含联调与返工缓冲)。 --- ## 5. 风险与依赖 | 风险 | 对策 | |---|---| | `@ohos.http` 流式 SSE 稳定性(长连接、断流) | 退避重连 + 生命周期暂停;降级方案:轮询 `/me/mail/inbox`(SSE 仅作加速) | | ArkTS 严格模式对三方库不友好 | 尽量零三方依赖;网络/存储/文件全走系统 API | | Markdown 渲染面 | 首版渲染子集 + 协议白名单;复杂稿需时评估 webview 方案并单独过 XSS 评审 | | 真机签名链路 | 材料已就绪;`devecocli signature generate --force` 兜底;无真机时模拟器验收为主 | | 服务端接口变更 | 依赖方(pi)的 API 解读;所有模型层集中在 `model/`,变更只动一处 | | 日历等 Web 新功能 | 首版明确排除,M5 后按 API 解读评估二版 | **需要 jianf / pi 提供的支持**: 1. 一个稳定的测试 Gateway 地址(开发联调用,含可登录账号); 2. 接口变化时按 `docs/API.md` 格式及时更新 —— 鸿蒙端模型层严格对齐该文档。 --- ## 6. 与 ele 端(pi)的协同点 - 两端共用**同一份 API.md 契约**,建议把「API 解读」沉淀为 `docs/API-INTERPRETATION.md` (字段语义、边界行为、踩坑),供双端引用,避免各自重复踩; - 后端新增字段时(如日历),双端各自按需跟进;鸿蒙端涉及协议层的问题由 pi 提供解读支持(按分工)。