diff --git a/Releases/v2.4/.claude/VoiceServer/linux-service/install.sh b/Releases/v2.4/.claude/VoiceServer/linux-service/install.sh new file mode 100755 index 000000000..329fab9ce --- /dev/null +++ b/Releases/v2.4/.claude/VoiceServer/linux-service/install.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +# Voice Server Linux Installation Script +# Installs the voice server as a systemd user service + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +VOICE_DIR="$(dirname "$SCRIPT_DIR")" +SERVICE_NAME="pai-voice-server" +SERVICE_DIR="$HOME/.config/systemd/user" +SERVICE_FILE="$SERVICE_DIR/${SERVICE_NAME}.service" +ENV_FILE="$HOME/.claude/.env" + +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${BLUE} Voice Server Linux Installation${NC}" +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo + +# Check for Bun +echo -e "${YELLOW}▶ Checking prerequisites...${NC}" +if ! command -v bun &> /dev/null; then + echo -e "${RED}✗ Bun is not installed${NC}" + echo " Install Bun first:" + echo " curl -fsSL https://bun.sh/install | bash" + exit 1 +fi +echo -e "${GREEN}✓ Bun is installed${NC}" + +# Check for audio player +if command -v mpv &> /dev/null; then + echo -e "${GREEN}✓ mpv is installed (audio playback)${NC}" +elif command -v ffplay &> /dev/null; then + echo -e "${GREEN}✓ ffplay is installed (audio playback)${NC}" +else + echo -e "${RED}✗ No audio player found${NC}" + echo " Install one of:" + echo " sudo apt install mpv # Recommended" + echo " sudo apt install ffmpeg # Alternative (provides ffplay)" + exit 1 +fi + +# Check for notify-send +if command -v notify-send &> /dev/null; then + echo -e "${GREEN}✓ notify-send is installed (desktop notifications)${NC}" +else + echo -e "${YELLOW}⚠ notify-send not found (optional, for desktop notifications)${NC}" + echo " Install with: sudo apt install libnotify-bin" +fi + +# Check for ElevenLabs configuration +echo -e "${YELLOW}▶ Checking ElevenLabs configuration...${NC}" +ELEVENLABS_CONFIGURED=false +if [ -f "$ENV_FILE" ] && grep -q "ELEVENLABS_API_KEY=" "$ENV_FILE"; then + API_KEY=$(grep "ELEVENLABS_API_KEY=" "$ENV_FILE" | cut -d'=' -f2 | tr -d "'" | tr -d '"') + if [ "$API_KEY" != "your_api_key_here" ] && [ -n "$API_KEY" ]; then + echo -e "${GREEN}✓ ElevenLabs API key configured${NC}" + ELEVENLABS_CONFIGURED=true + fi +fi + +if [ "$ELEVENLABS_CONFIGURED" = false ]; then + echo -e "${YELLOW}⚠ ElevenLabs API key not configured${NC}" + echo " Add your key to ~/.claude/.env:" + echo " echo 'ELEVENLABS_API_KEY=your_key_here' >> ~/.claude/.env" + echo " Get a free key at: https://elevenlabs.io" + echo +fi + +# Check if already installed +if systemctl --user is-active "$SERVICE_NAME" &> /dev/null; then + echo -e "${YELLOW}⚠ Voice server is already running${NC}" + read -p "Do you want to reinstall? (y/n): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}▶ Stopping existing service...${NC}" + systemctl --user stop "$SERVICE_NAME" 2>/dev/null || true + systemctl --user disable "$SERVICE_NAME" 2>/dev/null || true + echo -e "${GREEN}✓ Existing service stopped${NC}" + else + echo "Installation cancelled" + exit 0 + fi +fi + +# Create systemd user service directory +echo -e "${YELLOW}▶ Creating systemd user service...${NC}" +mkdir -p "$SERVICE_DIR" + +# Generate service file with resolved paths +BUN_PATH="$(which bun)" +cat > "$SERVICE_FILE" << EOF +[Unit] +Description=PAI Voice Server - ElevenLabs TTS notification server +After=network.target sound.target + +[Service] +Type=simple +ExecStart=${BUN_PATH} run ${VOICE_DIR}/server.ts +WorkingDirectory=${VOICE_DIR} +Restart=on-failure +RestartSec=5 +Environment=HOME=${HOME} +Environment=PATH=${HOME}/.bun/bin:/usr/local/bin:/usr/bin:/bin +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=default.target +EOF + +echo -e "${GREEN}✓ Systemd service file created${NC}" + +# Enable and start the service +echo -e "${YELLOW}▶ Starting voice server service...${NC}" +systemctl --user daemon-reload +systemctl --user enable "$SERVICE_NAME" +systemctl --user start "$SERVICE_NAME" + +# Wait for server to start +sleep 2 + +# Test the server +echo -e "${YELLOW}▶ Testing voice server...${NC}" +if curl -s -f -X GET http://localhost:8888/health > /dev/null 2>&1; then + echo -e "${GREEN}✓ Voice server is running${NC}" + + # Send test notification + echo -e "${YELLOW}▶ Sending test notification...${NC}" + curl -s -X POST http://localhost:8888/notify \ + -H "Content-Type: application/json" \ + -d '{"message": "Voice server installed successfully on Linux"}' > /dev/null + echo -e "${GREEN}✓ Test notification sent${NC}" +else + echo -e "${RED}✗ Voice server is not responding${NC}" + echo " Check logs: journalctl --user -u $SERVICE_NAME -f" + echo " Try running manually: bun run $VOICE_DIR/server.ts" + exit 1 +fi + +# Enable lingering so service runs even when not logged in (optional) +if command -v loginctl &> /dev/null; then + loginctl enable-linger "$(whoami)" 2>/dev/null || true +fi + +# Summary +echo +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${GREEN} ✓ Installation Complete!${NC}" +echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo +echo -e "${BLUE}Service Information:${NC}" +echo " Service: $SERVICE_NAME" +echo " Status: Running" +echo " Port: 8888" +echo " Logs: journalctl --user -u $SERVICE_NAME" + +if [ "$ELEVENLABS_CONFIGURED" = true ]; then + echo " Voice: ElevenLabs AI" +else + echo " Voice: Not configured (add ELEVENLABS_API_KEY to ~/.claude/.env)" +fi + +echo +echo -e "${BLUE}Management Commands:${NC}" +echo " Status: systemctl --user status $SERVICE_NAME" +echo " Stop: systemctl --user stop $SERVICE_NAME" +echo " Start: systemctl --user start $SERVICE_NAME" +echo " Restart: systemctl --user restart $SERVICE_NAME" +echo " Logs: journalctl --user -u $SERVICE_NAME -f" +echo " Uninstall: $SCRIPT_DIR/uninstall.sh" +echo +echo -e "${BLUE}Test the server:${NC}" +echo " curl -X POST http://localhost:8888/notify \\" +echo " -H 'Content-Type: application/json' \\" +echo " -d '{\"message\": \"Hello from PAI\"}'" +echo +echo -e "${GREEN}The voice server will start automatically on login.${NC}" diff --git a/Releases/v2.4/.claude/VoiceServer/linux-service/pai-voice-server.service b/Releases/v2.4/.claude/VoiceServer/linux-service/pai-voice-server.service new file mode 100644 index 000000000..28d06ac34 --- /dev/null +++ b/Releases/v2.4/.claude/VoiceServer/linux-service/pai-voice-server.service @@ -0,0 +1,18 @@ +[Unit] +Description=PAI Voice Server - ElevenLabs TTS notification server +After=network.target sound.target +Wants=pulseaudio.service + +[Service] +Type=simple +ExecStart=%h/.bun/bin/bun run %h/.claude/VoiceServer/server.ts +WorkingDirectory=%h/.claude/VoiceServer +Restart=on-failure +RestartSec=5 +Environment=HOME=%h +Environment=PATH=%h/.bun/bin:/usr/local/bin:/usr/bin:/bin +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=default.target diff --git a/Releases/v2.4/.claude/VoiceServer/linux-service/uninstall.sh b/Releases/v2.4/.claude/VoiceServer/linux-service/uninstall.sh new file mode 100755 index 000000000..602661ac8 --- /dev/null +++ b/Releases/v2.4/.claude/VoiceServer/linux-service/uninstall.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Voice Server Linux Uninstall Script + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +SERVICE_NAME="pai-voice-server" +SERVICE_FILE="$HOME/.config/systemd/user/${SERVICE_NAME}.service" + +echo -e "${YELLOW}▶ Uninstalling Voice Server...${NC}" + +if systemctl --user is-active "$SERVICE_NAME" &> /dev/null; then + echo -e "${YELLOW}▶ Stopping service...${NC}" + systemctl --user stop "$SERVICE_NAME" + echo -e "${GREEN}✓ Service stopped${NC}" +fi + +if systemctl --user is-enabled "$SERVICE_NAME" &> /dev/null; then + echo -e "${YELLOW}▶ Disabling service...${NC}" + systemctl --user disable "$SERVICE_NAME" + echo -e "${GREEN}✓ Service disabled${NC}" +fi + +if [ -f "$SERVICE_FILE" ]; then + rm "$SERVICE_FILE" + systemctl --user daemon-reload + echo -e "${GREEN}✓ Service file removed${NC}" +fi + +echo -e "${GREEN}✓ Voice server uninstalled${NC}" diff --git a/Releases/v2.4/.claude/VoiceServer/server.ts b/Releases/v2.4/.claude/VoiceServer/server.ts index 314eb9ff7..2cc484b82 100755 --- a/Releases/v2.4/.claude/VoiceServer/server.ts +++ b/Releases/v2.4/.claude/VoiceServer/server.ts @@ -5,8 +5,12 @@ import { serve } from "bun"; import { spawn } from "child_process"; -import { homedir } from "os"; +import { homedir, platform } from "os"; import { join } from "path"; + +// Platform detection for cross-platform support +const IS_LINUX = platform() === "linux"; +const IS_MACOS = platform() === "darwin"; import { existsSync, readFileSync } from "fs"; // Load .env from ~/.claude directory @@ -274,7 +278,7 @@ function getVolumeSetting(requestVolume?: number): number { return 1.0; // Default to full volume } -// Play audio using afplay (macOS) +// Play audio using platform-appropriate player async function playAudio(audioBuffer: ArrayBuffer, requestVolume?: number): Promise { const tempFile = `/tmp/voice-${Date.now()}.mp3`; @@ -284,22 +288,53 @@ async function playAudio(audioBuffer: ArrayBuffer, requestVolume?: number): Prom const volume = getVolumeSetting(requestVolume); return new Promise((resolve, reject) => { - // afplay -v takes a value from 0.0 to 1.0 - const proc = spawn('/usr/bin/afplay', ['-v', volume.toString(), tempFile]); + let proc; + + if (IS_LINUX) { + // Linux: use mpv (preferred) or ffplay (fallback) + proc = spawn('mpv', [ + '--no-video', + '--no-terminal', + `--volume=${Math.round(volume * 100)}`, + tempFile, + ]); + proc.on('error', () => { + // mpv not found, try ffplay + console.warn('mpv not found, trying ffplay...'); + const ffProc = spawn('ffplay', [ + '-nodisp', '-autoexit', + '-volume', Math.round(volume * 100).toString(), + tempFile, + ]); + ffProc.on('error', (error) => { + console.error('No audio player found. Install mpv or ffmpeg:', error); + spawn('rm', ['-f', tempFile]); + reject(error); + }); + ffProc.on('exit', (code) => { + spawn('rm', ['-f', tempFile]); + code === 0 ? resolve() : reject(new Error(`ffplay exited with code ${code}`)); + }); + }); + } else { + // macOS: afplay -v takes a value from 0.0 to 1.0 + proc = spawn('/usr/bin/afplay', ['-v', volume.toString(), tempFile]); + } proc.on('error', (error) => { + if (IS_LINUX) return; // Already handled above with fallback console.error('Error playing audio:', error); reject(error); }); proc.on('exit', (code) => { // Clean up temp file - spawn('/bin/rm', [tempFile]); + spawn('rm', ['-f', tempFile]); if (code === 0) { resolve(); } else { - reject(new Error(`afplay exited with code ${code}`)); + reject(new Error(`Audio player exited with code ${code}`)); } }); }); @@ -400,12 +435,18 @@ async function sendNotification( } } - // Display macOS notification - escape for AppleScript + // Display desktop notification try { - const escapedTitle = escapeForAppleScript(safeTitle); - const escapedMessage = escapeForAppleScript(safeMessage); - const script = `display notification "${escapedMessage}" with title "${escapedTitle}" sound name ""`; - await spawnSafe('/usr/bin/osascript', ['-e', script]); + if (IS_LINUX) { + // Linux: notify-send (libnotify) + await spawnSafe('notify-send', ['--app-name=PAI', safeTitle, safeMessage]); + } else { + // macOS: AppleScript notification + const escapedTitle = escapeForAppleScript(safeTitle); + const escapedMessage = escapeForAppleScript(safeMessage); + const script = `display notification "${escapedMessage}" with title "${escapedTitle}" sound name ""`; + await spawnSafe('/usr/bin/osascript', ['-e', script]); + } } catch (error) { console.error("Notification display error:", error); } diff --git a/Releases/v2.4/.claude/VoiceServer/start.sh b/Releases/v2.4/.claude/VoiceServer/start.sh index 348083d91..a1d99dd80 100755 --- a/Releases/v2.4/.claude/VoiceServer/start.sh +++ b/Releases/v2.4/.claude/VoiceServer/start.sh @@ -1,9 +1,7 @@ #!/bin/bash -# Start the Voice Server +# Start the Voice Server (cross-platform: macOS + Linux) -SERVICE_NAME="com.pai.voice-server" -PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_NAME}.plist" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # Colors @@ -14,38 +12,63 @@ NC='\033[0m' echo -e "${YELLOW}▶ Starting Voice Server...${NC}" -# Check if LaunchAgent exists -if [ ! -f "$PLIST_PATH" ]; then - echo -e "${RED}✗ Service not installed${NC}" - echo " Run ./install.sh first to install the service" - exit 1 -fi +if [[ "$(uname)" == "Darwin" ]]; then + # ── macOS: LaunchAgent ── + SERVICE_NAME="com.pai.voice-server" + PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_NAME}.plist" -# Check if already running -if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then - echo -e "${YELLOW}⚠ Voice server is already running${NC}" - echo " To restart, use: ./restart.sh" - exit 0 -fi + if [ ! -f "$PLIST_PATH" ]; then + echo -e "${RED}✗ Service not installed${NC}" + echo " Run ./install.sh first to install the service" + exit 1 + fi + + if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then + echo -e "${YELLOW}⚠ Voice server is already running${NC}" + echo " To restart, use: ./restart.sh" + exit 0 + fi + + launchctl load "$PLIST_PATH" 2>/dev/null + + if [ $? -eq 0 ]; then + sleep 2 + if curl -s -f -X GET http://localhost:8888/health > /dev/null 2>&1; then + echo -e "${GREEN}✓ Voice server started successfully${NC}" + echo " Port: 8888" + else + echo -e "${YELLOW}⚠ Server started but not responding yet${NC}" + echo " Check logs: tail -f ~/Library/Logs/pai-voice-server.log" + fi + else + echo -e "${RED}✗ Failed to start voice server${NC}" + echo " Try running manually: bun run $SCRIPT_DIR/server.ts" + exit 1 + fi +else + # ── Linux: systemd ── + SERVICE_NAME="pai-voice-server" + + if ! systemctl --user cat "$SERVICE_NAME" &> /dev/null; then + echo -e "${RED}✗ Service not installed${NC}" + echo " Run ./linux-service/install.sh first" + exit 1 + fi -# Load the service -launchctl load "$PLIST_PATH" 2>/dev/null + if systemctl --user is-active "$SERVICE_NAME" &> /dev/null; then + echo -e "${YELLOW}⚠ Voice server is already running${NC}" + echo " To restart, use: ./restart.sh" + exit 0 + fi + + systemctl --user start "$SERVICE_NAME" -if [ $? -eq 0 ]; then - # Wait for server to start sleep 2 - - # Test if server is responding if curl -s -f -X GET http://localhost:8888/health > /dev/null 2>&1; then echo -e "${GREEN}✓ Voice server started successfully${NC}" echo " Port: 8888" - echo " Test: curl -X POST http://localhost:8888/notify -H 'Content-Type: application/json' -d '{\"message\":\"Test\"}'" else echo -e "${YELLOW}⚠ Server started but not responding yet${NC}" - echo " Check logs: tail -f ~/Library/Logs/pai-voice-server.log" + echo " Check logs: journalctl --user -u $SERVICE_NAME -f" fi -else - echo -e "${RED}✗ Failed to start voice server${NC}" - echo " Try running manually: bun run $SCRIPT_DIR/server.ts" - exit 1 -fi \ No newline at end of file +fi diff --git a/Releases/v2.4/.claude/VoiceServer/status.sh b/Releases/v2.4/.claude/VoiceServer/status.sh index 1ccc01616..f5f6ea063 100755 --- a/Releases/v2.4/.claude/VoiceServer/status.sh +++ b/Releases/v2.4/.claude/VoiceServer/status.sh @@ -1,10 +1,7 @@ #!/bin/bash -# Check status of Voice Server +# Check status of Voice Server (cross-platform: macOS + Linux) -SERVICE_NAME="com.pai.voice-server" -PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_NAME}.plist" -LOG_PATH="$HOME/Library/Logs/pai-voice-server.log" ENV_FILE="$HOME/.claude/.env" # Colors @@ -19,17 +16,34 @@ echo -e "${BLUE} Voice Server Status${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -# Check LaunchAgent +# Service status (platform-specific) echo -e "${BLUE}Service Status:${NC}" -if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then - PID=$(launchctl list | grep "$SERVICE_NAME" | awk '{print $1}') - if [ "$PID" != "-" ]; then - echo -e " ${GREEN}✓ Service is loaded (PID: $PID)${NC}" +if [[ "$(uname)" == "Darwin" ]]; then + SERVICE_NAME="com.pai.voice-server" + LOG_PATH="$HOME/Library/Logs/pai-voice-server.log" + + if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then + PID=$(launchctl list | grep "$SERVICE_NAME" | awk '{print $1}') + if [ "$PID" != "-" ]; then + echo -e " ${GREEN}✓ Service is loaded (PID: $PID)${NC}" + else + echo -e " ${YELLOW}⚠ Service is loaded but not running${NC}" + fi else - echo -e " ${YELLOW}⚠ Service is loaded but not running${NC}" + echo -e " ${RED}✗ Service is not loaded${NC}" fi else - echo -e " ${RED}✗ Service is not loaded${NC}" + SERVICE_NAME="pai-voice-server" + LOG_CMD="journalctl --user -u $SERVICE_NAME" + + if systemctl --user is-active "$SERVICE_NAME" &> /dev/null; then + PID=$(systemctl --user show "$SERVICE_NAME" --property=MainPID --value 2>/dev/null) + echo -e " ${GREEN}✓ Service is active (PID: $PID)${NC}" + elif systemctl --user is-enabled "$SERVICE_NAME" &> /dev/null; then + echo -e " ${YELLOW}⚠ Service is enabled but not running${NC}" + else + echo -e " ${RED}✗ Service is not installed${NC}" + fi fi # Check if server is responding @@ -37,8 +51,6 @@ echo echo -e "${BLUE}Server Status:${NC}" if curl -s -f -X GET http://localhost:8888/health > /dev/null 2>&1; then echo -e " ${GREEN}✓ Server is responding on port 8888${NC}" - - # Get health info HEALTH=$(curl -s http://localhost:8888/health) echo " Response: $HEALTH" else @@ -48,10 +60,13 @@ fi # Check port binding echo echo -e "${BLUE}Port Status:${NC}" -if lsof -i :8888 > /dev/null 2>&1; then +if command -v lsof &> /dev/null && lsof -i :8888 > /dev/null 2>&1; then PROCESS=$(lsof -i :8888 | grep LISTEN | head -1) echo -e " ${GREEN}✓ Port 8888 is in use${NC}" echo " $PROCESS" | awk '{print " Process: " $1 " (PID: " $2 ")"}' +elif command -v ss &> /dev/null && ss -tlnp | grep -q ':8888' 2>/dev/null; then + echo -e " ${GREEN}✓ Port 8888 is in use${NC}" + ss -tlnp | grep ':8888' | head -1 | awk '{print " " $0}' else echo -e " ${YELLOW}⚠ Port 8888 is not in use${NC}" fi @@ -60,38 +75,69 @@ fi echo echo -e "${BLUE}Voice Configuration:${NC}" if [ -f "$ENV_FILE" ] && grep -q "ELEVENLABS_API_KEY=" "$ENV_FILE"; then - API_KEY=$(grep "ELEVENLABS_API_KEY=" "$ENV_FILE" | cut -d'=' -f2) + API_KEY=$(grep "ELEVENLABS_API_KEY=" "$ENV_FILE" | cut -d'=' -f2 | tr -d "'" | tr -d '"') if [ "$API_KEY" != "your_api_key_here" ] && [ -n "$API_KEY" ]; then echo -e " ${GREEN}✓ ElevenLabs API configured${NC}" if grep -q "ELEVENLABS_VOICE_ID=" "$ENV_FILE"; then - VOICE_ID=$(grep "ELEVENLABS_VOICE_ID=" "$ENV_FILE" | cut -d'=' -f2) + VOICE_ID=$(grep "ELEVENLABS_VOICE_ID=" "$ENV_FILE" | cut -d'=' -f2 | tr -d "'" | tr -d '"') echo " Voice ID: $VOICE_ID" fi else - echo -e " ${YELLOW}⚠ Using macOS 'say' (no API key)${NC}" + echo -e " ${YELLOW}⚠ API key not configured${NC}" fi else - echo -e " ${YELLOW}⚠ Using macOS 'say' (no configuration)${NC}" + echo -e " ${YELLOW}⚠ No ElevenLabs configuration found${NC}" +fi + +# Audio player status (Linux only) +if [[ "$(uname)" != "Darwin" ]]; then + echo + echo -e "${BLUE}Audio Player:${NC}" + if command -v mpv &> /dev/null; then + echo -e " ${GREEN}✓ mpv installed${NC}" + elif command -v ffplay &> /dev/null; then + echo -e " ${GREEN}✓ ffplay installed${NC}" + else + echo -e " ${RED}✗ No audio player found (install mpv or ffmpeg)${NC}" + fi + if command -v notify-send &> /dev/null; then + echo -e " ${GREEN}✓ notify-send installed${NC}" + else + echo -e " ${YELLOW}⚠ notify-send not found (install libnotify-bin)${NC}" + fi fi -# Check logs +# Recent logs echo echo -e "${BLUE}Recent Logs:${NC}" -if [ -f "$LOG_PATH" ]; then - echo " Log file: $LOG_PATH" +if [[ "$(uname)" == "Darwin" ]]; then + LOG_PATH="$HOME/Library/Logs/pai-voice-server.log" + if [ -f "$LOG_PATH" ]; then + echo " Log file: $LOG_PATH" + echo " Last 5 lines:" + tail -5 "$LOG_PATH" | while IFS= read -r line; do + echo " $line" + done + else + echo -e " ${YELLOW}⚠ No log file found${NC}" + fi +else + echo " Logs: journalctl --user -u pai-voice-server" echo " Last 5 lines:" - tail -5 "$LOG_PATH" | while IFS= read -r line; do + journalctl --user -u pai-voice-server --no-pager -n 5 2>/dev/null | while IFS= read -r line; do echo " $line" done -else - echo -e " ${YELLOW}⚠ No log file found${NC}" fi # Show commands echo echo -e "${BLUE}Available Commands:${NC}" -echo " • Start: ./start.sh" -echo " • Stop: ./stop.sh" -echo " • Restart: ./restart.sh" -echo " • Logs: tail -f $LOG_PATH" -echo " • Test: curl -X POST http://localhost:8888/notify -H 'Content-Type: application/json' -d '{\"message\":\"Test\"}'" \ No newline at end of file +echo " Start: ./start.sh" +echo " Stop: ./stop.sh" +echo " Restart: ./restart.sh" +if [[ "$(uname)" == "Darwin" ]]; then + echo " Logs: tail -f ~/Library/Logs/pai-voice-server.log" +else + echo " Logs: journalctl --user -u pai-voice-server -f" +fi +echo " Test: curl -X POST http://localhost:8888/notify -H 'Content-Type: application/json' -d '{\"message\":\"Test\"}'" diff --git a/Releases/v2.4/.claude/VoiceServer/stop.sh b/Releases/v2.4/.claude/VoiceServer/stop.sh index 2cce89b40..c55a4307f 100755 --- a/Releases/v2.4/.claude/VoiceServer/stop.sh +++ b/Releases/v2.4/.claude/VoiceServer/stop.sh @@ -1,9 +1,6 @@ #!/bin/bash -# Stop the Voice Server - -SERVICE_NAME="com.pai.voice-server" -PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_NAME}.plist" +# Stop the Voice Server (cross-platform: macOS + Linux) # Colors RED='\033[0;31m' @@ -13,24 +10,41 @@ NC='\033[0m' echo -e "${YELLOW}▶ Stopping Voice Server...${NC}" -# Check if service is loaded -if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then - # Unload the service - launchctl unload "$PLIST_PATH" 2>/dev/null - - if [ $? -eq 0 ]; then - echo -e "${GREEN}✓ Voice server stopped successfully${NC}" +if [[ "$(uname)" == "Darwin" ]]; then + # ── macOS: LaunchAgent ── + SERVICE_NAME="com.pai.voice-server" + PLIST_PATH="$HOME/Library/LaunchAgents/${SERVICE_NAME}.plist" + + if launchctl list | grep -q "$SERVICE_NAME" 2>/dev/null; then + launchctl unload "$PLIST_PATH" 2>/dev/null + if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ Voice server stopped successfully${NC}" + else + echo -e "${RED}✗ Failed to stop voice server${NC}" + exit 1 + fi else - echo -e "${RED}✗ Failed to stop voice server${NC}" - exit 1 + echo -e "${YELLOW}⚠ Voice server is not running${NC}" fi else - echo -e "${YELLOW}⚠ Voice server is not running${NC}" + # ── Linux: systemd ── + SERVICE_NAME="pai-voice-server" + + if systemctl --user is-active "$SERVICE_NAME" &> /dev/null; then + systemctl --user stop "$SERVICE_NAME" + echo -e "${GREEN}✓ Voice server stopped successfully${NC}" + else + echo -e "${YELLOW}⚠ Voice server is not running${NC}" + fi fi # Kill any remaining processes on port 8888 -if lsof -i :8888 > /dev/null 2>&1; then +if command -v lsof &> /dev/null && lsof -i :8888 > /dev/null 2>&1; then echo -e "${YELLOW}▶ Cleaning up port 8888...${NC}" lsof -ti :8888 | xargs kill -9 2>/dev/null echo -e "${GREEN}✓ Port 8888 cleared${NC}" -fi \ No newline at end of file +elif command -v fuser &> /dev/null && fuser 8888/tcp 2>/dev/null; then + echo -e "${YELLOW}▶ Cleaning up port 8888...${NC}" + fuser -k 8888/tcp 2>/dev/null + echo -e "${GREEN}✓ Port 8888 cleared${NC}" +fi