import traceback import folder_paths import locale import subprocess # don't remove this import concurrent import nodes import os import sys import threading import re import shutil import git from datetime import datetime from server import PromptServer import manager_core as core import manager_util import cm_global import logging import asyncio import queue import manager_downloader import manager_migration logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})") logging.info("[ComfyUI-Manager] network_mode: " + core.get_config()['network_mode']) comfy_ui_hash = "-" comfyui_tag = None SECURITY_MESSAGE_MIDDLE_OR_BELOW = "ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower." routes = PromptServer.instance.routes def has_per_queue_preview(): """ Check if ComfyUI PR #11261 (per-queue live preview override) is merged Returns: bool: True if ComfyUI has per-queue preview feature """ try: import latent_preview return hasattr(latent_preview, 'set_preview_method') except ImportError: return False # Detect ComfyUI per-queue preview override feature (PR #11261) COMFYUI_HAS_PER_QUEUE_PREVIEW = has_per_queue_preview() def handle_stream(stream, prefix): stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace') for msg in stream: if prefix == '[!]' and ('it/s]' in msg or 's/it]' in msg) and ('%|' in msg or 'it [' in msg): if msg.startswith('100%'): print('\r' + msg, end="", file=sys.stderr), else: print('\r' + msg[:-1], end="", file=sys.stderr), else: if prefix == '[!]': print(prefix, msg, end="", file=sys.stderr) else: print(prefix, msg, end="") from comfy.cli_args import args import latent_preview def is_loopback(address): import ipaddress try: return ipaddress.ip_address(address).is_loopback except ValueError: return False is_local_mode = is_loopback(args.listen) model_dir_name_map = { "checkpoints": "checkpoints", "checkpoint": "checkpoints", "unclip": "checkpoints", "text_encoders": "text_encoders", "clip": "text_encoders", "vae": "vae", "lora": "loras", "t2i-adapter": "controlnet", "t2i-style": "controlnet", "controlnet": "controlnet", "clip_vision": "clip_vision", "gligen": "gligen", "upscale": "upscale_models", "embedding": "embeddings", "embeddings": "embeddings", "unet": "diffusion_models", "diffusion_model": "diffusion_models", } def is_allowed_security_level(level): if level == 'block': return False elif level == 'high': if is_local_mode: return core.get_config()['security_level'] in ['weak', 'normal-'] else: return core.get_config()['security_level'] == 'weak' elif level == 'middle': return core.get_config()['security_level'] in ['weak', 'normal', 'normal-'] else: return True async def get_risky_level(files, pip_packages): json_data1 = await core.get_data_by_mode('local', 'custom-node-list.json') json_data2 = await core.get_data_by_mode('cache', 'custom-node-list.json', channel_url='https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main') all_urls = set() for x in json_data1['custom_nodes'] + json_data2['custom_nodes']: all_urls.update(x.get('files', [])) for x in files: if x not in all_urls: return "high" all_pip_packages = set() for x in json_data1['custom_nodes'] + json_data2['custom_nodes']: all_pip_packages.update(x.get('pip', [])) for p in pip_packages: if p not in all_pip_packages: return "block" return "middle" class ManagerFuncsInComfyUI(core.ManagerFuncs): def get_current_preview_method(self): if args.preview_method == latent_preview.LatentPreviewMethod.Auto: return "auto" elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB: return "latent2rgb" elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD: return "taesd" else: return "none" def run_script(self, cmd, cwd='.'): if len(cmd) > 0 and cmd[0].startswith("#"): logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`") return 0 process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, env=core.get_script_env()) stdout_thread = threading.Thread(target=handle_stream, args=(process.stdout, "")) stderr_thread = threading.Thread(target=handle_stream, args=(process.stderr, "[!]")) stdout_thread.start() stderr_thread.start() stdout_thread.join() stderr_thread.join() return process.wait() core.manager_funcs = ManagerFuncsInComfyUI() sys.path.append('../..') from manager_downloader import download_url, download_url_with_agent core.comfy_path = os.path.dirname(folder_paths.__file__) core.js_path = os.path.join(core.comfy_path, "web", "extensions") local_db_model = os.path.join(manager_util.comfyui_manager_path, "model-list.json") local_db_alter = os.path.join(manager_util.comfyui_manager_path, "alter-list.json") local_db_custom_node_list = os.path.join(manager_util.comfyui_manager_path, "custom-node-list.json") local_db_extension_node_mappings = os.path.join(manager_util.comfyui_manager_path, "extension-node-map.json") def set_preview_method(method): if method == 'auto': args.preview_method = latent_preview.LatentPreviewMethod.Auto elif method == 'latent2rgb': args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB elif method == 'taesd': args.preview_method = latent_preview.LatentPreviewMethod.TAESD else: args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews core.get_config()['preview_method'] = method if COMFYUI_HAS_PER_QUEUE_PREVIEW: logging.info( "[ComfyUI-Manager] ComfyUI per-queue preview override detected (PR #11261). " "Manager's preview method feature is disabled. " "Use ComfyUI's --preview-method CLI option or 'Settings > Execution > Live preview method'." ) elif args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews: set_preview_method(core.get_config()['preview_method']) else: logging.warning( "[ComfyUI-Manager] Since --preview-method is set, " "ComfyUI-Manager's preview method feature will be ignored." ) def set_component_policy(mode): core.get_config()['component_policy'] = mode def set_update_policy(mode): core.get_config()['update_policy'] = mode def set_db_mode(mode): core.get_config()['db_mode'] = mode def print_comfyui_version(): global comfy_ui_hash global comfyui_tag is_detached = False try: repo = git.Repo(os.path.dirname(folder_paths.__file__)) core.comfy_ui_revision = len(list(repo.iter_commits('HEAD'))) comfy_ui_hash = repo.head.commit.hexsha cm_global.variables['comfyui.revision'] = core.comfy_ui_revision core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime cm_global.variables['comfyui.commit_datetime'] = core.comfy_ui_commit_datetime is_detached = repo.head.is_detached current_branch = repo.active_branch.name comfyui_tag = core.get_comfyui_tag() try: if not os.environ.get('__COMFYUI_DESKTOP_VERSION__') and core.comfy_ui_commit_datetime.date() < core.comfy_ui_required_commit_datetime.date(): logging.warning(f"\n\n## [WARN] ComfyUI-Manager: Your ComfyUI version ({core.comfy_ui_revision})[{core.comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version. ##\n\n") except: pass # process on_revision_detected --> if 'cm.on_revision_detected_handler' in cm_global.variables: for k, f in cm_global.variables['cm.on_revision_detected_handler']: try: f(core.comfy_ui_revision) except Exception: logging.error(f"[ERROR] '{k}' on_revision_detected_handler") traceback.print_exc() del cm_global.variables['cm.on_revision_detected_handler'] else: logging.warning("[ComfyUI-Manager] Some features are restricted due to your ComfyUI being outdated.") # <-- if current_branch == "master": if comfyui_tag: logging.info(f"### ComfyUI Version: {comfyui_tag} | Released on '{core.comfy_ui_commit_datetime.date()}'") else: logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'") else: if comfyui_tag: logging.info(f"### ComfyUI Version: {comfyui_tag} on '{current_branch}' | Released on '{core.comfy_ui_commit_datetime.date()}'") else: logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} on '{current_branch}' [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'") except: if is_detached: logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] *DETACHED | Released on '{core.comfy_ui_commit_datetime.date()}'") else: logging.info("### ComfyUI Revision: UNKNOWN (The currently installed ComfyUI is not a Git repository)") print_comfyui_version() core.check_invalid_nodes() def setup_environment(): git_exe = core.get_config()['git_exe'] if git_exe != '': git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=git_exe) setup_environment() # Expand Server api from aiohttp import web import aiohttp import json import zipfile import urllib.request def security_403_response(): """Return appropriate 403 response based on ComfyUI version.""" if not manager_migration.has_system_user_api(): return web.json_response({"error": "comfyui_outdated"}, status=403) return web.json_response({"error": "security_level"}, status=403) # CORS "simple request" Content-Type set per Fetch spec ยง3.2.3. Browsers send #