sovereign_startup.py
1 #!/usr/bin/env python3 2 """ 3 Sovereign OS - Master Startup Script 4 5 Runs on machine login to ensure everything is ready: 6 1. Verify daemons are running 7 2. Git pull latest code 8 3. Run hygiene check 9 4. Open Obsidian to LIVE-COMPRESSION.md 10 5. Report status 11 12 Can also be run manually: python3 scripts/sovereign_startup.py 13 """ 14 15 import os 16 import sys 17 import subprocess 18 import json 19 from pathlib import Path 20 from datetime import datetime 21 22 # Configuration 23 SOVEREIGN_OS_ROOT = Path(__file__).parent.parent 24 LOGS_DIR = Path.home() / ".sovereign" / "logs" 25 LOGS_DIR.mkdir(parents=True, exist_ok=True) 26 27 # Daemons to check (name, process patterns to search for) 28 DAEMONS = [ 29 ("mission-control", ["mission_control", "run_mission_control"]), 30 ("first-officer", ["first_officer_local", "run_first_officer"]), 31 ("mesh", ["sos.js", "sovereign-mesh"]), 32 ] 33 34 LAUNCH_AGENTS = [ 35 "com.sovereign.mission-control", 36 "com.sovereign.first-officer", 37 "com.sovereign.mesh", 38 ] 39 40 41 def log(msg: str, level: str = "INFO"): 42 """Log with timestamp.""" 43 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 44 print(f"[{timestamp}] [{level}] {msg}") 45 46 47 def check_daemon_running(process_patterns: list) -> bool: 48 """Check if a daemon is running by any of the process patterns.""" 49 for pattern in process_patterns: 50 try: 51 result = subprocess.run( 52 ["pgrep", "-f", pattern], 53 capture_output=True, text=True 54 ) 55 if result.returncode == 0: 56 return True 57 except: 58 pass 59 return False 60 61 62 def start_launch_agent(agent_name: str) -> bool: 63 """Start a LaunchAgent.""" 64 plist_path = Path.home() / "Library" / "LaunchAgents" / f"{agent_name}.plist" 65 if not plist_path.exists(): 66 log(f"LaunchAgent not found: {plist_path}", "WARN") 67 return False 68 69 try: 70 # Unload first (in case it's in a bad state) 71 subprocess.run(["launchctl", "unload", str(plist_path)], 72 capture_output=True, timeout=5) 73 # Load fresh 74 result = subprocess.run(["launchctl", "load", str(plist_path)], 75 capture_output=True, timeout=5) 76 return result.returncode == 0 77 except: 78 return False 79 80 81 def verify_daemons() -> dict: 82 """Verify all daemons are running, start if needed.""" 83 status = {} 84 85 for daemon_name, process_pattern in DAEMONS: 86 if check_daemon_running(process_pattern): 87 status[daemon_name] = "running" 88 log(f"✓ {daemon_name} running") 89 else: 90 log(f"✗ {daemon_name} not running, attempting restart...", "WARN") 91 agent_name = f"com.sovereign.{daemon_name}" 92 if start_launch_agent(agent_name): 93 # Wait and verify 94 import time 95 time.sleep(2) 96 if check_daemon_running(process_pattern): 97 status[daemon_name] = "restarted" 98 log(f"✓ {daemon_name} restarted successfully") 99 else: 100 status[daemon_name] = "failed" 101 log(f"✗ {daemon_name} failed to start", "ERROR") 102 else: 103 status[daemon_name] = "failed" 104 log(f"✗ {daemon_name} LaunchAgent not found", "ERROR") 105 106 return status 107 108 109 def git_sync() -> bool: 110 """Pull latest code from git.""" 111 try: 112 log("Syncing git repository...") 113 result = subprocess.run( 114 ["git", "pull", "--rebase", "--autostash"], 115 cwd=SOVEREIGN_OS_ROOT, 116 capture_output=True, text=True, timeout=30 117 ) 118 if result.returncode == 0: 119 if "Already up to date" in result.stdout: 120 log("✓ Git: Already up to date") 121 else: 122 log(f"✓ Git: Pulled updates") 123 return True 124 else: 125 log(f"✗ Git pull failed: {result.stderr}", "WARN") 126 return False 127 except Exception as e: 128 log(f"✗ Git sync error: {e}", "WARN") 129 return False 130 131 132 def run_hygiene_check() -> dict: 133 """Run phoenix hygiene check.""" 134 try: 135 log("Running hygiene check...") 136 result = subprocess.run( 137 [sys.executable, str(SOVEREIGN_OS_ROOT / "scripts" / "phoenix_hygiene.py"), "--json"], 138 capture_output=True, text=True, timeout=30 139 ) 140 141 if result.returncode == 0: 142 log("✓ All hygiene checks pass") 143 return {"status": "pass", "issues": []} 144 else: 145 issues = json.loads(result.stdout) if result.stdout else [] 146 for issue in issues: 147 level = "ERROR" if issue.get("status") == "critical" else "WARN" 148 log(f"✗ [{issue.get('check')}] {issue.get('message')}", level) 149 return {"status": "issues", "issues": issues} 150 except Exception as e: 151 log(f"✗ Hygiene check error: {e}", "ERROR") 152 return {"status": "error", "error": str(e)} 153 154 155 def open_obsidian(): 156 """Open LIVE-COMPRESSION.md in Obsidian.""" 157 try: 158 log("Opening Obsidian...") 159 subprocess.run([ 160 "open", 161 "obsidian://open?vault=Sovereign_OS&file=sessions/LIVE-COMPRESSION" 162 ], timeout=5) 163 log("✓ Obsidian opened") 164 return True 165 except Exception as e: 166 log(f"✗ Failed to open Obsidian: {e}", "WARN") 167 return False 168 169 170 def write_status_file(status: dict): 171 """Write startup status to file for other tools to read.""" 172 status_file = LOGS_DIR / "startup-status.json" 173 status["timestamp"] = datetime.now().isoformat() 174 with open(status_file, "w") as f: 175 json.dump(status, f, indent=2) 176 log(f"Status written to {status_file}") 177 178 179 def main(): 180 print() 181 print("=" * 60) 182 print(" SOVEREIGN OS - STARTUP CHECK") 183 print("=" * 60) 184 print() 185 186 status = { 187 "daemons": {}, 188 "git_sync": False, 189 "hygiene": {}, 190 "obsidian": False, 191 } 192 193 # 1. Verify daemons 194 status["daemons"] = verify_daemons() 195 print() 196 197 # 2. Git sync 198 status["git_sync"] = git_sync() 199 print() 200 201 # 3. Hygiene check 202 status["hygiene"] = run_hygiene_check() 203 print() 204 205 # 4. Open Obsidian (only if running interactively) 206 if sys.stdout.isatty(): 207 status["obsidian"] = open_obsidian() 208 print() 209 210 # 5. Write status 211 write_status_file(status) 212 213 # Summary 214 print("=" * 60) 215 all_daemons_ok = all(s in ["running", "restarted"] for s in status["daemons"].values()) 216 hygiene_ok = status["hygiene"].get("status") == "pass" 217 218 if all_daemons_ok and hygiene_ok: 219 print(" ✓ SOVEREIGN OS READY") 220 else: 221 print(" ⚠ SOVEREIGN OS - ISSUES DETECTED") 222 if not all_daemons_ok: 223 print(" - Some daemons failed to start") 224 if not hygiene_ok: 225 print(" - Hygiene check found issues") 226 print("=" * 60) 227 print() 228 229 # Exit code 230 if all_daemons_ok and hygiene_ok: 231 sys.exit(0) 232 else: 233 sys.exit(1) 234 235 236 if __name__ == "__main__": 237 main()