#!/bin/bash # build_app.sh - Build standalone macOS VoiceAgent.app bundle set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$HERE" FORCE=0 for arg in "$@"; do if [ "$arg" = "-f" ] || [ "$arg" = "--force" ]; then FORCE=1 fi done APP_NAME="VoiceAgent" DIST_DIR="$HERE/dist" APP_BUNDLE="$DIST_DIR/$APP_NAME.app" INSTALLED_APP="/Applications/VoiceAgent.app" CONTENTS_DIR="$APP_BUNDLE/Contents" MACOS_DIR="$CONTENTS_DIR/MacOS" RESOURCES_DIR="$CONTENTS_DIR/Resources" SRC_DIR="$RESOURCES_DIR/src" ENV_FILE="$HOME/.voiceagent.env" echo "=== 0. Updating Local Environment Config ($ENV_FILE) ===" mkdir -p "$(dirname "$ENV_FILE")" touch "$ENV_FILE" # Helper to update or append key=val in .env file set_env_var() { local key="$1" local val="$2" if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then # Replace existing line using python helper for clean string replacement python3 -c " import sys, re path, k, v = sys.argv[1], sys.argv[2], sys.argv[3] with open(path, 'r') as f: content = f.read() new_content = re.sub(r'^' + re.escape(k) + r'=.*$', f'{k}={v}', content, flags=re.MULTILINE) with open(path, 'w') as f: f.write(new_content) " "$ENV_FILE" "$key" "$val" else echo "${key}=${val}" >> "$ENV_FILE" fi } set_env_var "VOICEAGENT_DIR" "$HERE" set_env_var "VOICEAGENT_PYTHON" "$HERE/.venv/bin/python" echo "Configured VOICEAGENT_DIR=$HERE in $ENV_FILE" echo "=== 1. Building Swift Helper Binaries ===" bash swift/build.sh "$@" INSTALLED_LAUNCHER="$INSTALLED_APP/Contents/MacOS/VoiceAgent" NEED_REBUILD=0 if [ "$FORCE" -eq 1 ] || [ ! -f "$INSTALLED_LAUNCHER" ]; then NEED_REBUILD=1 elif [ "$HERE/swift/VoiceAgentLauncher.swift" -nt "$INSTALLED_LAUNCHER" ]; then NEED_REBUILD=1 elif [ "$HERE/swift/SpeechHelper.swift" -nt "$INSTALLED_LAUNCHER" ]; then NEED_REBUILD=1 fi if [ "$NEED_REBUILD" -eq 0 ]; then echo "=== [SKIPPED BINARY REBUILD & CODESIGN] ===" echo "Native launcher binary is up to date." echo "Copying updated python resources without modifying code signature..." mkdir -p "$SRC_DIR" cp "$HERE"/*.py "$SRC_DIR/" 2>/dev/null || true cp -R "$HERE/bin" "$SRC_DIR/" 2>/dev/null || true if [ -d "$INSTALLED_APP/Contents/Resources/src" ]; then cp "$HERE"/*.py "$INSTALLED_APP/Contents/Resources/src/" 2>/dev/null || true cp -R "$HERE/bin" "$INSTALLED_APP/Contents/Resources/src/" 2>/dev/null || true fi echo "==========================================================" echo "Python changes deployed cleanly! App binary signature unchanged." echo "macOS permissions preserved for: $INSTALLED_APP" echo "==========================================================" exit 0 fi echo "=== 2. Creating macOS App Bundle Structure ===" rm -rf "$APP_BUNDLE" mkdir -p "$MACOS_DIR" mkdir -p "$RESOURCES_DIR/swift" mkdir -p "$SRC_DIR" echo "=== 3. Compiling Native App Bundle Launcher ===" swiftc -O -parse-as-library "$HERE/swift/VoiceAgentLauncher.swift" -o "$MACOS_DIR/VoiceAgent" chmod +x "$MACOS_DIR/VoiceAgent" echo "=== 4. Copying Source Files, Swift Helpers, and Assets ===" cp "$HERE/swift/speech-helper" "$RESOURCES_DIR/swift/" cp "$HERE/swift/llm-helper" "$RESOURCES_DIR/swift/" chmod +x "$RESOURCES_DIR/swift/speech-helper" "$RESOURCES_DIR/swift/llm-helper" # Copy python files and bin scripts to bundle resources cp "$HERE"/*.py "$SRC_DIR/" 2>/dev/null || true cp -R "$HERE/bin" "$SRC_DIR/" 2>/dev/null || true if [ -f "$HERE/vocabulary.example.txt" ]; then cp "$HERE/vocabulary.example.txt" "$RESOURCES_DIR/" fi if [ -f "$HERE/corrections.example.txt" ]; then cp "$HERE/corrections.example.txt" "$RESOURCES_DIR/" fi echo "=== 5. Generating Info.plist with Entitlements ===" cat << 'EOF' > "$CONTENTS_DIR/Info.plist" CFBundleExecutable VoiceAgent CFBundleIdentifier com.voiceagent.mac CFBundleName VoiceAgent CFBundleDisplayName VoiceAgent CFBundlePackageType APPL CFBundleShortVersionString 1.0.0 CFBundleVersion 1.0.0 LSMinimumSystemVersion 14.0 NSMicrophoneUsageDescription VoiceAgent requires access to your microphone to listen to your voice commands. NSSpeechRecognitionUsageDescription VoiceAgent uses on-device speech recognition to process your spoken input. NSHighResolutionCapable EOF echo "=== 6. Code-signing App Bundle ===" codesign -s - --deep --force "$APP_BUNDLE" echo "=== 7. Installing to /Applications ===" rm -rf "$INSTALLED_APP" cp -R "$APP_BUNDLE" /Applications/ codesign -s - --deep --force "$INSTALLED_APP" echo "==========================================================" echo "Successfully built and installed VoiceAgent.app to:" echo "1. $INSTALLED_APP" echo "2. $APP_BUNDLE" echo "=========================================================="