from __future__ import annotations import logging from dataclasses import dataclass from yuxi.channel.domain.exception.plugin_exception import InvalidManifestException from yuxi.channel.domain.model.plugin_registry.plugin_manifest import PluginManifest from yuxi.channel.domain.model.shared.channel_capabilities import ChannelCapabilities logger = logging.getLogger(__name__) @dataclass class ManifestParseResult: manifest: PluginManifest raw: dict class ManifestParser: def parse(self, data: dict) -> ManifestParseResult: try: plugin_id = data.get("id", "") name = data.get("name", "") version = data.get("version", "0.0.0") channel_type = data.get("channel_type", "") entry = data.get("entry", "") dependencies = data.get("dependencies", []) config_schema = data.get("config_schema", None) caps_data = data.get("capabilities", {}) capabilities = ChannelCapabilities( media=caps_data.get("media", False), group=caps_data.get("group", False), dm=caps_data.get("dm", True), streaming=caps_data.get("streaming", False), typing=caps_data.get("typing", False), reaction=caps_data.get("reaction", False), thread=caps_data.get("thread", False), max_text_length=caps_data.get("max_text_length", 4096), ) manifest = PluginManifest( id=plugin_id, name=name, version=version, channel_type=channel_type, capabilities=capabilities, config_schema=config_schema, entry=entry, dependencies=dependencies, ) return ManifestParseResult(manifest=manifest, raw=data) except InvalidManifestException: raise except Exception as exc: raise InvalidManifestException(str(exc)) from exc async def parse_file(self, path: str) -> ManifestParseResult: try: import yaml with open(path) as f: data = yaml.safe_load(f) or {} except FileNotFoundError as exc: raise InvalidManifestException(f"manifest file not found: {path}") from exc except Exception as exc: raise InvalidManifestException(f"failed to parse manifest: {exc}") from exc return self.parse(data)