102 KiB
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 frontend;frontend 通过同源
/api/microservices/*代理到 backend-core。deployment.mode=unidesk-direct的用户服务由 backend-core 通过目标 provider-gateway 的microservice.http能力访问计算节点本机后端;deployment.mode=k3sctl-managed的用户服务只允许经k3sctl-adapter微服务进入 k3s 标准服务路由,backend-core 不得直接向业务容器所在 provider 下发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这类节点本地地址,或明确登记为同一私有 Docker network 内的服务名;主 server 内置用户服务可使用同一 Compose 网络内的显式服务名,例如todo-note:4211。k3s 代管服务不得把业务容器地址登记成 provider-gateway 直连目标,backend.proxyMode必须使用k3sctl-adapter-http,backend.nodeBaseUrl可使用k3s://<service>这类逻辑服务名。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 代理的映射。deployment.mode,用于明确部署责任边界;unidesk-direct表示 UniDesk 直接登记和探测目标 provider 上的容器,k3sctl-managed表示 UniDesk 只登记逻辑服务并经deployment.adapterServiceId指向的k3sctl-adapter访问,代管条目还必须写明k3sServiceId、namespace、expectedNodeIds和当前activeNodeId。development.providerId、development.sshPassthrough=true和development.worktreePath,用于说明开发调试入口必须在计算节点上通过 UniDesk SSH 透传完成。frontend.route和frontend.integrated=true,用于说明该业务前端已经整合到 UniDesk React 控制台,而不是继续公开业务自身前端。
Runtime Configuration And Persistence Contract
每个长期运行的用户服务都必须把“登记配置、容器恢复、状态持久化、真实 health”作为同一交付项处理,不能只把容器拉起后登记到 config.json:
- 配置来源:
config.json只登记 UniDesk 代理与前端入口,业务运行配置必须保存在目标节点的业务仓库、.state/、Docker env-file、Windows 用户目录或主 PostgreSQL 中;不得把 token、登录态、运行日志或节点私钥提交到 UniDesk 仓库。 - 容器恢复:长驻业务后端必须使用业务仓库自己的 Compose 或显式
docker run固化容器名、镜像、端口、环境变量、healthcheck、restartpolicy 和持久化挂载;repository.composeService、repository.containerName与实际 Docker 容器必须一致,便于microservice list/status、Docker 状态页和自动恢复脚本互相校验。 - 重启策略:provider-gateway 必须使用
restart: always;普通用户服务至少使用restart: unless-stopped,如果服务是节点核心依赖或无人值守入口,可以提升为always,但必须说明手动停止后的恢复语义。仅靠 restart policy 只能覆盖 Docker daemon 已重新启动后的容器恢复,不能替代 Windows 登录任务、systemd、Docker Desktop 自启动或 WSL keepalive。 - 持久化边界:任务、队列、账号会话、订阅、token、未读状态和业务数据不得只存在容器 writable layer 或浏览器本地存储;应写入主 PostgreSQL、业务数据库、Docker named volume 或节点 host bind mount。
.state/默认只能放可重建缓存、日志、归档和服务自有小状态;如果某服务把.state/作为权威状态目录,必须在本节或服务小节明确字段、备份和重启恢复方式。 - 自恢复入口:计算节点上的服务应有节点本地的幂等 autorecover 脚本,在 Docker ready 后用
docker inspect、退出码和业务 HTTP readiness 判断是否需要docker compose -f <compose> up -d --force-recreate <service...>;该脚本必须从节点本地计划任务、systemd、Docker Desktop 自启动后的 WSL keepalive 或等价守护触发,不依赖正在被恢复的 UniDesk provider-gateway SSH 会话。 - 真实 health:
/health必须证明业务“可用”而不是“进程存在”。需要登录、外部连接、GPU、镜像、数据库或后台 worker 的服务,必须在 health body 中暴露这些依赖状态;依赖未满足时必须返回非 healthy 或ok=false,不能让/ops/status/把不可发消息、不可训练或不可访问的服务计为可用。 - 验收记录:新增或迁移用户服务时,交付说明必须记录 Compose/docker run 路径、restart policy、端口绑定、持久化挂载、关键环境变量来源、health readiness 字段、容器重启或 Docker daemon 重启后的恢复验证,以及公网 UniDesk frontend 或
microservice health/proxy的真实链路结果。
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 NoteReact 页面负责展示清单列表、树形任务、筛选、提醒、拖放/上移下移、撤销/重做、字号控制和显式原始 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 且不会污染长期数据。
OA Event Flow On Main Server
当前 OA Event Flow 作为 id=oa-event-flow 的用户服务登记在 config.json:
- Provider:
main-server,由 backend-core 直接访问同一 Compose 网络内的http://oa-event-flow:4255,公网不发布4255。 - 代码引用:
https://github.com/pikasTech/unidesk与配置中的repository.commitId;服务源码位于src/components/microservices/oa-event-flow,属于 UniDesk 自有主 server 用户服务。 - 部署引用:UniDesk 根仓库
docker-compose.yml中的oa-event-flowservice,Dockerfile 为src/components/microservices/oa-event-flow/Dockerfile,容器名为oa-event-flow-backend。 - 数据库:事件表、projection offset、Trace/STEP stats 和 Trace step 投影写入主 PostgreSQL;服务启动时自动创建/补齐 schema,不依赖仅首次生效的 database init SQL。
- API:
GET /health;POST /api/events;GET /api/events;GET /api/events/stream;GET /api/stats/trace;GET /api/diagnostics。 - 代理路径:只允许
/health、/logs和/api/前缀;允许方法为GET、HEAD、POST。 - UniDesk 前端:
用户服务 / OA Event FlowReact 页面负责展示服务健康、事件表、tag 过滤、live stream 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮。
OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得暴露公网端口,不得把事件或统计权威状态写入 .state/;Code Queue 与 Pipeline 都必须通过该服务发布事实事件、订阅 tag stream 和读取统计中心。共享事件流、统计中心和完成门禁见 docs/reference/oa-event-flow.md。
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-managerservice,Dockerfile 为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 ManagerReact 页面负责展示主 server 仓库引用、私有后端映射、项目指标、项目表格、筛选搜索、编辑表单、Excel 导入和 Excel 导出;完整原始 JSON 只能通过显式查看原始JSON打开。
Project Manager 在 UniDesk 语境中按纯后端服务管理:不得将 4233 映射为公网端口。浏览器只能通过 UniDesk frontend 的 /api/microservices/project-manager/health 和 /api/microservices/project-manager/proxy/... 同源代理访问项目管理后端。
Baidu Netdisk On Main Server
当前 Baidu Netdisk 作为 id=baidu-netdisk 的用户服务登记在 config.json:
- Provider:
main-server,由 backend-core 直接访问同一 Compose 网络内的http://baidu-netdisk:4244,公网不发布4244。 - 代码引用:
https://github.com/pikasTech/unidesk与配置中的repository.commitId;服务源码位于src/components/microservices/baidu-netdisk,属于 UniDesk 自有主 server 用户服务。 - 部署引用:UniDesk 根仓库
docker-compose.yml中的baidu-netdiskservice,Dockerfile 为src/components/microservices/baidu-netdisk/Dockerfile,容器名为baidu-netdisk-backend。 - 配置密钥:Compose 只透传
UNIDESK_BAIDU_NETDISK_CLIENT_ID、UNIDESK_BAIDU_NETDISK_CLIENT_SECRET、UNIDESK_BAIDU_NETDISK_TOKEN_KEY与可选UNIDESK_BAIDU_NETDISK_APP_ROOT;当前默认工作根目录为/,如需收回到应用目录可显式设为/apps/<name>;不得把百度 AppSecret、token key、access token 或 refresh token 写入仓库文件。 - 配置步骤:
UNIDESK_BAIDU_NETDISK_TOKEN_KEY可由本机生成;百度client_id和client_secret必须由账号拥有者在百度网盘开放平台创建应用后提供,操作清单见docs/issue/baidu-netdisk-env-setup.md。 - 数据库:OAuth 设备码会话、账号摘要、加密 token、传输任务和事件写入主 PostgreSQL 表
baidu_netdisk_*;服务启动时自动创建/补齐 schema,不依赖仅首次生效的 database init SQL。 - 文件边界:v1 只支持容器 staging 目录
/data/staging与百度网盘配置工作根之间的后台上传/下载任务;staging 目录由主 server.state/baidu-netdisk/staging挂载,.state/只保存可重建文件缓存,不作为 token 或任务权威状态。当前授权账号已实测可对百度网盘根目录/执行列表、上传、获取 dlink、下载和删除临时探针,因此UNIDESK_BAIDU_NETDISK_APP_ROOT默认直接设为/;仅当该值配置为/apps/...时,后端才会确保应用目录存在,目录已存在时必须返回/记录errno=-8并继续,禁止使用会重命名的策略重复创建_YYYYMMDD_...目录。 - API:
GET /health;GET /api/auth/status;POST /api/auth/device/start;GET /api/auth/device/status;POST /api/auth/refresh;POST /api/auth/logout;GET /api/account;GET /api/files;GET /api/files/meta;POST /api/folders;POST /api/files/manage;POST /api/transfers/upload-from-path;POST /api/transfers/download-to-path;POST /api/self-test;GET /api/transfers;GET|POST /api/transfers/{id}/cancel|retry;GET /logs。 - 授权轮询:百度设备码轮询返回的
authorization_pending和slow_down是正常中间态,后端必须把它们更新为 pending session(slow_down增加轮询间隔)而不是向前端抛 HTTP 错误;只有拒绝、过期或未知 OAuth 错误才进入 rejected/expired/failed。 - 自测:
POST /api/self-test会在 staging 生成小文本、上传到配置工作根、通过/api/files找到fs_id、下载回 staging 并校验 MD5;该端点不得回显 token/dlink,适合 CLI、前端按钮和交付验收使用。 - 代理路径:只允许
/health、/logs和/api/前缀;允许方法为GET、HEAD、POST、DELETE。 - UniDesk 前端:
用户服务 / Baidu NetdiskReact 页面负责展示设备码登录卡、账号容量、配置工作根文件表、staging 上传/下载任务、上传/下载自测按钮与结果、脱敏日志和显式原始 JSON 按钮。
Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度 token、dlink 或 staging 文件字节流给浏览器;浏览器只能通过 UniDesk frontend 的 /api/microservices/baidu-netdisk/health 和 /api/microservices/baidu-netdisk/proxy/... 同源代理访问控制面 JSON。
File Browser Host Files
当前 File Browser 作为两组用户服务登记在 config.json,共用上游 https://github.com/filebrowser/filebrowser 的 filebrowser/filebrowser:v2.63.3 镜像和 commit ca5e249e3c0c94159c2136a0cd431a424eb18472;主 server 不再运行 File Browser 容器,避免占用主 server CPU/内存和主机根目录遍历资源:
id=filebrowser:Provider 为D518,服务在 D518 节点本机绑定4251,provider-gateway 容器内通过http://host.docker.internal:4251访问;容器名为unidesk-filebrowser-d518,挂载 D518 WSL host/到/srv,因此可浏览/mnt/c等 Windows 盘符。D518 Docker Desktop 的host.docker.internal指向 Windows host IP,实际部署需使用0.0.0.0:4251->8080才能让 provider-gateway 容器访问。id=filebrowser-d601:Provider 为D601,服务在 D601 节点本机绑定127.0.0.1:4251,provider-gateway 容器内通过http://host.docker.internal:4251访问;容器名为unidesk-filebrowser-d601,挂载 D601 WSL host/到/srv,因此可浏览/mnt/c等 Windows 盘符。- 启动参数必须包含
--baseURL /api/microservices/<id>/proxy、--noauth、--disableExec、--disableTypeDetectionByHeader、--disableImageResolutionCalc和--disableThumbnails;容器必须使用user: "0:0"或--user 0:0,否则 upstream 镜像默认非 root 用户会导致/database/filebrowser.db写入失败。禁用头部类型探测、图片尺寸计算和缩略图可避免 Windows 盘根目录中的hiberfil.sys、pagefile.sys等受保护文件导致整个目录浏览失败。 - 代理路径允许
/,允许方法为GET、HEAD、POST、PUT、PATCH和DELETE;File Browser 的/api/login、/api/resources和上传 API 需要透传X-Auth、Range、Tus-Resumable等请求头。 - provider-gateway 容器必须配置
extra_hosts: ["host.docker.internal:host-gateway"],确保 D601/D518 Linux Docker 容器内能访问 WSL host 侧127.0.0.1:4251映射。 - UniDesk 前端:
用户服务 / File BrowserReact 页面展示 D518 主目标和 D601 备用目标的健康状态、仓库引用、私有后端映射,并以 iframe 嵌入对应 File Browser WebUI;页面必须提供截图导出入口,并对上游 WebUI 注入紧凑布局样式,避免 material icon 字体异常时folder文本遮挡文件名;File Browser 自身端口不得直接暴露公网。
k3s Control Plane Adapter On D601
k3sctl-adapter 是 UniDesk 直管的控制面适配微服务;它本身仍按 deployment.mode=unidesk-direct 登记在 D601,由 UniDesk 负责健康检查、重建和前端可见性,但它管理的业务服务必须走 k3s 标准服务路由,不得再被 backend-core 直接下发到业务容器所在 provider。
- Provider:
D601,由 D601 provider-gateway 仅维护和访问k3sctl-adapter的本机私有端口127.0.0.1:4266;provider-gateway 不再作为code-queue业务请求的直接代理。 - 代码引用:
https://github.com/pikasTech/unidesk与配置中的repository.commitId;服务源码位于src/components/microservices/k3sctl-adapter,属于 UniDesk 自有控制面组件。 - 部署引用:UniDesk 仓库中的
src/components/microservices/k3sctl-adapter/docker-compose.d601.yml,Dockerfile 为src/components/microservices/k3sctl-adapter/Dockerfile,容器名为k3sctl-adapter;当前 D601 Docker Desktop 包装必须挂载宿主 kubeconfig 与 WSL 维护 SSH key,并由容器入口脚本建立到 WSL host 原生 k3s API 的 SSH local tunnel。 - 控制桥边界:
k3sctl-adapter是 UniDesk 到 k3s 的控制桥,不是被 k3s 管理的业务 workload;它必须保持deployment.mode=unidesk-direct,或迁移为等价的 UniDesk/systemd 直管宿主服务,不得改成k3sctl-managedDeployment。原因是 UniDesk 依赖 adapter 做 k3s 服务代理、部署验证和故障诊断;如果 adapter 自身依赖 k3s Deployment、Service、CNI 或 kube-proxy 才能存活,k3s 网络故障时会失去修复 k3s 的入口,形成依赖顺序倒置。 - 原生 API 连接:D601 原生 k3s 的 kubeconfig 固定来自宿主
/etc/rancher/k3s/k3s.yaml,adapter 内部挂载为/var/lib/unidesk/k3s/kubeconfig;当 kubeconfig server 是127.0.0.1:6443时,adapter 容器必须通过受控 SSH local tunnel 把容器内127.0.0.1:6443转发到 WSL host127.0.0.1:6443,并设置K3SCTL_KUBE_API_CONNECT_HOST=127.0.0.1。不得依赖 Docker Desktop 的network_mode: host,因为它进入的是 Docker Desktop VM 网络而不是 D601 WSL Ubuntu 网络;也不得依赖host.docker.internal:6443、旧rancher/k3s容器 IP、NodePort 或手工 service endpoint。 - k3s 实现:D601 控制面、D518 或其他计算资源节点上的 k3s agent/worker 都必须原生安装在节点 host OS 或 WSL 发行版内,以
/usr/local/bin/k3s和 systemdk3s.service/k3s-agent.service运行;不得用 Docker、Compose、rancher/k3s长驻容器、kind/k3d 或其他容器化方式承载 k3s 控制面或 kubelet。Docker 只允许用于 provider-gateway、业务容器镜像构建、运行用户 workload 或临时提取 k3s 二进制/镜像 artifact,不能成为 k3s runtime 边界。验收时必须证明systemctl is-active k3s或 agent 服务正常、kubectl get nodes -o wide看到真实节点 OS/内核、k3s containerd socket 位于/run/k3s/containerd/containerd.sock,且不存在 activerancher/k3s控制面容器。 - k3s 路由对象:
k3sctl-managed可以落到 k3s、k8s 或等价标准 Kubernetes 控制面,但必须使用 Kubernetes 原生命名空间、Deployment、Service、readiness/liveness probe、Kubernetes API service proxy 等规范对象;不得把裸容器端口、NodePort、SSH curl、provider-gatewaymicroservice.http或 host 直连地址伪装成 k3s 服务路由。WSL 节点的 hostPath 和 local-path 语义必须解析到 WSL host 文件系统;例如 D601 Code Queue Pod 的/workspace必须映射 WSL/home/ubuntu,不能映射到容器化 k3s 内部的/home/ubuntu。 - k3s 系统组件:D601 原生 k3s server 必须禁用非必要的
traefik、servicelb和metrics-server,只保留业务必需的 API server、CoreDNS 与 local-path provisioner;CoreDNS 和 local-path provisioner 固定运行在 D601 控制面节点,避免 D518 维护隧道限制导致系统 DNS/readiness 抖动。 - manifest:代管服务声明放在
src/components/microservices/k3sctl-adapter/k3s/*.k3s.json,adapter 启动时通过K3SCTL_MANIFEST_PATHS读取;manifest 是 D601/D518 实例、active instance、single writer、expected nodes 和 health policy 的权威来源。K3SCTL_SERVICES_JSON不得承载 static HTTP 服务、不得覆盖同名服务、不得作为隐藏 fallback;如需追加服务也必须提供完整ManagedKubernetesServicemanifest。 - API:
GET /health只表示 adapter 控制面自身可用,并把代管服务 serving 健康作为managedServicesHealthy字段展示;GET /api/control-plane返回控制面、manifest、kubectl/k3s snapshot 和代管服务状态;GET /api/services返回代管服务列表;GET|HEAD /api/services/<id>/health返回该 k3s 服务的 active serving 健康;/api/services/<id>/proxy/*是业务请求进入 active service 的唯一代理入口。 - 代理路径:adapter 访问 active 业务服务的唯一正式路径是 Kubernetes API service proxy:
/api/v1/namespaces/<namespace>/services/<service>:<port>/proxy/...。D601 与 D518 不要求能彼此直连;D518 通过 k3s agent 加入控制面,控制面连接可以借助节点维护隧道建立,但业务请求不得退化为 provider-gateway 直连 Code Queue HTTP 端口。standby/worker 节点如果受 kubelet/service-proxy 可达性限制,可以在 manifest 中显式使用healthMode=pod-ready作为拓扑健康探针;这只读取 Kubernetes Pod readiness,不是业务代理路径,也不能替代 active Service proxy。 - 拓扑健康:
expectedNodeIds负责展示计划内节点;当前 Code Queue 目标拓扑必须同时包含 D601 和 D518,presentNodeIds应为["D601","D518"]、missingNodeIds=[]、topologyComplete=true、status=healthy。D518 未加入只允许作为迁移中的显式 degraded 状态,不能隐藏为 fallback;只有显式requireAllInstancesHealthy=true的服务才允许把缺失 standby/worker 节点提升为整体不健康。 - 前端:
用户服务 / k3s ControlReact 页面必须只通过/api/microservices/k3sctl-adapter/proxy/api/control-plane通信,展示控制面状态、manifest、D601/D518 实例、active instance、Kubernetes API service proxy/no-fallback 路径和显式原始 JSON 按钮;页面不得直接访问 provider-gateway、D601/D518 业务容器端口、NodePort 或 raw k3s/kubectl API。
Code Queue k3s-Managed
当前 Code Queue 作为 id=code-queue 的 k3sctl-managed 用户服务登记在 config.json,业务实例由 D601 k3s 控制面代管,并接入统一 oa-event-flow 发布 Trace/STEP 事实事件与读取统计中心:
- Orchestrator:
deployment.mode=k3sctl-managed,deployment.adapterServiceId=k3sctl-adapter,deployment.k3sServiceId=code-queue,backend.proxyMode=k3sctl-adapter-http,backend.nodeBaseUrl=k3s://code-queue;backend-core 对 Code Queue 的正式链路只能是frontend -> backend-core -> k3sctl-adapter -> Kubernetes API service proxy -> Kubernetes Service code-queue:4222。 - Direct path ban:
code-queue不得再登记http://code-queue:4222、http://host.docker.internal:4222、NodePort 或 provider-gatewaymicroservice.http作为业务代理目标;frontend 也不得使用旧/api/code-queue-direct兼容别名作为 Code Queue 页面数据源。provider-gateway 只允许用于维护 D601/D518、部署 adapter、部署 k3s/k8s 节点或诊断节点本机容器。 - 实例语义:D601 是默认 active/single-writer 实例,
CODE_QUEUE_INSTANCE_ID=D601且CODE_QUEUE_SCHEDULER_ENABLED=true;D518 是 standby 实例,必须设置CODE_QUEUE_INSTANCE_ID=D518、CODE_QUEUE_SCHEDULER_ENABLED=false和CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED=false,避免两个实例同时消费同一 PostgreSQL 队列或重复回放 OA 统计。D601 active 也默认关闭CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED;历史 OA Trace/STEP 回填必须通过显式/api/oa/backfill运维动作触发,不能在每次 Pod 重启时自动批量发布旧事件。 - 部署引用:Code Queue 镜像仍复用
src/components/microservices/code-queue/Dockerfile,Kubernetes 运行清单为src/components/microservices/k3sctl-adapter/k3s/code-queue.k8s.yaml,config.json对外记录 k3s manifestsrc/components/microservices/k3sctl-adapter/k3s/code-queue.k3s.json;主 server 根目录docker-compose.yml不包含code-queueservice,旧 D601 direct Compose 文件只作为迁移/本地诊断参考,不是正式运行入口。 - 主服务依赖映射:Code Queue 仍以主 PostgreSQL 为权威数据库,但 D601 k3s Pod 不能依赖公网直连
74.48.78.17:15432/4255。Pod 内DATABASE_URL和OA_EVENT_FLOW_BASE_URL必须指向集群内d601-tcp-egress-gatewayService,再由该 gateway 通过 D601 provider-gateway egress proxy 的 HTTP CONNECT 转发到主 PostgreSQL 和 OA Event Flow;新增 TCP 依赖时扩展TCP_EGRESS_ROUTES,不得在业务容器里新增一次性公网直连或 ad hoc 隧道。D601 active 实例的CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL直接使用本机 ClaudeQQ 映射http://host.docker.internal:3290。这些端口映射只服务受控节点运行时,必须用防火墙或等价策略限制来源,不得成为浏览器或任意公网客户端入口。 - K8s 探针与启动维护:Kubernetes liveness/startup probe 必须使用轻量
/live,readiness 和用户服务健康使用/health;/health不得执行全量任务聚合、历史回填或长事务索引维护,历史任务总览应由/api/tasks/overview读取 PostgreSQL。启动时允许后台执行队列元数据 flush、通知 outbox 读取、任务表索引维护和 overview warmup,但这些维护不得阻塞 Bun server、readiness endpoint 或 frontend overview;通知表索引和大批量 OA backfill 不得作为默认启动副作用。 - MiniMax/OpenCode 并发:
minimax-m2.7通过 OpenCode JSON 事件端口运行;每个 Code Queue task 必须使用独立的 OpenCode XDG data/config/cache/state 目录,禁止多队列并发任务共享同一个 OpenCode SQLite/WAL 状态目录,否则并发 smoke 会触发PRAGMA journal_mode = WAL之类的数据库锁或初始化错误。用于验证 k3s/k8s 链路的 MiniMax smoke 以“至少 4 个任务、分布到 2 个 queue、至少 2 个终态成功”为链路验收线;剩余失败如果是 OpenCode 最终回复捕获、业务任务判定或模型限流,应作为 Code Queue 执行可靠性问题单独排查,不能反推 k3s 代理链路失败。 - 默认出网代理:D601 active Code Queue Pod 必须默认把
HTTP_PROXY、HTTPS_PROXY和ALL_PROXY注入给 Codex/OpenCode、git、curl、npm等任务子进程;当前唯一上游是 D601 provider-gateway egress HTTP CONNECT 代理,并通过 KubernetesService d601-provider-egress-proxy暴露给unidesknamespace 内的 Pod。该 Service 通过 selector 指向 D601 上的 hostNetwork 桥接 Pod,桥接 Pod 在集群端监听 service port18789、在宿主侧只连接127.0.0.1:18789的 provider-gateway egress endpoint;不得再用手工 EndpointSlice、provider-gateway Docker bridge IP 或固定172.*地址作为长期拓扑。Pod 内代理 URL 使用http://d601-provider-egress-proxy.unidesk.svc.cluster.local:18789,provider-gateway 宿主端口仍只允许绑定127.0.0.1,不得开放公网;桥接 Pod 或 provider-gateway 重建后必须用 Code Queue/health.egressProxy.connected=true验证。这里的 provider-gateway 只承担出网代理,不承担 Code Queue 业务 HTTP 代理;业务访问仍只能走 Kubernetes API service proxy。k3s/k8s 原生 egress gateway、service mesh 或 CNI egress policy 只作为后续网络层增强方向,当前交付态不引入第二套出网控制面。远程开发/执行容器不得只依赖这些环境变量,必须在容器网络层用 TUN 默认路由和 OUTPUT 防火墙强制外网流量只能经 master TUN 出口。 - 出网代理无 fallback 纪律:Code Queue 的运行时配置只允许一个默认出网路径,即 provider-gateway egress proxy;不得在代码中同时保留 Code Queue 自建 WebSocket proxy、临时 shell proxy、D601 本地直连公网、主 server direct HTTP proxy 等隐式分支。任何新增网络 fallback 都必须先进入本参考文档并配套
/health可见状态,否则视为残留旧路径。 - 上线纪律:Code Queue 相关的前端或后端改进必须在同一任务内正式上线并验证公网 frontend 或 live API,不能只停留在源码、构建产物或“后续再上线”。修改 Code Queue 自身时不得等待当前 Code Queue task 结束、等待 queue idle 或等待
0 running后才重启;D601 active 实例的正式后端部署入口是bun scripts/cli.ts codex deploy <commitId>,它按已 push 的 remote commit 做 build-first 镜像替换、k3s image import、manifest apply、rollout 和健康验证,并用 k3s adapter、Code Queue live API 或公网 frontend 证明任务和队列仍可读可继续。 - 期望状态部署:新的通用入口是
bun scripts/cli.ts deploy apply --service code-queue,它从deploy.json读取 repo 与 commit,再按docs/reference/deploy.md的 target-side build 规范在 D601 构建、导入 k3s、rollout 并验证 live commit。codex deploy <commitId>是兼容入口,后续实现应复用同一个 reconciler,不得维护第二套部署语义。 - 更名与灾备恢复:旧版 Codex 队列服务名只允许作为兼容诊断和一次性迁移来源;
code-queue-backend容器自身/health正常但microservice health code-queue返回 provider 直连错误时,优先判定为 backend-core 仍加载旧MICROSERVICES_JSON或 adapter manifest 未刷新,必须刷新.state/docker-compose.env、重建/替换backend-core与k3sctl-adapter,随后用microservice list验证code-queue的runtime.orchestrator=k3sctl、backend.proxyMode=k3sctl-adapter-http和无业务容器直连摘要。 - Codex 认证:容器必须从 D601 的
/home/ubuntu/.codex/config.toml同步 Codex provider 配置到 D601.state/code-queue/codex-home/config.toml,并只读挂载/home/ubuntu/.codex/auth.json到容器/root/.codex/auth.json后同步到.state/code-queue/codex-home/auth.json,让codex app-server使用与 host 一致的 provider 登录态;同时通过 D601.state/code-queue-d601.env或 k8scode-queue-envsecret 透传OPENAI_API_KEY、CRS_OAI_KEY等 provider 所需变量。这些 provider 环境变量和 auth 文件不得写入仓库,必须由 D601 运行时文件或 k8s secret 注入,确保容器重建和重启后不会丢失认证。新增 provider 的env_key时必须增加同类运行时透传和 Compose/k8s 持久化,禁止把 Codex 或 MiniMax 密钥写入仓库文件。Code Queue 容器必须只读挂载 D601 WSL host 的 SSH 目录到/root/.ssh(默认/home/ubuntu/.ssh),让容器内git push、ssh -T git@github.com与 WSL host 使用同一套 GitHub SSH key/known_hosts;不得把私钥复制进镜像或仓库。 - Develop-ready 镜像:Code Queue 镜像必须在启动前预装 UniDesk/Pipeline 调试所需工具,至少包含
codex、bun、node、npm/npx、git、rg、curl、python3/pip3、docker、docker compose、docker-compose、jq、ssh、rsync、make、gcc/g++、iptables、tar、gzip和unzip;不得依赖 Codex 任务运行时再apt-get install这些基础环境。 - 远程开发容器与任务执行 Provider:Code Queue 必须能通过 live API 拉起 D601 等计算节点上的开发容器,入口为
POST /api/dev-containers/<providerId>/start,默认 Provider 为D601。该流程由 Code Queue 调用 UniDeskssh <providerId>维护桥在目标节点创建unidesk-codex-dev-<providerId>,并在 Code Queue 所在节点与开发容器之间建立ssh -wTUN 点对点链路;服务所在节点负责对开发容器的 TUN 源地址做 NAT/MASQUERADE,开发容器默认路由和 DNS 改走该 TUN,从而让ping google.com、DNS、HTTP(S) 等出网都经主 server 全局代理,而不是依赖 D601 本地网络。提交 Code Queue 任务时必须支持选择执行 Provider:D601在 D601code-queue-backend容器中本机执行,默认工作目录为/workspace,并且/workspace必须映射 D601 WSL host 的/home/ubuntu;同一个 hostPath 还必须挂载到容器内/home/ubuntu,让 WSL home 里的绝对 symlink(例如/workspace/cq-deploy -> /home/ubuntu/unidesk-code-queue-deploy)在任务中可解析,不能只看到 symlink 名而无法进入目标目录。/root/unidesk与/app必须单独映射/home/ubuntu/cq-deploy作为服务部署仓库;其他 Provider 在对应unidesk-codex-dev-<providerId>容器中执行,默认工作目录为/home/ubuntu,可按任务覆盖cwd。远程任务启动前必须自动复用或拉起该 Provider 的开发容器、同步 Codex 配置和允许的运行时 provider 环境变量,并通过同一 master TUN/NAT 链路出网;目标 host 存在/mnt时,开发容器必须挂载 host/mnt:/mnt,确保 D601 这类 WSL 节点的 Windows 盘符路径如/mnt/f/Work/ConStart在任务容器内可见,避免 agent 因缺少真实工作区而搜索到无关项目。TUN 建立必须幂等处理 stale 状态:启动前清理旧tun<id>、默认路由、旧 tunnel SSH 进程和旧 OUTPUT 跳转,缺失旧设备不能导致失败,冷启动运行时准备要有有界但足够的 timeout。TUN 建立后必须创建UD-CQ-EGRESS-<provider>OUTPUT 链,规则只允许 loopback、既有连接、tun<id>出口以及到 master server 的 SSH tunnel 控制连接,随后 reject 其他 IPv4/IPv6 出站包;这条网络层封口是开发/执行容器的权威外网边界,不能用HTTP_PROXY/NO_PROXY环境变量替代,容器镜像也必须使用已解析出的唯一unidesk-code-queue:<provider>或显式image,缺失时直接失败,禁止 provider-gateway image、latest或其他隐式镜像 fallback。验收必须保留三类日志:容器建隧道后ping google.com成功、强制指定原 Docker 网卡直连外网被sealed_direct_ping=blocked_expected拦截、服务所在节点上对应UNIDESK-CODEX-DEV-<providerId>NAT 链或tun<id>计数在 ping 前后增长;涉及 WSL 工作区任务时还必须在开发容器内验证目标/mnt/...路径可读。GET /api/dev-containers/<providerId>/status必须展示默认路由、route_8_8_8_8、egressFirewallChain和 OUTPUT 链跳转。开发容器代理密钥只生成到.state/code-queue/dev-proxy/与目标节点用户目录,不得提交到仓库。 - 远程维护桥调用:Code Queue 已迁移到 D601 后,
code-queue-backend容器内没有主 server 的unidesk-backend-core容器,不能再把bun scripts/cli.ts ssh ...实现为本地docker exec unidesk-backend-core。Code Queue 后端发起的 provider 维护命令必须通过主 server frontend/api/dispatch进入 backend-core,再由目标 provider-gateway 执行host.ssh;需要传递脚本时必须使用 base64 临时文件,超过 Host SSH 单命令长度上限时分块上传到目标/tmp后再执行,避免恢复到本地 Docker broker、交互 stdin 或手工 shell fallback。 - 远程 Provider 准备不得阻塞控制面:Code Queue 在请求处理、队列调度、远程开发容器准备、Host SSH/WSL SSH 透传、Codex/OpenCode 启动和日志导出路径中,禁止使用会长时间占用 Bun event loop 的同步子进程调用,例如针对远程 Provider 的
spawnSync、execSync或execFileSync。远程命令必须通过异步子进程执行,带显式 timeout、超时 kill、stdout/stderr 上限和任务 output 进度记录;远程准备失败只能让对应任务进入失败或 retry,不能让POST /api/tasks、SSE/api/events、/health、overview 或 frontend/core 用户服务代理等控制面请求等待远程 SSH 结束。凡是改动 D601/远程 Provider 准备、api/dev-containers/*、任务入队启动或runCodeQueueSsh等路径,验收必须在一个远程 SSH/status/start 探针运行期间并发验证容器直连/health和/api/tasks/overview仍能在 1s 内返回,证明远程超时不会复发为全站刷新卡死。 - OpenCode 远程执行:
minimax-m2.7走 OpenCode JSON event port 时,本地和远程命令都必须显式执行opencode run ...;远程 Docker exec 不得退化成exec run ...,否则会在目标容器内变成bash: exec: run: not found。OpenCode JSON stream 的终态判定以“当前进程退出码 + 当前 attempt 的最终 assistant response”为准:exit=0且当前 attempt 产生非空最终回复时,即使上游没有发step_finish事件,也应视为正常 terminal;非零退出、无当前最终回复或传输关闭才进入 retry。每个 attempt 的finalResponse必须只来自当前 OpenCode/Codex turn,禁止在当前 turn 未产出最终回复时回退复用 task 上一次finalResponse,否则会把旧任务内容误判为本轮完成。 - 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/steerprompt 必须写入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>。GET|POST /api/tasks/{id}/judge?attempt=N与 CLIbun scripts/cli.ts codex judge <taskId> --attempt N用于单步复现指定 attempt 的 judge,必须复用真实队列 worker 的上下文构建、prompt 压缩、MiniMax 调用、JSON 去噪/repair 和 fallback 路径;dryRun=1/--dry-run只输出 prompt/payload 和重建诊断,不调用 MiniMax。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 必须自动重新调度。 - 稳定性与重启恢复:Code Queue 的第一目标是长期稳定可用;部署修复或运维排障时不得因为担心容器重启会打断任务而拒绝重启、重建或替换 active Pod。容器重启、服务进程重启和镜像替换后,队列、
promptHistory、running/judging/retry_wait 任务和 active session 元数据必须从 PostgreSQL 恢复,并在已有codexThreadId可用时用thread/resume和 continuation prompt 无缝继续当前任务;如果原 app-server turn 已丢失,也必须把当前任务恢复到可 retry/continue 的状态,不能错误推进下一个任务或永久卡住。D601 侧重建必须走bun scripts/cli.ts codex deploy <commitId>;禁止先手工docker rm或只手工docker compose up再依赖后续命令补救,因为中断窗口会让 Pod/容器消失并触发 frontend/core 用户服务代理失败。重启后出现 active task 丢失、手动 steer/interrupt 记录丢失、running 任务卡死、误判完成、跳过当前任务、容器消失或阻塞队列,均属于 Code Queue 的 P0 核心缺陷,必须先修复并补充 restart-recovery 验收,不能把“避免重启”作为交付策略。 - 调度与 active run slot:Code Queue 必须把“queue processor 正在等待/退避/轮询”和“实际占用 Codex/OpenCode 子进程运行槽”分开建模;
CODE_QUEUE_MAX_ACTIVE_QUEUES只限制真实 active run slot,不能把 retry backoff、等待内存下降或等待前序任务的processingQueues计入 active slot,否则设置全局 active slot 上限时,一个空等队列会把其他 runnable queue 永久饿死。多个 queue 同时等待 active slot 时必须显式维护 FIFO waiter 队列,避免某个长 retry/backoff 队列刚释放 slot 就立刻重抢,导致更早进入等待的retry_wait任务长期饥饿;/health必须同时暴露真实activeQueueIds、activeRunSlotCount、等待中的processingQueueIds和 active slot waiters,排障时以 active run slot 与 waiter 顺序判断是否真的有任务在跑、谁应下一个启动。restart-recovery 后的retry_wait任务若缺失codexThreadId/OpenCode session id,不得无限拒绝 retry;必须用紧凑 recovery prompt 和原始任务摘要重新开一个 agent thread/session,让任务继续推进并在 Trace 中留下 recovery 证据。任何修改 scheduler、retry backoff、queue move、manual retry、shutdown recovery 或内存等待逻辑时,都必须保留“空等 processor 不占 active run slot”、“等待者 FIFO 不饥饿”和“缺失 thread/session 可恢复”的自测或 live 验证。 - 内存优化过程与防回归:Code Queue 已迁移到 D601,但内存治理仍必须按“PostgreSQL 权威源优先、进程热状态最小化、容器硬上限兜底”的顺序设计。长期可复用的优化路径是:先确认任务、queue、readAt、promptHistory、active session 和通知 outbox 均可从 PostgreSQL 恢复;再把历史任务列表、详情、统计、Trace/output 和
/health的只读查询改为 PostgreSQL 直读或聚合查询;随后只把queued、running、judging、retry_wait等调度必需任务载入 Bun 堆,并在 PostgreSQL 查询侧裁剪 hotoutput/events;最后用 dirty-only flush、append-only 输出归档、Codex SQLite 小批量导出、bun --smol、mem_limit=600m、memswap_limit=1536m、NODE_OPTIONS=--max-old-space-size=768和 cgroup memory watchdog 作为运行时防线。PostgreSQL 到进程的单次读取足够快,不能为了减少 SQL 查询把全部历史task_json、Trace、output 或统计摘要常驻内存;任何新增缓存都必须有默认较小的环境变量上限、明确淘汰策略、可从 PostgreSQL 或 append-only 归档重建,且不得影响重启恢复。新增或修改/api/tasks、overview、stats、summary、transcript、output、trace、health、flush、scheduler 和通知路径时,禁止在常规请求中调用会物化全量历史任务 JSON 的代码,禁止启动后无条件重写全量历史 task JSON,禁止用未设上限的Map/数组保存历史 output/event/Trace,CODE_QUEUE_MAX_ACTIVE_QUEUES=0表示不按 queue 数量设置全局排队上限;如显式设置为正数,必须同时说明内存预算并补充内存压测验收。memory watchdog 必须以 cgroup working set 为主要判断,且在 swap 仍有余量时不得提前杀掉唯一 active run;否则 TypeScript/Playwright 这类短时高内存验证会被错误中断并让 retry 队列反复震荡。 - 列表/详情延迟优化原则:Code Queue 控制面交互的长期目标是常规历史规模下首屏、
GET /api/tasks/overview、POST /api/tasks/<id>/read和分页加载均在 1s 内完成;性能面板出现十几秒级core_proxy或 Code Queue 用户服务代理慢操作时,必须优先按后端查询形态和前后端通信策略定位,不能把问题归因于 React 渲染后只改 UI。后端优化顺序是:先为 queue、status、updated/created 时间、readAt/terminal unread 和常用筛选条件补齐 PostgreSQL 索引;再用 SQLCOUNT、GROUP BY、条件聚合和分页 ID 查询生成 queue/status/stats/unread 摘要;随后按 ID 轻量加载当前页、selected、active 和 unread priority task,禁止为了列表或已读操作解析完整 Trace、output archive、Codex transcript 或物化全量历史task_json。read/read-all这类 mutation 必须是 SQL-only 更新并返回最小 patch/queue 计数,不能触发 overview 全量重算或重载所有任务;启动 warm 只能预热小体积聚合和索引路径,不得把历史任务作为常驻缓存。允许 frontend/backend 代理使用秒级、严格有界、mutation 自动失效的 overview micro-cache 来吸收重复刷新,但 cache 只能作为抖动保护,不能替代数据库索引、聚合查询和分页披露,也不能让 stale readAt/queue/status 状态跨设备可见。 - Trace/实时输出热路径防回归:Code Queue 的
appendOutput、output archive append、publishTaskEvent、SSE/api/events、任务列表、overview、task meta 和/health都属于热路径,必须保持 O(1) 或明确小常数上界;这些路径不得同步调用完整 transcript 构建器、taskFullOutput、output archive 全量读取、Codex session/log 文件解析、完整task_json物化或任何会随历史输出长度增长的统计。输出追加时必须增量维护轻量持久化指标,至少包括stepCount、llmStepCount、outputMaxSeq或等价字段;列表、overview、meta、SSE 事件和/health只能读取这些指标或小体积 SQL 聚合。完整 Trace、trace-summary、trace-steps、trace-step、transcript/output 详情允许在显式详情请求中解析归档,但必须分页或有界、使用短 TTL 或容量受限缓存,并在 archive append 后失效。若 frontend 性能面板出现 Code Queue 用户服务代理 502、/api/tasks/overview/trace 接口成批超时,或容器内/health在 active output 持续追加时也卡住,优先按 Bun event-loop starvation/backpressure 排查,而不是先改 React 渲染;修复必须证明热路径不再随 output/archive 历史线性增长。 - Trace STEP 权威来源:
GET /api/tasks/<id>/trace-steps与GET /api/tasks/<id>/trace-step必须直接从oa-event-flow读取trace-step-created事实事件,并在响应中暴露source=oa-event-flow;不得在 OA 事件缺失、读取失败或本地 transcript 数量更多/更少时静默回退到task-transcript、Codex session JSONL、output archive 或内存热状态。OA 事件不可用应显式失败或返回空事实集,避免 STEP 计数和执行过程摘要重新形成双路径分叉。 - 完成判定:app-server
turn/completed的turn.status=completed|interrupted|failed只代表 Codex turn 已结束;即使completed也必须把原始任务、当前 attempt 的 assistant 最终回复、command/file-change 事件、stderr tail 和 current attempt events 组成 execution record 交给 judge 判断是否真的完成。MiniMax judge 输入必须做有界压缩,保留终态、最终回复、关键错误/命令/部署证据和摘要计数,避免长 transcript 让 MiniMax 请求超时;默认UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_TIMEOUT_MS=90000。MiniMax judge 之前和之后都必须保留少量协议级硬门禁:明确用户 interrupt 判为 fail;当前 attemptterminalStatus=failed|null、传输在终态前关闭、或当前 attempt 最终回复为空时判为 retry;这些门禁只保护“本轮 turn 是否可被验收”的事实,不得发明业务实现要求。协议门禁通过后,配置了UNIDESK_CODE_QUEUE_MINIMAX_API_KEY且 MiniMax 可用时,MiniMaxMiniMax-M2.7对业务是否complete|retry|fail的判定是权威结果;当且仅当 MiniMax LLM 调用失效(未配置、额度/限流/网络/超时不可用、JSON 去噪与 repair 全部耗尽、或返回超预算反馈且修复耗尽)时,才允许启用非 LLM/fallback 判断。MiniMax 返回必须先做 JSON 去噪,支持去除 Markdown fence、json标签和从夹杂文本中提取平衡 JSON object;如果去噪后仍无法解析,服务必须把解析错误和上一轮去噪前原始回答反馈给 MiniMax 做 JSON repair 重试,重试次数由UNIDESK_CODE_QUEUE_MINIMAX_JUDGE_REPAIR_ATTEMPTS控制,默认2,耗尽后才进入 fallback,并在 fallback 原因、task JSON、attempt summary 和 TraceView 中保留 MiniMax 失败阶段、是否超时、耗时、prompt/payload 大小、HTTP 状态、错误名和响应预览。 - Judge 权威边界:MiniMax 成功返回可解析、预算内的 judge JSON 后,Code Queue 不得用旧 attempt 的 429/exceeded retry limit 证据、历史 output 字符串、面向特定任务的正则或
hardCompletionBlockers/retryRequiredReasons覆盖一次协议有效的完成判定;尤其不能因为 attempt 1 的限流中断仍在历史输出里,就禁止 MiniMax 把 attempt 2 的正常完成判为complete。允许的本地 safety override 必须限定为协议事实和系统交付纪律:用户显式打断、当前 attempt 未正常终止、当前 attempt 没有最终 assistant response、最终回复停在并发文件确认而非交付、或 runtime/UI/service 变更承认未部署验证。所有 override 都必须写入_safetyOverride、生成紧凑 continuation prompt,并由自测或 judge probe 覆盖;不得把业务猜测伪装成本地硬门禁。 - Retry/推进语义:
retry不是新开一个独立任务或完全新 session;只要已有codexThreadId,服务必须thread/resume原 thread 并 append 一个继续执行 prompt。continuation/judge feedback prompt 只应携带本轮缺口、恢复原因、验收要求和有界原始任务摘要,禁止重新注入完整引用上下文、历史 transcript 或长 JSON;服务重启恢复类 feedback 尤其必须保持短 prompt,依赖现有 thread 上文继续。超长 prompt 必须在 prompt 合成源头解决:每个 feedback/recovery/judge 生成器都要从结构化字段选择必要信息、去重合并缺口并提供按需查询入口,禁止先合成超长 prompt 再在末端用 substring/safePreview 一刀切硬截断;硬截断会静默丢失验收信息,风险高于长 prompt 本身。若 MiniMaxcontinuePrompt超出预算,必须要求 MiniMax 基于原始 judge 输入重新合成紧凑反馈,repair 耗尽后才可进入 fallback;不得把已生成的长 prompt 截尾后发送给 Codex。若 MiniMax 成功返回了预算内continuePrompt,必须原样使用该反馈,不得再用 71-Freq、period_sum/mpu_read_num、mpu_read_num、历史限流中断等字符串识别把它覆盖成“简洁原始需求 continuation”。只有 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 judge <taskId> --attempt N或/api/tasks/{id}/judge?attempt=N,响应要包含 attempt 窗口、promptChars/payloadBytes、stored judge 对比、MiniMax 失败阶段和是否因历史 per-attempt events 缺失而降级为 retained events 重建。 - 模型选择:默认 Codex 模型是
gpt-5.5,内置模型队列包含gpt-5.5、gpt-5.4-mini、gpt-5.4;gpt-5.5的默认 reasoning effort 必须是xhigh,可通过CODE_QUEUE_MODEL_REASONING_EFFORTS追加或覆盖模型级默认值;每个入队任务可通过前端模型下拉菜单或 API 覆盖model、cwd、reasoningEffort和maxAttempts,maxAttempts上限为99。Judge 判定retry或非用户取消类fail时必须继续已有codexThreadId,不能新建 session;重试间隔使用指数退避,从1s开始,最大10min。MiniMax 不可用而进入 fallback/non-LLM 判定时,当前 attempt 的 429、Too Many Requests、exceeded retry limit、overloaded、stream disconnected 等服务/限流错误应判定为retry,不能当作完成;MiniMax 可用时,这些内容只能作为当前 attempt 的 factual evidence 提供给 MiniMax,不能通过硬编码覆盖 MiniMax 结果。 - 状态与日志:
D601默认工作目录为容器内/workspace,该路径映射 D601 WSL host 的/home/ubuntu;容器内/home/ubuntu也必须映射同一个 hostPath,保证从/workspace看到的绝对 symlink 可以继续解析到真实文件。服务自身仓库路径/root/unidesk和/app单独映射 D601 WSL host 的/home/ubuntu/cq-deploy。其他 Provider 的任务默认工作目录为/home/ubuntu,任务 JSON、列表、Trace 摘要和 CLI 查询都必须显示providerId、executionMode与最终cwd。executionMode=default在 D601 本机 Code Queue 容器中运行 Codex/OpenCode;executionMode=windows-native只允许非本机 WSL Provider、Codex 模型和/mnt/<drive>工作目录,Code Queue 仍会启动远程执行容器,但容器只运行 stdio relay,经 WSL bridge 调用 Windows 宿主原生codex app-server --listen stdio://。Code Queue 的任务、queue、readAt/未读状态、attempt、judge、promptHistory、active session 元数据、控制状态和 ClaudeQQ 通知 outbox 一律以主 PostgreSQL 为权威,分别写入unidesk_code_queue_tasks、unidesk_code_queue_queues与unidesk_code_queue_notifications;DATABASE_URL是必需配置,服务不得在 PostgreSQL 缺失或不可用时进入文件存储模式。.state/code-queue/state.json不再作为任务或 queue 状态存储,不得重新引入本地 JSON fallback;服务启动必须以 PostgreSQL 为唯一来源恢复队列,并把 running/judging 任务恢复为 retry_wait。D601 资源比主 server 宽裕,但 Code Queue 仍必须把“内存是稀缺资源”作为核心设计约束:历史任务列表、详情、统计和只读 Trace 查询优先从 PostgreSQL 直读,进程内只保留当前 running/judging、queued、retry_wait 等调度必需热任务;需要短期热缓存时必须有严格上限、可裁剪、可从 PostgreSQL 和 append-only 输出归档重建。WebUI 不得用 browserlocalStorage、sessionStorage或 IndexedDB 持久化 task/queue/readAt/unread 等业务状态;浏览器只能保留临时 UI 内存缓存,刷新后必须重新从后端读取 PostgreSQL 权威数据。Codex CLI-like output/Trace 的完整记录可以使用 append-only 文件作为日志型归档,但任务状态、未读状态和列表摘要不得依赖这些文件作为权威来源;/api/tasks/<id>/transcript与/api/tasks/<id>/output必须能分页重建完整历史,不得因为热状态裁剪而丢失早期 trace。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue、提交时下拉选择执行 Provider 和执行模式,并支持把已创建且非 active 的任务移动到其他 queue;queue 内串行,queue 间默认并行且不互相排队;CODE_QUEUE_MAX_ACTIVE_QUEUES仅作为显式配置的全局 active slot 上限,0表示不按 queue 数量限流,内存不足时由 cgroup memory pressure 阻止新 run 并在任务响应中暴露QUEUED(MEM LIMIT)。Code Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,并使用bun --smol运行后端,保证队列任务能直接执行公网 frontend Playwright 回归且主进程内存可控。日志写入 D601.state/code-queue/logs挂载的 UniDesk JSONL 日志;Codex app-server 上游产生的logs_*.sqlite只能作为短暂缓冲,必须由 Code Queue 周期性导出为 JSONL,避免重新形成大 SQLite 文件;/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 状态;D601 本机默认执行必须能通过/root/.ssh复用 WSL host GitHub SSH key 执行 Git over SSH,跨 Provider SSH 或windows-native任务同样需要queue.devReady.ssh.ready=true。Codex CLI-like 输出可能很大,服务必须节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致/health和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程. - ClaudeQQ 通知:Code Queue 在 D601 上通过
CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL=http://host.docker.internal:3290直接调用本机 ClaudeQQ 后端POST /api/push/text,在每个任务进入succeeded、failed或canceled终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入0 running / 0 queued空闲态时,必须单独发送一次空闲提醒。通知由CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED控制,目标由CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group、CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID、CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID配置,默认私聊645275593;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL、CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS、CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS和CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表unidesk_code_queue_notifications再异步发送;不得使用.state/code-queue/claudeqq-notifications.json、CODE_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按CODE_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 超时或离线不会让任务完成通知永久丢失。 - OA 接入:Code Queue 后端通过 D601 env-file 中的
OA_EVENT_FLOW_BASE_URL指向主 server OA Event Flow 受限端口映射,发布每个 TraceView 可见执行行的trace-step-created、幂等种子/乱序校正用trace-stats-snapshot、task-updated和 queue 事件;服务启动或手动 backfill 时必须用相同eventId幂等回放历史 TraceView 可见执行行,避免历史任务停留在旧 STEP 统计口径。前端通过oa-event-flow的service:code-queuetag stream 更新 STEP 和 Trace Summary,Code Queue 私有 SSE 不再作为刷新权威。STEP表示 TraceView 可见且非 system 的执行行数;system 行可保留在任务原始输出/数据库中,但默认不展示、不计入 STEP,工具调用数必须由readCount+editCount+runCount展示,不能复用stepCount。 - 代理路径:只允许
/health、/logs和/api/前缀;允许方法为GET、HEAD、POST、DELETE、PATCH。Code Queue 只在 Compose 内网暴露4222/tcp,不得映射或开放到公网。 - UniDesk 前端:
用户服务 / Code QueueReact 页面负责展示队列卡片、任务 ID、复制任务 ID、引用按钮、任务耗时、默认模型、模型下拉、执行 Provider 下拉、执行模式下拉、Provider/模式对应默认工作目录、显式入队份数、引用任务 ID、清空输入、创建成功提示、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;选择windows-native时应优先切到支持 Windows 原生 Codex 的非主 server Provider,并把工作目录提示切到/mnt/<drive>默认路径;整个 agent loop 消息流统一命名为专有名词Trace,Trace包含 assistant message、user prompt、system event 和 tool call;Code 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 应使用入队份数一次性生成多条队列任务,而不是依赖快速连点按钮;左侧 queue/session 卡片的QUEUED状态必须显示原因,例如QUEUED(PREV TASK)、QUEUED(MEM LIMIT)、QUEUED(ACTIVE LIMIT);原始任务 JSON 只能通过显式查看原始JSON打开。
MDTODO k3s-Managed
当前 MDTODO 作为 id=mdtodo 的 k3sctl-managed 用户服务登记在 config.json,用于把 D601 Windows 工作区 F:\Work\vscode-mdtodo 从 VS Code 扩展形态拆成 UniDesk 可代理的后端服务:
- Orchestrator:
deployment.mode=k3sctl-managed,deployment.adapterServiceId=k3sctl-adapter,deployment.k3sServiceId=mdtodo,backend.proxyMode=k3sctl-adapter-http,backend.nodeBaseUrl=k3s://mdtodo;正式链路只能是frontend -> backend-core -> k3sctl-adapter -> Kubernetes API service proxy -> Kubernetes Service mdtodo:4267。 - 代码与部署引用:后端源码位于 UniDesk 仓库
src/components/microservices/mdtodo,Dockerfile 为src/components/microservices/mdtodo/Dockerfile;k3s manifest 为src/components/microservices/k3sctl-adapter/k3s/mdtodo.k3s.json,Kubernetes 运行清单为src/components/microservices/k3sctl-adapter/k3s/mdtodo.k8s.yaml,镜像名固定为unidesk-mdtodo:d601。 - 持久化边界:D601 的
F:\Work\vscode-mdtodo先同步到 k3s 可见的 WSL hostPath/home/ubuntu/cq-deploy/.state/mdtodo-workspace,Pod 将该目录挂载为/workspace,后端直接读写 Markdown TODO 文件;.state/mdtodo/logs只保存 JSONL 日志,不作为任务权威状态。该服务不得把原 VS Code webview 前端或 VSIX 构建产物作为浏览器入口。 - API:
GET /health、GET /live、GET /logs;GET /api/files;GET /api/tasks?file=...;GET|PATCH|DELETE /api/tasks/{id};POST /api/tasks;GET|PUT /api/content;POST /api/execute-command。/health必须证明 hostPath 可读并至少能扫描到 TODO Markdown 文件。 - 代理路径:只允许
/health、/live、/logs和/api/前缀;允许方法为GET、HEAD、POST、PUT、PATCH和DELETE。业务请求不得退化为 provider-gateway 直连、NodePort 或 D601 本机端口。 - UniDesk 前端:
用户服务 / MDTODOReact 页面负责展示文件列表、任务树、任务状态、标题/正文编辑、新增/删除任务、执行命令生成和显式原始 JSON 按钮;默认页面不得裸铺完整 Markdown 或 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 事件和发送记录。
D601 Docker Restart Recovery
D601 是 Windows + WSL Ubuntu + Docker Desktop 节点,Docker Desktop 当前 LiveRestore=false 时,机器或 Docker daemon 重启会停止容器,恢复链路必须同时覆盖 Windows 登录、WSL keepalive、Docker daemon ready、provider-gateway 和业务用户服务:
- Windows 登录任务:计划任务
UniDesk-D601-Autostart在用户DESKTOP-1MHOD9I\liang登录时运行C:\WINDOWS\System32\cmd.exe /c ""C:\Users\liang\AppData\Local\UniDesk\d601-autostart.cmd"",工作目录为C:\Users\liang\AppData\Local\UniDesk。 - Windows launcher:
C:\Users\liang\AppData\Local\UniDesk\d601-autostart.cmd先启动%ProgramFiles%\Docker\Docker\Docker Desktop.exe,再执行C:\Windows\System32\wsl.exe -d Ubuntu -u ubuntu -- /bin/bash -lc "/home/ubuntu/.local/bin/unidesk-d601-autostart task";D601 的 WSL distro 名必须写Ubuntu,不能写成未验证的Ubuntu-22.04。 - WSL keepalive:
/home/ubuntu/.local/bin/unidesk-d601-autostart使用~/.state/unidesk/d601-autostart.lock防重复,启动 WSLsshd,等待docker info,把unidesk-provider-gateway-D601修正为restart always并启动,然后调用/home/ubuntu/.local/bin/unidesk-microservice-autorecover boot;进入常驻 watchdog 后每 300 秒重复检查 provider-gateway 和用户服务。 - 用户服务 autorecover:
/home/ubuntu/.local/bin/unidesk-microservice-autorecover只在 Docker ready 后运行;它用容器State.Running、State.ExitCode和轻量 HTTP 探针判断是否需要恢复,MET Nonlinear 失败时在/home/ubuntu/met_nonlinear执行docker compose -f docker-compose.unidesk.yml up -d --force-recreate met-nonlinear-ts,ClaudeQQ/NapCat 失败时在/home/ubuntu/.agents/skills/claudeqq执行docker compose -f docker-compose.unidesk.yml up -d --force-recreate napcat claudeqq。 - 验收命令:Docker 恢复后必须同时验证
docker inspect --format '{{.HostConfig.RestartPolicy.Name}} {{.State.Status}}' met-nonlinear-ts claudeqq-backend claudeqq-napcat、bun scripts/cli.ts microservice health met-nonlinear、bun scripts/cli.ts microservice health claudeqq和公网 frontend 页面;ClaudeQQ 还必须验证/api/napcat/login中state=logged_in、HTTP connected 和 WebSocket connected。
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 前端:
用户服务 / FindJobReact 页面负责展示指标、岗位预览、草稿报告和原始 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 前端:
用户服务 / PipelineReact 页面负责展示 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/... 和供主 server oa-event-flow bridge 使用的 /api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics 访问 Pipeline control backend;主 server oa-event-flow 的迁移 bridge 当前只允许单一路径读取 /api/snapshot 与 /api/oa-event-flow/diagnostics,发布 pipeline-run-snapshot 到统一事件表,不得再实现 ledger/snapshot 双路径 fallback。超大 snapshot 必须使用 __unideskArrayLimit=registry.components:<limit>,runs:<limit> 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 .state、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由统一 oa-event-flow 与 Pipeline OA 状态机驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑;共享事件服务规则见 docs/reference/oa-event-flow.md,Pipeline 专有控制流规则见 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。 - 运行配置:
docker-compose.unidesk.yml中met-nonlinear-ts必须固定container_name: met-nonlinear-ts、restart: unless-stopped、127.0.0.1:3288:3288、extra_hosts: ["host.docker.internal:host-gateway"],并把/var/run/docker.sock、/usr/lib/wsl/lib:ro和/dev/dxg挂入容器,让 TS 编排后端能按需拉起 GPU 训练容器并读取 WSL GPU runtime。 - 持久化路径:业务源码与状态通过
/home/ubuntu/met_nonlinear:/workspace/met_nonlinear挂载,训练数据通过/mnt/f/BaiduSyncdisk/data:/data挂载;MET_STATE_DIR=/workspace/met_nonlinear/.state/unidesk-met保存队列和服务状态,MET_LOG_DIR=/workspace/met_nonlinear/logs/unidesk-met保存服务日志,MET_HOST_ROOT与MET_HOST_DATA_ROOT必须指向对应 host 路径,避免训练容器从容器内路径误反推 host 文件。 - 节点后端:D601 上
127.0.0.1:3288,provider-gateway 容器内通过http://host.docker.internal:3288访问。 - 代理路径:只允许
/health和/api/前缀;允许GET、HEAD、POST、PUT,用于读取队列/历史、从已有 Project fork 新 Project、保存队列设置、加入待启动队列和启动队列。 - UniDesk 前端:
用户服务 / MET NonlinearReact 页面采用类似下载器的工作台交互,负责从项目库选择已有 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 为 2080Ti,2080Ti 显存余量低于 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。 - 运行配置:
docker-compose.unidesk.yml必须同时定义napcat与claudeqq两个 service,均使用restart: unless-stopped;napcat固定ACCOUNT=${CLAUDEQQ_NAPCAT_ACCOUNT:-763382329}、WEBUI_PREFIX=/webui和本机端口127.0.0.1:3000/3001/6099,claudeqq固定127.0.0.1:3290:3290、CLAUDEQQ_AUTO_REPLY=false、CLAUDEQQ_NAPCAT_HTTP_HOST=napcat、CLAUDEQQ_NAPCAT_WS_HOST=napcat、CLAUDEQQ_ONLINE_NOTICE_USER_ID=645275593和CLAUDEQQ_LOGIN_MONITOR_INTERVAL_MS。 - 持久化路径:NapCat 登录态必须保存在业务目录下的
./napcat/qq:/app/.config/QQ,NapCat 配置和二维码缓存分别保存在./napcat/config:/app/napcat/config与./napcat/cache:/app/napcat/cache;ClaudeQQ 后端必须挂载./config.json:/app/config.json:ro、./bot_workspace:/bot_workspace、./logs:/app/logs、./.state:/app/.state和./napcat:/napcat:ro。如果这些 host 目录丢失或改成匿名 volume,Docker 重启后 QQ 登录态和事件/订阅状态会丢失,不得判定为已具备自动登录。 - 节点后端: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 同源代理给前端展示。/health的 healthy 条件必须包含ready=true、NapCat HTTP connected、NapCat WebSocket connected 和loginState=logged_in;仅有二维码、NapCat 容器 running 或后端进程 running 不能算健康。 - 订阅 API:
GET /api/events/recent返回最近 QQ 事件,GET|POST /api/events/subscriptions管理 webhook 订阅,DELETE /api/events/subscriptions/{id}删除订阅;订阅回调使用 HTTP POST JSON,并在配置 secret 时携带x-claudeqq-signatureHMAC-SHA256。 - 推送 API:
POST /api/push/text接受userId或groupId与message,由 ClaudeQQ 通过 NapCat HTTP API 发送 QQ 消息;NapCat 不可用时必须快速返回status=napcat_offline和具体连接错误;当前人工推送验收只允许发给主用户私聊账号645275593,其他用户服务和 main server 应通过 UniDesk 用户服务代理调用,不得直连 D601 公网端口。 - UniDesk 前端:
用户服务 / ClaudeQQReact 页面负责展示 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/...完成;通用 CLImicroservice 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,不得开放 D6013290/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 oa-event-flow、bun scripts/cli.ts microservice proxy oa-event-flow /api/diagnostics --raw与bun scripts/cli.ts microservice proxy oa-event-flow '/api/events?tags=service:code-queue&limit=20' --raw:验证统一 OA 事件流、事件表、tag 查询和统计中心。bun scripts/cli.ts microservice health k3sctl-adapter与bun scripts/cli.ts microservice proxy k3sctl-adapter /api/control-plane --raw:验证 D601unidesk-k3s控制面 adapter、manifest、D601 active/D518 standby 实例状态、presentNodeIds=[D601,D518]、missingNodeIds=[]和 no-fallback 运行路径。bun scripts/cli.ts microservice health code-queue与bun scripts/cli.ts microservice proxy code-queue /api/tasks/overview:验证 Code Queue 经过 backend-core -> k3sctl-adapter -> k3s active service 的单一路径;输出不得出现serviceId=code-queue的 provider-gatewaymicroservice.http业务代理任务,写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQL,frontend 不得用本地存储伪造成功状态。bun scripts/cli.ts microservice health filebrowser、bun scripts/cli.ts microservice health filebrowser-d601与bun scripts/cli.ts microservice proxy filebrowser / --max-body-bytes 2000:验证 D518 主 File Browser 和 D601 备用 File Browser 私有代理链路;浏览器 WebUI 必须通过/api/microservices/filebrowser/proxy/或/api/microservices/filebrowser-d601/proxy/访问,不得直接开放4251公网端口。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、code-queue.tsx、k3sctl.tsx。默认展示必须是业务控件:指标卡、状态徽标、表格、草稿卡片、运行卡片、树形任务、表单控件、结构化材料索引、链接和字段摘要;只有操作员点击 查看原始JSON 时才允许打开原始 JSON 弹窗。日志、JSONL 和大块 JSON 不得在主界面按行展示,避免把裸数据伪装成 UI。
对于超大业务 JSON,backend-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,确认k3sctl-adapter为providerId=D601、deployment.mode=unidesk-direct、后端私有端口127.0.0.1:4266,并确认code-queue为deployment.mode=k3sctl-managed、runtime.orchestrator=k3sctl、backend.proxyMode=k3sctl-adapter-http、backend.nodeBaseUrl=k3s://code-queue,且不再显示业务容器直连摘要。 - 在主 server 运行
bun scripts/cli.ts microservice list,确认filebrowser和filebrowser-d601分别显示为providerId=D518和providerId=D601,均为public=false、frontendOnly=true,仓库 URL 为https://github.com/filebrowser/filebrowser,后端映射为host.docker.internal:4251,容器摘要分别为unidesk-filebrowser-d518和unidesk-filebrowser-d601;列表中不得再出现主 serverfilebrowser-main容器。 - 运行
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 和主 servertodo-note-backend后端;输出中必须包含五个迁移清单和 PostgreSQL 存储健康状态。 - 运行
bun scripts/cli.ts microservice health k3sctl-adapter、bun scripts/cli.ts microservice proxy k3sctl-adapter /api/control-plane --raw、bun scripts/cli.ts microservice health code-queue与bun scripts/cli.ts microservice proxy code-queue /api/tasks/overview,确认真实链路经过 backend-core -> k3sctl-adapter -> k3s active service;adapter 验收还必须证明其作为 UniDesk 直管服务运行在 k3s 外部,Docker 形态下挂载宿主/etc/rancher/k3s/k3s.yaml与/run/host-ssh/id_ed25519,通过容器内 SSH local tunnel 连接 WSL 原生 k3s API,且没有 activerancher/k3s控制面容器。Code Queue/health必须仍返回业务后端自己的queue.storage.primary=postgres、queue.storage.postgresReady=true、queue.notifications.claudeqq.outbox.storage=postgres和egressProxy.connected=true,不得被 adapter 聚合健康 JSON 替代。还必须在 active Code Queue Pod 内验证主 PostgreSQL 端口映射、主 OA Event Flow 端口映射、本机 ClaudeQQhttp://host.docker.internal:3290和d601-provider-egress-proxy均可访问,并确认/workspace与/home/ubuntu指向同一 WSL home hostPath,/workspace/cq-deploy这类绝对 symlink 可以进入真实目录。再在 adapter 控制页确认 D601 active serving healthy、D518 standby pod ready、missingNodeIds=[]且整体不退化为 hidden fallback。再通过公网 frontend 提交一个gpt-5.5小任务,确认队列串行推进、输出实时更新、结束后有 judge 判定,且运行中可追加 prompt 或打断。Code Queue 的重启恢复必须作为验收项:运行中任务存在时重启或重建 active 实例后,任务必须从 PostgreSQL 恢复到可继续执行状态,不能丢失 active task、promptHistory、后续 queued 任务、readAt/未读状态或已入 outbox 的 ClaudeQQ 通知。Code Queue 服务名、表名前缀或持久化目录发生迁移后,还必须运行bun scripts/cli.ts e2e run --only microservice:catalog-code-queue,microservice:code-queue-status,microservice:code-queue-health,microservice:code-queue-tasks,证明 backend-core catalog、k3s adapter 私有代理、PostgreSQL 队列和任务列表都指向code-queue。批量验收必须通过公网 frontend 设置入队份数=5或使用多段 prompt 分隔,一次性入队 5 条任务,并确认 5 条任务按顺序进入 running/judging/succeeded,而不是只运行第一条。 - Code Queue 内存防回归验收:凡是改动 Code Queue 的持久化、scheduler、输出/Trace、health、列表/详情查询、日志导出或容器运行参数,交付前必须在 D601 用
kubectl -n unidesk get deploy,pod,svc,endpoints -o wide、kubectl -n unidesk describe deploy/code-queue或等价 Docker inspect 确认 memory/swap 硬上限符合预算,运行kubectl -n unidesk top pod或 Docker stats 确认常驻内存、OOMKilled=false和RestartCount未异常增长,再运行bun scripts/cli.ts microservice health code-queue确认/health是轻量 readiness 且暴露 PostgreSQL/notification/outbox 状态。验收还必须覆盖有历史任务存在时的/api/tasks/overview、单任务详情和 output/transcript 查询,证明热状态裁剪不会丢历史输出、也不会重新把全部历史task_json缓存在进程内;涉及 TypeScript/frontend 验证的任务应能在 D601 Code Queue memory/swap 预算中完成bun run --cwd src/components/frontend check这类短时高内存命令,而不是被 memory watchdog 反复 SIGTERM。 - Code Queue 延迟防回归验收:凡是改动 Code Queue 列表、overview、readAt、Trace/summary 懒加载、实时 output/SSE 事件发布、frontend 请求策略、backend-core 用户服务代理或 frontend Code Queue 请求路径,交付前必须在有历史任务数据且有 active output 流动的 live 环境验证
GET /api/tasks/overview、POST /api/tasks/<id>/read、选定 task 的trace-step和前端/app/code-queue/首屏均低于 1s 目标;可运行bun scripts/src/code-queue-perf.ts --json --target-ms 1000采集公网 frontend 下的首屏耗时、最慢 API 和 DOM 完成指标,并用bun scripts/cli.ts microservice proxy code-queue /api/tasks/overview --raw、D601 Pod/health与/api/tasks/overviewcurl、性能面板/api/performance与/api/frontend-performance失败/慢操作记录、kubectl -n unidesk top pod或 Docker stats 补充后端耗时、代理 502 和内存/CPU 证据。验收结论必须同时说明是否使用了短 TTL cache、cache 如何被 mutation 或 archive append 失效、数据库索引/聚合是否命中、输出热路径是否只读增量指标,以及分页加载是否跳过 selected/active/stats;不能只展示 cache 命中后的单次快照。 - 运行
bun scripts/cli.ts microservice health filebrowser、bun scripts/cli.ts microservice health filebrowser-d601和bun scripts/cli.ts microservice proxy filebrowser / --max-body-bytes 2000,确认 File Browser health 返回status=OK,WebUI HTML 包含File Browser,D518/D601 通过 provider-gateway 访问节点本机4251;随后在公网 frontend 的用户服务 / File Browser中确认 D518 为默认目标、可导出截图、iframe 紧凑布局不再有巨大folder标记遮挡文件名,并可浏览/mnt/c。 - 在 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 或逐行日志。