fix: 清理 trans 文本旧入口示例
This commit is contained in:
@@ -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 });
|
||||
|
||||
@@ -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
@@ -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.",
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
Reference in New Issue
Block a user