Summary
With the parsing bug from the BOM issue fixed, pulse.ts starts on Windows and exits immediately with code 0 and zero log output — no error, no crash message, nothing. It never reaches Bun.serve(), so Pulse never actually comes up.
Environment: Windows 10 Pro, PowerShell 7 and 5.1 both reproduce it, Bun 1.3.10.
Root cause
The singleton guard at the top of main() in pulse.ts does:
const oldPid = parseInt((await Bun.file(PID_PATH).text()).trim(), 10)
On this Windows/Bun combination, when PID_PATH doesn't exist yet — true on every first boot, since this is exactly the file this check itself creates — the awaited promise never settles. It doesn't throw, doesn't reject, it just never resolves. With nothing else keeping the event loop alive at that point in main(), the process quietly exits(0) with the promise still pending, before the surrounding try/catch or the outer main().catch() ever run. No log line, no stack trace.
Confirmed with targeted instrumentation: execution reaches the line, logs the path about to be read, and the process exit event fires with no further output. A standalone minimal repro of the exact same Bun.file(missingPath).text() call, with the same imports, does NOT reproduce the hang on its own — so this looks specific to some interaction with the rest of the file's module graph rather than something reproducible as a Bun.file bug in isolation. Flagging as observed-and-worked-around rather than fully root-caused.
Repro
- Fresh Windows box, no prior Pulse state (
PULSE/state/pulse.pid does not exist).
bun run pulse.ts.
- Process exits ~instantly, exit code 0, no console output at all.
Fix
Guard the read with an existence check first, since the file legitimately may not exist on first boot — this sidesteps the hang regardless of its exact cause, and is a reasonable belt-and-suspenders check on any platform:
- const oldPid = parseInt((await Bun.file(PID_PATH).text()).trim(), 10)
+ const oldPid = existsSync(PID_PATH) ? parseInt((await Bun.file(PID_PATH).text()).trim(), 10) : NaN
(existsSync is already imported at the top of pulse.ts.)
Separately noticed, not fixed here
The same guard's duplicate-instance detection shells out to ps -p <pid> -o command=, which doesn't exist on Windows. It fails silently into the existing catch block, so it isn't blocking, just a soft gap: the "another pulse.ts is already running" warning never fires on Windows. Needs a real Windows equivalent (e.g. Get-CimInstance Win32_Process) rather than a one-line patch, so leaving it as a known gap rather than folding a fix in here.
Testing performed
With this fix plus the BOM fix and the status-timeout fix (filed separately) applied together, verified the full lifecycle on the same Windows machine:
install → start → status (yes, healthz 200) → stop → status (no) → uninstall (task removed) → install → start → status (yes)
Pulse now runs continuously as a current-user scheduled task with no admin rights required, matching the design in #1733.
Summary
With the parsing bug from the BOM issue fixed,
pulse.tsstarts on Windows and exits immediately with code 0 and zero log output — no error, no crash message, nothing. It never reachesBun.serve(), so Pulse never actually comes up.Environment: Windows 10 Pro, PowerShell 7 and 5.1 both reproduce it, Bun 1.3.10.
Root cause
The singleton guard at the top of
main()inpulse.tsdoes:On this Windows/Bun combination, when
PID_PATHdoesn't exist yet — true on every first boot, since this is exactly the file this check itself creates — the awaited promise never settles. It doesn't throw, doesn't reject, it just never resolves. With nothing else keeping the event loop alive at that point inmain(), the process quietly exits(0) with the promise still pending, before the surroundingtry/catchor the outermain().catch()ever run. No log line, no stack trace.Confirmed with targeted instrumentation: execution reaches the line, logs the path about to be read, and the process
exitevent fires with no further output. A standalone minimal repro of the exact sameBun.file(missingPath).text()call, with the same imports, does NOT reproduce the hang on its own — so this looks specific to some interaction with the rest of the file's module graph rather than something reproducible as aBun.filebug in isolation. Flagging as observed-and-worked-around rather than fully root-caused.Repro
PULSE/state/pulse.piddoes not exist).bun run pulse.ts.Fix
Guard the read with an existence check first, since the file legitimately may not exist on first boot — this sidesteps the hang regardless of its exact cause, and is a reasonable belt-and-suspenders check on any platform:
(
existsSyncis already imported at the top ofpulse.ts.)Separately noticed, not fixed here
The same guard's duplicate-instance detection shells out to
ps -p <pid> -o command=, which doesn't exist on Windows. It fails silently into the existing catch block, so it isn't blocking, just a soft gap: the "another pulse.ts is already running" warning never fires on Windows. Needs a real Windows equivalent (e.g.Get-CimInstance Win32_Process) rather than a one-line patch, so leaving it as a known gap rather than folding a fix in here.Testing performed
With this fix plus the BOM fix and the status-timeout fix (filed separately) applied together, verified the full lifecycle on the same Windows machine:
Pulse now runs continuously as a current-user scheduled task with no admin rights required, matching the design in #1733.