81 KiB
UniDesk CLI Reference
UniDesk 的统一 CLI 入口是根目录 scripts/cli.ts,运行方式固定为 bun scripts/cli.ts <command>。CLI 默认输出 JSON,所有成功和失败路径都必须向 stdout 写出结构化对象,避免无输出造成状态不可观测。
CLI 可以从 master 快速演进,但必须兼容 deploy.json 固定的 CI/CD server 和生产运行面。CLI/server 能力协商、unsupported-version 失败语义和 release-line 边界由 docs/reference/release-governance.md 与 GitHub issue #6 约束。
Command Model
help输出命令索引,适合作为交互式入口。- 每个 CLI 命名空间必须支持
help、--help或-h并返回 JSON,不得为了打印帮助而访问 runtime 服务、拉起交互会话或执行长时任务。 --main-server-ip <ip> <command>默认通过公网 frontend 登录态调用主 server 的同源 API 代理,不要求计算节点持有主 server SSH key;显式提供--main-server-key或--main-server-transport ssh时才使用旧 SSH 传输。config show读取并校验根目录config.json,不从环境变量、默认值或隐藏文件静默补配置。check默认只执行轻量配置校验、Bun 版本检查和 Bun Transpiler 语法解析(覆盖 CLI 入口、主要scripts/模块和核心组件入口,不做类型推导);关键文件存在性、scripts/TypeScript 类型检查、src/components/TypeScript 类型检查、Docker Compose config 和日志轮转策略扫描默认不启用,分别通过--files、--scripts-typecheck、--components、--compose、--logs开启,或用--full一次性开启。--rust只允许在 D601 CI/dev execution 中配合UNIDESK_D601_RUST_CHECK=1使用,长期规则见docs/reference/dev-environment.md。server start创建异步 job,在后台执行 Docker 构建和启动;命令本身只负责返回 job id、日志路径和启动命令。server stop创建异步 job,在后台停止固定 Compose project 中的全部 UniDesk 服务。server status查询公开端口、受限宿主端口、内部端口、主机 swap 摘要、Compose 容器、core/frontend/dev-frontend/provider/database 健康检查和访问 URL;D601 Code Queue 使用的 PostgreSQL/OA Event Flow host mapping 必须出现在受限宿主端口而不是无条件公开入口中。低内存主 server 上swap.warning非空时,先执行server swap status或server swap ensure。server swap status|ensure [--path /swapfile] [--size 2GiB] [--dry-run]是主 server swap 管理入口。status仅读/proc/meminfo、/proc/swaps和/etc/fstab并返回 JSON;ensure在已有任何 active swap 时只报告 no-op,在无 active swap 时创建固定 swapfile、chmod 600、mkswap、swapon并尽量写入/etc/fstab。输出必须包含before、after、total memory、active swap、持久化状态、关键动作和错误详情;若 swap 已启用但 fstab 写入失败,状态为degraded,调用者需按返回的 detail 修复持久化。server logs返回logs/文件日志和 Docker 容器日志的尾部,默认限制输出大小,避免日志爆炸。实现必须只读取文件末尾字节,不得为了 tail 先把巨大日志完整读入 CLI 内存。server cleanup plan [--min-age-hours N] [--limit N]只生成主 server Docker 镜像清理 dry-run 计划,不执行删除;默认--min-age-hours 24,避免把刚发布或刚验证的镜像列为 stale。输出必须包含dryRun=true、mutation=false、policy.deletionExecuted=false、active containers/images、受保护镜像、candidate stale images、估算释放空间、风险等级、commandsToReview和人工审批清单。计划必须保守白名单:保留 running containers 使用的 image ID,保留 stopped containers 引用的 image ID 直到人工先复核容器,保留deploy.json/CI.json当前 commit-pinned artifact、Compose stable image、上游 digest pin 和 provider-gateway runner image;protectedStorage必须显式列出 PostgreSQL named volume、Baidu Netdisk.state、D601 registry storage 和 Docker volumes/host data policy。该入口禁止生成或执行docker system prune、docker image prune、docker builder prune、docker volume rm、docker compose down -v、数据库清理或 host datarm命令;未来若增加真实删除,必须另设显式审批参数并先复核 dry-run 输出。server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>创建异步 job,先构建目标服务镜像,随后在.state/locks/server-compose.lock串行保护下用--no-deps --force-recreate替换目标 service 并等待容器healthy/running;该命令用于替代手工删除容器的兜底流程,其中dev-frontend-proxy只更新主 server dev 入口薄代理,todo-note、code-queue-mgr、project-manager、baidu-netdisk和oa-event-flow只重建主 server 承载的对应后端,不会重建或删除 database 命名卷。D601 Code Queue 执行面不由server rebuild管理,Rust backend-core 迭代不得用server rebuild backend-core在 master server 编译,规则见docs/reference/dev-environment.md。provider attach <providerId> [--master-server URL] [--up] [--force]在新计算节点生成两项配置的 provider-gateway 挂载包:.state/provider-<ID>.env默认只包含UNIDESK_MASTER_SERVER与PROVIDER_ID,provider-<ID>.yml固定 Docker socket、pid: "host"、restart: always、只读/workspace和 SSH 维护私钥挂载;--up会立即执行生成的docker compose up -d --build。provider triage <providerId> [--observed-error text] [--observed-scope scope] [--microservice id ...] [--full|--raw]是只读多信号健康裁决入口,会把单路径provider is not online、SSH 超时、registry 失败和 service proxy 失败归类成runner-local-observation-gap、service-degraded、provider-degraded或global-blocker。默认输出只返回裁决、scope、失败/降级/未知信号和有界 evidence 摘要,完整 evidence 必须显式加--full或--raw;推荐交叉验证命令仍包含debug health、debug dispatch <providerId> host.ssh --wait-ms 15000、ssh <providerId> argv true、artifact-registry health --provider-id <providerId>、microservice health k3sctl-adapter、microservice health code-queue和codex tasks --view supervisor --limit 20。ssh <providerId> [ssh-like args...]通过 backend-core 内网 WebSocket broker 和 provider-gateway 的 Host SSH / WSL SSH 维护桥连接目标节点;无后续参数时进入远端登录 shell,有后续参数时按 ssh 远端命令体验执行并返回远端 exit code。非交互远端命令优先使用ssh <providerId> argv ...,需要 shell 特性时用bun scripts/cli.ts ssh D601 argv bash -lc '<command>';ssh-like 命令遇到 timeout/kex/255 类失败时,CLI 会在 stderr 追加一行UNIDESK_SSH_HINTJSON,提示 argv 重试和 provider triage 交叉验证。ssh <providerId> apply-patch [tool args...] < patch.diff直接调用远端注入的apply_patch工具,并把本地 stdin 中的标准*** Begin Patch/*** End Patchpatch 流透传给目标节点。ssh <providerId> py [script-args...] < script.py把本地 stdin 落到远端临时.py文件后再以python3 -u执行并自动清理,避免再手写'python3 -'、heredoc 或多层引号;script-args会按 argv 安全透传给远端脚本。ssh <providerId> skills [--scope all|wsl|windows] [--limit N]发现目标节点上的 WSL/Linux skill 根目录;当 provider 是 WSL 时同一次调用还会扫描 Windows 用户目录下的.agents/skills与.codex/skills。microservice list/status/health/diagnostics/tunnel-self-test/proxy通过 backend-core 内网 API 管理挂载在计算节点 Docker 或 k3s 控制面中的用户服务(底层命令名仍为 microservice);health、status和diagnostics默认返回 compact summary、body 字节数和--full|--raw展开命令,只有小 body 或无法抽取 summary 时才带有界 body preview,避免 Code Queue/k3s 诊断一次性输出爆炸;tunnel-self-test和proxy会走真实 backend-core -> provider-gateway 或 k3sctl-adapter -> 节点服务链路。microservice health code-queue使用 commander-safe 专用摘要,必须保留 ok/status、service id、running count、queue count、heartbeat freshness/risk、split-brain/live/degraded 解释和 raw drill-down 命令;需要完整健康 JSON 时显式加--raw或--full,等价深挖路径是microservice proxy code-queue /health --raw --full。proxy支持受控 JSON 请求体并对超大响应 body 默认输出有界预览,规则见docs/reference/microservices.md。decision upload/list/show/health通过 backend-core 用户服务代理访问 D601 k3s Decision Center,用于上传会议记录/决议 Markdown、列出权威记录、查看详情和健康检查;decision list默认只返回摘要并省略完整 Markdown body,需要排查大正文时显式加--include-body。正式文书字段通过 records 模型一等字段返回和查询:--doc-no DC-...、--doc-type DCSN|GOAL|PLAN|RPRT|ACTN|ISSU|RETR|RQST|RESP|MINS、--doc-priority P0|P1|P2|P3、--year YYYY、--signer、--issued-at、--effective-scope、--supersedes、--superseded-by;show和requirement update可使用id或docNo。decision requirement list/create/upsert/update/show在同一 records 模型上管理goal|decision|blocker|debt|experiment需求记录,docNo唯一,未传--doc-no但提供--doc-type/--doc-priority/--year时由服务分配下一个序号。它们不得直连 D601 Service、NodePort 或 provider-gateway 业务 HTTP。decision diary import <markdown-file>将带# YYYY年M月D日、# YYYY-MM-DD或# YYYY/M/D标题的工作日志拆成每天一篇 Markdown 日记,按YYYY-MM/YYYY-MM-DD.md虚拟路径写入 Decision Center PostgreSQL;decision diary list/history默认只返回摘要,需要完整 Markdown 时显式加--include-body;decision diary show <YYYY-MM-DD|id> [--source-file path]查看单日正文,--source-file用于同一天存在多个导入来源时精确选择;decision diary edit|upsert <YYYY-MM-DD|id> --body-file <path> [--title text] [--source-file path] [--tag tag]通过PUT /api/diary/entries/:idOrDate创建当天或历史条目并编辑既有条目。deploy check/plan/apply默认从根目录deploy.json读取服务 repo 与 commit 期望状态,joinconfig.json和现有 manifest 后使用 target-side build 或 reviewed artifact consumer 校验/更新已支持目标;deploy plan --env dev|prod只从origin/master:deploy.json#environments.<env>读取 manifest 并输出 dry-run 环境计划,不使用本地 dirty worktree;当前deploy apply --env dev支持backend-core、frontend、baidu-netdisk、decision-center、mdtodo、claudeqq和 dev-onlycode-queueartifact consumers,findjob/pipeline/met-nonlinear为 D601 direct Compose artifact consumers,k3sctl-adapter只提供 plan/dry-run;dev desired-state smoke 使用ci run-dev-e2e;规则见docs/reference/deploy.md、docs/reference/dev-environment.md和docs/reference/dev-ci-runner.md。deploy apply --env prod同时覆盖findjob和pipeline的 pull-only Compose CD,但met-nonlinear仍然只允许 dry-run/plan,k3sctl-adapter只允许 plan/dry-run。dev-env validate [--manifest path] [--kubectl-dry-run]离线校验 D601unidesk-devnamespace、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-devStatefulSet/Service、dev secret/config、迁移 Job 和 DB URL guard,core manifest 必须包含backend-core-dev/frontend-devDeployment/Service,Code 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-dirvolume。加--kubectl-dry-run时额外以KUBECONFIG=/etc/rancher/k3s/k3s.yaml执行kubectl apply --dry-run=client --validate=false -f <manifest>,仍不 apply 资源;默认docker-desktopkubeconfig 不得作为 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、不修改生产unidesknamespace。artifact-registry plan|render|status|health|install|deploy-backend-core|deploy-service管理 D601 host-managed CNCF Distribution registry 的声明、安装、只读检查和 pull-only artifact CD。该 registry 固定为 D601 loopback127.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是 host Codex 指挥官直管微服务 skeleton 入口。当前命令返回phase=source-contract、service/API/state/bridge/prompt/trace/#20/#46/ClaudeQQ 审批边界、.state/commander/ 状态模型、dev 无 daemon smoke contract 和 dry-run 计划,服务骨架只提供本地/health、/api/commander/contract、状态读写、trace summary 聚合和 approval draft preview,不接 live bridge、不注入 prompt、不发送 ClaudeQQ。approval request --dry-run会生成 200 字以内中文纯文本 ClaudeQQ 审批草案、notification-path-unavailableblocker 和授权后唯一可用的bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw命令;不得提示使用本机 ClaudeQQ skill、powershell 或本地 server。plan、smoke与approval request必须带--dry-run;缺少时返回error=dry-run-required。长期规则见docs/reference/host-codex-commander.md。gh auth status [--repo owner/name]探测 GitHub 操作前置条件并输出脱敏 JSON:是否存在ghbinary、是否存在GH_TOKEN/GITHUB_TOKEN或可用gh auth tokenfallback、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]是派发/steer 前的本地 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处理。codex submit --dry-run与codex steer --dry-run会嵌入同一promptLint结果,帮助指挥官在 dispatch/steer 前发现缺失或矛盾的 live mutation 授权。长期规则见docs/reference/code-queue-supervision.md的 DEV 测试授权分级。gh issue list [--state open|closed|all] [--limit N] [--repo owner/name] [--json number,title,state,url,updatedAt,createdAt,author,labels]通过 GitHub REST 列出 issue,默认state=open、limit=30,输出稳定 JSON 且不依赖系统ghbinary。--limit会映射到 GitHubper_page并限制返回数量,避免一次拉爆上下文;未知 state 或未知--json字段必须结构化失败并带runnerDisposition=business-failed。GitHub issues API 可能混入 PR,CLI 会从.data.issues中过滤 pull request。gh issue read <number|owner/repo#number> [--repo owner/name] [--json body,title,state,comments] [--raw|--full]通过 GitHub REST 读取 issue title/body/state/url 和 comments,默认输出 JSON;view只保留为兼容别名。owner/repo#numbershorthand 会自动派生--repo owner/repo和 issue number;若同时提供冲突的显式--repo,CLI 必须结构化失败并给出gh issue read <number> --repo owner/repo --json body,title,state,comments与 shorthand raw 的可执行命令。兼容旧脚本的--json body和--json body,title,state,comments字段选择,且正文仍稳定暴露在.data.issue.body,避免调用方因为 JSON 路径变化把空值当成正文。字段白名单是body,title,state,comments,number,url,author,createdAt,updatedAt,未知字段必须结构化失败并带runnerDisposition=business-failed。--raw与--full只在 read/view 上可用,是显式完整披露别名,会选择完整支持字段集并保持结构化 JSON 输出;当最终ghJSON 超过 20 KiB 时,CLI 必须把完整 JSON 写入/tmp/unidesk-cli-output/*.json,stdout 只返回outputTruncated=true、dump path、总 bytes/lines 和 head/tail 预览。默认 list/read 输出仍不得扩散到无界非 JSON 文本。gh issue create --title <title> --body-file <file> [--label label[,label...]]... [--dry-run]、gh issue update <number> --mode replace|append --body-file <file> [--title ...] [--dry-run]、gh issue comment create <number> --body-file <file> [--dry-run]、gh issue comment delete <commentId> [--dry-run]、gh issue close|reopen <number> [--dry-run]都走 REST,不依赖ghbinary。--label仅用于issue create,支持重复传入和逗号分隔;--dry-run会展示解析后的 labels 与 request plan,正式创建时把 labels 放入 GitHub REST create-issue payload,GitHub 返回不存在 label 等 422 校验失败时 CLI 结构化返回validation-failed,不静默成功。gh issue delete <number>是结构化unsupported-command,因为 GitHub REST 不支持 issue 硬删除;生命周期删除语义请使用close。gh issue update <number> --mode replace|append --body-file <file>是正文更新主入口,edit保留为兼容别名。replace用文件正文替换现有 body;append先读取当前 body,再按 UTF-8 文件字节追加,保留真实换行、反引号和 Markdown 表格。更新默认拒绝字面量null、空白正文和过短正文;只有真实需要写短正文时才允许显式加--allow-short-body,返回 JSON 会报告该风险。#20 总看板和指挥简报类 issue 是长期 body-only issue,--body-profile auto会按 issue number 自动启用 #20/#24 legacy guard:#20 必须包含## 看板(OPEN),#24 legacy 指挥简报必须包含## 常驻观察与长期建议。显式--body-profile commander-brief不再固定 #24;#24 仍兼容,标题为YYYY-MM-DD 指挥简报(北京时间)或既有正文首行/关键 heading 表明为每日滚动指挥简报的 issue 也合法,并仍必须包含## 常驻观察与长期建议。对非简报 issue 显式使用commander-brief会结构化失败为profile-issue-mismatch。--dry-run不 PATCH GitHub,输出新正文长度、SHA、关键标题检查结果、字面量\n、反引号、Markdown 表格和 shell 污染信号;若环境里有GH_TOKEN或GITHUB_TOKEN,dry-run 还会只读抓取旧正文长度、SHA 和updatedAt作为更新前对照。正式写入可带--expect-updated-at <updated_at>或--expect-body-sha <sha256>,CLI 会先读当前 issue,匹配后才 PATCH,防止旧缓存覆盖新正文。- #20 只允许承担长期 UniDesk 指挥官 / Code Queue / CLI / infra 治理总看板职责;每日进展必须写入当天滚动指挥简报 issue,并由 #20 顶部“指挥简报索引”引用。HWLAB 用户反馈、Cloud Workbench、DEV-LIVE、M3 虚拟硬件可信闭环等产品 issue 必须写到
pikasTech/HWLAB;#20 只可记录 UniDesk 侧 commander/Code Queue/CLI/infra 支撑工作。gh issue read/view 20会返回codeQueueBoardHint;gh issue update/edit 20的 body guard 会拒绝## 更新 YYYY-MM-DD HH:mm 北京时间、## YYYY-MM-DD HH:mm 北京时间指挥更新和### YYYY-MM-DD HH:mm CST:...这类简报段落,也会拒绝把pikasTech/HWLAB#N、HWLAB#N或 HWLAB 产品/live 验证行写入 #20,并在codeQueueBoardHint中提示改写到每日简报 issue 或pikasTech/HWLAB;gh issue board-row list|get|update|add|move|delete|upsert --board-issue 20也会返回同一 hint,提醒不要把每日简报或 HWLAB 产品看板混入 #20。 gh issue edit 24 --body-file <file> --notify-claudeqq-brief-diff [--dry-run]是 legacy #24 指挥简报的通知入口。正式执行会先读取 GitHub 上 #24 旧正文并通过 #24 body profile guard,再从--body-file读取新正文;随后先 PATCH issue 主体,再把本次新增的## 更新 YYYY-MM-DD HH:MM 北京时间段落发送给 ClaudeQQ,ClaudeQQ 失败不会回滚 issue 正文,失败只体现在返回 JSON 的claudeqq.ok=false和结构化degradedReason。每日滚动简报 issue 可用普通gh issue update <number> --body-profile commander-brief --dry-run和并发 guard 更新,但此通知 helper 仍只支持 #24。带通知 flag 的--dry-run不 PATCH、不发送;它按新正文做发送预览,并在输出中标明非 dry-run 才会读取旧正文做可靠 diff。默认 ClaudeQQ 目标是私聊645275593,默认 base URL 是 UniDesk 受控入口http://backend-core:8080/api/microservices/claudeqq/proxy;UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_BASE_URL只接受 backend-core/api/microservices/claudeqq/proxy等价路径,非 proxy URL 会结构化为notification-path-unavailable。可用UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_ENABLED、UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_TARGET_TYPE、UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_USER_ID、UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_GROUP_ID和UNIDESK_COMMANDER_BRIEF_CLAUDEQQ_TIMEOUT_MS调整开关、目标和超时。gh issue board-audit [--repo owner/name] [--board-issue 20] [--limit N] [--known-meta-issue N[,N...]] [--ignore-issue N[,N...]] [--dry-run]是总看板只读结构审计入口,默认 repo 为pikasTech/unidesk、board issue 为20、输出 JSON 且不 PATCH/POST/DELETE GitHub。它只读取目标 board issue 正文,返回正文长度、行数、body SHA、可解析 Markdown board sections、section 行数和 parser warnings;不再拉取 GitHub open/closed issue 列表,也不再校验 OPEN/CLOSED 表是否覆盖全部 issue。兼容字段missingOpenIssues、closedInOpenRows、missingClosedRows、openInClosedRows、rowValidationWarnings、ignoredIssues和recommendedActions仍保留,但固定为空数组或 0,用于避免旧调用方因字段缺失失败。需要维护旧式 OPEN/CLOSED 明细表时,继续使用gh issue board-row list|get|update|add|move|delete|upsert的行级结构化入口。gh issue board-row list --board-issue 20 [--state open|closed|all] [--dry-run]、gh issue board-row get <issueNumber> --board-issue 20和gh issue board-row update <issueNumber> --board-issue 20 --field progress|status|validation|branch|tasks|focus --value <text> [--dry-run] [--expect-updated-at ts|--expect-body-sha sha256]是 #20 看板表格单行结构化入口。list/get 复用 board-audit parser,只读返回 row、cells、fields、section、lineNumber、bodySha 和 rowValidationWarnings。update 只替换命中的一行里一个单元格,返回 old/new row、old/new body SHA、body guard、request plan 和 parser 结果;默认没有并发期望时即使不写--dry-run也只做 dry-run,正式 PATCH 必须带--expect-body-sha或--expect-updated-at。字段映射固定为:branch-> Branch,progress-> 进度,status/validation-> 验收状态,tasks-> 相关 Code Queue 任务,focus-> 当前关注点。单元格值中的 Markdown 表格管道会转义为\|,真实换行会折叠为空格,避免新增字面量\n污染。gh issue board-row upsert <issueNumber> --board-issue 20 --section open|closed [--category text] --branch <branch> --tasks <task> --summary <text> --focus <text> --validation <text> --progress <text> [--status OPEN|CLOSED] [--dry-run] [--expect-body-sha|--expect-updated-at]是行级补齐入口:若 issue 已存在则只更新传入字段并返回operation=update,未传字段保留原值;若不存在则按目标 section 表头生成完整行并返回operation=add。新增时--section必需,且目标表头中的 category/branch/tasks/summary/focus/validation/progress 列都必须有对应值;若表没有独立 Summary/摘要列,--summary会并入 Issue 单元格。upsert 不关闭、不删除、不重开 GitHub issue,也不做 OPEN/CLOSED 迁移;已存在行的--section或--status与当前 section 冲突时会结构化失败并提示使用board-row move。gh issue board-row add <issueNumber> --board-issue 20 --section open|closed --row-file <file> [--dry-run] [--expect-body-sha|--expect-updated-at]、move <issueNumber> --board-issue 20 --to open|closed [--status OPEN|CLOSED] [--dry-run] [--expect-body-sha|--expect-updated-at]和delete <issueNumber> --board-issue 20 [--dry-run] [--expect-body-sha|--expect-updated-at]是 row-scoped #20 结构化写入口。add 校验一行--row-file的 Issue 列、列数和 GitHub 状态列与目标 section 一致;move 允许跨 OPEN/CLOSED 表迁移并在需要时同步 GitHub 状态列;delete 仅删除匹配行。四类写入口默认 dry-run,非 dry-run 必须带--expect-body-sha或--expect-updated-at,并返回 old/new row、body SHA、line/section 计划和 parser 结果;duplicate/ambiguous row、列数不匹配、缺少新增必填字段、section/status 冲突或 body SHA 不匹配都会结构化失败,不会 fallback 到整篇 body 手工替换。gh issue scan-escape [--repo owner/name] [--limit N] [--dry-run]只读扫描 issue 主体和 comments 中的字面量\n、可疑\t、shell newline escape、escaped backtick、ANSI escape 字符串、短 body、blank body 和 null body。输出固定 JSON,findings会带bodyKind=issue-body|comment-body、issueNumber、issueId、commentId、lineNumber、column、kind、snippet和classification=suspected-pollution|explanatory-mention|risk,用于区分说明性提到\n和疑似污染;cleanupSuggestions只给 dry-run 清理建议、body/comment 定位和 diff-like preview,不 PATCH、不 DELETE、不真实清理历史 comment。gh issue cleanup-plan是同一只读能力的别名,默认dryRun=true。gh pr list [--state open|closed|all] [--json ...]提供 REST 列表,默认state=all以保持既有 UniDesk CLI 行为,字段白名单是body,title,state,number,url,author,head,base,draft,createdAt,updatedAt;未知 state 或未知--json字段必须结构化失败并带runnerDisposition=business-failed。mergeable、mergeStateStatus和statusCheckRollup不属于 list 字段,请对具体 PR 使用gh pr view <number> --json headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup,避免列表默认拉取 noisy/raw 状态汇总。gh pr files <number> [--limit N]是 PR changed-file/stat summary 的稳定 REST 入口,返回 boundedfiles、filesReturned、summary.files/additions/deletions/changes/commits、truncation和next.command,默认不输出 raw diff 或 patch;gh pr diff <number> --stat是兼容别名,返回同一 JSON,未带--stat的 raw diff 请求会结构化拒绝。gh pr read|view <number|owner/repo#number> [--json ...] [--raw|--full]继续稳定返回这些字段,并额外支持stateDetail,closed,closedAt,merged,mergedAt,mergeCommit,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup。owner/repo#numbershorthand 和冲突--repo规则与 issue read/view 相同。stateDetail是 UniDesk 归一化生命周期值open|closed|merged,用于区分 RESTstate=closed中的普通关闭和已合并;closed、closedAt、merged、mergedAt、mergeCommit、headRefName与baseRefName都来自 REST,不需要 GraphQL。mergeable、mergeStateStatus和statusCheckRollup只在 read/view 明确请求这些字段或用--raw|--full显式完整披露时通过 GitHub GraphQL 查询,GraphQL 权限不足或网络失败会结构化失败;GitHub 暂未计算完成时仍保留原始UNKNOWN/null,并额外返回closeoutMetadata.ok=false、missingOrUnknownFields、advice 和mergeBoundary.unideskCliMergeSupported=false。此时收口人员应优先重试一次;若仍缺失、需要完整gh pr view --json等 GitHub 官方字段、或需要执行 merge/review 这类 UniDesk CLI 尚未开放的操作,回退到系统gh只读观察或人工 GitHub UI,不要把空字段当作可合并证据。gh pr preflight <number> [--repo owner/name] [--full|--raw]是低噪声 PR 收口入口,gh preflight <number>和gh pr closeout <number>是兼容别名;它先输出脱敏 auth capability,再读取 PR state/draft/head/base、mergeable、mergeStateStatus 和 statusCheckRollup,默认只给 status check 计数与失败/等待预览,完整 contexts 和原始读取摘要必须显式加--full或--raw。该命令固定readOnly=true、writesRemote=false、policy.mergesPr=false、policy.unideskCliMergeSupported=false,不会创建、评论、更新或 merge PR。gh pr create --title <title> --body-file <file>|--body <text> --base <branch> --head <branch> [--draft] [--dry-run]、gh pr edit <number> [--title ...] [--body-file <file>|--body-file -|--body <text>] [--dry-run]、gh pr update <number> --mode replace|append [--body-file <file>|--body-file -|--body <text>] [--title ...] [--dry-run]、gh pr comment create <number> --body-file <file>|--body <text> [--dry-run]、gh pr comment delete <commentId> [--dry-run]、gh pr close|reopen <number> [--dry-run]是 PR CRUD/生命周期入口。pr create --dry-run只输出 planned operation,不访问 GitHub;非 dry-run 创建前会校验 repo、base、head 和 compare ahead 状态,成功时返回 PR number/url。pr edit/update使用 RESTPATCH /repos/{owner}/{repo}/pulls/{number},只发送显式提供的title和/或body字段,完全避开 GitHub Projects Classic GraphQL/projectCards;输出低噪声 JSON:ok、repo、PR number、changedFields、url、body 长度/SHA/source 元数据和 request plan,不默认回显完整正文。pr update --mode append会先读取当前 PR body 再追加正文。gh pr delete <number>和gh pr merge本阶段不开放,始终结构化返回unsupported-command;PR 生命周期删除语义请使用close。- PR dry-run/probe 的最小手动序列是:
bun scripts/cli.ts gh auth status --repo pikasTech/unidesk只读检查 token 来源、GitHub REST egress、repo 可见性和 issue read;bun scripts/cli.ts gh pr create --repo pikasTech/unidesk --title <title> --body-file <file> --base master --head <head> --dry-run检查创建计划;bun scripts/cli.ts gh pr list --repo pikasTech/unidesk --state open --limit 5 --json number,title,state,url,head,base、bun scripts/cli.ts gh pr files <number> --repo pikasTech/unidesk --limit 30、bun scripts/cli.ts gh pr view <number> --repo pikasTech/unidesk --json body,title,state,stateDetail,closed,closedAt,merged,mergedAt,mergeCommit,head,base,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup和bun scripts/cli.ts gh pr preflight <number> --repo pikasTech/unidesk做只读 PR 观察、文件统计和收口元数据检查;bun scripts/cli.ts gh pr edit <number> --repo pikasTech/unidesk --title <title> --body-file <file> --dry-run或cat <file> | bun scripts/cli.ts gh pr edit <number> --repo pikasTech/unidesk --body-file - --dry-run检查低噪声 PR 标题/正文编辑计划;bun scripts/cli.ts gh pr comment <number> --repo pikasTech/unidesk --body-file <file> --dry-run检查评论计划;bun scripts/cli.ts gh pr merge <number> --repo pikasTech/unidesk必须失败并返回结构化unsupported-command。Code Queue runner 可用bun scripts/code-queue-pr-preflight-example.ts --repo pikasTech/unidesk --base master --head <head> --comment-pr <number>一次性跑只读 auth status 与 PR create/comment dry-run;该脚本不得输出 token 值,也不会创建、评论或 merge PR。 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-e2esmoke,不提供 Code Queue CD,详细规则见docs/reference/codex-deploy.md。codex submit [prompt] [--prompt-file path|--prompt-stdin] [--queue queueId] [--provider-id id] [--cwd path] [--model model] [--reasoning-effort effort] [--execution-mode mode] [--max-attempts N] [--reference-task-id id] [--dry-run]通过 backend-core 私有代理向稳定code-queue用户服务路径提交任务;prompt 必须且只能来自位置参数、文件或 stdin 之一,--dry-run只返回结构化请求且不实际入队。长 prompt、多行 prompt、含引号/反引号/Markdown 表格/JSON/反斜杠的 prompt 必须优先用--prompt-stdin或--prompt-file,不要拼进 shell 单个参数;位置参数只适合短单行 smoke prompt。stdin 推荐用 quoted heredoc:cat <<'PROMPT' | bun scripts/cli.ts codex submit --prompt-stdin --queue <id> --dry-run,文件路径推荐bun scripts/cli.ts codex submit --prompt-file /tmp/code-queue-prompt.md --queue <id> --dry-run,确认 dry-run 后移除--dry-run提交同一 payload。dry-run 会额外输出routingRecommendation,包含推荐 route、runner、model、风险信号、prompt 自包含/issue 非唯一来源/prod-secret-DB 禁止/运行态或 release 禁止/证据要求/中等复杂度候选等 guard 状态;同时输出policyContract,固定暴露 GPT-5.5、DeepSeek、MiniMax 的风险分层、并发上限和外部 provider 429 退避处置。该建议只用于指挥官 preflight,不会改写 payload,不改变 runtime admission,也不假设生产 MiniMax 或 DeepSeek 可用。--dry-run必须返回完整 prompt、字符数和truncated=false用于人工验收;真实提交是写入操作,默认只返回accepted=true、task id、队列、写入保护摘要和后续查看命令,必须标记promptOmitted=true且不得回显 prompt 或 promptPreview。真实提交会经过本机本地串行化保护和短节流,避免同一指挥端并发 submit 把低内存主机或code-queue-mgr控制面打抖;返回值会附带executionMode、runnerPermissions和低噪声submitConcurrencyGuard,显式说明 requested/effective mode、服务级 runner sandbox/approvalPolicy、锁与等待信息。--execution-mode是 Code Queue runtime placement,不是 Codex sandbox 权限;有效模式是default和windows-native,--execution-mode full-access等 sandbox-like 值会保留 requested 值并显示 effectivedefault,同时提示当前不支持每任务 sandbox override。真实提交的queue摘要保持低噪声:submittedTaskIds、queuedTaskIds、activeTaskIds和databaseActiveTaskIds是有界预览对象,countContext与counts是权威计数;submitted.taskStates[]直接给出本次 task id、queue id、status 和state=queued|running|terminal|unknown,其来源固定为response.tasks[].status。当本次新任务仍是 queued/retry_wait,queuedTaskIds.items必须包含该 id;当 counts 非零但 active/queued id 列表因为 split-brain-live、上游省略或默认有界披露而不可枚举时,预览必须设置idsUnavailable=true、itemsOmitted=true和itemsMeaning=not-enumerated-in-default-submit-output,不得打印容易误读的items=[]。queue.activity.effectiveActiveTaskCount和queue.commanderConcurrency.activeRunnerCount是并发判断字段;splitBrainLive=true时继续把 fresh heartbeat/database active 计入 active。需要原始 drill-down 时使用queue.listPreviewPolicy.rawCommand,默认是bun scripts/cli.ts microservice proxy code-queue /api/tasks/overview?limit=30 --raw --full。backend-core 默认把提交、队列 CRUD、已读状态、历史摘要和轻量 Trace 读取分流到主 servercode-queue-mgr,由它写入主 PostgreSQL;D601 scheduler 只轮询并执行已入库任务。codex steer <taskId> [prompt|--prompt-file path|--prompt-stdin] [--steer-id id] [--dry-run] [--no-retry|--retry-attempts N] [--full|--raw]向运行中的 Code Queue 任务发送纠偏 prompt。CLI 会为同一 task/prompt 生成稳定steerId,也允许显式传入--steer-id;所有 retry 都复用同一steerId,支持后端按 key 抑制重复 trace 注入。真实成功只返回低噪声写入确认,不回显 prompt 或完整任务状态;输出包含steer.status、steer.deliveryState、steer.steerId、traceConfirmation和commands.traceConfirm。失败默认只返回accepted=false、原因、scope、retryable、attempt 摘要、operator guidance 和 task/read/submit/health drill-down 命令。upstreamBodyPreview、request 元数据和 raw upstream failure 必须显式加--full或--raw才输出。任务已终态时返回紧凑task-already-terminal、status=not_accepted、deliveryState=not_accepted、task 状态、终态状态、更新时间、retryable=false和codex task/codex read/codex submit --reference-task-id <taskId>后续命令。codex resume <taskId> [prompt|--prompt-file path|--prompt-stdin] [--resume-id id] [--dry-run] [--full|--raw]对已终态或 awaiting-closeout 的原 Code Queue task 创建后续 turn,优先用于 PR 小修、冲突、rebase、补测和 reviewer feedback,保留原 task、attempt、branch/PR 上下文和codexThreadId/OpenCode session。CLI 会为同一 task/prompt 生成稳定resumeId,也允许显式传入;同一resumeId加同 prompt 返回duplicate_suppressed且不重复注入,同一resumeId加不同 prompt 返回 409 conflict。真实成功只返回 taskId、resumeId/turnId、deliveryState、是否复用原codexThreadId、有界 trace confirmation 和codex task/detail/trace/output后续命令,不回显 prompt 或完整 task state。running/judging task 必须 fail closed 并给出disposition=use-steer-for-active-task与codex steer命令,不把 resume 伪装成新 task;不存在 task 返回结构化 not accepted。若 delivery timeout 或 trace 未确认,输出deliveryUnconfirmed和确认命令,调用方先查codex task <taskId> --trace再用同一resumeId重试。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-queueproxy 请求 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 RESTbun scripts/cli.ts gh可用性、push dry-run/PR create dry-run 的writesRemote=false、expected PR handoff、真实 PR 创建需要 commander 授权和gh pr merge的unsupported-command边界;系统ghbinary 缺失只进入tools.systemGhBinary,不得误判为 UniDesk RESTghCLI 不可用。--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。该摘要读取默认由主 servercode-queue-mgr从 PostgreSQL 返回,不依赖 D601code-queue-readService 可用。codex tasks [--view commander|supervisor|full] [--queue id] [--status succeeded|running|queued|failed|canceled|judging|retry_wait[,..]] [--unread|--unread-only] [--limit N] [--before-id id]通过同一私有代理输出渐进式披露视图。host commander 轮询应优先使用--view commander:它只返回有界 action map,包含activeRunners.count及来源/处置、queued/retry_wait 精确计数、terminal-unread 总数和已省略行数、active/stale/heartbeat/final-response blocker 风险、HWLAB#7/#99/#116/#164/#317 与 UniDesk#20/#118 命中、确定性分类和codex task/trace/output/readdrill-down 命令,不嵌入完整 prompt、final response、trace、output 或 raw overview。默认supervisor保持旧低噪声分区视图,只返回activeRunning、running、completedUnread、recentCompleted、queued、activity、commanderConcurrency和executionDiagnostics的紧凑行;activeRunning.count是 running+judging 的状态计数,exact=true时来自 queue summary counts,running.returned和activeRunning.rowPage.returned只是本次返回的紧凑行数。commanderConcurrency.activeRunnerCount是并发策略应使用的 active/running 计数,等于activity.effectiveActiveTaskCount;15 并发策略按15 - activeRunnerCount计算剩余窗口。commanderConcurrency.splitBrainDisposition=live-count-as-active表示 split-brain 有 fresh heartbeat 证据,应继续监督并计入 active;interventionRequired=true才提示介入。prompt/body 只给短预览和原始字符数,running/completedUnread/queued默认只返回一个有界小页并通过 sectioncommands.next继续分页,recentCompleted默认限量且不重复completedUnread未读终态,不嵌入完整 Trace、final response 或全量 overview。--limit在 commander/supervisor 中主要是扫描/分页预算,不是返回几十条肥行的开关;CLI 安全上限是 100,输出会在filters.requestedLimit、filters.effectiveLimit、filters.limitCapped和 disclosure 中说明显式请求是否被 capped;底层 overview 拉取预算独立显示在source.requestedLimit/source.effectiveLimit,所以--limit 260应显示 requested=260、effective=100、source requested/effective=200,而不是只露出一个含糊的limit。--unread是--unread-only的别名,必须只保留未读终态;--status必须真实过滤支持的状态,未知参数或未知状态必须结构化失败。需要更详细当前页任务行时显式使用--view full或--full,仍受--limit和--before-id分页约束。codex unread [summary|mark-read] [--queue id] [--repo owner/name] [--issue N] [--status succeeded|failed|canceled[,..]] [--limit N] [--before-id id] [--confirm]是完成未读积压的默认低噪声 triage 入口。默认只读返回 repo/issue/status/queue 计数和最新任务 id 小页,不拉取 per-task summary,不输出 raw prompt、final response、trace 或 output;每行只给codex task/detail/trace/output/readdrill-down 命令。批量已读必须使用codex unread mark-read ... --confirm,缺少--confirm时结构化失败且不 POST/read;单任务审阅仍优先codex read <taskId>。codex task <taskId> --trace --tail|--from-start|--after-seq N|--before-seq N --limit N按页拉取 Code Queue 的逻辑 trace;响应会返回nextAfterSeq、previousBeforeSeq、hasMore、hasBefore和下一页/上一页命令,默认--trace取最新一页,且仍以分页 trace 为主;需要完整 prompt/最终 response 时加--full,需要详细 task 摘要时加--detail。codex output <taskId> --tail|--from-start|--after-seq N|--before-seq N --limit N [--full-text]按原始 output seq 分页读取底层记录;当 trace 行提示commandOmittedLines、bodyOmittedLines或rawSeqs时,用该命令按 seq 补取信息。默认是低噪声 raw-output 摘要:即使传入很大的--limit,非--full-text也会限制返回行数和单条文本预览,并在disclosure.limitCapped、requestedLimit、effectiveLimit和commands.fullText中说明如何继续展开;显式--full-text才返回该页全文。codex read <taskId>在人工审阅后标记单个终态任务已读,并在同一次响应中返回稳定任务身份、执行元数据、终态 attempt 摘要、最后错误或 judge 信息和最终 response,避免标记已读后还要额外 drill-down 才能确认结果。该命令不返回完整 prompt、tool logs 或 feedback prompt,只返回字符数、计数和codex task/detail/trace/output渐进披露命令;列表、overview 和 supervisor 视图只返回这个命令字段,不得自动执行,也不得批量清空未读状态。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 <taskId> [prompt|--prompt-file path|--prompt-stdin] [--steer-id id] [--dry-run] [--no-retry|--retry-attempts N] [--full|--raw]通过 Code Queue 私有代理向正在运行的 task 注入纠偏提示,正式替代底层microservice proxy code-queue /api/tasks/<taskId>/steer调用。prompt 必须且只能来自位置参数、文件或 stdin 之一;--dry-run只输出method、path、stableProxyPath、steerId、retry policy、prompt 字符数、截断预览、commands.traceConfirm和 raw proxy 等价命令,不触碰运行中 session,也不得泄露超长 prompt 全文。真实执行是写入操作,成功只返回accepted=true、status=accepted、deliveryState、steerId、task id、prompt 字符数、promptOmitted=true、有界traceConfirmation、task/queue 确认、attempt summary 和后续查看命令,不回显 prompt 或完整 task state;路径固定为/api/microservices/code-queue/proxy/api/tasks/<taskId>/steer,只能作用于 D601 scheduler 上存在 active steerable turn 的 running task。默认对stable-proxy-failed和backend-core-unreachable这类 retryable control-plane failures 做一次有界重试;--retry-attempts N最大为 3,--retry-delay-ms N最大为 5000,--no-retry用于复现单次失败。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非 dry-run 失败仍输出 JSON 且退出非零,除非后续steer-confirm证明 trace 已含该steerId,此时返回ok=true、steer.status=accepted_response_timeout、deliveryState=accepted_response_timeout,表示 backend 已接受但响应路径超时。.data.diagnostics.reason用于 runner 分流,当前包括backend-core-unreachable、code-queue-microservice-unregistered、proxy-unauthorized、proxy-404、steer-endpoint-404、upstream-runtime-rejected、stable-proxy-failed和invalid-proxy-response。scope区分backend-core、stable-proxy、code-queue-runtime或unknown,默认带status、exitCode、retryable、attempts、retryPolicy、operator guidance 和推荐交叉验证命令,但不回显 steer prompt、不带 request body、不带upstreamBodyPreview,也不带 raw upstream response;这些详细字段必须显式加--full或--raw才展开。若任务不在 running/active-turn 状态,通常归类为upstream-runtime-rejected并显示status=not_accepted/deliveryState=not_accepted,不得静默成功。502 provider HTTP tunnel failed、provider-gateway-http-fetch、The operation was aborted或约 30 秒 tunnel wait abort 会归类为stable-proxy-failed,CLI 会先按 retry policy 重试并在失败后自动做一次steer-confirm查询;如果.data.steer.deliveryUnconfirmed=true仍为 true,指挥官应运行输出中的commands.traceConfirm,确认仍 unknown 后才用输出中的commands.retry复用同一个--steer-id重试。后端已支持同steerId+同 prompt 的重复请求返回duplicateSuppressed=true,不会再次注入 trace;同steerId搭配不同 prompt 会返回 409 conflict。若 D601 返回的 409 已包含 terminal task state,CLI 默认改为紧凑终态响应:reason=task-already-terminal、status=not_accepted、deliveryState=not_accepted、task status、terminal status、updatedAt/finishedAt、retryable=false,并只给出codex task <taskId>、codex read <taskId>和codex submit --prompt-file <path> --reference-task-id <taskId>后续命令,不回显 steer prompt、完整 request body 或大 task object。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 create <queueId>创建、queue merge <sourceQueueId> --into <targetQueueId>合并、move <taskId> --queue <queueId>迁移;这些队列管理入口默认由主 servercode-queue-mgr直管 PostgreSQL,仍通过稳定code-queue用户服务代理路径访问。codex queues默认只返回 active/nonempty/unread/runnable queue 摘要、activity、commanderConcurrency、全局 counts 和 execution diagnostics;--full或--all只切换为完整队列行视图的一页,仍受--limit/--page/--offset分页约束,不再默认携带 deprecated full array。summary 和 full 的稳定机读路径都是.data.queues.items[],全局元数据固定在.data.queues.commanderConcurrency、.data.queues.activity、.data.queues.counts、.data.queues.executionDiagnostics、.data.queues.activeTaskIds和.data.queues.queuedTaskIds;需要完整 upstream 时使用输出中的 raw command。commanderConcurrency.activeRunnerCount/activity.effectiveActiveTaskCount是指挥官并发判断的有效活跃数,schedulerLocalActiveQueueCount/activeQueueIds只描述本地 scheduler active-run slots,不能覆盖数据库 running 计数或 heartbeat-fresh runner 计数。旧 full 顶层数组语义已作为 deprecated 兼容信息记录,不再作为.data.queues主形态。同一个 queue 内部串行执行,不同 queue 之间并行执行。迁移只允许尚未被 scheduler claim 的queued/retry_wait任务,必须满足startedAt=null、currentAttempt=0且没有 active thread/turn;已进入running/judging或已有 claim 标记的任务返回 409,不得被 move/merge 回写成 queued。合并会移动可迁移任务归属并自动删除源 queue 记录,只保留合并后的目标 queue;若 source 或 target queue 存在 active/claimed 任务,合并整体返回 409。合并后的目标 queue 按任务原queueEnteredAt/createdAt时间顺序串行,成功迁移 queued/retry_wait 任务后由 D601 scheduler 轮询推进。 - 所有
codex查询和管理命令必须走与 WebUI 相同的 backend-core 私有代理路径/api/microservices/code-queue/proxy/...;CLI 不得为了提交、移动、中断、取消或队列管理直接调用 D601 内部 Service、数据库、pod curl 或 k3sctl scheduler 子服务。若该路径失败,应先修复 CLI/backend/provider tunnel 链路,而不是绕过控制面。 job list [--limit N] [--include-command]与job status <jobId|latest> [--tail-bytes N]查询.state/jobs/文件系统状态,是异步命令的可观测入口。job list默认只返回最新 50 条摘要;job status默认只返回 stdout/stderr 末尾 12000 字节,并带tailPolicy与完整日志路径。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跑最小必要集合。
Playwright Commander Wrapper
UniDesk 仓库自带 scripts/playwright-cli.ts 作为 host commander 浏览器手测 wrapper。它是短生命周期、JSON 输出的命令,不是长驻浏览器 daemon。
bun scripts/playwright-cli.ts screenshot <url> [path] [--session id] [--selector css] [--full-page]默认 headless 打开 URL、保存截图、把 storage state 写到.state/playwright-cli/sessions/,并返回status、finalUrl、title、screenshotPath等紧凑 JSON。bun scripts/playwright-cli.ts open <url> [--session id] [--screenshot path]执行一次导航;需要证据时加--screenshot。bun scripts/playwright-cli.ts eval <url> '<javascript-expression>' [--session id]在导航后执行单个表达式并返回结构化值。session-list和session-delete [sessionId]只管理 storage-state 文件。--session不表示 live page,也不会保留 element refs。click、fill、snapshot、tab-list、close等交互式 daemon 命令会返回unsupported-command和下一步建议,不会透传给npx playwright,因此--session=<id>不会再被上游 Playwright 当作未知参数。- 默认走 headless,适合无 XServer runner。确实需要 headed 时使用
xvfb-run -a bun scripts/playwright-cli.ts open <url> --headed --screenshot /tmp/page.png;Code Queue runner 镜像必须包含xvfb-run和xauth作为该兜底路径。
外部 agent skill ~/.agents/skills/playwright 是另一个 source of truth。当前宿主上它可能仍是 npx playwright passthrough,但 SKILL.md 里描述了更丰富的 --session、snapshot 和 element-ref 操作。外部 skill 分发更新前,UniDesk/HWLAB 指挥手测应使用本仓库 wrapper;不要把外部 skill 文档当成 daemon/session 能力已经可用的证据。
Async Job State
长时操作采用 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 id 确认结果;不要把连续 server rebuild 命令理解成“前一个重建已完成”,因为两个命令只是在快速创建异步 job。重建 frontend 只保留为维护/非标准路径;标准 frontend 发布必须先运行 ci publish-user-service --service frontend --commit <full-sha>,再运行 deploy apply --env dev --service frontend 和 deploy apply --env prod --service frontend,并验证 /health.deploy.commit。重建 dev 入口薄代理使用 bun scripts/cli.ts server rebuild dev-frontend-proxy,随后验证 server status 的 urls.devFrontend 和 http://127.0.0.1:18083/health;重建 Todo Note 后端使用 bun scripts/cli.ts server rebuild todo-note,随后用 microservice health todo-note 和 microservice proxy todo-note /api/instances 验证;重建 Code Queue Manager 使用 bun scripts/cli.ts server rebuild code-queue-mgr,随后用 microservice health code-queue-mgr、microservice health code-queue 和 codex submit --dry-run 验证主 server 控制面路径;重建 Project Manager 后端使用 bun scripts/cli.ts server rebuild project-manager,随后用 microservice health project-manager 和 microservice proxy project-manager /api/projects 验证;重建 Baidu Netdisk 后端使用 bun scripts/cli.ts server rebuild baidu-netdisk,随后用 microservice health baidu-netdisk 和 microservice proxy baidu-netdisk /api/transfers 验证,但该命令只保留为维护/非标准路径;重建 OA Event Flow 后端使用 bun scripts/cli.ts server rebuild oa-event-flow,随后用 microservice health oa-event-flow 和 microservice proxy oa-event-flow /api/diagnostics 验证。D601 Code Queue 执行面和 Decision Center 后端由 D601 k3s/k8s 控制面代管;persistent dev backend-core、frontend 和 Decision Center 都走 artifact consumer,当前 Code Queue 仍不得通过维护通道直连 D601 做部署。不得把 docker rm 手工兜底当成正式交付步骤。
新部署入口优先使用 deploy apply。deploy apply --env dev --service backend-core 是 D601 k3s artifact consumer,消费 ci publish-backend-core 产出的成品镜像;deploy apply --env dev|prod --service frontend、deploy apply --env dev|prod --service baidu-netdisk 和 deploy apply --env dev|prod --service decision-center 是 artifact-consumer 样板;旧的 codex deploy 已禁用;后续 Code Queue 等 D601 服务部署应另行收敛到同一类受控 CD 路径,部署后用 live commit 校验证明不是旧服务。
Output Contract
每条命令的最外层 JSON 包含 ok、command 和 data 或 error。失败时 CLI 设置非零退出码,但仍然输出 JSON 错误对象;错误对象应包含 name、message 和可用的 stack。
诊断命令默认采用渐进披露:server logs、job list/status、codex task/trace/output、microservice health code-queue 和 microservice proxy 都必须有默认条数、字节数或文本预览上限;用户显式传 --limit、--tail-bytes、--full-text、--raw 或 --full 才扩大单次输出。CLI stdout 遇到下游 pipe 关闭的 EPIPE 必须安静退出,不得打印 Bun stack trace。
microservice proxy 是面向人工验证和受控调试的私有后端入口。默认 method 为 GET;使用 --body-json JSON、--body-file path 或 --body-stdin 时默认 method 切换为 POST,也可显式加 --method POST|PUT|PATCH|DELETE,但 GET/HEAD 不允许携带请求体。所有请求仍受 config 中的 allowedMethods 和 allowedPathPrefixes 限制。为了避免 Pipeline snapshot 这类超大业务 JSON 造成 CLI 输出爆炸,响应 body 超过默认阈值时会返回 bodyOmitted=true、bodyPreview、bodyBytes 和 rawHint;--raw 仍受默认硬限额保护,需要完整 body 时显式添加 --raw --full,或用 --max-body-bytes <N> 调整预览阈值。正式 frontend 展示仍应优先使用业务控件和 __unideskArrayLimit 这类展示级裁剪参数,而不是默认倾倒完整 JSON。
GitHub issue/PR 写操作必须优先使用 bun scripts/cli.ts gh issue|pr ... --body-file <file>。不要把 Markdown 正文拼进 shell 参数、gh issue comment --body 或 gh api -f body=...;这些路径容易把真实换行污染成字面量 \n。从 shell 生成正文文件时使用 quoted heredoc,例如 cat <<'EOF' > /tmp/body.md,保证反引号、反斜杠和 Markdown 表格不被 shell 展开;之后再把文件交给 --body-file。gh issue 写命令不接受 stdin 正文;需要从生成内容写入 issue 时,先落到临时 Markdown 文件或已审阅的工作文件,再传给 --body-file。PR 安全写入口同样优先 --body-file;gh pr edit/update --body-file - 可从 stdin 读取已审阅 Markdown,适合 runner 管道化更新 PR title/body。--body 只适合短单行内容。JSON 请求体场景使用各命名空间自己的 --body-file 或 --body-stdin,避免长 JSON 直接塞进 shell 参数;GitHub issue Markdown 写入仍只走 --body-file。update --mode append 用 REST 读取旧正文后追加文件字节,不引入 shell 拼接正文路径。gh pr merge 暂不开放,不存在 --confirm 可绕过的真实 merge 路径。CLI 会按 UTF-8 原样读取文件或 stdin 内容并用 JSON body 调用 REST API;PR edit/update 输出不会默认回显完整正文。
network perf 用于生成组网性能前后对比数据。标准 Code Queue overview 读路径基准命令是 bun scripts/cli.ts network perf --service code-queue --path /api/tasks/overview?limit=30 --count 30 --concurrency 1 --label before,远程主 server 可用 bun scripts/cli.ts --main-server-ip 74.48.78.17 network perf ...。输出包含成功/失败数、状态码分布、x-unidesk-cache、x-unidesk-proxy-mode、x-unidesk-upstream-proxy-mode 分布和 min/p50/p90/p95/max;provider-gateway 长连接数据面验收应看到 proxyModeCounts.provider-ws-http-tunnel,adapter native Service 数据面验收应看到 upstream proxy mode 为 kubernetes-native-service,若出现 kubernetes-api-service-proxy 必须结合 /api/control-plane.nativeServiceProxy.failedServices 解释 fallback 原因。
Debug Contract
debug 子命令必须复用真实模块与真实端点,禁止维护平行实现。debug health 会摘要展示 /api/nodes/system-status 和 /api/nodes/docker-status,避免输出完整快照造成信息爆炸。debug dispatch 会通过 backend-core 容器内置 helper 访问 backend-core /api/dispatch,core 再通过 WebSocket 将 docker.ps、provider.upgrade、host.ssh、microservice.http 或 echo 任务下发给 provider gateway,因此它可以验证核心调度闭环,同时不需要公开 core REST API,也不要求 frontend 容器携带调试 broker。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
ssh <providerId> [ssh-like args...] 是面向人的终端透传入口,不包装 JSON 输出。CLI 会在宿主机启动 docker exec -i unidesk-backend-core backend-core --ssh-broker ...,broker 只连接 backend-core 的 Docker 内网 /ws/ssh,core 再把 stdin/stdout/stderr 流量通过目标 provider 的既有 WebSocket 转发到 provider-gateway,provider-gateway 最终执行维护用 SSH 连接宿主或 WSL sshd。TTY 策略固定为交互登录 shell 使用 ssh -tt,带远端命令的会话使用 ssh -T;脚本 stdin、apply-patch 和 py 这类命令模式不得被伪终端回显或注入控制字符。该入口不新增 core 公网端口,不暴露 database,也不改变 frontend/dev frontend/provider ingress 之外的公网边界。
bun scripts/cli.ts ssh --help 和 bun scripts/cli.ts ssh <providerId> --help 是本地 JSON 帮助命令,必须快速返回;不能把 --help 解析成 Provider ID,不能打开交互 shell,也不能等待 provider 会话。
bun scripts/cli.ts ssh D518 应表现为登录 D518 WSL 的 shell;bun scripts/cli.ts ssh D518 hostname 应像 ssh D518 hostname 一样只输出远端命令结果并返回远端 exit code。Provider ID 前的目标选择由 UniDesk 节点清单决定,-p、-i、-l、-o 等传统 ssh 传输参数由 provider-gateway 部署配置统一管理,CLI 会兼容性消费这些参数但不会覆盖节点侧维护桥配置。指挥官、CI 预检和其他非交互流程不要依赖 ssh-like 自由拼接;标准写法是 bun scripts/cli.ts ssh D601 argv true,或者在需要管道、变量展开和多条命令时使用 bun scripts/cli.ts ssh D601 argv bash -lc '<command>'。
core 只允许声明了 host.ssh capability 的 provider 使用 ssh 透传或 host.ssh dispatch;旧 provider 不支持该能力时必须快速失败并输出错误,不能把未知命令误判成 echo 成功。
本地 broker 默认等待 provider SSH 会话打开 60000ms,以便在目标节点同时有较多 microservice.http 任务时仍能建立维护会话;需要诊断慢连接时可用 UNIDESK_SSH_OPEN_TIMEOUT_MS=<ms> 临时调大,但最小有效值固定为 15000ms,避免把真实离线误判为长时间阻塞。
ssh-like 远端命令如果出现 kex_exchange_identification、Connection closed by remote host、provider session timeout 或 exit code 255,CLI 会在原始 stderr 后追加一行 UNIDESK_SSH_HINT { ... }。该 JSON 不回显原始远端命令,只包含 code=ssh-like-command-friction、trigger、try 和 triage;try 固定指向 bun scripts/cli.ts ssh D601 argv bash -lc '<command>' 形态,避免把一次 ssh-like 解析/握手摩擦误读成 D601 SSH 整体不可用。
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 临时文件:
bun scripts/cli.ts ssh D601 apply-patch <<'PATCH'
*** Begin Patch
*** Update File: /home/ubuntu/pipeline/scripts/src/nodeControl.ts
@@
-const value = "old";
+const value = "new";
*** End Patch
PATCH
如果只是想远端执行 Python 脚本,不要再手写 bun scripts/cli.ts ssh D601 'python3 -' < script.py。正式入口是 bun scripts/cli.ts ssh D601 py < script.py;CLI 会先把本地 stdin 写入远端临时 .py 文件,再以无缓冲模式执行并自动清理,同时对额外脚本参数逐个做 shell quoting,避免字符串转义问题。典型用法:
printf 'import sys\nprint(sys.argv)\n' | bun scripts/cli.ts ssh D601 py foo '--bar=baz'
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 挂载层。
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。典型用法:
bun scripts/cli.ts ssh D601 find /home/ubuntu --max-depth 4 --type d --icontains pika --limit 50 --sort
ssh <providerId> glob 是远端 glob 匹配入口,支持 --root DIR、--pattern PATTERN、--contains TEXT、--icontains TEXT、--type any|f|d、--limit N、--sort 和 --absolute。--contains 与 --icontains 可避免在本地 shell 中输入 *;若显式使用 --pattern '**/*.ts' 这类 pattern,仍应按本地 shell 规则加引号,防止参数到达 CLI 前已被本地 shell 展开。典型用法:
bun scripts/cli.ts ssh D601 glob --root /home/ubuntu/pikapython --pattern '**/*-test.cpp' --limit 20 --sort
ssh <providerId> argv <command> [args...] 是通用 argv 安全拼接入口;exec 是同义入口。它是非交互远端命令的默认成功路径,不需要 shell 管道时直接传命令和参数,例如 bun scripts/cli.ts ssh D601 argv true;需要管道、重定向、变量展开或多条命令时使用 bun scripts/cli.ts ssh D601 argv bash -lc '<command>',让 shell 脚本作为 bash -lc 的一个 argv token 传递。find、glob 和 apply-patch 有专用入口;rg、grep、sed、nl、stat、du、ls、cat、head、tail、wc 和 pwd 可以直接作为 ssh 子命令使用,CLI 会对每个 argv token 做 shell quoting。旧的自由 ssh-like 远端命令入口只保留为近似原生 ssh 的人工兼容路径。
通过 ssh <providerId> 执行多行脚本时,优先使用结构化 helper,例如 bun scripts/cli.ts ssh D601 py < script.py 或 printf ... | (bun scripts/cli.ts ssh D601 'bash -s') 这种单层 stdin 传输。不要在远端命令字符串里再嵌套 heredoc、复杂引号或 ssh 'python3 - <<EOF ...' 形态;多层 shell 解析容易把 stdin 绑定到错误进程,结果会打开远端交互解释器并留下悬挂的 broker/SSH 会话。长脚本需要复用时,优先通过 stdin 写入目标节点的临时脚本,再在同一个远端命令中显式执行并清理。
Remote Main Server Passthrough
--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。运行中纠偏 codex steer 属于 active run write control,应在主 server 本机 CLI 或显式 SSH 传输上执行,避免公网 frontend 透传限制 stdin/body 审计语义。其中 ssh 的 remote frontend 传输使用 host.ssh dispatch 执行有界远端命令,非交互命令同样优先 ssh D601 argv true 或 ssh D601 argv bash -lc '<command>';交互式登录 shell 仍应在主 server 本机 CLI 使用,或显式切换到旧 SSH 传输后在主 server 上执行。frontend 远程透传不会流式转发本地 stdin,因此 ssh py < script.py、ssh apply-patch < patch.diff 这类 stdin-backed helper 必须在主 server 本机运行,或显式切换到 --main-server-transport ssh。当 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 自测,不能视为交付完成。
远程透传的安全边界是公网 frontend 登录态和 frontend 到 backend-core 的内网代理;不要把 provider token、数据库端口或 backend-core REST API 暴露给计算节点。旧 SSH 传输只作为兼容路径保留,不得把“必须提供主 server SSH key”作为计算节点自测的前置条件。