""" modules/auto_installer.py — F.R.I.D.A.Y OMEGA Auto-Install System Automatically installs & configures ALL dependencies with ONE click. """ import os import subprocess import sys import threading import time from pathlib import Path from typing import Optional from config import BASE_DIR, DATA_DIR LOG_DIR = Path(DATA_DIR) / "runtime_logs" LOG_PATH = LOG_DIR / "auto_install.log" # Package categories CORE_PACKAGES = [ "psutil>=5.9.0", "google-generativeai>=0.5.0", "requests>=2.31.0", "keyboard>=0.13.5", "pywin32>=306", "websockets>=12.0", "cryptography>=41.0.0", ] VOICE_PACKAGES = [ "edge-tts>=6.1.0", "SpeechRecognition>=3.10.0", "pygame>=2.5.0", ] AI_PACKAGES = [ "openai>=1.0.0", "anthropic>=0.25.0", "numpy>=1.24.0", "pydantic>=2.0.0", ] AR_PACKAGES = [ "mediapipe>=0.10.0", "opencv-python>=4.8.0", ] OPTIONAL_PACKAGES = [ "pyqt5>=5.15.0", "pillow>=10.0.0", "pynvml>=11.5.0", "mss>=9.0.0", "aiohttp>=3.9.0", "matplotlib>=3.8.0", "python-dateutil>=2.8.0", "yfinance>=0.2.0", "pyowm>=3.3.0", "pytesseract>=0.3.10", "pypdf>=3.17.0", ] def _log(msg: str) -> None: """Log to file.""" LOG_DIR.mkdir(parents=True, exist_ok=True) ts = time.strftime("%Y-%m-%d %H:%M:%S") with open(LOG_PATH, "a", encoding="utf-8") as f: f.write(f"[{ts}] {msg}\n") def _pip_install(packages: list[str], spoke_callback=None) -> bool: """Install packages via pip with hidden window.""" if not packages: return True deps = " ".join(packages) _log(f"Installing: {deps}") try: pyexe = sys.executable args = [pyexe, "-m", "pip", "install", "--upgrade", deps] kwargs = {"capture_output": True, "text": True, "timeout": 300} if os.name == "nt": from modules.process_utils import windows_creationflags_no_window kwargs["creationflags"] = windows_creationflags_no_window() result = subprocess.run(args, **kwargs) if result.returncode == 0: _log(f"SUCCESS: {deps}") if spoke_callback: spoke_callback(f"Installed {len(packages)} packages.") return True else: _log(f"FAILED: {result.stderr}") if spoke_callback: spoke_callback(f"Some packages failed to install.") return False except Exception as e: _log(f"ERROR: {e}") return False def _check_package(name: str) -> bool: """Check if package is installed.""" try: __import__(name.split(">")[0].split("=")[0].replace("-", "_")) return True except Exception: return False def check_missing_packages() -> dict[str, list[str]]: """Check which packages are missing.""" missing = {"core": [], "voice": [], "ai": [], "ar": [], "optional": []} for pkg in CORE_PACKAGES: if not _check_package(pkg): missing["core"].append(pkg) for pkg in VOICE_PACKAGES: if not _check_package(pkg): missing["voice"].append(pkg) for pkg in AI_PACKAGES: if not _check_package(pkg): missing["ai"].append(pkg) for pkg in AR_PACKAGES: if not _check_package(pkg): missing["ar"].append(pkg) for pkg in OPTIONAL_PACKAGES: if not _check_package(pkg): missing["optional"].append(pkg) return missing def install_core(speak_fn=None) -> bool: """Install core packages.""" missing = check_missing_packages() all_pkgs = missing["core"] if missing["voice"]: all_pkgs.extend(missing["voice"]) if not all_pkgs: if speak_fn: speak_fn("Core packages already installed.") return True if speak_fn: speak_fn(f"Installing {len(all_pkgs)} core packages...") return _pip_install(all_pkgs, speak_fn) def install_ai(speak_fn=None) -> bool: """Install AI packages.""" missing = check_missing_packages() all_pkgs = missing["ai"] if not all_pkgs: if speak_fn: speak_fn("AI packages already installed.") return True if speak_fn: speak_fn(f"Installing {len(all_pkgs)} AI packages...") return _pip_install(all_pkgs, speak_fn) def install_ar(speak_fn=None) -> bool: """Install AR packages.""" missing = check_missing_packages() all_pkgs = missing["ar"] if not all_pkgs: if speak_fn: speak_fn("AR packages already installed.") return True if speak_fn: speak_fn(f"Installing {len(all_pkgs)} AR packages...") return _pip_install(all_pkgs, speak_fn) def install_all(speak_fn=None) -> bool: """Install ALL packages - one command.""" missing = check_missing_packages() all_pkgs = ( missing["core"] + missing["voice"] + missing["ai"] + missing["ar"] + missing["optional"] ) if not all_pkgs: if speak_fn: speak_fn("All packages already installed, boss.") return True total = len(all_pkgs) if speak_fn: speak_fn(f"Installing {total} packages... this may take a moment.") success = _pip_install(all_pkgs, speak_fn) if speak_fn: if success: speak_fn(f"All {total} packages installed successfully!") else: speak_fn("Installation complete. Some optional packages may have failed.") return success def install_system_binary(name: str, speak_fn=None) -> bool: """Install system binaries (Tailscale, NirCmd, etc).""" if name == "tailscale": try: from modules.prereq_installer import install_tailscale result = install_tailscale(auto_login=True) if speak_fn and result: speak_fn("Tailscale installed and configured!") return result except Exception as e: _log(f"Tailscale error: {e}") return False elif name == "nircmd": try: from modules.prereq_installer import install_nircmd result = install_nircmd() if speak_fn and result: speak_fn("NirCmd installed!") return result except Exception as e: _log(f"NirCmd error: {e}") return False return False def get_install_status() -> dict: """Get current installation status.""" missing = check_missing_packages() try: from modules.prerequisites import is_tailscale_present, is_nircmd_present tailscale = is_tailscale_present() nircmd = is_nircmd_present() except Exception: tailscale = nircmd = False return { "core_ready": len(missing["core"]) == 0, "voice_ready": len(missing["voice"]) == 0, "ai_ready": len(missing["ai"]) == 0, "ar_ready": len(missing["ar"]) == 0, "all_ready": all(len(v) == 0 for v in missing.values()), "missing": missing, "tailscale": tailscale, "nircmd": nircmd, } def run_full_install(speak_fn=None, background: bool = True) -> None: """Run full installation in background or foreground.""" def _install(): _log("Starting full auto-install...") # Install Python packages install_all(speak_fn) # Install system binaries install_system_binary("tailscale", speak_fn) install_system_binary("nircmd", speak_fn) # Mark complete try: from modules.reminders import add_important_memory add_important_memory("Auto-install completed successfully", importance=9) except Exception: pass _log("Auto-install complete!") if speak_fn: speak_fn("Full installation complete! FRIDAY is fully configured.") if background: thread = threading.Thread(target=_install, daemon=True) thread.start() else: _install()