from typing import Any from yuxi.channel.runtime.manager import gateway from yuxi.channel.security.pairing import PairingManager from yuxi.repositories.channel_binding_repo import ChannelBindingRepository from yuxi.repositories.channel_config_repo import ChannelConfigRepository from yuxi.repositories.channel_pairing_repo import ChannelPairingRecordRepository from yuxi.repositories.channel_thread_mapping_repo import ChannelThreadMappingRepository from yuxi.repositories.channel_user_mapping_repo import ChannelUserMappingRepository _PAIRING_PEER_ID_PREFIX = "admin:pairing" _config_repo = ChannelConfigRepository() _binding_repo = ChannelBindingRepository() _pairing_repo = ChannelPairingRecordRepository() _user_mapping_repo = ChannelUserMappingRepository() _thread_mapping_repo = ChannelThreadMappingRepository() async def list_channel_configs( channel_type: str | None = None, enabled: bool | None = None, ) -> list[dict[str, Any]]: if channel_type and enabled is not None: configs = await _config_repo.list_enabled(channel_type) elif enabled is not None: configs = await _config_repo.list_enabled() elif channel_type: configs = await _config_repo.list_by_type(channel_type) else: configs = await _config_repo.list_all() return [c.to_dict(mask_secrets=True) for c in configs] async def get_channel_config(config_id: str) -> dict[str, Any] | None: config = await _config_repo.get_by_id(config_id) return config.to_dict(mask_secrets=True) if config else None async def create_channel_config(data: dict[str, Any]) -> dict[str, Any]: config = await _config_repo.create(data) return config.to_dict(mask_secrets=True) async def update_channel_config(config_id: str, data: dict[str, Any]) -> dict[str, Any] | None: config = await _config_repo.update(config_id, data) return config.to_dict(mask_secrets=True) if config else None async def delete_channel_config(config_id: str) -> bool: return await _config_repo.delete(config_id) async def list_channel_bindings(agent_config_id: int | None = None) -> list[dict[str, Any]]: if agent_config_id is not None: bindings = await _binding_repo.list_by_agent(agent_config_id) else: bindings = await _binding_repo.list_all() return [b.to_dict() for b in bindings] async def get_channel_binding(binding_id: str) -> dict[str, Any] | None: binding = await _binding_repo.get_by_id(binding_id) return binding.to_dict() if binding else None async def create_channel_binding(data: dict[str, Any]) -> dict[str, Any]: binding = await _binding_repo.create(data) return binding.to_dict() async def update_channel_binding(binding_id: str, data: dict[str, Any]) -> dict[str, Any] | None: binding = await _binding_repo.update(binding_id, data) return binding.to_dict() if binding else None async def delete_channel_binding(binding_id: str) -> bool: return await _binding_repo.delete(binding_id) async def list_channel_bindings_by_type(channel_type: str) -> list[dict[str, Any]]: bindings = await _binding_repo.list_by_channel_type(channel_type) return [b.to_dict() for b in bindings] async def list_channel_pairing_records(status: str | None = None) -> list[dict[str, Any]]: records = await _pairing_repo.list_all(status=status) return [r.to_dict() for r in records] async def approve_channel_pairing(record_id: str) -> dict[str, Any] | None: record = await _pairing_repo.approve(record_id) return record.to_dict() if record else None async def reject_channel_pairing(record_id: str) -> dict[str, Any] | None: record = await _pairing_repo.reject(record_id) return record.to_dict() if record else None async def get_channel_pairing_record(record_id: str) -> dict[str, Any] | None: record = await _pairing_repo.get_by_id(record_id) return record.to_dict() if record else None async def delete_channel_pairing_record(record_id: str) -> bool: return await _pairing_repo.delete(record_id) async def generate_agent_pairing_code(agent_config_id: int) -> dict[str, Any]: bindings = await _binding_repo.list_by_agent(agent_config_id) if not bindings: raise ValueError(f"Agent config {agent_config_id} has no channel bindings") binding = bindings[0] mgr = PairingManager() peer_id = f"{_PAIRING_PEER_ID_PREFIX}:{agent_config_id}" req = await mgr.upsert_code( channel_type=binding.channel_type, peer_id=peer_id, account_id=binding.account_id or "default", ) return { "code": req.code, "channel_type": binding.channel_type, "account_id": binding.account_id or "default", "agent_config_id": agent_config_id, "record_id": req._record_id, } async def list_user_mappings() -> list[dict[str, Any]]: mappings = await _user_mapping_repo.list_all() return [m.to_dict() for m in mappings] async def get_user_mapping(mapping_id: int) -> dict[str, Any] | None: mapping = await _user_mapping_repo.get_by_id(mapping_id) return mapping.to_dict() if mapping else None async def find_user_mapping_by_channel(channel_id: str, channel_user_id: str) -> dict[str, Any] | None: mapping = await _user_mapping_repo.find_by_channel(channel_id, channel_user_id) return mapping.to_dict() if mapping else None async def resolve_channel_user(channel_id: str, channel_user_id: str) -> dict[str, Any]: mapping = await _user_mapping_repo.resolve_user(channel_id, channel_user_id) return mapping.to_dict() async def find_user_mappings_by_internal_user(internal_user_id: str) -> list[dict[str, Any]]: mappings = await _user_mapping_repo.find_by_internal_user(internal_user_id) return [m.to_dict() for m in mappings] async def create_user_mapping(data: dict[str, Any]) -> dict[str, Any]: mapping = await _user_mapping_repo.create(data) return mapping.to_dict() async def delete_user_mapping(mapping_id: int) -> bool: return await _user_mapping_repo.delete(mapping_id) async def resolve_channel_thread( channel_id: str, channel_chat_id: str, internal_user_id: str, agent_id: str | None = None, ) -> dict[str, Any]: mapping = await _thread_mapping_repo.resolve_thread( channel_id, channel_chat_id, internal_user_id, agent_id, ) return mapping.to_dict() async def get_thread_mapping(mapping_id: int) -> dict[str, Any] | None: mapping = await _thread_mapping_repo.get_by_id(mapping_id) return mapping.to_dict() if mapping else None async def find_thread_mapping_by_channel_chat( channel_id: str, channel_chat_id: str, internal_user_id: str, ) -> dict[str, Any] | None: mapping = await _thread_mapping_repo.find_by_channel_chat( channel_id, channel_chat_id, internal_user_id, ) return mapping.to_dict() if mapping else None async def find_thread_mapping_by_thread_id(thread_id: str) -> dict[str, Any] | None: mapping = await _thread_mapping_repo.find_by_thread_id(thread_id) return mapping.to_dict() if mapping else None async def create_thread_mapping(data: dict[str, Any]) -> dict[str, Any]: mapping = await _thread_mapping_repo.create(data) return mapping.to_dict() async def delete_thread_mapping(mapping_id: int) -> bool: return await _thread_mapping_repo.delete(mapping_id) async def get_gateway_health() -> dict[str, Any]: report = gateway.get_health() return { "status": report.status, "channels": report.channels, "summary": report.summary, "boot": gateway.get_boot_status().to_dict(), } async def get_gateway_healthz() -> dict[str, Any]: return {"status": "ok"} async def get_hook_status() -> dict[str, Any]: result = gateway.get_hook_status() return result.to_dict() async def reload_hooks() -> dict[str, Any]: result = gateway.reload_hooks() return result.to_dict() async def install_hook(hook_id: str, hook_md_content: str, target: str = "workspace") -> dict[str, Any]: return gateway.install_hook(hook_id, hook_md_content, target) async def uninstall_hook(source: str, hook_id: str) -> dict[str, Any]: return gateway.uninstall_hook(source, hook_id)