#!/usr/bin/env python3 """Inspect or switch VoiceAgent's live input/output device through its local UI API.""" import argparse import json import sys from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen BASE_URL = "http://127.0.0.1:8888" def request(path, method="GET", payload=None): data = json.dumps(payload).encode() if payload is not None else None req = Request(BASE_URL + path, data=data, method=method) if data is not None: req.add_header("Content-Type", "application/json") try: with urlopen(req, timeout=5) as response: return json.load(response) except HTTPError as exc: try: message = json.load(exc) except Exception: message = {"error": exc.read().decode("utf-8", "replace")} raise RuntimeError(message.get("error", str(exc))) from exc except URLError as exc: raise RuntimeError(f"VoiceAgent is not reachable at {BASE_URL}: {exc.reason}") from exc def main() -> int: parser = argparse.ArgumentParser(description=__doc__) commands = parser.add_subparsers(dest="command", required=True) commands.add_parser("list", help="List selectable input/output devices.") select = commands.add_parser("set", help="Switch one live route or follow the macOS default.") select.add_argument("direction", choices=("input", "output")) select.add_argument("device", help="Unique device-name substring, PortAudio index, or 'default'.") args = parser.parse_args() try: if args.command == "list": result = request("/api/audio-devices") for device in result["devices"]: kinds = "/".join(kind for kind in ("input" if device["input"] else "", "output" if device["output"] else "") if kind) print(f"[{device['id']}] {device['name']} ({kinds})") else: result = request("/api/audio-device", "POST", {"direction": args.direction, "device": args.device}) mode = "following macOS default" if result["following_default"] else f"pinned to [{result['device']}]" print(f"{args.direction} changed to {result['name']} ({mode})") return 0 except (KeyError, RuntimeError) as exc: print(f"audio-tool: {exc}", file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())