+
    #jC                      a  0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIHt ]P                  P                  ^ ]! ]! ]4      P                  4       P                  4      4       ^ RIHtHtHtHtHtHtHtHtHtHtHtHtHt / RRbRRbRRbR	RbR
RbRRbRRbRRbRRbRRbRRbRRbRRbRRbRRbRRbRRb/ RRbRRbRRbRRbR RbR!R"bR#R"bR$R"bR%R&bR'R&bR(R&bR)R&bR*R&bR+R&bR,R-bR.R-bR/R-bC/ R0R-bR1R2bR3R2bR4R5bR6R5bR7R8bR9R8bR:R;bR<R;bR=R>bR?R>bR@RAbRBRbRCRDbRERFbRGRFbCtRH]RI&   RJRKRLRK/tRH]RM&   RN RO lt RP RQ lt!RR RS lt"RT RU lt#RV RW lt$RX RY lt%RZ R[ lt&R\ R] lt'R^R/R_ R` llt(RdRa Rb llt)]*Rc8X  d   ]PV                  ! ])! 4       4       R# R# )eu  
check_deps.py — Verify a ComfyUI workflow's dependencies (custom nodes, models,
embeddings) against a running server.

Improvements over v1:
  - Cloud-aware endpoint mapping (handles `/api/experiment/models/{folder}` and
    `/api/object_info` variants verified against live cloud API)
  - Distinguishes 200-empty (genuinely no models in folder) vs 404
    (folder doesn't exist) vs 403 (auth/tier issue) — no silent passes
  - Outputs concrete remediation commands (e.g. `comfy node install <name>`)
    when nodes are missing
  - Detects embedding references inside prompt strings as model deps
  - Skips check on cloud free tier `/api/object_info` (403) without false alarm
  - Accepts API key from CLI flag OR $COMFY_CLOUD_API_KEY env var

Usage:
    python3 check_deps.py workflow_api.json
    python3 check_deps.py workflow_api.json --host 127.0.0.1 --port 8188
    python3 check_deps.py workflow_api.json --host https://cloud.comfy.org

Stdlib-only. Python 3.10+.
)annotationsN)Path)DEFAULT_LOCAL_HOSTENV_API_KEY	emit_jsonfolder_aliases_forhttp_getis_cloud_hostiter_embedding_refsiter_model_deps
iter_nodesparse_model_listresolve_api_keyresolve_urlunwrap_workflowzPower Lora Loader (rgthree)zrgthree-comfyzImage Comparer (rgthree)zSeed (rgthree)zDisplay Any (rgthree)zDisplay Int (rgthree)FaceDetailerzcomfyui-impact-packDetailerForEachBboxDetectorSEGS	SAMLoaderImpactWildcardProcessorUltralyticsDetectorProviderzcomfyui-impact-subpackz
Image Savezwas-node-suite-comfyuizNumber CounterzText Stringzeasy fullLoaderzcomfyui-easy-usezeasy positivezeasy negativez	easy seedzeasy imageSaveVHS_VideoCombinezcomfyui-videohelpersuiteVHS_LoadVideoVHS_LoadAudio ADE_AnimateDiffLoaderWithContextzcomfyui-animatediff-evolvedADE_AnimateDiffLoaderGen1ADE_LoadAnimateDiffModelCannyEdgePreprocessorcomfyui_controlnet_auxDWPreprocessorOpenposePreprocessorDepthAnythingPreprocessorZoe_DepthAnythingPreprocessorAnimalPosePreprocessorIPAdapterAdvancedcomfyui_ipadapter_plusIPAdapterUnifiedLoaderIPAdapterModelLoaderIPAdapterInsightFaceLoaderInstantIDModelLoadercomfyui_instantidApplyInstantIDzGetImageSize+comfyui_essentialszImageBatchMultiple+zShowText|pyssssszcomfyui-custom-scriptszPreviewImage|pysssssSUPIR_Upscalezcomfyui-supirSUPIR_first_stageUNETLoaderGGUFzComfyUI-GGUFDualCLIPLoaderGGUFFlorence2Runzcomfyui-florence2zImage Filter AdjustmentsPhotoMakerLoaderzComfyUI-PhotoMaker-PlusWanVideoSamplerzComfyUI-WanVideoWrapperWanVideoModelLoaderzdict[str, str]NODE_TO_PACKAGEHunyuanVideoSamplerz4https://github.com/kijai/ComfyUI-HunyuanVideoWrapperHunyuanVideoModelLoaderNODE_TO_GIT_URLc               $    V ^8  d   QhRRRRRR/# )   urlstrheadersdictreturn#tuple[set[str] | None, dict | None] )formats   "d/opt/hermes-venv/lib/python3.14/site-packages/../../../skills/creative/comfyui/scripts/check_deps.py__annotate__rD   {   s'     [ [3 [ [2U [    c                (   \        W^^R7      pVP                  ^8X  dL    VP                  4       p\        V\        4      '       d   \        VP                  4       4      R3#  RR^RR/3# VP                  R8X  d    VP                  4       pRRRRR	R
V/3# VP                  R8X  d	   RRRRR/3# RRVP                  RRR
VP                  4       R,          /3#   \         d     Li ; i  \         d    RTP                  4       R,          /p Li ; i)zwReturns (installed_node_set, error_info). Error info is a dict if we
couldn't query (e.g. cloud free tier), else None.
r=   retriestimeoutNhttp_statusreasonznon-dict response  rawN   N	forbiddenbody  endpoint not found
unexpected)	r   statusjson
isinstancer>   setkeys	Exceptiontext)r;   r=   rdatarQ   s   &&   rC   fetch_object_infor^   {   s     	q"=Axx3	668D$%%499;'-- & mS(4GHHHxx3	+668D mS(KNNNxx3mS(4HIII-8\61668TX>ZZZ  		  	+1668D>*D	+s$   A C =C* C'&C'*$DDc          
     ,    V ^8  d   QhRRRRRRRRRR	/# 
r:   baser<   folderr=   r>   is_cloudboolr?   r@   rA   )rB   s   "rC   rD   rD      s;     C C
CC%)C8<C(CrE   c                  \        V RV 2VR7      p\        WB^^R7      pVP                  ^8X  d    \        VP	                  4       4      R3# VP                  R8X  dn   VP                  4       p VP	                  4       p\        V\        4      '       d   VP                  R4      MRpVR8X  d   \        4       RRRRRV/3# RRRRRRV/3# VP                  R8X  d    VP	                  4       pRRRRRRV/3# RRVP                  RR/3#   \
         d    \        4       R^RR/3u # i ; i  \
         d    R	TR
,          /p Li ; i  \
         d    / p Lhi ; i)zFSingle-folder fetch, no aliasing. Returns (installed_set, error_info).z/models/rc   rG   NrJ   rK   znon-list responserR   rM   rN   codefolder_not_foundfolder_empty_or_unknownrQ   rS   rL   rP   rT   )r   r   rU   r   rV   rZ   rX   r[   rW   r>   get)	ra   rb   r=   rc   r;   r\   	body_textrQ   rg   s	   &&&$     rC   _fetch_one_folderrl      s    dhvh/(
CCq"=Axx3	N#AFFH-t33 	xx3FFH		,668D $.dD#9#9txxt%% 5=#x9RTZ\`aaamS(4H&RVWWWxx3	668D mS(KNNN-8\BBB)  	N5=#x9LMMM	N  	,9T?+D	,  	D	s5   D	 /D* E 	D'&D'*EEEEc          
     ,    V ^8  d   QhRRRRRRRRRR	/# r`   rA   )rB   s   "rC   rD   rD      s4      
%)8<(rE   c                   \        V4      p\        4       pRpRpV F/  p\        WW#R7      w  rV	e   VP                  V	4       RpRpK-  T
pK1  	  V'       g   RV3# VR3# )u<  Fetch installed models for a folder, trying aliases.

Folder renames over time (e.g. unet → diffusion_models, clip → text_encoders)
mean a workflow asking for a model in `unet` may need to look in
`diffusion_models`. We union models from every reachable alias.

Returns (combined_set | None, last_error | None).
FNrf   T)r   rX   rl   update)ra   rb   r=   rc   aliasescombinedany_successlast_erraliasmodelserrs   &&&$       rC   fetch_models_for_folderrw      sq     !(GHK H'WPOOF#KHH  X~T>rE   c               (    V ^8  d   QhRRRRRRRR/# )	r:   ra   r<   r=   r>   rc   rd   r?   r@   rA   )rB   s   "rC   rD   rD      s/     C C3 C CD CEh CrE   c                  V'       d   \        V RVRR7      # \        \        V RRR7      V^R7      pVP                  ^8X  d    VP	                  4       p\        V\        4      '       de   \        4       pV FP  p\        V\        4      '       g   K  VP                  V4       VP                  \        V4      P                  4       KR  	  VR3#  RRVP                  R	R
/3#   \         d     Li ; i)zLLocal ComfyUI exposes /embeddings; cloud uses /experiment/models/embeddings.
embeddingsTrf   z/embeddingsF)r=   rH   NrJ   rK   rT   )rw   r   r   rU   rV   rW   listrX   r<   addr   stemrZ   )ra   r=   rc   r\   r]   namesns   &&$    rC   fetch_embeddingsr      s    &t\7TRRT=5A7\]^Axx3	668D$%% A!!S))		!		$q',,/	 
 d{" & -8\BBB  		s   AC! <C! !C/.C/c                    V ^8  d   QhRRRR/# )r:   namer<   r?   set[str]rA   )rB   s   "rC   rD   rD      s     
 
c 
h 
rE   c                   V 0pVP                  \        V 4      P                  4       VP                  \        V 4      P                  4       RV 9   g   RV 9   d^   V P	                  RR4      P                  R4      R,          pVP                  V4       VP                  \        V4      P                  4       V Uu0 uF  q3'       g   K  VkK  	  up# u upi )zRGenerate matching variants of a model name (with/without extension, slashes, etc.)/\)r|   r   r}   r   replacesplit)r   sflatxs   &   rC   normalize_for_matchr      s    	AEE$t*//EE$t*//
d{ddl||D#&,,S1"5	d	d4jooq!AAAqs   ;	C	Cc               $    V ^8  d   QhRRRRRR/# )r:   neededr<   	installedr   r?   rd   rA   )rB   s   "rC   rD   rD      s!     2 2# 2( 2t 2rE   c                    V'       g   R # \        V 4      p\        4       pV F  pVP                  \        V4      4       K  	  \        W#,          4      # )F)r   rX   ro   rd   )r   r   needed_variantsinstalled_norminsts   &&   rC   model_presentr      sE    )&1O"uN1$78 011rE   c                    V ^8  d   QhRRRR/# r:   
node_classr<   r?   
str | NonerA   )rB   s   "rC   rD   rD      s       
 rE   c                J    \         P                  V 4      pV'       d   R V 2# R# )zcomfy node install N)r5   rj   )r   pkgs   & rC   suggest_install_commandr      s%    


j
)C
$SE**rE   c                    V ^8  d   QhRRRR/# r   rA   )rB   s   "rC   rD   rD     s     + + +
 +rE   c                ,    \         P                  V 4      # )zyFor nodes not on the registry, return a git URL the user can hand to
