docs: record sub2api timeout closeout and agentrun cli updates

This commit is contained in:
Codex
2026-06-11 03:31:53 +00:00
parent b76987b342
commit 27f6e4812a
10 changed files with 1100 additions and 174 deletions
+45 -47
View File
@@ -1,11 +1,11 @@
---
name: unidesk-code-queue
description: UniDesk AgentRun-backed Code Queue CLI — Skill(cli-spec)。legacy `codex` 子命令只保留历史只读/残留停止/prompt-lint;新任务提交、Aipod/Artificer 派单、steer、resume、queue mutation、session trace/output/read/cancel、run/command/runner 状态 drill-down 和 HWLAB Code Agent/CaseRun follow-up 必须使用 `agentrun queue|runs|commands|runner|sessions|aipod-specs`。用户提到 codex、Code Queue、submit、steer、resume、tasks、unread、code-queue、aipod、Artificer、HWLAB Code Agent 时使用。
description: UniDesk AgentRun-backed Code Queue CLI — Skill(cli-spec)。legacy `codex` 子命令只保留历史只读/残留停止/prompt-lint;新任务提交、Aipod/Artificer 派单、steer/send、events/logs/result、ack/cancel、run/command/runner 状态 drill-down 和 HWLAB Code Agent/CaseRun follow-up 必须使用 `agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send` 资源原语。用户提到 codex、Code Queue、submit、steer、resume、tasks、unread、code-queue、aipod、Artificer、HWLAB Code Agent 时使用。
---
# UniDesk Code Queue / AgentRun CLI
旧 Code Queue 已冻结新任务和写入口。`bun scripts/cli.ts codex ...` 现在只作为历史归档、只读排障、残留任务停止和 prompt-lint 入口;新的指挥官派单、Aipod/Artificer 执行、trace/output、read/cancel、steer/reuse 必须走 AgentRun Queue/Runs/Commands/Runner/Sessions/AipodSpec,并按 cli-spec 渐进披露
旧 Code Queue 已冻结新任务和写入口。`bun scripts/cli.ts codex ...` 现在只作为历史归档、只读排障、残留任务停止和 prompt-lint 入口;新的指挥官派单、Aipod/Artificer 执行、events/logs/result、ack/cancel、steer/send 必须走 AgentRun 资源原语,并按 cli-spec 渐进披露。默认输出是低噪声 human 表格/摘要;脚本读取显式使用 `-o json|yaml`,原始官方 bridge 调试显式使用 `--raw`
**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts agentrun ...`
@@ -15,52 +15,50 @@ description: UniDesk AgentRun-backed Code Queue CLI — Skill(cli-spec)。legacy
```bash
# 查看 AgentRun 指挥官队列
bun scripts/cli.ts agentrun queue commander --reader-id <id>
bun scripts/cli.ts agentrun queue commander --reader-id <id> --full
bun scripts/cli.ts agentrun get tasks --queue commander --limit 20
bun scripts/cli.ts agentrun get tasks --queue commander -o wide
# 按已有 CLI 组合查看一个 Aipod/Queue run 生命周期;不要新增 queue lifecycle 命令
bun scripts/cli.ts agentrun queue show <taskId>
bun scripts/cli.ts agentrun queue show <taskId> --full
bun scripts/cli.ts agentrun runs show <runId>
bun scripts/cli.ts agentrun runs result <runId> --command-id <commandId>
bun scripts/cli.ts agentrun runs events <runId> --after-seq <lastSeq> --limit 100 --tail-summary
bun scripts/cli.ts agentrun commands show <commandId> --run-id <runId>
bun scripts/cli.ts agentrun commands result <commandId> --run-id <runId>
bun scripts/cli.ts agentrun runner jobs --run-id <runId> --command-id <commandId>
bun scripts/cli.ts agentrun runner job-status <runnerJobId> --run-id <runId>
# 查看一个 task/run/command/runnerjob/session 生命周期
bun scripts/cli.ts agentrun describe task/<taskId>
bun scripts/cli.ts agentrun events run/<runId> --after-seq <lastSeq> --limit 100
bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100
bun scripts/cli.ts agentrun result run/<runId> --command <commandId>
bun scripts/cli.ts agentrun describe command/<commandId> --run <runId>
bun scripts/cli.ts agentrun describe runnerjob/<runnerJobId> --run <runId>
# 查看 / 渲染 AipodSpecArtificer 是默认分布式开发 agent
bun scripts/cli.ts agentrun aipod-specs list
bun scripts/cli.ts agentrun aipod-specs show Artificer
bun scripts/cli.ts agentrun get aipodspecs
bun scripts/cli.ts agentrun describe aipodspec/Artificer
bun scripts/cli.ts agentrun aipod-specs render Artificer --prompt-stdin
# 提交 AgentRun Queue payload
bun scripts/cli.ts agentrun queue submit --json-stdin <<'JSON'
{
"tenantId": "unidesk",
"projectId": "example",
"queue": "commander",
"title": "任务标题",
"payload": {
"prompt": "任务说明"
}
}
JSON
# 旧 bridge 管理入口仍用于 AipodSpec apply/delete
bun scripts/cli.ts agentrun aipod-specs apply --yaml-stdin --dry-run
# 提交 AgentRun task manifest
bun scripts/cli.ts agentrun apply -f - --dry-run <<'YAML'
kind: Task
spec:
tenantId: unidesk
projectId: example
queue: commander
title: 任务标题
payload:
prompt: 任务说明
YAML
# 用 AipodSpec 提交,优先用于新任务和 Artificer
bun scripts/cli.ts agentrun queue submit --aipod Artificer \
bun scripts/cli.ts agentrun create task --aipod Artificer \
--prompt-stdin --idempotency-key <key>
# 查看/控制 AgentRun session
bun scripts/cli.ts agentrun sessions trace <sessionId>
bun scripts/cli.ts agentrun sessions output <sessionId>
bun scripts/cli.ts agentrun sessions read <sessionId>
bun scripts/cli.ts agentrun sessions turn <sessionId> --aipod Artificer --prompt-stdin
bun scripts/cli.ts agentrun sessions steer <sessionId> --prompt-stdin
bun scripts/cli.ts agentrun sessions cancel <sessionId>
bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100
bun scripts/cli.ts agentrun ack session/<sessionId>
bun scripts/cli.ts agentrun send session/<sessionId> --aipod Artificer --prompt-stdin
bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin
bun scripts/cli.ts agentrun cancel session/<sessionId> --reason <text> --dry-run
```
日常一次性 JSON、prompt 和 runner JSON 输入优先使用 quoted heredoc/stdin`--json-stdin``--prompt-stdin``--runner-json-stdin``--*-file -`。UniDesk bridge 会把 stdin 直通 G14 `/root/agentrun-v01` 官方 `./scripts/agentrun --manager-url auto` CLI,不先落 dump 文件;`--json-file``--prompt-file``--runner-json-file` 只用于已审阅且可复用的受控文件。它不是旧 Code Queue adapter,不双写,也不迁移旧历史。
日常 task manifest 优先使用 YAML heredoc`agentrun apply -f -`;单 prompt 派单优先 `agentrun create task --aipod Artificer --prompt-stdin`。UniDesk bridge 会把 stdin 直通 G14 `/root/agentrun-v01` 官方 `./scripts/agentrun --manager-url auto` CLI,不先落 dump 文件;`--json-file``--prompt-file``--runner-json-file`作为旧 bridge 兼容入口用于已审阅且可复用的受控文件。它不是旧 Code Queue adapter,不双写,也不迁移旧历史。
`AipodSpec` 是 AgentRun v0.1 的声明式 agent 装配:模型 profile、gitbundle、skills/tools、SecretRef 和 tool credential 都从 YAML 规格渲染。`Artificer` 默认用于 UniDesk 分布式开发任务,使用 `sub2api` provider、`gpt-5.5``reasoningEffort=xhigh`,并通过 SecretRef 注入 GitHub PR token、GitHub SSH 和 UniDesk SSH 透传能力。更新规格时使用 `agentrun aipod-specs apply --yaml-stdin --dry-run` 先看计划,确认后再去掉 `--dry-run`;不得把 API key、SSH key 或 token 写入 prompt、payload、YAML 或 issue。
@@ -68,20 +66,20 @@ AgentRun Queue payload 需要 runner 内使用 UniDesk SSH 透传时,只通过
## Queue 渐进披露
AgentRun queue 生命周期不是一个单独的 `queue lifecycle` 命令,而是一组现有 CLI 的渐进披露组合:
AgentRun queue 生命周期不是一个单独的 `queue lifecycle` 命令,而是一组资源原语的渐进披露组合:
1. 默认总览用 `queue commander --reader-id <id>`,只看 active/unread/terminal 摘要和少量任务行
2. 单任务用 `queue show <taskId>`,读取 `latestAttempt.runId``commandId``runnerJobId``sessionId/sessionPath`、Aipod metadata 和 `pollCommands`
3. Run 级状态用 `runs show <runId>``runs result <runId> --command-id <commandId>`,判断 claimed、lease、stale、terminalClassification、failureKind、provider interruption、timeoutBudget 和 recoveryActions。
4. Command 级状态用 `commands show <commandId> --run-id <runId>``commands result <commandId> --run-id <runId>`,确认 command state、ack、terminal status 和结果摘要。
5. Runner job 只读状态用 `runner jobs --run-id <runId> --command-id <commandId>``runner job-status <runnerJobId> --run-id <runId>`,确认 env image reuse、jobName、namespace、phase、exitCode、retention 和 `valuesPrinted=false`。不要为了这些字段手动调用 `trans G14:k3s kubectl ...`
6. Session trace/output 只在 `queue show` 或 result 里有实际 `sessionId` 时使用 `sessions trace|output|read|steer|cancel``sessionRef=null` 时不要猜 session 命令。
1. 默认总览用 `get tasks --queue commander --limit 20`,只看 task state、queue/lane、run/cmd/rjob/session ref、age 和 attention
2. 单任务用 `describe task/<taskId>`,读取 `latestAttempt.runId``commandId``runnerJobId``sessionId/sessionPath` 和少量 `Next:`
3. Run 级状态用 `events run/<runId>``result run/<runId> --command <commandId>`,判断 terminalClassification、failureKind、provider interruption、timeoutBudget 和 recoveryActions。
4. Command 级状态用 `describe command/<commandId> --run <runId>``result command/<commandId> --run <runId>`,确认 command state、ack、terminal status 和结果摘要。
5. Runner job 只读状态用 `describe runnerjob/<runnerJobId> --run <runId>`,确认 env image reuse、jobName、namespace、phase、exitCode、retention 和 `valuesPrinted=false`。不要为了这些字段手动调用 `trans G14:k3s kubectl ...`
6. Session trace/output 只在 `describe task` 或 result 里有实际 `sessionId` 时使用 `logs|ack|steer|send|cancel session/<sessionId>``sessionRef=null` 时不要猜 session 命令。
默认视图必须低噪声`--full` 展开结构化详情,`--raw` 才保留原始响应;命令返回里的 drill-down 应优先是 `bun scripts/cli.ts agentrun ...`,不得把人工 k8s 查询作为日常下一步。
默认视图必须低噪声且不是 JSON envelope`-o json|yaml` 才输出稳定机器结构,`--raw` 才保留官方 AgentRun bridge 原始响应;命令返回里的下一步应优先是 `bun scripts/cli.ts agentrun ...` 资源原语,不得把人工 k8s 查询作为日常下一步。
## HWLAB Code Agent 入口整合
HWLAB Code Agent / CaseRun follow-up 的日常派单也归入 AgentRun Queue/Sessions:新任务用 `queue submit --aipod Artificer` 或包含 HWLAB gitbundle 的 `queue submit --json-stdin`;运行中纠偏用 `sessions steer``sessions turn --aipod Artificer`。需要验证 HWLAB Web/Cloud API 原入口时,仍按 `$hwlab-code-agent` 使用 G14 `/root/hwlab-v02``hwlab-cli client agent ...` 拉取同一 trace/result/inspect;不要回到旧 `codex submit/resume/steer`
HWLAB Code Agent / CaseRun follow-up 的日常派单也归入 AgentRun 资源原语:新任务用 `create task --aipod Artificer` 或包含 HWLAB gitbundle 的 `apply -f -`;运行中纠偏用 `steer session/<sessionId>``send session/<sessionId> --aipod Artificer`。需要验证 HWLAB Web/Cloud API 原入口时,仍按 `$hwlab-code-agent` 使用 G14 `/root/hwlab-v02``hwlab-cli client agent ...` 拉取同一 trace/result/inspect;不要回到旧 `codex submit/resume/steer`
---
@@ -111,7 +109,7 @@ bun scripts/cli.ts codex move <taskId> --queue <queueId>
bun scripts/cli.ts codex tasks --view commander [--limit N]
```
返回旧 Code Queue 历史/残留任务的有界 action mapactive runners 计数、少量 active item、queued/retry_wait 计数、terminal-unread 总数、关键风险计数、分类计数和 drill-down 命令。新任务队列状态用 `agentrun queue commander`
返回旧 Code Queue 历史/残留任务的有界 action mapactive runners 计数、少量 active item、queued/retry_wait 计数、terminal-unread 总数、关键风险计数、分类计数和 drill-down 命令。新任务队列状态用 `agentrun get tasks --queue commander`
### Supervisor(分区视图)
@@ -189,7 +187,7 @@ bun scripts/cli.ts codex interrupt <taskId>
bun scripts/cli.ts codex cancel <taskId>
```
仅用于停止旧 Code Queue 残留任务;新 AgentRun session 使用 `bun scripts/cli.ts agentrun sessions cancel <sessionId>`
仅用于停止旧 Code Queue 残留任务;新 AgentRun session 使用 `bun scripts/cli.ts agentrun cancel session/<sessionId>`
---
+2 -2
View File
@@ -113,7 +113,7 @@ bun scripts/cli.ts platform-infra sub2api codex-pool expose --confirm
-`publicExposure` YAML 控制。默认公共端是 `publicBaseUrl`master 本地消费端是 `masterBaseUrl`
- `expose --confirm` 只为 YAML 指定的 `remotePort` 补 master `frps` allow port,并在 G14 创建/更新 `sub2api-frpc`
- master Caddy site 也由 `publicExposure.masterCaddy` 渲染;`responseHeaderTimeoutSeconds` 必须足够覆盖 Codex `/responses/compact` 长请求,避免 Caddy 先返回 504 而 Sub2API 后台实际稍后成功。
- master Caddy site 也由 `publicExposure.masterCaddy` 渲染;`responseHeaderTimeoutSeconds` 必须足够覆盖 Codex `/responses/compact` 长请求,避免 Caddy 先返回 504 而 Sub2API 后台实际稍后成功。具体数值只改 `config/platform-infra/sub2api-codex-pool.yaml`,修改后跑 `codex-pool expose --confirm`,再核对 Caddyfile 中渲染出的 `response_header_timeout`
- 同一个 FRP TCP 入口同时暴露 OpenAI-compatible API 和 Sub2API 管理 UI `/login`。不要另开第二个管理端口,除非 YAML 明确声明新的暴露决策。
- Sub2API Kubernetes Service 继续保持 ClusterIP。
@@ -152,7 +152,7 @@ bun scripts/cli.ts platform-infra sub2api codex-pool configure-local --confirm
- pool key 401:跑 `codex-pool sync --confirm` 重建 Sub2API key 与 k3s Secret 绑定,再跑 `codex-pool validate`
- 运行中过去的验证探针残留:只用 `codex-pool cleanup-probes --confirm` 清理 `unidesk-probe-*` 临时资源;不要把真实 managed account 删除当作探针清理或可用性恢复。
- FRP 不通:先看 `codex-pool expose --confirm` 输出的 `masterFrps``masterCaddy``sub2api-frpc` 和 public 401 probe;需要低层证据时只用 `trans G14:k3s` 做 bounded 查询。
- `/responses/compact` 约 30 秒后返回 504 Sub2API 日志稍后记录 `codex.remote_compact.succeeded` 时,优先检查 master Caddy `response_header_timeout` 是否由 YAML `publicExposure.masterCaddy.responseHeaderTimeoutSeconds` 渲染,修正后跑 `codex-pool expose --confirm`;这类边缘代理超时不会触发 Sub2API 账号级临时下线。
- `/responses/compact` 在接近 master Caddy `response_header_timeout` 的固定时长后返回 504,或 Sub2API 日志稍后记录 `codex.remote_compact.succeeded` 时,优先检查 master Caddy `response_header_timeout` 是否由 YAML `publicExposure.masterCaddy.responseHeaderTimeoutSeconds` 渲染,修正后跑 `codex-pool expose --confirm`;这类边缘代理超时不会触发 Sub2API 账号级临时下线。reload 前已经在途的 compact 请求仍可能按旧 timeout 结束,判断修复是否生效时只看 reload 之后新发起的请求。
- default profile 递归:检查 YAML default entry 是否使用 `*.pre-sub2api` 备份文件;必要时恢复备份后重新 `configure-local --confirm`
- 上游需要 WebSocket v2:先做 direct Codex WSv2 probe;通过后才给该 profile 配 `openaiResponsesWebSocketsV2Mode: ctx_pool|passthrough` 并跑 `sync --confirm`;把它当 capability candidate,容量仍以 YAML 中的 `capacity` 或默认值为准。
- Codex 启动 WebSocket 回退:用原入口 Codex smoke 复现,再用 bounded Sub2API 日志确认 account;对 WS handshake 4xx/5xx、`openai.websocket_account_select_failed` 或 close-before-`response.completed` 的账号关闭 YAML WSv2 能力后同步。若没有剩余 WSv2-capable account,把 `localCodex.supportsWebSockets``localCodex.responsesWebSocketsV2` 一起关掉,不把临时可用性推断写成调度配置。
+4 -4
View File
@@ -81,7 +81,7 @@ bun scripts/cli.ts agentrun control-plane cleanup-released-pvs --limit 200 --con
`cleanup-runs` 是 AgentRun `v0.1` 完成态 CI workspace retention 入口,只清理 `agentrun-ci` namespace 中超过 `--min-age-minutes``agentrun-v01-ci-*` PipelineRun,通过 Tekton ownerRef 释放临时 workspace PVC。dry-run 必须披露候选 PipelineRun、owned PVC、active mount 保护、local-path 实际估算 bytes 和 confirm 命令。默认保护最新完成的 PipelineRun,保留当前 CI/CD 状态证据。`cleanup-released-pvs` 是二次回收入口,只处理 `agentrun-ci``local-path``Delete` reclaim policy 的 `Released` PV;它不触碰 `agentrun-v01` runtime namespace、业务 PVC、Secret、registry storage 或 GitOps desired state。磁盘治理和 G14 safe-stop 规则见 `docs/reference/gc.md`
涉及 AgentRun runner egress、`transientEnv` 或 Secret 不泄露的 closeout,必须用真实 `queue dispatch``sessions turn``runner-jobs` 路径创建 `agentrun-v01` runner Job,再检查 runner job response、event/trace 和 Kubernetes Pod spec。通过证据应显示 proxy env 是否存在、`NO_PROXY` 是否包含 `hyueapi.com`/`.hyueapi.com`、短期 `HWLAB_API_KEY``transientEnv` 是否通过 per-job Secret 的 `valueFrom.secretKeyRef` 注入,以及 response/event 只输出 env name、Secret metadata 和 `valuesPrinted=false`。不得在 issue、trace 或 Pod spec 摘要中输出 Secret value。AgentRun 内部 SecretRef 合同以 AgentRun 仓库 `docs/reference/spec-v01-secret-distribution.md``docs/reference/spec-v01-runtime-assembly.md` 为权威;UniDesk 只记录验证入口和跨仓库归因。
涉及 AgentRun runner egress、`transientEnv` 或 Secret 不泄露的 closeout,必须用真实 `create/apply/send` 资源原语触发 `agentrun-v01` runner Job,再通过 `describe runnerjob/...``events run/...``logs session/...` 或必要的兼容 bridge 检查 runner job response、event/trace 和 Kubernetes Pod spec。通过证据应显示 proxy env 是否存在、`NO_PROXY` 是否包含 `hyueapi.com`/`.hyueapi.com`、短期 `HWLAB_API_KEY``transientEnv` 是否通过 per-job Secret 的 `valueFrom.secretKeyRef` 注入,以及 response/event 只输出 env name、Secret metadata 和 `valuesPrinted=false`。不得在 issue、trace 或 Pod spec 摘要中输出 Secret value。AgentRun 内部 SecretRef 合同以 AgentRun 仓库 `docs/reference/spec-v01-secret-distribution.md``docs/reference/spec-v01-runtime-assembly.md` 为权威;UniDesk 只记录验证入口和跨仓库归因。
通过 `g14-provider-egress-proxy.unidesk.svc.cluster.local:18789` 验证 `codeload.github.com` 时,必须同时确认 G14 runtime egress Service 有 ready endpoint。Service/DNS 存在但 Deployment `0/1`、Endpoint 只有 notReady address、Pod `ImagePullBackOff``ContainerStatusUnknown` 时,问题归为 UniDesk/G14 runtime egress 基础设施;不能把 runner 已注入 proxy env 后的 `connect refused` 归为 AgentRun 业务修复失败,也不能关闭要求“通过受控 proxy 成功访问 codeload”的 issue。
@@ -109,11 +109,11 @@ UniDesk 不能作为以下内容的事实来源:
## AgentRun Queue 与旧 Code Queue 边界
AgentRun `v0.1` 的指挥官任务面已经按 AgentRun issue #105 完成真实运行面验收,可作为新任务派发、Queue commander、trace/output、steer、turn/reuse、read 和 cancel 的 AgentRun 侧标准路径。长期使用时仍以 AgentRun 仓库自身 SPEC 为能力事实来源;UniDesk 只记录该路径已经通过 G14 `agentrun-v01` 运行面和 `hy` profile + `gpt-5.5` 验证。
AgentRun `v0.1` 的指挥官任务面已经按 AgentRun issue #105 完成真实运行面验收,可作为新任务派发、commander queue 观察、events/logs/result、steer/send、ack 和 cancel 的 AgentRun 侧标准路径。长期使用时仍以 AgentRun 仓库自身 SPEC 为能力事实来源;UniDesk 只记录该路径已经通过 G14 `agentrun-v01` 运行面和 `hy` profile + `gpt-5.5` 验证。
UniDesk 指挥官新任务入口固定使用 `bun scripts/cli.ts agentrun queue|sessions`。该入口是 G14 `/root/agentrun-v01` 中官方 `./scripts/agentrun --manager-url auto` CLI 的直接 bridge;日常派单、dispatch、turn 和 steer 优先用 `--json-stdin``--prompt-stdin``--runner-json-stdin``--*-file -` 的 quoted heredoc/stdin 形式,stdin 会通过管道直通官方 CLI,不先落 dump 文件。`--json-file``--prompt-file``--runner-json-file` 只用于已审阅且可复用的受控文件,bridge 会将其 materialize 到 G14 临时文件后传给官方 CLI。UniDesk 不实现 AgentRun queue 协议,也不把任务 double-write 回旧 Code Queue。
UniDesk 指挥官新任务入口固定使用 `bun scripts/cli.ts agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send` 资源原语。该入口是 G14 `/root/agentrun-v01` 中官方 `./scripts/agentrun --manager-url auto` CLI 的低噪声包装;默认 human 输出只显示表格、生命周期摘要和下一步命令,脚本读取显式使用 `-o json|yaml`,官方 bridge 原始响应显式使用 `--raw`。日常派单优先用 `agentrun create task --aipod Artificer --prompt-stdin``agentrun apply -f -` 的 quoted YAML/JSON heredoc/stdin 形式;旧 bridge 的 `--json-file``--prompt-file``--runner-json-file` 只用于已审阅且可复用的兼容调试。UniDesk 不实现 AgentRun queue 协议,也不把任务 double-write 回旧 Code Queue。
`agentrun control-plane ...``agentrun queue|runs|commands|runner|sessions ...` 共用同一 UniDesk SSH capture bridge。主 server 本机可继续使用本地 backend-core brokerAgentRun runner、Artificer 或其他没有本地 Docker / `unidesk-backend-core` 容器的环境会自动改走既有 frontend `/ws/ssh` WebSocket backend,并在输出的 `bridge.capture.backend``reason``localBackendCore` 中披露选择依据。本地 `unidesk-backend-core` 容器不是 runner 环境使用这些 AgentRun CLI 入口的隐式前置条件。若所有 capture backend 都不可用,CLI 必须返回 `failureKind=bridge-execution-environment``capture-backend-unavailable``bridge-execution-environment-unavailable`,并给出受控恢复入口;AgentRun 官方 CLI 自身返回的 run/command/schema 错误不得被改写成 bridge 失败。
`agentrun control-plane ...`、资源原语和兼容 bridge 组共用同一 UniDesk SSH capture bridge。主 server 本机可继续使用本地 backend-core brokerAgentRun runner、Artificer 或其他没有本地 Docker / `unidesk-backend-core` 容器的环境会自动改走既有 frontend `/ws/ssh` WebSocket backend,并在输出的 `bridge.capture.backend``reason``localBackendCore` 中披露选择依据。本地 `unidesk-backend-core` 容器不是 runner 环境使用这些 AgentRun CLI 入口的隐式前置条件。若所有 capture backend 都不可用,CLI 必须返回 `failureKind=bridge-execution-environment``capture-backend-unavailable``bridge-execution-environment-unavailable`,并给出受控恢复入口;AgentRun 官方 CLI 自身返回的 run/command/schema 错误不得被改写成 bridge 失败。
AgentRun Queue 任务如果需要调用 UniDesk 维护桥,例如 `trans` / `unidesk-ssh`,长期契约以 AgentRun 仓库 `docs/reference/spec-v01-runtime-assembly.md``docs/reference/spec-v01-secret-distribution.md` 为准:调用方通过 `executionPolicy.secretScope.toolCredentials[].tool=unidesk-ssh` 请求 `UNIDESK_SSH_CLIENT_TOKEN` SecretRef;非敏感 endpoint 由 runner-job `transientEnv` 显式提供,或由 manager 受控默认值自动补齐。UniDesk bridge 提交 Queue payload 时不得在 prompt、payload 或 `transientEnv` 中携带 token,也不得使用 HWLAB runtime Web 入口冒充 UniDesk frontend。若 dispatcher 已正确请求 `unidesk-ssh` 但 trace 的 `runner-job-created.transientEnv.names` 没有 `UNIDESK_MAIN_SERVER_IP``UNIDESK_MAIN_SERVER_HOST``UNIDESK_FRONTEND_URL`,归为 AgentRun assembly 问题;若 endpoint env 已存在但 route denied/timeout,再按 UniDesk frontend/token scope 或 provider session 排查。
+9 -9
View File
@@ -50,7 +50,7 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 runtime lane 滚动
- `dev-env validate [--manifest path] [--kubectl-dry-run]` 离线校验 D601 `unidesk-dev` namespace、dev PostgreSQL 底座和 dev workload manifest。默认检查 `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-foundation.k8s.yaml`;也可显式校验 `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k8s.yaml``src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-code-queue.k8s.yaml`。所有 namespaced 对象必须只落到 `unidesk-dev`foundation manifest 必须包含 `postgres-dev` StatefulSet/Service、dev secret/config、迁移 Job 和 DB URL guardcore manifest 必须包含 `backend-core-dev`/`frontend-dev` Deployment/ServiceCode Queue dev manifest 必须包含 `code-queue-scheduler-dev``code-queue-read-dev``code-queue-write-dev`、dev provider egress proxy,以及只读挂载宿主 `/home/ubuntu/.agents/skills` 到容器 `/root/.agents/skills``skills-dir` volume。加 `--kubectl-dry-run` 时额外以 `KUBECONFIG=/etc/rancher/k3s/k3s.yaml` 执行 `kubectl apply --dry-run=client --validate=false -f <manifest>`,仍不 apply 资源;默认 `docker-desktop` kubeconfig 不得作为 D601 dry-run 目标。
- `dev-env prewarm-images [--image image] [--provider-id D601] [--no-pull] [--proxy-url URL] [--pull-timeout-ms N] [--dry-run]` 创建异步 job,通过 UniDesk SSH 维护桥在 D601 上把开发底座依赖镜像从 Docker 缓存导入原生 k3s containerd。默认镜像是 `postgres:16-alpine``rancher/mirrored-library-busybox:1.36.1`,用于避免 `postgres-dev` 与 local-path helper pod 卡在外部 registry 拉取。该命令固定验证 `/etc/rancher/k3s/k3s.yaml` 指向的 native k3s 上下文,并输出 `dev_env_containerd_image_ready=...` 作为成功判据;它不 apply manifest、不修改生产 `unidesk` namespace。
- `artifact-registry plan|render|status|health|install|deploy-backend-core|deploy-service` 管理 D601 host-managed CNCF Distribution registry 的声明、安装、只读检查和 pull-only artifact CD。该 registry 固定为 D601 loopback `127.0.0.1:5000`,由 systemd + Docker Compose 管理,位于 native k3s 故障域外;`deploy-service` 只拉取 CI 已发布的 commit-pinned 镜像、retag/recreate 或导入 native k3s,并做 live commit 验证,不构建 runtime source。`deploy-backend-core` 是 deprecated 兼容名,标准 backend-core prod CD 入口是 `deploy apply --env prod --service backend-core`。长期规则见 `docs/reference/artifact-registry.md`
- `commander contract|plan --dry-run|smoke --dry-run|approval request --dry-run|prompt-lint --kind gpt55-pr` 是 host Codex 指挥官直管微服务 skeleton 入口。当前命令返回 `phase=source-contract`、service/API/state/bridge/prompt/trace/#20/#46/ClaudeQQ 审批边界、.state/commander/ 状态模型、dev 无 daemon smoke contract、dry-run 计划和 GPT-5.5 PR prompt 边界辅助 lint,不接 live bridge、不注入 prompt、不发送 ClaudeQQ。`approval request --dry-run` 会生成 200 字以内中文纯文本 ClaudeQQ 审批草案、`notification-path-unavailable` blocker 和授权后唯一可用的 `bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw` 命令;不得提示使用本机 ClaudeQQ skill、powershell 或本地 server。`prompt-lint` 支持 `--prompt-file``--stdin`,输出 `ok``missingClauses``riskLevel``suggestedPatchSnippet` 且不回显完整 prompt;它是 commander 辅助检查,不是业务 PR 门禁;legacy `codex submit` 已冻结,新任务 payload 审查走 AgentRun Queue`plan``smoke``approval request` 必须带 `--dry-run`;缺少时返回 `error=dry-run-required`。长期规则见 `docs/reference/host-codex-commander.md`
- `commander contract|plan --dry-run|smoke --dry-run|approval request --dry-run|prompt-lint --kind gpt55-pr` 是 host Codex 指挥官直管微服务 skeleton 入口。当前命令返回 `phase=source-contract`、service/API/state/bridge/prompt/trace/#20/#46/ClaudeQQ 审批边界、.state/commander/ 状态模型、dev 无 daemon smoke contract、dry-run 计划和 GPT-5.5 PR prompt 边界辅助 lint,不接 live bridge、不注入 prompt、不发送 ClaudeQQ。`approval request --dry-run` 会生成 200 字以内中文纯文本 ClaudeQQ 审批草案、`notification-path-unavailable` blocker 和授权后唯一可用的 `bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw` 命令;不得提示使用本机 ClaudeQQ skill、powershell 或本地 server。`prompt-lint` 支持 `--prompt-file``--stdin`,输出 `ok``missingClauses``riskLevel``suggestedPatchSnippet` 且不回显完整 prompt;它是 commander 辅助检查,不是业务 PR 门禁;legacy `codex submit` 已冻结,新任务 payload 审查走 AgentRun `create/apply` 资源原语`plan``smoke``approval request` 必须带 `--dry-run`;缺少时返回 `error=dry-run-required`。长期规则见 `docs/reference/host-codex-commander.md`
- `hwlab g14 retirement status|plan|execute --confirm [--wait]` 是 legacy G14 DEV/PROD 的受控退役入口。`status` 只读报告 `argocd/hwlab-g14-dev``argocd/hwlab-g14-prod``hwlab-dev``hwlab-prod`、bounded legacy resource preview、受保护的 `hwlab-g14-v02`/`hwlab-node-v03``hwlab-v02`/`hwlab-v03`,以及 `.state/hwlab-g14/legacy-g14-retirement.json` marker。`plan` 是 dry-run,只列出 destructive targets 和 protected targets。`execute --confirm` 删除 legacy Argo Applications 与 legacy namespaces,取消本地 active `hwlab_g14_pr_monitor` job,并写 retirement marker 记录执行证据;`hwlab g14 monitor-prs --lane g14` 按退役合同固定结构化失败并指向 `retirement status` 和 runtime lane monitor `--lane v02|v03`。该入口禁止触碰 v0.2/v0.3 Application、namespace、PipelineRun、Secret、Git mirror 或 FRP desired state。
- `hwlab g14 monitor-prs --lane v02` 是 HWLAB `v0.2` 的 PR -> CI -> CD 自动化入口。它只监控 base=`v0.2` 的 open PR:每轮先用 UniDesk `gh pr preflight` 读取 GitHub CI/checks、mergeability 和冲突状态;pending 时在 PR 下写等待评论,blocked/conflict 时写阻塞评论;ready 时直接用 UniDesk `gh pr merge` 合并,不因为其他 commit 的运行中 PipelineRun 阻塞 merge 或 CI 启动。合并后执行受控 `control-plane trigger-current --lane v02 --confirm --wait`、轮询定点 `control-plane status --lane v02 --source-commit <merge-sha>`,必要时执行 `git-mirror flush --confirm --wait`。v0.2 CD 采用 latest-only:旧 PipelineRun 不取消、不等待,但 promotion 写 `v0.2-gitops` 前必须重新确认 source headstale commit 只能以 superseded/no-op 收口,不能回滚 runtime。不管 CD 成功、superseded、失败或超时,都在原 PR 下用 `gh pr comment create --body-stdin <<'EOF'` 追加语义化状态,正文固定包含起止时间、总耗时、冲突状态、CI/preflight conclusion、source commit、PipelineRun、targetValidation、Argo/webAssets 和 git mirror pendingFlush/githubInSync。评论去重状态写入 `.state/hwlab-g14/v02-pr-comment-signatures.json`,同一状态签名不会重复刷评论;v0.2 monitor 指针使用 `.state/hwlab-g14/latest-v02-monitor-job.json``latest-v02-once-job.json``latest-v02-dry-run-job.json``latest-v02-once-dry-run-job.json`,不会覆盖默认 G14 monitor 指针。`--lane v02 --once --dry-run` 只做单轮 preflight/merge/CD/comment plan,不写 GitHub、不触发 CD。
- `hwlab g14 monitor-prs --lane v03` 是 HWLAB `v0.3` 的 PR -> CI -> 自动合并 -> CD 入口。它只监控 base=`v0.3` 的 open PR:每轮先通过 UniDesk `gh pr preflight` 读取 GitHub checks、mergeability 和冲突状态;pending 时只在 PR 下写等待评论;失败 check、preflight blocker 或 conflict 时在 PR 下写阻塞评论,并按标题去重创建或更新 HWLAB failure issue。ready 时通过 UniDesk `gh pr merge` 合并,随后执行 runtime lane `control-plane trigger-current --lane v03 --confirm --wait`,轮询 `control-plane status --lane v03 --source-commit <merge-sha>`,判定 PipelineRun `True`、Argo `Synced/Healthy``hwlab-v03` runtime workload 可见、20666/20667 public probes 通过,并在必要时执行 `git-mirror flush --lane v03 --confirm --wait`。CD 成功、失败或超时都会在原 PR 下写语义化状态评论;失败和超时同时创建或更新 failure issue,正文必须包含 PR、base/head、commit、PipelineRun、失败阶段、preflight/CD 摘要和下一步 CLI。评论去重状态写入 `.state/hwlab-g14/v03-pr-comment-signatures.json`monitor 指针使用 `.state/hwlab-g14/latest-v03-monitor-job.json``latest-v03-once-job.json``latest-v03-dry-run-job.json``latest-v03-once-dry-run-job.json``--lane v03 --once --dry-run` 只做单轮 preflight/merge/CD/comment/issue plan,不写 GitHub、不触发 CD。
@@ -74,7 +74,7 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 runtime lane 滚动
- `trans gh:/owner/repo ...` 把 GitHub issue/PR 映射成只读/受控写入的虚拟文本目录,适合日报、PR 正文和 issue 正文的小补丁维护:`trans gh:/pikasTech/HWLAB ls` 展示 `pr/``issue/``trans gh:/pikasTech/HWLAB/pr ls [--limit N] [--full]``trans gh:/pikasTech/HWLAB/issue ls [--limit N] [--full]` 展示条目状态、楼层数、正文长度和标题,`trans gh:/pikasTech/HWLAB/pr/507 ls` 展示单个 PR 的一楼正文文件,`trans gh:/pikasTech/HWLAB/505/1 cat|rg|patch-apply` 兼容旧式 issue/PR number route。`patch-apply` 使用 UniDesk 默认 apply-patch v2 的虚拟文件 executor,把正文一楼映射为 `body.md`,写回仍走 `bun scripts/cli.ts gh issue/pr update` 的 guard/concurrency 规则;`rm` 对正文一楼结构化拒绝,避免误删 issue/PR 正文。大正文读取必须展开 UniDesk gh dump 文件,否则 `cat/rg/patch-apply` 会误读为空,这是 `gh:` 虚拟文件接口的 P0 可见性契约。
- `hwlab cd status|audit|preflight|apply --env dev [--dry-run]` 是旧 D601 HWLAB DEV CD 指挥侧 wrapper,仅用于显式 legacy 诊断和迁移对照。默认通过 UniDesk provider `host.ssh` 进入 D601,再调用 HWLAB repo-owned `scripts/dev-cd-apply.mjs`,不内嵌发布 kubectl 逻辑:`status` 汇总固定 CD mirror、Git clean/main/origin-main、`deploy/deploy.json`/artifact catalog/report、D601 native k3s guard 和 CD Lease lock,并用 `scripts/dev-cd-apply.mjs --status --skip-live-verify` 取得 target/promotion 摘要;`audit` 在 k3s/CD 恢复后做只读健康审计,返回有界 JSON 的 blocker 分类、D601 guard/node、SecretRef 存在性、registry 可达性、Lease phase/holder/staleness、deploy.json 与 artifact/workload image 收敛、current Deployment image/revision/rollout、16666/16667 public health commit/readiness 和 DB/runtime durability 摘要;`preflight` 进一步检查必需 SecretRef 对象/键存在性并运行 HWLAB `scripts/dev-cd-apply.mjs --dry-run --skip-live-verify` 受控事务摘要。完整远端 stdout/stderr 写入 D601 `~/.state/unidesk-hwlab-cd/<run-id>/` 和本地 `.state/hwlab-cd/<run-id>/` task dumpstdout 只返回有界摘要。默认 HWLAB CD repo 是 `/home/ubuntu/hwlab_cd``/home/ubuntu/hwlab` runner 历史目录不得作为发布真相。wrapper 强制 `KUBECONFIG=/etc/rancher/k3s/k3s.yaml` 并只以这个显式目标作为 gate;显式目标出现 `docker-desktop``desktop-control-plane``127.0.0.1:11700` 信号会结构化拒绝,audit/preflight/apply --dry-run 都必须观察到 node `d601`。真实 apply 只暴露 `scripts/dev-cd-apply.mjs --apply --confirm-dev --confirmed-non-production --write-report` 命令形状并标注 host-commander-only,本 runner 不执行 live apply、rollout、Lease mutation 或 DEV deploy apply。长期规则见 `docs/reference/hwlab.md`
- `gh auth status [--repo owner/name]` 探测 GitHub 操作前置条件并输出脱敏 JSON:是否存在 `gh` binary、是否存在 `GH_TOKEN`/`GITHUB_TOKEN` 或可用 `gh auth token` fallback、REST API 是否可达、目标 repo 是否可见、issue 是否可读。degraded reason 必须归类为 `missing-binary``missing-token``auth-failed``github-transient``network-proxy-failed``permission-denied``repo-not-found``repo-forbidden``issue-not-found``pr-not-found``scope-insufficient``validation-failed``invalid-response``unsupported-command`,不得打印 token;失败对象必须包含 `runnerDisposition=infra-blocked|business-failed`runner 应优先用该字段分流。`github-transient` 表示 GitHub DNS/API 连接在收到 HTTP 状态前失败,输出应带 `retryable=true` 或等价 commander action;这不是缺 token、认证失败、权限不足或 PR 语义失败。
- `codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]` 是派发前的本地 dry-run prompt lint。它只读取 prompt 文本,返回 `dryRun=true``mutation=false``declaredClass``effectiveClass``requiredClass``dispatchDisposition`、缺失或矛盾项和有界 evidence,不访问 live service、不提交任务、不打印完整 prompt。分级固定为 `read-only``live-read``live-mutating`;未声明时按 `read-only` 处理。新任务走 AgentRun Queue,指挥官应把 lint 结果纳入 `agentrun queue submit` payload 审查。长期规则见 `docs/reference/code-queue-supervision.md` 的 DEV 测试授权分级。
- `codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]` 是派发前的本地 dry-run prompt lint。它只读取 prompt 文本,返回 `dryRun=true``mutation=false``declaredClass``effectiveClass``requiredClass``dispatchDisposition`、缺失或矛盾项和有界 evidence,不访问 live service、不提交任务、不打印完整 prompt。分级固定为 `read-only``live-read``live-mutating`;未声明时按 `read-only` 处理。新任务走 AgentRun `create/apply` 资源原语,指挥官应把 lint 结果纳入 task manifest 或 prompt 审查。长期规则见 `docs/reference/code-queue-supervision.md` 的 DEV 测试授权分级。
- `gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--search text] [--label label[,label...]]... [--repo owner/name] [--json number,title,state,url,updatedAt,createdAt,author,labels] [--raw|--full]` 通过 GitHub REST 列出 issue,默认 `state=open``limit=30`,输出稳定 JSON 且不依赖系统 `gh` binary。`owner/repo` 位置参数是 `--repo owner/repo` 的兼容别名;若位置 repo 与 `--repo` 冲突,或位置参数不是 `owner/repo`,必须结构化失败,禁止静默 fallback 到默认 repo。`--limit` 是 CLI 返回上限,不等同 GitHub 单页 `per_page`:当 `--limit > 100` 或默认页中混入 PR 时,CLI 必须分页抓取 GitHub REST/Search page,过滤 PR 后再返回 issue,并在输出中披露 `pagination.fetchedPages/rawCount/hasMore``hasMore=true` 时只能说明当前有界扫描未穷尽,禁止把它当作“仓库没有更多 issue”。`--search` 使用 GitHub Search Issues API,并自动追加 `repo:<owner>/<name>``type:issue` 和 state qualifier,用于创建新 issue 前做低摩擦查重;未知 state 或未知 `--json` 字段必须结构化失败并带 `runnerDisposition=business-failed``--label` 是 GitHub REST `labels=label1,label2` 或 Search `label:` 服务端过滤,支持重复 `--label` 和逗号分隔;filter 不在本命令上下文中使用(如 `issue read``pr list`)必须结构化失败并指明 `gh issue create/list/stale-close` 才是合法作用域。GitHub issues API 可能混入 PRCLI 会从 `.data.issues` 中过滤 pull request。`--raw|--full``gh issue list` 上是绕过 20 KiB stdout 截断的显式开关:响应结果会带 `noDump=true``output.ts` 据此跳过 head/tail 替换并把完整数据 inline 输出;当响应未超阈值时 `--raw|--full` 行为等价默认。
- `gh issue lifecycle``--state` 只能作为 `gh issue list` / `gh issue board-row list` / `gh pr list` 的过滤参数;`gh issue update` / `gh issue edit` 只写 body/title**不接受** `--state` 改 open/closed。把 `gh issue update <n> --state closed` 落到错命令上时,CLI 必须返回 `validation-failed` 并显式提示 `gh issue close <n>` / `gh issue reopen <n>`PR 用 `gh pr close|reopen <n>`),并把 5 条受支持命令放进 `supportedCommands`,禁止把"无 `--state` 改 issue 状态"的命令升级为"接受 `--state`"。`gh issue close|reopen` 成功输出默认是 compact issue 摘要,不得回显完整 `issue.body`;需要正文时后续使用返回的 `readCommands``gh issue view --json body|--full|--raw`。生命周期 close/reopen 的评论推荐用 `--comment-stdin <<'EOF'` 直接写 heredoc/stdin;短单行可用 `--comment`,已有复用文件才用 `--comment-file`。需要附长篇 CLI 验收证据时,先用 `gh issue comment create <n> --body-stdin <<'EOF'` 写证据评论,再用 `gh issue close <n> --comment <短引用>` 关闭。issue 硬删除走 `close`PR 硬删除走 `close`,两者都没有"delete"语义。
- `gh issue comment create <number|owner/repo#number> --repo owner/name --body-stdin``gh issue comment update|edit <commentId> --repo owner/name --body-stdin``gh issue comment delete <commentId|owner/repo#number> --repo owner/name``gh issue close <number|owner/repo#number> --repo owner/name [--comment <text>|--comment-stdin]``gh issue reopen <number|owner/repo#number> --repo owner/name [--comment <text>|--comment-stdin]``gh issue update <number|owner/repo#number> --repo owner/name [--title ...] [--body-stdin]``gh issue edit <number|owner/repo#number> ...``gh issue board-row get|update|add|move|delete|upsert <number|owner/repo#number> --repo owner/name ...` 都接受与 `gh issue view|read``gh pr *` 一致的 `owner/repo#number` 位置 shorthandshorthand 与显式 `--repo` 冲突时结构化失败并把两者都回显到错误对象里,避免静默改写目标 repo。`gh issue view|read``gh pr view|read|files|diff|preflight|closeout|comment create|comment update|comment edit|comment delete|close|reopen|merge|edit|update` 已长期支持该 shorthandcomment update/edit/delete 的 `--number` 表示 commentId,不是 issue/PR number。issue 写命令对齐后整个 `gh` 子命令在 shorthand 行为上保持一致,不再需要把 `pikasTech/HWLAB#621` 拆成 `621 --repo pikasTech/HWLAB`。来源:HWLAB #621 CLI 验收 `gh issue comment create pikasTech/HWLAB#621` 摩擦改进。
@@ -93,9 +93,9 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 runtime lane 滚动
- `ci install|status|run|publish-backend-core|publish-user-service|run-dev-e2e|logs` 管理 D601 原生 k3s 上的 Tekton CI。`run` 手动创建每 commit 检查和 Code Queue 只读性能门禁;`publish-backend-core``publish-user-service` 从 pushed Git commit 构建并发布 `127.0.0.1:5000/unidesk/<service>:<commit>` commit-pinned artifacts,输出 `artifactSummary`(含 `serviceId``sourceCommit``sourceRepo``dockerfile``imageRef``tag``digest``digestRef`),但不部署生产;`run-dev-e2e` 的 Git 控制 runner、短 launcher、host fetch 边界、临时 smoke namespace 和 no-CD 规则只在 `docs/reference/dev-ci-runner.md` 定义;Tekton CI 通用规则见 `docs/reference/ci.md`
- `schedule list|get|runs|run|retry-run|delete|upsert-pgdata-backup` 管理 backend-core 定时任务和运行历史。`schedule list``schedule get``schedule runs --limit N``schedule runs <scheduleId> --limit N` 是只读观察入口;`schedule run``schedule retry-run``schedule delete``schedule upsert-pgdata-backup` 会触发运行或写入配置,生产恢复时必须有明确授权。`schedule runs --limit N` 是全局历史视图,返回 `scope=global``scheduleId=null``schedule runs <scheduleId> --limit N` 是指定 schedule 历史视图,返回 `scope=schedule` 和对应 `scheduleId`。CLI 必须拒绝 `schedule runs 50` 这类纯数字位置参数,并提示使用 `schedule runs --limit 50`,避免把空数组误判成“没有历史 run”。`schedule run <id> --wait-ms N` 触发同一 schedule,并且即使 wait 超时也必须返回 `newRunId``observeCommand``schedule retry-run <failedRunId>` 只接受 failed run,从原 run 反查 `scheduleId` 后重触发同一 schedule,并输出 `originalRunId``scheduleId``newRunId``observeCommand`。当 backend-core 目标容器缺失或只观察到 verify-only 容器时,schedule/microservice 命令必须以非零退出并返回 `failureKind=target-stack-not-running``runnerDisposition=infra-blocked``readOnlyCommands``authorizationRequiredForRecovery`,不得把 Docker 的 `No such container` 当成成功的空历史。
- `codex deploy <commitId>` 是旧 Code Queue 兼容部署入口,已禁用以防止维护通道直连 D601 部署 Code Queue;当前 dev 自动化只做 `ci run-dev-e2e` smoke,不提供 Code Queue CD,详细规则见 `docs/reference/codex-deploy.md`
- `agentrun queue|sessions` 是当前指挥官新任务和 AgentRun session 控制入口。UniDesk CLI 通过 G14 `/root/agentrun-v01` 中官方 `./scripts/agentrun --manager-url auto` 执行`queue commander` 查看指挥官队列,`queue submit --json-stdin` 创建新任务,`queue dispatch <taskId> --json-stdin` 派发,`queue read/cancel` 标记和取消队列任务;`sessions trace/output/read/steer/cancel` 读取和控制已创建 session。日常一次性 JSON、prompt 和 runner JSON 输入优先用 quoted heredoc/stdin`--json-file``--prompt-file``--runner-json-file` 只用于已审阅且可复用的受控文件。本地 bridge 对 stdin 直通官方 AgentRun CLI,不先落 dump 文件;它不是旧 Code Queue adapter,不做双写,也不迁移旧历史
- `agentrun control-plane ...``agentrun queue|runs|commands|runner|sessions ...` 共用 UniDesk SSH capture bridge。主 server 本机可使用本地 backend-core brokerArtificer/AgentRun runner 等没有本地 Docker 或 `unidesk-backend-core` 容器的环境会自动使用 frontend `/ws/ssh` WebSocket backend,并在 `bridge.capture.backend``reason``localBackendCore` 中披露选择依据。本地 `unidesk-backend-core` 不是 runner 环境的隐式前置条件;若 capture backend 不可用,错误必须归类为 `failureKind=bridge-execution-environment` 并给出受控恢复入口。
- `codex submit/enqueue``codex steer``codex resume``codex queue create``codex queue merge``codex move`、旧 Web 提交表单、旧队列管理和旧 workdir 管理是冻结的 legacy Code Queue 写入口。CLI 必须返回 `ok=false``frozen=true``degradedReason=legacy-code-queue-frozen` 和 AgentRun 替代命令;服务端旧 API 写入口必须返回 410。新任务、steer、trace/output、read 和 cancel 走 AgentRun Queue/Sessions
- `agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send` 是当前指挥官新任务和 AgentRun session 控制入口。UniDesk CLI 通过 G14 `/root/agentrun-v01` 中官方 `./scripts/agentrun --manager-url auto` 执行,默认 human 输出只显示表格、生命周期摘要和下一步命令;脚本读取显式使用 `-o json|yaml`,官方 bridge 原始响应显式使用 `--raw`。日常查看用 `get tasks --queue commander``describe task/<taskId>``events run/<runId>``logs session/<sessionId>``result run/<runId> --command <commandId>`;日常写入用 `create task --aipod Artificer --prompt-stdin``apply -f -``steer/send session/<sessionId>``ack/cancel task|session/<id>`。兼容 bridge 组 `queue|runs|commands|runner|sessions|aipod-specs` 只保留为 raw/debug 和尚未包装的低频能力入口
- `agentrun control-plane ...`、资源原语和兼容 bridge 组共用 UniDesk SSH capture bridge。主 server 本机可使用本地 backend-core brokerArtificer/AgentRun runner 等没有本地 Docker 或 `unidesk-backend-core` 容器的环境会自动使用 frontend `/ws/ssh` WebSocket backend,并在 `bridge.capture.backend``reason``localBackendCore` 中披露选择依据。本地 `unidesk-backend-core` 不是 runner 环境的隐式前置条件;若 capture backend 不可用,错误必须归类为 `failureKind=bridge-execution-environment` 并给出受控恢复入口。
- `codex submit/enqueue``codex steer``codex resume``codex queue create``codex queue merge``codex move`、旧 Web 提交表单、旧队列管理和旧 workdir 管理是冻结的 legacy Code Queue 写入口。CLI 必须返回 `ok=false``frozen=true``degradedReason=legacy-code-queue-frozen` 和 AgentRun 替代命令;服务端旧 API 写入口必须返回 410。新任务、steer/send、events/logs/result、ack 和 cancel 走 AgentRun 资源原语
- 旧 Code Queue 只保留历史归档、只读排障和残留任务停止。`codex task/tasks/output/read/unread/queues` 继续通过 backend-core 私有代理读取旧 PostgreSQL 历史;`codex interrupt|cancel <taskId>` 只用于停止旧运行面残留任务。旧 `steer-confirm` 只作为历史 trace confirmation 查询,不是新任务控制入口。
- `codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full|--raw]` 通过稳定 `code-queue` proxy 请求 D601 scheduler `/api/runtime-preflight`,用于 PR 型派单 admission。默认输出是紧凑 commander 视图,显式分出 `schedulerPreflight``activeRunnerPrCapability`,并附带 `commands``disclosure`,方便先看 scheduler auth 缺口、再看当前 runner/dev container 的 `gh auth status``gh pr create --dry-run` 能力;`--full``--raw` 才展开完整 `preflight`、工具、agent port、Git worktree、GitHub egress、repo/issue/PR 只读探测和观测原文。只报告 `GH_TOKEN`/`GITHUB_TOKEN` 是否存在和来源 key,不打印值。当 auth-broker 配置存在时,`tokenCoverage.source="auth-broker"``credentialSource="broker-issued-token"` 且 runner env token 不是成功前提;当仅 env token 存在时,`credentialSource="env-token"``authBroker.nextAction="use-env-token-until-auth-broker-live"`;两者都缺失时顶层 `ok=false``runnerDisposition=infra-blocked``degradedReason=auth-broker-needed``tokenCoverage.missing` 同时列出 `GH_TOKEN``GITHUB_TOKEN`,并输出 `authBroker.source="broker/auth-broker-needed"``capability.source="missing-token"`。该 `auth-missing` 的 scope 是 `scheduler-runner-env`,不能简化成“当前 active runner/dev container 不能创建 PR”;默认视图必须带 `scopeBoundary``activeRunnerPrCapability`。GitHub DNS/API 连接失败应归类为 `failureKind=github-transient``degradedReason=github-dns-api-transient`,并带 `retryable=true``commanderAction=retry-backoff-or-keep-running-if-heartbeat-fresh` 和有界 `githubTransient.failedProbes`;调用方应重试/退避,且在任务 heartbeat/trace 新鲜时继续监督,不把它当成 auth 缺失或 PR 语义失败。`prCapability` 是 runner-facing 合同摘要,必须包含目标分支、token/auth 来源、`systemGhBinaryRequiredForWrites=false`、UniDesk REST `bun scripts/cli.ts gh` 可用性、push dry-run/PR create dry-run 的 `writesRemote=false`、expected PR handoff、真实 PR 创建需要 commander 授权,以及 guarded `gh pr merge --dry-run` 预检路径;系统 `gh` binary 缺失只进入 `tools.systemGhBinary`,不得误判为 UniDesk REST `gh` CLI 不可用。`--remote` 在 runner-like 环境里不再依赖本地 `unidesk-backend-core``unidesk-database``baidu-netdisk-backend` 容器存在;这些缺失只作为本地观测证据。若远程控制面可达,则继续走远程控制面结果;若远程控制面不可达,则结构化返回 `failureKind=control-plane-missing` / `degradedReason=remote-control-plane-unreachable`,而不是把本地 `backend-core-container-missing` 当作最终阻塞。`--pr-create-dry-run` 不 POST GitHub,只证明 runner 内 PR body 生成、`scripts/cli.ts gh pr create --dry-run` 和 branch 参数形态可用;服务端创建权限仍以 token/auth broker、repo/issue/PR read、push dry-run 和最终授权后的真实 PR 创建结果为准。
- `codex task <taskId>` 通过 Code Queue 私有代理按任务 ID 查询结构化审阅摘要;默认只返回任务身份、执行 Provider、工作目录、attempt 计数、原始 prompt、最终 response、最后错误和渐进披露命令,适合指挥官审阅完成未读任务且避免上下文爆炸。`--detail` 仍是有界详细摘要:默认只返回少量 attempt/tool 行、短 prompt/response/stderr/feedback 预览和 omitted/truncated 元数据;需要完整 prompt/response 文本或更多 tool/attempt 细节时再显式加 `--full``--tool-limit N``--trace``codex output`。该摘要读取默认由主 server `code-queue-mgr` 从 PostgreSQL 返回,不依赖 D601 `code-queue-read` Service 可用。
@@ -107,10 +107,10 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 runtime lane 滚动
- `codex dev-ready` 查询 Code Queue `/api/dev-ready` 并返回有界 readiness 摘要,包括工具、Docker、Codex config、SSH 和 `devReady.skills``devReady.skills` 只暴露 `UNIDESK_SKILLS_PATH`、是否存在、是否只读、skillCount、`cli-spec` 是否可见和修复建议,不输出宿主 auth/token 文件内容。
- `codex judge <taskId> --attempt N [--dry-run] [--include-prompt]` 通过 Code Queue 私有代理按指定 attempt 单步复现 judge;这是执行面诊断入口,仍依赖 D601 scheduler/runner 侧的真实 judge builder、MiniMax 调用路径和执行环境。默认会真实调用 MiniMax,`--dry-run` 只返回 prompt/payload 大小、attempt 窗口和重建来源诊断,`--include-prompt` 仅用于本地深度排查。
- `codex steer-confirm <taskId> --steer-id <id> [--raw]` 是只读 trace confirmation lookup。默认输出 `traceConfirmation.found/accepted/deliveryState/trace.seq/trace.at/promptChars/promptHash``delivery.status`,不回显 prompt`--raw` 才附带原始 backend confirmation body。该命令用于处理 stable-proxy abort 后的 `deliveryUnconfirmed`,不要用重复 prompt 代替确认查询。
-`codex steer` 已冻结;`codex steer-confirm` 只作为历史 trace confirmation lookup。新运行中纠偏使用 `bun scripts/cli.ts agentrun sessions steer <sessionId> --prompt-stdin`,并用 `sessions trace/output/read` 观察。
-`codex steer` 已冻结;`codex steer-confirm` 只作为历史 trace confirmation lookup。新运行中纠偏使用 `bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin`,并用 `logs session/<sessionId>``events run/<runId>``result run/<runId> --command <commandId>``ack session/<sessionId>` 观察。
- `codex interrupt|cancel <taskId>` 通过 Code Queue 私有代理请求中断;running/judging 任务会请求 D601 当前 agent run 停止,queued/retry_wait 任务的取消也必须保持与 WebUI 相同代理路径,返回有界 task 摘要和后续查询命令。任何需要接触 active run 的动作仍属于 D601 执行面。
- 旧 Code Queue 多队列 lane 现在是归档视图:`codex queues [--full|--all] [--limit N] [--page N|--offset N]` 只读展示历史 queue 摘要、activity、commanderConcurrency、counts 和 execution diagnostics。`queue create``queue merge``move` 等旧队列写入口冻结并返回 `legacy-code-queue-frozen`;AgentRun 新任务的排队、派发和取消必须使用 `agentrun queue`
- 所有旧 `codex` 历史查询、已读和残留 interrupt/cancel 命令必须走与 WebUI 相同的 backend-core 私有代理路径 `/api/microservices/code-queue/proxy/...`。旧 submit/steer/resume/queue mutation/move/workdir mutation 不得绕过冻结;若需要新任务或新 session 控制,使用 AgentRun Queue/Sessions
- 旧 Code Queue 多队列 lane 现在是归档视图:`codex queues [--full|--all] [--limit N] [--page N|--offset N]` 只读展示历史 queue 摘要、activity、commanderConcurrency、counts 和 execution diagnostics。`queue create``queue merge``move` 等旧队列写入口冻结并返回 `legacy-code-queue-frozen`;AgentRun 新任务的排队、派发和取消必须使用 `agentrun create|apply|get|cancel`
- 所有旧 `codex` 历史查询、已读和残留 interrupt/cancel 命令必须走与 WebUI 相同的 backend-core 私有代理路径 `/api/microservices/code-queue/proxy/...`。旧 submit/steer/resume/queue mutation/move/workdir mutation 不得绕过冻结;若需要新任务或新 session 控制,使用 AgentRun 资源原语
- `job list [--limit N] [--include-command]``job status <jobId|latest> [--tail-bytes N]` 查询 `.state/jobs/` 文件系统状态,是异步命令的可观测入口。`job list` 默认只返回最新 50 条摘要,并为已知异步工作流返回轻量 `progress.summary` 与后续查询命令;`job status` 默认返回结构化 `progress`、stdout/stderr 末尾 12000 字节、`tailPolicy` 与完整日志路径。已知工作流应从有界日志尾部抽取阶段、关键对象名和下一步命令,避免为了判断当前阶段而手工打开完整 stdout/stderr。`hwlab_g14_v02_trigger_current` 的 progress 必须暴露 trigger 阶段、source commit 和 PipelineRun`hwlab_g14_v02_pr_monitor` 的 progress 必须暴露 preflight、merge、source-head、cd-trigger、cd-status、git-mirror-flush 和 pr-comment 阶段,以及 PR、source commit、PipelineRun、targetValidation/pendingFlush 摘要;`hwlab_g14_git_mirror_sync|flush``agentrun_v01_git_mirror_sync|flush` 的 progress 必须暴露 sync/flush 状态、Job 名、pendingFlush 与 fetch/push/total/SSH timing,并给出对应 repo 的 mirror status 命令。
- `debug health``debug dispatch``debug task` 走真实内部 core、WebSocket、数据库、provider、系统指标、Docker 状态和 Host SSH 维护桥流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。
- `e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]` 使用 publicHost 派生的公开 production frontend/dev frontend/provider ingress URL,并通过 Docker 内网验证 core API、PostgreSQL、provider self-connection、系统指标曲线、Docker 状态快照、provider.upgrade 预检和 Playwright 前端页面,是交付前的自动化 E2E 门禁;CLI 默认输出 check 状态摘要,完整诊断写入 `resultPath`,日常迭代应优先用 `--only` / `--skip` 跑最小必要集合。
@@ -445,7 +445,7 @@ PATCH
`--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``artifact-registry status|health``ci publish-user-service --dry-run``microservice list/status/health/diagnostics/tunnel-self-test/proxy``decision upload/list/show/health``decision requirement list/upsert``decision diary import/list/history/months/show/edit/upsert``codex task <taskId>``codex tasks``codex unread``codex queues``codex output <taskId>``codex judge <taskId> --attempt N``ssh <PROVIDER_ID> <remote-command>``microservice status/health/diagnostics` 经 frontend 远程传输时也复用本地 CLI 的默认 compact summary`microservice health code-queue` 只有显式 `--raw``--full` 才返回完整健康 body。运行中纠偏已切到 AgentRun `sessions steer`;旧 `codex steer` 属于冻结写入口,不应通过 frontend 远程传输或旧 proxy 绕过。其中 `ssh` 的 remote frontend 传输使用 authenticated frontend `/ws/ssh` WebSocket 代理接入 backend-core SSH bridgestdout/stderr 按字节流直通到调用端,不经过 `/api/dispatch``/api/tasks` 或 task JSON compactfrontend 运行时必须通过 `PROVIDER_TOKEN`/`UNIDESK_PROVIDER_TOKEN``PROVIDER_TOKEN_FILE`/`UNIDESK_PROVIDER_TOKEN_FILE` 读取 provider token,并且不能把 token 下发给 runner。因此 D601 Code Queue runner 内的 `tran G14 ...` 应与主 server 本机 `trans G14 ...` / `tran G14 ...` 在输出完整性上保持同一语义。非交互单进程命令优先 `trans D601 argv true``apply-patch`、stdin script、`py` 和旧 `apply-patch-v1` fallback 也走同一条 `/ws/ssh` 流式通道。交互式登录 shell 仍应在主 server 本机 CLI 使用,或显式切换到旧 SSH 传输后在主 server 上执行。当 backend-core、database、provider-dispatch 或 provider-host-ssh 缺失时,这些 read-only 预检必须返回结构化 `runnerDisposition=infra-blocked` 和缺失通道列表,而不是裸 `No such container`。若确实需要旧行为,可使用 `--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``artifact-registry status|health``ci publish-user-service --dry-run``microservice list/status/health/diagnostics/tunnel-self-test/proxy``decision upload/list/show/health``decision requirement list/upsert``decision diary import/list/history/months/show/edit/upsert``codex task <taskId>``codex tasks``codex unread``codex queues``codex output <taskId>``codex judge <taskId> --attempt N``ssh <PROVIDER_ID> <remote-command>``microservice status/health/diagnostics` 经 frontend 远程传输时也复用本地 CLI 的默认 compact summary`microservice health code-queue` 只有显式 `--raw``--full` 才返回完整健康 body。运行中纠偏已切到 AgentRun `steer session/<sessionId>`;旧 `codex steer` 属于冻结写入口,不应通过 frontend 远程传输或旧 proxy 绕过。其中 `ssh` 的 remote frontend 传输使用 authenticated frontend `/ws/ssh` WebSocket 代理接入 backend-core SSH bridgestdout/stderr 按字节流直通到调用端,不经过 `/api/dispatch``/api/tasks` 或 task JSON compactfrontend 运行时必须通过 `PROVIDER_TOKEN`/`UNIDESK_PROVIDER_TOKEN``PROVIDER_TOKEN_FILE`/`UNIDESK_PROVIDER_TOKEN_FILE` 读取 provider token,并且不能把 token 下发给 runner。因此 D601 Code Queue runner 内的 `tran G14 ...` 应与主 server 本机 `trans G14 ...` / `tran G14 ...` 在输出完整性上保持同一语义。非交互单进程命令优先 `trans D601 argv true``apply-patch`、stdin script、`py` 和旧 `apply-patch-v1` fallback 也走同一条 `/ws/ssh` 流式通道。交互式登录 shell 仍应在主 server 本机 CLI 使用,或显式切换到旧 SSH 传输后在主 server 上执行。当 backend-core、database、provider-dispatch 或 provider-host-ssh 缺失时,这些 read-only 预检必须返回结构化 `runnerDisposition=infra-blocked` 和缺失通道列表,而不是裸 `No such container`。若确实需要旧行为,可使用 `--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 自测,不能视为交付完成。
+1 -1
View File
@@ -42,7 +42,7 @@
- `profiles.entries[].openaiResponsesWebSocketsV2Mode` is the account-level Responses WebSocket v2 switch for OpenAI-compatible upstreams that require WebSocket transport. Allowed values are `off`, `ctx_pool`, and `passthrough`; omit the field unless that upstream needs it.
- `profiles.entries[].upstreamUserAgent` is an optional account-level upstream request User-Agent override. Use it only for upstreams that require a Codex CLI compatible User-Agent; keep the value YAML-controlled and newline-free.
- `publicExposure` controls the optional FRP bridge from master server to the G14 ClusterIP service.
- `publicExposure.masterCaddy.responseHeaderTimeoutSeconds` controls the master Caddy `response_header_timeout` for the public Sub2API site. It must be long enough for Codex `/responses/compact` requests; otherwise Caddy can return a client-visible 504 before Sub2API finishes the upstream compact request, and that edge timeout is not an account-level upstream failure that Sub2API can use for temporary-unschedulable failover.
- `publicExposure.masterCaddy.responseHeaderTimeoutSeconds` controls the master Caddy `response_header_timeout` for the public Sub2API site. It must be long enough for Codex `/responses/compact` requests; otherwise Caddy can return a client-visible 504 before Sub2API finishes the upstream compact request, and that edge timeout is not an account-level upstream failure that Sub2API can use for temporary-unschedulable failover. The numeric value belongs only in `config/platform-infra/sub2api-codex-pool.yaml`; after changing it, use `codex-pool expose --confirm` to reload Caddy and verify the rendered `response_header_timeout`. Requests that were already in flight before the reload may still finish with the previous timeout, so post-change evidence should check only requests that started after the reload.
- `localCodex` controls how the master server's current `~/.codex` consumer files are backed up and rewritten. Keep `supportsWebSockets` and `responsesWebSocketsV2` in the same state, and enable them only when at least one YAML-managed account has a current direct Codex WSv2 smoke that passes. If no upstream profile can sustain Responses WSv2, the honest long-term state is `false/false` so Codex uses HTTP Responses directly instead of repeatedly reconnecting before `response.completed`. `localCodex.responsesSmokeModel` is the YAML-declared model used by `codex-pool validate` for the lightweight `POST /v1/responses` smoke.
Enable account-level WebSocket v2 only for upstream profiles that have passed a direct Codex WSv2 probe. Treat this as a YAML-declared capability set, not a hard scheduling pin to one profile; if `localCodex` enables WebSocket transport, `codex-pool validate` must show at least one current `webSocketsV2.schedulableEnabled` account, and runtime smoke remains the availability proof. The same validation reports each managed account's runtime WebSocket v2 mode and whether it matches YAML, so stale `ctx_pool` / `passthrough` settings cannot silently keep routing Codex WS sessions to an upstream that closes with `no available account`, WS handshake 5xx/4xx, or before `response.completed`.
+20 -29
View File
@@ -27,10 +27,11 @@ assertCondition(
);
assertCondition(
agentRunUsage.some((line) => line.includes("queue submit --json-stdin --dry-run"))
&& agentRunUsage.some((line) => line.includes("queue dispatch <taskId> --json-stdin --dry-run"))
&& agentRunUsage.some((line) => line.includes("queue commander --reader-id cli --limit 20")),
"AgentRun help must expose stdin-first queue dry-run and compact commander usage",
agentRunUsage.some((line) => line.includes("agentrun get tasks --queue commander --limit 20"))
&& agentRunUsage.some((line) => line.includes("agentrun describe task/<taskId>"))
&& agentRunUsage.some((line) => line.includes("agentrun events run/<runId> --after-seq 0 --limit 100"))
&& agentRunUsage.some((line) => line.includes("agentrun logs session/<sessionId> --tail 100")),
"AgentRun help must expose Kubernetes-style resource observation primitives",
agentRunUsage,
);
@@ -41,37 +42,27 @@ assertCondition(
);
assertCondition(
agentRunUsage.some((line) => line.includes("queue show <taskId> --full"))
&& agentRunUsage.some((line) => line.includes("runs show <runId>"))
&& agentRunUsage.some((line) => line.includes("runs result <runId> --command-id <commandId>"))
&& agentRunUsage.some((line) => line.includes("commands show <commandId> --run-id <runId>"))
&& agentRunUsage.some((line) => line.includes("runner jobs --run-id <runId> --command-id <commandId>"))
&& agentRunUsage.some((line) => line.includes("runner job-status <runnerJobId> --run-id <runId>"))
&& !agentRunUsage.some((line) => line.includes("queue lifecycle")),
"AgentRun help must expose queue progressive disclosure through existing show/run/command/runner commands without a lifecycle command",
agentRunUsage.some((line) => line.includes("agentrun result run/<runId> --command <commandId>"))
&& agentRunUsage.some((line) => line.includes("agentrun ack task/<taskId> --reader-id cli"))
&& agentRunUsage.some((line) => line.includes("agentrun cancel task/<taskId> --reason <text> --dry-run"))
&& agentRunUsage.some((line) => line.includes("agentrun create task --aipod Artificer --prompt-stdin"))
&& agentRunUsage.some((line) => line.includes("agentrun apply -f - --dry-run"))
&& agentRunUsage.some((line) => line.includes("agentrun steer session/<sessionId> --prompt-stdin"))
&& agentRunUsage.some((line) => line.includes("agentrun send session/<sessionId> --aipod Artificer --prompt-stdin")),
"AgentRun help must expose resource lifecycle control primitives",
agentRunUsage,
);
const submitStdinIndex = agentRunUsage.findIndex((line) => line.includes("queue submit --json-stdin"));
const submitFileIndex = agentRunUsage.findIndex((line) => line.includes("queue submit --json-file"));
const dispatchStdinIndex = agentRunUsage.findIndex((line) => line.includes("queue dispatch <taskId> --json-stdin"));
const dispatchFileIndex = agentRunUsage.findIndex((line) => line.includes("queue dispatch <taskId> --json-file"));
assertCondition(
submitStdinIndex >= 0
&& dispatchStdinIndex >= 0
&& submitFileIndex > submitStdinIndex
&& dispatchFileIndex > dispatchStdinIndex
&& !agentRunUsage.some((line) => line.includes("queue submit --json-file <task.json> --dry-run"))
&& !agentRunUsage.some((line) => line.includes("queue dispatch <taskId> --json-file <dispatch.json> --dry-run")),
"AgentRun help must present heredoc/stdin before reusable file fallbacks",
agentRunUsage,
(agentRunHelp() as { output?: unknown }).output === "human by default; use -o json|yaml or --raw for machine/debug output",
"AgentRun help must declare human output as the default and machine output as opt-in",
agentRunHelp(),
);
const globalHelp = JSON.stringify(rootHelp());
assertCondition(
globalHelp.includes("agentrun aipod-specs|queue|runs|commands|runner|sessions|control-plane|git-mirror"),
globalHelp.includes("agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send"),
"global help must index AgentRun v0.1 entrypoints",
rootHelp(),
);
@@ -105,10 +96,10 @@ console.log(JSON.stringify({
checks: [
"AgentRun command help exposes cleanup-runs and cleanup-released-pvs",
"AgentRun command help exposes targeted control-plane status drill-down options",
"AgentRun command help exposes queue dry-run and compact commander usage",
"AgentRun command help exposes resource observation primitives",
"AgentRun command help hides the v01 lane from user-facing CLI entrypoints",
"AgentRun command help exposes queue progressive disclosure without queue lifecycle",
"AgentRun command help presents heredoc/stdin before reusable file fallbacks",
"AgentRun command help exposes resource lifecycle control primitives",
"AgentRun command help declares human output by default",
"global help indexes AgentRun v0.1 entrypoints",
"AgentRun control-plane status degrades empty runtime JSON snippets",
"AgentRun CLI bridge selects remote frontend backend in runner/no-Docker environments",
+17 -10
View File
@@ -1,7 +1,7 @@
import { readConfig } from "./src/config";
import { debugDispatch, debugHealth, debugTask, isDebugDispatchCommand, type DebugDispatchCommand } from "./src/debug";
import { isRebuildableService, rebuildService, stackLogs, stackStatus, startStack, stopStack, unsupportedRebuildService } from "./src/docker";
import { emitError, emitJson } from "./src/output";
import { emitError, emitJson, emitText, isRenderedCliResult } from "./src/output";
import { cancelJob, jobWithTail, listJobs, listJobsSummary, readJob, runJob } from "./src/jobs";
import { checkHelp, parseCheckOptions, runChecks, runRecoveryGuardrailsCheck } from "./src/check";
import { runSsh } from "./src/ssh";
@@ -232,6 +232,22 @@ async function main(): Promise<void> {
return;
}
if (top === "agentrun") {
const { runAgentRunCommand } = await import("./src/agentrun");
const agentRunArgs = args.slice(1);
const config = agentRunArgs.length === 0 || agentRunArgs.some(isHelpToken) ? null : readConfig();
const result = await runAgentRunCommand(config, agentRunArgs);
const ok = (result as { ok?: unknown }).ok !== false;
if (isRenderedCliResult(result)) {
emitText(result.renderedText);
if (!ok) process.exitCode = 1;
return;
}
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
const namespaceHelp = await staticNamespaceHelp(args);
if (namespaceHelp !== null) {
emitJson(commandName, namespaceHelp);
@@ -313,15 +329,6 @@ async function main(): Promise<void> {
return;
}
if (top === "agentrun") {
const { runAgentRunCommand } = await import("./src/agentrun");
const result = await runAgentRunCommand(readConfig(), args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
emitJson(commandName, result, ok);
if (!ok) process.exitCode = 1;
return;
}
if (top === "platform-infra") {
const { runPlatformInfraCommand } = await import("./src/platform-infra");
const result = await runPlatformInfraCommand(readConfig(), args.slice(1));
+956 -45
View File
File diff suppressed because it is too large Load Diff
+27 -27
View File
@@ -57,7 +57,7 @@ export function rootHelp(): unknown {
{ command: "commander contract|plan --dry-run|smoke --dry-run|approval request --dry-run|prompt-lint --kind gpt55-pr", description: "Host Codex commander skeleton contract, no-daemon smoke plan, dry-run approval preview, and advisory GPT-5.5 PR prompt boundary lint without live bridges, message sends, or submit gating." },
{ command: "hwlab nodes control-plane|git-mirror|secret --node G14 --lane v03", description: "Manage HWLAB node/lane runtime prerequisites for v0.3+ with the node identity passed as data instead of a command family." },
{ command: "hwlab g14 monitor-prs | hwlab g14 control-plane status|apply|trigger-current|runtime-migration|cleanup-runs|cleanup-released-pvs | hwlab g14 git-mirror status|apply|sync|flush | hwlab g14 tools-image status|build", description: "Start the legacy G14 PR monitor, run bounded v0.2 Tekton/Argo control-plane, manual PipelineRun trigger, runtime migration, CI workspace retention, manual devops-infra git mirror/relay maintenance, or fixed HWLAB CI tools image actions; long confirmed trigger/sync/flush actions return async jobs by default." },
{ command: "agentrun aipod-specs|queue|runs|commands|runner|sessions|control-plane|git-mirror", description: "Use AgentRun v0.1 AipodSpec, Queue, Run, Command, read-only runner status, and Sessions through the official G14 CLI bridge, plus bounded Tekton/Argo and git-mirror operations." },
{ command: "agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send|control-plane|git-mirror", description: "Use AgentRun v0.1 resource primitives with low-noise human output by default; legacy bridge groups remain available for raw compatibility." },
{ command: "platform-infra sub2api plan|apply|status|validate|codex-pool", description: "Deploy Sub2API in G14 platform-infra, manage the YAML-controlled Codex upstream pool, expose the unified API through FRP when needed, and configure master ~/.codex without printing API keys." },
{ command: "hwlab cd audit --env dev | hwlab cd status --env dev | hwlab cd apply --env dev --dry-run", description: "Legacy D601 HWLAB DEV CD wrapper kept for explicit old-path diagnostics; current HWLAB rollout uses G14 GitOps." },
{ command: "code-agent-sandbox", description: "Independent Code Agent Sandbox service skeleton for adapter, mode, and credential-boundary diagnostics." },
@@ -65,7 +65,7 @@ export function rootHelp(): unknown {
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Disabled legacy Code Queue deploy path; use the dev-only artifact consumer instead." },
{ command: "codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]", description: "Dry-run lint a runner prompt for DEV test class read-only/live-read/live-mutating authorization without echoing prompt text or touching live services." },
{ command: "codex submit|steer|resume|queue create|queue merge|move", description: "Frozen legacy Code Queue write commands; use agentrun queue and agentrun sessions for new commander work. Historical codex task/tasks/output/read/unread/queues remain available for archive troubleshooting." },
{ command: "codex submit|steer|resume|queue create|queue merge|move", description: "Frozen legacy Code Queue write commands; use agentrun create/apply/steer/send for new commander work. Historical codex task/tasks/output/read/unread/queues remain available for archive troubleshooting." },
{ command: "codex skills-sync --dry-run [--full]", description: "Inspect the controlled runner skills hostPath lifecycle contract without copying files, restarting services, reading secrets, or mutating live runner paths." },
{ command: "codex execution-plane [--full|--raw]", description: "Read-only D601 native k3s Code Queue execution-plane inspection; compares formal deployments, deprecated Compose residuals, commit markers, pod digest, and mounted worktree HEAD." },
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full|--raw]", description: "Read-only PR admission check with compact commander output by default; use --full or --raw to expand the full runtime preflight, tool, and observation payload." },
@@ -76,10 +76,10 @@ export function rootHelp(): unknown {
{ command: "codex read <taskId>", description: "Mark one reviewed terminal task read and return terminal metadata plus final response; prompt/tool logs stay behind drill-down commands." },
{ command: "codex dev-ready", description: "Fetch execution-container readiness, including sanitized skill injection status from /api/dev-ready." },
{ command: "codex judge <taskId> --attempt N [--dry-run] [--include-prompt]", description: "Replay one stored Code Queue attempt through the same judge context builder and MiniMax judge call path used by the live queue worker." },
{ command: "codex steer <taskId> / codex resume <taskId>", description: "Frozen legacy execution mutation entries; use agentrun sessions steer/read/cancel/output/trace against AgentRun sessions instead." },
{ command: "codex steer <taskId> / codex resume <taskId>", description: "Frozen legacy execution mutation entries; use agentrun steer/send/logs/events/result/ack/cancel against AgentRun resources instead." },
{ command: "codex steer-confirm <taskId> --steer-id <id> [--raw]", description: "Read-only lookup for a steerId in task trace so deliveryUnconfirmed can be resolved without resending the corrective prompt." },
{ command: "codex interrupt|cancel <taskId>", description: "Request interrupt for a running Code Queue task, or cancel a queued/retry_wait task, through the same private proxy." },
{ command: "codex queues [--full|--all] [--limit N] [--page N|--offset N]", description: "Read legacy Code Queue archive summaries. Legacy queue create/merge and move are frozen; use agentrun queue for new work." },
{ command: "codex queues [--full|--all] [--limit N] [--page N|--offset N]", description: "Read legacy Code Queue archive summaries. Legacy queue create/merge and move are frozen; use agentrun create/apply/get/cancel for new work." },
{ command: "job list [--limit N] [--include-command]", description: "List async jobs from .state/jobs with a bounded default page and progress summaries." },
{ command: "job status <jobId|latest> [--tail-bytes N]", description: "Show job state with a structured progress summary and bounded stdout/stderr tails." },
{ command: "job cancel <jobId>", description: "Cancel a queued/running async job through the .state/jobs control entry and keep a terminal canceled record." },
@@ -385,11 +385,11 @@ function codexHelp(): unknown {
usage: [
"bun scripts/cli.ts codex deploy <commitId> # disabled legacy deployment entry",
"bun scripts/cli.ts codex prompt-lint [prompt|--prompt-file path|--prompt-stdin]",
"bun scripts/cli.ts agentrun queue commander --reader-id cli",
"bun scripts/cli.ts agentrun aipod-specs show Artificer",
"bun scripts/cli.ts agentrun queue submit --aipod Artificer --prompt-stdin",
"bun scripts/cli.ts agentrun queue submit --json-stdin <<'JSON'",
"bun scripts/cli.ts agentrun sessions trace <sessionId> --after-seq 0 --limit 100",
"bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",
"bun scripts/cli.ts agentrun describe aipodspec/Artificer",
"bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin",
"bun scripts/cli.ts agentrun apply -f - --dry-run",
"bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100",
"bun scripts/cli.ts codex submit # frozen legacy write entry; returns legacy-code-queue-frozen",
"bun scripts/cli.ts codex task <taskId> [--detail] [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]",
"bun scripts/cli.ts codex tasks [--view commander|supervisor|full] [--queue id] [--status succeeded,running] [--unread|--unread-only] [--limit N] [--before-id id]",
@@ -402,7 +402,7 @@ function codexHelp(): unknown {
"bun scripts/cli.ts codex execution-plane [--full|--raw]",
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full|--raw]",
"bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]",
"bun scripts/cli.ts agentrun sessions steer <sessionId> --prompt-stdin",
"bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin",
"bun scripts/cli.ts codex steer <taskId> # frozen legacy write entry",
"bun scripts/cli.ts codex resume <taskId> # frozen legacy write entry",
"bun scripts/cli.ts codex steer-confirm <taskId> --steer-id <id> [--raw]",
@@ -419,11 +419,11 @@ function codexHelp(): unknown {
executionMode: {
validModes: ["default", "windows-native"],
boundary: "Legacy Code Queue submit is frozen; new runtime placement is selected by AgentRun Queue payloads.",
permissionVisibility: "Legacy Code Queue read commands expose historical runner metadata only; new tasks use AgentRun Queue/Sessions.",
permissionVisibility: "Legacy Code Queue read commands expose historical runner metadata only; new tasks use AgentRun resource primitives.",
},
submitSummary: {
default: "codex submit/enqueue now returns ok=false, frozen=true, degradedReason=legacy-code-queue-frozen and AgentRun replacement commands.",
replacement: "Use bun scripts/cli.ts agentrun queue submit --json-stdin with a quoted heredoc for new one-shot work; reserve --json-file for reusable reviewed files.",
replacement: "Use bun scripts/cli.ts agentrun apply -f - with a quoted YAML heredoc for structured work, or agentrun create task --aipod Artificer --prompt-stdin for one prompt task.",
noDoubleWrite: "UniDesk does not mirror AgentRun submissions into old Code Queue and does not migrate old history into AgentRun.",
rawDrillDown: "Use codex tasks/task/output/read/unread/queues only for legacy archive inspection.",
},
@@ -453,10 +453,10 @@ function codexHelp(): unknown {
},
examples: {
promptLint: "bun scripts/cli.ts codex prompt-lint --prompt-file /tmp/code-queue-prompt.md",
agentRunCommander: "bun scripts/cli.ts agentrun queue commander --reader-id cli",
agentRunAipod: "bun scripts/cli.ts agentrun queue submit --aipod Artificer --prompt-stdin",
agentRunSubmit: "bun scripts/cli.ts agentrun queue submit --json-stdin <<'JSON'",
agentRunTrace: "bun scripts/cli.ts agentrun sessions trace <sessionId> --after-seq 0 --limit 100",
agentRunCommander: "bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",
agentRunAipod: "bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin",
agentRunSubmit: "bun scripts/cli.ts agentrun apply -f - --dry-run",
agentRunTrace: "bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100",
frozenLegacySubmit: "bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md",
},
disclosure: {
@@ -482,7 +482,7 @@ function codexHelp(): unknown {
embeddedIn: [],
reference: "docs/reference/code-queue-supervision.md#dev-测试授权分级",
},
description: "Operate legacy Code Queue as a read-only archive through bounded task/output/read/unread/queues views. New task dispatch, retry/resume, steer, queue mutation, move, and workdir mutation are frozen and replaced by AgentRun Queue/Sessions via bun scripts/cli.ts agentrun queue|sessions.",
description: "Operate legacy Code Queue as a read-only archive through bounded task/output/read/unread/queues views. New task dispatch, retry/resume, steer, queue mutation, move, and workdir mutation are frozen and replaced by AgentRun resource primitives via bun scripts/cli.ts agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send.",
};
}
@@ -597,19 +597,19 @@ function artifactRegistryHelp(): unknown {
function agentRunHelpSummary(): unknown {
return {
command: "agentrun aipod-specs|queue|runs|commands|runner|sessions|control-plane|git-mirror",
output: "json",
command: "agentrun get|describe|events|logs|result|ack|cancel|create|apply|steer|send|control-plane|git-mirror",
output: "human by default; use -o json|yaml or --raw",
usage: [
"bun scripts/cli.ts agentrun aipod-specs show Artificer",
"bun scripts/cli.ts agentrun queue commander --reader-id cli",
"bun scripts/cli.ts agentrun queue show <taskId>",
"bun scripts/cli.ts agentrun runs show <runId>",
"bun scripts/cli.ts agentrun commands show <commandId> --run-id <runId>",
"bun scripts/cli.ts agentrun runner jobs --run-id <runId> --command-id <commandId>",
"bun scripts/cli.ts agentrun sessions trace <sessionId> --after-seq 0 --limit 100",
"bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",
"bun scripts/cli.ts agentrun describe task/<taskId>",
"bun scripts/cli.ts agentrun events run/<runId> --after-seq 0 --limit 100",
"bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100",
"bun scripts/cli.ts agentrun result run/<runId> --command <commandId>",
"bun scripts/cli.ts agentrun ack task/<taskId>",
"bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin",
"bun scripts/cli.ts agentrun control-plane status",
],
description: "Operate AgentRun v0.1 AipodSpec, queue, run, command, read-only runner status, sessions, and G14 control-plane entrypoints with progressive disclosure: queue commander -> queue show -> runs/commands/runner/session detail.",
description: "Operate AgentRun v0.1 resources with Kubernetes-style verbs and progressive disclosure. Legacy queue/runs/commands/runner/sessions bridge groups remain for raw compatibility.",
};
}
+19
View File
@@ -10,6 +10,13 @@ export interface JsonEnvelope<T> {
error?: unknown;
}
export interface RenderedCliResult {
ok: boolean;
command: string;
renderedText: string;
contentType: "text/plain" | "application/json" | "application/yaml";
}
const GH_OUTPUT_DUMP_THRESHOLD_BYTES = 20 * 1024;
const OUTPUT_DUMP_PREVIEW_CHARS = 2000;
const OUTPUT_DUMP_DIR = join(tmpdir(), "unidesk-cli-output");
@@ -33,6 +40,18 @@ export function emitJson<T>(command: string, data: T, ok = true): void {
safeStdoutWrite(renderEnvelope(command, envelope));
}
export function isRenderedCliResult(value: unknown): value is RenderedCliResult {
return typeof value === "object"
&& value !== null
&& typeof (value as { renderedText?: unknown }).renderedText === "string"
&& typeof (value as { command?: unknown }).command === "string"
&& typeof (value as { ok?: unknown }).ok === "boolean";
}
export function emitText(text: string): void {
safeStdoutWrite(text.endsWith("\n") ? text : `${text}\n`);
}
export function emitError(command: string, error: unknown): void {
const payload = normalizeErrorPayload(command, error);
const envelope: JsonEnvelope<never> = { ok: false, command, error: payload };