@@ -116,8 +116,23 @@ PipelineRun 失败或长时间未完成时,先按定点 `control-plane status
- `server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>` 创建异步 job,先构建目标服务镜像,随后在 `.state/locks/server-compose.lock` 串行保护下用 `--no-deps --force-recreate` 替换目标 service 并等待容器 `healthy/running` 。Todo Note 的 CC01/旧 Compose 回退入口已在迁移验收后删除;正式发布只走 NC01 PaC/Argo。D601 Code Queue 执行面不由 `server rebuild` 管理;Rust backend-core 常规迭代不得用该命令在 master server 编译,只有明确的 backend-core 主 server 上线例外可以按限流、异步轮询和 health 证据执行,规则见 `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` 、`trans <providerId> argv true` 、`artifact-registry health --provider-id <providerId>` 、`microservice health k3sctl-adapter` 、`microservice health code-queue` 和 `codex tasks --view supervisor --limit 20` 。
- `platform-db postgres plan|status|export-secrets|apply --config config/platform-db/postgres-pk01.yaml` 管理 YAML 声明的 PK01 host-native PostgreSQL。`plan` 和 `status` 只读采集远端 host、PostgreSQL、TLS、DNS alias 和 Secret 形态;`export-secrets --confirm` 只按 YAML 物化本地 Secret source/export 文件,不触碰远端 PostgreSQL; `apply --confirm` 默认创建本地异步 job, `apply --confirm --wait` 用远端 root-owned job 收敛 systemd PostgreSQL、TLS、`pg_hba` 、role/database、Secret export 和备份 timer。输出不得打印密码或完整 `DATABASE_URL` 。跨节点消费者使用 YAML 中的 `connectionHost` 直连 PK01 公网 endpoint, DNS alias 不作为 `sslmode=require` 切库 blocker, PK01 规则见 `docs/reference/pk01.md` 。
- `trans <route> [operation args...]` / `tran <route> [operation args...]` 通过 backend-core 内网 WebSocket broker 和 provider-gateway 的 Host SSH / WSL SSH 维护桥连接目标节点; `route` 基础形态是 provider id,例如 `D601` 或 `G14` ,也可以扩展为纯定位路径 `provider:plane[:namespace:resource[:container]]` ,例如 `D601:win` 、 `D601:win/c/test` 、 `G14:k3s` 、 `D601:k3s` 或 `G14:k3s:<namespace>:<workload>` 。WSL provider 的 Windows plane 固定使用 `win` ,不得使用 `win32` ; Windows route 中 `win` 只负责定位,operation 直接写 `ps` 、 `cmd` 、 `git` 或 fs helper,不要写成 `trans D601:win/... win ps` 。Windows operation 必须显式区分: `ps` 执行 Windows PowerShell heredoc 或一行 PowerShell 命令, `cmd` 执行 cmd.exe/batch, `skills` 发现 Windows skill 目录。需要 Windows cwd 时用 `trans D601:win/c/test ps` 、 `trans D601:win/c/test cmd cd` 、 `trans D601:win/c/test git diff --check` 或 `trans D601:win/c/test git commit -m 'message'` , CLI 自动设置 UTF-8/Python 编码默认值;`cmd` 额外设置 `chcp 65001` 。非交互远端命令优先使用 `trans <providerId> argv ...` ;需要 POSIX shell 脚本、管道、变量或循环时必须在 operation 位置显式写 `sh` 或 `bash` ,例如 `trans G14 sh <<'SH'` 、 `trans G14:k3s sh <<'SH'` 、 `trans G14:k3s:<namespace>:<workload> sh <<'SH'` 或 `trans D601:/workspace bash <<'BASH'` 。 `sh` 明确表示目标 `/bin/sh` , `bash` 明确表示目标 Bash; `script` 和 `shell` operation 已移除并会失败,避免隐藏 shell 方言。Windows PowerShell 必须写 `trans <provider>:win ps <<'PS'` 。一行远端 shell 逻辑使用 `sh -- '<单个字符串>'` 或 `bash -- '<单个字符串>'` ;顶层 remote option parser 必须保留命令已经开始后的 `--` ,不得把它吞成全局选项结束符。需要远端改文本文件时默认优先使用 `<route> apply-patch < patch.diff` ;需要可靠传输非文本或整文件时使用 `<route> upload <local-file> <remote-file>` 和 `<route> download <remote-file> <local-file>` , CLI 会按字节数与 SHA-256 自动校验并在 provider-gateway stdin/argv 限制下切换客户端分块策略;需要旧 helper 时显式使用 `<provider>:k3s:<namespace>:<workload> apply-patch-v1` 或 `<providerId> apply-patch-v1` 。ssh-like 命令遇到 timeout/kex/255 类失败时,CLI 会在 stderr 追加一行 `UNIDESK_SSH_HINT` JSON,提示改用 `sh` / `bash` stdin heredoc、argv 或 provider triage 交叉验证。
- `trans <route> apply-patch < patch.diff` 是默认推荐的远端 patch 入口:本地 TypeScript line-based engine 解析和计算新文件内容,远端 route 只负责读写文件;支持 host workspace、k3s pod workspace、Windows workspace route(例如 `D601:win/c/test` )和 frontend transport,并优先处理长中文/Unicode、低上下文插入、重复块 `@@` 定位等旧 helper 容易失败的场景。 `apply-patch` 输出按 Codex 标准文本口径,不套 UniDesk JSON 限制:成功 stdout 为 `Success. Updated the following files:` ,失败 stdout 为空、stderr 写失败原因;多文件补丁中途失败时,stderr 只列出第一个失败前已成功执行的 hunk 和失败 hunk,随后按 Codex 语义停止,不继续尝试后续 hunk。v2 兼容常见 MiniMax/MXCX 非标准补丁输入,例如重复 nested `*** Begin Patch` / `*** End Patch` envelope、unified-diff hunk header、Add/Delete 误加 `@@` 、Update context 漏掉前导空格,并在 stderr 给出 canonical 写法 hint; parser 或上下文失败时仍坚持唯一 v2 引擎,只提示修正 patch 文本或 hunk context,不自动重试或切换到 `apply-patch-v1` ;大块/函数替换因上下文过期失败时,正确动作是重新读取当前目标块、缩小或拆分 Update File hunk 后继续用 `apply-patch` ,不得改走 `download` / `upload` 、远端 Python/Perl/sed heredoc 或整文件重写。Windows route 复用同一套 v2 核心算法,只把底层读写替换成 PowerShell 文件系统接口; `trans <providerId> apply-patch-v1 [tool args...] < patch.diff` 保留为显式 legacy 入口,直接调用远端注入的 `apply_patch` sh/perl helper;默认 `apply-patch` 不把 v1 当 fallback 。
- `trans <route> [operation args...]` / `tran <route> [operation args...]` 通过 backend-core 内网 WebSocket broker 和 provider-gateway 的 Host SSH / WSL SSH 维护桥连接目标节点:
- `route` 基础形态是 provider id,例如 `D601` 或 `G14` ;也可以扩展为纯定位路径 `provider:plane[:namespace:resource[:container]]` ,例如 `D601:win` 、 `D601:win/c/test` 、 `G14:k3s` 、 `D601:k3s` 或 `G14:k3s:<namespace>:<workload>` 。
- WSL provider 的 Windows plane 固定使用 `win` ,不得使用 `win32` ; Windows route 中 `win` 只负责定位,operation 直接写 `ps` 、`cmd` 、`git` 或 fs helper。
- Windows operation 中,`ps` 执行 PowerShell heredoc 或一行 PowerShell 命令,`cmd` 执行 cmd.exe/batch, `skills` 发现 Windows skill 目录;CLI 自动设置 UTF-8/Python 编码默认值,`cmd` 额外设置 `chcp 65001` 。
- 非交互远端命令优先使用 `trans <providerId> argv ...` ;需要 POSIX shell 脚本、管道、变量或循环时,在 operation 位置显式写 `sh` 或 `bash` ,并通过 quoted heredoc 传入正文。
- 远端文本修改默认使用 `trans <route> apply-patch <<'PATCH'` ;已知文本路径不得改走 `upload` / `download` ,只有整体搬运生成文本产物时才显式加 `--allow-text-transfer` 。
- 非文本或二进制文件使用 `<route> upload <local-file> <remote-file>` 和 `<route> download <remote-file> <local-file>` ,CLI 自动核对字节数与 SHA-256。
- 只有人工确认需要旧 helper 时,才显式使用 `<provider>:k3s:<namespace>:<workload> apply-patch-v1` 或 `<providerId> apply-patch-v1` ,输入仍使用 quoted heredoc。
- ssh-like 命令遇到 timeout/kex/255 类失败时,CLI 在 stderr 追加 `UNIDESK_SSH_HINT` JSON,提示改用 `sh` / `bash` stdin heredoc、argv 或 provider triage 交叉验证。
- `trans <route> apply-patch <<'PATCH'` 是默认推荐的远端 patch 入口:
- 本地 TypeScript line-based engine 解析并计算新文件内容,远端 route 只负责读写文件;支持 host workspace、k3s pod workspace、Windows workspace route 和 frontend transport。
- quoted heredoc 中直接放置标准 `*** Begin Patch` / `*** End Patch` envelope,不先创建 patch 临时文件。
- 成功 stdout 为 `Success. Updated the following files:` ;失败 stdout 为空,stderr 写失败原因。多文件补丁中途失败时按 Codex 语义停止,保留失败前已提交的变更。
- v2 可规范化常见非标准补丁输入并给出 canonical 写法 hint; parser 或上下文失败时,只提示修正 patch 文本或 hunk context,不自动重试或切换到 `apply-patch-v1` 。
- 大块替换因上下文过期失败时,重新读取当前目标块并缩小或拆分 Update File hunk;不得改走 `download` / `upload` 、远端 Python/Perl/sed heredoc 或整文件重写。
- Windows route 复用同一套 v2 核心算法,只把底层读写替换成 PowerShell 文件系统接口。
- `trans <providerId> apply-patch-v1 [tool args...] <<'PATCH'` 是显式 legacy 入口,直接调用远端注入的 `apply_patch` sh/perl helper;默认 `apply-patch` 不把 v1 当 fallback。
- GitHub issue/PR 正文局部修补必须优先走 `trans gh:/owner/repo/issue/<number> apply-patch` 或 `trans gh:/owner/repo/pr/<number> apply-patch` ,而不是人工优先整篇 `gh issue update --mode replace` 或底层 `gh issue patch` 。`gh:/` 路由把 issue/PR body 暴露为一楼虚拟文件 `body.md` ; route 未显式写 `/1` 时默认一楼正文,`apply_patch` 与 `patch-apply` 只作为兼容别名。典型补丁写法是 `*** Update File: body.md` ;普通小补丁在已用 `trans gh:/... cat|rg|ls` 确认上下文后可以直接 apply, `--dry-run` 只作为高风险大段修改、关闭前验收文案或并发敏感正文的可选预览。写回仍通过 UniDesk `gh issue/pr update` guard 和 REST API,不使用原生 `gh` 、手写 REST 或整篇 shell 拼接正文。单条 issue comment 的局部修补当前仍使用 `bun scripts/cli.ts gh issue comment patch <commentId> --body-patch-stdin` ,直到评论楼层 route 成为稳定入口。
- `apply-patch` v2 每次结束都会在 stderr 追加一行 `UNIDESK_APPLY_PATCH_TIMING {json}` ,字段包含 `durationMs` 、`patchBytes` 、`fileCount` 、`hunkCount` 、`changedCount` 、`remoteOperationCount` 、`remoteOperationCounts` 、`remoteElapsedMs` 、`remoteFailureCount` 、`providerId` 、`route` 和 `transport` (可得时)。普通 POSIX host/k3s 和 Windows workspace 远端的多文件 `Update File` patch 会优先合并成 bulk read/write,避免每个文件单独 stat/read/write 的 SSH 往返;Add/Delete/Move 等复杂 patch 保持原有逐步语义。timing 摘要只用于定位慢在 patch 解析、远端 stat/read/write 或 bulk read/write、provider session 还是传输层,不能替代 Codex 标准 stdout/stderr 成功失败文本,也不是门禁或自动判断。
- `trans <providerId> py [script-args...] < script.py` 把本地 stdin 落到远端临时 `.py` 文件后再以 `python3 -u` 执行并自动清理,避免再手写 `'python3 -'` 、heredoc 或多层引号;`script-args` 会按 argv 安全透传给远端脚本。
@@ -154,7 +169,11 @@ PipelineRun 失败或长时间未完成时,先按定点 `control-plane status
- `secrets plan|sync|status --config config/secrets-distribution.yaml --scope platform-infra` 是平台服务本地 Secret sourceRef 到 Kubernetes Secret key 的受控下发入口。`plan` 只读展示 sourceRef、必需 key、目标 Secret/key、missingKeys 和 fingerprint; `sync --confirm` 只按 YAML 声明创建允许生成的本地 key 并下发到声明的目标 Secret;`status` 只验证 live Secret key presence。该入口禁止从 live pod 或 Kubernetes Secret 反推密码、API key、JWT secret、n8n encryption key 或 `DATABASE_URL` ,输出也不得打印 base64、解码值或远端 raw transcript;即使显式 `--raw` 也只返回脱敏 summary 和 raw omission 标记。LangBot/n8n Secret 轮换和缺 key 修复规则见 `docs/reference/platform-infra.md#secret-distribution-boundary` 。
- `hwlab g14 observability status|apply|query|targets|boundary|closeout [--lane v02] [--promql <expr>] [--expect-count N] [--expect-value V] [--dry-run|--confirm]` 是 G14 `devops-infra` 共享监控基础设施和 HWLAB v0.2 监控 closeout 的受控入口。`apply` 固定安装 Prometheus Operator `v0.91.0` 、Prometheus `v3.12.0` 、Prometheus 发现 RBAC、`devops-infra` 内 Prometheus 实例和 ClusterIP query Service,并给被允许发现的 workload namespace 打低风险 label;它不把 Prometheus、Grafana 或 Alertmanager 部署到 `hwlab-v02` ,也不接管 HWLAB runtime Deployment/Service。`status` 只读汇总 CRD、operator Deployment、Prometheus CR/pod/service、`hwlab-v02` ServiceMonitor/PrometheusRule 和 bounded `up` 查询;`query` 只通过 Kubernetes service proxy 查询 Prometheus,支持 `--expect-count` / `--expect-value` 输出 `assertion` 、bad values 和 missing/extra series; `targets` 汇总 ServiceMonitor/PrometheusRule、metrics sidecar readiness/restart、三层指标值和 `metrics.k8s.io` 当前 CPU/内存资源快照;`boundary` 验证 workload namespace 没有 Prometheus/Alertmanager,并对 `19666/19667` 公网 `/metrics` 做负向验证;`closeout` 聚合平台 ready、scrape reachable、sidecar serving、business health probe、resource snapshot、namespace boundary 和 public metrics exposure 语义结论。长期边界见 `docs/reference/g14-observability-infra.md` 。
- `hwlab g14 tools-image status|build --name ci-node-tools --tag <tag> [--dockerfile deploy/ci/hwlab-ci-node-tools.Dockerfile] [--dry-run|--confirm]` 是 G14 固定 HWLAB CI tools image 的受控 host build/push 入口;构建和 push 只发生在 G14 host 与本地 registry,不在 master server 构建,也不把 `apk add` /runtime install 塞进 Tekton PipelineRun。
- `trans gh:/owner/repo ...` 把 GitHub issue/PR 映射成只读/ 受控写入的虚拟文本目录,适合日报、PR 正文和 issue 正文的小补丁维护: `trans gh:/pikasTech/HWLAB ls` 展示 `pr/` 与 `issue/` , `trans gh:/pikasTech/HWLAB/pr ls [--state open|closed|all] [--limit N] [--full]` 展示 PR 条目状态、楼层数、正文长度、URL 和标题, `trans gh:/pikasTech/HWLAB/issue ls [--state open|closed|all] [--limit N] [--search TEXT] [--label L] [--full]` 替代常见 `gh issue list --search/--state` 读取并输出 URL, `trans gh:/pikasTech/HWLAB/pr/507 ls` 展示单个 PR 的一楼正文文件, `trans gh:/pikasTech/HWLAB/issue/1236 cat` 读取 issue 正文, `trans gh:/pikasTech/HWLAB/issue/1236 rg 'API_KEY|YAML-first'` 在正文内检索, `trans gh:/pikasTech/HWLAB/505/1 cat|rg|patch-apply` 兼容旧式 issue/PR number route。 `apply-patch` 使用 UniDesk 默认 apply-patch v2 的虚拟文件 executor,把正文一楼映射为 `body.md` ,写回仍走 `bun scripts/cli.ts gh issue/pr update` 的 guard/concurrency 规则;普通小补丁不需要固定先跑 `--dry-run` ,高风险正文可用 `--dry-run` 预览。 `rm` 对正文一楼结构化拒绝,避免误删 issue/PR 正文。大正文读取必须展开 UniDesk gh dump 文件,否则 `cat/rg/apply-patch` 会误读为空,这是 `gh:` 虚拟文件接口的 P0 可见性契约。
- `trans gh:/owner/repo ...` 把 GitHub issue/PR 映射成只读、 受控写入的虚拟文本目录:
- `ls` 展示 PR / issue 目录和有界条目摘要;`cat` 直接读取一楼正文;`rg` 在正文内检索。
- `apply-patch` 使用默认 v2 虚拟文件 executor,把正文一楼映射为 `body.md` ,写回仍走 `bun scripts/cli.ts gh issue/pr update` 的 guard/concurrency 规则。
- 普通小补丁不固定要求先跑 `--dry-run` ;高风险正文可用 `--dry-run` 预览;`rm` 对正文一楼结构化拒绝。
- 大正文默认不创建或依赖 dump;接口必须直接消费单份正文。超过显示预算时返回语义化窄查询,调用方继续用同一 `gh:` route 的 `rg` 、`cat` 或 `apply-patch` ,不得展开临时 dump 文件。
- `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 dump, stdout 只返回有界摘要。默认 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 语义失败。
- `gh issue list [owner/repo] [--state open|closed|all] [--limit N] [--search text] [--title-prefix 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 前做低摩擦查重;`--title-prefix` 在当前有界结果内按 issue 标题 `startsWith` 做本地过滤,输出 `titleFilter.inputCount/outputCount/filteredOut` ,适合 `[FEEDBACK]` 等标题规范去重;未知 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 可能混入 PR, CLI 会从 `.data.issues` 中过滤 pull request。`--raw|--full` 在 `gh issue list` 上是绕过 20 KiB stdout 截断的显式开关:响应结果会带 `noDump=true` , `output.ts` 据此跳过 head/tail 替换并把完整数据 inline 输出;当响应未超阈值时 `--raw|--full` 行为等价默认。
@@ -333,7 +352,12 @@ exec /root/unidesk/scripts/trans "$@"
### Standard Workspace-Prefixed Passthrough
- 长期参考、AGENTS 索引、CLI help、Codex 任务脚本、CI/CD 排障和人工远端操作必须统一把已知的远端 workspace 写在 route 的第一个 token,而不是塞进 ` cd` 串。` route` 段只表达分布式定位,operation 段才执行命令;workspace 路径是定位信息,不是命令。
- 标准形态是 ` trans <provider>:/absolute/workspace <operation> [args...]`: 例如 ` trans G14:/root/hwlab git status --short --branch `、` trans G14:/root/hwlab-v02 sh -- 'git fetch origin v0.2 && git pull --ff-only origin v0.2' `、` trans D601:/home/ubuntu/workspace/unidesk-dev sh <<'SH' `、` trans D601:/home/ubuntu/workspace/hwlab-v03 bash <<'BASH' `、` trans G14:/root/hwlab apply-patch < patch.diff ` 和 ` trans G14:/root/hwlab glob --root . --pattern 'web/hwlab-cloud-web/*.ts' --contains session-tabs `。
- 标准形态是 ` trans <provider>:/absolute/workspace <operation> [args...]`:
- 只读命令:` trans G14:/root/hwlab git status --short --branch`。
- 单行 shell: ` trans G14:/root/hwlab-v02 sh -- 'git fetch origin v0.2 && git pull --ff-only origin v0.2'`。
- 多行 shell: ` trans D601:/home/ubuntu/workspace/unidesk-dev sh <<'SH'` 或 ` trans D601:/home/ubuntu/workspace/hwlab-v03 bash <<'BASH'`。
- 文本修改:` trans G14:/root/hwlab apply-patch <<'PATCH'`。
- 结构化检索:` trans G14:/root/hwlab glob --root . --pattern 'web/hwlab-cloud-web/*.ts' --contains session-tabs`。
- 反面形态必须删除或迁移:` trans G14 sh -- 'cd /root/hwlab && git status --short --branch'`、` trans G14 sh <<'SH' cd /root/hwlab-v02 git fetch origin v0.2 SH`、` tran G14 sh -- 'cd /home/ubuntu/workspace/unidesk-dev && ...'`、route 后继续写旧的 ` script` / ` shell` operation、` bun scripts/cli.ts ssh G14 -- 'cd /root/hwlab && ...'`。这些写法把已知 workspace 写进 command 字符串,破坏 route/operation 分离,引入本地 shell 二次解析、远端 cwd 漂移和并行 worktree 切换摩擦,或继续使用已移除的模糊 shell 旧名。
- 例外只限于一次性探测、临时 heredoc 草稿或旧文档复用;任何被复用第二次的 ` cd <workspace> && ...` 都必须重写成 ` trans <provider>:/absolute/workspace` 形式。
- 当远端存在多个并行 workspace(例如 ` G14:/root/hwlab` 与 ` G14:/root/hwlab-v02`)时,route 必须显式带 workspace, CLI 的 ` pwd` 输出、后续 ` apply-patch` 的相对路径和 ` sh`/` bash` 的 cwd 全部跟随该 workspace;切换 workspace 必须切换 route,不允许在同一次 ` trans` 链里再 ` cd`。
@@ -376,13 +400,26 @@ core 只允许声明了 `host.ssh` capability 的 provider 使用 `host.ssh` dis
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` 固定指向显式 ` sh` stdin heredoc 形态,避免把一次 ssh-like 解析/握手摩擦误读成 D601 SSH 整体不可用。` ssh`/` trans`/` tran` 在失败路径识别到 tcp-pool 数据面问题时会追加 ` UNIDESK_SSH_TCP_POOL_HINT { ... }`, ` failureKind` 固定分为 ` provider-data-channel-closed`、` provider-data-channel-missing` 和 ` provider-data-pool-exhausted`;这类 hint 表示 transport/data-pool transient,幂等受控操作应先运行 ` bun scripts/cli.ts debug ssh-pool <providerId>` 查看 labels,再重试原受控 CLI,不能单独定性为远端 runtime 配置失败。backend-core/provider 控制面返回结构化 ` ssh.error` 时 broker 还会输出 ` UNIDESK_SSH_ERROR { ... }`,供 ` job status` 从旧日志或异步 job 日志中恢复 failureKind。` ssh`/` trans`/` tran` 运行时硬超时会输出 ` UNIDESK_SSH_RUNTIME_TIMEOUT { ... }` 或 wrapper 层 ` UNIDESK_TRAN_TIMEOUT_HINT { ... }`;这不是远端业务失败,而是调用方需要改成短查询/轮询。` ssh`/` trans`/` tran` 只有在运行耗时超过默认 10000ms 时才会在 stderr 追加一行 ` UNIDESK_SSH_TIMING { ... }`,且 ` level=warning`;正常短调用不输出 timing 噪声。慢成功命令也必须保留该 warning,因为它是 provider session、远端命令成本、helper bootstrap 和 ` trans`/` tran`/远端 patch 性能回归的重要监控信号。warning 包含 ` elapsedMs`、` elapsedSeconds`、` transport`、` invocationKind` 和 ` exitCode`,提示优先排查 provider/session 延迟、远端命令自身耗时、helper bootstrap 或工具层回归。阈值可用 ` UNIDESK_SSH_SLOW_WARNING_MS=<ms>` 临时调节,提示同样不回显原始远端命令。
非交互 ` ssh`/ ` trans`/ ` tran` 远端命令的流式 stdout/stderr 服从统一 CLI dump/preview 策略,默认预览预算来自 ` config/unidesk-cli.yaml #output .maxStdoutBytes `,完整流写入同一 YAML 声明的 dump 目录;交互登录 shell 不套该上限。超过预算时,CLI 继续读取远端流并把完整内容写入 ` /tmp/unidesk-cli-output/*.stdout.bin ` 或 ` *.stderr.bin `,本地 stderr 追加 ` UNIDESK_SSH_STDOUT_TRUNCATED { ... } ` 或 ` UNIDESK_SSH_STDERR_TRUNCATED { ... } `,其中包含 ` stream `、` thresholdBytes `、` forwardedBytes `、` observedBytesAtTruncation `、` dumpPath `、` dumpError `、` disclosurePolicy ` 和 ` recommendedRerun `;被截断的本地流只保留 YAML 字节预算内的开头预览。需要一次性扩大字节预算时可显式设置 ` UNIDESK_SSH_STDOUT_STREAM_MAX_BYTES ` / ` UNIDESK_TRAN_STDOUT_STREAM_MAX_BYTES ` 或 ` UNIDESK_SSH_STDERR_STREAM_MAX_BYTES ` / ` UNIDESK_TRAN_STDERR_STREAM_MAX_BYTES `,仍受最小 4KiB、最大 16MiB 的工具保护。该机制只做渐进披露和完整 dump,不替代远端命令失败判断;看到 hint 时应优先改成 ` rg -m `、` sed -n `、` tail `、` --limit `、` --tail-bytes `、` --raw/--full ` 等更窄的结构化 drill-down。
非交互 ` ssh` / ` trans` / ` tran` 远端命令的流式 stdout/stderr 使用统一的有界预览策略:
- 默认预览预算来自 ` config/unidesk-cli.yaml#output .maxStdoutBytes`;超过预算后继续消费远端流以取得最终 exit code,但超出部分不落盘,` dumpPath=null`。
- 本地 stderr 追加 ` UNIDESK_SSH_STDOUT_TRUNCATED { ... }` 或 ` UNIDESK_SSH_STDERR_TRUNCATED { ... }`,披露 stream、阈值、已观察字节数、` dumpPath`、disclosure policy 和推荐的窄查询。
- 只有用户显式设置 ` UNIDESK_TRAN_STREAM_DUMP=1` 时才把完整流写入受保护的 dump;` --full` / ` --raw` 属于上层命令的显式完整披露,不应由默认 ` trans` 静默推导。
- 需要一次性扩大预览预算时可显式设置 ` UNIDESK_SSH_STDOUT_STREAM_MAX_BYTES` / ` UNIDESK_TRAN_STDOUT_STREAM_MAX_BYTES` 或对应的 stderr 变量,仍受最小 4KiB、最大 16MiB 的工具保护。
- 看到截断 hint 时优先改成 ` rg -m`、` sed -n`、` tail`、` --limit` 或 ` --tail-bytes` 等更窄的结构化 drill-down;交互登录 shell 不套该上限。
` trans <providerId>` 透传只在当前 operation 需要 helper 时才注入 ` /tmp/unidesk-ssh-tools`,普通 ` argv`、` sh`/` bash`、` kubectl`、` logs` 和默认 ` apply-patch` 等路径不得传输无关工具源码。` apply-patch-v1` 只注入 ` apply_patch`; ` glob` 只注入 ` glob`; ` skills`/` skill discover` 只注入 ` skill-discover`。` apply_patch` 接受标准 ` *** Begin Patch` / ` *** End Patch` patch 格式,便于通过 SSH 透传编辑远端仓库文件;远端存在 ` perl` 时必须走快速精确匹配路径,避免大文件 hunk 被 sh 模式匹配拖成几十秒,缺少 ` perl` 时才退回 sh-only 实现。` glob` 和 ` skill-discover` 需要远端 ` python3`。注入工具只写 ` /tmp/unidesk-ssh-tools`,不修改目标仓库。
远端文本 patch 默认使用 ` apply-patch` 的 v2 引擎:它不把 hunk 解析交给远端 shell/perl helper,而是在本地按行序列匹配,支持长中文/Unicode 行、纯新增 hunk、低上下文插入和 ` @@` 上下文定位,再把完整新内容写回远端。v2 的文件操作提交顺序按 Codex 标准 ` apply_patch` 语义执行:空 patch 会失败;删除不存在的文件会失败;` Add File` 可覆盖已有文件;` Move to` 可覆盖目标文件;当大 patch 后续 hunk 不匹配时,已成功提交的前序文件操作会保留,并在错误详情中记录 ` partialChanges`,调用方应基于当前文件内容继续补一个更小的 patch,而不是期待全量事务回滚。若 stderr 报 ` failed to find expected lines` 且显示 partial context match,尤其是大块/函数替换,调用方必须先重读目标文件当前块,再用更少稳定上下文、` @@ <unique anchor>` 或多个小 hunk 重试;该失败不构成改用 ` download`/` upload`、远端脚本整文件替换或 ` apply-patch-v1` 的理由。` apply_patch` 旧 helper 默认拒绝低上下文 update hunk:空搜索/纯插入无锚点、只在插入点前有上下文而没有插入点后上下文、或同一 hunk search 在目标文件中匹配多个位置时,都会结构化失败并提示补充上下文。成功应用时每个 hunk 会在 stderr 输出 ` apply_patch: hunk N matched path:line`,用于复核实际落点;只有人工确认确实需要旧 helper 行为或 ` --allow-loose` 时,才显式调用 ` apply-patch-v1 --allow-loose`。
如果只是 远端打 文本补丁,不需要再手写 ` ssh D601 'apply_patch' < patch.diff ` 这种命令拼接;正式默认入口是 ` trans D601:/absolute/workspace apply-patch < patch.diff `、` trans D601:k3s:<namespace>:<workload>[:<container>] apply-patch --cwd /workspace < patch.diff ` 或 ` trans D601:win/c/test apply-patch < patch.diff `。旧 helper 只有 ` apply-patch-v1 ` 一个入口,附加参数会原样透传给远端 ` apply_patch `,例如 ` trans D601 apply-patch-v1 --help ` 或 ` trans D601 apply-patch-v1 --allow-loose < reviewed.patch `。标准单命令用法如下,不需要先创建本地 patch 临时文件:
远端文本补丁统一通过 stdin quoted heredoc 直送,不先创建、下载或上传 patch 临时文件:
- host workspace 使用 ` trans D601:/absolute/workspace apply-patch <<'PATCH'`。
- k3s workload 使用 ` trans D601:k3s:<namespace>:<workload>[:<container>] apply-patch --cwd /workspace <<'PATCH'`。
- Windows workspace 使用 ` trans D601:win/c/test apply-patch <<'PATCH'`。
- 旧 helper 只有 ` apply-patch-v1` 一个显式入口;附加参数原样透传,补丁正文仍使用 quoted heredoc,例如 ` trans D601 apply-patch-v1 --allow-loose <<'PATCH'`。
标准单命令用法如下:
pod 内文本热修的反面案例是:先在 host/source worktree 生成 ` git diff`,再用本地 ` sed` 改路径、拼 ` *** Begin Patch` 包头,最后管道到 ` G14:k3s:<namespace>:<pod>`。这种做法把 source workspace、local shell、远端 shell 和 pod workspace 四层混在一起,容易出现 patch 格式错误、路径漂移、部分成功后误判、以及“看起来在热修 pod,实际主要在搬运 host diff”的错误行为。正确做法是把第一个 route token 直接定位到目标 pod/container,容器内 cwd 用 ` --cwd /path` 表达,然后在同一条 route 上写标准 Codex patch: