"""Shared utilities for Apple MCP server.""" import json import subprocess from datetime import datetime, date from pathlib import Path from typing import Any CONFIG_PATH = Path(__file__).parent / "config.json" def load_config() -> dict: """Load the app permissions config.""" with open(CONFIG_PATH) as f: return json.load(f) def is_app_enabled(app_name: str) -> bool: """Check if an app is enabled in config.""" cfg = load_config() app = cfg.get("apps", {}).get(app_name, {}) return app.get("enabled", False) def is_write_allowed(app_name: str) -> bool: """Check if write access is allowed for an app.""" cfg = load_config() app = cfg.get("apps", {}).get(app_name, {}) return app.get("enabled", False) and app.get("mode") == "read-write" def run_applescript(script: str, timeout: int = 30) -> str: """Run an AppleScript and return stdout. Raises RuntimeError on failure. """ result = subprocess.run( ["osascript", "-e", script], capture_output=True, text=True, timeout=timeout, ) if result.returncode != 0: raise RuntimeError(f"AppleScript error: {result.stderr.strip()}") return result.stdout.strip() def run_jxa(script: str, timeout: int = 30) -> str: """Run a JXA (JavaScript for Automation) script and return stdout.""" result = subprocess.run( ["osascript", "-l", "JavaScript", "-e", script], capture_output=True, text=True, timeout=timeout, ) if result.returncode != 0: raise RuntimeError(f"JXA error: {result.stderr.strip()}") return result.stdout.strip() def parse_applescript_date(date_str: str) -> str | None: """Try to parse a macOS AppleScript date string into ISO format.""" if not date_str or date_str == "missing value": return None formats = [ "%A, %B %d, %Y at %I:%M:%S %p", "%A, %B %d, %Y at %H:%M:%S", "%m/%d/%Y %I:%M:%S %p", "%m/%d/%Y %H:%M:%S", "%Y-%m-%d %H:%M:%S %z", ] for fmt in formats: try: dt = datetime.strptime(date_str, fmt) return dt.isoformat() except ValueError: continue return date_str def format_date_for_applescript(iso_date: str) -> str: """Convert ISO date string to AppleScript date format.""" dt = datetime.fromisoformat(iso_date) return dt.strftime("%m/%d/%Y %I:%M:%S %p") def today_str() -> str: """Return today's date as YYYY-MM-DD.""" return date.today().isoformat() def safe_applescript_string(s: str) -> str: """Escape a string for safe embedding in AppleScript.""" return s.replace("\\", "\\\\").replace('"', '\\"')