Files
pikasTech-unidesk/docs/reference/microservices.md
T

211 lines
47 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.
# UniDesk User Services Reference
UniDesk 用户服务是挂载到 UniDesk 核心服务上的、面向用户使用的非核心业务服务;底层配置、API、CLI 和 E2E check 名称仍保留 `microservice` 兼容命名。UniDesk 核心服务(frontend、backend-core、database、provider-gateway、主 server 控制入口)不得依赖某个用户服务存在;缺少部分或全部用户服务时,核心仍必须能启动、运行和完成基础运维。
用户服务容器可运行在计算节点 Docker 或主 server Docker 中,主 server 只保存仓库引用、commit id、Dockerfile/docker-compose 引用、provider 映射和前端集成配置,不把业务仓库整体复制进 UniDesk。
## Boundary
- 用户服务后端端口默认只绑定计算节点本机地址,例如 `127.0.0.1:<port>`,不得直接暴露公网。
- 浏览器只访问 UniDesk frontendfrontend 通过同源 `/api/microservices/*` 代理到 backend-corebackend-core 再通过目标 provider-gateway 的 `microservice.http` 能力访问计算节点本机后端。
- backend-core REST API、database 和计算节点用户服务后端都不得新增公网端口;公网入口仍只有 frontend 和 provider ingress。
- `microservice.http` 只允许 provider-gateway 访问 `http://127.0.0.1``http://localhost``http://host.docker.internal` 这类节点本地地址;主 server 内置用户服务可使用同一 Compose 网络内的显式服务名,例如 `todo-note:4211``codex-queue:4222`。backend-core 还必须用 `allowedPathPrefixes``allowedMethods` 同时限制可代理路径和 HTTP 方法。
## Config Contract
`config.json``microservices` 是用户服务的唯一登记来源。每个条目必须包含:
- `id``name``providerId``description`,用于 CLI、backend-core 和 frontend 统一识别。
- `repository.url``repository.commitId`,用于记录业务代码的外部权威来源;UniDesk 不 vendoring 业务全量代码。
- `repository.dockerfile``repository.composeFile``repository.composeService``repository.containerName`,用于说明部署应复用业务仓库自身维护的 Dockerfile/docker-compose。
- `backend.nodeBaseUrl``backend.nodeBindHost``backend.nodePort``backend.proxyMode``backend.public=false``backend.frontendOnly=true``backend.allowedMethods``backend.allowedPathPrefixes``backend.healthPath`,用于定义计算节点端口到 UniDesk frontend-only 代理的映射。
- `development.providerId``development.sshPassthrough=true``development.worktreePath`,用于说明开发调试入口必须在计算节点上通过 UniDesk SSH 透传完成。
- `frontend.route``frontend.integrated=true`,用于说明该业务前端已经整合到 UniDesk React 控制台,而不是继续公开业务自身前端。
## Compute-Node Development Convention
主 server 本地开发边界固定为只开发 UniDesk frontend 与必要的 UniDesk 配置/代理登记;非 UniDesk 核心功能的后端、Dockerfile、GPU/训练容器、业务数据迁移和业务调试不得默认占用主 server 有限主机资源。涉及 findjob、pipeline、MET Nonlinear 这类业务功能时,应通过 `bun scripts/cli.ts ssh <PROVIDER_ID> ...` 或 remote CLI SSH 透传进入计算节点,在计算节点本地业务仓库中开发、构建和调试;开发完成后,只把业务服务以用户服务形式登记到 UniDesk。
业务仓库由业务系统自己维护,包括源码、Dockerfile、docker-compose、配置模板和业务测试。UniDesk 只引用业务仓库 URL、commit id、Dockerfile/docker-compose 路径和运行容器名;不得把业务全量代码复制到 `src/components/microservices/` 形成双维护。`src/components/microservices/` 只能放通用示例或 UniDesk 自有示例,不作为业务仓库镜像。
## Main Server User Services
主 server 只承载对统一入口、状态迁移或控制面自动化有明确必要的用户服务。该类服务仍遵守不暴露公网端口、前端统一 React 控件化展示的规则;业务持久状态必须写入主 PostgreSQL;`.state/` 只能保存日志归档、缓存或可重建工件,不能作为任务、队列、未读、通知 outbox 等权威状态来源。
### Todo Note On Main Server
当前 Todo Note 作为 `id=todo-note` 的用户服务登记在 `config.json`
- 来源工作树:D518 的 `/mnt/d/work/todo_note`;主 server 工作树固定放在 `/root/todo_note`,用于 Docker build 和后端维护。
- Provider`main-server`,由本机 provider-gateway 通过 `microservice.http` 访问同一 Compose 网络内的 `http://todo-note:4211`
- 代码引用:`https://gitee.com/Lyon1998/todo_note` 与配置中的 `repository.commitId`;UniDesk 仓库只记录引用,不 vendoring Todo Note 全量业务代码。
- 部署引用:`/root/todo_note/Dockerfile` 构建纯后端镜像,Compose service 为 `todo-note`,容器名为 `todo-note-backend`
- 数据库:Todo Note 不再使用 JSON 文件作为运行时权威存储;必须把 D518 `data/registry.json``data/instances/*.todo.json``*.history.jsonl` 迁移到主 server PostgreSQL 的 `todo_note_instances``todo_note_history` 表。
- 代理路径:只允许 `/api/` 前缀;允许方法为 `GET``HEAD``POST``DELETE`,用于保持 Todo Note 原有清单创建/删除、任务增删改、提醒、展开/收起、移动、撤销/重做等功能。
- UniDesk 前端:`用户服务 / Todo Note` React 页面负责展示清单列表、树形任务、筛选、提醒、拖放/上移下移、撤销/重做、字号控制和显式原始 JSON 按钮。
Todo Note 在 UniDesk 语境中按纯后端服务管理:不得继续公开 Todo Note 自身 Vite/Web 前端,也不得把 `4211` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/todo-note/...` 同源代理访问 Todo Note 后端。
Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网执行 `/root/todo_note/scripts/migrate-json-to-pg.ts`,并显式指向主 PostgreSQL`docker run --rm --network unidesk_default -v /root/todo_note:/app -w /app -e DATABASE_URL='postgres://unidesk:unidesk_dev_password@database:5432/unidesk' oven/bun:1-alpine bun scripts/migrate-json-to-pg.ts`。迁移脚本必须输出 `importedInstances: 5``totalTodos: 100``completedTodos: 54` 这一类可审计摘要,不能只依赖前端页面观察。
Todo Note 数据迁移后必须验证:`microservice proxy todo-note /api/instances` 至少能看到 `CONSTAR``大论文``找工作``小论文``事务` 五个迁移清单,总任务数不低于源数据的 100 条;再通过代理创建临时清单、添加任务、切换完成、撤销并删除临时清单,证明写入路径走 PostgreSQL 且不会污染长期数据。
### Project Manager On Main Server
当前 Project Manager 作为 `id=project-manager` 的用户服务登记在 `config.json`
- Provider`main-server`,由本机 provider-gateway/直接内网代理访问同一 Compose 网络内的 `http://project-manager:4233`
- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/project-manager`,属于 UniDesk 自有主 server 用户服务。
- 部署引用:UniDesk 根仓库 `docker-compose.yml` 中的 `project-manager` serviceDockerfile 为 `src/components/microservices/project-manager/Dockerfile`,容器名为 `project-manager-backend`
- 数据库:项目清单写入主 PostgreSQL 表 `project_manager_projects`;服务启动时自动创建/补齐 schema,不依赖仅首次生效的 database init SQL。
- 初始数据来源:D601 Windows 文件 `C:\Users\liang\xwechat_files\wxid_01rxm0yxjksk12_345f\msg\file\2026-05\合作项目列表_I_20260309.xlsx`,通过 UniDesk SSH 透传读取到主 server 后,用 `/api/import/excel` 导入。当前 Excel 表头为 `序号``合同号``项目名称``当前状况``待完成``付款情况``其它`
- API`GET /health``GET|POST /api/projects``GET|PUT|DELETE /api/projects/{id}``POST /api/import/excel``POST /api/import/projects``GET /api/projects/export.xlsx`
- 代理路径:只允许 `/health``/logs``/api/` 前缀;允许方法为 `GET``HEAD``POST``PUT``DELETE`
- UniDesk 前端:`用户服务 / Project Manager` React 页面负责展示主 server 仓库引用、私有后端映射、项目指标、项目表格、筛选搜索、编辑表单、Excel 导入和 Excel 导出;完整原始 JSON 只能通过显式 `查看原始JSON` 打开。
Project Manager 在 UniDesk 语境中按纯后端服务管理:不得将 `4233` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/project-manager/health``/api/microservices/project-manager/proxy/...` 同源代理访问项目管理后端。
### Codex Queue On Main Server
当前 Codex Queue 作为 `id=codex-queue` 的用户服务登记在 `config.json`
- Provider`main-server`,由本机 provider-gateway 通过 `microservice.http` 访问同一 Compose 网络内的 `http://codex-queue:4222`
- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/codex-queue`,属于 UniDesk 自有控制面组件。
- 部署引用:UniDesk 根仓库 `docker-compose.yml` 中的 `codex-queue` serviceDockerfile 为 `src/components/microservices/codex-queue/Dockerfile`,容器名为 `codex-queue-backend`
- 上线纪律:Codex Queue 相关的前端或后端改进必须在同一任务内正式上线并验证公网 frontend 或 live API,不能只停留在源码、构建产物或“后续再上线”。重建 `frontend` 只替换无状态 WebUI 容器,不会触碰 `codex-queue-backend`、PostgreSQL 队列或运行中 Codex thread,不能以“可能影响长期任务”为由延迟前端上线;`codex-queue-backend` 本身带有 restart-recovery,允许按 `server rebuild codex-queue` 或 Compose 重启/替换,停止、重启或重建后必须从持久化状态恢复运行中和排队任务。
- Codex 认证:容器只从主 server 的 `/root/.codex/config.toml` 同步 Codex provider 配置到 `.state/codex-queue/codex-home`,并通过运行时环境透传 `OPENAI_API_KEY``CRS_OAI_KEY` 等 provider 所需变量;这些 provider 环境变量必须由 `writeComposeEnv` 写入 `.state/docker-compose.env` 并由 Compose 注入,确保 `server rebuild codex-queue` 的外部 Docker job runner、自重建和容器重启后不会丢失认证。新增 provider 的 `env_key` 时必须增加同类运行时透传和 Compose env 持久化,禁止把 Codex 或 MiniMax 密钥写入仓库文件。Codex Queue 开发容器必须只读挂载 host 的 root SSH 目录到 `/root/.ssh`(默认 `${UNIDESK_HOST_ROOT_SSH_DIR:-/root/.ssh}`),让容器内 `git push``ssh -T git@github.com` 与 host 使用同一套 GitHub SSH key/known_hosts;不得把私钥复制进镜像或仓库。
- Develop-ready 镜像:Codex Queue 镜像必须在启动前预装 UniDesk/Pipeline 调试所需工具,至少包含 `codex``bun``node``npm`/`npx``git``rg``curl``python3`/`pip3``docker``docker compose``docker-compose``jq``ssh``rsync``make``gcc`/`g++``tar``gzip``unzip`;不得依赖 Codex 任务运行时再 `apt-get install` 这些基础环境。
- Codex 控制:服务内部启动 `codex app-server --listen stdio://`,用 JSON-RPC 调用 `thread/start``turn/start``turn/steer``turn/interrupt`,并监听 `turn/completed`、assistant delta、reasoning delta、command output delta、file diff delta 等通知生成前端可轮询的 transcript。
- 用户输入持久化:任务初始 prompt 以 `basePrompt/displayPrompt` 作为结构化来源,运行中追加的 `turn/steer` prompt 必须写入 `promptHistory`transcript 构建时从这些结构化字段合成 `Submitted prompt``Steer prompt`,不能只依赖有 600 条上限的 raw output,否则长任务输出增长后会丢失关键人工指令。
- 队列语义:`POST /api/tasks``/api/tasks/batch` 入队,服务始终只运行一个 Codex turn;当前任务真正终止后才推进下一个任务。`GET /api/tasks``GET /api/tasks/{id}` 返回队列、attempt、judge 和输出;`GET /api/tasks/{id}/summary` 返回按任务 ID 查询的结构化摘要,包括初始 prompt、最后 assistant message、工具调用摘要、attempt、judge、错误和耗时;CLI 入口是 `bun scripts/cli.ts codex task <taskId>``POST /api/tasks/{id}/steer` 向运行中 turn 推入 prompt`POST /api/tasks/{id}/interrupt``DELETE /api/tasks/{id}` 打断/取消;`POST /api/tasks/{id}/retry` 手动重试。队列 worker 必须隔离单个 task 的异常,不能因为某个 app-server、judge 异常或 judge 判定 `fail` 让后续 queued 任务停止;`fail` 只把当前任务标为 failed,随后必须继续扫描并推进下一个 queued/retry_wait 任务。当存在 queued/retry_wait 且 worker 空闲时,watchdog 必须自动重新调度。
- 稳定性与重启恢复:Codex Queue 的第一目标是长期稳定可用;部署修复或运维排障时不得因为担心容器重启会打断任务而拒绝重启、重建或替换 `codex-queue-backend`。容器重启、服务进程重启和镜像替换后,队列、`promptHistory`、running/judging/retry_wait 任务和 active session 元数据必须从 PostgreSQL 恢复,并在已有 `codexThreadId` 可用时用 `thread/resume` 和 continuation prompt 无缝继续当前任务;如果原 app-server turn 已丢失,也必须把当前任务恢复到可 retry/continue 的状态,不能错误推进下一个任务或永久卡住。主 server 侧重建必须走 `server rebuild codex-queue`,该 job 受 `.state/locks/server-compose.lock` 串行化约束,并且必须在 build 后执行 no-deps force-recreate 与 post-up health validation;禁止在 job 中先手工 `docker rm` 再依赖后续命令补救,因为中断窗口会让容器消失并触发 frontend `direct microservice proxy failed`。重启后出现 active task 丢失、手动 steer/interrupt 记录丢失、running 任务卡死、误判完成、跳过当前任务、容器消失或阻塞队列,均属于 Codex Queue 的 P0 核心缺陷,必须先修复并补充 restart-recovery 验收,不能把“避免重启”作为交付策略。
- 完成判定:app-server `turn/completed``turn.status=completed|interrupted|failed` 只代表 Codex turn 已结束;即使 `completed` 也必须把原始任务、assistant 最终回复、command/file-change 事件、stderr tail 和 recent events 组成 execution record 交给 judge 判断是否真的完成。配置了 `UNIDESK_CODEX_QUEUE_MINIMAX_API_KEY` 且 MiniMax 可用时,MiniMax `MiniMax-M2.7``complete|retry|fail` 的判定是权威结果;任何非 LLM 判断,包括字符串匹配、正则、硬编码 safety override,都不得覆盖、降级或提升一次成功的 MiniMax 判定。非 LLM/fallback 判断只允许在 MiniMax 未配置、额度/限流/网络/超时不可用,或 JSON 去噪与 repair 全部耗尽后启用。MiniMax 返回必须先做 JSON 去噪,支持去除 Markdown fence、`json` 标签和从夹杂文本中提取平衡 JSON object;如果去噪后仍无法解析,服务必须把解析错误和上一轮去噪前原始回答反馈给 MiniMax 做 JSON repair 重试,重试次数由 `UNIDESK_CODEX_QUEUE_MINIMAX_JUDGE_REPAIR_ATTEMPTS` 控制,默认 `2`,耗尽后才进入 fallback,并在 fallback 原因中保留 MiniMax 失败信息。
- Retry/推进语义:`retry` 不是新开一个独立任务或完全新 session;只要已有 `codexThreadId`,服务必须 `thread/resume` 原 thread 并 append 一个继续执行 prompt。continuation/judge feedback prompt 只应携带本轮缺口、恢复原因、验收要求和有界原始任务摘要,禁止重新注入完整引用上下文、历史 transcript 或长 JSON;服务重启恢复类 feedback 尤其必须保持短 prompt,依赖现有 thread 上文继续。只有 judge 判定 `complete` 后,队列 worker 才把当前任务标为成功并推进下一个 queued/retry_wait 任务。非 LLM/fallback 判定产生的 `retry` 最多累计 `3` 次;达到上限后当前任务必须转为 `failed` 并记录原因,worker 继续推进后续 queued/retry_wait 任务,避免 fallback safety override 或硬编码判断造成无限循环。
- Judge 探针:`GET|POST /api/judge/probe` 使用同一套 judge 逻辑跑内置 synthetic execution records,覆盖正常完成、正常结束但只给计划、未上线/未部署的服务或 WebUI 改动、传输中断和用户打断等样本,返回 `hits``total``hitRate`、每例 `expected``decision`;该接口不得回显 MiniMax API key。
- 模型选择:默认 Codex 模型是 `gpt-5.5`,内置模型队列包含 `gpt-5.5``gpt-5.4-mini``gpt-5.4``gpt-5.5` 的默认 reasoning effort 必须是 `xhigh`,可通过 `CODEX_QUEUE_MODEL_REASONING_EFFORTS` 追加或覆盖模型级默认值;每个入队任务可通过前端模型下拉菜单或 API 覆盖 `model``cwd``reasoningEffort``maxAttempts``maxAttempts` 上限为 `99`。Judge 判定 `retry` 或非用户取消类 `fail` 时必须继续已有 `codexThreadId`,不能新建 session;重试间隔使用指数退避,从 `1s` 开始,最大 `10min`。429、Too Many Requests、exceeded retry limit、overloaded、stream disconnected 等服务/限流错误一律判定为 `retry`,不能当作完成。
- 状态与日志:默认工作目录为容器内 `/root/unidesk`,该路径映射主 server 的 `~/unidesk`;同时保留 `/workspace` 映射以兼容历史任务。Codex Queue 的任务、queue、`readAt`/未读状态、attempt、judge、`promptHistory`、active session 元数据、控制状态和 ClaudeQQ 通知 outbox 一律以主 PostgreSQL 为权威,分别写入 `unidesk_codex_queue_tasks``unidesk_codex_queue_queues``unidesk_codex_queue_notifications``DATABASE_URL` 是必需配置,服务不得在 PostgreSQL 缺失或不可用时进入文件存储模式。`.state/codex-queue/state.json` 不再作为任务或 queue 状态存储,不得重新引入本地 JSON fallback;服务启动必须以 PostgreSQL 为唯一来源恢复队列,并把 running/judging 任务恢复为 retry_wait。WebUI 不得用 browser `localStorage``sessionStorage` 或 IndexedDB 持久化 task/queue/readAt/unread 等业务状态;浏览器只能保留临时 UI 内存缓存,刷新后必须重新从后端读取 PostgreSQL 权威数据。Codex CLI-like output/Trace 的完整记录可以使用 append-only 文件作为日志型归档,但任务状态、未读状态和列表摘要不得依赖这些文件作为权威来源;`/api/tasks/<id>/transcript``/api/tasks/<id>/output` 必须能分页重建完整历史,不得因为热状态裁剪而丢失早期 trace。热 task JSON 只保留可配置窗口(默认 600 条 output、400 条 event)以保证 `/health``/api/tasks` 和 PostgreSQL flush 不被长任务拖死。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue,并支持把已创建且非 active 的任务移动到其他 queuequeue 内串行,queue 间并行。Codex Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,保证队列任务能直接执行公网 frontend Playwright 回归,不得只在宿主机临时安装。日志写入 UniDesk `logs/{YYYYMMDD}/..._codex-queue.jsonl``/logs` 端点返回最近结构化日志。`/health``queue.storage.primary` 必须恒为 `postgres`,并通过 `queue.storage.postgresReady``queue.devReady``/api/dev-ready` 暴露 PostgreSQL 可用性、develop-ready 自检、必需工具、Docker socket、`docker compose`、默认工作目录、Codex config 状态和 `/root/.ssh` 共享 SSH key 状态。Codex CLI-like 输出可能很大,服务必须节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致 `/health` 和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程。
- ClaudeQQ 通知:Codex Queue 可通过 backend-core 的 `claudeqq` 用户服务代理调用 `POST /api/push/text`,在每个任务进入 `succeeded``failed``canceled` 终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入 `0 running / 0 queued` 空闲态时,必须单独发送一次空闲提醒。通知由 `CODEX_QUEUE_NOTIFY_CLAUDEQQ_ENABLED` 控制,目标由 `CODEX_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group``CODEX_QUEUE_NOTIFY_CLAUDEQQ_USER_ID``CODEX_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID` 配置,默认私聊 `645275593`;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由 `CODEX_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL``CODEX_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS``CODEX_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS``CODEX_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS` 配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表 `unidesk_codex_queue_notifications` 再异步发送;不得使用 `.state/codex-queue/claudeqq-notifications.json``CODEX_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH` 或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按 `CODEX_QUEUE_NOTIFY_CLAUDEQQ_RETRY_INTERVAL_MS` 指数退避重试并跨进程/容器重启保留。`/health``queue.notifications.claudeqq` 必须暴露非敏感配置、目标配置状态和 PostgreSQL outbox 统计;`GET /api/notifications/claudeqq` 返回 outbox 明细,`POST /api/notifications/claudeqq/drain` 手动触发发送,`POST /api/notifications/claudeqq/backfill` 可按 `since` 补入某次故障窗口内已终态任务,确保 QQ/NapCat 超时或离线不会让任务完成通知永久丢失。
- 代理路径:只允许 `/health``/logs``/api/` 前缀;允许方法为 `GET``HEAD``POST``DELETE`。Codex Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。
- UniDesk 前端:`用户服务 / Codex Queue` React 页面负责展示队列卡片、任务 ID、复制任务 ID、引用按钮、任务耗时、默认模型、模型下拉、显式入队份数、引用任务 ID、清空输入、创建成功提示、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;整个 agent loop 消息流统一命名为专有名词 `Trace``Trace` 包含 assistant message、user prompt、system event 和 tool callCodex Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;点击队列卡片引用按钮必须自动把该任务 ID 写入提交表单的引用任务 ID 输入框;引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示,让 Codex 读取初始 prompt、最后消息和工具摘要后继续;连续执行同一 prompt 应使用 `入队份数` 一次性生成多条队列任务,而不是依赖快速连点按钮;原始任务 JSON 只能通过显式 `查看原始JSON` 打开。
## D601 User Services
当前 `D601` 同时承载以下 UniDesk 用户服务:
- `findjob`FindJob 纯后端服务,UniDesk frontend 渲染岗位指标、岗位预览和草稿报告。
- `pipeline`Pipeline v2 控制与观测服务,UniDesk frontend 渲染组件矩阵、React Flow 控制图、epoch 甘特图、运行材料索引和 node 精细控制面板。
- `met-nonlinear`MET Nonlinear 训练编排服务,UniDesk frontend 渲染 GPU/镜像、训练队列、Project config 预览、训练进度、ETA 和历史记录。
- `claudeqq`ClaudeQQ 纯后端 QQ 消息网关,UniDesk frontend 渲染 NapCat 连接、事件订阅、消息推送、最近 QQ 事件和发送记录。
### FindJob On D601
当前 FindJob 作为 `id=findjob` 的用户服务登记在 `config.json`
- Provider`D601`
- 开发工作树:`/home/ubuntu/findjob`,开发和调试必须通过 UniDesk SSH 透传进入 D601。
- 代码引用:`https://gitee.com/Lyon1998/findjob` 与配置中的 `repository.commitId`
- 部署引用:业务仓库自身 `Dockerfile``docker-compose.yml``composeService=server``containerName=findjob-server`
- 节点后端:D601 上 `127.0.0.1:3254`provider-gateway 容器内通过 `http://host.docker.internal:3254` 访问。
- 代理路径:只允许 `/api/` 前缀;`/` 上的业务旧前端即使仍存在,也不作为 UniDesk 用户服务入口使用。
- UniDesk 前端:`用户服务 / FindJob` React 页面负责展示指标、岗位预览、草稿报告和原始 JSON 显式查看按钮。
FindJob 在 UniDesk 语境中按纯后端服务管理:默认页面不得 iframe 或跳转到 findjob 自身前端,也不得直接暴露 D601 的 `3254` 到公网。UniDesk frontend 只能通过 `/api/microservices/findjob/health``/api/microservices/findjob/proxy/api/...` 访问 FindJob 后端。
### Pipeline On D601
当前 Pipeline v2 作为 `id=pipeline` 的用户服务登记在 `config.json`
- Provider`D601`
- 开发工作树:`/home/ubuntu/pipeline`,开发和调试必须通过 UniDesk SSH 透传进入 D601。
- 代码引用:`https://github.com/pikasTech/pipeline` 与配置中的 `repository.commitId`
- 部署引用:业务仓库自身 `Dockerfile``docker-compose.yml``composeService=pipeline-control``containerName=pipeline-v2-control`
- 节点后端:D601 上 `127.0.0.1:18082`provider-gateway 容器内通过 `http://host.docker.internal:18082` 访问。
- 代理路径:只允许 `/health``/api/` 前缀;允许方法为 `GET``HEAD``POST`,其中 `POST` 仅用于 `/api/node-control/...` 这类 node 控制动作;Pipeline 自身 WebUI 前端已废弃,UniDesk 只访问 Pipeline control backend。
- UniDesk 前端:`用户服务 / Pipeline` React 页面负责展示 health、组件数量、React Flow pipeline 控制图框图、epoch 列表、epoch 甘特图、OA/procedure 结构化摘要、运行材料索引、点击 node 后的执行过程抓取、append prompt、guide 和 redo/restart 控件,以及显式原始 JSON 按钮。
Pipeline 在 UniDesk 语境中按控制与观测后端服务管理:默认页面不得 iframe 或跳转到 Pipeline 自身 WebUI,也不得直接暴露 D601 的 `18082` 到公网。UniDesk frontend 只能通过 `/api/microservices/pipeline/health``/api/microservices/pipeline/proxy/api/snapshot?...``/api/microservices/pipeline/proxy/api/node-control/...` 访问 Pipeline control backend;超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:<limit>,runs:<limit>` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 `.state`、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由 OA 事件流驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑,权威规则见 `docs/reference/pipeline-oa-event-flow.md`
Pipeline 的一个 epoch 是同一个 pipeline 从入口到终态完整执行一遍,UniDesk 前端把同一 `pipelineId` 下的多个 run 作为多个 epoch 管理。甘特图必须从 Pipeline snapshot 中的 `startedAt``finishedAt``durationMs` 和 procedure run 摘要生成,不得按某个 pipeline 实例或 node 名称硬编码布局。甘特图纵轴按时间从上到下排列,左侧固定时间列,后续每列对应一个 node;当前可见时间窗口内没有工作区间的 node 列应自动隐藏。默认页面不得展示裸 JSON、JSONL、worker log 或 control event 行;运行材料和 node 过程只能作为一组一行的结构化索引展示,完整原始内容只能在操作员点击 `查看原始JSON` 后显示。
### MET Nonlinear On D601
当前 MET Nonlinear 作为 `id=met-nonlinear` 的用户服务登记在 `config.json`
- Provider`D601`
- 开发工作树:`/home/ubuntu/met_nonlinear`,后端、Dockerfile、训练队列和训练容器都必须通过 UniDesk SSH 透传在 D601 开发调试;主 server 本地只允许开发 UniDesk frontend 与代理登记。
- 数据挂载:D601 的 `/mnt/f/BaiduSyncdisk/data` 挂载为训练容器内 `/data/data`,并设置 `MET_DATA_BASE=/data`,让旧配置中的 `data/M50` 解析为 `/data/data/M50`WSL 本机不安装 TensorFlow 训练环境。
- 代码引用:`https://github.com/pikasTech/met_nonlinear` 与配置中的 `repository.commitId`
- 部署引用:业务仓库内 `docker-compose.unidesk.yml``docker/unidesk/Dockerfile.server``docker/unidesk/Dockerfile.ml``composeService=met-nonlinear-ts``containerName=met-nonlinear-ts`
- 节点后端:D601 上 `127.0.0.1:3288`provider-gateway 容器内通过 `http://host.docker.internal:3288` 访问。
- 代理路径:只允许 `/health``/api/` 前缀;允许 `GET``HEAD``POST``PUT`,用于读取队列/历史、从已有 Project fork 新 Project、保存队列设置、加入待启动队列和启动队列。
- UniDesk 前端:`用户服务 / MET Nonlinear` React 页面采用类似下载器的工作台交互,负责从项目库选择已有 Project、fork 新 Project、加入待启动队列、启动队列、调整最大并发、分标签展示当前队列/已完成/失败诊断/GPU 与镜像,并展示训练进度、ETA、训练速度 `epoch/h`、历史训练记录和显式原始 JSON 按钮。项目库必须按 `projects/``ex_projects/` 的真实目录层级渲染文件树,文件夹计数等于子树 Project 数;项目库和任务列表行都必须可点击打开结构化详情,详情以控件展示 `config.json``data/` 中的训练状态、模型参数量、模型层和指标,不默认展示裸 JSON。
MET Nonlinear 的长期服务边界写在业务仓库 `~/met_nonlinear/docs/reference/unidesk_microservice.md``met-nonlinear-ts` 是长驻 Bun TypeScript 编排后端,`met-nonlinear-ml:tf26` 是按需训练镜像,每个训练任务用一个 `docker run --rm` 容器执行 `python cli.py -t <projectPath>`,训练完成后容器自动销毁。训练镜像 Dockerfile 必须使用中国大陆可达的软件源;当前固定使用 Huawei Cloud mirror 的 `nvidia/cuda:11.2.2-cudnn8-runtime-ubuntu20.04`、Aliyun apt mirror、Tsinghua PyPI mirror、Ubuntu Python 3.8 和 `tensorflow==2.6.0`,避免官方 TensorFlow 2.6 GPU 镜像 Python 3.6 与业务源码类型注解不兼容。
MET Nonlinear 验收必须通过公网 UniDesk frontend 的交互式 UI 完成:选择已有 source Project,设置训练轮数和最大并发,使用 `Fork Project` 创建新的 `projects/unidesk_forks/` Project,确认新 Project 只是被选中而不会直接训练,再加入待启动队列并点击 `启动队列`。验收时必须确认项目库的 `projects/``ex_projects/` 按文件树层级展开、文件夹 Project 计数与后端返回数量一致;点击项目行后详情显示 `config.json``data/` 训练状态、模型参数量和指标;待启动、排队中、训练中、已完成和失败诊断分标签可见;训练队列和已完成行显示 `epoch/h` 训练速度且可点击打开任务详情。最大并发必须按 UI 设置生效,运行中行显示训练进度和 ETA,目标 GPU 为 2080Ti2080Ti 显存余量低于 20% 时自动限制并发,并确认训练容器结束后不残留。批量规模由 UI 输入框决定,完整验收可以通过输入 `Fork 数量=10``训练轮数=200``最大并发=3` 执行,但不得把该规模做成专用硬编码按钮。CLI `/api/queue/server-test` 仅保留为后端兼容入口,不作为 frontend 操作入口。
### ClaudeQQ On D601
当前 ClaudeQQ 作为 `id=claudeqq` 的用户服务登记在 `config.json`
- Provider`D601`
- 开发工作树:`/home/ubuntu/.agents/skills/claudeqq`,后端、Dockerfile、订阅分发和 NapCat 连接调试必须通过 UniDesk SSH 透传在 D601 完成;主 server 本地只允许开发 UniDesk frontend 与代理登记。
- 代码引用:`https://gitee.com/lyon1998/agent_skills` 与配置中的 `repository.commitId`,实际服务目录为仓库内 `claudeqq/`
- 部署引用:业务目录内 `Dockerfile``docker-compose.unidesk.yml`Compose service 为 `claudeqq``napcat`,容器名分别为 `claudeqq-backend``claudeqq-napcat`
- 节点后端:D601 上 `127.0.0.1:3290`provider-gateway 容器内通过 `http://host.docker.internal:3290` 访问。
- 代理路径:只允许 `/health``/logs``/api/` 前缀;允许方法为 `GET``HEAD``POST``DELETE`
- 服务模式:ClaudeQQ 在 UniDesk 中按纯后端运行,默认 `CLAUDEQQ_AUTO_REPLY=false`,只负责 NapCat HTTP/WS 连接、QQ 事件入站记录、HTTP webhook 订阅投递和 `/api/push/text` 消息推送,不把 ClaudeQQ 自身旧 WebUI 作为用户入口。NapCat 必须随同 `docker-compose.unidesk.yml` 容器化部署,D601 只绑定 `127.0.0.1:3000``127.0.0.1:3001``127.0.0.1:6099`ClaudeQQ 容器通过 Compose 内网 `napcat:3000/3001` 访问。
- NapCat 登录 API`GET /api/napcat/login``GET /api/napcat/status` 返回容器化状态、HTTP/WS 连通性、登录状态和二维码 data URL;`GET /api/napcat/qrcode` 只返回二维码。二维码来源为共享挂载中的 `/napcat/cache/qrcode.png`,由 ClaudeQQ 后端转为 JSON data URL 后经 UniDesk 同源代理给前端展示。
- 订阅 API`GET /api/events/recent` 返回最近 QQ 事件,`GET|POST /api/events/subscriptions` 管理 webhook 订阅,`DELETE /api/events/subscriptions/{id}` 删除订阅;订阅回调使用 HTTP POST JSON,并在配置 secret 时携带 `x-claudeqq-signature` HMAC-SHA256。
- 推送 API`POST /api/push/text` 接受 `userId``groupId``message`,由 ClaudeQQ 通过 NapCat HTTP API 发送 QQ 消息;NapCat 不可用时必须快速返回 `status=napcat_offline` 和具体连接错误;当前人工推送验收只允许发给主用户私聊账号 `645275593`,其他用户服务和 main server 应通过 UniDesk 用户服务代理调用,不得直连 D601 公网端口。
- UniDesk 前端:`用户服务 / ClaudeQQ` React 页面负责展示 D601 仓库引用、私有后端映射、NapCat 容器登录二维码、NapCat HTTP/WS 状态、事件缓存、订阅表、订阅创建表单、消息推送表单、主用户私聊账号 `645275593` 标记、最近 QQ 事件和已发送记录;完整原始 JSON 只能通过显式 `查看原始JSON` 打开。
ClaudeQQ 在 UniDesk 语境中按消息网关后端服务管理:不得直接暴露 D601 的 `3290``3000``3001``6099` 到公网,不得 iframe ClaudeQQ 旧 WebUI。浏览器只能通过 UniDesk frontend 的 `/api/microservices/claudeqq/health``/api/microservices/claudeqq/proxy/...` 同源代理访问。
## CLI
- `bun scripts/cli.ts microservice list`:列出全部用户服务、provider 映射、仓库引用、后端映射和运行态容器摘要。
- `bun scripts/cli.ts microservice status findjob`:查看单个用户服务的配置与运行态。
- `bun scripts/cli.ts microservice health findjob`:通过 backend-core -> provider-gateway -> D601 本机后端链路探测 FindJob `/api/health`
- `bun scripts/cli.ts microservice proxy findjob /api/summary`:通过同一私有代理读取业务 API,适合人工验证,不用于公开业务端口。
- `bun scripts/cli.ts microservice health pipeline`:通过 backend-core -> provider-gateway -> D601 本机后端链路探测 Pipeline `/health`
- `bun scripts/cli.ts microservice proxy pipeline '/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3'`:读取 Pipeline snapshot 的有界预览,适合人工验证,不用于公开业务端口;验证甘特图时应确认 run 和 procedureRun 摘要包含 `startedAt``finishedAt``durationMs`;若 body 仍超过 CLI 阈值,默认只输出 `bodyPreview`,需要完整 body 时显式追加 `--raw`
- Pipeline node 控制写入由 UniDesk frontend 调用同源 `/api/microservices/pipeline/proxy/api/node-control/...` 完成;通用 CLI `microservice proxy` 仍主要作为读取验证入口,不作为人工批量写入工具。
- `bun scripts/cli.ts microservice health met-nonlinear`:通过 backend-core -> provider-gateway -> D601 本机 TS 编排后端链路探测 MET Nonlinear `/health`
- `bun scripts/cli.ts microservice proxy met-nonlinear /api/queue``bun scripts/cli.ts microservice proxy met-nonlinear /api/images`:读取 MET Nonlinear 队列、GPU 策略和训练镜像状态,适合人工验证,不用于公开业务端口。
- `bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects?root=projects&limit=500'``bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects/config?path=projects/<name>' --raw`:验证项目库文件树输入和结构化项目详情;详情应包含 config、progress、data、model、metrics 字段,供前端渲染训练状态、模型参数量和指标。
- `bun scripts/cli.ts microservice health claudeqq``bun scripts/cli.ts microservice proxy claudeqq /api/napcat/login``bun scripts/cli.ts microservice proxy claudeqq /api/events/recent``bun scripts/cli.ts microservice proxy claudeqq /api/events/subscriptions`:验证 ClaudeQQ 后端、NapCat 容器登录、事件订阅和私有代理链路;消息推送使用 `POST /api/push/text`,不得开放 D601 `3290/3000/3001/6099` 公网端口。
- `bun scripts/cli.ts microservice health todo-note``bun scripts/cli.ts microservice proxy todo-note /api/instances`:验证主 server Todo Note 后端、PostgreSQL 存储和本机 provider-gateway 私有代理链路。
- `bun scripts/cli.ts microservice health codex-queue``bun scripts/cli.ts microservice proxy codex-queue /api/tasks`:验证主 server Codex Queue 后端、PostgreSQL 强制持久化和本机 provider-gateway 私有代理链路;写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQLfrontend 不得用本地存储伪造成功状态。
- `bun scripts/cli.ts --main-server-ip 74.48.78.17 microservice health findjob`:在计算节点或其他非主 server 主机上通过公网 frontend remote CLI 进行同一验证,不需要主 server SSH key。
`debug dispatch D601 microservice.http --payload-json ...` 仅用于开发调试 provider-gateway 代理能力;正式验收和用户入口应优先使用 `microservice` 命令与 frontend 用户服务页面。
## Frontend Rules
用户服务前端必须整合到 `src/components/frontend/src/` 下的 TypeScript + React 模块中。`app.tsx` 只做 shell/router 和导入分发,业务页面必须拆成独立 TSX,例如 `todo-note.tsx``findjob.tsx``pipeline.tsx``met-nonlinear.tsx``codex-queue.tsx`。默认展示必须是业务控件:指标卡、状态徽标、表格、草稿卡片、运行卡片、树形任务、表单控件、结构化材料索引、链接和字段摘要;只有操作员点击 `查看原始JSON` 时才允许打开原始 JSON 弹窗。日志、JSONL 和大块 JSON 不得在主界面按行展示,避免把裸数据伪装成 UI。
对于超大业务 JSONbackend-core 可把 `__unideskArrayLimit=<path>:<limit>` 作为 frontend-only 代理参数传给 provider-gateway,由 provider-gateway 在返回前裁剪指定 JSON 数组并写入 `_unidesk.arrayLimits` 元数据。该参数只用于控制 UniDesk 展示预览,不能替代业务后端自身分页 API 的长期设计。CLI 的 `microservice proxy` 还会对超过默认阈值的 body 做二次有界预览,防止人工验证时输出爆炸;只有显式 `--raw` 才允许倾倒完整 body。
## Verification
用户服务交付必须同时通过后端、CLI 和公网 frontend 验证:
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `findjob``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:3254` 映射和 `findjob-server` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `pipeline``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:18082` 映射和 `pipeline-v2-control` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `met-nonlinear``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:3288` 映射和 `met-nonlinear-ts` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `claudeqq``providerId=D601``public=false``frontendOnly=true`、仓库 URL、commit id、`127.0.0.1:3290` 映射和 `claudeqq-backend` 容器摘要可见。
- 在主 server 运行 `bun scripts/cli.ts microservice list`,确认 `codex-queue``providerId=main-server``public=false``frontendOnly=true`、UniDesk 仓库 URL、`codex-queue:4222` 映射和 `codex-queue-backend` 容器摘要可见。
- 运行 `bun scripts/cli.ts microservice health findjob``bun scripts/cli.ts microservice proxy findjob /api/summary`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 FindJob 后端。
- 运行 `bun scripts/cli.ts microservice health pipeline``bun scripts/cli.ts microservice proxy pipeline '/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3'`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 Pipeline 后端,且 run/procedure 摘要包含甘特图所需时间字段。
- 运行 `bun scripts/cli.ts microservice health met-nonlinear``bun scripts/cli.ts microservice proxy met-nonlinear /api/queue``bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects?root=projects&limit=20'``bun scripts/cli.ts microservice proxy met-nonlinear /api/images`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 MET Nonlinear TS 后端。
- 运行 `bun scripts/cli.ts microservice health claudeqq``bun scripts/cli.ts microservice proxy claudeqq /api/napcat/login``bun scripts/cli.ts microservice proxy claudeqq /api/events/recent``bun scripts/cli.ts microservice proxy claudeqq /api/events/subscriptions`,确认真实链路经过 backend-core、WebSocket、D601 provider-gateway 和 D601 本机 ClaudeQQ 后端;在 D601 上 `curl http://127.0.0.1:3290/health` 应显示 `service=claudeqq``pureBackend=true``napcat.containerized=true`、NapCat HTTP/WS 状态、二维码状态和订阅计数。
- 运行 `bun scripts/cli.ts microservice health todo-note``bun scripts/cli.ts microservice proxy todo-note /api/instances`,确认真实链路经过 backend-core、WebSocket、main-server provider-gateway 和主 server `todo-note-backend` 后端;输出中必须包含五个迁移清单和 PostgreSQL 存储健康状态。
- 运行 `bun scripts/cli.ts microservice health codex-queue``bun scripts/cli.ts microservice proxy codex-queue /api/tasks`,确认真实链路经过 backend-core、WebSocket、main-server provider-gateway 和主 server `codex-queue-backend` 后端,并且 `/health``queue.storage.primary=postgres``queue.storage.postgresReady=true`,不得出现 file fallback`queue.notifications.claudeqq.outbox.storage=postgres` 且暴露 pending/failed/sent 统计。再通过公网 frontend 提交一个 `gpt-5.5` 小任务,确认队列串行推进、输出实时更新、结束后有 judge 判定,且运行中可追加 prompt 或打断。Codex Queue 的重启恢复必须作为验收项:运行中任务存在时重启或重建 `codex-queue-backend` 后,任务必须从 PostgreSQL 恢复到可继续执行状态,不能丢失 active task、`promptHistory`、后续 queued 任务、readAt/未读状态或已入 outbox 的 ClaudeQQ 通知;ClaudeQQ/NapCat 离线期间结束的任务必须能在 `/api/notifications/claudeqq` 中看到 pending/failed,并在登录恢复后通过 `POST /api/notifications/claudeqq/drain` 发送。批量验收必须通过公网 frontend 设置 `入队份数=5` 或使用多段 prompt 分隔,一次性入队 5 条任务,并确认 5 条任务按顺序进入 running/judging/succeeded,而不是只运行第一条。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试业务仓库和容器,确认 `curl http://127.0.0.1:3254/api/health` 可用;不要把调试服务部署到主 server。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试业务仓库和容器,确认 `curl http://127.0.0.1:18082/health``curl http://127.0.0.1:18082/api/snapshot` 可用;不要把 Pipeline 调试服务部署到主 server。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试 `~/met_nonlinear`,确认 `curl http://127.0.0.1:3288/health` 可用;最终验收必须回到公网 UniDesk frontend,通过项目库选择、Fork、加入待启动队列和启动队列完成,不要把 MET Nonlinear 后端、Docker build 或训练任务部署到主 server。
- 在 D601 上用 `bun scripts/cli.ts ssh D601 ...` 调试 `~/.agents/skills/claudeqq`,确认 `docker compose -f docker-compose.unidesk.yml up -d --build claudeqq``claudeqq-backend``claudeqq-napcat` 都运行,`curl http://127.0.0.1:3290/health``curl http://127.0.0.1:3290/api/napcat/login` 可用;不要把 ClaudeQQ 后端或 NapCat 调试服务部署到主 server。
- 运行 `bun scripts/cli.ts e2e run`,确认用户服务相关检查 passed,并确认 Playwright 访问的是公网 `http://74.48.78.17:18081/`
- 登录公网 frontend,进入 `用户服务 / 服务目录``用户服务 / Todo Note``用户服务 / FindJob``用户服务 / Pipeline``用户服务 / MET Nonlinear``用户服务 / ClaudeQQ`,确认能看到主 server 与 D601 provider、仓库引用、后端私有映射、Todo Note 迁移清单与树形任务、FindJob 指标和岗位预览、Pipeline 组件矩阵、React Flow 控制图、epoch 列表、epoch 甘特图和运行材料索引、MET Nonlinear 队列/GPU/镜像/Project config/训练历史、ClaudeQQ NapCat 容器登录二维码/NapCat 状态/事件订阅/消息推送/最近 QQ 事件;Todo Note 页面必须能创建临时清单、添加任务并删除临时清单,删除前必须按唯一临时清单名称重新选中对应行,禁止用未确认的当前 active 清单执行删除,FindJob 页面必须显示真实数字指标、`HEALTH OK` 和非空岗位预览,Pipeline 页面必须显示 `Pipeline v2 工作台``Health OK`、组件数、epoch 甘特图和结构化运行材料索引,MET Nonlinear 页面必须显示 `Health OK``Fork Project``启动队列``当前队列`、最大并发设置和 GPU/镜像面板,ClaudeQQ 页面必须显示 `Health OK``NapCat 容器登录``QQ 事件订阅``消息推送``事件缓存` 和私有代理说明,不能只停留在 loading 骨架;页面默认不得出现裸 JSON、JSONL 或逐行日志。