ComfyUI-Manager's `/manager/queue/install` endpoint.)r8   rj   )r   s   &rC   suggest_git_urlr     s     z**rE   api_keyc               (    V ^8  d   QhRRRRRRRR/# )r:   workflowr>   hostr<   r   r   r?   rA   )rB   s   "rC   rD   rD     s2     t ttt+5t	trE   c                  / pV'       d   W#R &   \        V4      pVP                  R4      p\        4       p\        V 4       F  w  rxVP	                  VR,          4       K  	  \        VRVR7      p	\        W4      w  r. pRpV
f   RpMn\        V4       F_  pW9  g   K  RV/p\        V4      p\        V4      pV'       d   VVR&   MV'       d   VVR	&   R
V 2VR&   MRVR&   VP                  V4       Ka  	  / p. p/ p\        V 4       F  pVR,          pVV9  d   \        VVW4R7      VV&   VV,          w  ppVf   TP                  TT;'       g    / 4       KR  \        VR,          V4      '       d   Km  \        V4      pRV RVR,          : 2VR&   VP                  V4       K  	  \!        WSVR7      w  pp. p\        4       p\#        V 4       F[  w  ppVV3V9   d   K  VP	                  VV34       Vf   K*  \        VV4      '       d   K>  VP                  RVRVRRRRV R2/4       K]  	  V'       d   Vf   VP                  RV4       V'       * ;'       d&    V'       * ;'       d    V'       * ;'       d    V'       * pRTRTRV'       d   TMRRTRTRTRTRV
e   \%        V
4      MRR\%        V4      R\        V4      R VR!V/# )"z	X-API-Keyr   
class_typez/object_inforf   FNTfix_commandfix_git_urlz8Not on registry. Install via Manager with this git URL: fix_hintzdSearch https://registry.comfy.org or use ComfyUI-Manager UI to find the package providing this node.rb   valuez8comfy model download --url <URL> --relative-path models/z --filename node_idembedding_namerz   z	Download z|.pt or .safetensors and place in models/embeddings/, or `comfy model download --url <URL> --relative-path models/embeddings`is_readynode_check_skippednode_check_skip_reasonmissing_nodesmissing_modelsmissing_embeddingsfolder_errorsinstalled_node_countrequired_node_countrequired_nodesr   rc   )r	   rstriprX   r   r|   r   r^   sortedr   r   appendr   rw   
setdefaultr   r>   r   r
   len) r   r   r   r=   rc   ra   r   _nodeobject_info_urlinstalled_nodesobj_errr   r   clsentrycmdgit_urlmodel_cacher   r   deprb   r   rv   emb_installedemb_errr   seen_embnidemb_namer   s    &&$                             rC   
