156 lines
5.2 KiB
Bash
156 lines
5.2 KiB
Bash
#!/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"
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CFBundleExecutable</key>
|
|
<string>VoiceAgent</string>
|
|
<key>CFBundleIdentifier</key>
|
|
<string>com.voiceagent.mac</string>
|
|
<key>CFBundleName</key>
|
|
<string>VoiceAgent</string>
|
|
<key>CFBundleDisplayName</key>
|
|
<string>VoiceAgent</string>
|
|
<key>CFBundlePackageType</key>
|
|
<string>APPL</string>
|
|
<key>CFBundleShortVersionString</key>
|
|
<string>1.0.0</string>
|
|
<key>CFBundleVersion</key>
|
|
<string>1.0.0</string>
|
|
<key>LSMinimumSystemVersion</key>
|
|
<string>14.0</string>
|
|
<key>NSMicrophoneUsageDescription</key>
|
|
<string>VoiceAgent requires access to your microphone to listen to your voice commands.</string>
|
|
<key>NSSpeechRecognitionUsageDescription</key>
|
|
<string>VoiceAgent uses on-device speech recognition to process your spoken input.</string>
|
|
<key>NSHighResolutionCapable</key>
|
|
<true/>
|
|
</dict>
|
|
</plist>
|
|
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 "=========================================================="
|