import { spawnSync } from "node:child_process"; import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { sshHelp } from "./src/help"; import { providerTriageRecommendedCrossChecks } from "./src/provider-triage"; import { formatSshFailureHint, parseSshArgs, parseSshInvocation, remoteApplyPatchSource, sshFailureHint } from "./src/ssh"; type JsonRecord = Record; function assertCondition(condition: unknown, message: string, detail: unknown = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function assertThrows(fn: () => unknown, pattern: RegExp, message: string): void { try { fn(); } catch (error) { const text = error instanceof Error ? error.message : String(error); assertCondition(pattern.test(text), message, { error: text }); return; } throw new Error(`${message}: expected throw`); } function applyPatchFixture(args: string[], patch: string, files: Record): { status: number | null; stdout: string; stderr: string; files: Record } { const root = mkdtempSync(path.join(os.tmpdir(), "unidesk-apply-patch-contract-")); try { const helperPath = path.join(root, "apply_patch"); writeFileSync(helperPath, remoteApplyPatchSource, "utf8"); for (const [relativePath, content] of Object.entries(files)) { const target = path.join(root, relativePath); writeFileSync(target, content, "utf8"); } const run = spawnSync("sh", [helperPath, ...args], { cwd: root, input: patch, encoding: "utf8", }); const outputFiles: Record = {}; for (const relativePath of Object.keys(files)) { outputFiles[relativePath] = readFileSync(path.join(root, relativePath), "utf8"); } return { status: run.status, stdout: run.stdout, stderr: run.stderr, files: outputFiles, }; } finally { rmSync(root, { recursive: true, force: true }); } } export function runSshArgvGuidanceContract(): JsonRecord { const argv = parseSshArgs(["argv", "true"]); assertCondition(argv.invocationKind === "argv", "argv subcommand must be classified as argv", argv); assertCondition(argv.remoteCommand === "'true'", "argv command must shell-quote each token", argv); assertCondition(argv.requiresStdin === false, "argv command must not require stdin", argv); assertCondition(sshFailureHint("D601", argv, 255, "kex_exchange_identification: Connection closed by remote host") === null, "argv failures must not produce ssh-like friction hint", argv); const shortcut = parseSshArgs(["pwd"]); assertCondition(shortcut.invocationKind === "argv", "safe command shortcuts must use argv quoting", shortcut); assertCondition(shortcut.remoteCommand === "'pwd'", "safe command shortcut should be shell-quoted", shortcut); const hostWorkspace = parseSshInvocation("D601:/home/ubuntu/workspace/hwlab-dev", ["pwd"]); assertCondition(hostWorkspace.route.plane === "host" && hostWorkspace.route.workspace === "/home/ubuntu/workspace/hwlab-dev", "host workspace route must parse provider:/absolute/path", hostWorkspace); assertCondition(hostWorkspace.parsed.remoteCommand === "'pwd'", "host workspace route must leave operation argv independent from location", hostWorkspace); const hostWorkspaceLongForm = parseSshInvocation("D601:host:/home/ubuntu/workspace/hwlab-dev", ["argv", "git", "status", "--short"]); assertCondition(hostWorkspaceLongForm.route.workspace === "/home/ubuntu/workspace/hwlab-dev", "host: workspace route must parse as the same location model", hostWorkspaceLongForm); assertCondition(hostWorkspaceLongForm.parsed.remoteCommand === "'git' 'status' '--short'", "host workspace argv operation must stay argv-quoted", hostWorkspaceLongForm); const script = parseSshArgs(["script", "--shell", "bash", "--", "alpha beta"]); assertCondition(script.invocationKind === "helper", "script stdin helper must be classified as helper", script); assertCondition(script.remoteCommand === "'bash' '-s' '--' 'alpha beta'", "script helper must pass stdin to shell directly", script); assertCondition(script.requiresStdin === true, "script helper must require stdin", script); const k3sGuard = parseSshInvocation("D601:k3s", ["guard"]).parsed; assertCondition(k3sGuard.invocationKind === "helper", "k3s guard must be classified as helper", k3sGuard); assertCondition(k3sGuard.remoteCommand?.includes("KUBECONFIG") && k3sGuard.remoteCommand.includes("/etc/rancher/k3s/k3s.yaml"), "k3s guard must force native k3s kubeconfig", k3sGuard); const k3sExec = parseSshInvocation("D601:k3s", ["exec", "--namespace", "hwlab-dev", "--deployment", "hwlab-cloud-api", "--", "node", "-e", "console.log(process.version)"]).parsed; assertCondition(k3sExec.invocationKind === "helper", "k3s exec must be classified as helper", k3sExec); assertCondition(k3sExec.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'node' '-e' 'console.log(process.version)'", "k3s exec must assemble kubectl argv without nested shell quoting", k3sExec); const routeKubectl = parseSshInvocation("D601:k3s", ["kubectl", "get", "pods", "-n", "hwlab-dev"]); assertCondition(routeKubectl.providerId === "D601", "route must preserve provider id", routeKubectl); assertCondition(routeKubectl.route.plane === "k3s" && routeKubectl.route.entry === null, "route must keep kubectl as an operation, not as a route entry", routeKubectl); assertCondition(routeKubectl.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'get' 'pods' '-n' 'hwlab-dev'", "D601:k3s kubectl must map to kubectl argv", routeKubectl); const g14Guard = parseSshInvocation("G14:k3s", []); assertCondition(g14Guard.providerId === "G14" && g14Guard.route.plane === "k3s", "G14:k3s must parse as a native k3s route", g14Guard); assertCondition(g14Guard.parsed.remoteCommand?.includes("UNIDESK_K3S_PROVIDER_ID") && g14Guard.parsed.remoteCommand.includes("ubuntu-rog-zephyrus-g14-ga401iv-ga401iv"), "G14:k3s guard must use the G14 node profile", g14Guard); assertThrows( () => parseSshInvocation("G14", ["k3s", "kubectl", "get", "nodes"]), /unsupported.*ssh G14:k3s/u, "k3s must be a route plane, not a post-provider shorthand", ); const routeTarget = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["node", "-e", "console.log(process.version)"]); assertCondition(routeTarget.route.namespace === "hwlab-dev" && routeTarget.route.resource === "hwlab-cloud-api", "route target must parse namespace and workload", routeTarget); assertCondition(routeTarget.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'node' '-e' 'console.log(process.version)'", "D601:k3s:: must default to deployment exec", routeTarget); const routeScript = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["script", "--shell", "bash", "--", "arg"]); assertCondition(routeScript.parsed.requiresStdin === true, "k3s script operation must stream local stdin", routeScript); assertCondition(routeScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'bash' '-s' '--' 'arg'", "D601:k3s:: script must map stdin to shell -s", routeScript); const routeControlScript = parseSshInvocation("D601:k3s", ["script", "--shell", "bash", "--", "arg"]); assertCondition(routeControlScript.parsed.requiresStdin === true, "k3s control-plane script operation must stream local stdin", routeControlScript); assertCondition(routeControlScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'bash' '-s' '--' 'arg'", "D601:k3s script must inject native kubeconfig without manual export", routeControlScript); const routeApplyPatch = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["apply-patch"]); assertCondition(routeApplyPatch.parsed.requiresStdin === true, "k3s apply-patch operation must stream local patch stdin", routeApplyPatch); assertCondition(routeApplyPatch.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-s' '--'", "D601:k3s:: apply-patch must enter pod with stdin", routeApplyPatch); assertCondition(routeApplyPatch.parsed.stdinPrefix?.includes("apply_patch") && routeApplyPatch.parsed.stdinPrefix.includes("__UNIDESK_APPLY_PATCH_PAYLOAD__"), "k3s apply-patch operation must inject pod helper before patch stdin", routeApplyPatch); assertCondition(routeApplyPatch.parsed.stdinPrefix?.includes("#!/bin/sh"), "k3s apply-patch operation must inject the same sh helper used by host apply-patch", routeApplyPatch); assertCondition(!routeApplyPatch.parsed.stdinPrefix?.includes("python3") && !routeApplyPatch.parsed.stdinPrefix?.includes("node "), "k3s apply-patch operation must use the sh-only pod helper", routeApplyPatch); assertCondition(routeApplyPatch.parsed.stdinSuffix === "\n__UNIDESK_APPLY_PATCH_PAYLOAD__\n", "k3s apply-patch operation must terminate patch heredoc", routeApplyPatch); const hostApplyPatchLoose = parseSshArgs(["apply-patch", "--allow-loose"]); assertCondition(hostApplyPatchLoose.remoteCommand === "'apply_patch' '--allow-loose'", "host apply-patch must pass --allow-loose as an explicit helper argument", hostApplyPatchLoose); const safePatch = applyPatchFixture([], [ "*** Begin Patch", "*** Update File: sample.txt", "@@", " function alpha() {", " return 1;", " }", "+function inserted() {", "+ return 3;", "+}", " function beta() {", " return 2;", " }", "*** End Patch", "", ].join("\n"), { "sample.txt": [ "function alpha() {", " return 1;", "}", "function beta() {", " return 2;", "}", "", ].join("\n"), }); assertCondition(safePatch.status === 0, "apply_patch sh helper should accept anchored insertions", safePatch); assertCondition(safePatch.stderr.includes("apply_patch: hunk 1 matched sample.txt:1"), "apply_patch sh helper must report matched file:line", safePatch); assertCondition(safePatch.files["sample.txt"]?.includes("function inserted()"), "apply_patch sh helper should apply anchored insertion", safePatch); const lowContextPatch = applyPatchFixture([], [ "*** Begin Patch", "*** Update File: sample.txt", "@@", " function alpha() {", " return 1;", " }", "+function misplaced() {", "+ return 9;", "+}", "*** End Patch", "", ].join("\n"), { "sample.txt": [ "function alpha() {", " return 1;", "}", "function beta() {", " return 2;", "}", "", ].join("\n"), }); assertCondition(lowContextPatch.status !== 0, "apply_patch sh helper should reject insert-only hunks without trailing context", lowContextPatch); assertCondition(lowContextPatch.stderr.includes("insert-only without both leading and trailing context"), "low-context rejection must explain the risk", lowContextPatch); const duplicateContextPatch = applyPatchFixture([], [ "*** Begin Patch", "*** Update File: sample.txt", "@@", " marker", "+inserted", " x", "*** End Patch", "", ].join("\n"), { "sample.txt": "marker\nx\nmarker\nx\n", }); assertCondition(duplicateContextPatch.status !== 0, "apply_patch sh helper should reject hunks whose context matches multiple locations", duplicateContextPatch); assertCondition(duplicateContextPatch.stderr.includes("matched multiple locations"), "duplicate context rejection must explain the risk", duplicateContextPatch); const reviewedLoosePatch = applyPatchFixture(["--allow-loose"], [ "*** Begin Patch", "*** Update File: sample.txt", "@@", " marker", "+reviewed", " x", "*** End Patch", "", ].join("\n"), { "sample.txt": "marker\nx\nmarker\nx\n", }); assertCondition(reviewedLoosePatch.status === 0, "--allow-loose should allow an explicitly reviewed ambiguous hunk", reviewedLoosePatch); assertCondition(reviewedLoosePatch.files["sample.txt"] === "marker\nreviewed\nx\nmarker\nx\n", "--allow-loose should still apply only one hunk", reviewedLoosePatch); assertThrows( () => parseSshInvocation("D601:k3s:kubectl", ["get", "pods"]), /route must locate a target only.*ssh D601:k3s kubectl/u, "operation names must not be accepted as k3s route segments", ); assertThrows( () => parseSshInvocation("D601:k3s:hwlab-ci:kubectl", ["get", "pods"]), /route must locate a target only.*operation "kubectl" after the route/u, "operation names must not be accepted as nested k3s route segments", ); assertThrows( () => parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api:logs", []), /route must locate a target only.*operation "logs" after the route/u, "operation names must not be accepted as k3s container route segments", ); assertThrows( () => parseSshInvocation("D601:k3s:apply-patch:hwlab-dev:hwlab-cloud-api", []), /route must locate a target only.*apply-patch/u, "pod apply-patch must be an operation after the route", ); const routePodTarget = parseSshInvocation("D601:k3s:hwlab-dev:pod/hwlab-cloud-api-abc:api", ["printenv", "HOSTNAME"]); assertCondition(routePodTarget.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'pod/hwlab-cloud-api-abc' '-c' 'api' '--' 'printenv' 'HOSTNAME'", "pod route with container must preserve explicit pod kind", routePodTarget); const routePodWorkspace = parseSshInvocation("D601:k3s:hwlab-dev:pod/hwlab-cloud-api-abc/workspace/app:api", ["pwd"]); assertCondition(routePodWorkspace.route.resource === "pod/hwlab-cloud-api-abc" && routePodWorkspace.route.container === "api" && routePodWorkspace.route.workspace === "/workspace/app", "pod route must support a workspace suffix after the pod id", routePodWorkspace); assertCondition(routePodWorkspace.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'pod/hwlab-cloud-api-abc' '-c' 'api' '--' 'sh' '-c' 'cd \"$1\" || exit; shift; exec \"$@\"' 'unidesk-cwd' '/workspace/app' 'pwd'", "pod workspace route must run commands through a fixed cwd wrapper", routePodWorkspace); const routeDeploymentWorkspaceScript = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["script"]); assertCondition(routeDeploymentWorkspaceScript.route.resource === "hwlab-cloud-api" && routeDeploymentWorkspaceScript.route.workspace === "/app", "deployment shorthand route must support workspace suffix", routeDeploymentWorkspaceScript); assertCondition(routeDeploymentWorkspaceScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-c' 'cd \"$1\" || exit; shift; exec \"$@\"' 'unidesk-cwd' '/app' 'sh' '-s' '--'", "pod workspace script must set cwd before shell -s consumes stdin", routeDeploymentWorkspaceScript); const routeApplyPatchWorkspace = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["apply-patch"]); assertCondition(routeApplyPatchWorkspace.parsed.requiresStdin === true, "pod workspace apply-patch must still stream patch stdin", routeApplyPatchWorkspace); assertCondition(routeApplyPatchWorkspace.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-c' 'cd \"$1\" || exit; shift; exec \"$@\"' 'unidesk-cwd' '/app' 'sh' '-s' '--'", "pod workspace apply-patch must set cwd before injecting the sh helper", routeApplyPatchWorkspace); const sshLike = parseSshArgs(["echo hello"]); const hint = sshFailureHint("D601", sshLike, 255, "kex_exchange_identification: Connection closed by remote host"); assertCondition(hint !== null, "ssh-like kex failure must produce a hint", sshLike); assertCondition(hint?.try === "bun scripts/cli.ts ssh D601 script <<'SCRIPT'", "hint must provide canonical stdin script retry", hint); assertCondition(hint?.triage.includes("provider triage D601"), "hint must provide provider triage command", hint); const formatted = formatSshFailureHint(hint!); assertCondition(formatted.startsWith("UNIDESK_SSH_HINT "), "formatted hint must have structured prefix", formatted); assertCondition(!formatted.includes("echo hello"), "formatted hint must not echo the original remote command", formatted); const timeoutHint = sshFailureHint("D601", sshLike, 255, "unidesk ssh bridge timed out waiting for provider session"); assertCondition(timeoutHint?.trigger === "timeout-or-kex", "provider session timeout must map to timeout-or-kex", timeoutHint); const helpText = JSON.stringify(sshHelp()); assertCondition(helpText.includes("ssh script [--shell sh|bash] [script-args...] <<'SCRIPT'"), "ssh help must recommend stdin script passthrough for shell scripts", helpText); assertCondition(helpText.includes("inherits provider proxy variables"), "ssh help must state default script inherits provider proxy env", helpText); assertCondition(helpText.includes("not as a proxy workaround"), "ssh help must reserve --shell bash for bash syntax instead of proxy workarounds", helpText); assertCondition(helpText.includes("ssh D601:/home/ubuntu/workspace/hwlab-dev git status --short --branch"), "ssh help must document host workspace routes", helpText); assertCondition(helpText.includes("ssh D601:k3s kubectl get pods -n hwlab-dev"), "ssh help must document k3s kubectl operation", helpText); assertCondition(helpText.includes("ssh G14:k3s kubectl get pipelineruns -n hwlab-ci"), "ssh help must document G14 k3s route operation", helpText); assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api/app pwd"), "ssh help must document k3s pod workspace route", helpText); assertCondition(helpText.includes("ssh D601:k3s script <<'SCRIPT'"), "ssh help must document k3s control-plane script operation", helpText); assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText); assertCondition(helpText.includes("apply-patch [--allow-loose]") && helpText.includes("low-context update hunks"), "ssh help must document apply-patch loose-context guard", helpText); assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api script <<'SCRIPT'"), "ssh help must document k3s script operation", helpText); assertCondition(helpText.includes("UNIDESK_SSH_HINT"), "ssh help must document structured failure hint", helpText); const crossChecks = providerTriageRecommendedCrossChecks("D601"); assertCondition(crossChecks.includes("bun scripts/cli.ts ssh D601 argv true"), "provider triage cross-checks must keep argv true", crossChecks); return { ok: true, checks: [ "argv form is classified and quoted as the success path for non-interactive commands", "stdin script form removes shell-command strings for host and k3s workload scripts", "pod apply-patch operation injects helper and forwards patch stdin", "apply-patch uses one sh helper for host and pod paths and rejects low-context hunks unless --allow-loose is explicit", "legacy operation-in-route forms are rejected in any k3s route segment with canonical route-plus-operation guidance", "post-provider k3s shorthand is rejected so location and operation stay separated", "k3s route stays location-only while operations fix native kubeconfig and assemble kubectl exec as argv", "ssh-like timeout/kex failures emit one structured argv retry hint", "help text documents stdin script passthrough and UNIDESK_SSH_HINT", "provider triage recommendedCrossChecks keeps ssh D601 argv true", ], }; } if (import.meta.main) { process.stdout.write(`${JSON.stringify(runSshArgvGuidanceContract(), null, 2)}\n`); }