fix: adapt claudeqq k3s deploy and notifications

This commit is contained in:
Codex
2026-05-16 19:03:56 +00:00
parent cd2aebfe75
commit 550a12b0a9
7 changed files with 212 additions and 10 deletions
+3 -3
View File
@@ -180,14 +180,14 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度
- Judge 探针与复现:`GET|POST /api/judge/probe` 使用同一套 judge 逻辑跑内置 synthetic execution records,覆盖正常完成、正常结束但只给计划、未上线/未部署的服务或 WebUI 改动、传输中断和用户打断等样本,返回 `hits``total``hitRate`、每例 `expected``decision`;该接口不得回显 MiniMax API key。真实任务排障必须优先使用 `codex judge <taskId> --attempt N``/api/tasks/{id}/judge?attempt=N`,响应要包含 attempt 窗口、promptChars/payloadBytes、stored judge 对比、MiniMax 失败阶段和是否因历史 per-attempt events 缺失而降级为 retained events 重建。
- 模型选择:默认 Codex 模型是 `gpt-5.5`,内置模型队列包含 `gpt-5.5``gpt-5.4-mini``gpt-5.4``gpt-5.5` 的默认 reasoning effort 必须是 `xhigh`,可通过 `CODE_QUEUE_MODEL_REASONING_EFFORTS` 追加或覆盖模型级默认值;每个入队任务可通过前端模型下拉菜单或 API 覆盖 `model``cwd``reasoningEffort``maxAttempts``maxAttempts` 上限为 `99`。Judge 判定 `retry` 或非用户取消类 `fail` 时必须继续已有 `codexThreadId`,不能新建 session;重试间隔使用指数退避,从 `1s` 开始,最大 `10min`。MiniMax 不可用而进入 fallback/non-LLM 判定时,当前 attempt 的 429、Too Many Requests、exceeded retry limit、overloaded、stream disconnected 等服务/限流错误应判定为 `retry`,不能当作完成;MiniMax 可用时,这些内容只能作为当前 attempt 的 factual evidence 提供给 MiniMax,不能通过硬编码覆盖 MiniMax 结果。
- 状态与日志:`D601` 默认工作目录为容器内 `/workspace`,该路径映射 D601 WSL host 的 `/home/ubuntu`;容器内 `/home/ubuntu` 也必须映射同一个 hostPath,保证从 `/workspace` 看到的绝对 symlink 可以继续解析到真实文件。服务自身仓库路径 `/root/unidesk``/app` 单独映射 D601 WSL host 的 `/home/ubuntu/cq-deploy`。其他 Provider 的任务默认工作目录为 `/home/ubuntu`,任务 JSON、列表、Trace 摘要和 CLI 查询都必须显示 `providerId``executionMode` 与最终 `cwd``executionMode=default` 在 D601 本机 Code Queue 容器中运行 Codex/OpenCode`executionMode=windows-native` 只允许非本机 WSL Provider、Codex 模型和 `/mnt/<drive>` 工作目录,Code Queue 仍会启动远程执行容器,但容器只运行 stdio relay,经 WSL bridge 调用 Windows 宿主原生 `codex app-server --listen stdio://`。Code Queue 的任务、queue、`readAt`/未读状态、attempt、judge、`promptHistory`、active session 元数据、控制状态和 ClaudeQQ 通知 outbox 一律以主 PostgreSQL 为权威,分别写入 `unidesk_code_queue_tasks``unidesk_code_queue_queues``unidesk_code_queue_notifications``DATABASE_URL` 是必需配置,服务不得在 PostgreSQL 缺失或不可用时进入文件存储模式。`.state/code-queue/state.json` 不再作为任务或 queue 状态存储,不得重新引入本地 JSON fallback;服务启动必须以 PostgreSQL 为唯一来源恢复队列,并把 running/judging 任务恢复为 retry_wait。D601 资源比主 server 宽裕,但 Code Queue 仍必须把“内存是稀缺资源”作为核心设计约束:历史任务列表、详情、统计和只读 Trace 查询优先从 PostgreSQL 直读,进程内只保留当前 running/judging、queued、retry_wait 等调度必需热任务;需要短期热缓存时必须有严格上限、可裁剪、可从 PostgreSQL 和 append-only 输出归档重建。WebUI 不得用 browser `localStorage``sessionStorage` 或 IndexedDB 持久化 task/queue/readAt/unread 等业务状态;浏览器只能保留临时 UI 内存缓存,刷新后必须重新从后端读取 PostgreSQL 权威数据。Codex CLI-like output/Trace 的完整记录可以使用 append-only 文件作为日志型归档,但任务状态、未读状态和列表摘要不得依赖这些文件作为权威来源;`/api/tasks/<id>/transcript``/api/tasks/<id>/output` 必须能分页重建完整历史,不得因为热状态裁剪而丢失早期 trace。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue、提交时下拉选择执行 Provider 和执行模式,并支持把已创建且非 active 的任务移动到其他 queuequeue 内串行,queue 间默认并行且不互相排队;`CODE_QUEUE_MAX_ACTIVE_QUEUES` 仅作为显式配置的全局 active slot 上限,`0` 表示不按 queue 数量限流,内存不足时由 cgroup memory pressure 阻止新 run 并在任务响应中暴露 `QUEUED(MEM LIMIT)`。Code Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,并使用 `bun --smol` 运行后端,保证队列任务能直接执行公网 frontend Playwright 回归且主进程内存可控。日志写入 D601 `.state/code-queue/logs` 挂载的 UniDesk JSONL 日志;Codex app-server 上游产生的 `logs_*.sqlite` 只能作为短暂缓冲,必须由 Code Queue 周期性导出为 JSONL,避免重新形成大 SQLite 文件;`/logs` 端点返回最近结构化日志。`/health``queue.storage.primary` 必须恒为 `postgres`,并通过 `queue.storage.postgresReady``queue.devReady``/api/dev-ready` 暴露 PostgreSQL 可用性、develop-ready 自检、必需工具、Docker socket、`docker compose`、默认工作目录、Codex config 状态和 `/root/.ssh` 共享 SSH key 状态;D601 本机默认执行必须能通过 `/root/.ssh` 复用 WSL host GitHub SSH key 执行 Git over SSH,跨 Provider SSH 或 `windows-native` 任务同样需要 `queue.devReady.ssh.ready=true`。Codex CLI-like 输出可能很大,服务必须节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致 `/health` 和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程.
- ClaudeQQ 通知:Code Queue 在 D601 k3s 上通过 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL=http://claudeqq.unidesk.svc.cluster.local:3290` 调用 k3s 代管 ClaudeQQ 后端 `POST /api/push/text`,在每个任务进入 `succeeded``failed``canceled` 终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入 `0 running / 0 queued` 空闲态时,必须单独发送一次空闲提醒。通知由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED` 控制,目标由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group``CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID``CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID` 配置,默认私聊 `645275593`;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL``CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS``CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS``CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS` 配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表 `unidesk_code_queue_notifications` 再异步发送;不得使用 `.state/code-queue/claudeqq-notifications.json``CODE_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH` 或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按 `CODE_QUEUE_NOTIFY_CLAUDEQQ_RETRY_INTERVAL_MS` 指数退避重试并跨进程/容器重启保留。`/health``queue.notifications.claudeqq` 必须暴露非敏感配置、目标配置状态和 PostgreSQL outbox 统计;`GET /api/notifications/claudeqq` 返回 outbox 明细,`POST /api/notifications/claudeqq/drain` 手动触发发送,`POST /api/notifications/claudeqq/backfill` 可按 `since` 补入某次故障窗口内已终态任务,确保 QQ/NapCat 超时或离线不会让任务完成通知永久丢失。
- ClaudeQQ 通知:Code Queue 在 D601 k3s 上通过 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL=http://claudeqq.unidesk.svc.cluster.local:3290` 调用 k3s 代管 ClaudeQQ 后端 `POST /api/push/text`并在旧 ClaudeQQ 只暴露 `/api/send/text` 时按同一目标自动兼容该接口;在每个任务进入 `succeeded``failed``canceled` 终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入 `0 running / 0 queued` 空闲态时,必须单独发送一次空闲提醒。通知由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED` 控制,目标由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group``CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID``CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID` 配置,默认私聊 `645275593`;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL``CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS``CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS``CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS` 配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表 `unidesk_code_queue_notifications` 再异步发送;不得使用 `.state/code-queue/claudeqq-notifications.json``CODE_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH` 或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按 `CODE_QUEUE_NOTIFY_CLAUDEQQ_RETRY_INTERVAL_MS` 指数退避重试并跨进程/容器重启保留。`/health``queue.notifications.claudeqq` 必须暴露非敏感配置、目标配置状态和 PostgreSQL outbox 统计;`GET /api/notifications/claudeqq` 返回 outbox 明细,`POST /api/notifications/claudeqq/drain` 手动触发发送,`POST /api/notifications/claudeqq/backfill` 可按 `since` 补入某次故障窗口内已终态任务,确保 QQ/NapCat 超时或离线不会让任务完成通知永久丢失。
### ClaudeQQ k3s-Managed
当前 ClaudeQQ 作为 `id=claudeqq``k3sctl-managed` 用户服务登记在 `config.json`,业务实例由 D601 k3s 控制面代管:
- Orchestrator`deployment.mode=k3sctl-managed``deployment.adapterServiceId=k3sctl-adapter``deployment.k3sServiceId=claudeqq``backend.proxyMode=k3sctl-adapter-http``backend.nodeBaseUrl=k3s://claudeqq`backend-core 对 ClaudeQQ 的正式链路只能是 `frontend -> backend-core -> k3sctl-adapter -> Kubernetes API service proxy -> Kubernetes Service claudeqq:3290`
- 部署引用:ClaudeQQ 源码来自 `https://gitee.com/lyon1998/agent_skills``claudeqq` 子目录镜像构建使用该目录内 `Dockerfile`Kubernetes 运行清单为 `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k8s.yaml``config.json` 对外记录 k3s manifest `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k3s.json`。旧 `docker-compose.unidesk.yml` 只作为迁移/本地诊断参考,不是正式运行入口。
- 部署引用:ClaudeQQ 业务源码来自 `https://gitee.com/lyon1998/agent_skills``claudeqq` 子目录镜像构建 Dockerfile 和 `0.0.0.0:3290` API 适配器由 UniDesk 仓库 `src/components/microservices/claudeqq/` 作为 k3s 部署资产注入,不能依赖 D601 未提交工作树文件。Kubernetes 运行清单为 `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k8s.yaml``config.json` 对外记录 k3s manifest `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k3s.json`。旧 `docker-compose.unidesk.yml` 只作为迁移/本地诊断参考,不是正式运行入口。
- 运行对象:ClaudeQQ Pod 在 D601 上以同 Pod 双容器运行 `claudeqq``napcat`,仅通过 ClusterIP Service 暴露 `claudeqq:3290` 给 UniDesk 代理和 Code Queue 通知链路;NapCat 的 `3000/3001/6099` 只在 Pod 内可达,不得作为 NodePort、hostPort 或 provider-gateway 业务直连目标。
- 持久化路径:NapCat 登录态保存在 D601 hostPath `/home/ubuntu/.agents/skills/claudeqq/napcat/qq`,NapCat 配置和二维码缓存分别保存在 `napcat/config``napcat/cache`;ClaudeQQ 后端挂载同一业务目录中的 `config.json``bot_workspace``logs``.state` 和只读 `napcat`。这些路径不得改成匿名 volume,避免重建 Pod 后 QQ 登录态和事件/订阅状态丢失。
- 代理/API:只允许 `/health``/logs``/api/` 前缀;允许方法为 `GET``HEAD``POST``DELETE``POST /api/push/text` 接受 `userId``groupId``message`,由 ClaudeQQ 通过同 Pod NapCat HTTP API 发送 QQ 消息;NapCat 不可用时必须快速返回 `status=napcat_offline` 或可解释错误。
@@ -279,7 +279,7 @@ ClaudeQQ 的业务源码和持久化数据仍在 D601,但正式运行由 k3s
- Provider`D601`
- 开发工作树:`/home/ubuntu/.agents/skills/claudeqq`,后端、Dockerfile、订阅分发和 NapCat 连接调试必须通过 UniDesk SSH 透传在 D601 完成;主 server 本地只允许开发 UniDesk frontend、代理登记和 k3s manifest。
- 代码引用:`https://gitee.com/lyon1998/agent_skills` 与配置中的 `repository.commitId`,实际服务目录为仓库内 `claudeqq/`
- 部署引用:标准 desired state 为根目录 `deploy.json` 中的 `claudeqq` 服务;运行清单为 UniDesk 仓库内 `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k3s.json``claudeqq.k8s.yaml`,镜像名固定 `unidesk-claudeqq:d601`。业务仓库内 `docker-compose.unidesk.yml` 只作为迁移/本地诊断参考,不是正式部署入口。
- 部署引用:标准 desired state 为根目录 `deploy.json` 中的 `claudeqq` 服务;运行清单为 UniDesk 仓库内 `src/components/microservices/k3sctl-adapter/k3s/claudeqq.k3s.json``claudeqq.k8s.yaml`,镜像名固定 `unidesk-claudeqq:d601`;构建 Dockerfile 与 API 适配器由 `src/components/microservices/claudeqq/` 注入。业务仓库内 `docker-compose.unidesk.yml` 只作为迁移/本地诊断参考,不是正式部署入口。
- 运行配置:ClaudeQQ Pod 同时包含 `napcat``claudeqq` 两个容器;`claudeqq` 固定 `CLAUDEQQ_AUTO_REPLY=false``CLAUDEQQ_NAPCAT_HTTP_HOST=127.0.0.1``CLAUDEQQ_NAPCAT_WS_HOST=127.0.0.1``CLAUDEQQ_ONLINE_NOTICE_USER_ID=645275593``CLAUDEQQ_LOGIN_MONITOR_INTERVAL_MS`。NapCat 的 `3000/3001/6099` 仅 Pod 内访问。
- 节点后端:UniDesk 逻辑后端为 `k3s://unidesk/claudeqq:3290`,由 backend-core 经过 k3sctl-adapter 和 Kubernetes API service proxy 访问 Kubernetes Service `claudeqq:3290`;不得再通过 provider-gateway 业务代理或 D601 本机端口作为正式链路。
- NapCat 登录 API`GET /api/napcat/login``GET /api/napcat/status` 返回容器化状态、HTTP/WS 连通性、登录状态和二维码 data URL;`GET /api/napcat/qrcode` 只返回二维码。二维码来源为共享挂载中的 `/napcat/cache/qrcode.png`,由 ClaudeQQ 后端转为 JSON data URL 后经 UniDesk 同源代理给前端展示。`/health` 的 healthy 条件必须包含 `ready=true`、NapCat HTTP connected、NapCat WebSocket connected 和 `loginState=logged_in`;仅有二维码、NapCat 容器 running 或后端进程 running 不能算健康。