#!/bin/zsh # ============================================================ # ๐Ÿ”ฑ [Taemini CLI] macOS Local Agent Build (PyInstaller) # ============================================================ # build_agent.bat ์˜ macOS ๋Œ€์‘ํŒ. venv Python์œผ๋กœ PyInstaller --windowed # ์‹คํ–‰ํ•ด dist/TaeminiCLI.app ์„ ๋งŒ๋“ ๋‹ค. ์ฝ”๋“œ์„œ๋ช…/๊ณต์ฆ/dmg๋Š” ์ƒ์œ„ # BUILD_TAEMINI_CLI_MACOS.command ๊ฐ€ ๋‹ด๋‹นํ•œ๋‹ค(์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ์ˆœ์ˆ˜ ๋นŒ๋“œ๋งŒ). # ============================================================ set -uo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" cd "$ROOT" echo "[Taemini CLI] Starting Local Agent Build (macOS)..." # venv Python ๊ฒฝ๋กœ: ์™ธ๋ถ€(BUILD_TAEMINI_CLI_MACOS.command)์—์„œ ์ฃผ์ž…๋˜๊ฑฐ๋‚˜ ์ž๋™ ํƒ์ƒ‰ if [ -z "${SHADOW_VENV_PYTHON:-}" ]; then if [ -x "$ROOT/venv/bin/python3" ]; then SHADOW_VENV_PYTHON="$ROOT/venv/bin/python3" else SHADOW_VENV_PYTHON="python3" fi fi echo "[*] Using Python: $SHADOW_VENV_PYTHON" # 0. Kill existing TaeminiCLI process to prevent file lock echo "[Step 0] Stopping existing TaeminiCLI process..." pkill -f "dist/TaeminiCLI.app/Contents/MacOS/TaeminiCLI" 2>/dev/null || true sleep 1 # 1. Check and install dependencies echo "[Step 1] Installing dependencies..." "$SHADOW_VENV_PYTHON" -m pip install --upgrade pip "$SHADOW_VENV_PYTHON" -m pip install pyinstaller "$SHADOW_VENV_PYTHON" -m pip install -r requirements.txt # 2. Clean previous builds echo "[Step 2] Cleaning previous builds..." rm -rf build dist TaeminiCLI.spec taemini.spec # 3. Package with PyInstaller (macOS: --windowed โ†’ .app ๋ฒˆ๋“ค, GUI/์ฝ˜์†” ์—†์Œ) echo "" echo "[Step 3] Packaging into .app bundle..." "$SHADOW_VENV_PYTHON" -m PyInstaller --name "TaeminiCLI" --windowed -y \ --icon "resources/app_icon.icns" \ --osx-bundle-identifier com.taemingames.shadowbrain \ --add-data "config:config" \ --hidden-import=flask --hidden-import=flask_cors --hidden-import=flask_sock \ --hidden-import=requests --hidden-import=dotenv --hidden-import=psycopg2 \ --hidden-import=google.genai --hidden-import=tkinter --hidden-import=mcp \ --hidden-import=yaml --hidden-import=PIL --hidden-import=psutil \ --hidden-import=pyautogui --hidden-import=pyperclip --hidden-import=boto3 \ --hidden-import=soundfile --hidden-import=cffi --hidden-import=discord \ --hidden-import=sqlite3 --hidden-import=_sqlite3 --hidden-import=obsidian_index \ --hidden-import=rumps \ --collect-submodules=keyring.backends --hidden-import=jwt --hidden-import=cryptography \ --add-data "packs:packs" --hidden-import=addon_packs --hidden-import=mlx_engine_fetch \ `# ๐Ÿงฉ ์ถ”๊ฐ€ ํŒฉ โ€” ๋ฐฐํฌํŒ์— ๋„ฃ์ง€ ์•Š๊ณ  ์ฒ˜์Œ ์“ธ ๋•Œ ๋ฐ›๋Š”๋‹ค(addon_packs.py ยท packs/*.lock, 2026-10-13)` \ `# ๋ณด์ปฌ ๋ถ„๋ฆฌ(Demucs): torchยทsympyยทmpmathยทdemucsยทjuliusยทeinopsยทlameenc (์•ฝ 0.5GB)` \ --exclude-module torch --exclude-module torchaudio --exclude-module torchvision \ --exclude-module demucs --exclude-module julius --exclude-module einops --exclude-module lameenc \ --exclude-module sympy --exclude-module mpmath \ --paths ../jarvis_django --paths ../scripts/tools local_launcher.py echo "" echo "[COMPLETE] Please check the dist/TaeminiCLI.app bundle." # 3.1. Dock/Finder ํ‘œ์‹œ ์ด๋ฆ„์„ "Taemini CLI"(๊ณต๋ฐฑ)๋กœ ์ง€์ •. # ๋ฒˆ๋“ค/๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” TaeminiCLI(๊ณต๋ฐฑ ์—†์Œ)์ง€๋งŒ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์ด๋Š” ์ด๋ฆ„์€ # CFBundleDisplayName ์œผ๋กœ "Taemini CLI"๋ฅผ ๋…ธ์ถœํ•œ๋‹ค. if [ -d "dist/TaeminiCLI.app" ]; then /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName Taemini CLI" \ "dist/TaeminiCLI.app/Contents/Info.plist" 2>/dev/null \ || /usr/libexec/PlistBuddy -c "Add :CFBundleDisplayName string Taemini CLI" \ "dist/TaeminiCLI.app/Contents/Info.plist" echo "[+] CFBundleDisplayName set to 'Taemini CLI'." fi # 3.5. taemini CLI ๋‹จ์ผ ์‹คํ–‰ํŒŒ์ผ ๋นŒ๋“œ โ†’ .app ์•ˆ์— ๋™๋ด‰ # Contents/MacOS/ ์— ๋‘๋Š” ์ด์œ : --deep ์ฝ”๋“œ์„œ๋ช…์ด '์ค‘์ฒฉ ์ฝ”๋“œ'๋กœ ์ธ์‹ํ•ด ํ•จ๊ป˜ # ์„œ๋ช…ยท๊ณต์ฆ๋˜๋Š” ์œ„์น˜๋‹ค (Resources์— ๋‘๋ฉด ์„œ๋ช… ์•ˆ ๋œ Mach-O๋กœ ๊ณต์ฆ ๊ฑฐ๋ถ€๋จ). # PATH ๋“ฑ๋ก์€ ์•ฑ์ด ๋ถ€ํŒ… ์‹œ ์Šค์Šค๋กœ ํ•œ๋‹ค (local_launcher._ensure_taemini_cli_on_path). echo "" echo "[Step 3.5] Building taemini CLI (onefile console)..." "$SHADOW_VENV_PYTHON" -m PyInstaller --name "taemini" --onefile -y \ --hidden-import=prompt_toolkit taemini_cli.py if [ -f "dist/taemini" ] && [ -d "dist/TaeminiCLI.app" ]; then cp "dist/taemini" "dist/TaeminiCLI.app/Contents/MacOS/taemini" chmod +x "dist/TaeminiCLI.app/Contents/MacOS/taemini" echo "[+] taemini CLI bundled into TaeminiCLI.app/Contents/MacOS/" else echo "[!] taemini CLI build failed - app will ship without the CLI." fi # 4. Copy config files to dist (mcp_servers.json, Supertonic ์†Œ์Šค vendoring) # ๐Ÿ”ฑ local_settings.json์€ dist์— ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. ์•ฑ์ด ~/Library/Application Support ์—์„œ # ์ฝ๊ณ  ์ตœ์ดˆ ์‹คํ–‰ ์‹œ ์ž๋™ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ์•ˆ ์ฝํžˆ๋Š” ์‚ฌ๋ณธ์€ ํ˜ผ๋ž€๋งŒ ์ค€๋‹ค. echo "[Step 4] Copying config files to dist..." RESOURCES_DIR="dist/TaeminiCLI.app/Contents/Resources" if [ -d "dist/TaeminiCLI.app" ]; then if [ -f "mcp_servers.json" ]; then cp "mcp_servers.json" "$RESOURCES_DIR/mcp_servers.json" echo "[+] mcp_servers.json copied." fi if [ -d "config" ]; then mkdir -p "$RESOURCES_DIR/config" cp -R config/* "$RESOURCES_DIR/config/" echo "[+] config directory copied." fi # [Supertonic] vendoring๋œ ์†Œ์Šค๋ฅผ ์•ฑ ๋ฆฌ์†Œ์Šค ์•ˆ์œผ๋กœ ๋ณต์‚ฌ # ๋ชจ๋ธ ๊ฐ€์ค‘์น˜(ONNX)๋Š” ์ตœ์ดˆ ์‹คํ–‰ ์‹œ HF์—์„œ ์ž๋™ ๋‹ค์šด๋กœ๋“œ๋จ if [ -d "../vendor/supertonic" ]; then mkdir -p "$RESOURCES_DIR/vendor" cp -R "../vendor/supertonic" "$RESOURCES_DIR/vendor/supertonic" echo "[+] Supertonic source vendored into dist (Resources/vendor/supertonic)" else echo "[!] vendor/supertonic not found - Supertonic engine will be unavailable in this build." fi # 4.5. [MLX Serve] ๐ŸŽ Apple Silicon ๋„ค์ดํ‹ฐ๋ธŒ ์„œ๋น™ ๋Ÿฐํƒ€์ž„ ๋ฒˆ๋“ค๋ง (Zero-Setup Metal ๊ฐ€์†) echo "" echo "[Step 4.5] Bundling MLX Serve native runtime into TaeminiCLI.app..." # ๋ฐฐํฌํŒ์—๋Š” ๋„ฃ์ง€ ์•Š๋Š”๋‹ค โ€” ์•ฑ์ด ์ฒ˜์Œ ์“ธ ๋•Œ ๊ณต์‹ GitHub ๋ฆด๋ฆฌ์Šค์—์„œ ๋ฐ›๋Š”๋‹ค(shadow_brain_core/mlx_engine_fetch.py). # ์˜ˆ์ „์—” ์—ฌ๊ธฐ์„œ ์—”์ง„๊ณผ ๊ฐ™์€ 174MB metallib ๋ฅผ ๋‹ค์„ฏ ๊ตฐ๋ฐ ๋ณต์‚ฌํ•ด ์•ฑ์ด 870MB ๋ถ€ํ’€์—ˆ๋‹ค(2026-10-13 ์ธก์ •). echo "[=] MLX Serve: ๋ฒˆ๋“ค ์•ˆ ํ•จ โ€” ์ฒ˜์Œ ์“ธ ๋•Œ github.com/ddalcu/mlx-serve ์—์„œ ๋ฐ›๋Š”๋‹ค(์•ฝ 79MB)" fi if [ "${1:-}" = "--no-pause" ]; then exit 0 fi read -r "?Press Enter to continue..."