fix: 清理 trans 文本旧入口示例

This commit is contained in:
Codex
2026-07-13 05:29:30 +02:00
parent c04983455e
commit 76758b58fd
7 changed files with 96 additions and 18 deletions
+43 -6
View File
@@ -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 公网 endpointDNS alias 不作为 `sslmode=require` 切库 blockerPK01 规则见 `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 写法 hintparser 或上下文失败时仍坚持唯一 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 写法 hintparser 或上下文失败时,只提示修正 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 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 语义失败。
- `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 可能混入 PRCLI 会从 `.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 必须显式带 workspaceCLI 的 `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 255CLI 会在原始 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
+6 -1
View File
@@ -147,7 +147,12 @@ MiniMax prompt 必须自包含:目标、背景、写入范围、禁止动作
MiniMax 风险控制必须固定包含:禁止 prod/重启/密钥/DB 写入;禁止 release/v1 runtime 修改;禁止 heavy check/E2E/Playwright,除非任务明确改为 GPT-5.5 且用户授权;必须给出可验证证据,包括修改文件、命令、测试输出、commit 和未覆盖风险;完成后保持未读,由指挥官用 `codex task <taskId>` 审阅后再单独 `codex read <taskId>`
MiniMax 任务涉及远端文本修改时,prompt 必须显式指定标准写入入口和失败恢复动作:默认使用 workspace-prefixed `trans <route> apply-patch < patch.diff` 或对应业务 CLI 的 `apply-patch` 子命令;若 stderr 报 `failed to find expected lines`,必须重新读取当前目标块,缩小或拆分 `Update File` hunk 后继续 `apply-patch`。不得把上下文过期当作改用 `download`/`upload`、远端 Python/Perl/sed heredoc、`cat >`/`tee` 整文件重写或 `apply-patch-v1` 的理由。指挥官验收 MiniMax trace 时应搜索这些 forbidden command 形态;若出现,优先修 CLI hint/prompt 或拆小任务后复测,而不是把绕行作为合格完成。
MiniMax 任务涉及远端文本修改时,prompt 必须显式指定标准写入入口和失败恢复动作:
- 默认使用 workspace-prefixed `trans <route> apply-patch <<'PATCH'`,或对应业务 CLI 的 `apply-patch` 子命令;补丁正文直接进入 quoted heredoc,不先创建 patch 文件。
- 若 stderr 报 `failed to find expected lines`,必须重新读取当前目标块,缩小或拆分 `Update File` hunk 后继续 `apply-patch`
- 不得把上下文过期当作改用 `download` / `upload`、远端 Python/Perl/sed heredoc、`cat >` / `tee` 整文件重写或 `apply-patch-v1` 的理由。
- 指挥官验收 MiniMax trace 时应搜索这些 forbidden command 形态;若出现,优先修 CLI hint/prompt 或拆小任务后复测,不把绕行作为合格完成。
对 MiniMax 的验收不能看 final response 自述。指挥官至少核验 diff、commit 是否在声明目标分支可 fetch、轻量验证命令是否真实存在、输出是否和任务范围匹配;遇到推测、遗漏、伪造证据或把失败写成成功时,先拆成更小任务或改派 GPT-5.5。
+1 -1
View File
@@ -391,7 +391,7 @@ wrapper 负责 bridge 命令语法,本文只记录运行边界。
- route 在前,operation 在后;
- 默认使用 `apply-patch` v2。
- 稳定写入形式:
- `trans <provider>:/absolute/workspace apply-patch < patch.diff`
- `trans <provider>:/absolute/workspace apply-patch <<'PATCH'`
- 只读检查:
- 使用 `trans <provider>:/absolute/workspace nl -ba file` 或同等有界命令。
- `apply-patch` 报告 `failed to find expected lines` 时:
+29
View File
@@ -3,6 +3,7 @@ import { Readable, Writable } from "node:stream";
import { describe, expect, test } from "bun:test";
import {
ApplyPatchV2Error,
applyPatchV2HelpPayload,
runApplyPatchV2,
type ApplyPatchV2BulkReplacementWritePlan,
type ApplyPatchV2FileSystem,
@@ -35,6 +36,34 @@ function applyReplacementPlan(original: string, plan: ApplyPatchV2BulkReplacemen
}
describe("apply-patch v2 stdin preflight", () => {
test("documents quoted heredoc input without a temporary patch file", () => {
const help = applyPatchV2HelpPayload();
const serialized = JSON.stringify(help);
expect(help.command).toContain("<<'PATCH'");
expect(help.usage.every((example) => example.includes("<<'PATCH'"))).toBe(true);
expect(serialized).not.toMatch(/<\s+patch[.]diff/);
expect(serialized).not.toMatch(/\/tmp\/patch[.]diff/);
});
test("malformed input hint points directly to a quoted heredoc", async () => {
const stdout = new CaptureWritable();
const stderr = new CaptureWritable();
const exitCode = await runApplyPatchV2({
executor: {},
stdin: Readable.from(["not a patch\n"]),
stdout,
stderr,
});
expect(exitCode).toBe(1);
expect(stdout.text()).toBe("");
expect(stderr.text()).toContain("trans <route> apply-patch <<'PATCH'");
expect(stderr.text()).toContain("without a temporary patch file");
expect(stderr.text()).not.toMatch(/\/tmp\/patch[.]diff/);
});
test("fails immediately on an interactive TTY and points to a quoted heredoc", async () => {
const stdin = Readable.from([]) as Readable & { isTTY?: boolean };
Object.defineProperty(stdin, "isTTY", { value: true });
+1 -1
View File
@@ -780,7 +780,7 @@ function appendParserFailureHint(lines: string[], message: string, details: Reco
return;
}
if (message.startsWith("invalid patch:")) {
lines.push("Hint: apply-patch expects a single stdin patch whose first non-wrapper line is *** Begin Patch and last line is *** End Patch. Quoted heredocs such as `cat <<'PATCH' > /tmp/patch.diff` preserve the required leading spaces.");
lines.push("Hint: apply-patch expects a single stdin patch whose first non-wrapper line is *** Begin Patch and last line is *** End Patch. Send it directly with `trans <route> apply-patch <<'PATCH'`; quoted heredocs preserve required leading spaces without a temporary patch file.");
lines.push("Hint: apply-patch remains the v2 engine only; v1 is never auto-selected for malformed MiniMax/MXCX patch envelopes.");
}
}
+7 -6
View File
@@ -33,7 +33,7 @@ export function rootHelp(): unknown {
{ command: "trans gh:/owner/repo[/pr|/issue][/number[/1]] ls|cat|rg|apply-patch", description: "Treat GitHub PRs/issues as virtual text directories; `issue ls --search/--state` covers filtered reads, `cat|rg` reads first-floor body text, and `apply-patch` updates `body.md` through UniDesk gh plus apply-patch v2." },
{ command: "trans <route> apply-patch <<'PATCH'", description: "Default remote text patch entry: send a quoted stdin heredoc directly to the local TypeScript v2 engine while the remote route only reads and writes files." },
{ command: "trans <route> upload|download [--allow-text-transfer] ...", description: "Transfer binary files or generated artifacts through SSH passthrough with automatic endpoint byte/SHA-256 verification; known text paths fail before transfer unless explicitly overridden." },
{ command: "trans <providerId> apply-patch-v1 [tool args...] < patch.diff", description: "Fallback to the injected legacy remote apply_patch helper directly over SSH passthrough and stream the patch from local stdin." },
{ command: "trans <providerId> apply-patch-v1 [tool args...] <<'PATCH'", description: "Fallback to the injected legacy remote apply_patch helper directly over SSH passthrough and stream the patch from a quoted stdin heredoc." },
{ command: "trans <providerId> py [script-args...] < script.py", description: "Run remote Python from local stdin through SSH passthrough without nested shell quoting; extra args become script argv." },
{ command: "trans <providerId> sh|bash [arg...] <<'SH|BASH' ...", description: "Run a remote POSIX sh or Bash body from local stdin; the operation name declares the shell dialect, and removed `script`/`shell` operations fail with a migration hint." },
{ command: "trans <providerId> skills [--scope all|wsl|windows] [--limit N]", description: "Discover WSL/Linux and, for WSL providers, Windows skill directories in one SSH passthrough call." },
@@ -264,7 +264,7 @@ const SSH_OPERATION_HELP: Record<SshHelpScope, SshOperationHelp> = {
"apply-patch-v1": {
group: "patch",
description: "显式调用 legacy remote apply_patch helper;只作为人工确认后的兼容入口。",
usage: ["trans <route> apply-patch-v1 [--allow-loose] < patch.diff"],
usage: ["trans <route> apply-patch-v1 [--allow-loose] <<'PATCH'"],
},
upload: {
group: "transfer",
@@ -429,10 +429,11 @@ function sshDownloadHelp(): unknown {
},
},
examples: {
host: "trans D601:/tmp download /tmp/trace.json ./trace.json",
k3sWorkload: "trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/trace.json ./trace.json",
windowsDrive: "trans D601:win download 'D:\\tmp\\trace.json' ./trace.json",
explicitWorktree: `UNIDESK_TRANS_REPO_ROOT=${repoRoot} trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/trace.json ./trace.json`,
host: "trans D601:/tmp download /tmp/firmware.bin ./firmware.bin",
k3sWorkload: "trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/runtime-bundle.tar.gz ./runtime-bundle.tar.gz",
windowsDrive: "trans D601:win download 'D:\\tmp\\capture.zip' ./capture.zip",
explicitWorktree: `UNIDESK_TRANS_REPO_ROOT=${repoRoot} trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/runtime-bundle.tar.gz ./runtime-bundle.tar.gz`,
generatedTextOverride: "trans D601:/tmp download --allow-text-transfer /tmp/trace.json ./trace.json",
},
notes: [
"A k3s download requires a workload route; the control-plane-only <provider>:k3s route cannot name a remote file inside a workload.",
+9 -3
View File
@@ -79,11 +79,17 @@ describe("ssh bounded progressive help", () => {
expect(scoped.usage[0]).toBe("trans <route> cat <remote-text-file>");
expect(serialized).toContain("text-transfer-discouraged");
expect(serialized).toContain("--allow-text-transfer");
expect(scoped.examples.host).toContain("download /tmp/trace.json ./trace.json");
expect(scoped.examples.host).toContain("download /tmp/firmware.bin ./firmware.bin");
expect(scoped.examples.k3sWorkload).toBe(
"trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/trace.json ./trace.json",
"trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/runtime-bundle.tar.gz ./runtime-bundle.tar.gz",
);
expect(scoped.examples.windowsDrive).toContain("D:\\tmp\\trace.json");
expect(scoped.examples.windowsDrive).toContain("D:\\tmp\\capture.zip");
expect(scoped.examples.generatedTextOverride).toBe(
"trans D601:/tmp download --allow-text-transfer /tmp/trace.json ./trace.json",
);
expect(scoped.examples.host).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/);
expect(scoped.examples.k3sWorkload).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/);
expect(scoped.examples.windowsDrive).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/);
});
test("renders compact top-level and scoped help without dump fallback", () => {