from __future__ import annotations import subprocess import sys from pathlib import Path import plistlib from .paths import app_data_dir TASK_NAME = "ProxyChainManagerAutoStart" LAUNCH_AGENT_ID = "local.proxy-god.mac" def _launch_agent_path() -> Path: p = Path.home() / "Library" / "LaunchAgents" p.mkdir(parents=True, exist_ok=True) return p / f"{LAUNCH_AGENT_ID}.plist" def _mac_program_arguments() -> list[str]: root = Path(__file__).resolve().parent.parent return [str(root / "scripts" / "run_proxy_god.sh")] def _install_launch_agent() -> tuple[bool, str]: plist_path = _launch_agent_path() data = { "Label": LAUNCH_AGENT_ID, "ProgramArguments": _mac_program_arguments(), "RunAtLoad": True, "WorkingDirectory": str(Path(__file__).resolve().parent.parent), "StandardOutPath": str(Path.home() / "Library" / "Logs" / "ProxyGod.out.log"), "StandardErrorPath": str(Path.home() / "Library" / "Logs" / "ProxyGod.err.log"), } try: plist_path.write_bytes(plistlib.dumps(data)) subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True, text=True, timeout=10) r = subprocess.run(["launchctl", "load", str(plist_path)], capture_output=True, text=True, timeout=10) out = (r.stdout or "") + (r.stderr or "") return r.returncode == 0, out.strip() or "LaunchAgent installed" except Exception as e: return False, str(e) def _uninstall_launch_agent() -> tuple[bool, str]: plist_path = _launch_agent_path() try: subprocess.run(["launchctl", "unload", str(plist_path)], capture_output=True, text=True, timeout=10) plist_path.unlink(missing_ok=True) return True, "LaunchAgent removed" except Exception as e: return False, str(e) def _write_launcher_cmd() -> Path: """Create a stable launcher script so Task Scheduler does not need fragile quoting.""" if getattr(sys, "frozen", False): exe = Path(sys.executable).resolve() body = f'@echo off\r\nstart "" /B "{exe}"\r\n' else: py = Path(sys.executable).resolve() if py.name.lower() == "python.exe": cand = py.with_name("pythonw.exe") if cand.is_file(): py = cand root = Path(__file__).resolve().parent.parent run_py = (root / "run.py").resolve() body = f'@echo off\r\n"{py}" "{run_py}"\r\n' p = app_data_dir() / "launch_proxy_chain_manager.cmd" p.write_text(body, encoding="utf-8") return p def install_logon_task() -> tuple[bool, str]: if sys.platform == "darwin": return _install_launch_agent() launcher = _write_launcher_cmd() tr = str(launcher) try: r = subprocess.run( [ "schtasks", "/Create", "/F", "/TN", TASK_NAME, "/TR", tr, "/SC", "ONLOGON", "/RL", "LIMITED", ], capture_output=True, text=True, timeout=60, ) out = (r.stdout or "") + (r.stderr or "") return r.returncode == 0, out.strip() or ("OK" if r.returncode == 0 else "Failed") except Exception as e: return False, str(e) def uninstall_logon_task() -> tuple[bool, str]: if sys.platform == "darwin": return _uninstall_launch_agent() try: r = subprocess.run( ["schtasks", "/Delete", "/F", "/TN", TASK_NAME], capture_output=True, text=True, timeout=60, ) out = (r.stdout or "") + (r.stderr or "") return r.returncode == 0, out.strip() or ("OK" if r.returncode == 0 else "Failed") except Exception as e: return False, str(e) def task_exists() -> bool: if sys.platform == "darwin": return _launch_agent_path().is_file() try: r = subprocess.run( ["schtasks", "/Query", "/TN", TASK_NAME], capture_output=True, text=True, timeout=30, ) return r.returncode == 0 except Exception: return False