check_depsr     s7    !G&T"H;;sD  #uNh'4-. ( "$JO0JO "M!.)C)%s+-c2)#.+.E-(+2E-(RSZR[\ *%
Z *% $$U+# *( CEK!#N%'Mx(X$"9fg#K %V,	3$$VSYYB7S\955IEJ6( S!'l-/ * !!%(% )* .dhOM7%'%(UH,X6X?h&c8_%  X}55%%3 (,z *9 :	' 	 7( =(  w7  	# 	#	# 	#	# 	# #"	  	H0 -?'T.08SO 4Y]s>2&0H rE   c                    V ^8  d   QhRRRR/# )r:   argvzlist[str] | Noner?   intrA   )rB   s   "rC   rD   rD     s     / / /3 /rE   c                6   \         P                  ! R R7      pVP                  RRR7       VP                  R\        RR7       VP                  R\        R	R
7       VP                  RR\
         R2R7       VP                  RRRR7       VP                  V 4      pVP                  pVP                  eP   ^ RI	H
pHp T! RV9   d   TMRV 24      pVP                   RVP                   2pV! VP                  VR7      4      p\        VP                  4      p\!        VP"                  4      P%                  4       p	V	P'                  4       '       g   \)        RRVP"                   2/4       ^#  V	P+                  4       ;_uu_ 4       p
\,        P.                  ! V
4      pRRR4       \1        X4      p \9        YTR7      p\)        T4       TR,          '       g   ^# TP<                  '       d   TR,          '       d   ^# ^ #   + '       g   i     Lj; i  \2         d"   p\)        R\5        T4      /4        Rp?^# Rp?i\,        P6                   d   p\)        RRT 2/4        Rp?^# Rp?ii ; i  \:         d   p\)        RRT 2RT/4        Rp?^# Rp?ii ; i) z<Check ComfyUI workflow dependencies against a running server)descriptionr   zPath to workflow API JSON file)helpz--hostzComfyUI server URL)defaultr   z--portz#Server port (overrides --host port))typer   z	--api-keyzAPI key for cloud (or set $z	 env var)z--strict
store_truez@Exit non-zero if node check is skipped (e.g. on cloud free tier))actionr   N)urlparse
urlunparsez://zhttp://:)netlocerrorzWorkflow file not found: zInvalid JSON: )r   r   zDep check failed: r   r   r   )argparseArgumentParseradd_argumentr   r   r   
parse_argsr   porturllib.parser   r   hostname_replacer   r   r   r   
expanduserexistsr   openrV   loadr   
ValueErrorr<   JSONDecodeErrorr   rZ   strict)r   pargsr   r   r   parsed
new_netlocr   wf_pathfpayloadr   eresults   &              rC   mainr     s(   ,jkANN:$DNENN8%7>RNSNN8#,QNRNN;'B;-y%YNZNN:lZ  \<<D99Dyy5%4-$wtf5EF($))5
&///<=dll+G4==!,,.G>>77GHI	\\^^qiilG "7+HA
 f*{{{v233- ^  7CF#$ 7nQC012  704fdCDsZ   /H H H 2I0  H	H I-H::I-I-I((I-0J;JJ__main__)N),__conditional_annotations____doc__
__future__r   r   rV   syspathlibr   pathinsertr<   __file__resolveparent_commonr   r   r   r   r   r	   r
   r   r   r   r   r   r   r5   __annotations__r8   r^   rl   rw   r   r   r   r   r   r   r   __name__exit)r   s   @rC   <module>r     s  . #   
  3tH~--/667 8   E#!?E# E# o	E#
 _E# _E# )E# ,E# -E# &E# 4E# "#;E#  *!E#" .#E#$ +%E#( ))E#* '+E#, '-E#. #/E#0 (1E#4 25E#6 /7E#8 /9E#< '(E=E#>  !>?E#@  =AE#D 5EE#F .GE#H 4IE#J  !9KE#L $%=ME#N 6OE#R 1SE#T 6UE#V 4WE#X !":YE#\ /]E#^ )_E#b )cE#d /eE#h 0iE#j 4kE#n _oE#p qE#t nuE#v .wE#z '{E#~  8E#B 1CE#F 0GE#H 4IE# ET QU# [0C@8C0
2+t8<tn/d zHHTV rE   