Files
hermes-agent/apps/bootstrap-installer/src/lib/format.ts
T
brooklyn! d753957e8a fix(install): Windows setup no longer hangs forever on Node.js dependencies (#85529)
* fix(install): time-box the Windows node-deps stage so a stalled npm or Playwright install can't hang setup forever

scripts/install.sh has bounded this same work with run_with_timeout
"$NODE_DEPS_TIMEOUT" (600s default) since #39219, but install.ps1 never got
the guard: Install-NodeDeps ran both `npm install` and `npx playwright
install chromium` unbounded. A stalled registry fetch or a wedged Chromium
archive extraction (#76222, #84614) froze the installer indefinitely -- one
user left it running 12+ hours overnight before asking for help.

Route both invocations through _Invoke-NativeWithTimeout: cmd.exe launches
the native command with its output merged to a log, the parent polls with a
wall-clock deadline and tails new log lines to the console each tick (the
live progress that makes a 3-minute download distinguishable from a hang),
and on timeout taskkill /T /F kills the real process tree and returns 124 --
the same convention as coreutils timeout and bash's run_with_timeout.
Wait-Job was rejected for this: jobs swallow live output and Stop-Job leaves
the npm child running. Windows PowerShell 5.1-safe throughout.

Timeouts surface as a warning with the log path, a note that re-running the
installer resumes (stages are idempotent), and the NODE_DEPS_TIMEOUT env
override for slow links -- mirroring bash.

Fixes #76222.
Closes #84614.
Supersedes #76303.

Co-authored-by: JonthanaHanh <JonthanaHanh@users.noreply.github.com>

* fix(installer): roll stage timers over to hours so an overnight stall doesn't read as "744 hours"

formatElapsed rendered a running stage as m:ss with unbounded minutes: a
node-deps stage left hanging overnight showed "744:38", which the user who
reported the hang understandably read as 744 hours. formatDuration
(completed stages) had the same unbounded-minutes shape.

Move both formatters into src/lib/format.ts (pure, no React) and add the
hour rollover: h:mm:ss live, "Xh Ym" completed. tests-js pins the shapes,
including 744m38s -> 12:24:38.

---------

Co-authored-by: JonthanaHanh <JonthanaHanh@users.noreply.github.com>
2026-08-13 13:38:20 -05:00

36 lines
1.2 KiB
TypeScript

/*
* Duration formatters for the stage list. Pure functions, no React — kept out
* of progress.tsx so tests-js can exercise them without dragging in the Tauri
* renderer.
*/
// Duration of a completed stage: ms, then s, then "Xm Ys", then "Xh Ym".
export function formatDuration(ms: number): string {
if (ms < 1000) {return `${ms}ms`}
if (ms < 60000) {return `${(ms / 1000).toFixed(1)}s`}
const m = Math.floor(ms / 60000)
const s = Math.round((ms % 60000) / 1000)
if (m < 60) {return `${m}m ${s}s`}
const h = Math.floor(m / 60)
return `${h}h ${m - h * 60}m`
}
// Live elapsed for a running stage: bare seconds under a minute, then m:ss,
// then h:mm:ss past an hour. Without the hour rollover a stalled overnight
// stage read as "744:38" — minutes rendered unbounded — which one user
// understandably reported as "744 hours".
export function formatElapsed(ms: number): string {
const s = Math.max(0, Math.floor(ms / 1000))
if (s < 60) {return `${s}s`}
const m = Math.floor(s / 60)
if (m < 60) {return `${m}:${String(s - m * 60).padStart(2, '0')}`}
const h = Math.floor(m / 60)
return `${h}:${String(m - h * 60).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`
}