fix(ssh-controlmaster): prevent session_start from hanging on unreachable host
pi awaits session_start before the agent loop accepts input, so a blocking ssh call there presents as "TUI is up but prompts are silently ignored". The remote pwd probe and ControlMaster start had no ConnectTimeout, no BatchMode, and no wall-clock cap, so an unreachable host (classic case: `pi-dev --ssh <lan-host>` runs pi *inside* the devbox container, which has no route to LAN hosts without a ProxyJump) blocked startup on the OS TCP timeout (~75s+) while silently swallowing keystrokes. Hardening: - CONNECT_OPTS (ConnectTimeout=8 + ServerAlive keepalives) on every ssh invocation: pwd probe, master start (both key and password paths), sshExec, and bash exec. - run() gains a timeoutMs option (kills child + rejects); pwd probe and both master-start paths bounded by STARTUP_TIMEOUT_MS (15s). - BATCH_OPTS (BatchMode=yes) on key-auth calls so ssh never waits on a /dev/tty password/passphrase/host-key prompt behind the TUI; omitted under --ssh-ask-pass so the SSH_ASKPASS path still works. askPass flag is now read before the probe to pick the right opts. - Probe failure sets an "unreachable" status + error toast and returns, instead of throwing an unhandled rejection from the handler. Docs: README "How it works" + AGENTS.md technical note updated.
This commit is contained in:
@@ -115,6 +115,25 @@ on a remote machine via SSH when `--ssh user@host` is passed.
|
||||
so a read-only system `ControlPath` (which a plain `ssh … pwd` would try, and
|
||||
fail, to bind a master socket into) cannot make the initial probe exit 255.
|
||||
|
||||
- **Hang-proofing (no ssh call can block the UI).**
|
||||
`session_start` is `await`ed by pi before the agent loop accepts input, so a
|
||||
blocking ssh call there manifests as *"the TUI is up but my prompts are
|
||||
silently ignored."* Guards: a shared `CONNECT_OPTS` (`ConnectTimeout=8` +
|
||||
`ServerAliveInterval=5`/`ServerAliveCountMax=3`) is appended to **every** ssh
|
||||
invocation (pwd probe, master start, `sshExec`, bash exec); the `pwd` probe
|
||||
uses `run(..., { timeoutMs: STARTUP_TIMEOUT_MS })` (15 s) and both master-start
|
||||
paths kill the child + reject on the same wall-clock cap. Key-auth calls also
|
||||
carry `BATCH_OPTS` (`BatchMode=yes`) so ssh refuses any `/dev/tty`
|
||||
password/passphrase/host-key prompt rather than waiting on it invisibly;
|
||||
`--ssh-ask-pass` omits `BatchMode` (it would disable `SSH_ASKPASS`). The
|
||||
`askPass` flag is read **before** the probe so the probe picks the right opts.
|
||||
On probe failure the handler sets an `✗ unreachable` status + error toast and
|
||||
`return`s (instead of throwing an unhandled rejection). Common trigger: a LAN
|
||||
host reachable from the macOS host but **not** from inside the pi-devbox
|
||||
container (`pi-dev` = `docker compose … exec devbox pi`); the container has no
|
||||
route without a `ProxyJump host` entry, so `pi --ssh <lan-host>` from the
|
||||
container now fails fast instead of stalling on the OS TCP timeout.
|
||||
|
||||
- **Password auth via `--ssh-ask-pass`.**
|
||||
When the flag is set, `ctx.ui.input()` prompts for a password before
|
||||
connecting. The password is passed to SSH via `SSH_ASKPASS`: a temp script at
|
||||
|
||||
Reference in New Issue
Block a user