Skip to content

voice.ts: showDesktopNotification spawns /usr/bin/osascript unconditionally — ENOENT on Linux #1239

Description

@JElliottMiller

Summary

Releases/v5.0.0/.claude/PAI/PULSE/VoiceServer/voice.ts:375-388 showDesktopNotification() unconditionally spawns /usr/bin/osascript (macOS-only). On Linux / WSL2 every voice notification fires a posix_spawn '/usr/bin/osascript' ENOENT in the daemon log. With Pulse cron jobs producing voice output, journal spam can reach one ENOENT every few seconds, drowning real signal.

Repro

# Linux/WSL2, PAI v5.0.0:
curl -X POST http://localhost:31337/notify \
  -H 'Content-Type: application/json' \
  -d '{"message":"test","voice_enabled":true}'

# Watch the journal:
journalctl --user -u pai-pulse --since "30 seconds ago" | grep osascript
# Output:
# error: ENOENT: no such file or directory, posix_spawn '/usr/bin/osascript'

The TTS HTTP request still returns 200 OK and audio bytes are generated, but the desktop-notification path crashes.

Current code (upstream main)

// voice.ts:375-388
async function showDesktopNotification(title: string, message: string): Promise<void> {
  // ... no platform check
  const proc = spawn("/usr/bin/osascript", ["-e", script])
  proc.on("exit", (code) => (code === 0 ? resolve() : reject(new Error(`osascript exited ${code}`))))
}

Expected

On non-darwin platforms, showDesktopNotification should either:

  • Early-return silently (no-op — voice itself still plays through playAudio), OR
  • Branch to a Linux equivalent — notify-send on Linux desktop, powershell.exe -c '...' shim on WSL2.

Early-return is the smallest reversible change and matches what PR #1116 does on the adjacent playAudio path for browser-audio-streaming.

Suggested fix (locally applied — 1 line)

 async function showDesktopNotification(title: string, message: string): Promise<void> {
+  if (process.platform !== "darwin") return
   const escapedTitle = title.replace(/"/g, '\\"')
   ...
   const proc = spawn("/usr/bin/osascript", ["-e", script])

Tested on WSL2 + systemd-user. ENOENT spam stopped immediately after restart; voice TTS audio playback (via locally-patched playAudio → mpv) continued working without notification.

Environment

  • PAI v5.0.0 (file unchanged in current main as of 2026-05-12)
  • Ubuntu 24.04 on WSL2, systemd-user supervisor

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions