@xynogen/pix-ssh 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/lib.ts +28 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-ssh",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Pi tool — ssh_run: run remote commands over SSH with password/key auth and remote sudo",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "typebox": "^1.1.38",
41
- "@xynogen/pix-pretty": "^1.19.0",
41
+ "@xynogen/pix-pretty": "^1.25.0",
42
42
  "@xynogen/pix-runtime": "^0.8.0"
43
43
  },
44
44
  "peerDependencies": {
package/src/lib.ts CHANGED
@@ -569,29 +569,42 @@ export function runTransfer(
569
569
  recursive: boolean,
570
570
  opts: Pick<RunOptions, "controlPath" | "loginPassword" | "signal">,
571
571
  ): Promise<SshResult> {
572
- const scpArgs = [
573
- ...baseScpArgs(spec, opts.controlPath, recursive),
574
- "--",
575
- ...transferArgs(spec, direction, source, destination),
576
- ];
572
+ const endpoint = ["--", ...transferArgs(spec, direction, source, destination)];
577
573
  const bin = opts.loginPassword ? "sshpass" : "scp";
578
- const args = opts.loginPassword ? ["-e", "scp", ...scpArgs] : scpArgs;
574
+ // Same rule as runSsh: without a password in hand, force BatchMode so scp
575
+ // never opens its own /dev/tty prompt. sshpass path keeps prompts on.
576
+ const args = opts.loginPassword
577
+ ? ["-e", "scp", ...baseScpArgs(spec, opts.controlPath, recursive), ...endpoint]
578
+ : [...baseScpArgs(spec, opts.controlPath, recursive), "-o", "BatchMode=yes", ...endpoint];
579
579
  const env = opts.loginPassword ? { ...process.env, SSHPASS: opts.loginPassword } : process.env;
580
580
  return spawnResult(bin, args, env, opts.signal);
581
581
  }
582
582
 
583
- export function runSsh(spec: HostSpec, command: string, opts: RunOptions): Promise<SshResult> {
583
+ /**
584
+ * Build the ssh/sshpass argv for a run.
585
+ *
586
+ * Without a login password, force `BatchMode=yes` so ssh NEVER falls back to a
587
+ * `/dev/tty` password prompt (which leaks into the TUI). A run-time auth failure
588
+ * then returns a clean error the caller surfaces — all interactive password
589
+ * entry goes through the overlay + `sshpass`. With a password, BatchMode stays
590
+ * off so sshpass can answer ssh's own prompt.
591
+ */
592
+ export function buildRunSshArgs(
593
+ spec: HostSpec,
594
+ command: string,
595
+ opts: Pick<RunOptions, "controlPath" | "loginPassword" | "sudo">,
596
+ ): { bin: string; args: string[] } {
584
597
  const remote = remoteCommand(command, opts.sudo === true);
585
- const sshArgs = [...baseSshArgs(spec, opts.controlPath), hostTarget(spec), remote];
586
-
587
- let bin = "ssh";
588
- let args = sshArgs;
589
- let env = process.env;
598
+ const base = baseSshArgs(spec, opts.controlPath);
590
599
  if (opts.loginPassword) {
591
- bin = "sshpass";
592
- args = ["-e", "ssh", ...sshArgs];
593
- env = { ...process.env, SSHPASS: opts.loginPassword };
600
+ return { bin: "sshpass", args: ["-e", "ssh", ...base, hostTarget(spec), remote] };
594
601
  }
602
+ return { bin: "ssh", args: [...base, "-o", "BatchMode=yes", hostTarget(spec), remote] };
603
+ }
604
+
605
+ export function runSsh(spec: HostSpec, command: string, opts: RunOptions): Promise<SshResult> {
606
+ const { bin, args } = buildRunSshArgs(spec, command, opts);
607
+ const env = opts.loginPassword ? { ...process.env, SSHPASS: opts.loginPassword } : process.env;
595
608
 
596
609
  // Remote sudo reads its password from stdin (first line); anything else
597
610
  // closes stdin so the remote command sees EOF.