- add Codex Queue microservice/frontend integration and related deployment docs - document 100% Pipeline OA event-flow requirements and E2E gates - harden Pipeline frontend Gantt/timeline E2E assertions and rendering
24 KiB
UniDesk Frontend Reference
UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效果。设计目标是高信息密度、低装饰、低字号、低间距,并让调度、节点、事件和配置入口在单屏内快速切换。
Source Contract
frontend 应用源码必须使用 TypeScript + React,禁止在 src/components/frontend 中维护手写 .js / .jsx 应用源码。浏览器请求的 /app.js 只能由 frontend Bun server 从 src/components/frontend/src/app.tsx 及其 TSX imports 转译生成;public/ 目录只保存 HTML/CSS 等静态资产,不提交手写 app.js。
src/components/frontend/src/app.tsx 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。业务 microservice 前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 app.tsx。当前长期固定入口为:todo-note.tsx 承载 Todo Note 工作台,findjob.tsx 承载 FindJob 工作台,pipeline.tsx 承载 Pipeline 工作台,met-nonlinear.tsx 承载 MET Nonlinear 训练编排工作台,codex-queue.tsx 承载 Codex Queue 控制台;新增业务 microservice 也必须按同样规则新增独立页面模块,并由 app.tsx 只做导入和路由分发。
Layout
左侧边栏只切换主模块:运行总览、资源节点、任务调度、微服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,微服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、Codex Queue 只属于微服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
Route Model
frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL 路由,而不是在 router 中手工为每个页面逐个挂路径。长期规则如下:
- 导航权威数据只有一份:主模块、子标签、显示文案、route segment、默认子标签都从同一个 TypeScript 导航定义派生,左侧边栏、顶部标签、浏览器地址栏解析和前进/后退都复用同一套 registry。
- Canonical route 必须按主模块前缀分组:运行总览使用
/ops/<tab-segment>/,资源节点使用/nodes/<tab-segment>/,任务调度使用/tasks/<tab-segment>/,系统配置使用/config/<tab-segment>/;只有微服务主模块使用/app/<tab-segment>/。例如态势总览固定为/ops/status/,Docker 状态固定为/nodes/docker/,Pipeline固定为/app/pipeline/。 - 当 future 需要新增主模块时,通用机制必须允许为该模块声明自己的顶层前缀,而不是继续把所有页面都塞进
/app/*。 - 主模块根路径如
/ops/、/nodes/、/tasks/、/app/、/config/只作为默认子标签或最近活动子标签的入口别名,实际当前页面仍应落到某个具体子标签;浏览器地址栏不能停留在“只有主模块,没有具体页面”的模糊状态。 - route segment 生成顺序固定为:显式
routeSegment> ASCII-safeid> 由 label 派生的 Unicode-safe slug > 稳定 hash fallback。这样新增 Unicode 标签时默认仍可得到稳定路径,而不要求每个标签单独写一段路由代码。 - 浏览器直开、刷新、
history.back()/history.forward()、点击总览 drilldown 卡片、点击左侧边栏、点击顶部标签都必须走同一个路由状态机;不得出现“页面内容切换了,但 URL 没变”或“URL 变了,但 shell 仍停在旧 tab”的分裂状态。 - frontend Bun server 必须把这些模块前缀下的深链接路由作为 SPA 入口返回同一个
index.html;实现上允许统一把非静态资源路径都回到同一个 shell,但判定标准是公网直开/ops/status/、/nodes/docker/、/app/pipeline/等深链接时都不得 404。
Overview Task Drilldown
态势总览 中的 待处理任务 指标必须可点击进入任务调度的 待处理任务 子标签,展示具体 queued、dispatched、running 任务的状态、Provider、已等待时间、payload 摘要和显式 查看原始JSON 操作。总览不得只给出无法追溯的数字;当后台把超时未终态任务转为 failed 后,待处理指标应回落,历史记录仍可在任务历史和执行结果中查看。核心指标还必须展示 PGDATA,显示 PostgreSQL 当前数据库用量、命名卷 unidesk_pgdata_10gb 和配置容量,便于从总览判断数据库状态。
Task History Diagnostics
任务调度 / 任务历史 必须把任务生命周期渲染为可诊断表格,不得只显示更新时间和原始 payload 摘要。每行至少展示状态、任务命令和 id、Provider、任务耗时、载荷摘要、诊断信息、更新时间和显式 查看原始JSON 操作;终态任务的耗时按 updatedAt - createdAt 计算,待处理任务按当前时间减 createdAt 计算。耗时必须保留毫秒到秒的精度,小于 1 秒的任务显示小数秒或 <0.01s,不得把真实的亚秒级任务四舍五入或向下取整成 0s。失败任务必须在默认视图中提取 result.error、result.message、result.stderr、result.reason 或等价字段作为失败原因,并将 exit code、timeout、previous status 等关键诊断字段渲染为控件;完整 result 只能通过 查看原始JSON 展开。
Resource Node Monitor View
资源节点模块必须提供 资源监控 子标签,用类似 Windows 任务管理器的性能页展示每个 provider 节点的 CPU、内存和硬盘用量历史曲线。该页面应包含节点切换、当前用量摘要、CPU/Memory/Disk 三条曲线、进程资源占用表、采样说明和 Provider Gateway 升级 控制区;曲线和进程表数据来自 backend-core 的 /api/nodes/system-status,不得在页面默认展示原始 JSON。内存曲线必须使用实际内存口径,不把 Linux page cache / buffer 计入占用。进程资源占用表必须用 React 表格控件展示进程名、命令摘要、PID、用户、状态、CPU、内存、RSS、磁盘 I/O、线程和运行时长,并支持按列排序;默认排序必须是内存 RSS 降序,表头要能明确显示当前排序方向。完整进程快照只能通过 查看原始JSON 显式查看。
Resource Node Docker View
资源节点模块必须提供 Docker 状态 子标签,用类似 Docker Desktop 的结构展示每个 provider 节点的 Docker daemon 状态。该页面应包含节点切换、daemon 摘要、Containers/Images/Volumes/Networks 指标、容器表格、镜像/卷/网络侧栏,并通过状态徽标区分 running、paused、exited 等状态。数据库命名卷 unidesk_pgdata_10gb 的检测和数据库命名卷卡片只对 main-server Provider 有效;D518、D601 等计算节点不要求也不应提示缺少 pgdata 数据库卷。main-server 的 Volumes 区域和数据库命名卷卡片必须显式展示 unidesk_pgdata_10gb,不得因为列表截断或匿名卷排序被隐藏。
Provider Gateway Version View
资源节点模块必须提供 网关版本 子标签,按每个 Provider 展示 provider-gateway 版本号、升级策略、启动时间、能力摘要、SSH 透传可用性、远程更新可用性、最近远程更新状态和远程更新记录。SSH 透传可用性必须由 unideskCapabilities 是否包含 host.ssh、hostSshConfigured、hostSshKeyPresent 和 hostSshTarget 渲染为结构化徽标;远程更新可用性必须由 unideskCapabilities 是否包含 provider.upgrade 与 providerGatewayUpgradePolicy: "always-enabled" 渲染为结构化徽标。远程更新记录的数据源是 provider.upgrade 任务历史,默认必须渲染为结构化表格字段:状态、模式、任务 id、来源、耗时、策略、结果摘要和更新时间;亚秒级升级耗时必须显示小数秒,不得显示成 0s;不得把升级 plan、task result 或服务日志作为裸 JSON 直接铺在页面上。最近远程更新 应优先选择最新 mode: "schedule" 的真实升级记录,避免后续预检 plan 覆盖真正的升级结果;完整升级任务 JSON 只能通过对应行的 查看原始JSON 按钮显式打开。
Provider Operation Availability
资源节点 / 节点清单、资源节点 / 网关版本 和总览中的 Provider 卡片必须显示每个计算节点的运维可用性,不允许只在原始 labels JSON 中隐藏。SSH 透传 徽标至少区分可用、未配置、缺 key、未声明能力;远程更新 徽标至少区分可用、策略待确认、未声明能力。可用性判断只来自 provider-gateway 注册/心跳 labels 和能力声明,不用前端猜测,也不得把缺失能力默认显示为成功。
Provider Gateway Upgrade Control
资源监控 子标签中的升级控制区通过 backend-core /api/dispatch 下发 provider.upgrade 任务。默认 预检升级 只生成升级计划并回传任务结果;执行升级 才允许调度节点本地 updater 容器执行 Compose 重建。前端只展示结构化任务状态、task id、摘要和当前节点的远程更新记录,完整升级计划必须通过 查看原始JSON 显式查看。
Microservice Frontend
微服务主模块用于展示挂载在计算节点或主 server Docker 中的业务后端。服务目录必须显示 service id、Provider、仓库 URL、commit id、业务 Dockerfile/docker-compose 引用、节点后端私有映射、SSH 透传开发入口和运行态容器摘要。Todo Note子标签必须把主 servertodo-note-backend后端渲染为 UniDesk React 控件,包括迁移清单、树形任务、筛选、提醒、拖放/移动、撤销/重做、字号控制和显式原始 JSON 按钮。FindJob子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。Codex Queue子标签必须把主 servercodex-queue-backend后端渲染为 UniDesk React 控件,包括串行队列、任务提交/批量提交、默认模型gpt-5.4-mini、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮。- 业务 microservice 页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把 microservice 后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。
Pipeline子标签是 D601/home/ubuntu/pipeline的 UniDesk host UI。- Pipeline 仓库自带 WebUI 前端已经废弃;UniDesk frontend 是唯一用户可见的 Pipeline UI。
- Pipeline microservice 只提供 backend/control API,UniDesk 通过
/api/microservices/pipeline/proxy/...拉取 snapshot、Gantt DTO、node detail 和控制接口。 - Pipeline 控制与观测的最终权威是 100% OA 事件流;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,完整规则见
docs/reference/pipeline-oa-event-flow.md。 - 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。
- Pipeline scorer 结果必须结构化展示为
x/N、通过率、scorer 状态、item pass/fail badge 和 raw inspector 入口;主界面不得裸展示 scorer JSON。 - 用户点击控制图中的 node 后,必须通过同源 microservice 代理抓取该 node 执行过程,并支持向运行中 node 追加 prompt、给下次尝试下发 guide、对已完成 node 排队 modify、提交 monitor 审核 approve,以及排队 restart/redo。
- append-prompt、guide、modify、redo/restart 统称为 node 的管理行为;approve 是 monitor 审核决策;fetch/status 只属于观察行为。
- UniDesk 只负责同源代理、结构化展示和人工控制入口,不直接写 Pipeline
.state文件。 - node 精细控制面板的 append、guide、modify、approve、redo/restart 必须调用 Pipeline 后端 OA control API;
node-control可作为 HTTP 路由名保留,但内部必须写入 OA 控制事件,并把 UniDesk frontend 发起者记录为结构化事件;历史兼容字段可继续使用sourceKind=webui表示前端来源。
Pipelineepoch 和甘特图规则。- 一个 epoch 定义为同一个 pipeline 从入口到终态完整执行一遍。
- 同一
pipelineId下的多个 run 必须作为多个 epoch 展示,并允许操作员切换当前 epoch;控制图、node 控制面板、运行材料索引和甘特图都必须跟随当前 epoch。 - epoch 下拉框必须通过同源 snapshot 轮询自动看到新 run,不能依赖浏览器强制刷新,但也不应在用户已选中旧 epoch 时自动跳走。
- 甘特图默认时间尺度固定为
100 px/min,并且默认关闭自动隐藏空闲列,让操作员首次进入页面时先看到完整 node 列。 - epoch 甘特图的纵轴是时间,左侧第一列展示时间刻度,后续每列对应一个 node;node 在
startedAt到finishedAt或当前时间之间处于工作态时绘制竖向工作条,空闲时间留空。 - 甘特图 node 列顺序必须遵循 pipeline 拓扑,从上游到下游依次向右展开;不得把下游 node 排在上游左侧。
- 甘特图必须提供时间尺度滑块,用同一份时间数据调整每分钟像素密度:全局尺度压缩纵向高度以查看完整 epoch,细节尺度拉长纵向高度以查看短时间内的 prompt 点、控制点和执行线。
- 甘特图必须提供当前 epoch 的渲染图导出按钮;导出图应使用当前可见布局,执行区间先绘制,控制/观察箭头覆盖在执行区间之上,真实事件点覆盖在最上层,避免箭头被执行条遮住。
- 精确数学图形的坐标权威规则。
- 凡是精确时序图、甘特图或其他包含严格数学映射关系的渲染,坐标权威必须在后端完成。
- Pipeline
GET /api/node-control/runs/{runId}?view=gantt&scale=0..100应返回可直接展示的layout.chartHeight、时间刻度ticks[].y、执行区间startY/endY/y1/y2/height、事件点y/timeAxisY、控制箭头sourceY/targetY/y1/y2和对齐诊断。 - UniDesk 前端在该 DTO 存在时不得独立把时间戳重新换算为 y 轴坐标,只能按后端坐标纯展示。
- 时间尺度滑块变化必须重新请求后端布局。
- 甘特图必须根据当前可见的后端 y 区间自动隐藏该窗口内没有任何工作区间或事件点的 node 列,避免宽图把无关空闲 node 挤在屏幕中。
Pipeline甘特图事件来源。- 甘特图上的执行线、prompt 点、控制点和 monitor 虚线箭头必须通过同源
node-controlHTTP 读取接口驱动。 - run 级图形数据使用
GET /api/node-control/runs/{runId}?view=gantt&scale=0..100。 - node 级 OpenCode 明细来自用户选择后的
GET /api/node-control/runs/{runId}/nodes/{nodeId},避免跨 UniDesk provider HTTP 代理时一次性拉取全 run 的大体积 step 数组。 - 这些 monitor/control 事件的权威来源是 Pipeline OA 事件流;Pipeline 后端会为每条事件自动附加当前
epoch:{runId}与pipeline:{pipelineId}scope tag,并在 monitor 订阅时自动追加同一组 scope 限制,避免不同 epoch 之间串流。UniDesk 只消费 Pipeline 暴露的结构化读接口,不直接依赖旧的 monitor append 文件语义;OA 事件流的完整约束见docs/reference/pipeline-oa-event-flow.md。 - prompt 点来源于 attempt 级
controlEventRecords中的 prompt-delivered 事件,必要时回退到controlPromptRecords/monitorPromptRecords。 - Monitor prompt 点必须将
node-finished、node-long-running-observation作为一等结构化事件展示;node-finished是中性完成事实,不得携带是否审核的权威策略字段。是否需要审核由 OA backend 根据当前 epoch 的 Pipeline config/topology 判定;审核开启时同一个 node completion 在 UI 中只保留一条完成/等待审核点,不得再生成独立legacy node audit request event或legacy monitor audit request event点。 - 控制点和 monitor/人工控制箭头来源于 run 级
controlEvents,必要时回退到controlCommands。 - 控制事件必须依赖后端记录的
sourceKind、sourceNodeId、targetNodeId、commandId、eventId、resetNodeIds和interruptedProcedureRunIds等结构化字段识别 monitor、UniDesk frontend、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。
- 甘特图上的执行线、prompt 点、控制点和 monitor 虚线箭头必须通过同源
Pipelinemonitor 审核与详情展示。- Pipeline 页面必须提供结构化
OA 事件流诊断面板,通过/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics展示 100% 事件流模式、禁止残留数、no-audit 证据、monitor 审核证据、控制 queued/applied 计数和近期 run 证据;原始 diagnostics 只能通过查看原始JSON打开。 - Pipeline 后端可通过
control.monitoring.audit.enabled=true开启 monitor 审核门禁,但是否审核必须由 OA backend 加载 config snapshot 后判断,不能写入node-finished事实事件。 - 当前 pipeline 存在 monitor node 时,node 成功后必须先进入 OA 事件流;OA 判定需要审核后才向 monitor 推送 prompt,随后必须等待 monitor 通过 OA 控制事件发起
approve、modify、redo/restart或等价管理行为。 - 长时间处于
running的 node 必须触发node-long-running-observation观察事件,默认节奏为 2、5、10、20 分钟,之后每 20 分钟继续发送一次;前端需要把这类观察事件和普通node-finished清晰区分。 - 前端应把审核/控制事件渲染为结构化点和虚线箭头;有 monitor 的 pipeline 中,monitor node 必须固定在甘特图第 1 个 node 列,其余 node 再按上游到下游顺序从左到右排列。
node-long-running-observation必须显示从被观察 node 指向 monitor node 的观察连线,不能只在 monitor 列留下孤立事件点;观察来源本身不是一个真实行为,不能在被观察 node 上额外绘制“观察来源”点,点只代表真实收到 prompt、发出管理行为、发出审核结果等事件。- 甘特图箭头的末端必须在目标点外回缩约一个箭头长度,箭头尖不能插入 prompt 点、控制点或 monitor 观察点内部,避免把“连线”和“真实行为点”混在一起。
- 仍处于
running的 node 必须显示从实际开始时间延伸到当前时间的实时执行条,并用明确的闪动/扫描效果标识“仍在执行”;不得把 running node 渲染成只有起始点或 1s 极短条线。 - 点击甘特图中的执行线、prompt 点或控制点后,右侧边栏必须展示结构化事件字段、匹配的 procedure/attempt、以及对应 OpenCode step 的摘要与展开详情,而不是在主界面直接铺 raw JSON、JSONL、worker log 或 control event 文本。
- OpenCode step 展示必须参考
~/.agents/skills/agent-sessions/scripts/webui/src/components/MessageList.tsx的信息组织方式:按消息流时间线展示角色、模型、tokens、创建/完成时间,正文摘要直接可读,思考和工具调用分区折叠,工具调用要显示工具名、状态、输入字段、输出摘要和元数据。 - 右侧边栏中的 OpenCode Step Timeline 必须把公共 session 信息(agent、model、session id)聚合到 session 头部,不得在每个 step 重复;单个 step 的默认摘要只保留时间、消息、工具调用三类信息,统计信息和 tag 必须折叠到展开层。
- 右侧边栏排版必须优先保护横向可读宽度:时间放在 step 顶部 header,而不是单独占用左侧窄列;默认摘要不得引入右侧边栏内部横向滚动条,也不得因为窄列挤压把 step 高度拉得过高。
- OpenCode Step Timeline 不能使用跨越所有 step 的连续装饰线;相邻 step 之间若存在真实时间空闲区间,例如上一个 step
completedAt到下一个 stepcreatedAt,该区间必须视觉留白,不能被误渲染为持续执行条线。 - 调整任何高信息密度右侧边栏布局时,都必须把
总高度与横向滚动条作为显式验收指标,用 Playwright 打开真实页面验证,而不是只看静态代码或本地想象。 - 运行材料只能作为结构化索引行展示计数、状态、时间和来源摘要,完整 JSON、JSONL 或 log tail 只能通过显式
查看原始JSON按钮打开。
- Pipeline 页面必须提供结构化
Pipeline渲染与算法验证。- 涉及 monitor 审核、管理行为或甘特图算法的改动,必须用 Pipeline 侧通用 fixture 组合验证。
- 验证组合至少包括拓扑 split/merge/feedback、生产型 PikaPython workflow、
monitor-management-behavior-test和monitor-audit-behavior-test。 - 所有 fixture 都应走同一渲染与事件解析路径;不得为了某个 pipeline id、node id 或 run id 在 UniDesk 前端加入特例。
MET Nonlinear子标签必须把 D601/home/ubuntu/met_nonlinear的训练编排后端渲染为下载器式工作台。- 工作台包括项目库选择、从已有 Project fork 新 Project、加入待启动队列、启动队列、最大并发设置、当前队列、已完成、失败诊断、GPU/镜像、训练进度、ETA、历史记录和显式原始 JSON 按钮。
- 运行中训练若后端未直接给出 ETA,前端必须用
startedAt、当前 epoch 和目标 epoch 做可解释的剩余时间估算。 - 训练队列和已完成列表必须显示训练速度
epoch/h。 - 项目库必须按真实文件路径分层显示
projects/和ex_projects/,文件夹计数必须等于其子树中的 Project 数,不能用模型名、状态或其他派生字段替代文件树层级。 - 项目库、当前队列、已完成和失败诊断中的行必须可点击打开结构化详情。
- 详情必须把
config.json、data/training_state.json、data/training_info.json、data/metrics.json、data/model_info.json和data/compute_analysis.json中的训练状态、模型参数量、模型层、指标和 data 文件清单渲染为字段卡、表格和 chip,不得默认显示裸 JSON。 - 不得提供硬编码的固定数量/固定轮数测试按钮。
Component Data Rendering
前端必须把 backend-core 返回的 JSON 渲染为合适的控件:状态徽标、指标卡、表格列、标签 chip、字段摘要、任务结果卡、日志行和表单控件。默认页面禁止暴露裸 JSON、pre JSON 或整段 JSON.stringify 文本;只有用户明确点击 查看原始JSON 按钮后,才允许在弹窗或高级编辑区展示原始 JSON。
Login
frontend 提供账号密码登录,默认账号为 admin,默认密码为 Liang6516.。登录会话使用 frontend 容器签发的 HttpOnly Cookie;浏览器后续只访问同源 frontend API,frontend 再通过 Docker 内网代理 backend-core。
Visual Language
界面使用深钢蓝、炭黑、琥珀和冷青作为工业控制台色板;字体选择窄体和等宽组合,以减少横向浪费。字号、表格行高和面板间距保持克制,避免大标题和松散卡片造成信息密度下降。
Data Flow
浏览器不直接访问 backend-core。frontend 容器服务 React 静态资产、登录接口、会话接口和同源 API 代理;代理目标是 Docker 内网 http://backend-core:8080。provider-gateway 的外部接入地址只作为连接拓扑信息展示,不作为浏览器数据源。