"""Unified MuseClient implementation.""" from typing import Dict, Any, Optional, List from .gateway import NoiseGateway from .fallback import GatewayFallback from .queue import QueueSnapshot class MuseClient: """Unified client for the NetVM Muse fleet. Tries the primary Noise WebSocket gateway first, seamlessly falling back to the Chromebox HTTPS gateway on error or timeout. """ def __init__(self, node: str = "muse", gateway_url: Optional[str] = None, token: Optional[str] = None): self.node = node self.gateway = NoiseGateway(node=node) self.fallback = GatewayFallback(endpoint=gateway_url, token=token) def list_threads(self) -> List[Dict[str, Any]]: """List active threads for this node.""" threads, err = self.gateway.list_threads() if threads is not None: return threads # Fallback to HTTPS gateway res = self.fallback.execute_op("chat.sidechats", {"agent": self.node}) if res.get("ok"): try: import json return json.loads(res.get("stdout", "[]")) except Exception: return [{"raw": res.get("stdout", "")}] return [] def send_message(self, message: str, thread_id: Optional[str] = None) -> Dict[str, Any]: """Send message via primary gateway or fallback.""" res, err = self.gateway.send_message(message=message, thread_id=thread_id) if res is not None: return res # Fallback to HTTPS gateway params = {"agent": self.node, "message": message} if thread_id: params["target"] = thread_id return self.fallback.execute_op("chat.send", params) def send_dm(self, to: str, message: str, target: str = "main", tags: Optional[List[str]] = None) -> Dict[str, Any]: """Send signed peer DM via HTTPS gateway.""" params = { "agent": self.node, "to": to, "message": message, "target": target, "tags": tags or [], } return self.fallback.execute_op("dm.send", params) def read_dms(self, target: str = "main", n: int = 5) -> Dict[str, Any]: """Read recent DMs via HTTPS gateway.""" params = { "agent": self.node, "target": target, "n": n, } return self.fallback.execute_op("dm.read", params) def get_queue(self) -> QueueSnapshot: """Query fleet task queue status.""" data = self.fallback.get_queue() return QueueSnapshot.from_dict(data)