feat: add tran win cmd route
This commit is contained in:
+5
-2
@@ -19,7 +19,7 @@ export function rootHelp(): unknown {
|
||||
{ command: "server cleanup plan [--min-age-hours N] [--limit N]", description: "Dry-run Docker image cleanup plan only: list active/protected images, stale candidates older than the default 24h threshold, risk, estimated reclaim, and manual review commands without deleting anything." },
|
||||
{ command: "server rebuild <backend-core|frontend|dev-frontend-proxy|provider-gateway|todo-note|code-queue-mgr|project-manager|baidu-netdisk|oa-event-flow>", description: "Maintenance-only local Compose rebuild for reviewed main-server services; frontend standard release must use CI artifact plus deploy apply dev/prod artifact consumers." },
|
||||
{ command: "provider attach <providerId> [--master-server URL] [--up] [--force] | provider triage <providerId> [--observed-error text] [--observed-scope scope] [--microservice id ...] [--full|--raw]", description: "Generate the minimal external provider-gateway env/compose bundle or run the low-noise read-only provider health triage contract." },
|
||||
{ command: "ssh <route> [operation args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge; route syntax such as `G14:k3s` only locates distributed targets." },
|
||||
{ command: "ssh <route> [operation args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge; route syntax such as `G14:k3s` or `D601:win/c/test` only locates distributed targets." },
|
||||
{ command: "ssh <providerId> apply-patch [tool args...] < patch.diff", description: "Invoke the injected remote apply_patch helper directly over SSH passthrough and stream the patch from local stdin." },
|
||||
{ command: "ssh <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: "ssh <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT' ...", description: "Run a remote shell script from local stdin using shell -s; the default sh inherits provider proxy env, so --shell bash is only for bash-specific syntax." },
|
||||
@@ -156,6 +156,8 @@ export function sshHelp(): unknown {
|
||||
"bun scripts/cli.ts ssh <providerId> find <path...> [--contains TEXT] [--limit N]",
|
||||
"bun scripts/cli.ts ssh <providerId> glob [--root DIR] [--pattern PATTERN]",
|
||||
"bun scripts/cli.ts ssh D601:/home/ubuntu/workspace/hwlab-dev git status --short --branch",
|
||||
"bun scripts/cli.ts ssh D601:win cmd ver",
|
||||
"bun scripts/cli.ts ssh D601:win/c/test cmd cd",
|
||||
"bun scripts/cli.ts ssh D601:k3s",
|
||||
"bun scripts/cli.ts ssh D601:k3s kubectl get pods -n hwlab-dev",
|
||||
"bun scripts/cli.ts ssh G14:k3s",
|
||||
@@ -176,7 +178,8 @@ export function sshHelp(): unknown {
|
||||
"For arbitrary stdin streams into a workload command, use a workload route plus `exec --stdin -- <command> ...`; this keeps the route as location-only and avoids heredoc/base64/tar shell wrapping.",
|
||||
"apply-patch rejects low-context update hunks by default, reports the matched file:line for each hunk on stderr, and only accepts --allow-loose when the caller has manually reviewed an intentionally ambiguous insertion.",
|
||||
"script defaults to target /bin/sh and inherits provider proxy variables such as HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY; use --shell bash only for bash syntax such as pipefail, arrays, or [[ ... ]], not as a proxy workaround.",
|
||||
"Route syntax is `{provider}:{plane}[:{scope...}] {operation} [operation-args...]`: the first argv token locates a distributed target only, and every following token belongs to the operation parser. Host workspace routes use `<provider>:/absolute/workspace`; native k3s providers such as D601 and G14 use <provider>:k3s for the control plane, <provider>:k3s:<namespace>:<workload> for a workload, and <provider>:k3s:<namespace>:<workload>/<pod-workspace> for a pod workspace.",
|
||||
"Route syntax is `{provider}:{plane}[:{scope...}] {operation} [operation-args...]`: the first argv token locates a distributed target only, and every following token belongs to the operation parser. Host workspace routes use `<provider>:/absolute/workspace`; WSL providers can use `<provider>:win cmd <command-line>` to run Windows host cmd.exe with UTF-8 defaults, and `<provider>:win/c/test cmd cd` maps the Windows cwd to `C:\\test`; native k3s providers such as D601 and G14 use <provider>:k3s for the control plane, <provider>:k3s:<namespace>:<workload> for a workload, and <provider>:k3s:<namespace>:<workload>/<pod-workspace> for a pod workspace.",
|
||||
"Use `win`, not `win32`; the win route sets chcp 65001, PYTHONUTF8=1, and PYTHONIOENCODING=utf-8 before running the requested cmd command line.",
|
||||
"Do not put operation names in any colon route segment, including nested k3s namespace/workload/container segments.",
|
||||
"Do not use post-provider shorthand such as `ssh G14 k3s ...`; write `ssh G14:k3s ...` so location and operation stay separated.",
|
||||
"If an ssh-like remote command fails with timeout/kex/exit-255 friction, stderr includes one low-noise UNIDESK_SSH_HINT JSON line with the argv retry command.",
|
||||
|
||||
@@ -6,7 +6,7 @@ import { type UniDeskConfig } from "./config";
|
||||
import { type DebugDispatchCommand, isDebugDispatchCommand } from "./debug";
|
||||
import { summarizeMicroserviceHealthResponse, summarizeMicroserviceObservation, summarizeMicroserviceProxyResponse } from "./microservices";
|
||||
import { parseNetworkPerfOptions, runNetworkPerf } from "./network-perf";
|
||||
import { formatSshFailureHint, formatSshRuntimeTimingHint, parseSshInvocation, sshFailureHint, sshRuntimeTimingHint, wrapSshRemoteCommand } from "./ssh";
|
||||
import { formatSshFailureHint, formatSshRuntimeTimingHint, parseSshInvocation, sshFailureHint, sshRoutePayloadCwd, sshRuntimeTimingHint, wrapSshRemoteCommand } from "./ssh";
|
||||
import { codexJudgeQueryAsync, codexOutputQueryAsync, codexPrPreflightQueryAsync, codexQueuesQueryAsync, codexTaskQueryAsync, codexTasksQueryAsync, codexUnreadTriageAsync } from "./code-queue";
|
||||
import { runDecisionCenterCommandAsync } from "./decision-center";
|
||||
import {
|
||||
@@ -901,7 +901,7 @@ async function runRemoteSshWebSocket(
|
||||
const payload = {
|
||||
providerId: invocation.providerId,
|
||||
command: wrapSshRemoteCommand(parsed.remoteCommand, parsed.requiredHelpers),
|
||||
cwd: invocation.route.plane === "host" ? invocation.route.workspace ?? undefined : undefined,
|
||||
cwd: sshRoutePayloadCwd(invocation.route),
|
||||
tty: parsed.remoteCommand === null,
|
||||
stdinEotOnEnd: parsed.remoteCommand !== null,
|
||||
openTimeoutMs,
|
||||
@@ -1048,7 +1048,7 @@ export function remoteSshFrontendPlanForTest(target: string, args: string[]): Re
|
||||
wrappedRemoteCommand: wrapSshRemoteCommand(invocation.parsed.remoteCommand, invocation.parsed.requiredHelpers),
|
||||
requiresStdin: invocation.parsed.requiresStdin,
|
||||
invocationKind: invocation.parsed.invocationKind,
|
||||
payloadCwd: invocation.route.plane === "host" ? invocation.route.workspace : null,
|
||||
payloadCwd: sshRoutePayloadCwd(invocation.route) ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+98
-2
@@ -15,7 +15,7 @@ export type SshHelperName = "apply_patch" | "glob" | "skill-discover";
|
||||
|
||||
export interface ParsedSshRoute {
|
||||
providerId: string;
|
||||
plane: "host" | "k3s";
|
||||
plane: "host" | "k3s" | "win";
|
||||
entry: string | null;
|
||||
namespace: string | null;
|
||||
resource: string | null;
|
||||
@@ -59,6 +59,9 @@ export interface SshRuntimeTimingHint {
|
||||
|
||||
const argvQuotedSshSubcommands = new Set(["git", "rg", "grep", "sed", "nl", "stat", "du", "ls", "cat", "head", "tail", "wc", "pwd"]);
|
||||
const nativeK3sKubeconfig = "/etc/rancher/k3s/k3s.yaml";
|
||||
const windowsBridgeCwd = "/mnt/c/Windows";
|
||||
const windowsPowerShellExePath = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe";
|
||||
const windowsCmdExeNativePath = "C:\\Windows\\System32\\cmd.exe";
|
||||
const defaultSshSlowWarningMs = 10_000;
|
||||
const k3sResourceKindAliases = new Set(["pod", "po", "pods", "deployment", "deploy", "deployments", "statefulset", "sts", "daemonset", "ds", "job", "jobs"]);
|
||||
const legacyK3sOperationRouteSegments = new Set([
|
||||
@@ -859,6 +862,9 @@ export function parseSshInvocation(target: string, args: string[]): ParsedSshInv
|
||||
if (route.plane === "k3s") {
|
||||
return { providerId: route.providerId, route, parsed: parseK3sRouteArgs(route, args) };
|
||||
}
|
||||
if (route.plane === "win") {
|
||||
return { providerId: route.providerId, route, parsed: parseWinRouteArgs(route, args) };
|
||||
}
|
||||
if ((args[0] ?? "") === "k3s") {
|
||||
throw new Error(`ssh k3s shorthand is unsupported; use route syntax instead: ssh ${route.providerId}:k3s ${args.slice(1).join(" ")}`.trim());
|
||||
}
|
||||
@@ -880,6 +886,15 @@ export function parseSshRoute(target: string): ParsedSshRoute {
|
||||
if (tail.startsWith("/")) {
|
||||
return hostSshRoute(providerId, target, tail);
|
||||
}
|
||||
if (tail === "win32" || tail.startsWith("win32/") || tail.startsWith("win32:")) {
|
||||
throw new Error(`unsupported ssh route plane: win32; use ${providerId}:win or ${providerId}:win/c/path`);
|
||||
}
|
||||
if (tail === "win" || tail.startsWith("win/")) {
|
||||
return winSshRoute(providerId, target, parseWinRouteWorkspace(providerId, tail));
|
||||
}
|
||||
if (tail.startsWith("win:")) {
|
||||
throw new Error(`ssh win workspace route uses slash syntax, for example: ssh ${providerId}:win/c/test cmd cd`);
|
||||
}
|
||||
const [plane, ...rest] = tail.split(":");
|
||||
if (plane === undefined || plane.length === 0 || plane === "host") {
|
||||
const workspace = rest.length > 0 ? rest.join(":") : null;
|
||||
@@ -908,6 +923,87 @@ function hostSshRoute(providerId: string, raw: string, workspace: string | null)
|
||||
return { providerId, plane: "host", entry: null, namespace: null, resource: null, container: null, workspace, raw };
|
||||
}
|
||||
|
||||
function winSshRoute(providerId: string, raw: string, workspace: string | null): ParsedSshRoute {
|
||||
return { providerId, plane: "win", entry: null, namespace: null, resource: null, container: null, workspace, raw };
|
||||
}
|
||||
|
||||
function parseWinRouteWorkspace(providerId: string, tail: string): string | null {
|
||||
if (tail === "win") return null;
|
||||
const suffix = tail.slice("win/".length);
|
||||
const slashIndex = suffix.indexOf("/");
|
||||
const drive = slashIndex < 0 ? suffix : suffix.slice(0, slashIndex);
|
||||
if (!/^[A-Za-z]$/u.test(drive)) {
|
||||
throw new Error(`ssh win workspace route requires a drive letter, for example: ssh ${providerId}:win/c/test cmd cd`);
|
||||
}
|
||||
const rest = slashIndex < 0 ? "" : suffix.slice(slashIndex + 1);
|
||||
const segments = rest.split("/").filter((segment) => segment.length > 0);
|
||||
return `${drive.toUpperCase()}:\\${segments.join("\\")}`;
|
||||
}
|
||||
|
||||
function parseWinRouteArgs(route: ParsedSshRoute, args: string[]): ParsedSshArgs {
|
||||
const operation = args[0] ?? "";
|
||||
if (operation.length === 0) {
|
||||
throw new Error(`ssh ${route.raw} requires a Windows operation, for example: ssh ${route.providerId}:win cmd ver`);
|
||||
}
|
||||
if (operation !== "cmd" && operation !== "cmd.exe") {
|
||||
throw new Error(`unsupported ssh win operation: ${operation}; use ssh ${route.providerId}:win cmd <command-line>`);
|
||||
}
|
||||
const commandArgs = args[1] === "--" ? args.slice(2) : args.slice(1);
|
||||
if (commandArgs.length === 0) throw new Error(`ssh ${route.raw} cmd requires a command line, for example: ssh ${route.providerId}:win cmd ver`);
|
||||
return {
|
||||
remoteCommand: shellArgv([
|
||||
windowsPowerShellExePath,
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-EncodedCommand",
|
||||
buildWindowsPowerShellEncodedCommand(buildWindowsCmdLine(commandArgs.join(" "), route.workspace)),
|
||||
]),
|
||||
requiresStdin: false,
|
||||
invocationKind: "argv",
|
||||
};
|
||||
}
|
||||
|
||||
function buildWindowsCmdLine(userCommand: string, cwd: string | null): string {
|
||||
const parts = [
|
||||
"chcp 65001>nul",
|
||||
"set PYTHONUTF8=1",
|
||||
"set PYTHONIOENCODING=utf-8",
|
||||
];
|
||||
if (cwd !== null) parts.push(`cd /d ${windowsCmdQuote(cwd)}`);
|
||||
parts.push(userCommand);
|
||||
return parts.join(" && ");
|
||||
}
|
||||
|
||||
function windowsCmdQuote(value: string): string {
|
||||
if (/[\r\n"]/u.test(value)) throw new Error("ssh win workspace path must not contain quotes or newlines");
|
||||
return `"${value}"`;
|
||||
}
|
||||
|
||||
function powerShellSingleQuote(value: string): string {
|
||||
return `'${value.replace(/'/g, "''")}'`;
|
||||
}
|
||||
|
||||
function buildWindowsPowerShellEncodedCommand(cmdLine: string): string {
|
||||
const script = [
|
||||
"$ErrorActionPreference = 'Stop';",
|
||||
"[Console]::InputEncoding = [System.Text.UTF8Encoding]::new();",
|
||||
"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new();",
|
||||
"$OutputEncoding = [System.Text.UTF8Encoding]::new();",
|
||||
"$env:PYTHONUTF8 = '1';",
|
||||
"$env:PYTHONIOENCODING = 'utf-8';",
|
||||
`& ${powerShellSingleQuote(windowsCmdExeNativePath)} /d /s /c ${powerShellSingleQuote(cmdLine)};`,
|
||||
"exit $LASTEXITCODE;",
|
||||
].join(" ");
|
||||
return Buffer.from(script, "utf16le").toString("base64");
|
||||
}
|
||||
|
||||
export function sshRoutePayloadCwd(route: ParsedSshRoute): string | undefined {
|
||||
if (route.plane === "host") return route.workspace ?? undefined;
|
||||
if (route.plane === "win") return windowsBridgeCwd;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function routeSegmentHead(segment: string): string {
|
||||
return segment.split("/")[0] ?? segment;
|
||||
}
|
||||
@@ -1806,7 +1902,7 @@ export async function runSsh(config: UniDeskConfig, providerId: string, args: st
|
||||
const payload = {
|
||||
providerId: invocation.providerId,
|
||||
command: wrapSshRemoteCommand(parsed.remoteCommand, parsed.requiredHelpers),
|
||||
cwd: invocation.route.plane === "host" ? invocation.route.workspace ?? undefined : undefined,
|
||||
cwd: sshRoutePayloadCwd(invocation.route),
|
||||
tty: parsed.remoteCommand === null,
|
||||
stdinEotOnEnd: parsed.remoteCommand !== null,
|
||||
openTimeoutMs,
|
||||
|
||||
@@ -24,6 +24,13 @@ function assertThrows(fn: () => unknown, pattern: RegExp, message: string): void
|
||||
throw new Error(`${message}: expected throw`);
|
||||
}
|
||||
|
||||
function decodeWinEncodedCommand(remoteCommand: string | null | undefined): string {
|
||||
const text = String(remoteCommand ?? "");
|
||||
const match = /'-EncodedCommand' '([^']+)'/u.exec(text);
|
||||
assertCondition(match !== null, "win command must use PowerShell -EncodedCommand", remoteCommand);
|
||||
return Buffer.from(match[1] ?? "", "base64").toString("utf16le");
|
||||
}
|
||||
|
||||
function applyPatchFixture(args: string[], patch: string, files: Record<string, string>): { status: number | null; stdout: string; stderr: string; files: Record<string, string> } {
|
||||
const root = mkdtempSync(path.join(os.tmpdir(), "unidesk-apply-patch-contract-"));
|
||||
try {
|
||||
@@ -123,6 +130,22 @@ export function runSshArgvGuidanceContract(): JsonRecord {
|
||||
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 winCmd = parseSshInvocation("D601:win", ["cmd", "ver"]);
|
||||
assertCondition(winCmd.route.plane === "win" && winCmd.route.workspace === null, "win route must parse as the Windows cmd plane", winCmd);
|
||||
const winCmdScript = decodeWinEncodedCommand(winCmd.parsed.remoteCommand);
|
||||
assertCondition(String(winCmd.parsed.remoteCommand).startsWith("'/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'") && winCmdScript.includes("C:\\Windows\\System32\\cmd.exe") && winCmdScript.includes("chcp 65001>nul") && winCmdScript.includes("PYTHONIOENCODING"), "win route must execute cmd.exe through a UTF-8 Windows launcher", { winCmd, winCmdScript });
|
||||
|
||||
const winCmdCwd = parseSshInvocation("D601:win/c/test", ["cmd", "echo", "中文"]);
|
||||
assertCondition(winCmdCwd.route.plane === "win" && winCmdCwd.route.workspace === String.raw`C:\test`, "win route slash workspace must map to a Windows drive cwd", winCmdCwd);
|
||||
const winCmdCwdScript = decodeWinEncodedCommand(winCmdCwd.parsed.remoteCommand);
|
||||
assertCondition(winCmdCwdScript.includes('cd /d "C:\\test"') && winCmdCwdScript.includes("echo 中文"), "win route workspace must cd in Windows cmd before running the command", { winCmdCwd, winCmdCwdScript });
|
||||
|
||||
assertThrows(
|
||||
() => parseSshInvocation("D601:win32", ["cmd", "ver"]),
|
||||
/use D601:win/u,
|
||||
"win32 route spelling must be rejected in favor of win",
|
||||
);
|
||||
|
||||
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);
|
||||
@@ -376,6 +399,8 @@ export function runSshArgvGuidanceContract(): JsonRecord {
|
||||
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:win cmd ver") && helpText.includes("ssh D601:win/c/test cmd cd"), "ssh help must document Windows cmd win routes", helpText);
|
||||
assertCondition(helpText.includes("Use `win`, not `win32`") && helpText.includes("chcp 65001") && helpText.includes("PYTHONIOENCODING=utf-8"), "ssh help must document win route UTF-8 defaults and naming", 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);
|
||||
@@ -409,10 +434,16 @@ export function runSshArgvGuidanceContract(): JsonRecord {
|
||||
assertCondition(frontendRemoteWorkspacePlan.payloadCwd === "/home/ubuntu/workspace/hwlab-dev", "remote frontend host workspace route must pass cwd to host.ssh payload", frontendRemoteWorkspacePlan);
|
||||
assertCondition(frontendRemoteWorkspacePlan.remoteCommand === "'git' 'status' '--short'", "remote frontend host workspace route must keep command argv-quoted", frontendRemoteWorkspacePlan);
|
||||
|
||||
const frontendRemoteWinPlan = remoteSshFrontendPlanForTest("D601:win/c/test", ["cmd", "cd"]);
|
||||
assertCondition(frontendRemoteWinPlan.providerId === "D601" && frontendRemoteWinPlan.payloadCwd === "/mnt/c/Windows", "remote frontend win route must dispatch through provider host.ssh from a Windows-mounted cwd", frontendRemoteWinPlan);
|
||||
const frontendRemoteWinScript = decodeWinEncodedCommand(String(frontendRemoteWinPlan.remoteCommand));
|
||||
assertCondition(frontendRemoteWinScript.includes("cmd.exe") && frontendRemoteWinScript.includes("cd /d \"C:\\test\""), "remote frontend win route must assemble Windows cmd cwd internally", { frontendRemoteWinPlan, frontendRemoteWinScript });
|
||||
|
||||
const tranScript = readFileSync(new URL("./tran", import.meta.url), "utf8");
|
||||
assertCondition(tranScript.includes("CODE_QUEUE_DEV_CONTAINER_MASTER_HOST") && tranScript.includes("--main-server-ip"), "tran wrapper must auto-select frontend transport inside Code Queue runner pods", tranScript);
|
||||
assertCondition(tranScript.includes("UNIDESK_TRAN_LOCAL"), "tran wrapper must keep an explicit local override for diagnostics", tranScript);
|
||||
assertCondition(tranScript.includes("tran_lock_scope") && tranScript.includes("UNIDESK_TRAN_LOCK_DIR"), "tran wrapper must serialize concurrent provider session opens with a local sh lock", tranScript);
|
||||
assertCondition(tranScript.includes("*:win|*:win/*) plane=win"), "tran wrapper must lock win route calls separately from host/k3s calls", tranScript);
|
||||
const tranLock = tranConcurrentLockFixture();
|
||||
assertCondition(tranLock.status === 0, "tran lock fixture shell should complete", tranLock);
|
||||
assertCondition(tranLock.stdout.trim() === "0 0", "parallel tran invocations for one provider must serialize instead of overlapping fake bun", tranLock);
|
||||
@@ -450,6 +481,7 @@ export function runSshArgvGuidanceContract(): JsonRecord {
|
||||
"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",
|
||||
"win route runs Windows cmd.exe with UTF-8 defaults and slash cwd syntax such as D601:win/c/test",
|
||||
"top-level remote option parsing preserves command-local -- separators for script -- sed -n style commands",
|
||||
"ssh-like timeout/kex failures emit one structured argv retry hint",
|
||||
"ssh runtime emits structured timing for slow operations over 10 seconds, including successful slow calls",
|
||||
|
||||
@@ -26,6 +26,7 @@ tran_lock_scope() {
|
||||
[ -n "$provider" ] || return 1
|
||||
plane=host
|
||||
case "$route" in
|
||||
*:win|*:win/*) plane=win ;;
|
||||
*:k3s*) plane=k3s ;;
|
||||
esac
|
||||
printf '%s\n' "$provider-$plane"
|
||||
|
||||
Reference in New Issue
Block a user