Compare commits

...

3 Commits

Author SHA1 Message Date
Евгений
7ac1b18560
Merge 2e9b639081 into 66108ccdbc 2026-04-28 23:46:38 +04:00
Dr.Lt.Data
66108ccdbc update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2026-04-27 05:28:49 +09:00
Dr.Lt.Data
491f847bbc
fix(security): harden CSRF with Content-Type gate and OpenAPI sync (#2819)
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Defense-in-depth over GET→POST alone: reject the three CORS-safelisted
simple-form Content-Types (x-www-form-urlencoded, multipart/form-data,
text/plain) on 5 no-body POST handlers (snapshot/save,
manager/queue/{reset,start,update_comfyui}, manager/reboot) to block
<form method=POST> CSRF that bypasses method-only gating. Convert 10 pure
state-changing endpoints (fetch_updates, queue/{update_all,reset,start,
update_comfyui}, snapshot/{remove,restore,save}, comfyui_switch_version,
reboot) from GET to POST and split 5 config endpoints
(db_mode/preview_method/channel_url_list/policy/{component,update}) into
GET(read) + POST(write, JSON body). Emit the in_progress + done event pair
from the /manager/queue/install sync-enable fast-path so client UI
finalizes (previously only queue/start's empty worker done fired, leaving
item.restart unset and the Enable button visible after a successful enable).
Harden js/custom-nodes-manager.js completion path: await onQueueCompleted
with try/catch (surfaces silent turbogrid stale-item throws), replace the
{}.length == 0 no-op empty guard, set install_context before queue/install
to avoid a sync-completion race, wrap classList/updateCell in try/catch.
Resynchronize openapi.yaml with the converted routes (method → post, query
params → requestBody JSON schema, sibling post on 5 split endpoints).
Update 31 JS fetchApi call sites across 7 files; add
tests/test_csrf_content_type_helper.py covering 5 Content-Type cases via
aiohttp TestClient.

Reported-by: XlabAI Team of Tencent Xuanwu Lab
CVSS: 8.1 (AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H)
2026-04-22 05:04:07 +09:00
17 changed files with 6231 additions and 5659 deletions

View File

@ -1,5 +1,25 @@
{
"custom_nodes": [
{
"author": "Carasibana",
"title": "ComfyUI-SimpleFloatSlider",
"reference": "https://github.com/Carasibana/ComfyUI-SimplayboyleFloatSlider",
"files": [
"https://github.com/Carasibana/ComfyUI-SimpleFloatSlider"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes providing styled draggable float slider widgets"
},
{
"author": "18yz153",
"title": "ComfyUI-Persona-Director",
"reference": "https://github.com/c/ComfyUI-Persona-Director",
"files": [
"https://github.com/18yz153/ComfyUI-Persona-Director"
],
"install_type": "git-clone",
"description": "A visual state machine for consistent character generation. It intelligently maintains character identity, outfits, and locations across multiple generations using LLM-driven state management."
},
{
"author": "Dr.Lt.Data",
"title": "ComfyUI-Manager",
@ -6608,26 +6628,6 @@
"install_type": "git-clone",
"description": "Calculate the execution time of all nodes."
},
{
"author": "Daniel Lewis",
"title": "ComfyUI-Llama",
"reference": "https://github.com/daniel-lewis-ab/ComfyUI-Llama",
"files": [
"https://github.com/daniel-lewis-ab/ComfyUI-Llama"
],
"install_type": "git-clone",
"description": "This is a set of nodes to interact with llama-cpp-python"
},
{
"author": "Daniel Lewis",
"title": "ComfyUI-TTS",
"reference": "https://github.com/daniel-lewis-ab/ComfyUI-TTS",
"files": [
"https://github.com/daniel-lewis-ab/ComfyUI-TTS"
],
"install_type": "git-clone",
"description": "Text To Speech (TTS) for ComfyUI"
},
{
"author": "djbielejeski",
"title": "a-person-mask-generator",
@ -29554,16 +29554,6 @@
"install_type": "git-clone",
"description": "A wild collection of custom nodes for ComfyUI including noise schedulers, samplers, audio preview, latent visualizers, and more — built for maximal creative chaos."
},
{
"author": "shiertier",
"title": "ComfyUI-TeaCache-Lumina",
"reference": "https://github.com/shiertier/ComfyUI-TeaCache-lumina2",
"files": [
"https://github.com/shiertier/ComfyUI-TeaCache-lumina2"
],
"install_type": "git-clone",
"description": "ComfyUI Node Implementation: TeaCache Acceleration Specifically Designed for the Lumina Model"
},
{
"author": "sjh00",
"title": "ComfyUI LoadImageWithInfo",
@ -41752,16 +41742,6 @@
"description": "ComfyUI custom node for flexible image loading with file picker and random folder selection. Features instant preview, auto mode switching, and browser-based file selection.",
"nodename_pattern": "RandomImagePicker"
},
{
"author": "18yz153",
"title": "ComfyUI-Persona-Director",
"reference": "https://github.com/c/ComfyUI-Persona-Director",
"files": [
"https://github.com/18yz153/ComfyUI-Persona-Director"
],
"install_type": "git-clone",
"description": "A visual state machine for consistent character generation. It intelligently maintains character identity, outfits, and locations across multiple generations using LLM-driven state management."
},
{
"author": "BWDrum",
"title": "ComfyUI Random Wildcard Loader",
@ -45056,16 +45036,6 @@
"install_type": "git-clone",
"description": "Professional OpenColorIO / ACES color-management nodes for ComfyUI. Mirrors Nuke's OCIO node set with ACES 2.0, 1.3, and 1.2 support, EXR sequence loading, animated preview, and video export (MP4, WebP, GIF)."
},
{
"author": "Carasibana",
"title": "ComfyUI-SimpleFloatSlider",
"reference": "https://github.com/Carasibana/ComfyUI-SimplayboyleFloatSlider",
"files": [
"https://github.com/Carasibana/ComfyUI-SimpleFloatSlider"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes providing styled draggable float slider widgets"
},
{
"author": "Carasibana",
"title": "ComfyUI-ResizeToCanvasSize",

View File

@ -559,6 +559,9 @@
"1hew_StringCoordinateToBBoxes",
"1hew_StringFilter",
"1hew_StringJoinMulti",
"1hew_StringRatioGemini31FlashImage",
"1hew_StringRatioGpt20Image",
"1hew_StringResolution",
"1hew_TextCustomExtract",
"1hew_TextEncodeQwenImageEdit",
"1hew_TextListToString",
@ -843,6 +846,8 @@
"PDGeminiNanoBanana2",
"PDGeminiProImageGenAPIKey",
"PDGeminiProImageGenAuthToken",
"PDOpenAIGPTImage2APIKey",
"PDOpenAIGPTImage2AuthToken",
"PDOpenAIGPTImageAPIKey",
"PDOpenAIGPTImageAuthToken",
"PD_comfyplus_image"
@ -1213,16 +1218,16 @@
],
"https://github.com/AI-TEC/ComfyUI-AITECCAFE-Toolkit": [
[
"AITEC_ChatGPT_Chat",
"AITEC_Image_Loader",
"AITEC_Image_Moderation",
"AITEC_LLM_Chat",
"AITEC_LLM_Loader",
"AITEC_LLM_Vision",
"AITEC_LLM_Vision_Loader",
"ChatGPTNode",
"CustomStringMerge",
"NSFWChecker",
"OpenAIImageModeration",
"SequentialImageLoader",
"SequentialMediaLoader"
"AITEC_Media_Loader",
"AITEC_NSFW_Checker",
"AITEC_String_Merge"
],
{
"title_aux": "ComfyUI_AITECCAFE_Toolkit"
@ -2203,12 +2208,14 @@
"AcademiaSD_BatchLoader",
"AcademiaSD_Downloader",
"AcademiaSD_Gemini_Node",
"AcademiaSD_LTXVMultiFrames",
"AcademiaSD_MultiLora",
"AcademiaSD_Numeric",
"AcademiaSD_Resolution",
"AcademiaSD_ResolutionDisplay",
"AcademiaSD_SaveAndSend",
"AcademiaSD_SaveCaption",
"AcademiaSD_TimeCalculator",
"AcademiaVisionNode",
"IntegerBypasser",
"LoopCounterToFile",
@ -3076,6 +3083,7 @@
"AGSoftInt",
"AGSoftLoadVideo",
"AGSoftMathExpression",
"AGSoftReferenceToLatent",
"AGSoftSaveImage",
"AGSoftShowAny",
"AGSoftTextConcatenateX2",
@ -3241,7 +3249,7 @@
"ElevenLabsTTSNode",
"Flux2Replicate",
"FluxKontextReplicate",
"GPTImageEditNode",
"GPTImageNode",
"GeminiChatNode",
"GeminiDiarisationAPI",
"GeminiSegmentationNode",
@ -6571,6 +6579,8 @@
"ClipSkipSliderNode",
"CountListNode",
"DA_Base_KSampler",
"DA_BusInNode",
"DA_BusOutNode",
"DA_Enhanced_KSampler",
"DiffusionModelGeneratorNode",
"DiffusionModelSelectorNode",
@ -8659,6 +8669,7 @@
"Prompt Assembler",
"Resize Images To Megapixels",
"Resize To Megapixels",
"Save_as_jpg",
"Text Enable With Prefix",
"Video Params",
"Video Params Expand",
@ -8940,12 +8951,13 @@
"PromptGenerator",
"PromptManager",
"PromptManagerAdvanced",
"WorkflowBridge",
"WorkflowBuilder",
"WorkflowExtractor",
"WorkflowManager",
"WorkflowModelLoader",
"WorkflowRenderer"
"RecipeBuilder",
"RecipeBuilderWan",
"RecipeExtractor",
"RecipeManager",
"RecipeModelLoader",
"RecipeRelay",
"RecipeRenderer"
],
{
"title_aux": "ComfyUI-Prompt-Manager"
@ -9594,6 +9606,7 @@
"ImageRGBA2RGB",
"ReActorBuildFaceModel",
"ReActorFaceBoost",
"ReActorFaceSimilarity",
"ReActorFaceSwap",
"ReActorFaceSwapOpt",
"ReActorImageDublicator",
@ -12192,6 +12205,7 @@
"https://github.com/JosefKuchar/ComfyUI-AdvancedTiling": [
[
"AdvancedTiling",
"AdvancedTilingRay",
"AdvancedTilingSettings",
"AdvancedTilingVAEDecode"
],
@ -13721,13 +13735,7 @@
],
"https://github.com/Lex-DRL/ComfyUI-StringConstructor": [
[
"StringConstructorDictAddAny",
"StringConstructorDictAddString",
"StringConstructorDictExtractString",
"StringConstructorDictFromText",
"StringConstructorDictPreview",
"StringConstructorFormatter",
"StringConstructorValidateKeys"
"_BaseNode"
],
{
"title_aux": "String Constructor (Text-Formatting)"
@ -13838,6 +13846,7 @@
"LTXVExtendSampler",
"LTXVGemmaCLIPModelLoader",
"LTXVGemmaEnhancePrompt",
"LTXVHDRDecodePostprocess",
"LTXVImgToVideoConditionOnly",
"LTXVInContextSampler",
"LTXVInpaintPreprocess",
@ -14908,6 +14917,7 @@
],
"https://github.com/Meisoftcoltd/comfyui-sequential-batcher": [
[
"LazySessionCache",
"MasterSwitch"
],
{
@ -15942,7 +15952,7 @@
],
"https://github.com/Nekodificador/ComfyUI-NKD-Popup-Preview": [
[
"NKD_PopupPreview"
"NKDPopupPreviewNode"
],
{
"title_aux": "NKD Popup Preview"
@ -16772,10 +16782,6 @@
"CRT_DynamicPromptScheduler",
"CRT_FileBatchPromptScheduler",
"CRT_ImageLoaderCrawlBatch",
"CRT_IsolateInput",
"CRT_IsolateInputTBG",
"CRT_IsolateOutput",
"CRT_IsolateOutputTBG",
"CRT_KSamplerBatch",
"CRT_KSamplerBatchAdvanced",
"CRT_LTX23AutoDownload",
@ -18287,6 +18293,7 @@
"Runware Audio Inference Settings",
"Runware Audio Inference Settings Voice Modify",
"Runware Audio Inference Speech",
"Runware Audio Inference Speech Voices",
"Runware Audio Model Search",
"Runware Audio Sections",
"Runware Background Removal",
@ -18337,6 +18344,7 @@
"Runware Pixverse Provider Settings",
"Runware Provider Settings Recraft Color",
"Runware Recraft Provider Settings",
"Runware Reference Audios",
"Runware Reference Images",
"Runware Reference Videos",
"Runware Reference Voices",
@ -18370,10 +18378,15 @@
"Runware Video Inference Inputs",
"Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Images",
"Runware Video Inference Inputs Reference Images Multiple Images Connector",
"Runware Video Inference Inputs Reference Videos",
"Runware Video Inference Inputs Reference Voices",
"Runware Video Inference Outputs",
"Runware Video Inference Settings",
"Runware Video Inference Settings Active Speaker Bounding Boxes",
"Runware Video Inference Settings Active Speaker Detection",
"Runware Video Inference Settings Segments",
"Runware Video Inference Settings TTS",
"Runware Video Inference Speech Input",
"Runware Video Inputs Frame Images",
"Runware Video Inputs References",
@ -18394,6 +18407,7 @@
"RunwareAlibabaProviderSettings",
"RunwareAudioInferenceInputs",
"RunwareAudioInferenceSpeech",
"RunwareAudioInferenceSpeechVoices",
"RunwareAudioInput",
"RunwareAudioSettings",
"RunwareAudioSettingsVoiceModify",
@ -18434,8 +18448,13 @@
"RunwareVideoAdvancedFeatureInputs",
"RunwareVideoBgRemoval",
"RunwareVideoInferenceOutputs",
"RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes",
"RunwareVideoInferenceSettingsActiveSpeakerDetection",
"RunwareVideoInferenceSettingsSegments",
"RunwareVideoInferenceSettingsTts",
"RunwareVideoInferenceSpeechInput",
"RunwareVideoInputsFrameImages",
"RunwareVideoInputsReferenceImagesMultipleImagesConnector",
"RunwareVideoInputsReferences",
"RunwareVideoSettings",
"RunwareVideoUpscaler",
@ -18947,6 +18966,7 @@
"BananaImageGenerationNode",
"ChatGPTImageEditNode",
"ChatGPTImageGenerationNode",
"ChatGPTImageModelGenerationNode",
"ImageToBase64"
],
{
@ -20606,6 +20626,7 @@
"INT8DynamicLoraLoader",
"INT8DynamicLoraStack",
"INT8KernelConfigTuner",
"INT8LazyTorchCompile",
"INT8LoraLoader",
"INT8LoraLoaderStack",
"INT8ModelAdapter",
@ -23812,7 +23833,7 @@
"Qwen3Caption",
"Qwen3CaptionBatch",
"StringToBbox",
"StringToSam3Box"
"StringToComfyBbox"
],
{
"title_aux": "ComfyUI_QwenVL_PromptCaption"
@ -24019,6 +24040,7 @@
"https://github.com/Xyc2016/Comfyui_Fd_Nodes": [
[
"FD_Flux2KleinGenImage",
"FD_GTPImage",
"FD_GeminiImage",
"FD_RemoveWatermark",
"FD_SeedreamImage",
@ -26335,6 +26357,7 @@
[
"FPFoldedPrompts",
"FPTabbedTextArea",
"FPTabedTextPassthrough",
"FPTextAreaPlus",
"FPTextCleanAndSplitt"
],
@ -27179,6 +27202,7 @@
"ColorToGrayscale",
"CompositeMaskAdjuster",
"CompositeMaskExtractor",
"CropFromBboxData",
"CropToMaskWithPadding",
"CurvatureGenerator",
"CustomColorToMask",
@ -27216,6 +27240,7 @@
"LotusNormalProcessor",
"Marigold_AOV_Extractor",
"MaskCompositor",
"MatchSubjectToReferenceImage",
"MicroDetailOverlay",
"MultiTextureBlender",
"MultimattePassApplicator",
@ -27253,6 +27278,7 @@
"ProceduralNoiseGenerator",
"QwenImagePrep",
"SSSMapGenerator",
"ScaleImageToReferenceBbox",
"ScratchesGenerator",
"SeamlessTiling",
"SharpenDepth",
@ -27412,6 +27438,7 @@
"Sage_Ace15AudioEncode",
"Sage_AceAdvOptions",
"Sage_AdvSamplerInfo",
"Sage_AnythingToStr",
"Sage_AverageConditioning",
"Sage_CLIPLoaderFromInfo",
"Sage_CLIPSelector",
@ -30054,7 +30081,7 @@
"Flux2KleinColorAnchor",
"Flux2KleinDetailController",
"Flux2KleinEnhancer",
"Flux2KleinKSampler",
"Flux2KleinKSamplerExperimental",
"Flux2KleinMaskRefController",
"Flux2KleinRefLatentController",
"Flux2KleinRefLatentWeight",
@ -30062,6 +30089,7 @@
"Flux2KleinTextEnhancer",
"Flux2KleinTextRefBalance",
"IdentityFeatureTransfer",
"IdentityFeatureTransferAdvanced",
"IdentityGuidance"
],
{
@ -32646,6 +32674,8 @@
"ByteDance2FirstLastFrameNode",
"ByteDance2ReferenceNode",
"ByteDance2TextToVideoNode",
"ByteDanceCreateImageAsset",
"ByteDanceCreateVideoAsset",
"ByteDanceFirstLastFrameNode",
"ByteDanceImageNode",
"ByteDanceImageReferenceNode",
@ -32763,6 +32793,8 @@
"FluxProExpandNode",
"FluxProFillNode",
"FluxProUltraImageNode",
"FrameInterpolate",
"FrameInterpolationModelLoader",
"FreSca",
"FreeU",
"FreeU_V2",
@ -32869,7 +32901,6 @@
"LTXAVTextEncoderLoader",
"LTXVAddGuide",
"LTXVAudioVAEDecode",
"LTXVAudioVAEEncode",
"LTXVAudioVAELoader",
"LTXVConcatAVLatent",
"LTXVConditioning",
@ -33072,6 +33103,10 @@
"RunwayImageToVideoNodeGen3a",
"RunwayImageToVideoNodeGen4",
"RunwayTextToImageNode",
"SAM3_Detect",
"SAM3_TrackPreview",
"SAM3_TrackToMask",
"SAM3_VideoTrack",
"SDPoseDrawKeypoints",
"SDPoseFaceBBoxes",
"SDPoseKeypointExtractor",
@ -33257,6 +33292,7 @@
"VAESave",
"VPScheduler",
"Veo3FirstLastFrameNode",
"Veo3VideoGenerationNode",
"VeoVideoGenerationNode",
"Video Slice",
"VideoLinearCFGGuidance",
@ -33356,6 +33392,7 @@
"InoCastAnyToModel",
"InoCastAnyToString",
"InoCastAnyToVae",
"InoCastStringToCombo",
"InoCivitaiDownloadModel",
"InoCompareFloat",
"InoCompareInt",
@ -33405,6 +33442,9 @@
"InoIncrementBatchName",
"InoIntToFloat",
"InoIntToString",
"InoIsImageLandscape",
"InoIsImagePortrait",
"InoIsImageSquare",
"InoJsonGetField",
"InoJsonSetField",
"InoLength",
@ -33417,6 +33457,9 @@
"InoLoadMultipleLora",
"InoLoadSamplerModels",
"InoLoadVaeModel",
"InoMathFloat",
"InoMathInt",
"InoMegapixelResolution",
"InoMovePath",
"InoNotBoolean",
"InoOnImageListCompleted",
@ -33430,6 +33473,7 @@
"InoRemoveDuplicateFiles",
"InoRemoveFile",
"InoRemoveFolder",
"InoResizeCropImage",
"InoS3Config",
"InoS3DownloadAudio",
"InoS3DownloadFile",
@ -34293,44 +34337,6 @@
"title_aux": "ComfyUI ProPainter Nodes"
}
],
"https://github.com/daniel-lewis-ab/ComfyUI-Llama": [
[
"Call LLM Advanced",
"Call LLM Basic",
"LLM Create Completion Advanced",
"LLM Detokenize",
"LLM Embed",
"LLM Eval",
"LLM Reset",
"LLM Sample",
"LLM Tokenize",
"LLM_Create_Completion Advanced",
"LLM_Detokenize",
"LLM_Embed",
"LLM_Eval",
"LLM_Load_State",
"LLM_Reset",
"LLM_Sample",
"LLM_Save_State",
"LLM_Token_BOS",
"LLM_Token_EOS",
"LLM_Tokenize",
"Load LLM Model Advanced",
"Load LLM Model Basic"
],
{
"title_aux": "ComfyUI-Llama"
}
],
"https://github.com/daniel-lewis-ab/ComfyUI-TTS": [
[
"Load_Piper_Model",
"Piper_Speak_Text"
],
{
"title_aux": "ComfyUI-TTS"
}
],
"https://github.com/danieljanata/ComfyUI-Prompting-System": [
[
"PS_MetadataCleaner",
@ -35588,14 +35594,24 @@
"GeminiTextGeneration",
"GeminiVision",
"JibMixQwenImageNode",
"KlingO3ImageToVideoNode",
"KlingO3TextToVideoNode",
"KlingV3ImageToVideoNode",
"Ltx23ImageToVideoNode",
"Ltx23TextToVideoNode",
"Ltx2ProImageToVideoNode",
"Ltx2ProTextToVideoNode",
"OpenAIAPIConfig",
"OpenAIChat",
"OpenAIImageEdit",
"OpenAIImageGeneration",
"OpenAIImageResponses",
"OpenAISystemInstruction",
"OpenAITextGeneration",
"OpenAIVision",
"PreviewVideo",
"QwenImage20EditNode",
"QwenImage20TextToImageNode",
"QwenImageEditLoraNode",
"QwenImageEditNode",
"QwenImageEditPlusLoraNode",
@ -35610,6 +35626,8 @@
"SHARPRenderVideo",
"SHARPRenderViews",
"SaveAudio",
"Seedance20ImageToVideoNode",
"Seedance20TextToVideoNode",
"SeedreamV4EditNode",
"SeedreamV4EditSequentialNode",
"SeedreamV4Node",
@ -35625,7 +35643,12 @@
"UploadImage",
"VeoImageToVideo",
"VeoTextToVideo",
"WaveSpeedAIAPIClient"
"Wan27ImageToVideoNode",
"Wan27TextToVideoNode",
"Wan27VideoExtendNode",
"WaveSpeedAIAPIClient",
"WaveSpeedVeo31ImageToVideoNode",
"WaveSpeedVeo31TextToVideoNode"
],
{
"title_aux": "ERPK Collection"
@ -35682,7 +35705,8 @@
"VAEScaleFlux2Block",
"VAEScaleFluxBlock",
"VAEScaleQwenBlock",
"VAEScaleSDXLBlock"
"VAEScaleSDXLBlock",
"VAEScaleWanVideo"
],
{
"title_aux": "ComfyUI-easygoing-nodes"
@ -36418,7 +36442,8 @@
],
"https://github.com/facok/ComfyUI-DiversityBoost": [
[
"DiversityBoostCore"
"DiversityBoostCore",
"DiversityBoostCoreV3"
],
{
"title_aux": "comfyui-diversityboost"
@ -36981,6 +37006,7 @@
"FL_DirectoryCrawl",
"FL_Dither",
"FL_FILM",
"FL_Fal_GPTImage2_Edit",
"FL_Fal_Gemini_ImageEdit",
"FL_Fal_Kling_AIAvatar",
"FL_Fal_Kontext",
@ -37051,6 +37077,7 @@
"FL_KsamplerPlus",
"FL_KsamplerPlusV2",
"FL_KsamplerSettings",
"FL_KsamplerSigma",
"FL_LoadCSV",
"FL_LoadImage",
"FL_MadLibGenerator",
@ -40790,7 +40817,9 @@
[
"LumiGeminiImagenConfig",
"LumiGoogleImagenProvider",
"LumiLLMImagenConfig",
"LumiLLMImagenProcessor",
"LumiLLMImagenProvider",
"LumiLLMPromptProcessor",
"LumiLoadImage",
"LumiNoiseToSeed",
@ -41482,6 +41511,7 @@
[
"APICostTracker",
"DeterministicHashVault",
"HashVaultLabelBuilder",
"HashVaultSave",
"LazyAPISwitch"
],
@ -41559,7 +41589,8 @@
"GeminiImageGenerate",
"GeminiStyleTransfer",
"GeminiStyleTransferSettings",
"PromptStudio"
"PromptStudio",
"PromptStudioSettings"
],
{
"title_aux": "ComfyUI-Gemini-Direct"
@ -43679,6 +43710,7 @@
"GetTrackRange",
"GradientToFloat",
"GrowMaskWithBlur",
"HDRPreviewKJ",
"HunyuanVideoBlockLoraSelect",
"HunyuanVideoEncodeKeyframesToCond",
"INTConstant",
@ -44284,15 +44316,82 @@
[
"Aspect Ratio to Size",
"Grow Mask",
"IPT-AnySwitchAny",
"IPT-AspectRatioToSize",
"IPT-CaptionFileSaver",
"IPT-CheckpointSelector",
"IPT-ClipSelector",
"IPT-CombineLoraStacks",
"IPT-CombinePrompts",
"IPT-ConsoleLogRelay",
"IPT-DetailerEnd",
"IPT-DetailerStart",
"IPT-DiffusionModelSelector",
"IPT-DualClipSelector",
"IPT-FlattenPromptForCaption",
"IPT-GetFloatExtra",
"IPT-GetIntExtra",
"IPT-GetLoraStackExtra",
"IPT-GetSamplerParamsExtra",
"IPT-GetSizeExtra",
"IPT-GetStringExtra",
"IPT-GrowMask",
"IPT-ImageBatchCountDebug",
"IPT-ImageDirectoryReader",
"IPT-ImageInfoContext",
"IPT-ImageInfoCountDebug",
"IPT-ImageInfoDefaults",
"IPT-ImageInfoFallback",
"IPT-ImageInfoToInfotext",
"IPT-ImageListToBatch",
"IPT-ImageReader",
"IPT-ImageSaver",
"IPT-InfotextToImageInfo",
"IPT-LatentBatchCountDebug",
"IPT-LoadNewModel",
"IPT-LoopEndSimple",
"IPT-LoopImageListRelaySimple",
"IPT-LoopStartSimple",
"IPT-LoraSelector",
"IPT-LoraStackLorader",
"IPT-MaskOverlayComparer",
"IPT-MergeCaptionTokens",
"IPT-ModelSamplingAuraFlow",
"IPT-NormalizePromptTokens",
"IPT-PixAITagger",
"IPT-PromptTemplate",
"IPT-PromptToLoraStack",
"IPT-QuadrupleClipSelector",
"IPT-RemoveCaptionTokens",
"IPT-RemoveImageInfoExtraKeys",
"IPT-RemoveImageInfoMainFields",
"IPT-RemovePromptComments",
"IPT-RemoveSmallMaskRegions",
"IPT-RemoveSmallSoftMaskRegions",
"IPT-Sam3PromptToMask",
"IPT-SamplerCustomFromParams",
"IPT-SamplerCustomFromParamsTiled",
"IPT-SamplerParams",
"IPT-SamplerSelector",
"IPT-ScaleWidthHeight",
"IPT-SchedulerSelector",
"IPT-SeedGenerator",
"IPT-SetFloatExtra",
"IPT-SetIntExtra",
"IPT-SetLoraStackExtra",
"IPT-SetSamplerParamsExtra",
"IPT-SetSizeExtra",
"IPT-SetStringExtra",
"IPT-SplitSamplerParams",
"IPT-SplitWidthHeight",
"IPT-TripleClipSelector",
"IPT-UnetModelSelector",
"IPT-UseLoadedModel",
"IPT-VaeSelector",
"IPT-VideoReader",
"IPT-VideoSaver",
"IPT-XYPlotModifier",
"IPT-XYPlotStart",
"Lora Selector",
"Mask Overlay Comparer",
"PixAI Tagger",
@ -44864,7 +44963,6 @@
],
"https://github.com/l3ony2k/comfyui-leon-nodes": [
[
"Leon_DALLE_Image_API_Node",
"Leon_Flux_2_Image_API_Node",
"Leon_Flux_Image_API_Node",
"Leon_Flux_Kontext_API_Node",
@ -46519,6 +46617,7 @@
"AUNInputsRefineBasic",
"AUNKSamplerPlusv3",
"AUNKSamplerPlusv4",
"AUNManualAutoImageSwitch",
"AUNManualAutoTextSwitch",
"AUNModelNamePass",
"AUNModelShorten",
@ -46555,6 +46654,7 @@
"AUNTextIndexSwitch",
"AUNTextIndexSwitch3",
"AUNTitleImagePreview",
"AUNWildcardAddToPrompt",
"AnyType(str)",
"AudioInputOptions",
"JNodes_AnyToString",
@ -47463,7 +47563,7 @@
],
"https://github.com/lum3on/ComfyUI_NativeBlockSwap": [
[
"wanBlockSwap"
"nativeWanVideoBlockSwap"
],
{
"title_aux": "ComfyUI_NativeBlockSwap"
@ -47704,38 +47804,6 @@
"EasyResize",
"EasyWan22Prompt",
"Easy_Version",
"FXTDAIUpscale",
"FXTDBitDepthConvert",
"FXTDCameraShake",
"FXTDCinematicPromptEncoder",
"FXTDCompressionArtifacts",
"FXTDDepthOfField",
"FXTDDownscale32bit",
"FXTDEXRChannelMerge",
"FXTDFilmGrain",
"FXTDFilmGrainAdvanced",
"FXTDFilmLook",
"FXTDHelp",
"FXTDLensEffects",
"FXTDMotionBlur",
"FXTDPhysicalCamera",
"FXTDProFilmEffects",
"FXTDProUpscale",
"FXTDRadianceLoader",
"FXTDRealisticGrain",
"FXTDResolution",
"FXTDRollingShutter",
"FXTDSaveEXR",
"FXTDSaveEXRCryptomatte",
"FXTDSaveEXRMultiLayer",
"FXTDSaveEXRSequence",
"FXTDUpscaleBySize",
"FXTDUpscaleTiled",
"FXTDWhiteBalance",
"FXTD_DepthMapGenerator",
"FXTD_Grade",
"FXTD_RadianceViewer",
"FXTD_Radiance_Sampler_Pro",
"Float32ColorCorrect",
"GPUTensorOps",
"HDR360Generate",
@ -47753,8 +47821,6 @@
"NAGParamtersSetting",
"OCIOColorTransform",
"OCIOListColorspaces",
"RadianceGPUColorMatrix",
"RadianceLUTApply",
"RadianceLogCurveDecode",
"RadianceOCIOColorTransformV2",
"RadianceVAEDecode",
@ -48419,6 +48485,7 @@
"mrmth_ag_NoiseMathNode",
"mrmth_ag_SelectiveGuiderMathNode",
"mrmth_ag_SigmasMathNode",
"mrmth_ag_StringMathNode",
"mrmth_ag_VAEMathNode",
"mrmth_ag_VideoMathNode"
],
@ -48836,6 +48903,7 @@
"IterVideoRouter",
"IterationSwitch",
"LatentExists",
"LtxResolutionPicker",
"MaskDirectionalExtend",
"MaskExists",
"MultiChannelSlicer",
@ -51149,14 +51217,20 @@
],
"https://github.com/olivv-cs/ComfyUI-FunPack": [
[
"FunPackApplyLoraWeights",
"FunPackClipVisionOutputCombine",
"FunPackContinueVideo",
"FunPackGemmaEmbeddingRefiner",
"FunPackHybridEuler2SSampler",
"FunPackLoraLoader",
"FunPackLorebookEnhancer",
"FunPackPromptCombiner",
"FunPackPromptEnhancer",
"FunPackSaveRefinementLatent",
"FunPackStoryMemKeyframeExtractor",
"FunPackStoryMemLastFrameExtractor",
"FunPackStoryWriter",
"FunPackVideoRefiner",
"FunPackVideoStitch"
],
{
@ -55181,6 +55255,7 @@
"CameraMoveVideoNode",
"CameraShakeNode",
"CameraShakeVideoNode",
"ChromaticAberrationNode",
"CloseUpImageNode",
"CloseUpNode",
"ComfyAddSoundtrack",
@ -55692,16 +55767,6 @@
"title_aux": "ComfyUI_Memeplex_DALLE"
}
],
"https://github.com/shiertier/ComfyUI-TeaCache-lumina2": [
[
"TeaCacheForLumina2",
"TeaCacheForLuminaAuto",
"TeaCacheForLuminaNext"
],
{
"title_aux": "ComfyUI-TeaCache-Lumina"
}
],
"https://github.com/shiimizu/ComfyUI-PhotoMaker-Plus": [
[
"PhotoMakerEncodePlus",
@ -55921,6 +55986,8 @@
],
"https://github.com/silveroxides/ComfyUI-ModelUtils": [
[
"BaseInfoMetaDownloaderNode",
"BaseModelInfoLoader",
"CheckpointMetaKeys",
"CheckpointPruneKeys",
"CheckpointRenameKeys",
@ -57660,6 +57727,7 @@
"VACEOutpaint",
"WanVACEBatchContext",
"WanVACEExtend",
"WanVACEInpaint",
"WanVACEPrep",
"WanVACEPrepBatch"
],
@ -58325,7 +58393,8 @@
"https://github.com/tea-time-labs/sweet-tea-nodes": [
[
"UltimateSDUpscalePoseTiled",
"WanVaceToVideoCapsInpaint"
"WanVaceToVideoCapsInpaint",
"WanVaceToVideoHybridSubjectSwap"
],
{
"title_aux": "sweet-tea-nodes"
@ -60098,6 +60167,7 @@
"Trellis2FillHolesWithCuMesh",
"Trellis2FillHolesWithMeshlib",
"Trellis2ImageCondGenerator",
"Trellis2ImageCondMultiViewGenerator",
"Trellis2LaplacianSmoothingWithOpen3d",
"Trellis2LoadImageWithTransparency",
"Trellis2LoadMesh",
@ -60127,7 +60197,9 @@
"Trellis2RenderMultiView",
"Trellis2SaveImage",
"Trellis2ShapeCascadeGenerator",
"Trellis2ShapeCascadeMultiViewGenerator",
"Trellis2ShapeGenerator",
"Trellis2ShapeMultiViewGenerator",
"Trellis2SimplifyMesh",
"Trellis2SimplifyMeshAdvanced",
"Trellis2SimplifyTrimesh",
@ -60135,8 +60207,10 @@
"Trellis2SmoothNormals",
"Trellis2SparseGenerator",
"Trellis2SparseGeneratorWithReconViaGen",
"Trellis2SparseMultiViewGenerator",
"Trellis2StringSelector",
"Trellis2TexSlatGenerator",
"Trellis2TexSlatMultiViewGenerator",
"Trellis2TrimeshToMeshWithVoxel",
"Trellis2UnWrapAndRasterizer",
"Trellis2UnWrapTrimesh",
@ -60293,6 +60367,7 @@
"VRGDG_DisplayIndex",
"VRGDG_DurationIndexFloat",
"VRGDG_Extract_Frame_Number",
"VRGDG_GeneralGGUF",
"VRGDG_GeneralPromptBatcher",
"VRGDG_GeneralVLM",
"VRGDG_GetAudioFilePath",
@ -60386,6 +60461,7 @@
"VRGDG_StoryBoardCreator",
"VRGDG_String2Json",
"VRGDG_StringConcat",
"VRGDG_SuperGemmaGGUFChat",
"VRGDG_TextBox",
"VRGDG_ThemeSplitter",
"VRGDG_TimecodeFromIndex",
@ -61055,10 +61131,12 @@
],
"https://github.com/wildminder/ComfyUI-VoxCPM": [
[
"VoxCPM_AdvancedParams",
"VoxCPM_DatasetMaker",
"VoxCPM_LoraTrainer",
"VoxCPM_TTS",
"VoxCPM_TrainConfig"
"VoxCPM_TrainConfig",
"VoxCPM_VoiceCloning"
],
{
"title_aux": "ComfyUI-VoxCPM"
@ -61502,7 +61580,8 @@
],
"https://github.com/wywywywy/ComfyUI-pause": [
[
"PauseWorkflowNode"
"PauseWorkflowNode",
"PauseWorkflowNodeWithSound"
],
{
"title_aux": "ComfyUI Pause Workflow Node"
@ -62317,6 +62396,7 @@
"MaskSmartValleySplit",
"MaskSplitFilter",
"MaskTopNFilter",
"TextBracketReplace",
"TextKeyword",
"YC Extract Number",
"YC Mask Condition Switch",
@ -62999,12 +63079,13 @@
"Y7Nodes_ColorMatchMasked",
"Y7Nodes_CropToNearestMultiple",
"Y7Nodes_ImageBatchPath",
"Y7Nodes_ImageRow",
"Y7Nodes_ImageSizePresets",
"Y7Nodes_ImageStitcher",
"Y7Nodes_JoyCaption",
"Y7Nodes_JoyCaption_ExtraOptions",
"Y7Nodes_LMStudioText",
"Y7Nodes_LMStudioVision",
"Y7Nodes_LoadImage",
"Y7Nodes_PasteCroppedImageBack",
"Y7Nodes_PromptEnhancerFlux",
"Y7Nodes_PromptEnhancerFlux2",
@ -63653,6 +63734,7 @@
"ImageScaleByLongSide",
"ImageScaleByShortSide",
"ImageScaleByShortSideFactor",
"ImageScaleByShortSideTarget",
"ImageSharpen",
"ImageUpscale"
],

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ import manager_migration
from node_package import InstalledNodePackage
version_code = [3, 39, 2]
version_code = [3, 40]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')

View File

@ -312,6 +312,57 @@ def security_403_response():
return web.json_response({"error": "security_level"}, status=403)
# CORS "simple request" Content-Type set per Fetch spec §3.2.3. Browsers send
# <form method=POST> submissions with one of these three MIME types and do NOT
# trigger a CORS preflight, so a malicious cross-origin page can silently POST
# into state-changing endpoints if we only gate on HTTP method. Blocking these
# three Content-Types on no-body mutation endpoints forces any non-same-origin
# POST to use a non-simple Content-Type (e.g. application/json), which triggers
# a preflight that this server rejects by not advertising an Access-Control-
# Allow-Origin response.
_SIMPLE_FORM_CONTENT_TYPES = frozenset({
'application/x-www-form-urlencoded',
'multipart/form-data',
'text/plain',
})
def _reject_simple_form_content_type(request):
"""Reject Content-Types that enable preflight-less <form method=POST> CSRF.
Applied ONLY to POST handlers that do not consume a request body (e.g.,
/snapshot/save, /manager/queue/{reset,start,update_comfyui},
/manager/reboot). These are vulnerable to cross-origin <form method=POST>
attacks because the handler accepts the request without parsing any body
the attacker needs no ability to forge a valid payload, only to point a
hidden form at the URL.
Handlers that already read a body via ``await request.json()`` are NOT
gated here: a cross-origin <form method=POST> cannot forge a valid JSON
body because the browser refuses to send ``application/json`` without a
CORS preflight, which this server does not answer.
DO NOT add this gate to body-reading handlers redundant and UX-breaking.
DO NOT remove this gate from no-body handlers this is the bypass vector.
aiohttp's ``request.content_type`` normalizes the header (lower-cases,
strips parameters), so ``multipart/form-data; boundary=----X`` is compared
as ``multipart/form-data``.
Returns:
web.Response(status=400) when the request has a simple-form
Content-Type that must be rejected. None when the request is allowed
to proceed (no Content-Type, application/json, or any non-simple
Content-Type).
"""
if request.content_type in _SIMPLE_FORM_CONTENT_TYPES:
return web.Response(
status=400,
text='Invalid Content-Type for this endpoint. Use application/json or omit body.',
)
return None
def get_model_dir(data, show_log=False):
if 'download_model_base' in folder_paths.folder_names_and_paths:
models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]
@ -737,16 +788,19 @@ async def fetch_customnode_mappings(request):
return web.json_response(json_obj, content_type='application/json')
@routes.get("/customnode/fetch_updates")
@routes.post("/customnode/fetch_updates")
async def fetch_updates(request):
try:
if request.rel_url.query["mode"] == "local":
json_data = await request.json()
mode = json_data.get("mode", "default")
if mode == "local":
channel = 'local'
else:
channel = core.get_config()['channel_url']
await core.unified_manager.reload(request.rel_url.query["mode"])
await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"])
await core.unified_manager.reload(mode)
await core.unified_manager.get_custom_nodes(channel, mode)
res = core.unified_manager.fetch_or_pull_git_repo(is_pull=False)
@ -764,7 +818,7 @@ async def fetch_updates(request):
return web.Response(status=400)
@routes.get("/manager/queue/update_all")
@routes.post("/manager/queue/update_all")
async def update_all(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@ -774,16 +828,19 @@ async def update_all(request):
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
if is_processing:
return web.Response(status=401)
await core.save_snapshot_with_postfix('autosave')
if request.rel_url.query["mode"] == "local":
json_data = await request.json()
mode = json_data.get("mode", "default")
if mode == "local":
channel = 'local'
else:
channel = core.get_config()['channel_url']
await core.unified_manager.reload(request.rel_url.query["mode"])
await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"])
await core.unified_manager.reload(mode)
await core.unified_manager.get_custom_nodes(channel, mode)
for k, v in core.unified_manager.active_nodes.items():
if k == 'comfyui-manager':
@ -1006,14 +1063,15 @@ def get_safe_snapshot_path(target):
return os.path.join(core.manager_snapshot_path, f"{target}.json")
@routes.get("/snapshot/remove")
@routes.post("/snapshot/remove")
async def remove_snapshot(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
return security_403_response()
try:
target = request.rel_url.query["target"]
json_data = await request.json()
target = json_data["target"]
path = get_safe_snapshot_path(target)
if path is None:
@ -1028,14 +1086,15 @@ async def remove_snapshot(request):
return web.Response(status=400)
@routes.get("/snapshot/restore")
@routes.post("/snapshot/restore")
async def restore_snapshot(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
return security_403_response()
try:
target = request.rel_url.query["target"]
json_data = await request.json()
target = json_data["target"]
path = get_safe_snapshot_path(target)
if path is None:
@ -1066,8 +1125,11 @@ async def get_current_snapshot_api(request):
return web.Response(status=400)
@routes.get("/snapshot/save")
@routes.post("/snapshot/save")
async def save_snapshot(request):
resp = _reject_simple_form_content_type(request)
if resp is not None:
return resp
try:
await core.save_snapshot_with_postfix('snapshot')
return web.Response(status=200)
@ -1228,8 +1290,11 @@ async def reinstall_custom_node(request):
await install_custom_node(request)
@routes.get("/manager/queue/reset")
@routes.post("/manager/queue/reset")
async def reset_queue(request):
resp = _reject_simple_form_content_type(request)
if resp is not None:
return resp
global task_queue
task_queue = queue.Queue()
return web.Response(status=200)
@ -1270,6 +1335,26 @@ async def install_custom_node(request):
if skip_post_install:
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
core.unified_manager.unified_enable(cnr_id)
# Mirror the pair of events (in_progress then done) that the async
# task_worker normally emits for queued operations. The in_progress
# event is what sets item.restart=true on the client row so the
# action cell re-renders as "Restart Required"; without it, the
# "Enable" button remains visible after successful enable. The done
# event drives the completion UI (toast, restart indicator, button
# loading clear).
ui_id = json_data.get('ui_id', cnr_id)
PromptServer.instance.send_sync(
"cm-queue-status",
{'status': 'in_progress',
'target': ui_id,
'ui_target': 'nodepack_manager',
'total_count': 1, 'done_count': 0})
PromptServer.instance.send_sync(
"cm-queue-status",
{'status': 'done',
'nodepack_result': {ui_id: 'success'},
'model_result': {},
'total_count': 1, 'done_count': 1})
return web.Response(status=200)
elif selected_version is None:
selected_version = 'latest'
@ -1311,8 +1396,11 @@ async def install_custom_node(request):
task_worker_thread:threading.Thread = None
@routes.get("/manager/queue/start")
@routes.post("/manager/queue/start")
async def queue_start(request):
resp = _reject_simple_form_content_type(request)
if resp is not None:
return resp
global nodepack_result
global model_result
global task_worker_thread
@ -1427,8 +1515,11 @@ async def update_custom_node(request):
return web.Response(status=200)
@routes.get("/manager/queue/update_comfyui")
@routes.post("/manager/queue/update_comfyui")
async def update_comfyui(request):
resp = _reject_simple_form_content_type(request)
if resp is not None:
return resp
is_stable = core.get_config()['update_policy'] != 'nightly-comfyui'
task_queue.put(("update-comfyui", ('comfyui', is_stable)))
return web.Response(status=200)
@ -1445,11 +1536,12 @@ async def comfyui_versions(request):
return web.Response(status=400)
@routes.get("/comfyui_manager/comfyui_switch_version")
@routes.post("/comfyui_manager/comfyui_switch_version")
async def comfyui_switch_version(request):
try:
if "ver" in request.rel_url.query:
core.switch_comfyui(request.rel_url.query['ver'])
json_data = await request.json()
if "ver" in json_data:
core.switch_comfyui(json_data['ver'])
return web.Response(status=200)
except Exception as e:
@ -1526,83 +1618,87 @@ async def install_model(request):
@routes.get("/manager/preview_method")
async def preview_method(request):
# Setting change request
if "value" in request.rel_url.query:
# Reject setting change if per-queue preview feature is available
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=403)
async def get_preview_method(request):
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=200)
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
# Process normally if not available
set_preview_method(request.rel_url.query['value'])
core.write_config()
return web.Response(status=200)
# Status query request
else:
# Return DISABLED if per-queue preview feature is available
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=200)
@routes.post("/manager/preview_method")
async def set_preview_method_handler(request):
if COMFYUI_HAS_PER_QUEUE_PREVIEW:
return web.Response(text="DISABLED", status=403)
# Return current value if not available
return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200)
json_data = await request.json()
set_preview_method(json_data['value'])
core.write_config()
return web.Response(status=200)
@routes.get("/manager/db_mode")
async def db_mode(request):
if "value" in request.rel_url.query:
set_db_mode(request.rel_url.query['value'])
core.write_config()
else:
return web.Response(text=core.get_config()['db_mode'], status=200)
async def get_db_mode(request):
return web.Response(text=core.get_config()['db_mode'], status=200)
@routes.post("/manager/db_mode")
async def set_db_mode_handler(request):
json_data = await request.json()
set_db_mode(json_data['value'])
core.write_config()
return web.Response(status=200)
@routes.get("/manager/policy/component")
async def component_policy(request):
if "value" in request.rel_url.query:
set_component_policy(request.rel_url.query['value'])
core.write_config()
else:
return web.Response(text=core.get_config()['component_policy'], status=200)
async def get_component_policy(request):
return web.Response(text=core.get_config()['component_policy'], status=200)
@routes.post("/manager/policy/component")
async def set_component_policy_handler(request):
json_data = await request.json()
set_component_policy(json_data['value'])
core.write_config()
return web.Response(status=200)
@routes.get("/manager/policy/update")
async def update_policy(request):
if "value" in request.rel_url.query:
set_update_policy(request.rel_url.query['value'])
core.write_config()
else:
return web.Response(text=core.get_config()['update_policy'], status=200)
async def get_update_policy(request):
return web.Response(text=core.get_config()['update_policy'], status=200)
@routes.post("/manager/policy/update")
async def set_update_policy_handler(request):
json_data = await request.json()
set_update_policy(json_data['value'])
core.write_config()
return web.Response(status=200)
@routes.get("/manager/channel_url_list")
async def channel_url_list(request):
async def get_channel_url_list(request):
channels = core.get_channel_dict()
if "value" in request.rel_url.query:
channel_url = channels.get(request.rel_url.query['value'])
if channel_url is not None:
core.get_config()['channel_url'] = channel_url
core.write_config()
else:
selected = 'custom'
selected_url = core.get_config()['channel_url']
selected = 'custom'
selected_url = core.get_config()['channel_url']
for name, url in channels.items():
if url == selected_url:
selected = name
break
for name, url in channels.items():
if url == selected_url:
selected = name
break
res = {'selected': selected,
'list': core.get_channel_list()}
return web.json_response(res, status=200)
res = {'selected': selected,
'list': core.get_channel_list()}
return web.json_response(res, status=200)
@routes.post("/manager/channel_url_list")
async def set_channel_url_list(request):
json_data = await request.json()
channels = core.get_channel_dict()
channel_url = channels.get(json_data['value'])
if channel_url is not None:
core.get_config()['channel_url'] = channel_url
core.write_config()
return web.Response(status=200)
@ -1700,8 +1796,11 @@ async def get_startup_alerts(request):
return web.json_response(alerts)
@routes.get("/manager/reboot")
@routes.post("/manager/reboot")
def restart(self):
resp = _reject_simple_form_content_type(self)
if resp is not None:
return resp
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
return security_403_response()

View File

@ -52,7 +52,7 @@ async function tryInstallCustomNode(event) {
}
}
let response = await api.fetchApi("/manager/reboot");
let response = await api.fetchApi("/manager/reboot", { method: 'POST' });
if(response.status == 403) {
await handle403Response(response);
return false;

View File

@ -470,12 +470,12 @@ async function updateComfyUI() {
set_inprogress_mode();
const response = await api.fetchApi('/manager/queue/update_comfyui');
const response = await api.fetchApi('/manager/queue/update_comfyui', { method: 'POST' });
showTerminal();
is_updating = true;
await api.fetchApi('/manager/queue/start');
await api.fetchApi('/manager/queue/start', { method: 'POST' });
}
function showVersionSelectorDialog(versions, current, onSelect) {
@ -625,14 +625,14 @@ async function switchComfyUI() {
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
if(selected_version == 'nightly') {
update_policy_combo.value = 'nightly-comfyui';
api.fetchApi('/manager/policy/update?value=nightly-comfyui');
api.fetchApi('/manager/policy/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: 'nightly-comfyui' }) });
}
else {
update_policy_combo.value = 'stable-comfyui';
api.fetchApi('/manager/policy/update?value=stable-comfyui');
api.fetchApi('/manager/policy/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: 'stable-comfyui' }) });
}
let response = await api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
let response = await api.fetchApi('/comfyui_manager/comfyui_switch_version', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ver: selected_version }), cache: "no-store" });
if (response.status == 200) {
infoToast(`ComfyUI version is switched to ${selected_version}`);
}
@ -769,10 +769,10 @@ async function updateAll(update_comfyui) {
if(update_comfyui) {
update_all_button.innerText = "Updating ComfyUI...";
await api.fetchApi('/manager/queue/update_comfyui');
await api.fetchApi('/manager/queue/update_comfyui', { method: 'POST' });
}
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
const response = await api.fetchApi('/manager/queue/update_all', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mode: mode }) });
if (response.status == 403) {
await handle403Response(response);
@ -784,7 +784,7 @@ async function updateAll(update_comfyui) {
}
else if(response.status == 200) {
is_updating = true;
await api.fetchApi('/manager/queue/start');
await api.fetchApi('/manager/queue/start', { method: 'POST' });
}
}
@ -813,7 +813,7 @@ function restartOrStop() {
rebootAPI();
}
else {
api.fetchApi('/manager/queue/reset');
api.fetchApi('/manager/queue/reset', { method: 'POST' });
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
}
@ -967,7 +967,7 @@ class ManagerMenuDialog extends ComfyDialog {
.then(data => { this.datasrc_combo.value = data; });
this.datasrc_combo.addEventListener('change', function (event) {
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
api.fetchApi('/manager/db_mode', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: event.target.value }) });
});
const dbRetrievalSetttingItem = createSettingsCombo("DB", this.datasrc_combo);
@ -1043,7 +1043,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
// Normal operation
api.fetchApi(`/manager/preview_method?value=${event.target.value}`)
api.fetchApi('/manager/preview_method', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: event.target.value }) })
.then(response => {
if (response.status === 403) {
// Feature transitioned to native
@ -1087,7 +1087,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
channel_combo.addEventListener('change', function (event) {
api.fetchApi(`/manager/channel_url_list?value=${event.target.value}`);
api.fetchApi('/manager/channel_url_list', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: event.target.value }) });
});
channel_combo.value = data.selected;
@ -1152,7 +1152,7 @@ class ManagerMenuDialog extends ComfyDialog {
});
component_policy_combo.addEventListener('change', function (event) {
api.fetchApi(`/manager/policy/component?value=${event.target.value}`);
api.fetchApi('/manager/policy/component', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: event.target.value }) });
set_component_policy(event.target.value);
});
@ -1171,7 +1171,7 @@ class ManagerMenuDialog extends ComfyDialog {
});
update_policy_combo.addEventListener('change', function (event) {
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
api.fetchApi('/manager/policy/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ value: event.target.value }) });
});
const updateSetttingItem = createSettingsCombo("Update", update_policy_combo);

View File

@ -185,7 +185,7 @@ export async function rebootAPI() {
const isConfirmed = await customConfirm("Are you sure you'd like to reboot the server?");
if (isConfirmed) {
try {
const response = await api.fetchApi("/manager/reboot");
const response = await api.fetchApi("/manager/reboot", { method: 'POST' });
if (response.status == 403) {
await handle403Response(response);
return false;

View File

@ -462,7 +462,7 @@ export class CustomNodesManager {
".cn-manager-stop": {
click: () => {
api.fetchApi('/manager/queue/reset');
api.fetchApi('/manager/queue/reset', { method: 'POST' });
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
@ -1476,9 +1476,15 @@ export class CustomNodesManager {
let needRestart = false;
let errorMsg = "";
await api.fetchApi('/manager/queue/reset');
await api.fetchApi('/manager/queue/reset', { method: 'POST' });
// Set install_context BEFORE per-item queue enqueue calls so that any
// server-side synchronous completion (e.g., sync enable of an inactive
// node) that emits cm-queue-status before we return here still finds
// install_context populated in onQueueCompleted. target_items is shared
// by reference so further pushes below remain visible.
let target_items = [];
this.install_context = {btn: btn, targets: target_items};
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash);
@ -1550,8 +1556,6 @@ export class CustomNodesManager {
}
}
this.install_context = {btn: btn, targets: target_items};
if(errorMsg) {
this.showError(errorMsg);
show_message("[Installation Errors]\n"+errorMsg);
@ -1563,7 +1567,7 @@ export class CustomNodesManager {
}
}
else {
await api.fetchApi('/manager/queue/start');
await api.fetchApi('/manager/queue/start', { method: 'POST' });
this.showStop();
showTerminal();
}
@ -1576,6 +1580,10 @@ export class CustomNodesManager {
const item = self.grid.getRowItemBy("hash", hash);
if (!item) {
return;
}
item.restart = true;
self.restartMap[item.hash] = true;
self.grid.updateCell(item, "action");
@ -1583,45 +1591,81 @@ export class CustomNodesManager {
}
else if(event.detail.status == 'done') {
self.hideStop();
self.onQueueCompleted(event.detail);
// Await + error logging so any unhandled rejection surfaces to the
// console instead of silently swallowing completion finalization
// (root cause of disable/enable button staying loading with no toast).
try {
await self.onQueueCompleted(event.detail);
} catch (e) {
console.error("[ComfyUI-Manager] onQueueCompleted failed:", e);
}
}
}
async onQueueCompleted(info) {
// `nodepack_result` is a dict serialized from a Python dict, not an array.
// `dict.length` is `undefined` and `undefined == 0` is `false`, so the
// previous `result.length == 0` guard was a no-op; switch to a correct
// empty-check that also tolerates null/undefined.
let result = info.nodepack_result;
if(result.length == 0) {
if (!result || Object.keys(result).length === 0) {
return;
}
let self = CustomNodesManager.instance;
if(!self.install_context) {
if (!self || !self.install_context) {
return;
}
const { target, label, mode } = self.install_context.btn;
target.classList.remove("cn-btn-loading");
const targets = self.install_context.targets || [];
// Compute errorMsg upfront so the downstream user-visible finalization
// (showRestart / showMessage / infoToast) fires regardless of whether
// any DOM-touching step below throws.
let errorMsg = "";
for(let hash in result){
for (let hash in result) {
let v = result[hash];
if(v != 'success' && v != 'skip')
errorMsg += v+'\n';
if (v != 'success' && v != 'skip') {
errorMsg += v + '\n';
}
}
for(let k in self.install_context.targets) {
let item = self.install_context.targets[k];
self.grid.updateCell(item, "action");
// Defensive: `target` may be a detached DOM node (the in_progress
// handler's updateCell can re-render the row and replace the button
// element). classList.remove on a detached node is a no-op, but we
// still guard in case target was torn down entirely.
try {
if (target && target.classList) {
target.classList.remove("cn-btn-loading");
}
} catch (e) {
console.warn("[ComfyUI-Manager] Failed to clear button loading state:", e);
}
// Defensive: grid.updateCell can throw if the item was removed or the
// grid was re-rendered between in_progress and done. Do NOT let this
// loop abort the completion finalization below — that was the observed
// failure mode for disable/enable (no toast, no "restart required"
// message).
try {
for (let k in targets) {
let item = targets[k];
if (item) {
self.grid.updateCell(item, "action");
}
}
} catch (e) {
console.warn("[ComfyUI-Manager] Failed to refresh target cells after queue completion:", e);
}
if (errorMsg) {
self.showError(errorMsg);
show_message("Installation Error:\n"+errorMsg);
show_message("Installation Error:\n" + errorMsg);
} else {
self.showStatus(`${label} ${result.length} custom node(s) successfully`);
self.showStatus(`${label} ${Object.keys(result).length} custom node(s) successfully`);
}
self.showRestart();

View File

@ -170,7 +170,7 @@ export class ModelManager {
".cmm-manager-stop": {
click: () => {
api.fetchApi('/manager/queue/reset');
api.fetchApi('/manager/queue/reset', { method: 'POST' });
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
@ -444,7 +444,7 @@ export class ModelManager {
let needRefresh = false;
let errorMsg = "";
await api.fetchApi('/manager/queue/reset');
await api.fetchApi('/manager/queue/reset', { method: 'POST' });
let target_items = [];
@ -503,7 +503,7 @@ export class ModelManager {
}
}
else {
await api.fetchApi('/manager/queue/start');
await api.fetchApi('/manager/queue/start', { method: 'POST' });
this.showStop();
showTerminal();
}

View File

@ -9,7 +9,7 @@ loadCss("./snapshot.css");
async function restore_snapshot(target) {
if(SnapshotManager.instance) {
try {
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
const response = await api.fetchApi('/snapshot/restore', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ target: target }), cache: "no-store" });
if(response.status == 403) {
await handle403Response(response);
@ -37,7 +37,7 @@ async function restore_snapshot(target) {
async function remove_snapshot(target) {
if(SnapshotManager.instance) {
try {
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
const response = await api.fetchApi('/snapshot/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ target: target }), cache: "no-store" });
if(response.status == 403) {
await handle403Response(response);
@ -63,7 +63,7 @@ async function remove_snapshot(target) {
async function save_current_snapshot() {
try {
const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
const response = await api.fetchApi('/snapshot/save', { method: 'POST', cache: "no-store" });
app.ui.dialog.close();
return true;
}

View File

@ -380,16 +380,6 @@
"install_type": "git-clone",
"description": "A ComfyUI node implementing SMC-CFG (Sliding Mode Control CFG) that replaces standard linear CFG with a nonlinear sliding mode controller for stable guidance at any CFG scale. NOT WORKING - Work in progress. (Description by CC)"
},
{
"author": "AiSatan",
"title": "ComfyUI_CSM [NAME CONFLICT]",
"reference": "https://github.com/AiSatan/ComfyUI_CSM",
"files": [
"https://github.com/AiSatan/ComfyUI_CSM"
],
"install_type": "git-clone",
"description": "A ComfyUI node for the CSM model featuring text-to-speech, voice cloning, and automatic model downloading from Hugging Face."
},
{
"author": "unobtuse",
"title": "comfyui-topaz-ai-upscale",
@ -6232,16 +6222,6 @@
"install_type": "git-clone",
"description": "NODES: TelegramSend, TelegramReply"
},
{
"author": "qlikpetersen",
"title": "ComfyUI-AI_Tools [UNSAFE]",
"reference": "https://github.com/qlikpetersen/ComfyUI-AI_Tools",
"files": [
"https://github.com/qlikpetersen/ComfyUI-AI_Tools"
],
"install_type": "git-clone",
"description": "NODES: DoLogin, HttpRequest, Json2String, String2Json, CreateListString, CreateListJSON, Query_OpenAI, Image_Attachment, JSON_Attachment, String_Attachment, RunPython\n[w/This nodepack contains a node with a vulnerability that allows arbitrary code execution.]"
},
{
"author": "MuAIGC",
"title": "DMXAPI Nodes [WIP]",

View File

@ -1,5 +1,55 @@
{
"custom_nodes": [
{
"author": "qlikpetersen",
"title": "ComfyUI-AI_Tools [UNSAFE] [REMOVED]",
"reference": "https://github.com/qlikpetersen/ComfyUI-AI_Tools",
"files": [
"https://github.com/qlikpetersen/ComfyUI-AI_Tools"
],
"install_type": "git-clone",
"description": "NODES: DoLogin, HttpRequest, Json2String, String2Json, CreateListString, CreateListJSON, Query_OpenAI, Image_Attachment, JSON_Attachment, String_Attachment, RunPython\n[w/This nodepack contains a node with a vulnerability that allows arbitrary code execution.]"
},
{
"author": "AiSatan",
"title": "ComfyUI_CSM [NAME CONFLICT] [REMOVED]",
"reference": "https://github.com/AiSatan/ComfyUI_CSM",
"files": [
"https://github.com/AiSatan/ComfyUI_CSM"
],
"install_type": "git-clone",
"description": "A ComfyUI node for the CSM model featuring text-to-speech, voice cloning, and automatic model downloading from Hugging Face."
},
{
"author": "shiertier",
"title": "ComfyUI-TeaCache-Lumina [REMOVED]",
"reference": "https://github.com/shiertier/ComfyUI-TeaCache-lumina2",
"files": [
"https://github.com/shiertier/ComfyUI-TeaCache-lumina2"
],
"install_type": "git-clone",
"description": "ComfyUI Node Implementation: TeaCache Acceleration Specifically Designed for the Lumina Model"
},
{
"author": "Daniel Lewis",
"title": "ComfyUI-TTS [REMOVED]",
"reference": "https://github.com/daniel-lewis-ab/ComfyUI-TTS",
"files": [
"https://github.com/daniel-lewis-ab/ComfyUI-TTS"
],
"install_type": "git-clone",
"description": "Text To Speech (TTS) for ComfyUI"
},
{
"author": "Daniel Lewis",
"title": "ComfyUI-Llama [REMOVED]",
"reference": "https://github.com/daniel-lewis-ab/ComfyUI-Llama",
"files": [
"https://github.com/daniel-lewis-ab/ComfyUI-Llama"
],
"install_type": "git-clone",
"description": "This is a set of nodes to interact with llama-cpp-python"
},
{
"author": "Solarish",
"title": "fyUI-MidnightLook [REMOVED]",

View File

@ -559,6 +559,9 @@
"1hew_StringCoordinateToBBoxes",
"1hew_StringFilter",
"1hew_StringJoinMulti",
"1hew_StringRatioGemini31FlashImage",
"1hew_StringRatioGpt20Image",
"1hew_StringResolution",
"1hew_TextCustomExtract",
"1hew_TextEncodeQwenImageEdit",
"1hew_TextListToString",
@ -843,6 +846,8 @@
"PDGeminiNanoBanana2",
"PDGeminiProImageGenAPIKey",
"PDGeminiProImageGenAuthToken",
"PDOpenAIGPTImage2APIKey",
"PDOpenAIGPTImage2AuthToken",
"PDOpenAIGPTImageAPIKey",
"PDOpenAIGPTImageAuthToken",
"PD_comfyplus_image"
@ -1213,16 +1218,16 @@
],
"https://github.com/AI-TEC/ComfyUI-AITECCAFE-Toolkit": [
[
"AITEC_ChatGPT_Chat",
"AITEC_Image_Loader",
"AITEC_Image_Moderation",
"AITEC_LLM_Chat",
"AITEC_LLM_Loader",
"AITEC_LLM_Vision",
"AITEC_LLM_Vision_Loader",
"ChatGPTNode",
"CustomStringMerge",
"NSFWChecker",
"OpenAIImageModeration",
"SequentialImageLoader",
"SequentialMediaLoader"
"AITEC_Media_Loader",
"AITEC_NSFW_Checker",
"AITEC_String_Merge"
],
{
"title_aux": "ComfyUI_AITECCAFE_Toolkit"
@ -2203,12 +2208,14 @@
"AcademiaSD_BatchLoader",
"AcademiaSD_Downloader",
"AcademiaSD_Gemini_Node",
"AcademiaSD_LTXVMultiFrames",
"AcademiaSD_MultiLora",
"AcademiaSD_Numeric",
"AcademiaSD_Resolution",
"AcademiaSD_ResolutionDisplay",
"AcademiaSD_SaveAndSend",
"AcademiaSD_SaveCaption",
"AcademiaSD_TimeCalculator",
"AcademiaVisionNode",
"IntegerBypasser",
"LoopCounterToFile",
@ -3076,6 +3083,7 @@
"AGSoftInt",
"AGSoftLoadVideo",
"AGSoftMathExpression",
"AGSoftReferenceToLatent",
"AGSoftSaveImage",
"AGSoftShowAny",
"AGSoftTextConcatenateX2",
@ -3241,7 +3249,7 @@
"ElevenLabsTTSNode",
"Flux2Replicate",
"FluxKontextReplicate",
"GPTImageEditNode",
"GPTImageNode",
"GeminiChatNode",
"GeminiDiarisationAPI",
"GeminiSegmentationNode",
@ -6571,6 +6579,8 @@
"ClipSkipSliderNode",
"CountListNode",
"DA_Base_KSampler",
"DA_BusInNode",
"DA_BusOutNode",
"DA_Enhanced_KSampler",
"DiffusionModelGeneratorNode",
"DiffusionModelSelectorNode",
@ -8659,6 +8669,7 @@
"Prompt Assembler",
"Resize Images To Megapixels",
"Resize To Megapixels",
"Save_as_jpg",
"Text Enable With Prefix",
"Video Params",
"Video Params Expand",
@ -8940,12 +8951,13 @@
"PromptGenerator",
"PromptManager",
"PromptManagerAdvanced",
"WorkflowBridge",
"WorkflowBuilder",
"WorkflowExtractor",
"WorkflowManager",
"WorkflowModelLoader",
"WorkflowRenderer"
"RecipeBuilder",
"RecipeBuilderWan",
"RecipeExtractor",
"RecipeManager",
"RecipeModelLoader",
"RecipeRelay",
"RecipeRenderer"
],
{
"title_aux": "ComfyUI-Prompt-Manager"
@ -9594,6 +9606,7 @@
"ImageRGBA2RGB",
"ReActorBuildFaceModel",
"ReActorFaceBoost",
"ReActorFaceSimilarity",
"ReActorFaceSwap",
"ReActorFaceSwapOpt",
"ReActorImageDublicator",
@ -12192,6 +12205,7 @@
"https://github.com/JosefKuchar/ComfyUI-AdvancedTiling": [
[
"AdvancedTiling",
"AdvancedTilingRay",
"AdvancedTilingSettings",
"AdvancedTilingVAEDecode"
],
@ -13721,13 +13735,7 @@
],
"https://github.com/Lex-DRL/ComfyUI-StringConstructor": [
[
"StringConstructorDictAddAny",
"StringConstructorDictAddString",
"StringConstructorDictExtractString",
"StringConstructorDictFromText",
"StringConstructorDictPreview",
"StringConstructorFormatter",
"StringConstructorValidateKeys"
"_BaseNode"
],
{
"title_aux": "String Constructor (Text-Formatting)"
@ -13838,6 +13846,7 @@
"LTXVExtendSampler",
"LTXVGemmaCLIPModelLoader",
"LTXVGemmaEnhancePrompt",
"LTXVHDRDecodePostprocess",
"LTXVImgToVideoConditionOnly",
"LTXVInContextSampler",
"LTXVInpaintPreprocess",
@ -14908,6 +14917,7 @@
],
"https://github.com/Meisoftcoltd/comfyui-sequential-batcher": [
[
"LazySessionCache",
"MasterSwitch"
],
{
@ -15942,7 +15952,7 @@
],
"https://github.com/Nekodificador/ComfyUI-NKD-Popup-Preview": [
[
"NKD_PopupPreview"
"NKDPopupPreviewNode"
],
{
"title_aux": "NKD Popup Preview"
@ -16772,10 +16782,6 @@
"CRT_DynamicPromptScheduler",
"CRT_FileBatchPromptScheduler",
"CRT_ImageLoaderCrawlBatch",
"CRT_IsolateInput",
"CRT_IsolateInputTBG",
"CRT_IsolateOutput",
"CRT_IsolateOutputTBG",
"CRT_KSamplerBatch",
"CRT_KSamplerBatchAdvanced",
"CRT_LTX23AutoDownload",
@ -18287,6 +18293,7 @@
"Runware Audio Inference Settings",
"Runware Audio Inference Settings Voice Modify",
"Runware Audio Inference Speech",
"Runware Audio Inference Speech Voices",
"Runware Audio Model Search",
"Runware Audio Sections",
"Runware Background Removal",
@ -18337,6 +18344,7 @@
"Runware Pixverse Provider Settings",
"Runware Provider Settings Recraft Color",
"Runware Recraft Provider Settings",
"Runware Reference Audios",
"Runware Reference Images",
"Runware Reference Videos",
"Runware Reference Voices",
@ -18370,10 +18378,15 @@
"Runware Video Inference Inputs",
"Runware Video Inference Inputs Reference Audios",
"Runware Video Inference Inputs Reference Images",
"Runware Video Inference Inputs Reference Images Multiple Images Connector",
"Runware Video Inference Inputs Reference Videos",
"Runware Video Inference Inputs Reference Voices",
"Runware Video Inference Outputs",
"Runware Video Inference Settings",
"Runware Video Inference Settings Active Speaker Bounding Boxes",
"Runware Video Inference Settings Active Speaker Detection",
"Runware Video Inference Settings Segments",
"Runware Video Inference Settings TTS",
"Runware Video Inference Speech Input",
"Runware Video Inputs Frame Images",
"Runware Video Inputs References",
@ -18394,6 +18407,7 @@
"RunwareAlibabaProviderSettings",
"RunwareAudioInferenceInputs",
"RunwareAudioInferenceSpeech",
"RunwareAudioInferenceSpeechVoices",
"RunwareAudioInput",
"RunwareAudioSettings",
"RunwareAudioSettingsVoiceModify",
@ -18434,8 +18448,13 @@
"RunwareVideoAdvancedFeatureInputs",
"RunwareVideoBgRemoval",
"RunwareVideoInferenceOutputs",
"RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes",
"RunwareVideoInferenceSettingsActiveSpeakerDetection",
"RunwareVideoInferenceSettingsSegments",
"RunwareVideoInferenceSettingsTts",
"RunwareVideoInferenceSpeechInput",
"RunwareVideoInputsFrameImages",
"RunwareVideoInputsReferenceImagesMultipleImagesConnector",
"RunwareVideoInputsReferences",
"RunwareVideoSettings",
"RunwareVideoUpscaler",
@ -18947,6 +18966,7 @@
"BananaImageGenerationNode",
"ChatGPTImageEditNode",
"ChatGPTImageGenerationNode",
"ChatGPTImageModelGenerationNode",
"ImageToBase64"
],
{
@ -20606,6 +20626,7 @@
"INT8DynamicLoraLoader",
"INT8DynamicLoraStack",
"INT8KernelConfigTuner",
"INT8LazyTorchCompile",
"INT8LoraLoader",
"INT8LoraLoaderStack",
"INT8ModelAdapter",
@ -23812,7 +23833,7 @@
"Qwen3Caption",
"Qwen3CaptionBatch",
"StringToBbox",
"StringToSam3Box"
"StringToComfyBbox"
],
{
"title_aux": "ComfyUI_QwenVL_PromptCaption"
@ -24019,6 +24040,7 @@
"https://github.com/Xyc2016/Comfyui_Fd_Nodes": [
[
"FD_Flux2KleinGenImage",
"FD_GTPImage",
"FD_GeminiImage",
"FD_RemoveWatermark",
"FD_SeedreamImage",
@ -26335,6 +26357,7 @@
[
"FPFoldedPrompts",
"FPTabbedTextArea",
"FPTabedTextPassthrough",
"FPTextAreaPlus",
"FPTextCleanAndSplitt"
],
@ -27179,6 +27202,7 @@
"ColorToGrayscale",
"CompositeMaskAdjuster",
"CompositeMaskExtractor",
"CropFromBboxData",
"CropToMaskWithPadding",
"CurvatureGenerator",
"CustomColorToMask",
@ -27216,6 +27240,7 @@
"LotusNormalProcessor",
"Marigold_AOV_Extractor",
"MaskCompositor",
"MatchSubjectToReferenceImage",
"MicroDetailOverlay",
"MultiTextureBlender",
"MultimattePassApplicator",
@ -27253,6 +27278,7 @@
"ProceduralNoiseGenerator",
"QwenImagePrep",
"SSSMapGenerator",
"ScaleImageToReferenceBbox",
"ScratchesGenerator",
"SeamlessTiling",
"SharpenDepth",
@ -27412,6 +27438,7 @@
"Sage_Ace15AudioEncode",
"Sage_AceAdvOptions",
"Sage_AdvSamplerInfo",
"Sage_AnythingToStr",
"Sage_AverageConditioning",
"Sage_CLIPLoaderFromInfo",
"Sage_CLIPSelector",
@ -30054,7 +30081,7 @@
"Flux2KleinColorAnchor",
"Flux2KleinDetailController",
"Flux2KleinEnhancer",
"Flux2KleinKSampler",
"Flux2KleinKSamplerExperimental",
"Flux2KleinMaskRefController",
"Flux2KleinRefLatentController",
"Flux2KleinRefLatentWeight",
@ -30062,6 +30089,7 @@
"Flux2KleinTextEnhancer",
"Flux2KleinTextRefBalance",
"IdentityFeatureTransfer",
"IdentityFeatureTransferAdvanced",
"IdentityGuidance"
],
{
@ -32646,6 +32674,8 @@
"ByteDance2FirstLastFrameNode",
"ByteDance2ReferenceNode",
"ByteDance2TextToVideoNode",
"ByteDanceCreateImageAsset",
"ByteDanceCreateVideoAsset",
"ByteDanceFirstLastFrameNode",
"ByteDanceImageNode",
"ByteDanceImageReferenceNode",
@ -32763,6 +32793,8 @@
"FluxProExpandNode",
"FluxProFillNode",
"FluxProUltraImageNode",
"FrameInterpolate",
"FrameInterpolationModelLoader",
"FreSca",
"FreeU",
"FreeU_V2",
@ -32869,7 +32901,6 @@
"LTXAVTextEncoderLoader",
"LTXVAddGuide",
"LTXVAudioVAEDecode",
"LTXVAudioVAEEncode",
"LTXVAudioVAELoader",
"LTXVConcatAVLatent",
"LTXVConditioning",
@ -33072,6 +33103,10 @@
"RunwayImageToVideoNodeGen3a",
"RunwayImageToVideoNodeGen4",
"RunwayTextToImageNode",
"SAM3_Detect",
"SAM3_TrackPreview",
"SAM3_TrackToMask",
"SAM3_VideoTrack",
"SDPoseDrawKeypoints",
"SDPoseFaceBBoxes",
"SDPoseKeypointExtractor",
@ -33257,6 +33292,7 @@
"VAESave",
"VPScheduler",
"Veo3FirstLastFrameNode",
"Veo3VideoGenerationNode",
"VeoVideoGenerationNode",
"Video Slice",
"VideoLinearCFGGuidance",
@ -33356,6 +33392,7 @@
"InoCastAnyToModel",
"InoCastAnyToString",
"InoCastAnyToVae",
"InoCastStringToCombo",
"InoCivitaiDownloadModel",
"InoCompareFloat",
"InoCompareInt",
@ -33405,6 +33442,9 @@
"InoIncrementBatchName",
"InoIntToFloat",
"InoIntToString",
"InoIsImageLandscape",
"InoIsImagePortrait",
"InoIsImageSquare",
"InoJsonGetField",
"InoJsonSetField",
"InoLength",
@ -33417,6 +33457,9 @@
"InoLoadMultipleLora",
"InoLoadSamplerModels",
"InoLoadVaeModel",
"InoMathFloat",
"InoMathInt",
"InoMegapixelResolution",
"InoMovePath",
"InoNotBoolean",
"InoOnImageListCompleted",
@ -33430,6 +33473,7 @@
"InoRemoveDuplicateFiles",
"InoRemoveFile",
"InoRemoveFolder",
"InoResizeCropImage",
"InoS3Config",
"InoS3DownloadAudio",
"InoS3DownloadFile",
@ -34293,44 +34337,6 @@
"title_aux": "ComfyUI ProPainter Nodes"
}
],
"https://github.com/daniel-lewis-ab/ComfyUI-Llama": [
[
"Call LLM Advanced",
"Call LLM Basic",
"LLM Create Completion Advanced",
"LLM Detokenize",
"LLM Embed",
"LLM Eval",
"LLM Reset",
"LLM Sample",
"LLM Tokenize",
"LLM_Create_Completion Advanced",
"LLM_Detokenize",
"LLM_Embed",
"LLM_Eval",
"LLM_Load_State",
"LLM_Reset",
"LLM_Sample",
"LLM_Save_State",
"LLM_Token_BOS",
"LLM_Token_EOS",
"LLM_Tokenize",
"Load LLM Model Advanced",
"Load LLM Model Basic"
],
{
"title_aux": "ComfyUI-Llama"
}
],
"https://github.com/daniel-lewis-ab/ComfyUI-TTS": [
[
"Load_Piper_Model",
"Piper_Speak_Text"
],
{
"title_aux": "ComfyUI-TTS"
}
],
"https://github.com/danieljanata/ComfyUI-Prompting-System": [
[
"PS_MetadataCleaner",
@ -35588,14 +35594,24 @@
"GeminiTextGeneration",
"GeminiVision",
"JibMixQwenImageNode",
"KlingO3ImageToVideoNode",
"KlingO3TextToVideoNode",
"KlingV3ImageToVideoNode",
"Ltx23ImageToVideoNode",
"Ltx23TextToVideoNode",
"Ltx2ProImageToVideoNode",
"Ltx2ProTextToVideoNode",
"OpenAIAPIConfig",
"OpenAIChat",
"OpenAIImageEdit",
"OpenAIImageGeneration",
"OpenAIImageResponses",
"OpenAISystemInstruction",
"OpenAITextGeneration",
"OpenAIVision",
"PreviewVideo",
"QwenImage20EditNode",
"QwenImage20TextToImageNode",
"QwenImageEditLoraNode",
"QwenImageEditNode",
"QwenImageEditPlusLoraNode",
@ -35610,6 +35626,8 @@
"SHARPRenderVideo",
"SHARPRenderViews",
"SaveAudio",
"Seedance20ImageToVideoNode",
"Seedance20TextToVideoNode",
"SeedreamV4EditNode",
"SeedreamV4EditSequentialNode",
"SeedreamV4Node",
@ -35625,7 +35643,12 @@
"UploadImage",
"VeoImageToVideo",
"VeoTextToVideo",
"WaveSpeedAIAPIClient"
"Wan27ImageToVideoNode",
"Wan27TextToVideoNode",
"Wan27VideoExtendNode",
"WaveSpeedAIAPIClient",
"WaveSpeedVeo31ImageToVideoNode",
"WaveSpeedVeo31TextToVideoNode"
],
{
"title_aux": "ERPK Collection"
@ -35682,7 +35705,8 @@
"VAEScaleFlux2Block",
"VAEScaleFluxBlock",
"VAEScaleQwenBlock",
"VAEScaleSDXLBlock"
"VAEScaleSDXLBlock",
"VAEScaleWanVideo"
],
{
"title_aux": "ComfyUI-easygoing-nodes"
@ -36418,7 +36442,8 @@
],
"https://github.com/facok/ComfyUI-DiversityBoost": [
[
"DiversityBoostCore"
"DiversityBoostCore",
"DiversityBoostCoreV3"
],
{
"title_aux": "comfyui-diversityboost"
@ -36981,6 +37006,7 @@
"FL_DirectoryCrawl",
"FL_Dither",
"FL_FILM",
"FL_Fal_GPTImage2_Edit",
"FL_Fal_Gemini_ImageEdit",
"FL_Fal_Kling_AIAvatar",
"FL_Fal_Kontext",
@ -37051,6 +37077,7 @@
"FL_KsamplerPlus",
"FL_KsamplerPlusV2",
"FL_KsamplerSettings",
"FL_KsamplerSigma",
"FL_LoadCSV",
"FL_LoadImage",
"FL_MadLibGenerator",
@ -40790,7 +40817,9 @@
[
"LumiGeminiImagenConfig",
"LumiGoogleImagenProvider",
"LumiLLMImagenConfig",
"LumiLLMImagenProcessor",
"LumiLLMImagenProvider",
"LumiLLMPromptProcessor",
"LumiLoadImage",
"LumiNoiseToSeed",
@ -41482,6 +41511,7 @@
[
"APICostTracker",
"DeterministicHashVault",
"HashVaultLabelBuilder",
"HashVaultSave",
"LazyAPISwitch"
],
@ -41559,7 +41589,8 @@
"GeminiImageGenerate",
"GeminiStyleTransfer",
"GeminiStyleTransferSettings",
"PromptStudio"
"PromptStudio",
"PromptStudioSettings"
],
{
"title_aux": "ComfyUI-Gemini-Direct"
@ -43679,6 +43710,7 @@
"GetTrackRange",
"GradientToFloat",
"GrowMaskWithBlur",
"HDRPreviewKJ",
"HunyuanVideoBlockLoraSelect",
"HunyuanVideoEncodeKeyframesToCond",
"INTConstant",
@ -44284,15 +44316,82 @@
[
"Aspect Ratio to Size",
"Grow Mask",
"IPT-AnySwitchAny",
"IPT-AspectRatioToSize",
"IPT-CaptionFileSaver",
"IPT-CheckpointSelector",
"IPT-ClipSelector",
"IPT-CombineLoraStacks",
"IPT-CombinePrompts",
"IPT-ConsoleLogRelay",
"IPT-DetailerEnd",
"IPT-DetailerStart",
"IPT-DiffusionModelSelector",
"IPT-DualClipSelector",
"IPT-FlattenPromptForCaption",
"IPT-GetFloatExtra",
"IPT-GetIntExtra",
"IPT-GetLoraStackExtra",
"IPT-GetSamplerParamsExtra",
"IPT-GetSizeExtra",
"IPT-GetStringExtra",
"IPT-GrowMask",
"IPT-ImageBatchCountDebug",
"IPT-ImageDirectoryReader",
"IPT-ImageInfoContext",
"IPT-ImageInfoCountDebug",
"IPT-ImageInfoDefaults",
"IPT-ImageInfoFallback",
"IPT-ImageInfoToInfotext",
"IPT-ImageListToBatch",
"IPT-ImageReader",
"IPT-ImageSaver",
"IPT-InfotextToImageInfo",
"IPT-LatentBatchCountDebug",
"IPT-LoadNewModel",
"IPT-LoopEndSimple",
"IPT-LoopImageListRelaySimple",
"IPT-LoopStartSimple",
"IPT-LoraSelector",
"IPT-LoraStackLorader",
"IPT-MaskOverlayComparer",
"IPT-MergeCaptionTokens",
"IPT-ModelSamplingAuraFlow",
"IPT-NormalizePromptTokens",
"IPT-PixAITagger",
"IPT-PromptTemplate",
"IPT-PromptToLoraStack",
"IPT-QuadrupleClipSelector",
"IPT-RemoveCaptionTokens",
"IPT-RemoveImageInfoExtraKeys",
"IPT-RemoveImageInfoMainFields",
"IPT-RemovePromptComments",
"IPT-RemoveSmallMaskRegions",
"IPT-RemoveSmallSoftMaskRegions",
"IPT-Sam3PromptToMask",
"IPT-SamplerCustomFromParams",
"IPT-SamplerCustomFromParamsTiled",
"IPT-SamplerParams",
"IPT-SamplerSelector",
"IPT-ScaleWidthHeight",
"IPT-SchedulerSelector",
"IPT-SeedGenerator",
"IPT-SetFloatExtra",
"IPT-SetIntExtra",
"IPT-SetLoraStackExtra",
"IPT-SetSamplerParamsExtra",
"IPT-SetSizeExtra",
"IPT-SetStringExtra",
"IPT-SplitSamplerParams",
"IPT-SplitWidthHeight",
"IPT-TripleClipSelector",
"IPT-UnetModelSelector",
"IPT-UseLoadedModel",
"IPT-VaeSelector",
"IPT-VideoReader",
"IPT-VideoSaver",
"IPT-XYPlotModifier",
"IPT-XYPlotStart",
"Lora Selector",
"Mask Overlay Comparer",
"PixAI Tagger",
@ -44864,7 +44963,6 @@
],
"https://github.com/l3ony2k/comfyui-leon-nodes": [
[
"Leon_DALLE_Image_API_Node",
"Leon_Flux_2_Image_API_Node",
"Leon_Flux_Image_API_Node",
"Leon_Flux_Kontext_API_Node",
@ -46519,6 +46617,7 @@
"AUNInputsRefineBasic",
"AUNKSamplerPlusv3",
"AUNKSamplerPlusv4",
"AUNManualAutoImageSwitch",
"AUNManualAutoTextSwitch",
"AUNModelNamePass",
"AUNModelShorten",
@ -46555,6 +46654,7 @@
"AUNTextIndexSwitch",
"AUNTextIndexSwitch3",
"AUNTitleImagePreview",
"AUNWildcardAddToPrompt",
"AnyType(str)",
"AudioInputOptions",
"JNodes_AnyToString",
@ -47463,7 +47563,7 @@
],
"https://github.com/lum3on/ComfyUI_NativeBlockSwap": [
[
"wanBlockSwap"
"nativeWanVideoBlockSwap"
],
{
"title_aux": "ComfyUI_NativeBlockSwap"
@ -47704,38 +47804,6 @@
"EasyResize",
"EasyWan22Prompt",
"Easy_Version",
"FXTDAIUpscale",
"FXTDBitDepthConvert",
"FXTDCameraShake",
"FXTDCinematicPromptEncoder",
"FXTDCompressionArtifacts",
"FXTDDepthOfField",
"FXTDDownscale32bit",
"FXTDEXRChannelMerge",
"FXTDFilmGrain",
"FXTDFilmGrainAdvanced",
"FXTDFilmLook",
"FXTDHelp",
"FXTDLensEffects",
"FXTDMotionBlur",
"FXTDPhysicalCamera",
"FXTDProFilmEffects",
"FXTDProUpscale",
"FXTDRadianceLoader",
"FXTDRealisticGrain",
"FXTDResolution",
"FXTDRollingShutter",
"FXTDSaveEXR",
"FXTDSaveEXRCryptomatte",
"FXTDSaveEXRMultiLayer",
"FXTDSaveEXRSequence",
"FXTDUpscaleBySize",
"FXTDUpscaleTiled",
"FXTDWhiteBalance",
"FXTD_DepthMapGenerator",
"FXTD_Grade",
"FXTD_RadianceViewer",
"FXTD_Radiance_Sampler_Pro",
"Float32ColorCorrect",
"GPUTensorOps",
"HDR360Generate",
@ -47753,8 +47821,6 @@
"NAGParamtersSetting",
"OCIOColorTransform",
"OCIOListColorspaces",
"RadianceGPUColorMatrix",
"RadianceLUTApply",
"RadianceLogCurveDecode",
"RadianceOCIOColorTransformV2",
"RadianceVAEDecode",
@ -48419,6 +48485,7 @@
"mrmth_ag_NoiseMathNode",
"mrmth_ag_SelectiveGuiderMathNode",
"mrmth_ag_SigmasMathNode",
"mrmth_ag_StringMathNode",
"mrmth_ag_VAEMathNode",
"mrmth_ag_VideoMathNode"
],
@ -48836,6 +48903,7 @@
"IterVideoRouter",
"IterationSwitch",
"LatentExists",
"LtxResolutionPicker",
"MaskDirectionalExtend",
"MaskExists",
"MultiChannelSlicer",
@ -51149,14 +51217,20 @@
],
"https://github.com/olivv-cs/ComfyUI-FunPack": [
[
"FunPackApplyLoraWeights",
"FunPackClipVisionOutputCombine",
"FunPackContinueVideo",
"FunPackGemmaEmbeddingRefiner",
"FunPackHybridEuler2SSampler",
"FunPackLoraLoader",
"FunPackLorebookEnhancer",
"FunPackPromptCombiner",
"FunPackPromptEnhancer",
"FunPackSaveRefinementLatent",
"FunPackStoryMemKeyframeExtractor",
"FunPackStoryMemLastFrameExtractor",
"FunPackStoryWriter",
"FunPackVideoRefiner",
"FunPackVideoStitch"
],
{
@ -55181,6 +55255,7 @@
"CameraMoveVideoNode",
"CameraShakeNode",
"CameraShakeVideoNode",
"ChromaticAberrationNode",
"CloseUpImageNode",
"CloseUpNode",
"ComfyAddSoundtrack",
@ -55692,16 +55767,6 @@
"title_aux": "ComfyUI_Memeplex_DALLE"
}
],
"https://github.com/shiertier/ComfyUI-TeaCache-lumina2": [
[
"TeaCacheForLumina2",
"TeaCacheForLuminaAuto",
"TeaCacheForLuminaNext"
],
{
"title_aux": "ComfyUI-TeaCache-Lumina"
}
],
"https://github.com/shiimizu/ComfyUI-PhotoMaker-Plus": [
[
"PhotoMakerEncodePlus",
@ -55921,6 +55986,8 @@
],
"https://github.com/silveroxides/ComfyUI-ModelUtils": [
[
"BaseInfoMetaDownloaderNode",
"BaseModelInfoLoader",
"CheckpointMetaKeys",
"CheckpointPruneKeys",
"CheckpointRenameKeys",
@ -57660,6 +57727,7 @@
"VACEOutpaint",
"WanVACEBatchContext",
"WanVACEExtend",
"WanVACEInpaint",
"WanVACEPrep",
"WanVACEPrepBatch"
],
@ -58325,7 +58393,8 @@
"https://github.com/tea-time-labs/sweet-tea-nodes": [
[
"UltimateSDUpscalePoseTiled",
"WanVaceToVideoCapsInpaint"
"WanVaceToVideoCapsInpaint",
"WanVaceToVideoHybridSubjectSwap"
],
{
"title_aux": "sweet-tea-nodes"
@ -60098,6 +60167,7 @@
"Trellis2FillHolesWithCuMesh",
"Trellis2FillHolesWithMeshlib",
"Trellis2ImageCondGenerator",
"Trellis2ImageCondMultiViewGenerator",
"Trellis2LaplacianSmoothingWithOpen3d",
"Trellis2LoadImageWithTransparency",
"Trellis2LoadMesh",
@ -60127,7 +60197,9 @@
"Trellis2RenderMultiView",
"Trellis2SaveImage",
"Trellis2ShapeCascadeGenerator",
"Trellis2ShapeCascadeMultiViewGenerator",
"Trellis2ShapeGenerator",
"Trellis2ShapeMultiViewGenerator",
"Trellis2SimplifyMesh",
"Trellis2SimplifyMeshAdvanced",
"Trellis2SimplifyTrimesh",
@ -60135,8 +60207,10 @@
"Trellis2SmoothNormals",
"Trellis2SparseGenerator",
"Trellis2SparseGeneratorWithReconViaGen",
"Trellis2SparseMultiViewGenerator",
"Trellis2StringSelector",
"Trellis2TexSlatGenerator",
"Trellis2TexSlatMultiViewGenerator",
"Trellis2TrimeshToMeshWithVoxel",
"Trellis2UnWrapAndRasterizer",
"Trellis2UnWrapTrimesh",
@ -60293,6 +60367,7 @@
"VRGDG_DisplayIndex",
"VRGDG_DurationIndexFloat",
"VRGDG_Extract_Frame_Number",
"VRGDG_GeneralGGUF",
"VRGDG_GeneralPromptBatcher",
"VRGDG_GeneralVLM",
"VRGDG_GetAudioFilePath",
@ -60386,6 +60461,7 @@
"VRGDG_StoryBoardCreator",
"VRGDG_String2Json",
"VRGDG_StringConcat",
"VRGDG_SuperGemmaGGUFChat",
"VRGDG_TextBox",
"VRGDG_ThemeSplitter",
"VRGDG_TimecodeFromIndex",
@ -61055,10 +61131,12 @@
],
"https://github.com/wildminder/ComfyUI-VoxCPM": [
[
"VoxCPM_AdvancedParams",
"VoxCPM_DatasetMaker",
"VoxCPM_LoraTrainer",
"VoxCPM_TTS",
"VoxCPM_TrainConfig"
"VoxCPM_TrainConfig",
"VoxCPM_VoiceCloning"
],
{
"title_aux": "ComfyUI-VoxCPM"
@ -61502,7 +61580,8 @@
],
"https://github.com/wywywywy/ComfyUI-pause": [
[
"PauseWorkflowNode"
"PauseWorkflowNode",
"PauseWorkflowNodeWithSound"
],
{
"title_aux": "ComfyUI Pause Workflow Node"
@ -62317,6 +62396,7 @@
"MaskSmartValleySplit",
"MaskSplitFilter",
"MaskTopNFilter",
"TextBracketReplace",
"TextKeyword",
"YC Extract Number",
"YC Mask Condition Switch",
@ -62999,12 +63079,13 @@
"Y7Nodes_ColorMatchMasked",
"Y7Nodes_CropToNearestMultiple",
"Y7Nodes_ImageBatchPath",
"Y7Nodes_ImageRow",
"Y7Nodes_ImageSizePresets",
"Y7Nodes_ImageStitcher",
"Y7Nodes_JoyCaption",
"Y7Nodes_JoyCaption_ExtraOptions",
"Y7Nodes_LMStudioText",
"Y7Nodes_LMStudioVision",
"Y7Nodes_LoadImage",
"Y7Nodes_PasteCroppedImageBack",
"Y7Nodes_PromptEnhancerFlux",
"Y7Nodes_PromptEnhancerFlux2",
@ -63653,6 +63734,7 @@
"ImageScaleByLongSide",
"ImageScaleByShortSide",
"ImageScaleByShortSideFactor",
"ImageScaleByShortSideTarget",
"ImageSharpen",
"ImageUpscale"
],

View File

@ -191,11 +191,20 @@ paths:
description: Mapping of node packages to node classes
/customnode/fetch_updates:
get:
post:
summary: Check for updates
description: Fetches updates for custom nodes
parameters:
- $ref: '#/components/parameters/modeParam'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
mode:
type: string
enum: [local, remote, default]
description: Source mode (e.g., "local", "remote")
responses:
'200':
description: No updates available
@ -423,13 +432,22 @@ paths:
# Queue Management Endpoints
/manager/queue/update_all:
get:
post:
summary: Update all custom nodes
description: Queues update operations for all installed custom nodes
security:
- securityLevel: []
parameters:
- $ref: '#/components/parameters/modeParam'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
mode:
type: string
enum: [local, remote, default]
description: Source mode (e.g., "local", "remote")
responses:
'200':
description: Update queued successfully
@ -439,7 +457,7 @@ paths:
description: Security policy violation
/manager/queue/reset:
get:
post:
summary: Reset queue
description: Resets the operation queue
responses:
@ -479,7 +497,7 @@ paths:
description: Target node not found or security issue
/manager/queue/start:
get:
post:
summary: Start queue processing
description: Starts processing the operation queue
responses:
@ -575,7 +593,7 @@ paths:
description: Disable operation queued successfully
/manager/queue/update_comfyui:
get:
post:
summary: Update ComfyUI
description: Queues an update operation for ComfyUI itself
responses:
@ -621,13 +639,22 @@ paths:
$ref: '#/components/schemas/SnapshotItem'
/snapshot/remove:
get:
post:
summary: Remove snapshot
description: Removes a specified snapshot
security:
- securityLevel: []
parameters:
- $ref: '#/components/parameters/targetParam'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [target]
properties:
target:
type: string
description: Target identifier
responses:
'200':
description: Snapshot removed successfully
@ -637,13 +664,22 @@ paths:
description: Security policy violation
/snapshot/restore:
get:
post:
summary: Restore snapshot
description: Restores a specified snapshot
security:
- securityLevel: []
parameters:
- $ref: '#/components/parameters/targetParam'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [target]
properties:
target:
type: string
description: Target identifier
responses:
'200':
description: Snapshot restoration scheduled
@ -667,7 +703,7 @@ paths:
description: Error creating snapshot
/snapshot/save:
get:
post:
summary: Save snapshot
description: Saves the current system state as a new snapshot
responses:
@ -699,15 +735,19 @@ paths:
description: Error retrieving versions
/comfyui_manager/comfyui_switch_version:
get:
post:
summary: Switch ComfyUI version
description: Switches to a specified ComfyUI version
parameters:
- name: ver
in: query
description: Target version
schema:
type: string
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
ver:
type: string
description: Target version
responses:
'200':
description: Version switch successful
@ -715,7 +755,7 @@ paths:
description: Error switching version
/manager/reboot:
get:
post:
summary: Reboot ComfyUI
description: Restarts the ComfyUI server
security:
@ -746,7 +786,32 @@ paths:
text/plain:
schema:
type: string
post:
summary: Set preview method
description: Sets the latent preview method (write-only; use GET to read)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
enum: [auto, latent2rgb, taesd, none]
description: New preview method
responses:
'200':
description: Setting updated
content:
text/plain:
schema:
type: string
'400':
description: Invalid value
/manager/db_mode:
get:
summary: Get or set database mode
@ -766,7 +831,32 @@ paths:
text/plain:
schema:
type: string
post:
summary: Set database mode
description: Sets the database mode (write-only; use GET to read)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
enum: [channel, local, remote]
description: New database mode
responses:
'200':
description: Setting updated
content:
text/plain:
schema:
type: string
'400':
description: Invalid value
/manager/policy/component:
get:
summary: Get or set component policy
@ -785,7 +875,29 @@ paths:
text/plain:
schema:
type: string
post:
summary: Set component policy
description: Sets the component policy (write-only; use GET to read)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
description: New component policy
responses:
'200':
description: Setting updated
content:
text/plain:
schema:
type: string
/manager/policy/update:
get:
summary: Get or set update policy
@ -805,7 +917,32 @@ paths:
text/plain:
schema:
type: string
post:
summary: Set update policy
description: Sets the update policy (write-only; use GET to read)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
enum: [stable, nightly, nightly-comfyui]
description: New update policy
responses:
'200':
description: Setting updated
content:
text/plain:
schema:
type: string
'400':
description: Invalid value
/manager/channel_url_list:
get:
summary: Get or set channel URL
@ -836,7 +973,29 @@ paths:
type: string
url:
type: string
post:
summary: Set channel URL
description: Sets the channel URL for custom node sources (write-only; use GET to read current + list)
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
description: New channel name
responses:
'200':
description: Setting updated
content:
text/plain:
schema:
type: string
# Component Management Endpoints
/manager/component/save:
post:

View File

@ -1,7 +1,7 @@
[project]
name = "comfyui-manager"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
version = "3.39.3"
version = "3.40"
license = { file = "LICENSE.txt" }
dependencies = ["GitPython", "PyGithub", "matrix-nio", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]

View File

@ -0,0 +1,121 @@
"""AC verification for wi-001 (B2): Content-Type rejection helper.
Validates _reject_simple_form_content_type against the 5-item curl matrix
from the WI's acceptance criteria using aiohttp test client. The test
builds a minimal aiohttp app that mirrors the helper's wiring into a
no-body POST handler, so we exercise the real request.content_type
parsing path rather than a mock.
AC matrix:
form-url 400
multipart 400
text/plain 400
no-CT 200
application/json 200
"""
import asyncio
import unittest
from pathlib import Path
from aiohttp import web
from aiohttp.test_utils import TestClient, TestServer
# Parse the helper from manager_server.py without importing it, to avoid
# pulling in the full ComfyUI/PromptServer stack. Note: we intentionally do
# NOT add the `glob/` directory to sys.path — the dir name would shadow
# Python's stdlib `glob` module and break pytest collection.
REPO_ROOT = Path(__file__).resolve().parent.parent
def _load_helper():
"""Parse manager_server.py and execute only the helper definition."""
import ast
source = (REPO_ROOT / "glob" / "manager_server.py").read_text()
tree = ast.parse(source)
wanted = {"_SIMPLE_FORM_CONTENT_TYPES", "_reject_simple_form_content_type"}
nodes = []
for node in tree.body:
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id in wanted:
nodes.append(node)
elif isinstance(node, ast.FunctionDef) and node.name in wanted:
nodes.append(node)
module = ast.Module(body=nodes, type_ignores=[])
ns = {"web": web, "frozenset": frozenset}
exec(compile(module, "manager_server_helpers", "exec"), ns)
return ns["_reject_simple_form_content_type"]
_reject_simple_form_content_type = _load_helper()
async def _handler(request):
resp = _reject_simple_form_content_type(request)
if resp is not None:
return resp
return web.Response(status=200)
class ContentTypeRejectionTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.loop = asyncio.new_event_loop()
asyncio.set_event_loop(cls.loop)
app = web.Application()
app.router.add_post("/noop", _handler)
cls.server = TestServer(app, loop=cls.loop)
cls.client = TestClient(cls.server, loop=cls.loop)
cls.loop.run_until_complete(cls.client.start_server())
@classmethod
def tearDownClass(cls):
cls.loop.run_until_complete(cls.client.close())
cls.loop.close()
def _post(self, headers):
async def go():
return await self.client.post("/noop", headers=headers, data=b"")
return self.loop.run_until_complete(go())
def test_form_urlencoded_rejected(self):
r = self._post({"Content-Type": "application/x-www-form-urlencoded"})
self.assertEqual(r.status, 400)
def test_multipart_form_data_rejected(self):
# aiohttp requires a boundary for multipart; helper should still reject
# based on the primary mimetype.
r = self._post({"Content-Type": "multipart/form-data; boundary=xyz"})
self.assertEqual(r.status, 400)
def test_text_plain_rejected(self):
r = self._post({"Content-Type": "text/plain"})
self.assertEqual(r.status, 400)
def test_no_content_type_allowed(self):
# Explicitly strip Content-Type: aiohttp client may add a default,
# so we use a raw request to ensure absence is tested.
async def go():
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.post(
self.client.make_url("/noop"),
data=None,
skip_auto_headers=["Content-Type"],
) as resp:
return resp.status
status = self.loop.run_until_complete(go())
self.assertEqual(status, 200)
def test_application_json_allowed(self):
r = self._post({"Content-Type": "application/json"})
self.assertEqual(r.status, 200)
if __name__ == "__main__":
unittest.main(verbosity=2)