docs: standardize trans passthrough alias
This commit is contained in:
@@ -58,10 +58,10 @@ export function runProviderRunnerTriageContract(): JsonRecord {
|
||||
assertCondition(result.decision === "retryable-transient", "single path provider offline should be retryable transient", result);
|
||||
assertCondition(result.retryable === true, "single path provider offline should be retryable", result);
|
||||
assertCondition(result.contract.singlePathProviderOfflineIsGlobalBlocker === false, "triage contract should reject single-path global blocker", result);
|
||||
assertCondition(result.recommendedCrossChecks.includes("bun scripts/cli.ts ssh D601 argv true"), "provider triage result must recommend argv Host SSH cross-check", result.recommendedCrossChecks);
|
||||
assertCondition(result.recommendedCrossChecks.includes("trans D601 argv true"), "provider triage result must recommend argv Host SSH cross-check", result.recommendedCrossChecks);
|
||||
|
||||
const crossChecks = providerTriageRecommendedCrossChecks("D601");
|
||||
assertCondition(crossChecks.includes("bun scripts/cli.ts ssh D601 argv true"), "provider triage recommendedCrossChecks must keep ssh argv true", crossChecks);
|
||||
assertCondition(crossChecks.includes("trans D601 argv true"), "provider triage recommendedCrossChecks must keep ssh argv true", crossChecks);
|
||||
assertCondition(crossChecks.includes("bun scripts/cli.ts debug dispatch D601 host.ssh --wait-ms 15000"), "provider triage recommendedCrossChecks must keep host.ssh dispatch probe", crossChecks);
|
||||
|
||||
const rateLimitTriage = buildProviderTriageResult("D601", [
|
||||
|
||||
@@ -320,7 +320,7 @@ async function triggerCurrent(config: UniDeskConfig, options: TriggerOptions): P
|
||||
created: compactCapture(created),
|
||||
next: {
|
||||
status: "bun scripts/cli.ts agentrun v01 control-plane status",
|
||||
logs: `bun scripts/cli.ts ssh G14:k3s logs -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} --tail 120`,
|
||||
logs: `trans G14:k3s logs -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} --tail 120`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,11 +104,11 @@ export function isApplyPatchV2HelpArgs(args: string[] = []): boolean {
|
||||
export function applyPatchV2HelpPayload() {
|
||||
return {
|
||||
ok: true,
|
||||
command: "ssh <route> apply-patch < patch.diff",
|
||||
command: "trans <route> apply-patch < patch.diff",
|
||||
summary: "Apply a standard apply_patch patch to a remote route with the local v2 line-based engine.",
|
||||
usage: [
|
||||
"tran G14:/root/hwlab/.worktree/task apply-patch < patch.diff",
|
||||
"bun scripts/cli.ts ssh D601:/tmp apply-patch < patch.diff"
|
||||
"trans G14:/root/hwlab/.worktree/task apply-patch < patch.diff",
|
||||
"trans D601:/tmp apply-patch < patch.diff"
|
||||
],
|
||||
input: {
|
||||
required: true,
|
||||
|
||||
+52
-52
@@ -22,19 +22,19 @@ export function rootHelp(): unknown {
|
||||
{ command: "gc plan|run|db-trace|policy|remote [--confirm] [--logs-keep-days N] [--include-browser-cache]", description: "One-time main-server or remote provider disk relief and low-risk anti-bloat policy for logs, journald, allowlisted /tmp artifacts, scoped core dumps and explicit trace telemetry retention; plan is read-only and run requires --confirm." },
|
||||
{ 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` or `D601:win/c/test` only locates distributed targets." },
|
||||
{ command: "ssh gh:/owner/repo[/pr|/issue][/number[/1]] ls|cat|rg|patch-apply", description: "Treat GitHub PRs/issues as virtual text directories; `ls --full` shows state/floors/body length, and `patch-apply` updates first-floor `body.md` through UniDesk gh plus apply-patch v2." },
|
||||
{ command: "ssh <route> apply-patch < patch.diff", description: "Default remote text patch entry: apply a standard patch with the local TypeScript v2 engine while the remote route only reads and writes files." },
|
||||
{ command: "ssh <route> upload <local-file> <remote-file> | ssh <route> download <remote-file> <local-file>", description: "Transfer whole files through SSH passthrough with remote temp files, automatic endpoint byte/SHA-256 verification, and client-side chunk fallback." },
|
||||
{ command: "ssh <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: "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; default sh inherits provider proxy env and gets the portable printf helper used by shell/script." },
|
||||
{ command: "ssh <providerId> skills [--scope all|wsl|windows] [--limit N]", description: "Discover WSL/Linux and, for WSL providers, Windows skill directories in one SSH passthrough call." },
|
||||
{ command: "ssh <providerId> find <path...> [--max-depth N] [--type d|f|l] [--contains TEXT] [--iname PATTERN] [--limit N] [--sort]", description: "Run a structured remote find command without nested shell quoting or parentheses." },
|
||||
{ command: "ssh <providerId> glob [--root DIR] [--pattern PATTERN] [--contains TEXT] [--type any|f|d] [--limit N] [--sort]", description: "Run remote glob matching through the injected helper without shell glob expansion." },
|
||||
{ command: "ssh <providerId>:/absolute/workspace <operation args...>", description: "Route directly into a host workspace while keeping the operation parser independent from the location." },
|
||||
{ command: "ssh <providerId>:k3s[:namespace:workload[:container]] <kubectl|logs|exec|script|apply-patch|apply-patch-v1|command> ...", description: "Locate a native k3s control plane or workload with route syntax, then run a separate operation with KUBECONFIG fixed and argv assembled by the CLI." },
|
||||
{ command: "ssh <providerId> argv <command> [args...]", description: "Run a non-interactive remote command with each argv token shell-quoted by UniDesk before SSH passthrough; use `ssh <providerId> script` when shell features are required." },
|
||||
{ command: "trans <route> [operation args...] (alias of ssh <route> ...)", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge; `trans` is the short alias for this ssh passthrough path." },
|
||||
{ command: "trans gh:/owner/repo[/pr|/issue][/number[/1]] ls|cat|rg|patch-apply", description: "Treat GitHub PRs/issues as virtual text directories; `ls --full` shows state/floors/body length, and `patch-apply` updates first-floor `body.md` through UniDesk gh plus apply-patch v2." },
|
||||
{ command: "trans <route> apply-patch < patch.diff", description: "Default remote text patch entry: apply a standard patch with the local TypeScript v2 engine while the remote route only reads and writes files." },
|
||||
{ command: "trans <route> upload <local-file> <remote-file> | trans <route> download <remote-file> <local-file>", description: "Transfer whole files through SSH passthrough with remote temp files, automatic endpoint byte/SHA-256 verification, and client-side chunk fallback." },
|
||||
{ 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> 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> script [--shell sh|bash] [script-args...] <<'SCRIPT' ...", description: "Run a remote shell script from local stdin using shell -s; default sh inherits provider proxy env and gets the portable printf helper used by shell/script." },
|
||||
{ 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." },
|
||||
{ command: "trans <providerId> find <path...> [--max-depth N] [--type d|f|l] [--contains TEXT] [--iname PATTERN] [--limit N] [--sort]", description: "Run a structured remote find command without nested shell quoting or parentheses." },
|
||||
{ command: "trans <providerId> glob [--root DIR] [--pattern PATTERN] [--contains TEXT] [--type any|f|d] [--limit N] [--sort]", description: "Run remote glob matching through the injected helper without shell glob expansion." },
|
||||
{ command: "trans <providerId>:/absolute/workspace <operation args...>", description: "Route directly into a host workspace while keeping the operation parser independent from the location." },
|
||||
{ command: "trans <providerId>:k3s[:namespace:workload[:container]] <kubectl|logs|exec|script|apply-patch|apply-patch-v1|command> ...", description: "Locate a native k3s control plane or workload with route syntax, then run a separate operation with KUBECONFIG fixed and argv assembled by the CLI." },
|
||||
{ command: "trans <providerId> argv <command> [args...]", description: "Run a non-interactive remote command with each argv token shell-quoted by UniDesk before SSH passthrough; use `trans <providerId> script` when shell features are required." },
|
||||
{ command: "microservice list", description: "List UniDesk-managed user services and their provider/runtime mapping." },
|
||||
{ command: "microservice status <id>", description: "Show one user service config, repository reference, backend mapping, and runtime status." },
|
||||
{ command: "microservice health <id> [--compact|--raw|--full]", description: "Probe one user service through backend-core -> provider-gateway HTTP proxy; default output is compact, and code-queue uses a commander-safe liveness summary unless raw/full is requested." },
|
||||
@@ -155,44 +155,44 @@ export function sshHelp(): unknown {
|
||||
output: "json",
|
||||
description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge.",
|
||||
usage: [
|
||||
"bun scripts/cli.ts ssh <route>",
|
||||
"bun scripts/cli.ts ssh <providerId> argv <command> [args...]",
|
||||
"bun scripts/cli.ts ssh <providerId>:/absolute/workspace apply-patch < patch.diff",
|
||||
"bun scripts/cli.ts ssh <route> upload <local-file> <remote-file>",
|
||||
"bun scripts/cli.ts ssh <route> download <remote-file> <local-file>",
|
||||
"bun scripts/cli.ts ssh <providerId> apply-patch-v1 [--allow-loose] < patch.diff",
|
||||
"bun scripts/cli.ts ssh <providerId> py [script-args...] < script.py",
|
||||
"bun scripts/cli.ts ssh <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT'",
|
||||
"bun scripts/cli.ts ssh <providerId> shell [--shell sh|bash] \"sed -n '1,20p' a && sed -n '1,20p' b\"",
|
||||
"bun scripts/cli.ts ssh <providerId> skills [--scope all|wsl|windows] [--limit N]",
|
||||
"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:win skills [--scope agents|codex|all] [--limit N]",
|
||||
"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",
|
||||
"bun scripts/cli.ts ssh G14:k3s kubectl get pipelineruns -n hwlab-ci",
|
||||
"bun scripts/cli.ts ssh D601:k3s script <<'SCRIPT'",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api/app pwd",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api/app apply-patch <<'PATCH'",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api apply-patch-v1 <<'PATCH'",
|
||||
"tar -C /path/to/files -cf - . | bun scripts/cli.ts ssh D601:k3s:unidesk:code-queue/root/unidesk exec --stdin -- tar -xf - -C /root/unidesk",
|
||||
"bun scripts/cli.ts ssh D601:win/c/test apply-patch <<'PATCH'",
|
||||
"bun scripts/cli.ts ssh D601:win upload ./tool.mjs F:\\Work\\hwlab\\.tmp\\tool.mjs",
|
||||
"bun scripts/cli.ts ssh D601:win download F:\\Work\\hwlab\\.tmp\\tool.mjs ./tool.mjs",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api node -e 'console.log(process.version)'",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api script <<'SCRIPT'",
|
||||
"bun scripts/cli.ts ssh D601:k3s:hwlab-dev:hwlab-cloud-api logs --tail 80",
|
||||
"bun scripts/cli.ts ssh G14:k3s logs -n agentrun-ci -l tekton.dev/pipelineRun=<run> --tail 120",
|
||||
"trans <route>",
|
||||
"trans <providerId> argv <command> [args...]",
|
||||
"trans <providerId>:/absolute/workspace apply-patch < patch.diff",
|
||||
"trans <route> upload <local-file> <remote-file>",
|
||||
"trans <route> download <remote-file> <local-file>",
|
||||
"trans <providerId> apply-patch-v1 [--allow-loose] < patch.diff",
|
||||
"trans <providerId> py [script-args...] < script.py",
|
||||
"trans <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT'",
|
||||
"trans <providerId> shell [--shell sh|bash] \"sed -n '1,20p' a && sed -n '1,20p' b\"",
|
||||
"trans <providerId> skills [--scope all|wsl|windows] [--limit N]",
|
||||
"trans <providerId> find <path...> [--contains TEXT] [--limit N]",
|
||||
"trans <providerId> glob [--root DIR] [--pattern PATTERN]",
|
||||
"trans D601:/home/ubuntu/workspace/hwlab-dev git status --short --branch",
|
||||
"trans D601:win cmd ver",
|
||||
"trans D601:win/c/test cmd cd",
|
||||
"trans D601:win skills [--scope agents|codex|all] [--limit N]",
|
||||
"trans D601:k3s",
|
||||
"trans D601:k3s kubectl get pods -n hwlab-dev",
|
||||
"trans G14:k3s",
|
||||
"trans G14:k3s kubectl get pipelineruns -n hwlab-ci",
|
||||
"trans D601:k3s script <<'SCRIPT'",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api/app pwd",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api/app apply-patch <<'PATCH'",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api apply-patch-v1 <<'PATCH'",
|
||||
"tar -C /path/to/files -cf - . | trans D601:k3s:unidesk:code-queue/root/unidesk exec --stdin -- tar -xf - -C /root/unidesk",
|
||||
"trans D601:win/c/test apply-patch <<'PATCH'",
|
||||
"trans D601:win upload ./tool.mjs F:\\Work\\hwlab\\.tmp\\tool.mjs",
|
||||
"trans D601:win download F:\\Work\\hwlab\\.tmp\\tool.mjs ./tool.mjs",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api node -e 'console.log(process.version)'",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api script <<'SCRIPT'",
|
||||
"trans D601:k3s:hwlab-dev:hwlab-cloud-api logs --tail 80",
|
||||
"trans G14:k3s logs -n agentrun-ci -l tekton.dev/pipelineRun=<run> --tail 120",
|
||||
],
|
||||
notes: [
|
||||
"ssh --help and ssh <route> --help print this JSON help and never open an interactive session.",
|
||||
"trans --help and trans <route> --help print this JSON help and never open an interactive session; the underlying ssh subcommand keeps the same help behavior.",
|
||||
"For non-interactive remote commands, prefer argv for a single process and script/stdin for shell logic.",
|
||||
"For one-line remote shell logic without a heredoc, use `script -- '<command && command>'`; outer shell operators written outside tran, such as `tran G14:/repo sed ... && sed ...`, are parsed by the local shell before UniDesk starts and therefore cannot be redirected by the CLI. The explicit `shell '<command>'` operation remains available for the same sh -c path.",
|
||||
"When a one-line shell command is easier to type through the script path, `script -- '<command && command>'` runs that single string through the remote shell without waiting for stdin. When `script --` is followed by multiple tokens, it stays a direct argv form for commands such as `tran D601:/work script -- sed -n '1,20p' file`.",
|
||||
"For one-line remote shell logic without a heredoc, use `script -- '<command && command>'`; outer shell operators written outside trans, such as `trans G14:/repo sed ... && sed ...`, are parsed by the local shell before UniDesk starts and therefore cannot be redirected by the CLI. The explicit `shell '<command>'` operation remains available for the same sh -c path.",
|
||||
"When a one-line shell command is easier to type through the script path, `script -- '<command && command>'` runs that single string through the remote shell without waiting for stdin. When `script --` is followed by multiple tokens, it stays a direct argv form for commands such as `trans D601:/work script -- sed -n '1,20p' file`.",
|
||||
"script and shell helper modes inject a tiny POSIX-compatible printf wrapper before user shell text, so portable printf headings such as `printf \"--- section ---\\n\"` work consistently under dash/sh and bash. Direct argv commands are unchanged.",
|
||||
"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` is the default remote text patch entry and uses the v2 local line-based patch engine with remote read/write operations, including Windows routes such as `D601:win/c/test`, so long Unicode/Chinese lines and pure insertion hunks avoid the legacy remote shell hunk parser. Its stdout/stderr follows Codex apply_patch text output rather than UniDesk JSON output; on multi-file failure, stderr lists applied hunks before the first failed hunk and the failed hunk, then stops like Codex apply_patch.",
|
||||
@@ -203,11 +203,11 @@ export function sshHelp(): unknown {
|
||||
"Use `win`, not `win32`; the win route sets chcp 65001, PYTHONUTF8=1, and PYTHONIOENCODING=utf-8 before running the requested cmd command line.",
|
||||
"`<provider>:win skills` discovers the current Windows user's `%USERPROFILE%\\.agents\\skills` by default; use `--scope all` to include `%USERPROFILE%\\.codex\\skills`.",
|
||||
"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.",
|
||||
"Do not use post-provider shorthand such as `trans G14 k3s ...`; write `trans 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.",
|
||||
"Non-interactive ssh/tran operations have a hard top-level runtime timeout capped at 60s. Timeout writes UNIDESK_SSH_RUNTIME_TIMEOUT or UNIDESK_TRAN_TIMEOUT_HINT and disconnects the broker; long CI/CD, trace, logs, build, or hardware work must use submit-and-poll / short query loops instead of keeping tran open.",
|
||||
"Only slow ssh/tran runtime writes UNIDESK_SSH_TIMING JSON to stderr; operations over 10s are marked level=warning even when they succeed, because slow successful calls are a distributed performance monitoring signal. Check provider latency, remote command cost, helper bootstrap, or remote patch optimization before repeating high-frequency work. Routine short calls do not emit timing noise.",
|
||||
"The local tran wrapper must not add provider/plane directory locks; rely on k8s/Tekton/Argo/Lease or server-side TTL queues for coordination.",
|
||||
"Non-interactive ssh/trans/tran operations have a hard top-level runtime timeout capped at 60s. Timeout writes UNIDESK_SSH_RUNTIME_TIMEOUT or UNIDESK_TRAN_TIMEOUT_HINT and disconnects the broker; long CI/CD, trace, logs, build, or hardware work must use submit-and-poll / short query loops instead of keeping trans open.",
|
||||
"Only slow ssh/trans/tran runtime writes UNIDESK_SSH_TIMING JSON to stderr; operations over 10s are marked level=warning even when they succeed, because slow successful calls are a distributed performance monitoring signal. Check provider latency, remote command cost, helper bootstrap, or remote patch optimization before repeating high-frequency work. Routine short calls do not emit timing noise.",
|
||||
"The local trans/tran wrapper must not add provider/plane directory locks; rely on k8s/Tekton/Argo/Lease or server-side TTL queues for coordination.",
|
||||
"Use -- before a remote command that intentionally starts with a dash.",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1709,8 +1709,8 @@ function runControlPlaneReleasedPvCleanup(options: G14ControlPlaneOptions): Reco
|
||||
candidatesBefore: candidates,
|
||||
deletion,
|
||||
followUp: {
|
||||
diskPressure: "bun scripts/cli.ts ssh G14:k3s kubectl get node ubuntu-rog-zephyrus-g14-ga401iv-ga401iv -o jsonpath='{range .status.conditions[*]}{.type}{\"=\"}{.status}{\" \"}{.reason}{\"\\n\"}{end}'",
|
||||
storage: "bun scripts/cli.ts ssh G14 script -- 'df -h /; sudo du -xh -d 1 /var/lib/rancher/k3s/storage 2>/dev/null | sort -h | tail -20'",
|
||||
diskPressure: "trans G14:k3s kubectl get node ubuntu-rog-zephyrus-g14-ga401iv-ga401iv -o jsonpath='{range .status.conditions[*]}{.type}{\"=\"}{.status}{\" \"}{.reason}{\"\\n\"}{end}'",
|
||||
storage: "trans G14 script -- 'df -h /; sudo du -xh -d 1 /var/lib/rancher/k3s/storage 2>/dev/null | sort -h | tail -20'",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1803,7 +1803,7 @@ function runControlPlaneCleanup(options: G14ControlPlaneOptions): Record<string,
|
||||
deletion,
|
||||
followUp: {
|
||||
status: "bun scripts/cli.ts hwlab g14 control-plane status --lane v02",
|
||||
diskPressure: "bun scripts/cli.ts ssh G14:k3s kubectl get node ubuntu-rog-zephyrus-g14-ga401iv-ga401iv -o jsonpath='{.spec.taints}{\"\\n\"}{range .status.conditions[*]}{.type}{\"=\"}{.status}{\" \"}{.reason}{\"\\n\"}{end}'",
|
||||
diskPressure: "trans G14:k3s kubectl get node ubuntu-rog-zephyrus-g14-ga401iv-ga401iv -o jsonpath='{.spec.taints}{\"\\n\"}{range .status.conditions[*]}{.type}{\"=\"}{.status}{\" \"}{.reason}{\"\\n\"}{end}'",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -3660,7 +3660,7 @@ function newDailyBriefBody(date: string): string {
|
||||
"## 常驻观察与长期建议",
|
||||
"",
|
||||
"- G14 HWLAB source truth 固定为 G14 `/root/hwlab` 与 `origin/G14`;DEV rollout 只接受 G14 Tekton/GitOps/Argo 和公网 health 证据。",
|
||||
"- GitHub issue/PR 写操作必须走 UniDesk `gh` 子命令;G14 k3s 操作必须走 UniDesk `ssh G14:k3s`。",
|
||||
"- GitHub issue/PR 写操作必须走 UniDesk `gh` 子命令;G14 k3s 操作必须走 UniDesk `trans G14:k3s`。",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ export function providerTriageRecommendedCrossChecks(providerId: string): string
|
||||
`${commandPrefix} provider triage ${providerId}`,
|
||||
`${commandPrefix} debug health`,
|
||||
`${commandPrefix} debug dispatch ${providerId} host.ssh --wait-ms 15000`,
|
||||
`${commandPrefix} ssh ${providerId} argv true`,
|
||||
`trans ${providerId} argv true`,
|
||||
`${commandPrefix} artifact-registry health --provider-id ${providerId}`,
|
||||
`${commandPrefix} microservice health k3sctl-adapter`,
|
||||
`${commandPrefix} microservice health code-queue`,
|
||||
|
||||
@@ -145,7 +145,7 @@ function parseSshFileTransferCliOptions(args: string[]): SshFileTransferCliOptio
|
||||
continue;
|
||||
}
|
||||
if (arg === "--help" || arg === "-h" || arg === "help") {
|
||||
throw new Error(`ssh ${action} usage: ssh <route> ${action} ${action === "upload" ? "<local-file> <remote-file>" : "<remote-file> <local-file>"} [--chunk-bytes N]`);
|
||||
throw new Error(`ssh ${action} usage: trans <route> ${action} ${action === "upload" ? "<local-file> <remote-file>" : "<remote-file> <local-file>"} [--chunk-bytes N]`);
|
||||
}
|
||||
if (arg.startsWith("-")) throw new Error(`unsupported ssh ${action} option: ${arg}`);
|
||||
positionals.push(arg);
|
||||
|
||||
+5
-5
@@ -879,7 +879,7 @@ export function parseSshArgs(args: string[]): ParsedSshArgs {
|
||||
return { remoteCommand: shellArgv(["glob", ...args.slice(1)]), requiresStdin: false, invocationKind: "helper", requiredHelpers: ["glob"] };
|
||||
}
|
||||
if (subcommand === "k3s") {
|
||||
throw new Error("ssh k3s shorthand is unsupported; put k3s in the route, for example: ssh D601:k3s kubectl get nodes");
|
||||
throw new Error("ssh k3s shorthand is unsupported; put k3s in the route, for example: trans D601:k3s kubectl get nodes");
|
||||
}
|
||||
if (argvQuotedSshSubcommands.has(subcommand)) {
|
||||
return { remoteCommand: shellArgv(args), requiresStdin: false, invocationKind: "argv" };
|
||||
@@ -919,13 +919,13 @@ export function parseSshInvocation(target: string, args: string[]): ParsedSshInv
|
||||
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());
|
||||
throw new Error(`ssh k3s shorthand is unsupported; use route syntax instead: trans ${route.providerId}:k3s ${args.slice(1).join(" ")}`.trim());
|
||||
}
|
||||
return { providerId: route.providerId, route, parsed: parseSshArgs(args) };
|
||||
}
|
||||
|
||||
export function parseSshRoute(target: string): ParsedSshRoute {
|
||||
if (!target) throw new Error("ssh requires provider id, for example: bun scripts/cli.ts ssh D601");
|
||||
if (!target) throw new Error("ssh requires provider id, for example: trans D601");
|
||||
const firstColon = target.indexOf(":");
|
||||
if (firstColon < 0) {
|
||||
return hostSshRoute(target, target, null);
|
||||
@@ -946,7 +946,7 @@ export function parseSshRoute(target: string): ParsedSshRoute {
|
||||
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`);
|
||||
throw new Error(`ssh win workspace route uses slash syntax, for example: trans ${providerId}:win/c/test cmd cd`);
|
||||
}
|
||||
const [plane, ...rest] = tail.split(":");
|
||||
if (plane === undefined || plane.length === 0 || plane === "host") {
|
||||
@@ -2034,7 +2034,7 @@ export function sshFailureHint(providerId: string, parsed: ParsedSshArgs, exitCo
|
||||
trigger,
|
||||
exitCode,
|
||||
message: "ssh-like remote command failed before proving Host SSH is globally unavailable; prefer structured argv or stdin script passthrough for non-interactive commands.",
|
||||
try: `bun scripts/cli.ts ssh ${shownProviderId} script <<'SCRIPT'`,
|
||||
try: `trans ${shownProviderId} script <<'SCRIPT'`,
|
||||
triage: `bun scripts/cli.ts provider triage ${shownProviderId} --observed-scope ssh --observed-error '<ssh-like timeout or kex failure>'`,
|
||||
note: "This hint intentionally does not echo the original remote command.",
|
||||
};
|
||||
|
||||
@@ -548,7 +548,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
|
||||
assertThrows(
|
||||
() => parseSshInvocation("G14", ["k3s", "kubectl", "get", "nodes"]),
|
||||
/unsupported.*ssh G14:k3s/u,
|
||||
/unsupported.*trans G14:k3s/u,
|
||||
"k3s must be a route plane, not a post-provider shorthand",
|
||||
);
|
||||
|
||||
@@ -1075,7 +1075,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
|
||||
assertThrows(
|
||||
() => parseSshInvocation("D601:k3s:kubectl", ["get", "pods"]),
|
||||
/route must locate a target only.*ssh D601:k3s kubectl/u,
|
||||
/route must locate a target only.*trans D601:k3s kubectl/u,
|
||||
"operation names must not be accepted as k3s route segments",
|
||||
);
|
||||
assertThrows(
|
||||
@@ -1123,7 +1123,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
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?.try === "trans 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);
|
||||
@@ -1172,30 +1172,30 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
const help = sshHelp() as { notes?: unknown };
|
||||
const helpText = JSON.stringify(help);
|
||||
const helpNotes = Array.isArray(help.notes) ? help.notes.map(String).join("\n") : "";
|
||||
assertCondition(helpText.includes("ssh <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT'"), "ssh help must recommend stdin script passthrough for shell scripts", helpText);
|
||||
assertCondition(helpText.includes("ssh <providerId> shell [--shell sh|bash]") && helpText.includes("outer shell operators written outside tran"), "ssh help must document one-line shell passthrough and the local operator boundary", helpText);
|
||||
assertCondition(helpText.includes("trans <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT'"), "ssh help must recommend stdin script passthrough for shell scripts", helpText);
|
||||
assertCondition(helpText.includes("trans <providerId> shell [--shell sh|bash]") && helpText.includes("outer shell operators written outside trans"), "ssh help must document one-line shell passthrough and the local operator boundary", 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(helpNotes.includes("portable printf headings") && helpNotes.includes('printf "--- section ---\\n"'), "ssh help must document script/shell printf heading compatibility", helpNotes);
|
||||
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") && helpText.includes("ssh D601:win skills"), "ssh help must document Windows cmd and skills win routes", helpText);
|
||||
assertCondition(helpText.includes("trans D601:/home/ubuntu/workspace/hwlab-dev git status --short --branch"), "ssh help must document host workspace routes", helpText);
|
||||
assertCondition(helpText.includes("trans D601:win cmd ver") && helpText.includes("trans D601:win/c/test cmd cd") && helpText.includes("trans D601:win skills"), "ssh help must document Windows cmd and skills 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);
|
||||
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/app apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText);
|
||||
assertCondition(helpText.includes("ssh <route> upload <local-file> <remote-file>") && helpText.includes("ssh <route> download <remote-file> <local-file>"), "ssh help must document verified file transfer operations", helpText);
|
||||
assertCondition(helpText.includes("ssh D601:k3s:unidesk:code-queue/root/unidesk exec --stdin -- tar -xf - -C /root/unidesk"), "ssh help must document one-step stdin file streaming into pod exec", helpText);
|
||||
assertCondition(helpText.includes("trans D601:k3s kubectl get pods -n hwlab-dev"), "ssh help must document k3s kubectl operation", helpText);
|
||||
assertCondition(helpText.includes("trans G14:k3s kubectl get pipelineruns -n hwlab-ci"), "ssh help must document G14 k3s route operation", helpText);
|
||||
assertCondition(helpText.includes("trans D601:k3s:hwlab-dev:hwlab-cloud-api/app pwd"), "ssh help must document k3s pod workspace route", helpText);
|
||||
assertCondition(helpText.includes("trans D601:k3s script <<'SCRIPT'"), "ssh help must document k3s control-plane script operation", helpText);
|
||||
assertCondition(helpText.includes("trans D601:k3s:hwlab-dev:hwlab-cloud-api/app apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText);
|
||||
assertCondition(helpText.includes("trans <route> upload <local-file> <remote-file>") && helpText.includes("trans <route> download <remote-file> <local-file>"), "ssh help must document verified file transfer operations", helpText);
|
||||
assertCondition(helpText.includes("trans D601:k3s:unidesk:code-queue/root/unidesk exec --stdin -- tar -xf - -C /root/unidesk"), "ssh help must document one-step stdin file streaming into pod exec", helpText);
|
||||
assertCondition(helpText.includes("apply-patch-v1 [--allow-loose]") && helpText.includes("low-context update hunks"), "ssh help must document apply-patch-v1 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("trans 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);
|
||||
assertCondition(helpText.includes("UNIDESK_SSH_RUNTIME_TIMEOUT") && helpText.includes("UNIDESK_TRAN_TIMEOUT_HINT") && helpText.includes("60s") && helpText.includes("submit-and-poll"), "ssh help must document top-level runtime timeout and short polling discipline", helpText);
|
||||
assertCondition(helpText.includes("UNIDESK_SSH_TIMING") && helpText.includes("10s") && helpText.includes("slow successful calls are a distributed performance monitoring signal") && helpText.includes("Routine short calls do not emit timing noise"), "ssh help must document slow-only runtime timing hints", helpText);
|
||||
assertCondition(helpText.includes("must not add provider/plane directory locks") && helpText.includes("k8s/Tekton/Argo/Lease"), "ssh help must document tran's no-local-lock boundary", 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);
|
||||
assertCondition(crossChecks.includes("trans D601 argv true"), "provider triage cross-checks must keep argv true", crossChecks);
|
||||
|
||||
const frontendRemoteK3sPlan = remoteSshFrontendPlanForTest("D601:k3s", ["kubectl", "get", "nodes", "-o", "name"]);
|
||||
assertCondition(frontendRemoteK3sPlan.transport === "frontend-websocket", "remote frontend ssh must use the streaming websocket bridge", frontendRemoteK3sPlan);
|
||||
@@ -1280,7 +1280,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
"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",
|
||||
"help text documents stdin script passthrough and UNIDESK_SSH_HINT",
|
||||
"provider triage recommendedCrossChecks keeps ssh D601 argv true",
|
||||
"provider triage recommendedCrossChecks keeps trans D601 argv true",
|
||||
"remote frontend ssh uses the same structured route parser for host, k3s and pod argv routes",
|
||||
"ssh helper bootstrap is lazy so plain argv/script commands do not transfer helper sources",
|
||||
"host apply-patch-v1 bootstraps only the apply_patch helper and uses a Perl fast path for large files",
|
||||
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
repo=${UNIDESK_TRANS_REPO_ROOT:-/root/unidesk}
|
||||
if [ ! -f "$repo/scripts/cli.ts" ]; then
|
||||
self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
repo=$(CDPATH= cd -- "$self_dir/.." && pwd)
|
||||
fi
|
||||
|
||||
exec bun "$repo/scripts/cli.ts" ssh "$@"
|
||||
Reference in New Issue
Block a user