from __future__ import annotations import json import re from pathlib import Path __version__ = "1.0.11" _PLUGIN_DIR = Path(__file__).resolve().parent _CUSTOM_NODES_DIR = _PLUGIN_DIR.parent _VERSION_RE = re.compile(r'__version__\s*=\s*["\']([^"\']+)["\']') _PYPROJECT_VERSION_RE = re.compile(r'^\s*version\s*=\s*["\']([^"\']+)["\']', re.MULTILINE) def _version_from_init_py(init_path: Path) -> str | None: if not init_path.is_file(): return None try: text = init_path.read_text(encoding="utf-8", errors="replace") m = _VERSION_RE.search(text) return m.group(1).strip() if m else None except Exception: return None def _version_from_metadata(package_name: str) -> str | None: try: from importlib.metadata import version for name in (package_name, package_name.replace("_", "-"), package_name.replace("-", "_")): try: return version(name) except Exception: continue except Exception: pass return None def _version_from_pyproject_toml(package_dir: Path) -> str | None: path = package_dir / "pyproject.toml" if not path.is_file(): return None try: text = path.read_text(encoding="utf-8", errors="replace") for m in _PYPROJECT_VERSION_RE.finditer(text): return m.group(1).strip() except Exception: pass return None def _version_from_package_json(package_dir: Path) -> str | None: path = package_dir / "package.json" if not path.is_file(): return None try: data = json.loads(path.read_text(encoding="utf-8", errors="replace")) if isinstance(data.get("version"), str) and data["version"].strip(): return data["version"].strip() except Exception: pass return None def _version_from_version_file(package_dir: Path) -> str | None: for name in ("version.py", "version.json"): path = package_dir / name if not path.is_file(): continue try: text = path.read_text(encoding="utf-8", errors="replace") if name.endswith(".json"): data = json.loads(text) if isinstance(data.get("version"), str) and data["version"].strip(): return data["version"].strip() else: m = _VERSION_RE.search(text) if m: return m.group(1).strip() except Exception: pass return None def _is_git_repo(package_dir: Path) -> bool: return (package_dir / ".git").exists() def get_installed_modules() -> list[dict]: result: list[dict] = [] if not _CUSTOM_NODES_DIR.is_dir(): return result for path in sorted(_CUSTOM_NODES_DIR.iterdir()): if not path.is_dir(): continue init_py = path / "__init__.py" if not init_py.is_file(): continue name = path.name version = ( _version_from_init_py(init_py) or _version_from_metadata(name) or _version_from_pyproject_toml(path) or _version_from_package_json(path) or _version_from_version_file(path) ) if version is None and _is_git_repo(path): version = "nightly" result.append({"name": name, "version": version}) return result def get_version_info_payload() -> dict: return { "workflowui_plugin_version": __version__, "installed_custom_nodes": get_installed_modules(), }