From 33459658644a20e2c45886f1b976587c4a8c41b6 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 16 Jul 2026 04:18:09 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E6=95=9B=20trans=20shell=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- .agents/skills/unidesk-trans/SKILL.md | 2 ++ scripts/src/ssh-help.test.ts | 18 ++++++++++++++++++ scripts/src/ssh.ts | 12 +++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.agents/skills/unidesk-trans/SKILL.md b/.agents/skills/unidesk-trans/SKILL.md index 9d3eef1b..3175b32d 100644 --- a/.agents/skills/unidesk-trans/SKILL.md +++ b/.agents/skills/unidesk-trans/SKILL.md @@ -57,6 +57,8 @@ trans gh:/owner/repo/issue/ cat - 诊断不披露文件正文,也不回退 `apply-patch-v1`。 - 当前 HWLAB node/lane source workspace 的正式预检/同步入口是 `bun scripts/cli.ts hwlab nodes control-plane source-workspace sync --node --lane --confirm` 和 `source-workspace status`;不要把裸 `trans : git fetch ...` 当成当前 HWLAB node/lane workspace 预检/修复入口。 - `sh`/`bash` 必须显式声明 shell;单进程命令优先 direct argv 或已知 operation。 + - `sh`/`bash` 不接受 `-c` 等 shell 选项;错误返回 `ssh-sh-option-unsupported`,并在连接前退出。 + - shell 逻辑使用 `trans sh <<'SH' ... SH`;单进程直接使用 `trans [args...]`。 - 普通 trans/ssh 短连接硬预算 60s;长 CI/CD、trace、logs、build、硬件流程必须 submit-and-poll。 - 一次性远程实验不得临时发明第二套状态协议: - 使用 [远程临时实验 smoke](references/remote-experiment-smoke.md) 的参数合同; diff --git a/scripts/src/ssh-help.test.ts b/scripts/src/ssh-help.test.ts index 961d886d..f77b3fd9 100644 --- a/scripts/src/ssh-help.test.ts +++ b/scripts/src/ssh-help.test.ts @@ -181,6 +181,24 @@ describe("ssh bounded progressive help", () => { expect(result.stdout).not.toContain(" at "); }); + test("rejects unsupported shell options locally with compact correction paths", () => { + for (const outputArgs of [[], ["--json"]]) { + const result = spawnSync("bun", ["scripts/ssh-cli.ts", "NC01:/root/unidesk", "sh", "-c", "git status --short", ...outputArgs], { + cwd: repoRoot, + env: { ...process.env, UNIDESK_SSH_ENTRYPOINT: "trans", UNIDESK_TRANS_REPO_ROOT: repoRoot }, + encoding: "utf8", + }); + expect(result.status).toBe(1); + expect(result.stderr).toBe(""); + expect(result.stdout).toContain("ssh-sh-option-unsupported"); + expect(result.stdout).toContain("trans sh <<'SH' ... SH"); + expect(result.stdout).toContain("trans [args...]"); + expect(result.stdout).not.toContain("scripts/src/ssh.ts"); + expect(result.stdout).not.toContain("ssh-runtime.ts"); + expect(result.stdout).not.toContain("\"stack\""); + } + }); + test("rejects the moved root entrypoint locally with one exact replacement", () => { const result = spawnSync("bun", ["scripts/cli.ts", "trans", "NC01:/root/unidesk", "git", "status", "--short"], { cwd: repoRoot, diff --git a/scripts/src/ssh.ts b/scripts/src/ssh.ts index b9ec0e93..08758809 100644 --- a/scripts/src/ssh.ts +++ b/scripts/src/ssh.ts @@ -2024,7 +2024,17 @@ function buildShellCommand(args: string[], shell: "sh" | "bash", commandName: st scriptArgs.push(...args.slice(index + 1)); break; } - if (arg.startsWith("-")) throw new Error(`unsupported ${commandName} option: ${arg}`); + if (arg.startsWith("-")) { + throw new CliInputError(`unsupported ${commandName} option: ${arg}`, { + code: "ssh-sh-option-unsupported", + argument: arg, + usage: [ + `trans ${shell} <<'SH' ... SH`, + "trans [args...]", + ], + hint: `Use a quoted ${shell} heredoc for shell logic, or pass a single process as direct argv. ${shell} options such as ${arg} are not accepted.`, + }); + } scriptArgs.push(arg); } return { remoteCommand: shellArgv([shell, "-s", "--", ...scriptArgs]), requiresStdin: true, invocationKind: "helper", stdinPrefix: shellScriptStdinPrefix() };