Platform-Specific Code Differences Between PAI on macOS and Linux
PAI implements conditional code paths that detect the host operating system at runtime to handle BSD versus GNU utility syntax, LaunchAgent versus systemd service management, and platform-specific binaries like afplay versus mpv.
PAI (Personal AI Infrastructure) by Daniel Miessler was originally architected for macOS and later extended to support Linux through a series of platform-aware abstractions. Understanding these platform-specific code differences between PAI on macOS and Linux is essential for contributors deploying the voice server or observability components on heterogeneous environments.
Shell Syntax and Path Conventions
PAI shell scripts handle incompatible syntax between BSD and GNU utilities, particularly for in-place file editing and package manager paths.
sed -i syntax differences represent the most common friction point. macOS uses BSD sed, which requires an empty string argument for the backup suffix, while Linux uses GNU sed, which does not. According to the source analysis in PLATFORM.md (lines 25-28), PAI implements a runtime check:
if [ "$(uname -s)" = "Darwin" ]; then
sed -i '' 's/old/new/g' file.txt
else
sed -i 's/old/new/g' file.txt
fi
Homebrew path handling also differs. On macOS Apple Silicon, PAI references /opt/homebrew/bin as the default Homebrew location. The Linux implementation checks for this directory first and falls back to the system $PATH if it does not exist, as documented in PLATFORM.md (lines 30-34) and implemented in pai-observability-server/manage.sh.
Service Management Architecture
PAI uses entirely different service managers for background processes, conditional on the host platform.
macOS LaunchAgent implementation relies on launchctl and plist files stored under ~/Library/LaunchAgents. The installer creates a plist defining the PAI voice server environment variables and scheduling, then loads it via launchctl load.
Linux systemd user service provides equivalent functionality using a unit file stored in ~/.config/systemd/user. The installation script writes the service definition and executes systemctl --user enable pai.service followed by systemctl --user start pai.service.
Both paths are documented in PLATFORM.md (lines 36-49) and implemented conditionally in Voice system/INSTALL.md.
Audio and Notification Subsystems
Platform-specific binaries require abstraction layers for media playback and desktop notifications.
Audio playback uses afplay on macOS, which is a built-in command-line player. On Linux, PAI implements a cascading fallback chain: it detects and uses the first available player in the order mpg123 → mpv → snap/mpv. If none are found, it logs a warning and continues without audio. This logic appears in the voice server TypeScript source and is documented in PLATFORM.md (lines 62-70).
Desktop notifications use osascript to trigger Notification Center alerts on macOS. The Linux branch uses notify-send from the libnotify package. Both implementations are guarded by process.platform checks in the TypeScript runtime, as shown in PLATFORM.md (lines 71-74).
Runtime Platform Detection Patterns
PAI implements consistent detection patterns across shell scripts and TypeScript code.
Shell-based detection uses uname -s to set an OS_TYPE variable at the start of management scripts:
OS_TYPE="$(uname -s)"
if [ "$OS_TYPE" = "Darwin" ]; then
# macOS-specific section
elif [ "$OS_TYPE" = "Linux" ]; then
# Linux-specific section
else
echo "Unsupported platform: $OS_TYPE"
exit 1
fi
This pattern appears in pai-observability-server/manage.sh and is recommended in PLATFORM.md (lines 38-44).
TypeScript/Bun detection relies on the Node.js process.platform global:
if (process.platform === 'darwin') {
// macOS: use afplay for audio
exec('afplay', [audioFile]);
} else if (process.platform === 'linux') {
// Linux: pick the first available player
const player = which('mpg123') ?? which('mpv') ?? which('snap')?.includes('mpv') ? 'mpv' : null;
if (player) exec(player, [audioFile]);
else console.warn('No audio player found – install mpg123 or mpv');
}
This implementation is documented in PLATFORM.md (lines 53-60) and implemented in the voice server source.
Summary
- Shell utility syntax differs between BSD (macOS) and GNU (Linux) tools, requiring runtime checks for commands like
sed -i. - Service management uses
launchctland LaunchAgents on macOS versussystemduser units on Linux, with conditional installation logic in the setup scripts. - Audio and notifications require platform-specific binaries:
afplayandosascripton macOS, versusmpg123/mpvandnotify-sendon Linux. - Runtime detection consistently uses
uname -sin shell scripts andprocess.platformin TypeScript to branch execution paths. - Path conventions follow OS standards:
~/Libraryon macOS versus~/.configon Linux for logs and service definitions.
Frequently Asked Questions
How does PAI detect which platform it's running on?
PAI uses two primary detection mechanisms. In shell scripts, it reads the OS_TYPE variable from uname -s, checking for "Darwin" or "Linux". In TypeScript code running under Bun, it evaluates process.platform === 'darwin' or process.platform === 'linux'. These checks appear at the entry points of platform-specific operations in pai-observability-server/manage.sh and the voice server source.
What audio player does PAI use on Linux if multiple are installed?
PAI implements a cascading priority chain on Linux: it first attempts to use mpg123, then falls back to mpv, and finally checks for snap/mpv. The first binary found in the system PATH is selected. If none are available, PAI logs a warning and continues execution without audio playback rather than failing. This logic is implemented in the voice server's TypeScript runtime.
Why does PAI use different service managers on macOS and Linux?
PAI adheres to each operating system's native service management conventions to ensure proper integration with user sessions and system startup. macOS uses LaunchAgents controlled by launchctl, which is the standard for user-level background processes on Darwin systems. Linux uses systemd user services, which provide equivalent functionality while following the predominant Linux service management standard. The installer scripts in Voice system/INSTALL.md conditionally generate the appropriate unit files for each platform.
Can I run PAI on Windows?
The current PAI codebase does not implement Windows-specific code paths. While the TypeScript process.platform check could theoretically be extended to handle win32, the shell scripts rely heavily on Unix-specific utilities like sed, uname, and launchctl/systemd that have no direct Windows equivalents. Users wishing to run PAI on Windows would need to use the Windows Subsystem for Linux (WSL2) to provide a Linux-compatible environment.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →