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
+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", () => {