65fada0945
On Windows, `hermes update` can hang past its own 660s cua-driver timeout until the user kills an orphaned PowerShell by hand. The timeout ceiling is not the problem; the code that runs after it is. `_run_cua_driver_installer` handles `TimeoutExpired` by killing the process tree and then draining the pipes with a bare `proc.communicate()`. The kill is best-effort by construction: every `psutil.Error` in `_kill_installer_tree` is logged at debug level and stepped over, on the reasoning that a partly killed tree beats none. That is the right call, but it means the drain has to survive a partial kill, and an unbounded drain does not. The concrete case is the one reported. `install.ps1` self-elevates through `Start-Process -Verb RunAs`, so the descendant runs at High integrity and a medium-integrity `child.kill()` raises `AccessDenied`. The per-child handler logs it and continues. That survivor is still holding the `stdout=PIPE` write handle it inherited, so the following `communicate()` waits for an EOF that arrives only when somebody kills that process manually. A bounded 660s wait becomes an unbounded one, after the warning has already printed. Bound the drain instead. A kill that landed closes the pipe immediately, so this costs nothing on the normal path; a kill that did not costs 15s rather than forever. The original `TimeoutExpired` is re-raised either way, so the existing manual re-run hint still prints and the update unwinds. Losing the tail of a timed-out installer's log is the cheaper half of that trade, and it is only lost in the case where the run already failed. The drain deliberately does not close the pipe handles. `communicate()`'s reader threads are still blocked on them and closing underneath them races; they are daemon threads, so abandoning them does not hold the interpreter open. Both timeout handlers (streaming and captured) now go through one helper. The streaming child inherits the console rather than a pipe, so it is much harder to stall there, but the two branches should not drift on a rule this small. Tests: 5, in a new `TestInstallerTimeoutDrainIsBounded`. Two fail without the fix, including the reported scenario end to end (a child kill refused with `psutil.AccessDenied`, asserting the drain still carries a deadline). The deadline is asserted as a kwarg rather than by timing, because a test that proved the hang by hanging would be the same defect wearing a test's name. Scope note: this does not touch the `stdin` inheritance that lets `install.ps1`'s `Read-Host` block in the first place. That is #79684 and open PR #79871 already carries the one-line `stdin=DEVNULL` fix; the two are independent and neither subsumes the other, since `DEVNULL` cannot unblock a UAC elevation dialog. Fixes #87703