chore: checkpoint before performance tuning
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
- This design allows verification of the full distributed dispatching flow on a single main server
|
||||
- Main Server Components
|
||||
- UniDesk Stateless Services
|
||||
- Run all business microservices as Docker containers
|
||||
- Run all user services as Docker containers; these user-facing services are mounted onto the UniDesk core and the core can still run without them
|
||||
- Includes frontend gateway, task scheduler, project management, provider ingress, and other stateless modules
|
||||
- Instances can scale horizontally; failure recovery requires no state synchronization
|
||||
- Only the frontend gateway and provider ingress are public; core REST APIs and PostgreSQL remain on the Docker internal network
|
||||
|
||||
+21
-7
@@ -12,11 +12,16 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
|
||||
- `server stop` 创建异步 job,在后台停止固定 Compose project 中的全部 UniDesk 服务。
|
||||
- `server status` 查询公开端口、内部端口、Compose 容器、core/frontend/provider/database 健康检查和访问 URL。
|
||||
- `server logs` 返回 `logs/` 文件日志和 Docker 容器日志的尾部,默认限制输出大小,避免日志爆炸。
|
||||
- `server rebuild <backend-core|frontend|provider-gateway|todo-note|codex-queue>` 创建异步 job,先构建目标服务镜像,构建成功后只按 Compose project/service label 移除该服务旧容器,再用 `--no-deps` 启动目标服务;该命令用于替代手工删除容器的兜底流程,其中 `todo-note` 和 `codex-queue` 只重建主 server 承载的对应后端,不会重建或删除 database 命名卷。
|
||||
- `server rebuild <backend-core|frontend|provider-gateway|todo-note|codex-queue|project-manager>` 创建异步 job,先构建目标服务镜像,随后在 `.state/locks/server-compose.lock` 串行保护下用 `--no-deps --force-recreate` 替换目标 service 并等待容器 `healthy/running`;该命令用于替代手工删除容器的兜底流程,其中 `todo-note`、`codex-queue` 和 `project-manager` 只重建主 server 承载的对应后端,不会重建或删除 database 命名卷。
|
||||
- `ssh <providerId> [ssh-like args...]` 通过 backend-core 内网 WebSocket broker 和 provider-gateway 的 Host SSH / WSL SSH 维护桥连接目标节点;无后续参数时进入远端登录 shell,有后续参数时按 ssh 远端命令体验执行并返回远端 exit code。
|
||||
- `ssh <providerId> apply-patch [tool args...] < patch.diff` 直接调用远端注入的 `apply_patch` 工具,并把本地 stdin 中的标准 `*** Begin Patch` / `*** End Patch` patch 流透传给目标节点。
|
||||
- `ssh <providerId> py [script-args...] < script.py` 把本地 stdin 落到远端临时 `.py` 文件后再以 `python3 -u` 执行并自动清理,避免再手写 `'python3 -'`、heredoc 或多层引号;`script-args` 会按 argv 安全透传给远端脚本。
|
||||
- `microservice list/status/health/proxy` 通过 backend-core 内网 API 管理挂载在计算节点 Docker 中的 microservice;`health` 和 `proxy` 会走真实 backend-core -> provider-gateway -> 节点本机后端链路,`proxy` 对超大 body 默认输出有界预览,规则见 `docs/reference/microservices.md`。
|
||||
- `ssh <providerId> skills [--scope all|wsl|windows] [--limit N]` 发现目标节点上的 WSL/Linux skill 根目录;当 provider 是 WSL 时同一次调用还会扫描 Windows 用户目录下的 `.agents/skills` 与 `.codex/skills`。
|
||||
- `microservice list/status/health/proxy` 通过 backend-core 内网 API 管理挂载在计算节点 Docker 中的用户服务(底层命令名仍为 microservice);`health` 和 `proxy` 会走真实 backend-core -> provider-gateway -> 节点本机后端链路,`proxy` 对超大 body 默认输出有界预览,规则见 `docs/reference/microservices.md`。
|
||||
- `codex task <taskId>` 通过 Codex Queue 私有代理按任务 ID 查询结构化执行摘要;默认只返回有界 prompt/response 预览、最后 assistant message、最近工具调用摘要、attempt、judge、错误、耗时和 trace 翻页提示,适合在新队列任务中引用历史 session 且避免噪声爆炸。
|
||||
- `codex task <taskId> --trace --tail|--from-start|--after-seq N|--before-seq N --limit N` 按页拉取 Codex Queue 的逻辑 trace;响应会返回 `nextAfterSeq`、`previousBeforeSeq`、`hasMore`、`hasBefore` 和下一页/上一页命令,默认 `--trace` 取最新一页,需要完整 prompt/最后 response 时加 `--full`。
|
||||
- `codex output <taskId> --tail|--from-start|--after-seq N|--before-seq N --limit N [--full-text]` 按原始 output seq 分页读取底层记录;当 trace 行提示 `commandOmittedLines`、`bodyOmittedLines` 或 `rawSeqs` 时,用该命令按 seq 补取完整信息,默认仍有单条文本预览上限,显式 `--full-text` 才返回该页全文。
|
||||
- `codex queues`、`codex queue create <queueId>`、`codex move <taskId> --queue <queueId>` 管理 Codex Queue 的多队列 lane;同一个 queue 内部串行执行,不同 queue 之间并行执行,迁移 queued/retry_wait 任务后会立即调度目标 queue。
|
||||
- `job list` 与 `job status` 查询 `.state/jobs/` 文件系统状态,是异步命令的可观测入口。
|
||||
- `debug health`、`debug dispatch` 与 `debug task` 走真实内部 core、WebSocket、数据库、provider、系统指标、Docker 状态和 Host SSH 维护桥流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。
|
||||
- `e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]` 使用 publicHost 派生的公开 frontend/provider ingress URL,并通过 Docker 内网验证 core API、PostgreSQL、provider self-connection、系统指标曲线、Docker 状态快照、provider.upgrade 预检和 Playwright 前端页面,是交付前的自动化 E2E 门禁;CLI 默认输出 check 状态摘要,完整诊断写入 `resultPath`,日常迭代应优先用 `--only` / `--skip` 跑最小必要集合。
|
||||
@@ -25,17 +30,17 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
|
||||
|
||||
长时操作采用 Fire-and-Forget 模式:CLI 创建 `.state/jobs/{jobId}.json`,后台进程执行真实命令,并将 stdout、stderr 分别写入 `.state/jobs/{jobId}.stdout.log` 与 `.state/jobs/{jobId}.stderr.log`。调用者通过 `bun scripts/cli.ts job status <jobId>` 查询进度和尾部输出。
|
||||
|
||||
`server rebuild` 与 `server start`、`server stop` 一样必须通过 job 状态确认结果。重建 frontend 的标准流程是运行 `bun scripts/cli.ts server rebuild frontend`,随后轮询 `bun scripts/cli.ts job status latest` 到 `succeeded`,再用 `server status` 或 `e2e run` 验证公网 frontend;重建 Todo Note 后端使用 `bun scripts/cli.ts server rebuild todo-note`,随后用 `microservice health todo-note` 和 `microservice proxy todo-note /api/instances` 验证;重建 Codex Queue 后端使用 `bun scripts/cli.ts server rebuild codex-queue`,随后用 `microservice health codex-queue` 和 `microservice proxy codex-queue /api/tasks` 验证。不得把 `docker rm` 手工兜底当成正式交付步骤。
|
||||
`server rebuild` 与 `server start`、`server stop` 一样必须通过返回的 job id 确认结果;不要把 `server rebuild codex-queue && server rebuild frontend` 理解成“前一个重建已完成”,因为两个命令只是在快速创建异步 job。重建 frontend 的标准流程是运行 `bun scripts/cli.ts server rebuild frontend`,随后轮询 `bun scripts/cli.ts job status <jobId>` 到 `succeeded`,再用 `server status` 或 `e2e run` 验证公网 frontend;重建 Todo Note 后端使用 `bun scripts/cli.ts server rebuild todo-note`,随后用 `microservice health todo-note` 和 `microservice proxy todo-note /api/instances` 验证;重建 Codex Queue 后端使用 `bun scripts/cli.ts server rebuild codex-queue`,随后用 `microservice health codex-queue` 和 `microservice proxy codex-queue /api/tasks` 验证;重建 Project Manager 后端使用 `bun scripts/cli.ts server rebuild project-manager`,随后用 `microservice health project-manager` 和 `microservice proxy project-manager /api/projects` 验证。不得把 `docker rm` 手工兜底当成正式交付步骤。
|
||||
|
||||
## Output Contract
|
||||
|
||||
每条命令的最外层 JSON 包含 `ok`、`command` 和 `data` 或 `error`。失败时 CLI 设置非零退出码,但仍然输出 JSON 错误对象;错误对象应包含 `name`、`message` 和可用的 `stack`。
|
||||
|
||||
`microservice proxy` 是面向人工验证的私有后端读取入口。正式写入型 microservice 操作由 frontend 同源代理或 E2E 直接调用 backend-core 完成,并由 config 中的 `allowedMethods` 限制;CLI `proxy` 默认仍作为 GET/HEAD 读取验证入口。为了避免 Pipeline snapshot 这类超大业务 JSON 造成 CLI 输出爆炸,响应 body 超过默认阈值时会返回 `bodyOmitted=true`、`bodyPreview`、`bodyBytes` 和 `rawHint`;需要完整 body 时显式添加 `--raw`,或用 `--max-body-bytes <N>` 调整预览阈值。正式 frontend 展示仍应优先使用业务控件和 `__unideskArrayLimit` 这类展示级裁剪参数,而不是默认倾倒完整 JSON。
|
||||
`microservice proxy` 是面向人工验证的私有后端读取入口。正式写入型用户服务操作由 frontend 同源代理或 E2E 直接调用 backend-core 完成,并由 config 中的 `allowedMethods` 限制;CLI `proxy` 默认仍作为 GET/HEAD 读取验证入口。为了避免 Pipeline snapshot 这类超大业务 JSON 造成 CLI 输出爆炸,响应 body 超过默认阈值时会返回 `bodyOmitted=true`、`bodyPreview`、`bodyBytes` 和 `rawHint`;需要完整 body 时显式添加 `--raw`,或用 `--max-body-bytes <N>` 调整预览阈值。正式 frontend 展示仍应优先使用业务控件和 `__unideskArrayLimit` 这类展示级裁剪参数,而不是默认倾倒完整 JSON。
|
||||
|
||||
## Debug Contract
|
||||
|
||||
`debug` 子命令必须复用真实模块与真实端点,禁止维护平行实现。`debug health` 会摘要展示 `/api/nodes/system-status` 和 `/api/nodes/docker-status`,避免输出完整快照造成信息爆炸。`debug dispatch` 会在 backend-core 容器内调用内部 `/api/dispatch`,core 再通过 WebSocket 将 `docker.ps`、`provider.upgrade`、`host.ssh`、`microservice.http` 或 `echo` 任务下发给 provider gateway,因此它可以验证核心调度闭环,同时不需要公开 core REST API。`provider.upgrade` 默认使用 `mode: "plan"` 预检;需要验证一键升级时必须显式加 `--mode schedule`,并通过 `--wait-ms` 或 `debug task` 确认任务进入 `succeeded`、result 中包含 updater 容器信息和 `policy: "always-enabled"`。`host.ssh` 默认使用 `mode: "probe"` 做短超时维护桥自检;需要执行明确命令时使用 `--ssh-command` 进入 `mode: "exec"`,并配合 `--wait-ms` 和 `debug task` 查看 stdout、stderr、exitCode 与 probeLine。`microservice.http` 只用于开发调试 provider-gateway 私有 HTTP 代理,正式用户入口应使用 `microservice` CLI 或 frontend 页面。
|
||||
`debug` 子命令必须复用真实模块与真实端点,禁止维护平行实现。`debug health` 会摘要展示 `/api/nodes/system-status` 和 `/api/nodes/docker-status`,避免输出完整快照造成信息爆炸。`debug dispatch` 会在 backend-core 容器内调用内部 `/api/dispatch`,core 再通过 WebSocket 将 `docker.ps`、`provider.upgrade`、`host.ssh`、`microservice.http` 或 `echo` 任务下发给 provider gateway,因此它可以验证核心调度闭环,同时不需要公开 core REST API。`provider.upgrade` 默认使用 `mode: "plan"` 预检;需要验证一键升级时必须显式加 `--mode schedule`,并通过 `--wait-ms` 或 `debug task` 确认任务进入 `succeeded`、result 中包含 updater 容器信息和 `policy: "always-enabled"`。`host.ssh` 默认使用 `mode: "probe"` 做短超时维护桥自检;需要执行明确命令时使用 `--ssh-command` 进入 `mode: "exec"`,并配合 `--wait-ms` 和 `debug task` 查看 stdout、stderr、exitCode 与 probeLine。`microservice.http` 只用于开发调试 provider-gateway 私有 HTTP 代理,正式用户入口应使用 `microservice` CLI 或 frontend 的用户服务页面。
|
||||
|
||||
## SSH Command
|
||||
|
||||
@@ -47,7 +52,7 @@ core 只允许声明了 `host.ssh` capability 的 provider 使用 `ssh` 透传
|
||||
|
||||
本地 broker 默认等待 provider SSH 会话打开 60000ms,以便在目标节点同时有较多 microservice.http 任务时仍能建立维护会话;需要诊断慢连接时可用 `UNIDESK_SSH_OPEN_TIMEOUT_MS=<ms>` 临时调大,但最小有效值固定为 15000ms,避免把真实离线误判为长时间阻塞。
|
||||
|
||||
`ssh <providerId>` 会在远端会话启动时注入 `/tmp/unidesk-ssh-tools/apply_patch` 和 `/tmp/unidesk-ssh-tools/glob`,并把该目录加入远端 `PATH`。`apply_patch` 接受标准 `*** Begin Patch` / `*** End Patch` patch 格式,便于通过 SSH 透传编辑远端仓库文件;`glob` 在远端用 Python 执行路径匹配,避免依赖 shell glob 展开。目标节点需要具备 `python3` 和 `base64`。注入工具只写 `/tmp/unidesk-ssh-tools`,不修改目标仓库,交互式 shell 和远端命令都可以直接调用这些工具。
|
||||
`ssh <providerId>` 会在远端会话启动时注入 `/tmp/unidesk-ssh-tools/apply_patch`、`/tmp/unidesk-ssh-tools/glob` 和 `/tmp/unidesk-ssh-tools/skill-discover`,并把该目录加入远端 `PATH`。`apply_patch` 接受标准 `*** Begin Patch` / `*** End Patch` patch 格式,便于通过 SSH 透传编辑远端仓库文件;`glob` 在远端用 Python 执行路径匹配,避免依赖 shell glob 展开;`skill-discover` 用于列出远端 Linux/WSL 与 Windows skill。目标节点需要具备 `python3` 和 `base64`。注入工具只写 `/tmp/unidesk-ssh-tools`,不修改目标仓库,交互式 shell 和远端命令都可以直接调用这些工具。
|
||||
|
||||
如果只是远端打小补丁,不需要再手写 `ssh D601 'apply_patch' < patch.diff` 这种命令拼接;正式入口是 `bun scripts/cli.ts ssh D601 apply-patch < patch.diff`。`apply-patch` 与 `patch` 等价,附加参数会原样透传给远端 `apply_patch`,例如 `bun scripts/cli.ts ssh D601 apply-patch --help`。标准单命令用法如下,不需要先创建本地 patch 临时文件:
|
||||
|
||||
@@ -70,6 +75,15 @@ printf 'import sys\nprint(sys.argv)\n' | bun scripts/cli.ts ssh D601 py foo '--b
|
||||
|
||||
`ssh <providerId> py` 的附加参数是脚本参数,不是 Python 解释器参数;如需 `-m`、`-X` 或多条 shell 命令,仍使用原始远端命令入口。为了保证 CLI 输出及时可见,helper 固定采用“临时文件 + `python3 -u`”模式;provider 命令模式不分配 TTY,因此脚本内容不应被远端回显。
|
||||
|
||||
`ssh <providerId> skills` 是远端 skill 发现入口,也可写作 `ssh <providerId> skill discover`。输出固定为 JSON,包含 `node`、`roots`、`counts` 和 `skills`:`roots` 会显示每个候选 skill 根目录是否存在、扫描到多少 skill 以及错误;`skills` 会给出 `scope`、`name`、`description`、`path`、`skillMd` 和可转换时的 `windowsPath`。默认扫描远端用户的 `~/.agents/skills`、`~/.codex/skills`、可访问的 `/root/.agents/skills`、`/root/.codex/skills`;如果目标是 WSL,还会扫描 `/mnt/c/Users/*/.agents/skills` 与 `/mnt/c/Users/*/.codex/skills`,从而一次性看清 WSL 和 Windows 两套 skill。常用参数是 `--scope wsl`、`--scope windows`、`--limit N`、`--max-depth N`、`--root <path>` 和 `--windows-root <path>`;不要用宽泛的 Linux `find /mnt/*` 扫 Windows 盘,优先用这个结构化入口避免卡在 Windows 挂载层。
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts ssh D601 skills --limit 80
|
||||
bun scripts/cli.ts ssh D601 skills --scope windows --limit 40
|
||||
```
|
||||
|
||||
Windows 工具链透传的 wrapper、路径转换、是否修改 skill、是否额外安装依赖等长期规则见 `docs/reference/windows-passthrough.md`;`ssh skills` 本身只负责发现,不会修改远端 skill。
|
||||
|
||||
`ssh <providerId> find` 是常用远端搜索的结构化入口,避免在 Host SSH / WSL SSH 透传里手写 `find \( ... \)`、`*`、管道和多层引号。它会把路径、谓词和 pattern 作为 argv 安全拼接,并支持重复 `--name`、`--iname`、`--path` 或 `--ipath`,重复 pattern 默认按 OR 组合。稳定参数包括 `--max-depth`/`-maxdepth`、`--min-depth`/`-mindepth`、`--type`/`-type`、`--contains`、`--icontains`、`--name`/`-name`、`--iname`/`-iname`、`--path`/`-path`、`--ipath`/`-ipath`、`--mtime`/`-mtime`、`--mmin`/`-mmin`、`--size`/`-size`、`--sort` 和 `--limit N`。典型用法:
|
||||
|
||||
```bash
|
||||
@@ -90,7 +104,7 @@ bun scripts/cli.ts ssh D601 glob --root /home/ubuntu/pikapython --pattern '**/*-
|
||||
|
||||
`--main-server-ip` 是一个全局前缀,必须放在需要透传的命令同一次调用中,例如 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug health`。默认传输是公网 frontend:本地 CLI 读取本仓库 `config.json` 中的 frontend 登录账号密码,登录 `http://<ip>:<frontendPort>/` 获取 HttpOnly session cookie,然后通过 frontend 的 `/api/*` 同源代理访问 backend-core 内网 API;因此计算节点只需要能访问公网 frontend,不需要主 server SSH key,也不需要打开 backend-core REST API 或 PostgreSQL 端口。
|
||||
|
||||
默认 frontend 传输支持 `debug health`、`debug dispatch`、`debug task`、`microservice list/status/health/proxy` 和 `ssh <PROVIDER_ID> <remote-command>`。其中 `ssh` 的 remote frontend 传输使用 `host.ssh` dispatch 执行有界远端命令,适合 `ssh D601 hostname` 这类自测;交互式登录 shell 仍应在主 server 本机 CLI 使用,或显式切换到旧 SSH 传输后在主 server 上执行。frontend 远程透传不会流式转发本地 stdin,因此 `ssh py < script.py`、`ssh apply-patch < patch.diff` 这类 stdin-backed helper 必须在主 server 本机运行,或显式切换到 `--main-server-transport ssh`。若确实需要旧行为,可使用 `--main-server-key <key>` 或 `--main-server-transport ssh`,这时 CLI 会通过 SSH 登录主 server 的 `--main-server-root` 目录执行同一个 `bun scripts/cli.ts <command>`。
|
||||
默认 frontend 传输支持 `debug health`、`debug dispatch`、`debug task`、`microservice list/status/health/proxy`、`codex task <taskId>` 和 `ssh <PROVIDER_ID> <remote-command>`。其中 `ssh` 的 remote frontend 传输使用 `host.ssh` dispatch 执行有界远端命令,适合 `ssh D601 hostname` 和 `ssh D601 skills` 这类自测;交互式登录 shell 仍应在主 server 本机 CLI 使用,或显式切换到旧 SSH 传输后在主 server 上执行。frontend 远程透传不会流式转发本地 stdin,因此 `ssh py < script.py`、`ssh apply-patch < patch.diff` 这类 stdin-backed helper 必须在主 server 本机运行,或显式切换到 `--main-server-transport ssh`。若确实需要旧行为,可使用 `--main-server-key <key>` 或 `--main-server-transport ssh`,这时 CLI 会通过 SSH 登录主 server 的 `--main-server-root` 目录执行同一个 `bun scripts/cli.ts <command>`。
|
||||
|
||||
计算节点可以用该入口测试自身的远程升级闭环,而不需要在计算节点公开 core REST API 或 database。标准顺序是:先运行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug health` 确认主 server 看到当前 Provider 在线,且该 Provider labels 中 `unideskCapabilities` 包含 `host.ssh`、`hostSshConfigured=true`、`hostSshKeyPresent=true`;再运行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> provider.upgrade --mode schedule --wait-ms 15000` 触发真实 `provider.upgrade`;随后再次运行 `debug health` 确认节点重新上线;最后运行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000` 和 `bun scripts/cli.ts --main-server-ip 74.48.78.17 ssh <PROVIDER_ID> hostname` 验证 SSH 透传能力。provider-gateway 新部署或升级后没有完成这组 remote CLI 自测,不能视为交付完成。
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@ TypeScript 运行时固定为 Bun。根目录 CLI、backend-core、frontend 和
|
||||
|
||||
`sshForwarding` 定义 provider-gateway 维护专用 Host SSH / WSL SSH 桥的显式配置。CLI 会把 `sshForwarding.keyDir` 写入 `.state/docker-compose.env` 的 `UNIDESK_HOST_SSH_KEY_DIR`,Compose 将该目录只读挂载到 provider-gateway 的 `/run/host-ssh`,并把 `sshForwarding.host`、`sshForwarding.port`、`sshForwarding.user` 映射为 `HOST_SSH_HOST`、`HOST_SSH_PORT`、`HOST_SSH_USER`。目录中必须存在 `id_ed25519` 私钥且权限收紧,provider-gateway 才会把 `hostSshKeyPresent` 上报为 true,并允许 `host.ssh` 维护探测;该桥只用于故障诊断和 WSL 维护,不替代 Docker socket 调度。
|
||||
|
||||
## Microservices
|
||||
## User Services
|
||||
|
||||
`microservices` 定义挂载在计算节点或主 server Docker 中的非核心业务后端。该数组只保存业务仓库 URL、commit id、业务仓库自身 Dockerfile/docker-compose 引用、provider 映射、节点后端端口和 UniDesk frontend 集成入口;不得把业务全量代码复制进 UniDesk。`backend.public` 必须为 `false`,`backend.frontendOnly` 必须为 `true`,`backend.allowedPathPrefixes` 必须限制到业务 API 前缀,`backend.allowedMethods` 必须显式列出允许代理的 HTTP 方法;浏览器只能通过 frontend 同源代理访问这些后端。详细规则见 `docs/reference/microservices.md`。
|
||||
`microservices` 定义挂载在计算节点或主 server Docker 中的非核心用户服务。用户服务是挂在 UniDesk 核心服务上的用户业务能力,缺少这些服务时 UniDesk 核心仍必须能运行。该数组只保存业务仓库 URL、commit id、业务仓库自身 Dockerfile/docker-compose 引用、provider 映射、节点后端端口和 UniDesk frontend 集成入口;不得把业务全量代码复制进 UniDesk。`backend.public` 必须为 `false`,`backend.frontendOnly` 必须为 `true`,`backend.allowedPathPrefixes` 必须限制到业务 API 前缀,`backend.allowedMethods` 必须显式列出允许代理的 HTTP 方法;浏览器只能通过 frontend 同源代理访问这些后端。详细规则见 `docs/reference/microservices.md`。
|
||||
|
||||
主 server 承载的 Todo Note microservice 使用 `providerId=main-server`、`nodeBaseUrl=http://todo-note:4211` 和 `allowedMethods=["GET","HEAD","POST","DELETE"]`,数据库使用主 PostgreSQL;Codex Queue 使用 `providerId=main-server`、`nodeBaseUrl=http://codex-queue:4222` 和 `allowedPathPrefixes=["/health","/logs","/api/"]`,只通过 frontend/backend/provider-gateway 私有代理访问 Compose 内网服务名;D601 的 FindJob 只允许 `GET/HEAD` 展示型读取路径;D601 的 Pipeline 允许 `GET/HEAD/POST`,其中 `POST` 只用于 Pipeline 后端 `/api/node-control/...` 的 append prompt、guide 和 redo/restart 等受控 node 操作。
|
||||
主 server 承载的 Todo Note 用户服务使用 `providerId=main-server`、`nodeBaseUrl=http://todo-note:4211` 和 `allowedMethods=["GET","HEAD","POST","DELETE"]`,数据库使用主 PostgreSQL;Codex Queue 使用 `providerId=main-server`、`nodeBaseUrl=http://codex-queue:4222` 和 `allowedPathPrefixes=["/health","/logs","/api/"]`,只通过 frontend/backend/provider-gateway 私有代理访问 Compose 内网服务名;D601 的 FindJob 只允许 `GET/HEAD` 展示型读取路径;D601 的 Pipeline 允许 `GET/HEAD/POST`,其中 `POST` 只用于 Pipeline 后端 `/api/node-control/...` 的 append prompt、guide 和 redo/restart 等受控 node 操作。
|
||||
|
||||
## Compose Env Generation
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# ConStart / constar on D601
|
||||
|
||||
本文记录 D601 Provider 上 ConStart 固件工作区通过 UniDesk 维护桥进入、用 WSL wrapper 调用 Windows skill,以及完成编译、下载和运行态验证的稳定入口。项目内部细节仍以 ConStart 仓库自己的 `docs/reference/` 为权威来源。
|
||||
|
||||
## 入口
|
||||
|
||||
- UniDesk 侧从主 server 仓库执行:`bun scripts/cli.ts ssh D601 -- '<command>'`。
|
||||
- 当前真实工作区路径是 D601 WSL 内的 `/mnt/f/Work/ConStart`。用户口头说的 `/mnt/f/work/constar` 可视为简称;实际执行前要先解析到真实大小写路径。
|
||||
- 远端工作优先使用 UniDesk SSH helper:`ssh D601 skills`、`ssh D601 find`、`ssh D601 glob`、`ssh D601 py`、`ssh D601 apply-patch`,避免多层 shell/Windows 路径转义问题。
|
||||
- 不要宽泛扫描 `/mnt/*`;Windows 挂载层可能很慢且有权限噪声。应把查找范围限制到 `/mnt/f/Work/ConStart` 或已知子目录。
|
||||
|
||||
## D601 skill 栈
|
||||
|
||||
D601 WSL 用户的 `~/.local/bin` 已提供项目级 wrapper:
|
||||
|
||||
- `keil`:经 `win-py` 调 Windows 侧 Keil skill,Keil MDK、pyOCD、USB probe 驱动和 Keil pack 都在 Windows 侧解析。
|
||||
- `serial-monitor`:经 `win-npm` 调 Windows 侧 Serial Monitor skill,因此能访问 Windows `COMx` 串口。
|
||||
- `board-comm`:直接用 WSL 侧 Board Comm skill 执行 JSON-RPC over TCP。
|
||||
- `win-cmd`、`win-powershell`、`win-py`、`win-npm`、`win-skill-path`:节点 bootstrap wrapper,通用规则见 `docs/reference/windows-passthrough.md`。
|
||||
|
||||
基础自检:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts ssh D601 skills --limit 80
|
||||
bun scripts/cli.ts ssh D601 -- 'export PATH="$HOME/.local/bin:$PATH"; command -v win-py win-npm keil serial-monitor board-comm'
|
||||
bun scripts/cli.ts ssh D601 -- 'export PATH="$HOME/.local/bin:$PATH"; keil status; serial-monitor ports; board-comm debug build-jrpctcp-request get api'
|
||||
```
|
||||
|
||||
## ConStart 项目地图
|
||||
|
||||
以下命令默认在 `/mnt/f/Work/ConStart` 中执行,并先把 `~/.local/bin` 放进 `PATH`。
|
||||
|
||||
| Project | Keil project | Target(s) | 常见 probe / runtime |
|
||||
| --- | --- | --- | --- |
|
||||
| `71-FREQ` | `projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx` | `FREQ_Controller_FW`, `BOOTLOADER` | STM32H723,通常 probe `3FD750C63E342E24`,APP JSON-RPC `192.168.0.154:8000`,bootloader TCP `192.168.0.154:9001`,串口 `921600` |
|
||||
| `41-MASTER` | `projects/41-00426-20-Controller-Code/Master_Controller/SDK/GD32470Z/Projects/26_ENET/Projects/FreeRTOS_tcpudp/MDK-ARM/Project.uvprojx` | `MASTER`, `SLAVE`, `DEVELOP` | GD32F470,通常 probe `123456789ABCDEF`,JSON-RPC `192.168.0.151:8000`,串口 `115200` |
|
||||
| `71-ACMOD` | `projects/41-00426-20-Controller-Code/Slave_AC_Module/code/MDK-ARM/STM32H723ZGT6.uvprojx` | `STM32H723ZGT6` | STM32H723,必须显式选择 H7 probe,JSON-RPC `192.168.0.152:8000`,串口 `115200` |
|
||||
|
||||
probe 和 COM 口是现场硬件状态,不是永久事实。下载前必须先跑 `keil list-devices -p <project.uvprojx>`;H7 目标还要跑 `keil detect -u <probe_uid> -p <project.uvprojx> -t <target>`。41/GD32 的 pyOCD `detect` 预期会因为 pyOCD 不支持该 target 而失败;41 下载应走 Keil UV4 工程链,并通过串口或 `board-comm` 验证。
|
||||
|
||||
## 编译与下载模式
|
||||
|
||||
所有命令都显式传 `project`、`target` 和 `probe`,不要依赖 Keil 当前 target 或隐式 probe 选择。
|
||||
`program` / `flash` 会改写真实目标板;只摸清流程或做文档核查时,先停在 `project targets`、`list-devices`、`detect` 和 `build`,确认目标板、probe、串口和期望固件后再执行下载命令。
|
||||
|
||||
```bash
|
||||
# 查看 target 与 probe 绑定。
|
||||
keil project targets -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx
|
||||
keil list-devices -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx
|
||||
keil detect -u 3FD750C63E342E24 -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx -t FREQ_Controller_FW
|
||||
|
||||
# 只编译。
|
||||
keil build --wait -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx -t FREQ_Controller_FW
|
||||
|
||||
# 编译后下载,或下载已编译 target。
|
||||
keil flash --wait -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx -t FREQ_Controller_FW -m daplink --program-backend pyocd -u 3FD750C63E342E24
|
||||
keil program --wait -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx -t FREQ_Controller_FW -m daplink --program-backend pyocd -u 3FD750C63E342E24
|
||||
|
||||
# 不重烧,只复位运行。
|
||||
keil reset-run --wait -p projects/71-00075-11/FirmWare/MDK-ARM/FREQ_Controller_FW.uvprojx -t FREQ_Controller_FW -u 3FD750C63E342E24
|
||||
```
|
||||
|
||||
41/GD32 优先使用 Keil UV4 工程 backend,不要把 pyOCD generic 失败当成固件失败:
|
||||
|
||||
```bash
|
||||
keil build --wait -p projects/41-00426-20-Controller-Code/Master_Controller/SDK/GD32470Z/Projects/26_ENET/Projects/FreeRTOS_tcpudp/MDK-ARM/Project.uvprojx -t MASTER
|
||||
keil program --wait -p projects/41-00426-20-Controller-Code/Master_Controller/SDK/GD32470Z/Projects/26_ENET/Projects/FreeRTOS_tcpudp/MDK-ARM/Project.uvprojx -t MASTER -m daplink --program-backend keil -u 123456789ABCDEF
|
||||
```
|
||||
|
||||
## 运行态验证
|
||||
|
||||
`program`、`flash` 或 `reset-run` 前先打开串口抓取,启动日志确认网络和 JSON-RPC ready 后再用 `board-comm` 主动请求。
|
||||
|
||||
```bash
|
||||
serial-monitor ports
|
||||
serial-monitor monitor start -p COM5 -b 115200
|
||||
serial-monitor fetch --session-only --no-dedup -l 100
|
||||
|
||||
board-comm jrpctcp --host 192.168.0.154 --port 8000 get api
|
||||
board-comm jrpctcp --host 192.168.0.154 --port 8000 get system/status
|
||||
```
|
||||
|
||||
编译或下载成功只证明工具阶段完成。固件任务交付证据应同时包含 Keil 结果、新鲜串口启动证据,以及固件暴露 JSON-RPC 时与目标身份匹配的 `board-comm` 响应。
|
||||
|
||||
## ConStart 内部文档
|
||||
|
||||
项目特有规则继续回到 ConStart 仓库:
|
||||
|
||||
- `AGENTS.md`:项目索引和术语约定。
|
||||
- `docs/reference/workspace/keil_skill_usage.md`:Keil skill 使用和工程文件维护约束。
|
||||
- `docs/reference/workspace/serial_monitor_skill_usage.md`:串口证据和会话抓取规则。
|
||||
- `docs/reference/workspace/board_comm_skill_usage.md`:JSON-RPC 主动通信规则。
|
||||
- `docs/reference/workspace/wsl_development_on_d601.md`:D601 WSL 开发注意事项。
|
||||
- `docs/reference/projects/71-00075-11/entry_and_build.md` 与 `docs/reference/projects/71-00075-11/TEST.md`:`71-FREQ` target、产物和验收入口。
|
||||
@@ -8,14 +8,15 @@
|
||||
- `backend-core` 是无状态核心服务,提供 Docker 内网 REST API、provider ingress WebSocket、任务调度入口和数据库访问层。
|
||||
- `frontend` 是唯一公开 Web 控制台,提供登录、从 TSX 转译出的 React 应用资产和到 backend-core 的同源代理。
|
||||
- `provider-gateway` 是当前主 server 的本机计算节点代理,通过 WebSocket 主动连到 provider ingress,挂载 `/var/run/docker.sock` 作为自动任务执行主路径,使用 `pid: "host"` 读取节点级进程资源,并周期性上报系统资源指标、进程占用与 Docker daemon 状态;维护用 Host SSH / WSL SSH 私钥目录只读挂载到 `/run/host-ssh`,不得作为自动任务调度主路径。
|
||||
- `todo-note` 是主 server 承载的 Todo Note 纯后端 microservice,容器名 `todo-note-backend`,只在 Compose 内网暴露 `4211/tcp`,使用主 PostgreSQL 存储迁移后的 Todo Note 数据。
|
||||
- `codex-queue` 是主 server 承载的 Codex app-server 队列 microservice,容器名 `codex-queue-backend`,仅在 Compose 内网暴露 `4222/tcp` 给本机 provider-gateway 私有代理访问,浏览器只能通过 UniDesk frontend 同源代理查看运行输出、追加 prompt、打断和重试。
|
||||
- `todo-note` 是主 server 承载的 Todo Note 纯后端用户服务,容器名 `todo-note-backend`,只在 Compose 内网暴露 `4211/tcp`,使用主 PostgreSQL 存储迁移后的 Todo Note 数据。
|
||||
- `codex-queue` 是主 server 承载的 Codex app-server 队列用户服务,容器名 `codex-queue-backend`,仅在 Compose 内网暴露 `4222/tcp` 给本机 provider-gateway 私有代理访问,任务状态优先写入主 PostgreSQL 并保留 `.state/codex-queue/state.json` fallback 快照,浏览器只能通过 UniDesk frontend 同源代理查看运行输出、追加 prompt、打断和重试。
|
||||
- `project-manager` 是主 server 承载的项目管理用户服务,容器名 `project-manager-backend`,仅在 Compose 内网暴露 `4233/tcp`,项目清单写入主 PostgreSQL,浏览器只能通过 UniDesk frontend 同源代理执行增删改查、Excel 导入和 Excel 导出。
|
||||
|
||||
## Public Exposure Boundary
|
||||
|
||||
Docker Compose 只能向公网暴露两个接口:frontend host port 和 provider ingress host port。backend-core REST API 和 PostgreSQL database 必须只在 Docker 内部网络中可达,不允许映射到宿主机公网端口;浏览器访问 core API 必须通过 frontend 的同源代理完成。
|
||||
|
||||
计算节点上的 microservice 后端也遵守同一边界:业务容器端口只绑定节点本机地址,并由 provider-gateway 主动连出 WebSocket 后接受 backend-core 的 `microservice.http` 调度访问。主 server 不为 microservice 新增公网反向代理端口;最终用户只通过 UniDesk frontend 的 React 页面访问结构化业务控件。
|
||||
计算节点上的用户服务后端也遵守同一边界:业务容器端口只绑定节点本机地址,并由 provider-gateway 主动连出 WebSocket 后接受 backend-core 的 `microservice.http` 调度访问。主 server 不为用户服务新增公网反向代理端口;最终用户只通过 UniDesk frontend 的 React 页面访问结构化业务控件。
|
||||
|
||||
## Docker Compose Runtime
|
||||
|
||||
@@ -25,19 +26,19 @@ Compose v2 安装后仍然必须遵守 UniDesk 的服务控制入口:全栈生
|
||||
|
||||
## Start And Stop
|
||||
|
||||
`bun scripts/cli.ts server start` 与 `bun scripts/cli.ts server stop` 都是异步 job。启动 job 会先清理固定 Compose project 的旧容器,再重新构建并启动,避免主 server 上残留旧容器或旧镜像配置。启动和停止流程禁止删除 Docker named volume。
|
||||
`bun scripts/cli.ts server start` 与 `bun scripts/cli.ts server stop` 都是异步 job。启动 job 只执行固定 Compose project 的 `up -d --build --remove-orphans`,不得先 `down`,避免在 provider-gateway 旧容器或网络冲突时把 `codex-queue-backend` 等长任务容器先删掉又启动失败;停止 job 才允许执行 `down --remove-orphans`。启动和停止流程都禁止删除 Docker named volume。所有会改变主 server Compose 状态的 job 必须通过 `.state/locks/server-compose.lock` 串行化;`server rebuild codex-queue && server rebuild frontend` 这类连续命令只代表连续创建异步 job,不能代表第一个 job 已结束,实际容器变更仍必须由 Compose lock 串行执行。
|
||||
|
||||
## Single Service Rebuild
|
||||
|
||||
前端、backend-core、本机 provider-gateway 或主 server 承载的 Todo Note/Codex Queue microservice 需要重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core`、`frontend`、`provider-gateway`、`todo-note` 或 `codex-queue`。该命令先执行目标服务镜像构建,只有构建成功后才移除旧容器,避免构建失败导致运行中的服务被提前停掉。
|
||||
前端、backend-core、本机 provider-gateway 或主 server 承载的 Todo Note/Codex Queue/Project Manager 用户服务需要重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core`、`frontend`、`provider-gateway`、`todo-note`、`codex-queue` 或 `project-manager`。该命令先执行目标服务镜像构建,构建成功后才通过 `up -d --no-deps --force-recreate <service>` 替换目标容器,避免构建失败导致运行中的服务被提前停掉。
|
||||
|
||||
单服务重建必须按 Docker Compose label 精确选择旧容器:`com.docker.compose.project` 等于 `config.json` 中的固定 project name,`com.docker.compose.service` 等于目标服务名。删除范围不得扩大到其他 Compose project、database 容器、named volume 或未匹配 label 的容器;随后必须通过 CLI 解析出的 Compose 命令执行 `up -d --no-deps <service>` 启动目标服务,避免因为重建 frontend 而连带重启 database 或 backend-core。
|
||||
单服务重建必须由 CLI 解析出的 Compose 命令执行,且只能影响目标 service,不得连带重启 database、backend-core 或其他未指定服务。重建后 job 必须按 Docker Compose label 校验目标容器:`com.docker.compose.project` 等于 `config.json` 中的固定 project name,`com.docker.compose.service` 等于目标服务名,并等待容器进入 `healthy`;没有 healthcheck 的服务至少要进入 `running`。如果验证失败,job 必须失败并输出目标容器状态,禁止把“无输出”或“只完成 build”当作成功。
|
||||
|
||||
当前主 server 只安装 Docker Compose v1 时,直接执行 `docker-compose up -d --no-deps --build frontend` 可能走 recreate 路径并触发 `ContainerConfig` 兼容问题。正式流程不得依赖人工 `docker rm` 兜底,而应由 `server rebuild frontend` 固化为可观测 job:build-first、label-scoped remove、no-deps up、保留 named volume。
|
||||
正式流程不得依赖人工 `docker rm` 兜底;手工删除旧容器后若 job、Docker client 或 daemon 在 `up` 前中断,会直接造成 `direct microservice proxy failed`。`server rebuild <service>` 必须是可观测 job:build-first、Compose lock、no-deps force-recreate、post-up validation、保留 named volume。Codex Queue 等长任务服务即使被重建也必须依赖服务自身 restart-recovery 恢复任务,不能用“避免重建”掩盖恢复缺陷。
|
||||
|
||||
## Health Criteria
|
||||
|
||||
服务跑通的最低标准是:backend-core 内网 `/health` 返回 ok,frontend 公网 `/health` 返回 ok,provider ingress 公网 `/health` 返回 ok,database 在容器内 `pg_isready` 可用,Todo Note 后端 `/api/health` 返回 `storage=postgres`,Codex Queue `/health` 返回队列摘要和默认模型,`/api/nodes` 中出现 `main-server` provider 且状态为 `online`,`/api/nodes/system-status` 中出现 `main-server` 的 CPU/内存/硬盘采样,`/api/nodes/docker-status` 中出现 `main-server` 的 Docker 快照。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
|
||||
服务跑通的最低标准是:backend-core 内网 `/health` 返回 ok,frontend 公网 `/health` 返回 ok,provider ingress 公网 `/health` 返回 ok,database 在容器内 `pg_isready` 可用,Todo Note 后端 `/api/health` 返回 `storage=postgres`,Codex Queue `/health` 返回队列摘要、默认模型和 `queue.storage`,Project Manager `/health` 返回 `storage.primary=postgres` 和项目数量,backend-core `/api/performance` 返回性能指标,`/api/nodes` 中出现 `main-server` provider 且状态为 `online`,`/api/nodes/system-status` 中出现 `main-server` 的 CPU/内存/硬盘采样,`/api/nodes/docker-status` 中出现 `main-server` 的 Docker 快照。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
|
||||
|
||||
## Database Volume
|
||||
|
||||
|
||||
@@ -31,19 +31,19 @@ Typical targeted commands:
|
||||
- `bun scripts/cli.ts e2e run --only frontend --skip frontend:todo-note-integrated-visible,frontend:findjob-integrated-visible`
|
||||
- `bun scripts/cli.ts e2e run --only network,provider-ingress`
|
||||
|
||||
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private microservice ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211` and Codex Queue host port `14222` probes must fail.
|
||||
- Core API: `docker exec unidesk-backend-core` calls internal `GET /api/overview`, which must report `dbReady: true`, `pgdata.volumeName=unidesk_pgdata_10gb`, a positive PostgreSQL database byte count, and at least one online node.
|
||||
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private user-service ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211` and Codex Queue host port `14222` probes must fail.
|
||||
- Core API: `docker exec unidesk-backend-core` calls internal `GET /api/overview`, which must report `dbReady: true`, `pgdata.volumeName=unidesk_pgdata_10gb`, a positive PostgreSQL database byte count, and at least one online node; internal `GET /api/performance` must report component request statistics, internal operation statistics, PGDATA usage and Codex Queue PostgreSQL storage metadata.
|
||||
- Provider self-connection: internal `GET /api/nodes` must contain `main-server` with `status: online`, `labels.providerGatewayVersion` equal to `src/components/provider-gateway/package.json` and `labels.providerGatewayUpgradePolicy: "always-enabled"`; internal `GET /api/nodes/system-status` must contain CPU/memory/disk samples plus a non-empty process resource list sorted by memory by default; internal `GET /api/nodes/docker-status` must contain a Docker snapshot for `main-server`; public provider ingress `/health` must return ok.
|
||||
- Provider remote control: internal `/api/dispatch` must successfully complete a real `provider.upgrade` task in `mode: "plan"` so the upgrade path is validated without recreating the running gateway during E2E.
|
||||
- Microservices: internal `/api/microservices` must include `todo-note` and `codex-queue` on `main-server` plus `findjob`, `pipeline` and `met-nonlinear` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/codex-queue/health` must return a Codex Queue summary with default model `gpt-5.4-mini`, and `/api/microservices/codex-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=<projectPath>` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status.
|
||||
- User services: internal `/api/microservices` must include `todo-note` and `codex-queue` on `main-server` plus `findjob`, `pipeline` and `met-nonlinear` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/codex-queue/health` must return a Codex Queue summary with default model `gpt-5.5`, and `/api/microservices/codex-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=<projectPath>` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status.
|
||||
- Database: the command writes an `unidesk_e2e_markers` row through `docker exec unidesk-database psql`, confirms provider state is stored in PostgreSQL, and checks Todo Note rows exist in `todo_note_instances` using the same named volume.
|
||||
- Pipeline OA event flow: `microservice:pipeline-oa-event-flow` must prove both no-audit and monitor-audit runs are driven by OA events end to end. The event stream must show `node-finished` as a neutral fact with `pipeline:{pipelineId}` and `epoch:{runId}` tags, OA policy as the source of downstream/audit decisions, monitor decisions as OA control events, and runner control-result evidence. E2E must fail if delivery still depends on a legacy detail audit policy flag as policy authority, independent legacy audit-request points, a legacy batch completion gate, direct monitor-to-runner calls, or frontend/CLI writes to Pipeline `.state`.
|
||||
- The same Pipeline OA diagnostics must fail on legacy file-transport residuals. Procedure containers, monitor sessions, UI/Gantt DTO builders and CLI fetches must consume prompt/control/stop/display evidence only from the OA event ledger and normalized HTTP read APIs; `control-prompts.jsonl`, `monitor-prompts.jsonl`, `monitor-control`, `control-events.jsonl`, monitor stop files, `.state/pipeline-runs/{runId}/control/commands/`, `PIPELINE_*_APPEND_FILE`, local JSONL append/read helpers, and monitor `/pipeline-state` mounts are forbidden in runtime source.
|
||||
- Pipeline live Gantt setup: when `frontend:pipeline-gantt-observation-live-running` is selected, E2E first looks for a current Pipeline run that already contains both a `node-long-running-observation` marker and a still-running execution interval. If no such candidate exists, the E2E setup starts the D601 `monitor-management-behavior-test` pipeline through `bun scripts/cli.ts ssh D601 ...` and polls the private backend proxy until the observation candidate exists; the acceptance assertion itself still opens the public frontend with Playwright and verifies the rendered arrows, absence of observation source pseudo-points, target arrow inset, and live flashing running bar through React DOM controls.
|
||||
- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `微服务 / 服务目录`、`微服务 / Todo Note`、`微服务 / Codex Queue`、`微服务 / FindJob`、`微服务 / Pipeline` and `微服务 / MET Nonlinear` to verify 主 server Todo Note/Codex Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、Codex Queue 队列/模型/输出/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Step Timeline、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证至少一个深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;同时 `态势总览` 这类非微服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`.
|
||||
- Frontend dense-layout regression gate: whenever a frontend change touches Pipeline 右侧边栏、step timeline、详情抽屉或其他高信息密度面板, Playwright acceptance must inspect both `总高度` and `横向滚动条`. For Pipeline specifically, the OpenCode Step Timeline session head must carry shared agent/model/session facts, each step summary must keep time in the header rather than a left narrow column, and Playwright must fail if the timeline or the collapsed step summary introduces an internal horizontal scrollbar or if the collapsed summary height grows beyond a bounded compact threshold.
|
||||
- OpenCode Step Timeline must also prove that idle gaps between adjacent steps remain visually blank. Playwright should fail if `.pipeline-opencode-flow::before` or any equivalent continuous connector draws across the whole timeline, because an interval such as previous `completedAt` to next `createdAt` is not execution activity.
|
||||
- Microservice frontend assertions must wait for real backend data, not only the page skeleton. For Todo Note this means the page must show the migrated lists `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务`, support creating a temporary list and task through the frontend, and delete that temporary list afterwards. The temporary list must be selected again by its unique generated name before deletion so E2E never deletes a migrated source list by accident. For FindJob this means the page must show a numeric `岗位总量`, `HEALTH OK`, and a non-empty `PREVIEW` count such as `40/1463 PREVIEW`; for Pipeline this means the page must show `Pipeline v2 工作台`, `Health OK`, a numeric component count, a non-empty React Flow control graph, `控制图`, `Epoch 甘特图`, and after clicking a Gantt execution line it must show `OpenCode Step Timeline` with role/model/tokens/text/reasoning/tool sections; for MET Nonlinear this means the page must show `MET Nonlinear 训练编排`, `Health OK`, `Fork Project`, `加入待启动队列`, `启动队列`, `当前队列`, 最大并发设置、task queue and GPU/image panels, and must not show the removed hard-coded `创建10个10轮任务` frontend entry. The MET Nonlinear project library must render `projects/` and `ex_projects/` as a true path tree with folder Project counts; clicking a project row must open a structured detail panel containing `config.json`, `data/ 训练状态`, `模型参数`, `指标` and a parameter count such as `Total Params`; clicking a completed/current/failed job row must open a structured job detail and both the row and detail must show `epoch/h`. Full MET Nonlinear acceptance is driven by public frontend controls: choose a visible source Project, set batch size, epochs and max concurrency in inputs, fork into `projects/unidesk_forks/`, stage the selected forks, start the queue, and verify completed rows plus automatic `metnl-train-*` container removal; loading placeholders like `--` or empty states are not sufficient for E2E success.
|
||||
- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, opens `运行总览 / 性能面板` to verify `Bwebui`、组件汇总、最近失败请求、内部操作汇总和最近慢操作, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `用户服务 / 服务目录`、`用户服务 / Todo Note`、`用户服务 / Codex Queue`、`用户服务 / FindJob`、`用户服务 / Pipeline` and `用户服务 / MET Nonlinear` to verify 主 server Todo Note/Codex Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、Codex Queue 队列/模型/输出/初始 `Submitted prompt`/终态任务自动加载完整 Trace/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、MiniMax 限额卡片、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Trace、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证深链接直达路由,例如公网 `http://<publicHost>:<frontendPort>/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;还必须直开 `/app/codex-queue/` 验证页面存在 `app-shell`、左侧主模块边栏、顶部状态栏、顶部子标签和 `codex-queue-page`,防止用户服务 deep link 退化成缺 shell 的 standalone 页面;同时 `态势总览` 这类非用户服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`.
|
||||
- Frontend dense-layout regression gate: whenever a frontend change touches Pipeline 右侧边栏、Trace timeline、详情抽屉、甘特图坐标或其他高信息密度面板, Playwright acceptance must inspect both `总高度` and `横向滚动条`. For Pipeline specifically, the OpenCode Trace session head must carry shared agent/model/session facts and the Trace body must use the same Codex Queue `TraceView` styling; Playwright must fail if old `.pipeline-opencode-step`, `.pipeline-opencode-flow`, `.pipeline-step-message-card` or `.pipeline-opencode-part` user-visible styles reappear, if the Trace container introduces an internal horizontal scrollbar, or if `frontend:pipeline-gantt-frontend-y-accuracy` fails to prove the frontend `frontend-y` layout maps ticks, markers and execution bars from timestamps to y coordinates within tolerance.
|
||||
- OpenCode Trace must use Codex Queue Trace styling and must not render the deprecated Pipeline continuous step connector; Playwright should fail if `.pipeline-opencode-flow`, `.pipeline-opencode-step` or any equivalent continuous connector/card returns to the user-visible Trace.
|
||||
- User service frontend assertions must wait for real backend data, not only the page skeleton. For Todo Note this means the page must show the migrated lists `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务`, support creating a temporary list and task through the frontend, and delete that temporary list afterwards. The temporary list must be selected again by its unique generated name before deletion so E2E never deletes a migrated source list by accident. For FindJob this means the page must show a numeric `岗位总量`, `HEALTH OK`, and a non-empty `PREVIEW` count such as `40/1463 PREVIEW`; for Pipeline this means the page must show `Pipeline v2 工作台`, `Health OK`, a numeric component count, a non-empty React Flow control graph, `控制图`, `Epoch 甘特图`, and after clicking a Gantt execution line it must show `OpenCode Trace` rendered by the shared Codex Queue-style Trace component with messages and tool-call groups; for MET Nonlinear this means the page must show `MET Nonlinear 训练编排`, `Health OK`, `Fork Project`, `加入待启动队列`, `启动队列`, `当前队列`, 最大并发设置、task queue and GPU/image panels, and must not show the removed hard-coded `创建10个10轮任务` frontend entry. The MET Nonlinear project library must render `projects/` and `ex_projects/` as a true path tree with folder Project counts; clicking a project row must open a structured detail panel containing `config.json`, `data/ 训练状态`, `模型参数`, `指标` and a parameter count such as `Total Params`; clicking a completed/current/failed job row must open a structured job detail and both the row and detail must show `epoch/h`. Full MET Nonlinear acceptance is driven by public frontend controls: choose a visible source Project, set batch size, epochs and max concurrency in inputs, fork into `projects/unidesk_forks/`, stage the selected forks, start the queue, and verify completed rows plus automatic `metnl-train-*` container removal; loading placeholders like `--` or empty states are not sufficient for E2E success.
|
||||
|
||||
## Frontend JSON Rule
|
||||
|
||||
@@ -53,7 +53,7 @@ Remote update records in the frontend are covered by the same rule: `provider.up
|
||||
|
||||
Provider operation availability is also covered by the structured rendering rule. `host.ssh` availability must be displayed as badges or equivalent controls derived from capabilities and `hostSsh*` labels, and remote update availability must be displayed from `provider.upgrade` capability plus the `always-enabled` policy; these fields must not require opening raw Provider JSON.
|
||||
|
||||
Microservice pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `Codex Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode step timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; the full microservice config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click.
|
||||
User service pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `Codex Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode Trace timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; the full user-service config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click.
|
||||
|
||||
## Public Boundary Rule
|
||||
|
||||
|
||||
+30
-21
@@ -6,28 +6,34 @@ UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效
|
||||
|
||||
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` 只做导入和路由分发。
|
||||
`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`codex-queue.tsx` 承载 Codex Queue 控制台;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。
|
||||
|
||||
## Layout
|
||||
|
||||
左侧边栏只切换主模块:运行总览、资源节点、任务调度、微服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,微服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、Codex Queue 只属于微服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。
|
||||
左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、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/`。
|
||||
- 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-safe `id` > 由 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。
|
||||
- frontend Bun server 必须把这些模块前缀下的深链接路由作为 SPA 入口返回同一个 `index.html` 和同一个 UniDesk shell;实现上允许统一把非静态资源路径都回到同一个 shell,但判定标准是公网直开 `/ops/status/`、`/nodes/docker/`、`/app/pipeline/`、`/app/codex-queue/` 等深链接时都不得 404,且必须和从主页导航进入时一样显示左侧主模块边栏、顶部状态栏、顶部子标签和完整业务页面。禁止为某个用户服务 deep link 返回缺少 UniDesk shell 的独立/standalone bundle;新增应用服务、普通页面和性能优化入口也必须满足“直开 URL 与站内导航同壳同页”的一致性要求。
|
||||
|
||||
## Overview Task Drilldown
|
||||
|
||||
`态势总览` 中的 `待处理任务` 指标必须可点击进入任务调度的 `待处理任务` 子标签,展示具体 queued、dispatched、running 任务的状态、Provider、已等待时间、payload 摘要和显式 `查看原始JSON` 操作。总览不得只给出无法追溯的数字;当后台把超时未终态任务转为 failed 后,待处理指标应回落,历史记录仍可在任务历史和执行结果中查看。核心指标还必须展示 `PGDATA`,显示 PostgreSQL 当前数据库用量、命名卷 `unidesk_pgdata_10gb` 和配置容量,便于从总览判断数据库状态。
|
||||
|
||||
## Performance Panel
|
||||
|
||||
`运行总览 / 性能面板` 固定路由为 `/ops/performance/`,用于汇总 UniDesk 控制面的通用性能指标。页面必须参考服务端性能看板形态,包含 `Bwebui` 内存/Bundle 趋势图、组件汇总、最近失败请求、内部操作汇总和最近慢操作。组件汇总至少展示组件名、请求数、失败数、失败率、平均延迟和 P95;最近失败请求必须在没有失败时明确显示“最近没有失败请求”,不能留空;内部操作汇总和慢操作必须展示服务、操作名、耗时、结果和细节。
|
||||
|
||||
性能面板的数据来自两个同源端点:frontend 自身的 `/api/frontend-performance` 记录 webui 静态资源、登录/session 和 API 代理请求,backend-core 的 `/api/performance` 记录 core API、用户服务代理、provider ingress、数据库与内部操作。页面只展示聚合表和趋势图,完整原始指标只能通过 `查看原始JSON` 打开。Bwebui 曲线优先使用浏览器 `performance.memory.usedJSHeapSize`,不可用时回退到 frontend bundle size 或 frontend 进程 heap,用 MB 作为纵轴口径。
|
||||
|
||||
## 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` 展开。
|
||||
@@ -52,21 +58,24 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
|
||||
|
||||
`资源监控` 子标签中的升级控制区通过 backend-core `/api/dispatch` 下发 `provider.upgrade` 任务。默认 `预检升级` 只生成升级计划并回传任务结果;`执行升级` 才允许调度节点本地 updater 容器执行 Compose 重建。前端只展示结构化任务状态、task id、摘要和当前节点的远程更新记录,完整升级计划必须通过 `查看原始JSON` 显式查看。
|
||||
|
||||
## Microservice Frontend
|
||||
## User Service Frontend
|
||||
|
||||
- `微服务` 主模块用于展示挂载在计算节点或主 server Docker 中的业务后端。
|
||||
- `用户服务` 主模块用于展示挂载在计算节点或主 server Docker 中的业务后端。
|
||||
- `服务目录` 必须显示 service id、Provider、仓库 URL、commit id、业务 Dockerfile/docker-compose 引用、节点后端私有映射、SSH 透传开发入口和运行态容器摘要。
|
||||
- `Todo Note` 子标签必须把主 server `todo-note-backend` 后端渲染为 UniDesk React 控件,包括迁移清单、树形任务、筛选、提醒、拖放/移动、撤销/重做、字号控制和显式原始 JSON 按钮。
|
||||
- `FindJob` 子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。
|
||||
- `Codex Queue` 子标签必须把主 server `codex-queue-backend` 后端渲染为 UniDesk React 控件,包括串行队列、任务提交/批量提交、模型下拉、显式入队份数、默认模型 `gpt-5.4-mini`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。
|
||||
- 业务 microservice 页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把 microservice 后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。
|
||||
- `ClaudeQQ` 子标签必须把 D601 ClaudeQQ 后端渲染为 UniDesk React 控件,包括 NapCat 容器登录二维码、NapCat HTTP/WS 状态、事件缓存、QQ 事件订阅表、订阅创建表单、消息推送表单、主用户私聊账号 `645275593` 标记、最近 QQ 事件、已发送记录和显式原始 JSON 按钮。
|
||||
- `Codex Queue` 子标签必须把主 server `codex-queue-backend` 后端渲染为 UniDesk React 控件,包括多 queue lane、queue 内串行、queue 间并行、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Codex 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 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Codex Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,但必须在初始消息和 Prompt 全量面板提供可展开的“最终传入 Codex 的真实完整 prompt”;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task <taskId>` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。
|
||||
- `Codex Queue` 的 queue/session 左侧边栏必须采用顶部对齐和内容高度优先布局:列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。
|
||||
- 用户服务页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把用户服务后端端口暴露为浏览器直连 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 用户服务只提供 backend/control API,UniDesk 通过 `/api/microservices/pipeline/proxy/...` 拉取 snapshot、Gantt DTO、node detail 和控制接口。
|
||||
- Pipeline 页面必须通过同源用户服务代理展示 `model/minimax-m27` 的 MiniMax 限额,包括当前窗口总量/已用/剩余、重置时间和查询状态;主界面不得展示 API key,完整 quota JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
- Pipeline 控制与观测的最终权威是 100% OA 事件流;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,完整规则见 `docs/reference/pipeline-oa-event-flow.md`。
|
||||
- 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。
|
||||
- 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。首屏信息顺序必须把核心操作前置:`控制图` 是 Pipeline hero 之后的第一个业务面板,`Epoch 甘特图` 紧随其后;观测指标、评分器、MiniMax 限额、OA 事件流和组件矩阵只能排在这两个核心面板之后,移动端也必须保持同一 DOM 顺序。控制图和甘特图的右侧详情栏默认收起,主图占满可用宽度;点击控制图 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。
|
||||
- 用户点击控制图中的 node 后,必须通过同源用户服务代理抓取该 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` 表示前端来源。
|
||||
@@ -80,14 +89,14 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
|
||||
- 甘特图必须提供时间尺度滑块,用同一份时间数据调整每分钟像素密度:全局尺度压缩纵向高度以查看完整 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 epoch 甘特图的 y 坐标权威在 UniDesk 前端完成,后端只提供 run、procedure、prompt/control 事件和时间戳事实,避免跨 provider HTTP 代理在后端反复计算布局。
|
||||
- 前端必须用同一线性公式把 `startMs/endMs/tick.ms/marker.ms` 映射到 `layout.source=frontend-y` 的 `chartHeight`:`y = clamp((ms - startMs) / (endMs - startMs), 0..1) * chartHeight`;执行区间 `top` 使用 `startMs`,自然高度使用 `endMs-startMs` 对应 y 差,短区间可设最小可点击高度但不得改变记录的 `data-y1/data-y2`。
|
||||
- 时间尺度滑块只改变前端每分钟像素密度和 `chartHeight`,不得因为缩放重新请求后端 Gantt 布局;run 级过程数据使用轻量 timeline 视图刷新。
|
||||
- 甘特图必须根据当前可见的前端 y 区间自动隐藏该窗口内没有任何工作区间或事件点的 node 列,避免宽图把无关空闲 node 挤在屏幕中。
|
||||
- E2E 必须验证前端 DOM 暴露的 `data-start-ms/data-end-ms/data-chart-height/data-y*` 与公式计算结果一致,并确认布局来源为 `frontend-y`。
|
||||
- `Pipeline` 甘特图事件来源。
|
||||
- 甘特图上的执行线、prompt 点、控制点和 monitor 虚线箭头必须通过同源 `node-control` HTTP 读取接口驱动。
|
||||
- run 级图形数据使用 `GET /api/node-control/runs/{runId}?view=gantt&scale=0..100`。
|
||||
- run 级图形事实数据使用 `GET /api/node-control/runs/{runId}?view=timeline&tail=N`;该接口不得要求返回后端 y 坐标。
|
||||
- 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`。
|
||||
@@ -103,11 +112,11 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL
|
||||
- `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 必须折叠到展开层。
|
||||
- 点击甘特图中的执行线、prompt 点或控制点后,右侧边栏必须从默认收起状态展开,并展示结构化事件字段、匹配的 procedure/attempt、以及对应 OpenCode step 的摘要与展开详情,而不是在主界面直接铺 raw JSON、JSONL、worker log 或 control event 文本。
|
||||
- OpenCode step/message 展示必须进入公共 `TraceView`,视觉与交互以 `Codex Queue` 的 Trace 为唯一标准;Pipeline 原有 `pipeline-opencode-step`、`pipeline-step-message-card`、`pipeline-opencode-part` 等 step/message/tool 卡片风格已废弃,不得继续作为用户可见 Trace。
|
||||
- 右侧边栏中的 OpenCode Trace 必须把公共 session 信息(agent、model、session id)聚合到 Trace 头部,不得在每个 step 重复;Trace 正文必须由 `src/components/frontend/src/trace.tsx` 的 opencode port 转换后统一渲染,工具调用折叠、摘要、横向滚动、message 去缩进规则与 Codex Queue 完全一致。
|
||||
- 右侧边栏排版必须优先保护横向可读宽度:时间放在 step 顶部 header,而不是单独占用左侧窄列;默认摘要不得引入右侧边栏内部横向滚动条,也不得因为窄列挤压把 step 高度拉得过高。
|
||||
- OpenCode Step Timeline 不能使用跨越所有 step 的连续装饰线;相邻 step 之间若存在真实时间空闲区间,例如上一个 step `completedAt` 到下一个 step `createdAt`,该区间必须视觉留白,不能被误渲染为持续执行条线。
|
||||
- OpenCode Trace 不能使用 Pipeline 旧连续 step 装饰线或旧 step 卡片;相邻 step 之间若存在真实时间空闲区间,不得被任何连续连接线误渲染为持续执行。
|
||||
- 调整任何高信息密度右侧边栏布局时,都必须把 `总高度` 与 `横向滚动条` 作为显式验收指标,用 Playwright 打开真实页面验证,而不是只看静态代码或本地想象。
|
||||
- 运行材料只能作为结构化索引行展示计数、状态、时间和来源摘要,完整 JSON、JSONL 或 log tail 只能通过显式 `查看原始JSON` 按钮打开。
|
||||
- `Pipeline` 渲染与算法验证。
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
# UniDesk Microservices Reference
|
||||
# UniDesk User Services Reference
|
||||
|
||||
UniDesk microservice 是挂载到主 server 控制面的非核心业务后端。业务容器运行在计算节点 Docker 中,主 server 只保存仓库引用、commit id、Dockerfile/docker-compose 引用、provider 映射和前端集成配置,不把业务仓库整体复制进 UniDesk。
|
||||
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
|
||||
|
||||
- microservice 后端端口默认只绑定计算节点本机地址,例如 `127.0.0.1:<port>`,不得直接暴露公网。
|
||||
- 用户服务后端端口默认只绑定计算节点本机地址,例如 `127.0.0.1:<port>`,不得直接暴露公网。
|
||||
- 浏览器只访问 UniDesk frontend;frontend 通过同源 `/api/microservices/*` 代理到 backend-core,backend-core 再通过目标 provider-gateway 的 `microservice.http` 能力访问计算节点本机后端。
|
||||
- backend-core REST API、database 和计算节点 microservice 后端都不得新增公网端口;公网入口仍只有 frontend 和 provider ingress。
|
||||
- `microservice.http` 只允许 provider-gateway 访问 `http://127.0.0.1`、`http://localhost`、`http://host.docker.internal` 这类节点本地地址;主 server 内置 microservice 可使用同一 Compose 网络内的显式服务名,例如 `todo-note:4211` 或 `codex-queue:4222`。backend-core 还必须用 `allowedPathPrefixes` 和 `allowedMethods` 同时限制可代理路径和 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` 是 microservice 的唯一登记来源。每个条目必须包含:
|
||||
`config.json` 的 `microservices` 是用户服务的唯一登记来源。每个条目必须包含:
|
||||
|
||||
- `id`、`name`、`providerId` 和 `description`,用于 CLI、backend-core 和 frontend 统一识别。
|
||||
- `repository.url` 与 `repository.commitId`,用于记录业务代码的外部权威来源;UniDesk 不 vendoring 业务全量代码。
|
||||
@@ -22,17 +24,17 @@ UniDesk microservice 是挂载到主 server 控制面的非核心业务后端。
|
||||
|
||||
## 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 透传进入计算节点,在计算节点本地业务仓库中开发、构建和调试;开发完成后,只把业务服务以 microservice 形式登记到 UniDesk。
|
||||
主 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 Microservices
|
||||
## Main Server User Services
|
||||
|
||||
主 server 只承载对统一入口、状态迁移或控制面自动化有明确必要的 microservice。该类服务仍遵守不暴露公网端口、前端统一 React 控件化展示的规则;业务持久状态优先写入主 PostgreSQL,控制队列这类运行态可使用 `.state/` 文件并必须提供 `/logs` 与结构化状态端点。
|
||||
主 server 只承载对统一入口、状态迁移或控制面自动化有明确必要的用户服务。该类服务仍遵守不暴露公网端口、前端统一 React 控件化展示的规则;业务持久状态优先写入主 PostgreSQL,控制队列这类运行态可使用 `.state/` 文件并必须提供 `/logs` 与结构化状态端点。
|
||||
|
||||
### Todo Note On Main Server
|
||||
|
||||
当前 Todo Note 作为 `id=todo-note` 的 microservice 登记在 `config.json`:
|
||||
当前 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`。
|
||||
@@ -40,7 +42,7 @@ UniDesk microservice 是挂载到主 server 控制面的非核心业务后端。
|
||||
- 部署引用:`/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 按钮。
|
||||
- UniDesk 前端:`用户服务 / Todo Note` React 页面负责展示清单列表、树形任务、筛选、提醒、拖放/上移下移、撤销/重做、字号控制和显式原始 JSON 按钮。
|
||||
|
||||
Todo Note 在 UniDesk 语境中按纯后端服务管理:不得继续公开 Todo Note 自身 Vite/Web 前端,也不得把 `4211` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/todo-note/...` 同源代理访问 Todo Note 后端。
|
||||
|
||||
@@ -48,50 +50,69 @@ Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网
|
||||
|
||||
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` service,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 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` 的 microservice 登记在 `config.json`:
|
||||
当前 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` service,Dockerfile 为 `src/components/microservices/codex-queue/Dockerfile`,容器名为 `codex-queue-backend`。
|
||||
- Codex 认证:容器只从主 server 的 `/root/.codex/config.toml` 同步 Codex provider 配置到 `.state/codex-queue/codex-home`,并通过运行时环境透传 `OPENAI_API_KEY`、`CRS_OAI_KEY` 等 provider 所需变量;新增 provider 的 `env_key` 时必须增加同类运行时透传,禁止把 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;不得把私钥复制进镜像或仓库。
|
||||
- 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。
|
||||
- 队列语义:`POST /api/tasks` 或 `/api/tasks/batch` 入队,服务始终只运行一个 Codex turn;当前任务真正终止后才推进下一个任务。`GET /api/tasks` 与 `GET /api/tasks/{id}` 返回队列、attempt、judge 和输出;`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 异常让后续 queued 任务停止;当存在 queued/retry_wait 且 worker 空闲时,watchdog 必须自动重新调度。
|
||||
- 用户输入持久化:任务初始 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 与 fallback 快照恢复,并在已有 `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-M2.7` 判定 `complete|retry|fail`,否则使用 fallback 规则。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。只有 judge 判定 `complete` 后,队列 worker 才把当前任务标为成功并推进下一个 queued/retry_wait 任务。
|
||||
- Judge 探针:`GET|POST /api/judge/probe` 使用同一套 judge 逻辑跑内置 synthetic execution records,覆盖正常完成、正常结束但只给计划、传输中断和用户打断四类样本,返回 `hits`、`total`、`hitRate`、每例 `expected` 与 `decision`;该接口不得回显 MiniMax API key。
|
||||
- 模型选择:默认 Codex 模型是 `gpt-5.4-mini`,内置模型队列包含 `gpt-5.4-mini`、`gpt-5.4`、`gpt-5.5`;每个入队任务可通过前端模型下拉菜单或 API 覆盖 `model`、`cwd`、`reasoningEffort` 和 `maxAttempts`。
|
||||
- 状态与日志:默认工作目录为容器内 `/root/unidesk`,该路径映射主 server 的 `~/unidesk`;同时保留 `/workspace` 映射以兼容历史任务。队列状态保存在 `.state/codex-queue/state.json` 对应的容器挂载路径,日志写入 UniDesk `logs/{YYYYMMDD}/..._codex-queue.jsonl`,`/logs` 端点返回最近结构化日志。`/health` 的 `queue.devReady` 和 `/api/dev-ready` 必须暴露 develop-ready 自检,包括必需工具、Docker socket、`docker compose`、默认工作目录、Codex config 状态和 `/root/.ssh` 共享 SSH key 状态。Codex CLI-like 输出可能很大,服务必须对输出条数设上限并节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致 `/health` 和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程。
|
||||
- 模型选择:默认 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` 映射以兼容历史任务。队列任务的权威持久化优先写入主 PostgreSQL 表 `unidesk_codex_queue_tasks`,包含状态索引字段和 task 热状态;`.state/codex-queue/state.json` 仅作为本地恢复快照和 PostgreSQL 不可用时的 fallback,服务启动时必须合并 PG 与文件快照并把 running/judging 任务恢复为 retry_wait。Codex CLI-like output/Trace 的完整记录必须同步写入 `.state/codex-queue/output-archive/*.jsonl`,`/api/tasks/<id>/transcript` 与 `/api/tasks/<id>/output` 必须从 archive 分页重建完整历史,不得因为热状态裁剪而丢失早期 trace;热 task JSON 只保留可配置窗口(默认 600 条 output、400 条 event)以保证 `/health`、`/api/tasks` 和 PostgreSQL flush 不被长任务拖死。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue,并支持把已创建且非 active 的任务移动到其他 queue;queue 内串行,queue 间并行。Codex Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,保证队列任务能直接执行公网 frontend Playwright 回归,不得只在宿主机临时安装。日志写入 UniDesk `logs/{YYYYMMDD}/..._codex-queue.jsonl`,`/logs` 端点返回最近结构化日志。`/health` 的 `queue.storage`、`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` 配置。通知必须异步发送,失败或重试只能写 warn 日志,不能阻塞队列继续推进;`/health` 的 `queue.notifications.claudeqq` 必须暴露非敏感配置与是否已配置目标。
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`。Codex Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。
|
||||
- UniDesk 前端:`微服务 / Codex Queue` React 页面负责展示队列卡片、默认模型、模型下拉、显式入队份数、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;连续执行同一 prompt 应使用 `入队份数` 一次性生成多条队列任务,而不是依赖快速连点按钮;原始任务 JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
- 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 call;Codex 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 Microservices
|
||||
## D601 User Services
|
||||
|
||||
当前 `D601` 同时承载以下 UniDesk microservice:
|
||||
当前 `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` 的 microservice 登记在 `config.json`:
|
||||
当前 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 microservice 入口使用。
|
||||
- UniDesk 前端:`微服务 / FindJob` React 页面负责展示指标、岗位预览、草稿报告和原始 JSON 显式查看按钮。
|
||||
- 代理路径:只允许 `/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` 的 microservice 登记在 `config.json`:
|
||||
当前 Pipeline v2 作为 `id=pipeline` 的用户服务登记在 `config.json`:
|
||||
|
||||
- Provider:`D601`。
|
||||
- 开发工作树:`/home/ubuntu/pipeline`,开发和调试必须通过 UniDesk SSH 透传进入 D601。
|
||||
@@ -99,7 +120,7 @@ FindJob 在 UniDesk 语境中按纯后端服务管理:默认页面不得 ifram
|
||||
- 部署引用:业务仓库自身 `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 按钮。
|
||||
- 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`。
|
||||
|
||||
@@ -107,7 +128,7 @@ Pipeline 的一个 epoch 是同一个 pipeline 从入口到终态完整执行一
|
||||
|
||||
### MET Nonlinear On D601
|
||||
|
||||
当前 MET Nonlinear 作为 `id=met-nonlinear` 的 microservice 登记在 `config.json`:
|
||||
当前 MET Nonlinear 作为 `id=met-nonlinear` 的用户服务登记在 `config.json`:
|
||||
|
||||
- Provider:`D601`。
|
||||
- 开发工作树:`/home/ubuntu/met_nonlinear`,后端、Dockerfile、训练队列和训练容器都必须通过 UniDesk SSH 透传在 D601 开发调试;主 server 本地只允许开发 UniDesk frontend 与代理登记。
|
||||
@@ -116,16 +137,34 @@ Pipeline 的一个 epoch 是同一个 pipeline 从入口到终态完整执行一
|
||||
- 部署引用:业务仓库内 `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。
|
||||
- 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 为 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`。
|
||||
- 节点后端: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`:列出全部 microservice、provider 映射、仓库引用、后端映射和运行态容器摘要。
|
||||
- `bun scripts/cli.ts microservice status findjob`:查看单个 microservice 的配置与运行态。
|
||||
- `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`。
|
||||
@@ -134,33 +173,37 @@ MET Nonlinear 验收必须通过公网 UniDesk frontend 的交互式 UI 完成
|
||||
- `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 后端、队列状态文件和本机 provider-gateway 私有代理链路;写入、追加 prompt 和打断由 frontend 同源代理或直接 HTTP API 发起。
|
||||
- `bun scripts/cli.ts microservice health codex-queue` 与 `bun scripts/cli.ts microservice proxy codex-queue /api/tasks`:验证主 server Codex Queue 后端、PostgreSQL 优先持久化、文件 fallback 快照和本机 provider-gateway 私有代理链路;写入、追加 prompt 和打断由 frontend 同源代理或直接 HTTP API 发起。
|
||||
- `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 页面。
|
||||
`debug dispatch D601 microservice.http --payload-json ...` 仅用于开发调试 provider-gateway 代理能力;正式验收和用户入口应优先使用 `microservice` 命令与 frontend 用户服务页面。
|
||||
|
||||
## Frontend Rules
|
||||
|
||||
microservice 前端必须整合到 `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。
|
||||
用户服务前端必须整合到 `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。
|
||||
|
||||
对于超大业务 JSON,backend-core 可把 `__unideskArrayLimit=<path>:<limit>` 作为 frontend-only 代理参数传给 provider-gateway,由 provider-gateway 在返回前裁剪指定 JSON 数组并写入 `_unidesk.arrayLimits` 元数据。该参数只用于控制 UniDesk 展示预览,不能替代业务后端自身分页 API 的长期设计。CLI 的 `microservice proxy` 还会对超过默认阈值的 body 做二次有界预览,防止人工验证时输出爆炸;只有显式 `--raw` 才允许倾倒完整 body。
|
||||
|
||||
## Verification
|
||||
|
||||
microservice 交付必须同时通过后端、CLI 和公网 frontend 验证:
|
||||
用户服务交付必须同时通过后端、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` 后端;再通过公网 frontend 提交一个 `gpt-5.4-mini` 小任务,确认队列串行推进、输出实时更新、结束后有 judge 判定,且运行中可追加 prompt 或打断。批量验收必须通过公网 frontend 设置 `入队份数=5` 或使用多段 prompt 分隔,一次性入队 5 条任务,并确认 5 条任务按顺序进入 running/judging/succeeded,而不是只运行第一条。
|
||||
- 运行 `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` 或在 PG 不可用时显式显示 fallback 原因;再通过公网 frontend 提交一个 `gpt-5.5` 小任务,确认队列串行推进、输出实时更新、结束后有 judge 判定,且运行中可追加 prompt 或打断。Codex Queue 的重启恢复必须作为验收项:运行中任务存在时重启或重建 `codex-queue-backend` 后,任务必须从持久化状态恢复到可继续执行状态,不能丢失 active task、`promptHistory` 或后续 queued 任务。批量验收必须通过公网 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。
|
||||
- 运行 `bun scripts/cli.ts e2e run`,确认 microservice 相关检查 passed,并确认 Playwright 访问的是公网 `http://74.48.78.17:18081/`。
|
||||
- 登录公网 frontend,进入 `微服务 / 服务目录`、`微服务 / Todo Note`、`微服务 / FindJob`、`微服务 / Pipeline` 和 `微服务 / MET Nonlinear`,确认能看到主 server 与 D601 provider、仓库引用、后端私有映射、Todo Note 迁移清单与树形任务、FindJob 指标和岗位预览、Pipeline 组件矩阵、React Flow 控制图、epoch 列表、epoch 甘特图和运行材料索引、MET Nonlinear 队列/GPU/镜像/Project config/训练历史;Todo Note 页面必须能创建临时清单、添加任务并删除临时清单,删除前必须按唯一临时清单名称重新选中对应行,禁止用未确认的当前 active 清单执行删除,FindJob 页面必须显示真实数字指标、`HEALTH OK` 和非空岗位预览,Pipeline 页面必须显示 `Pipeline v2 工作台`、`Health OK`、组件数、epoch 甘特图和结构化运行材料索引,MET Nonlinear 页面必须显示 `Health OK`、`Fork Project`、`启动队列`、`当前队列`、最大并发设置和 GPU/镜像面板,不能只停留在 loading 骨架;页面默认不得出现裸 JSON、JSONL 或逐行日志。
|
||||
- 在 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 或逐行日志。
|
||||
|
||||
@@ -17,3 +17,9 @@ UniDesk 的可观测性优先级高于静默成功。CLI、服务日志、Docker
|
||||
## Task Liveness
|
||||
|
||||
backend-core 必须把 queued、dispatched、running 视为待处理任务,并通过 `TASK_PENDING_TIMEOUT_MS` 对长时间没有 provider 终态回报的任务做超时处理。超时任务转为 failed,result 中保留 timeout、previousStatus 和 previousResult 摘要,避免 `态势总览` 的待处理数量长期卡住且无法解释。
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
backend-core 必须提供 `/api/performance`,返回滚动窗口内的 HTTP 组件请求统计、最近失败请求、内部操作统计、最近慢操作、进程内存、PGDATA 用量和 Codex Queue PostgreSQL 存储摘要。组件统计必须包含请求数、失败数、失败率、平均延迟和 P95,内部操作统计必须包含服务名、操作名、次数、平均延迟和 P95;失败和慢操作记录必须保留时间、状态、耗时、路径或细节,避免只给汇总数字而无法定位。
|
||||
|
||||
frontend Bun server 必须提供同源 `/api/frontend-performance`,记录 webui 静态资源、登录/session、API 代理和 frontend->core 代理操作耗时。浏览器中的 `运行总览 / 性能面板` 必须把 frontend 与 backend-core 指标合并展示为 Bwebui 曲线、组件汇总、最近失败请求、内部操作汇总和最近慢操作;完整性能 JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
|
||||
@@ -8,9 +8,37 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程
|
||||
- Node 之间不得直接启动、暂停、重试或通知彼此;monitor 不得绕过 OA 直接控制 runner;frontend 不得直接写 Pipeline `.state`、prompt 文件、命令队列或 worker 状态文件。
|
||||
- 事实事件只描述已经发生的事实,策略判断由 OA 加载 Pipeline config/topology 后完成;`node-finished` 不得携带 `legacy audit policy flag`、`nextNodeIds`、`shouldStartDownstream` 这类控制决策字段。
|
||||
- 控制行为必须由 OA 控制/管理事件表达,例如 start downstream、pause、resume、single-step、append prompt、guide、modify、approve、redo/restart、skip、cancel 和 retry。
|
||||
- 如果 procedure 或 monitor 结束后缺少预期 OA 提交,runner 必须把它视为可恢复的 OA contract failure:在重试预算内通过 OA 事件写入明确反馈并启动 fresh attempt,不能直接把一次缺失提交升级成最终失败;预算耗尽后才 fail closed。
|
||||
- UI、CLI、E2E 和调试工具都应消费 OA 归一化读接口或通过 OA 控制 API 写入事件,不得解析 monitor 文本、worker log、runner 私有文件或旧 JSONL 文件来反推控制语义。
|
||||
- 旧的本地文件传输通道必须完全退出控制路径:不得创建、轮询或依赖 `control-prompts.jsonl`、`monitor-prompts.jsonl`、`monitor-control`、`control-events.jsonl`、monitor stop 文件、`.state/pipeline-runs/{runId}/control/commands/` 或给 monitor 容器挂载可读 `/pipeline-state` 来完成 prompt、stop、guide、approve、redo/restart、append 或 UI/Gantt 事件取证。
|
||||
|
||||
## Benchmark Prompt And Skill Boundary
|
||||
|
||||
- Pipeline benchmark 的入口 `task` prompt 必须保持需求导向:描述要恢复的用户可见行为、输入输出和验收目标,不得为了某个 PikaBench 或其他切片在任务 prompt 中写入内部流程控制、精确补丁路线、已知失败符号、采样 item id、隐藏 oracle 细节或上一轮失败诊断。
|
||||
- Dispatch/repair procedure prompt 只能声明角色、作用域、OA 提交、分支规范、通用验证习惯和安全边界;不得在失败后通过追加“定向解题 prompt”把单个 benchmark 的答案固化进 procedure config。
|
||||
- 历史运行中沉淀出的可复用经验必须进入版本化 skill 或机械 quality gate:skill 承载领域经验和源码形态经验,gate 只给出可判定的机器证据。若经验不能泛化为 skill/gate,应视为本轮人工调参,不得计入组件贡献。
|
||||
- 消融实验必须保留干净边界:同一个前门需求 prompt、同一个拓扑和同一个 scorer 下,注入经验型 skill 的 variant 可以使用该 skill;`no-skill` variant 必须能真正移除这部分经验。如果无 skill 仍因 prompt 中残留定向答案而达到满分,该消融结果无效。
|
||||
- 对 PikaBench-4/PikaBench-8 这类 Pipeline benchmark,若满分依赖把 `list.pop`、`max/min` 等具体修复路线直接写入任务/dispatch/repair prompt,应先把这些路线改写为泛化的 PikaPython benchmark skill 经验,再重跑 baseline 与 no-skill 对照。
|
||||
|
||||
## Benchmark Train/Valid Isolation
|
||||
|
||||
- 任何用于迭代优化 Pipeline、skill、gate 或 monitor 的 benchmark 切片,都必须在优化前固定一个不重合的 valid holdout;只看 train 满分不能证明泛化。
|
||||
- PikaBench-4 是当前优先的短循环抗过拟合 benchmark;pipeline config 必须显式声明 `train-banch` 与 `valid-banch`(可保留兼容 alias),分别绑定 `pikabench-4` train scorer/prompt 与 `pikabench-4-valid` scorer/prompt,避免把单一 bench 当作泛化证据。PikaBench-8 只能在 4-item train/valid 先稳定出分后用于更慢的确认。
|
||||
- Train 与 valid 必须使用同一个 pipeline id、同一拓扑、同一模型、同一重试预算、同一 monitor 策略、同一 merge gate 和同一 per-node quality gate;只允许前门任务 prompt 与最终 scorer/benchmark oracle 不同。
|
||||
- Train 运行可以完整取证:trace、OA record、node log、gate evidence、scorer failed case 和 artifact 都可以用于分析和改进通用机制。
|
||||
- Valid 运行在优化期间只能读最终聚合 score,例如 `4/4` 或 `8/8`;不得查看 valid trace、OA record、node log、gate artifact、scorer stdout、failed case id、hidden filter、intermediate file 或生成报告。
|
||||
- 机制贡献或泛化结论必须以 train 和 valid 同时达到目标分作为验收;若 train 达标而 valid 未达标,结论只能写作过拟合或未通过验证,不能继续用 valid 细节调参。
|
||||
|
||||
## Benchmark Execution And Model Quota
|
||||
|
||||
- Benchmark execution depends on develop-ready environment readiness. Large optional dependency payloads used by the benchmark build, such as PikaPython `port/linux/package/lvgl/lvgl`, must be pre-cloned or pre-installed by the referenced environment repo Dockerfile in a cacheable layer. Procedure nodes, evidence gates, and scorers must not clone those dependencies ad hoc during the run, because transient network failures then become false benchmark failures and repeated clones defeat Docker build cache.
|
||||
- If a benchmark stalls or returns a low score because an environment dependency such as LVGL could not be cloned, classify it as infrastructure failure first. The preferred fix is to update the environment repo so the dependency is already present in the develop-ready image and its Dockerfile layer can be reused by subsequent train/valid/evidence builds; shrinking the benchmark denominator is only a diagnostic shortcut after the environment contract is fixed.
|
||||
- MiniMax quota 是 benchmark 调度资源,不是跳过 Pipeline 实跑的理由。MiniMax 当前按约 5 小时窗口刷新;在窗口内不用完的额度会过期浪费,因此有明确实验问题、修复验证或消融对照时,应在额度可用时启动对应 Pipeline,而不是因担心“额度用完”而空等。
|
||||
- 成本意识体现在缩小实验切片、减少无效重复、记录 quota/耗时/attempt 和优先跑最小可判定对照;不得把成本意识解释为不跑 baseline、修复后不重跑或不跑关键消融。
|
||||
- 如果运行中发现 MiniMax quota 已耗尽,应立即转入离线工作:整理已有 scorer/artifact/OA/gate/monitor 证据,定位基础设施与组件边界问题,准备下一轮最小 run plan。等 quota 刷新后必须继续从该 plan 恢复实跑验证。
|
||||
- 如果 MiniMax quota 在运行中的 node 内耗尽,quota sleep/retry 的墙钟时间不得计入该 node 的 active timeout;否则一次 quota 暂停会被错误放大为 pipeline node 超时,阻塞 score 产出。
|
||||
- 消融或 benchmark 报告必须区分“quota 暂停”和“实验终止”:因额度耗尽暂停时,结论只能基于已有结果明确标注为临时分析,不能把未跑对照写成已验证结论。
|
||||
|
||||
## Event Identity And Tags
|
||||
|
||||
- 每条事件必须有稳定 `eventId`、`type`、`createdAt`、`sourceKind`、`sourceId`、`correlationId` 和可选 `causationId`,用于排序、幂等、回放和追踪控制来源。
|
||||
|
||||
@@ -8,7 +8,7 @@ Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴
|
||||
|
||||
## Upgrade Safety Gate
|
||||
|
||||
计算节点 `provider-gateway` 容器的重建和升级权威路径是 `provider.upgrade` 的 `mode: "schedule"`,或 frontend 中等价的显式升级调度。该路径由在线 provider 通过本地 Docker socket 启动 detached updater 容器,让升级动作脱离当前 WebSocket 与 SSH 透传会话的生命周期;重建目标只能是 `provider-gateway` service,并且必须带 `--no-deps` 与 `--force-recreate`,不得牵连 database、backend-core、frontend 或业务 microservice,也不得因为镜像 tag 未变而 no-op。
|
||||
计算节点 `provider-gateway` 容器的重建和升级权威路径是 `provider.upgrade` 的 `mode: "schedule"`,或 frontend 中等价的显式升级调度。该路径由在线 provider 通过本地 Docker socket 启动 detached updater 容器,让升级动作脱离当前 WebSocket 与 SSH 透传会话的生命周期;重建目标只能是 `provider-gateway` service,并且必须带 `--no-deps` 与 `--force-recreate`,不得牵连 database、backend-core、frontend 或业务用户服务,也不得因为镜像 tag 未变而 no-op。
|
||||
|
||||
远程升级必须采用 sleep-and-validate 回滚保护:旧 gateway 在成功调度 updater 后关闭当前 WebSocket 并进入最长 5 分钟的助眠期;updater 先构建新镜像,再用旧容器的环境变量、挂载、网络和 `extra_hosts` 拉起候选 gateway;候选 gateway 必须在日志中出现 `connect_open` 和 register ack 成功,才允许把候选容器 restart policy 改为 `always`、删除旧 gateway、并把候选容器改名为原容器名。候选验证失败时 updater 必须删除候选容器并退出失败,旧 gateway 到达助眠上限后自动重连主 server,形成自动回滚。backend-core 必须在同一 Provider ID 被新 WebSocket 替换后忽略旧 WebSocket 的 close 事件,避免候选已上线后又被旧连接关闭标记为 offline。
|
||||
|
||||
@@ -66,7 +66,7 @@ WSL 节点还应补充一次真实调度验证:向该 `PROVIDER_ID` 下发 `do
|
||||
|
||||
SSH 透传自测是 provider-gateway 部署验收的一部分。目标 Provider 在线后,先确认 frontend 节点清单或 `debug health` 中该节点 labels 显示 `hostSshConfigured=true`、`hostSshKeyPresent=true` 且能力包含 `host.ssh`;再运行 `bun scripts/cli.ts debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000`,任务必须 `succeeded`,result 中 `probeLine` 必须包含 `UNIDESK_SSH_TEST` 且 `exitCode=0`;最后运行 `bun scripts/cli.ts ssh <PROVIDER_ID> hostname`,输出必须是目标宿主或 WSL 的 hostname,进程退出码必须为 0。任何 provider 在线但不声明 `host.ssh` 的状态都只能算未完成部署。
|
||||
|
||||
如果该节点承载 microservice,还必须声明 `microservice.http` capability,并通过 `bun scripts/cli.ts microservice health <id>` 或 remote CLI 等价命令验证 backend-core 能经 provider-gateway 访问节点本机后端。microservice 后端端口不得映射到公网;provider-gateway 只允许代理节点本地 HTTP 地址或主 server 显式 Compose 服务名,业务 API 路径和 HTTP 方法还要受 backend-core `allowedPathPrefixes` 与 `allowedMethods` 限制。
|
||||
如果该节点承载用户服务,还必须声明 `microservice.http` capability,并通过 `bun scripts/cli.ts microservice health <id>` 或 remote CLI 等价命令验证 backend-core 能经 provider-gateway 访问节点本机后端。用户服务后端端口不得映射到公网;provider-gateway 只允许代理节点本地 HTTP 地址或主 server 显式 Compose 服务名,业务 API 路径和 HTTP 方法还要受 backend-core `allowedPathPrefixes` 与 `allowedMethods` 限制。
|
||||
|
||||
自动化验证必须使用 Playwright 访问公网 frontend,而不是在容器内直接调 core API 代替浏览器验收。标准命令是 `bun scripts/cli.ts e2e run`;该命令会让 Playwright 打开公网 `http://74.48.78.17:18081/`、登录、抓取页面中的 Provider 信息和 `查看原始JSON` 内容,并检查 Provider 自接入、资源指标、Docker 状态和 `provider.upgrade` 预检。外部新增节点的人工验收应复用同一套前端路径:先确认 Provider 信息出现在节点清单,再确认资源监控和 Docker 状态页面有该节点的数据,最后通过任务调度向该 Provider 下发 `echo`、`docker.ps` 或维护专用 `host.ssh` probe,并在任务历史中查看耗时、状态、stdout/stderr 摘要和失败原因。
|
||||
|
||||
@@ -78,9 +78,9 @@ provider ingress 是唯一允许公网暴露的 provider 连接接口,当前
|
||||
|
||||
自动任务执行只允许走本地 Docker socket。Compose 将 `/var/run/docker.sock` 挂入 provider-gateway,provider 标签会报告 `dockerSocketPresent`,`docker.ps` 调试任务会通过该 socket 查询宿主 Docker 容器。
|
||||
|
||||
## Microservice HTTP Proxy
|
||||
## User Service HTTP Proxy
|
||||
|
||||
`microservice.http` 是 provider-gateway 给 UniDesk microservice 使用的私有后端访问能力。backend-core 通过真实 WebSocket dispatch 下发目标 service id、节点本机 `targetBaseUrl`、path、query、method、request body、timeout 和可选 JSON 数组裁剪参数;provider-gateway 支持 `GET`、`HEAD`、`POST`、`PUT`、`PATCH`、`DELETE`,但最终允许方法必须由每个 microservice 的 `backend.allowedMethods` 显式配置。provider-gateway 只允许访问 `http://127.0.0.1`、`http://localhost`、`http://host.docker.internal` 这些节点本地地址;主 server 内置 Todo Note 与 Codex Queue 后端可分别使用 Compose 服务名 `http://todo-note:4211` 与 `http://codex-queue:4222`。该能力不打开 provider-gateway 入站端口,也不替代业务仓库自身 Dockerfile/docker-compose。
|
||||
`microservice.http` 是 provider-gateway 给 UniDesk 用户服务使用的私有后端访问能力。backend-core 通过真实 WebSocket dispatch 下发目标 service id、节点本机 `targetBaseUrl`、path、query、method、request body、timeout 和可选 JSON 数组裁剪参数;provider-gateway 支持 `GET`、`HEAD`、`POST`、`PUT`、`PATCH`、`DELETE`,但最终允许方法必须由每个用户服务的 `backend.allowedMethods` 显式配置。provider-gateway 只允许访问 `http://127.0.0.1`、`http://localhost`、`http://host.docker.internal` 这些节点本地地址;主 server 内置 Todo Note 与 Codex Queue 后端可分别使用 Compose 服务名 `http://todo-note:4211` 与 `http://codex-queue:4222`。该能力不打开 provider-gateway 入站端口,也不替代业务仓库自身 Dockerfile/docker-compose。
|
||||
|
||||
超大 JSON 响应可以使用 `jsonArrayLimits` 在 provider-gateway 返回前裁剪指定数组,并在响应体中写入 `_unidesk.arrayLimits` 元数据,便于 UniDesk frontend 预览列表而不展示裸 JSON。长期应优先推动业务后端提供分页 API;裁剪只是 UniDesk 集成层的展示保护。
|
||||
|
||||
@@ -126,8 +126,10 @@ backend-core 可以通过真实 WebSocket 调度向在线 provider 下发 `provi
|
||||
|
||||
WSL 计算节点使用 Docker Desktop daemon 时,provider-gateway 容器通常应连接 `host.docker.internal:22`,目标是当前 WSL 发行版里的 sshd,而不是给节点开放公网 SSH。节点侧必须确认 sshd 监听 `22`、目标用户可用维护公钥免密登录、`authorized_keys` 与挂载到 `/run/host-ssh/id_ed25519` 的私钥匹配;如果容器内直连 `host.docker.internal` 都失败,先修复 WSL sshd、Docker Desktop host gateway 或密钥权限,再排查 UniDesk WebSocket 透传。
|
||||
|
||||
WSL provider 需要调用 Windows-only 工具链时,优先在 WSL 用户的 `~/.local/bin` 放轻量 shim,而不是维护一套完全分叉的 Windows skill。稳定形态是:`win-powershell` 负责 Windows 发现和诊断,`win-cmd` 通过临时 `.cmd` 文件执行命令以规避 UNC 当前目录和带空格路径的 cmd 引号问题,`win-py` 将 `/mnt/<drive>/...` 参数转换为 Windows 路径后调用 `py.exe`,`win-npm` 通过 `npm.cmd --prefix <skill_dir>` 运行需要访问 COM 口的 TypeScript skill。Keil 这类依赖 Windows GUI/驱动/注册表的 skill 应由 WSL wrapper 调用 Windows 侧 skill 脚本;board-comm 这类纯 TCP JSON-RPC skill 可以直接用 WSL Python 运行;serial-monitor 需要访问 Windows COM 口时应走 Windows Node/npm。不要用宽泛的 `find /mnt/*` 扫 Windows 盘,skill 位置先用 `bun scripts/cli.ts ssh <PROVIDER_ID> skills`,项目位置优先用 PowerShell 或结构化 `glob`/`find` 缩小范围。Windows 透传 wrapper 属于节点 bootstrap,不等于修改 WSL skill 业务代码;完整规则见 `docs/reference/windows-passthrough.md`。
|
||||
|
||||
维护桥通过真实 WebSocket dispatch 暴露为 `host.ssh` 命令。默认 payload 使用 `mode: "probe"`,远端只执行一个短命令并返回 `UNIDESK_SSH_TEST user=... host=... bridge=host.ssh cwd=...`;需要人工诊断时可以显式使用 `mode: "exec"` 与 `command` 字段执行有界命令。所有 `host.ssh` 执行都必须有超时,stdout/stderr 在 task result 中截断展示;自动升级和普通任务仍必须使用 Docker socket 与 `provider.upgrade`,不得把 WSL SSH 维护桥当成调度通道。
|
||||
|
||||
面向人的终端入口是 `bun scripts/cli.ts ssh <PROVIDER_ID> [ssh-like args...]`。无后续参数时打开远端登录 shell,有后续参数时执行远端命令并返回远端 exit code;该入口走 backend-core 内网 `/ws/ssh` broker 和 provider 既有 WebSocket,不新增公网 core 端口。传统 ssh 传输参数由 provider-gateway 环境变量统一控制,CLI 只负责把 Provider ID 后的远端命令和终端 stdin/stdout/stderr 透传过去。
|
||||
面向人的终端入口是 `bun scripts/cli.ts ssh <PROVIDER_ID> [ssh-like args...]`。无后续参数时打开远端登录 shell,有后续参数时执行远端命令并返回远端 exit code;该入口走 backend-core 内网 `/ws/ssh` broker 和 provider 既有 WebSocket,不新增公网 core 端口。传统 ssh 传输参数由 provider-gateway 环境变量统一控制,CLI 只负责把 Provider ID 后的远端命令和终端 stdin/stdout/stderr 透传过去。WSL 节点需要同时看清 Linux/WSL 与 Windows 两套 skill 时,使用 `bun scripts/cli.ts ssh <PROVIDER_ID> skills`,该命令只通过已建立的维护桥读取 `SKILL.md` 元数据,不要求 provider-gateway 新增业务 API。
|
||||
|
||||
验证 WSL SSH 桥时,先在目标 WSL 中启动 sshd 并确保维护公钥写入目标用户的 `authorized_keys`,再确认目标 provider 注册 labels 中 `unideskCapabilities` 包含 `host.ssh`。运行 `bun scripts/cli.ts debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000` 后,结果应在 `debug task latest` 或前端任务历史中显示 `status: succeeded`、`probeLine` 含 `UNIDESK_SSH_TEST`、`exitCode: 0`,并且目标节点 labels 中 `hostSshKeyPresent` 为 true;随后运行 `bun scripts/cli.ts ssh <PROVIDER_ID> hostname` 验证近似原生 ssh 的远端命令体验。在计算节点本机自测时,使用 remote CLI 透传同一组命令:`bun scripts/cli.ts --main-server-ip 74.48.78.17 debug health`、`bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000` 和 `bun scripts/cli.ts --main-server-ip 74.48.78.17 ssh <PROVIDER_ID> hostname`;默认 remote CLI 走公网 frontend 登录态,不需要主 server SSH key。健康检查必须能看到该 Provider 在线、`hostSshConfigured=true`、`hostSshKeyPresent=true`、`hostSshTarget` 正确、`unideskCapabilities` 包含 `host.ssh`,probe 必须返回 `UNIDESK_SSH_TEST`,`ssh <PROVIDER_ID> hostname` 必须输出目标 WSL/宿主 hostname 且 exit code 为 0。如果 D518 这类 WSL 节点没有公网 SSH 入口,也必须通过这个 provider-gateway 自连维护桥完成验证,而不是要求主 server 直接连节点公网 22 端口;旧版 provider 未声明 `host.ssh` 时必须先升级 provider-gateway,否则 core 会拒绝 SSH 透传。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
- AGENTS.md (Top-level agent index and `scripts/cli.ts` usage guide)
|
||||
- TEST.md (Manual CLI test plan following cli-spec expectations)
|
||||
- config.json (Single source of truth for ports, tokens, runtime, paths, and provider identity)
|
||||
- docker-compose.yml (Main server orchestration for database, backend-core, frontend, provider-gateway, and managed main-server microservices such as Todo Note)
|
||||
- docker-compose.yml (Main server orchestration for database, backend-core, frontend, provider-gateway, and managed main-server user services such as Todo Note)
|
||||
- package.json / bun.lock (Root Bun tooling for CLI checks)
|
||||
- .gitignore
|
||||
- reference -> docs/reference (Compatibility symlink for older references)
|
||||
@@ -53,9 +53,9 @@
|
||||
- Dockerfile
|
||||
- src/index.ts (Bun static server, login/session handling, and same-origin internal API proxy)
|
||||
- src/app.tsx (TypeScript + React browser app shell, login, global data loading, and route dispatcher; `/app.js` is generated by Bun at runtime)
|
||||
- src/todo-note.tsx (Todo Note microservice React page; do not fold back into `app.tsx`)
|
||||
- src/findjob.tsx (FindJob microservice React page; do not fold back into `app.tsx`)
|
||||
- src/pipeline.tsx (Pipeline microservice React page and React Flow control graph; do not fold back into `app.tsx`)
|
||||
- src/todo-note.tsx (Todo Note user-service React page; do not fold back into `app.tsx`)
|
||||
- src/findjob.tsx (FindJob user-service React page; do not fold back into `app.tsx`)
|
||||
- src/pipeline.tsx (Pipeline user-service React page and React Flow control graph; do not fold back into `app.tsx`)
|
||||
- src/met-nonlinear.tsx (MET Nonlinear D601 training orchestration React page; do not fold back into `app.tsx`)
|
||||
- public/ (HTML/CSS static assets for the compact industrial console; no handwritten app JS)
|
||||
- provider-gateway/ (Compute node Provider Gateway container)
|
||||
@@ -67,5 +67,5 @@
|
||||
- database/ (PostgreSQL initialization and configuration)
|
||||
- config/postgresql.conf
|
||||
- init/001_unidesk_init.sql
|
||||
- microservices/ (Reserved for future stateless microservices)
|
||||
- microservices/ (Compatibility path reserved for future stateless user services)
|
||||
- example-service/
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Windows Passthrough Reference
|
||||
|
||||
Windows 透传用于让 WSL provider 通过 UniDesk 的 Host SSH / WSL SSH 维护桥调用 Windows 侧工具链、驱动和 skill,同时尽量保持一套可维护的 skill 入口。它的目标不是把 Windows 当成新的 provider,而是让 `ssh <PROVIDER_ID>` 进入 WSL 后,可以稳定触达 Windows `cmd.exe`、PowerShell、Python、Node/npm、Keil、COM 串口和 USB 调试器。
|
||||
|
||||
## Architecture
|
||||
|
||||
标准链路是:本机或计算节点 CLI 调用 `bun scripts/cli.ts ssh <PROVIDER_ID> ...`,main server 的 backend-core 通过 provider-gateway 的既有 WebSocket 把终端流量转给目标 provider,provider-gateway 再用只读挂载的维护私钥连到目标 WSL sshd。进入 WSL 后,Windows-only 工具由 WSL 用户目录下的轻量 wrapper 调用 Windows 可执行文件。
|
||||
|
||||
这条链路分三层维护:
|
||||
|
||||
- `provider-gateway` 只负责 Host SSH / WSL SSH 维护桥,不直接理解 Keil、串口或业务 skill。
|
||||
- WSL wrapper 负责路径转换、当前目录转换、Windows 进程启动、UTF-8 输出和 cmd/PowerShell 差异。
|
||||
- skill wrapper 负责把用户仍然熟悉的 `keil`、`serial-monitor`、`board-comm` 命令映射到正确运行侧,避免维护两套互相分叉的 skill。
|
||||
|
||||
## Skill Discovery
|
||||
|
||||
先用 UniDesk SSH 透传内置的 skill 发现入口确认目标节点上 WSL 与 Windows 两侧 skill 的实际位置:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> skills --limit 80
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> skills --scope windows --limit 40
|
||||
bun scripts/cli.ts --main-server-ip <MAIN_SERVER_IP> ssh <PROVIDER_ID> skills --scope wsl --limit 20
|
||||
```
|
||||
|
||||
`ssh <PROVIDER_ID> skills` 输出 JSON,包含 `roots` 和 `skills`。WSL provider 会同时扫描 WSL/Linux 的 `~/.agents/skills`、`~/.codex/skills`,以及 Windows 用户目录下的 `/mnt/c/Users/*/.agents/skills`、`/mnt/c/Users/*/.codex/skills`。不要用宽泛的 `find /mnt/*` 搜 Windows 盘;Windows 挂载层可能因权限、索引或设备状态导致长时间阻塞。
|
||||
|
||||
## Wrapper Contract
|
||||
|
||||
WSL 用户的 `~/.local/bin` 是推荐 wrapper 放置位置,并应在 SSH 登录 shell 的 `PATH` 中优先于系统路径。wrapper 名称和职责固定如下:
|
||||
|
||||
- `win-powershell <powershell-command>`:用于 Windows 发现、WMI/CIM、注册表、设备和 JSON 输出;应设置 UTF-8 输出,并用 `Set-Location -LiteralPath` 进入 Windows 当前目录。
|
||||
- `win-cmd <cmd-line>`:用于 `.cmd`、`.bat` 或必须走 `cmd.exe` 的 legacy 工具;应先写入 Windows 临时 `.cmd` 文件再执行,避免 UNC 当前目录警告和带空格路径引号错误。
|
||||
- `win-argpath <arg>`:把 `/mnt/<drive>/...` 或存在的 WSL 路径转换为 Windows 路径,其余参数原样返回。
|
||||
- `win-py <args...>`:调用 Windows `py.exe -3`,并对路径参数做 `/mnt/<drive>` 到 `X:\...` 的转换。
|
||||
- `win-node <args...>`:调用 Windows `node.exe`,适合必须运行在 Windows 设备上下文中的 Node 脚本。
|
||||
- `win-npm <args...>`:调用 Windows `npm.cmd`;涉及 COM 口、USB 设备或 Windows-only npm 依赖时优先使用它。
|
||||
- `win-skill-path <skill-name>`:在 Windows `.agents/skills` 与 `.codex/skills` 中定位 skill 根目录,供上层 skill wrapper 复用。
|
||||
|
||||
`win-cmd` 不应直接从 WSL 默认 UNC cwd 运行 `cmd.exe /C <command>`。稳定做法是选择一个 Windows 可写临时目录,例如 `C:\Temp`,生成 `.cmd` 文件,在脚本中 `chcp 65001`、`cd /d "<Windows cwd>"`,再执行用户命令。这样可同时处理 `C:\Program Files` 这类带空格路径和 Windows 工具不支持 UNC 当前目录的问题。
|
||||
|
||||
## Path And CWD Rules
|
||||
|
||||
Windows 可执行文件只能稳定访问 Windows 盘符路径。WSL wrapper 应遵循这些规则:
|
||||
|
||||
- 当前目录在 `/mnt/<drive>/...` 下时,自动转换为 `<DRIVE>:\...` 作为 Windows cwd。
|
||||
- 当前目录不在 `/mnt/<drive>` 下时,默认使用 `C:\Windows`,或由调用者显式设置 `WIN_CWD`。
|
||||
- 参数中存在的 `/mnt/<drive>/...` 路径应转换为 Windows 路径;普通参数、IP、端口、JSON 字符串不应被转换。
|
||||
- 需要 Windows 工具直接读写的工程仓库应放在 Windows 盘符挂载路径下;WSL 原生 `/home/...` 更适合作为 UniDesk、脚本和临时 Linux 工作区。
|
||||
- 从 WSL 到 Windows 的搜索优先使用 `win-powershell` 或 `ssh <PROVIDER_ID> skills`,再用结构化 `glob`/`find` 缩小到已知目录。
|
||||
|
||||
## Skill Compatibility Strategy
|
||||
|
||||
不要为 Windows 和 WSL 分别维护两套业务逻辑分叉的 skill。推荐策略是保留一个用户可见命令入口,在 wrapper 中选择正确运行侧:
|
||||
|
||||
- `keil`:依赖 Windows Keil、CMSIS-DAP/ST-Link/J-Link 驱动、注册表和工程路径,WSL 入口应调用 Windows 侧 `keil` skill 的 `keil-cli.py`,通常经 `win-py` 执行。
|
||||
- `serial-monitor`:访问 Windows COM 口时必须运行在 Windows Node/npm 上,WSL 入口应通过 `win-npm --prefix <windows-skill-dir> run cli -- ...` 调用。
|
||||
- `board-comm`:当前 JSON-RPC over TCP 不依赖 Windows 驱动,可直接用 WSL Python 运行 WSL 侧 skill;只有未来新增 serial transport 时才需要再评估是否切到 Windows 侧。
|
||||
|
||||
工程级 wrapper 可以保留 `*-wsl` 别名,例如 `keil-wsl`、`serial-monitor-wsl`、`board-comm-wsl`,但用户文档中的正式入口仍应优先写 `keil`、`serial-monitor` 和 `board-comm`,避免让任务 prompt 绑定到某台机器的一次性脚本名。
|
||||
|
||||
## Skill Modification And Bootstrap Status
|
||||
|
||||
Windows 透传机制不要求修改 WSL skill 的业务代码。标准交付边界应区分三类内容:
|
||||
|
||||
- UniDesk 内置能力:`ssh <PROVIDER_ID> skills`、`apply-patch`、`glob`、`py` 等 helper 由 UniDesk CLI 在 SSH 会话启动时注入 `/tmp/unidesk-ssh-tools`,属于开箱即用能力;它们不修改远端 skill 目录。
|
||||
- 节点 bootstrap:`win-cmd`、`win-powershell`、`win-py`、`win-npm`、`win-skill-path` 以及 `keil`、`serial-monitor`、`board-comm` 的 WSL wrapper 属于节点运行环境 bootstrap,通常放在目标 WSL 用户的 `~/.local/bin`;这不是 skill 本身开箱即用的一部分。
|
||||
- skill 代码:除非明确要把 wrapper 能力合入某个 skill,否则不得为了某台节点直接改 `~/.agents/skills/<skill>` 或 `~/.codex/skills/<skill>` 的业务代码;如果确实修改了 skill CLI 或 `SKILL.md`,必须按该 skill 的测试和文档规则独立验收。
|
||||
|
||||
因此,判断一个节点是否“开箱即用”时要分别检查:UniDesk CLI 是否支持 `ssh skills`,目标 WSL 是否已有 wrapper bootstrap,Windows 侧是否已有 Keil/Node/npm/Python/驱动,目标 skill 是否已有依赖。缺少 wrapper 或 Windows 工具链时,应报告为节点环境未 bootstrap 完成,而不是把它误判成 skill 逻辑失败。
|
||||
|
||||
当前推荐不在 wrapper 中自动安装依赖。`serial-monitor` 所需 Windows `node_modules`、Keil pack、pyOCD、USB probe 驱动、COM 驱动等都应由节点 bootstrap 或人工安装完成;wrapper 只负责调用和给出明确错误。这样可以避免 agent 在硬件节点上静默安装版本不明的依赖,导致后续验收不可复现。
|
||||
|
||||
## Dependency Bootstrap
|
||||
|
||||
Windows 透传依赖分为四类:UniDesk 侧能力、WSL 侧基础环境、Windows 侧工具链、skill 本地依赖。它们应由节点 bootstrap 或人工维护显式安装,不应由业务任务中的 wrapper 静默安装。
|
||||
|
||||
### UniDesk And WSL Prerequisites
|
||||
|
||||
WSL provider 至少需要:
|
||||
|
||||
- WSL sshd 已启动,provider-gateway 容器可用维护私钥免密登录目标 WSL 用户。
|
||||
- 目标 WSL 用户 `PATH` 包含 `~/.local/bin`,用于放置 `win-*` 与 skill wrapper。
|
||||
- WSL 内有 `python3`、`base64`、`bash`、`wslpath`;UniDesk SSH 注入的 `py`、`apply-patch`、`glob` 和 `skill-discover` 依赖这些基础命令。
|
||||
- WSL 可访问 Windows 盘符挂载,例如 `/mnt/c`、`/mnt/d`、`/mnt/f`。
|
||||
- 如果 provider-gateway 跑在 Docker Desktop daemon 上,容器到 WSL sshd 的 `host.docker.internal:22` 链路必须可用。
|
||||
|
||||
WSL wrapper bootstrap 至少应安装或生成这些脚本:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.local/bin
|
||||
# win-cmd, win-powershell, win-argpath, win-py, win-node, win-npm, win-skill-path
|
||||
# keil, serial-monitor, board-comm wrappers
|
||||
chmod +x ~/.local/bin/win-* ~/.local/bin/keil ~/.local/bin/serial-monitor ~/.local/bin/board-comm
|
||||
```
|
||||
|
||||
这些 wrapper 属于节点环境,不应提交到 UniDesk 仓库,除非后续专门新增一个受版本管理的 provider bootstrap 脚本。
|
||||
|
||||
### Windows Toolchain Prerequisites
|
||||
|
||||
Windows 侧至少需要按目标硬件场景安装:
|
||||
|
||||
- Python Launcher:`py.exe` 可用,且 `py -3` 指向 Python 3。
|
||||
- Node.js/npm:`node.exe` 和 `npm.cmd` 可用;涉及 COM 口的 TypeScript skill 应运行在 Windows Node 上。
|
||||
- Keil MDK:`UV4.exe` 可用,Keil Pack 已安装到工程需要的 MCU 系列。
|
||||
- pyOCD:Windows Python 环境中可用 `python -m pyocd`,用于 CMSIS-DAP 探头检测和 pyOCD backend 下载。
|
||||
- USB 调试器驱动:CMSIS-DAP、DAPLink、ST-Link 或 J-Link 对应驱动可被 Windows 识别。
|
||||
- 串口驱动:目标板 UART bridge 在 Windows 设备管理器中显示为 `COMx`。
|
||||
- PowerShell:用于 WMI/CIM 设备发现、路径诊断和 Windows 侧环境检查。
|
||||
|
||||
推荐的只读检查命令:
|
||||
|
||||
```bash
|
||||
win-powershell "Get-Command py.exe,node.exe,npm.cmd -ErrorAction SilentlyContinue | Select-Object Name,Source | ConvertTo-Json"
|
||||
win-powershell "Get-CimInstance Win32_SerialPort | Select-Object DeviceID,Name,Description,PNPDeviceID | ConvertTo-Json -Depth 3"
|
||||
win-cmd "where py && where node && where npm"
|
||||
```
|
||||
|
||||
Keil 与 pyOCD 检查应通过 skill 入口完成:
|
||||
|
||||
```bash
|
||||
keil status
|
||||
keil list-devices -p <project.uvprojx>
|
||||
```
|
||||
|
||||
### Skill Dependencies
|
||||
|
||||
各 skill 的依赖安装边界如下:
|
||||
|
||||
- `keil`:Windows 侧 `~/.agents/skills/keil` 应已存在 `keil-cli.py`、`config.json` 与所需 Python 依赖;Windows Python 中必须能导入 pyOCD。Keil pack 和 probe 驱动属于 Windows 工具链依赖,不属于 WSL wrapper 自动安装范围。
|
||||
- `serial-monitor`:Windows 侧 `~/.agents/skills/serial-monitor` 需要已执行过 `npm install`,生成对应 Windows 平台的 `node_modules`。从 WSL 调用时应使用 `win-npm --prefix <windows-skill-dir> run cli -- ...`,不要复用 WSL 的 `node_modules` 去访问 Windows COM 口。
|
||||
- `board-comm`:JSON-RPC over TCP 可直接使用 WSL 侧 `~/.agents/skills/board-comm` 与 WSL Python;如果 `check` 因缺少 pyright 失败,但 `jrpctcp` 正常,不应阻塞硬件通信验证。若未来增加 serial transport,再按运行侧补充 Windows 依赖。
|
||||
|
||||
依赖安装必须显式、可复现,并优先在对应 skill 的 `SKILL.md` 或节点 bootstrap 文档中记录。业务任务中如果发现依赖缺失,应输出缺失项、建议安装命令和验证命令,而不是直接静默安装。
|
||||
|
||||
### Verification Matrix
|
||||
|
||||
节点完成依赖 bootstrap 后,至少运行以下验证:
|
||||
|
||||
```bash
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> skills --limit 20
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; command -v win-cmd win-powershell win-py win-npm keil serial-monitor board-comm'
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; win-py -V; win-npm -v'
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; keil status'
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; serial-monitor ports'
|
||||
bun scripts/cli.ts ssh <PROVIDER_ID> -- 'export PATH="$HOME/.local/bin:$PATH"; board-comm debug build-jrpctcp-request get api'
|
||||
```
|
||||
|
||||
硬件项目级验收还应覆盖真实 `build`、`program`、串口抓取和 `board-comm jrpctcp get api`。如果只有依赖检查通过但没有真实硬件闭环,不能宣称该工程已完成下载和通信验收。
|
||||
|
||||
## Hardware Workflow
|
||||
|
||||
Keil/串口/board-comm 的通用顺序如下:
|
||||
|
||||
1. `ssh <PROVIDER_ID> skills` 确认 WSL 与 Windows skill 位置。
|
||||
2. `win-powershell` 或 `serial-monitor ports` 确认可见 COM 口。
|
||||
3. `keil status` 和 `keil list-devices -p <project.uvprojx>` 确认 Keil、pyOCD 和 USB probe。
|
||||
4. `keil build --wait -p <project.uvprojx> -t <target>` 构建。
|
||||
5. `serial-monitor server start --force` 和 `serial-monitor monitor start -p <COMx> -b <baud>` 先打开串口。
|
||||
6. `keil program --wait ... -u <probe_uid>` 下载并运行。
|
||||
7. `serial-monitor fetch --session-only --no-dedup -l <N>` 抓取串口证据。
|
||||
8. `board-comm jrpctcp --host <board_ip> --port <port> get api` 验证主动通信面。
|
||||
|
||||
多 probe 同时在线时,`keil program`、`keil detect` 必须显式传 `-u <probe_uid>`。若 Keil UV4 backend 报缺少 flash/download metadata,可优先用 pyOCD backend 完成下载;是否补齐 UV4 工程下载配置应作为工程维护问题处理,而不是 SSH/Windows 透传问题。
|
||||
|
||||
## Remote Frontend Limits
|
||||
|
||||
`--main-server-ip ... ssh <PROVIDER_ID> <command>` 走 frontend 登录态和 `host.ssh` dispatch,适合短命令和 skill discovery。它不流式转发 stdin,也受 provider-gateway 的 host.ssh command length 限制;`apply-patch`、`py < stdin`、超长 inline 脚本和需要完整终端流的操作应在 main server 本机 CLI 上执行,或显式走旧 SSH transport。
|
||||
|
||||
`ssh <PROVIDER_ID> skills` 在 remote frontend 模式下使用短 inline 发现脚本以避开命令长度限制;如果输出被任务历史截断,应改在 main server 本机 CLI 执行同一命令以获得完整 stdout。
|
||||
|
||||
## Safety Rules
|
||||
|
||||
- Windows 透传只用于节点诊断、硬件工具链和人工维护,不得作为 provider-gateway 自重建通道;provider-gateway 升级仍必须走 `provider.upgrade mode=schedule`。
|
||||
- 不要把 Windows 透传 wrapper 写入业务仓库根目录;它们属于节点运行环境,应放在 WSL 用户目录或节点私有 bootstrap 脚本中。
|
||||
- 不要在 wrapper 中静默安装依赖;缺少 `py.exe`、Node/npm、Keil、COM 驱动或 probe 驱动时,应输出明确错误并让节点 bootstrap 修复。
|
||||
- 不要把 Windows 用户目录、串口日志、Keil 日志、SSH key 或 provider token 提交到 UniDesk 仓库。
|
||||
- 串口抓取和烧录会影响真实硬件状态;执行前应确认目标 project、target、probe UID、COM 口和 baud rate。
|
||||
Reference in New Issue
Block a user