mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-03-07 18:27:40 +08:00
feat(isolation-bootstrap): cli flag + host/child startup fencing
This commit is contained in:
parent
1f1ec377ce
commit
3cfd5e3311
@ -179,6 +179,8 @@ parser.add_argument("--disable-api-nodes", action="store_true", help="Disable lo
|
||||
|
||||
parser.add_argument("--multi-user", action="store_true", help="Enables per-user storage.")
|
||||
|
||||
parser.add_argument("--use-process-isolation", action="store_true", help="Enable process isolation for custom nodes with pyisolate.yaml manifests.")
|
||||
|
||||
parser.add_argument("--verbose", default='INFO', const='DEBUG', nargs="?", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], help='Set the logging level')
|
||||
parser.add_argument("--log-stdout", action="store_true", help="Send normal process output to stdout instead of stderr (default).")
|
||||
|
||||
|
||||
96
main.py
96
main.py
@ -1,7 +1,21 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
IS_PYISOLATE_CHILD = os.environ.get("PYISOLATE_CHILD") == "1"
|
||||
|
||||
if __name__ == "__main__" and IS_PYISOLATE_CHILD:
|
||||
del os.environ["PYISOLATE_CHILD"]
|
||||
IS_PYISOLATE_CHILD = False
|
||||
|
||||
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
if CURRENT_DIR not in sys.path:
|
||||
sys.path.insert(0, CURRENT_DIR)
|
||||
|
||||
IS_PRIMARY_PROCESS = (not IS_PYISOLATE_CHILD) and __name__ == "__main__"
|
||||
|
||||
import comfy.options
|
||||
comfy.options.enable_args_parsing()
|
||||
|
||||
import os
|
||||
import importlib.util
|
||||
import folder_paths
|
||||
import time
|
||||
@ -9,24 +23,38 @@ from comfy.cli_args import args, enables_dynamic_vram
|
||||
from app.logger import setup_logger
|
||||
from app.assets.scanner import seed_assets
|
||||
import itertools
|
||||
import utils.extra_config
|
||||
import logging
|
||||
import sys
|
||||
from comfy_execution.progress import get_progress_state
|
||||
from comfy_execution.utils import get_executing_context
|
||||
from comfy_api import feature_flags
|
||||
|
||||
import comfy_aimdo.control
|
||||
if '--use-process-isolation' in sys.argv:
|
||||
from comfy.isolation import initialize_proxies
|
||||
initialize_proxies()
|
||||
|
||||
if enables_dynamic_vram():
|
||||
comfy_aimdo.control.init()
|
||||
# Explicitly register the ComfyUI adapter for pyisolate (v1.0 architecture)
|
||||
try:
|
||||
import pyisolate
|
||||
from comfy.isolation.adapter import ComfyUIAdapter
|
||||
pyisolate.register_adapter(ComfyUIAdapter())
|
||||
logging.info("PyIsolate adapter registered: comfyui")
|
||||
except ImportError:
|
||||
logging.warning("PyIsolate not installed or version too old for explicit registration")
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to register PyIsolate adapter: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
#NOTE: These do not do anything on core ComfyUI, they are for custom nodes.
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
if 'PYTORCH_CUDA_ALLOC_CONF' not in os.environ:
|
||||
os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'backend:native'
|
||||
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
from comfy_execution.progress import get_progress_state
|
||||
from comfy_execution.utils import get_executing_context
|
||||
from comfy_api import feature_flags
|
||||
|
||||
if IS_PRIMARY_PROCESS:
|
||||
os.environ['HF_HUB_DISABLE_TELEMETRY'] = '1'
|
||||
os.environ['DO_NOT_TRACK'] = '1'
|
||||
|
||||
setup_logger(log_level=args.verbose, use_stdout=args.log_stdout)
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
setup_logger(log_level=args.verbose, use_stdout=args.log_stdout)
|
||||
|
||||
if os.name == "nt":
|
||||
os.environ['MIMALLOC_PURGE_DELAY'] = '0'
|
||||
@ -78,14 +106,15 @@ if args.enable_manager:
|
||||
|
||||
|
||||
def apply_custom_paths():
|
||||
from utils import extra_config # Deferred import - spawn re-runs main.py
|
||||
# extra model paths
|
||||
extra_model_paths_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "extra_model_paths.yaml")
|
||||
if os.path.isfile(extra_model_paths_config_path):
|
||||
utils.extra_config.load_extra_path_config(extra_model_paths_config_path)
|
||||
extra_config.load_extra_path_config(extra_model_paths_config_path)
|
||||
|
||||
if args.extra_model_paths_config:
|
||||
for config_path in itertools.chain(*args.extra_model_paths_config):
|
||||
utils.extra_config.load_extra_path_config(config_path)
|
||||
extra_config.load_extra_path_config(config_path)
|
||||
|
||||
# --output-directory, --input-directory, --user-directory
|
||||
if args.output_directory:
|
||||
@ -158,14 +187,16 @@ def execute_prestartup_script():
|
||||
else:
|
||||
import_message = " (PRESTARTUP FAILED)"
|
||||
logging.info("{:6.1f} seconds{}: {}".format(n[0], import_message, n[1]))
|
||||
logging.info("")
|
||||
logging.info("")
|
||||
|
||||
apply_custom_paths()
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
apply_custom_paths()
|
||||
|
||||
if args.enable_manager:
|
||||
if args.enable_manager and not IS_PYISOLATE_CHILD:
|
||||
comfyui_manager.prestartup()
|
||||
|
||||
execute_prestartup_script()
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
execute_prestartup_script()
|
||||
|
||||
|
||||
# Main code
|
||||
@ -177,17 +208,22 @@ import gc
|
||||
if 'torch' in sys.modules:
|
||||
logging.warning("WARNING: Potential Error in code: Torch already imported, torch should never be imported before this point.")
|
||||
|
||||
import comfy_aimdo.control
|
||||
|
||||
if enables_dynamic_vram():
|
||||
comfy_aimdo.control.init()
|
||||
|
||||
import comfy.utils
|
||||
|
||||
import execution
|
||||
import server
|
||||
from protocol import BinaryEventTypes
|
||||
import nodes
|
||||
import comfy.model_management
|
||||
import comfyui_version
|
||||
import app.logger
|
||||
import hook_breaker_ac10a0
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
import execution
|
||||
import server
|
||||
from protocol import BinaryEventTypes
|
||||
import nodes
|
||||
import comfy.model_management
|
||||
import comfyui_version
|
||||
import app.logger
|
||||
import hook_breaker_ac10a0
|
||||
|
||||
import comfy.memory_management
|
||||
import comfy.model_patcher
|
||||
@ -384,6 +420,10 @@ def start_comfyui(asyncio_loop=None):
|
||||
asyncio.set_event_loop(asyncio_loop)
|
||||
prompt_server = server.PromptServer(asyncio_loop)
|
||||
|
||||
if args.use_process_isolation:
|
||||
from comfy.isolation import start_isolation_loading_early
|
||||
start_isolation_loading_early(asyncio_loop)
|
||||
|
||||
if args.enable_manager and not args.disable_manager_ui:
|
||||
comfyui_manager.start()
|
||||
|
||||
@ -428,7 +468,9 @@ def start_comfyui(asyncio_loop=None):
|
||||
if __name__ == "__main__":
|
||||
# Running directly, just start ComfyUI.
|
||||
logging.info("Python version: {}".format(sys.version))
|
||||
logging.info("ComfyUI version: {}".format(comfyui_version.__version__))
|
||||
if not IS_PYISOLATE_CHILD:
|
||||
import comfyui_version
|
||||
logging.info("ComfyUI version: {}".format(comfyui_version.__version__))
|
||||
|
||||
if sys.version_info.major == 3 and sys.version_info.minor < 10:
|
||||
logging.warning("WARNING: You are using a python version older than 3.10, please upgrade to a newer one. 3.12 and above is recommended.")
|
||||
|
||||
43
nodes.py
43
nodes.py
@ -1925,6 +1925,7 @@ class ImageInvert:
|
||||
|
||||
class ImageBatch:
|
||||
SEARCH_ALIASES = ["combine images", "merge images", "stack images"]
|
||||
ESSENTIALS_CATEGORY = "Image Tools"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@ -2306,6 +2307,27 @@ async def init_external_custom_nodes():
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
whitelist = set()
|
||||
isolated_module_paths = set()
|
||||
if args.use_process_isolation:
|
||||
from pathlib import Path
|
||||
from comfy.isolation import await_isolation_loading, get_claimed_paths
|
||||
from comfy.isolation.host_policy import load_host_policy
|
||||
|
||||
# Load Global Host Policy
|
||||
host_policy = load_host_policy(Path(folder_paths.base_path))
|
||||
whitelist_dict = host_policy.get("whitelist", {})
|
||||
# Normalize whitelist keys to lowercase for case-insensitive matching
|
||||
# (matches ComfyUI-Manager's normalization: project.name.strip().lower())
|
||||
whitelist = set(k.strip().lower() for k in whitelist_dict.keys())
|
||||
logging.info(f"][ Loaded Whitelist: {len(whitelist)} nodes allowed.")
|
||||
|
||||
isolated_specs = await await_isolation_loading()
|
||||
for spec in isolated_specs:
|
||||
NODE_CLASS_MAPPINGS.setdefault(spec.node_name, spec.stub_class)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.setdefault(spec.node_name, spec.display_name)
|
||||
isolated_module_paths = get_claimed_paths()
|
||||
|
||||
base_node_names = set(NODE_CLASS_MAPPINGS.keys())
|
||||
node_paths = folder_paths.get_folder_paths("custom_nodes")
|
||||
node_import_times = []
|
||||
@ -2329,6 +2351,16 @@ async def init_external_custom_nodes():
|
||||
logging.info(f"Blocked by policy: {module_path}")
|
||||
continue
|
||||
|
||||
if args.use_process_isolation:
|
||||
if Path(module_path).resolve() in isolated_module_paths:
|
||||
continue
|
||||
|
||||
# Tri-State Enforcement: If not Isolated (checked above), MUST be Whitelisted.
|
||||
# Normalize to lowercase for case-insensitive matching (matches ComfyUI-Manager)
|
||||
if possible_module.strip().lower() not in whitelist:
|
||||
logging.warning(f"][ REJECTED: Node '{possible_module}' is blocked by security policy (not whitelisted/isolated).")
|
||||
continue
|
||||
|
||||
time_before = time.perf_counter()
|
||||
success = await load_custom_node(module_path, base_node_names, module_parent="custom_nodes")
|
||||
node_import_times.append((time.perf_counter() - time_before, module_path, success))
|
||||
@ -2343,6 +2375,14 @@ async def init_external_custom_nodes():
|
||||
logging.info("{:6.1f} seconds{}: {}".format(n[0], import_message, n[1]))
|
||||
logging.info("")
|
||||
|
||||
if args.use_process_isolation:
|
||||
from comfy.isolation import isolated_node_timings
|
||||
if isolated_node_timings:
|
||||
logging.info("\nImport times for isolated custom nodes:")
|
||||
for timing, path, count in sorted(isolated_node_timings):
|
||||
logging.info("{:6.1f} seconds: {} ({})".format(timing, path, count))
|
||||
logging.info("")
|
||||
|
||||
async def init_builtin_extra_nodes():
|
||||
"""
|
||||
Initializes the built-in extra nodes in ComfyUI.
|
||||
@ -2435,7 +2475,6 @@ async def init_builtin_extra_nodes():
|
||||
"nodes_audio_encoder.py",
|
||||
"nodes_rope.py",
|
||||
"nodes_logic.py",
|
||||
"nodes_resolution.py",
|
||||
"nodes_nop.py",
|
||||
"nodes_kandinsky5.py",
|
||||
"nodes_wanmove.py",
|
||||
@ -2443,12 +2482,10 @@ async def init_builtin_extra_nodes():
|
||||
"nodes_zimage.py",
|
||||
"nodes_glsl.py",
|
||||
"nodes_lora_debug.py",
|
||||
"nodes_textgen.py",
|
||||
"nodes_color.py",
|
||||
"nodes_toolkit.py",
|
||||
"nodes_replacements.py",
|
||||
"nodes_nag.py",
|
||||
"nodes_sdpose.py",
|
||||
]
|
||||
|
||||
import_failed = []
|
||||
|
||||
@ -3,7 +3,6 @@ import sys
|
||||
import asyncio
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import nodes
|
||||
import folder_paths
|
||||
import execution
|
||||
@ -196,6 +195,8 @@ def create_block_external_middleware():
|
||||
class PromptServer():
|
||||
def __init__(self, loop):
|
||||
PromptServer.instance = self
|
||||
if loop is None:
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/javascript; charset=utf-8', '.js')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user