
    )j΃                       U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
mZ ddlmZ ddlmZmZ ddlmZ 	 ddlZdZn# e$ r d	ZddlZddlZY nw xY wd
ZdZdZdZdZdZdZh dZ dZ!h dZ"h dZ#de$d<   ddgddgddgddgddgdZ%de$d<   dd$Z&i d%d&gd'd&gd(d&gd)d&gd*d&gd+d,gd-d,gd.d,gd/d0gd1d2gd3d2gd4d2gd5d6gd7d8d9gd:g d;d<d=gd>d?gd@gd?gdAgdBgdCgdDgdEgdFgdGgdGgdGgdHgg dIZ'dJe$dK<   g dLZ(dMe$dN<   h dOZ) ej*        dPej+                  Z,dQZ-dRhZ.ddUZ/ddVdd[Z0dd\Z1dd]dd_Z2ddbZ3e G dc dd                      Z4eefddjZ5dddddeded	ddk
dd}Z6d~Z7er G d dej8                  Z9ddZ:ddZ;ddZ<ddZ=ddZ>ddZ?ddZ@ddZAddZBddZCddZDddZEdZFdZGddZHddZIddZJddZKddddZLddZMdS )u  
_common.py — Shared logic for ComfyUI skill scripts.

Single source of truth for:
- HTTP transport (with retry/backoff, streaming, timeout handling)
- Cloud detection and endpoint mapping (local ComfyUI vs Comfy Cloud)
- Workflow node-type catalogs (param patterns, model loaders, output nodes)
- API-format validation
- Path-traversal-safe file writes
- API-key loading from env / CLI

Stdlib-only by design (with optional `requests` upgrade if installed). Python 3.10+.
    )annotationsN)	dataclass)Path)AnyIterator)urlparseTFzhttp://127.0.0.1:8188zhttps://cloud.comfy.orgCOMFY_CLOUD_API_KEY<      g      ?g      >@>   
                i   >   	SaveAudio	SaveVideoCogVideoSamplerLTXVideoSamplerSaveAnimatedPNGWanVideoSamplerSaveAnimatedWEBPVHS_VideoCombineHunyuanVideoSamplerSaveAnimateDiffVideoSVD_img2vid_Conditioning>   
Image Saveeasy imageSaveShowText|pysssssPreviewImage|pysssssImage Save With MetadataSave3DSaveGLB	ImageSaver   	SaveImager   
SaveLatentPreviewAudioPreviewImagePreviewVideor   r   r   set[str]OUTPUT_NODESunetdiffusion_modelscliptext_encoders
controlnetcontrol_net)r.   r/   r0   r1   r2   zdict[str, list[str]]FOLDER_ALIASESfolderstrreturn	list[str]c                :    t                               | | g          S )z8Return the search order of folder names (primary first).)r4   get)r5   s    A/home/wildlama/.hermes/skills/creative/comfyui/scripts/_common.pyfolder_aliases_forr<   f   s    fvh///    CheckpointLoaderSimple)	ckpt_namecheckpointsCheckpointLoaderzCheckpointLoader (Simple)ImageOnlyCheckpointLoaderunCLIPCheckpointLoader
LoraLoader)	lora_namelorasLoraLoaderModelOnlyLoraLoaderTagsQuery	VAELoader)vae_namevaeControlNetLoader)control_net_namer2   DiffControlNetLoaderControlNetLoaderAdvanced
CLIPLoader)	clip_namer0   DualCLIPLoader
clip_name1r0   
clip_name2r0   TripleCLIPLoader)rS   rU   )
clip_name3r0   CLIPVisionLoader)rQ   clip_vision
UNETLoader)	unet_namer.   )
model_namer/   )r]   upscale_models)style_model_namestyle_models)gligen_namegligen)hypernetwork_namehypernetworks)ipadapter_file	ipadapter)instantid_file	instantid)r]   animatediff_models)photomaker_model_name
photomaker)DiffusionModelLoaderUNETLoaderGGUFUpscaleModelLoaderStyleModelLoaderGLIGENLoaderHypernetworkLoaderIPAdapterModelLoaderInstantIDModelLoaderADE_LoadAnimateDiffModel ADE_AnimateDiffLoaderWithContextADE_AnimateDiffLoaderGen1PhotoMakerLoaderModelSamplingFluxz dict[str, list[tuple[str, str]]]MODEL_LOADERS)u)CLIPTextEncodetextprompt)CLIPTextEncodeSDXLtext_gr|   )r}   text_lprompt_l)CLIPTextEncodeSDXLRefinerr{   refiner_prompt)CLIPTextEncodeFluxclip_lr   )r   t5xxlr|   )r   guidancer   )zsmZ CLIPTextEncoder{   r|   )BNK_CLIPTextEncodeAdvancedr{   r|   )KSamplerseedr   )r   stepsr   )r   cfgr   )r   sampler_namer   )r   	schedulerr   )r   denoiser   )KSamplerAdvanced
noise_seedr   )r   r   r   )r   r   r   )r   r   r   )r   r   r   )r   start_at_stepr   )r   end_at_stepr   )RandomNoiser   r   )BasicSchedulerr   r   )r   r   r   )r   r   r   )KSamplerSelectr   r   )	CFGGuiderr   r   )DualCFGGuider	cfg_condsr   )r   cfg_cond2_negativecfg_negative)rx   	max_shiftr   )rx   
base_shiftr   )rx   widthmodel_width)rx   heightmodel_height)ModelSamplingSD3shiftr   )ModelSamplingDiscretesamplingr   )SDTurboSchedulerr   r   )r   r   r   )SamplerCustomr   r   )r   r   r   )EmptyLatentImager   r   )r   r   r   )r   
batch_sizer   )EmptySD3LatentImager   r   )r   r   r   )r   r   r   )EmptyHunyuanLatentVideor   r   )r   r   r   )r   lengthr   )r   r   r   )EmptyMochiLatentVideor   r   )r   r   r   )r   r   r   )EmptyLTXVLatentVideor   r   )r   r   r   )r   r   r   )LatentUpscaler   upscale_width)r   r   upscale_height)LatentUpscaleByscale_byr   )
ImageScaler   r   )r   r   r   )	LoadImageimager   )LoadImageMaskr   
mask_image)LoadImageOutputr   r   )VHS_LoadVideovideor   )VHS_LoadAudioaudior   )r>   r?   r?   )rA   r?   r?   )rB   r?   r?   )rI   rJ   rJ   )r[   r\   r\   )rl   r]   diffusion_model_name)rn   r]   upscale_model_name)rP   rQ   rQ   )rR   rT   rT   )rR   rV   rV   )rL   rM   controlnet_name)rD   rE   rE   )rD   strength_modellora_strength)rD   strength_cliplora_strength_clip)rG   rE   rE   )rG   r   r   )ControlNetApplystrengthcontrolnet_strength)ControlNetApplyAdvancedr   r   )r   start_percentcontrolnet_start)r   end_percentcontrolnet_end)IPAdapterAdvancedweightipadapter_weight)r   start_atipadapter_start)r   end_atipadapter_end)	IPAdapterr   r   )ImageUpscaleWithModelupscale_methodr   )ru   motion_scaler   )rv   r   r   )r   
frame_rater   )r   formatvideo_format)r   filename_prefixr   )r'   r   r   )r   r   r   )r   r   r   )r   r   r   )r   r   r   )r   r   r   )r   r   r   )LTXVSchedulerr   r   )r   r   r   )zSeed (rgthree)r   r   )zImage Comparer (rgthree)image_ar   )zPower Lora Loader (rgthree)PowerLoraLoaderHeaderWidget_lora_header)PrimitiveNodevalueprimitive_value)z	easy seedr   r   )zeasy positivepositiver|   )zeasy negativenegativenegative_prompt)easy fullLoaderr?   r?   )r   rJ   rJ   )r   rE   rE   )r   r   r|   )r   r   r   zlist[tuple[str, str, str]]PARAM_PATTERNS>   r{   r   r   r~   r   r   r   zc(?:^|[\s,(\[])embedding\s*:\s*([A-Za-z0-9_\-\./\\]+?)(?:\.(?:pt|safetensors|bin))?(?=[\s:,)\(\]]|$))z
.comfy.orgzcloud.comfy.orghostboolc                    t          d| v r| nd|            }|j        pd                                t          v rdS t	          fdt
          D                       S )zFTrue if the host points at Comfy Cloud (or staging/preview subdomain).z://zhttp:// Tc              3  B   K   | ]}                     |          V  d S N)endswith).0shostnames     r;   	<genexpr>z is_cloud_host.<locals>.<genexpr>U  s1      CCx  ##CCCCCCr=   )r   r   lowerCLOUD_DOMAIN_EXACTanyCLOUD_DOMAIN_SUFFIXES)r   parsedr   s     @r;   is_cloud_hostr   O  sr    etmmdd1A41A1ABBF%2,,..H%%%tCCCC-BCCCCCCr=   force_cloudbasepathr   bool | Nonec                   |                      d          } |t          |           n|}|                    d          sd|z   }|r|                    d          sd|z   }| |z   S )a  Build a URL that adds /api prefix when targeting Comfy Cloud.

    Local ComfyUI accepts both `/foo` and `/api/foo` for many endpoints.
    Cloud requires `/api/foo`.

    `path` should be a path component (e.g. "/prompt") or full path with query
    (e.g. "/view?filename=x").
    /Nz/api/z/api)rstripr   
startswith)r   r   r   clouds       r;   build_cloud_aware_urlr  X  sw     ;;sD#.#6M$KE??3 Tz T__W-- }$;r=   c                    |                      d          r/|                      d          sd| t          d          d         z   S |                      d          rd| t          d          d         z   S | dk    rdS | S )zMap a cloud endpoint path to its current canonical form.

    Handles known renames documented in the Comfy Cloud API:
      /history       -> /history_v2
      /models/<f>    -> /experiment/models/<f>
      /models        -> /experiment/models
    z/historyz/history_v2Nz/models/z/experiment/models/z/modelsz/experiment/models)r   len)r   s    r;   cloud_endpointr  j  s     z"" 64??=+I+I 6tC
OO$4$4555z"" >$tC
OO,<,<'===y##Kr=   )is_cloudr  c               n    |t          |           n|}|rt          |          }t          | ||          S )zETop-level URL resolver. Applies cloud rename + /api prefix as needed.Nr   )r   r  r  )r   r   r  r  s       r;   resolve_urlr  {  sC    #+#3M$E $d## t????r=   explicit
str | Nonec                    | r| n#t           j                            t                    }|dS |                                                    d          }|pdS )uH   Look up API key from CLI flag → env var. Strips whitespace and quotes.Nz'")osenvironr:   ENV_API_KEYstrip)r	  vals     r;   resolve_api_keyr    sM    
?((BJNN;$?$?C
{t
))++

E
"
"C;$r=   c                  J    e Zd ZU ded<   ded<   ded<   ded<   dddZddZdS )HTTPResponseintstatusdict[str, str]headersbytesbodyr6   urlutf-8encodingr7   c                :    | j                             |d          S )Nreplaceerrors)r  decode)selfr  s     r;   r{   zHTTPResponse.text  s    y;;;r=   r   c                ^    t          j        | j                            dd                    S )Nr  r  r  )jsonloadsr  r!  )r"  s    r;   r$  zHTTPResponse.json  s'    z$)**79*EEFFFr=   N)r  )r  r6   r7   r6   )r7   r   )__name__
__module____qualname____annotations__r{   r$   r=   r;   r  r    sp         KKKKKKHHH< < < < <G G G G G Gr=   r  attemptr  floatcapNonec                    t          ||d| z  z            }t          j        d|          }t          j        |           dS )z+Sleep with full-jitter exponential backoff.   r   N)minrandomuniformtimesleep)r+  r   r-  delays       r;   _sleep_backoffr7    sA    TQ'\*++EN1e$$EJur=   )
r  	json_bodydatafilesformtimeoutfollow_redirectsretriesstreamsinkmethodr  r  dict[str, str] | Noner8  r   r9  bytes | Noner:  dict | Noner;  r<  r=  r>  r?  r@  Path | Nonec       
           |i }t          |          }|                    dd           |s|t          st          d          d}t	          |	          D ]}	 t          | |||||||||
|          }|j        t          v r|dz   |	k     rt          |           D|c S # t          t          t          f$ r%}|}|dz   |	k     rt          |           Y d}~~ d}~ww xY w|r|t          d          )a  Single entry point for all HTTP traffic.

    Behavior:
      - Retries on connection errors and on HTTP statuses in RETRY_STATUS_CODES,
        with exponential backoff + jitter.
      - For cross-host redirects, drops Authorization-style headers (so signed
        URLs don't leak the API key to S3/CloudFront).
      - When `stream=True` and `sink` is a Path, streams the response body to
        disk in 64 KiB chunks instead of buffering.

    Either `json_body`, `data`, or `files`+`form` may be supplied (mutually exclusive).
    Nz
User-Agentzhermes-comfyui-skill/5.0zTMultipart upload requires the `requests` package. Install with: pip install requests)rA  r  r  r8  r9  r:  r;  r<  r=  r?  r@     z0http_request: retries exhausted with no response)dict
setdefaultHAS_REQUESTSRuntimeErrorrange
_http_oncer  RETRY_STATUS_CODESr7  TimeoutErrorConnectionErrorOSError)rA  r  r  r8  r9  r:  r;  r<  r=  r>  r?  r@  last_excr+  respes                   r;   http_requestrU    sZ   6 7mmG|%?@@@    	5  
 "&H>>  	3#$e$2BD	  D {000Wq[75J5Jw'''KKKow7 	 	 	H{W$$w'''	  
I
J
JJs%   A BBC4CCC)	x-api-keyauthorizationcookiec                  "     e Zd ZdZ fdZ xZS ) _StripSensitiveOnRedirectSessionu  Session that drops sensitive headers on cross-host redirects.

        `requests` already strips `Authorization` cross-host (rebuild_auth),
        but it does NOT strip custom headers like `X-API-Key`. We override
        `rebuild_auth` to additionally strip every header in
        `_SENSITIVE_HEADERS` when the destination is a different host —
        critical when ComfyUI Cloud's `/api/view` redirects to a signed S3 URL.
        c                   t                                          ||           	 |j        j        }|j        }t	          |          j        pd                                }t	          |          j        pd                                }|rQ|rQ||k    rM|j        }t          |	                                          D ]&}|                                t          v r||= !d S d S d S d S # t          $ r Y d S w xY w)Nr   )superrebuild_authrequestr  r   r   r   r  listkeys_SENSITIVE_HEADERS	Exception)
r"  prepared_requestresponseold_urlnew_urlold_hostnew_hostr  key	__class__s
            r;   r]  z-_StripSensitiveOnRedirectSession.rebuild_auth  s   GG  !18<<<"*.*.$W--6<"CCEE$W--6<"CCEE - -X-A-A.6G#GLLNN33 - -99;;*<<< '	- - - --A-A- -    s   B6C# #
C10C1)r&  r'  r(  __doc__r]  __classcell__rj  s   @r;   rZ  rZ    sB        	 		 	 	 	 	 	 	 	 	r=   rZ  r  c                   t           r| ||||d}|||d<   n|||d<   n||
||d<   ||d<   |	rd|d<   t                      5 }	  |j        di |}|	r|
|
j                            dd           |
                    d	          5 }|                    t                    D ]}|r|                    |           	 ddd           n# 1 swxY w Y   d
}n|j	        }t          |j        d |j                                        D             ||j                  cddd           S # t          j        j        $ r^}t%          |t          j        j                  rt)          t+          |                    |t-          t+          |                    |d}~ww xY w# 1 swxY w Y   |>t/          j        |                              d          }|                    dd           n|}t6          j                            ||||           } G d dt6          j        j                  }t=          |          j        pd                                 }t6          j        !                     |||                    }	 |                    ||          }nw# t6          j"        j#        $ r`}t          |j$        |j        rtK          |j                  ni |&                                pd
tO          |d|                    cY d}~S d}~ww xY w|(                                }|j)        }tK          |j                  }|	r|
|
j                            dd           |
                    d	          5 }	 |&                    t                    }|sn|                    |           3	 ddd           n# 1 swxY w Y   t          ||d
|          S t          |||&                                |          S )zOne HTTP attempt. No retry.)rA  r  r  r<  allow_redirectsNr$  r9  r:  Tr?  )parentsexist_okwbr=   c                    i | ]\  }}||	S r*  r*  r   kvs      r;   
<dictcomp>z_http_once.<locals>.<dictcomp>0  s    @@@daQ@@@r=   )r  r  r  r  r  zContent-Typezapplication/json)r9  r  rA  c                  &     e Zd ZddZ fdZ xZS )$_http_once.<locals>._RedirectHandleroriginal_hostr6   followr   c                "    || _         || _        d S r   )rz  r{  )r"  rz  r{  s      r;   __init__z-_http_once.<locals>._RedirectHandler.__init__G  s    !.D DKKKr=   c                L   | j         sd S t          |          j        pd                                }|| j        k    rBd |                                D             }t          j                            ||d          }	|	S t                      
                    ||||||          S )Nr   c                F    i | ]\  }}|                                 d v||S )>   rX  rW  rV  )r   rt  s      r;   rw  zI_http_once.<locals>._RedirectHandler.redirect_request.<locals>.<dictcomp>Q  s<     ! ! !!Qwwyy(PPP qPPPr=   GET)r  rA  )r{  r   r   r   rz  header_itemsurllibr^  Requestr\  redirect_request)r"  req2fpcodemsghdrsnewurlrh  clean_headersnew_reqrj  s             r;   r  z5_http_once.<locals>._RedirectHandler.redirect_requestK  s    ; t ((17R>>@@H4---! !%)%6%6%8%8! ! ! !.00W\0]]77++D"dCvNNNr=   )rz  r6   r{  r   )r&  r'  r(  r}  r  rl  rm  s   @r;   _RedirectHandlerry  F  sU        	! 	! 	! 	!	O 	O 	O 	O 	O 	O 	O 	O 	Or=   r  r   )r<  r  r*  )*rJ  rZ  r^  parentmkdiropeniter_contentDOWNLOAD_CHUNK_SIZEwritecontentr  status_coder  itemsr  requests
exceptionsRequestException
isinstanceTimeoutrO  r6   rP  r$  dumpsencoderI  r  r  HTTPRedirectHandlerr   r   r   build_openererror	HTTPErrorr  rH  readgetattrgeturlr  )rA  r  r  r8  r9  r:  r;  r<  r=  r?  r@  kwargsr   rfchunkr  rT  
body_bytesreqr  rz  openerrS  	final_urlfinal_statusfinal_headerss                              r;   rM  rM    s!     '5SW3C"
 "
  &F6NN!F6NN$"2#F7O!F6N 	$#F8 .// 	515AI'''' %d.K%%dT%BBB4 /A%&^^4G%H%H / /E$ / !// / / / / / / / / / / / / / / DD9D#=@@aioo.?.?@@@	  	5 	5 	5 	5 	5 	5 	5 	5$ &7 5 5 5 a!4!<== 6&s1vv..A5%c!ff--145%	5 	5 	5 	5 	5 	5 	5 	54 Z	**11'::
>+=>>>>

.
 
 :wv
 
V
VC
O O O O O6>= O O O& c]]+1r88::M^(()9)9-IY)Z)Z[[F
{{3{00<! 
 
 
6'(y8DOOObS5#&&	
 
 
 	
 	
 	
 	
 	
 	

 I;L&&M a$"$666YYt__ 			"566 	 		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 <SV_````|]Zcdddds   FAD)5C
>D)
C	D)C	A
D))F=AFFFF"%F"=J L	)AL>L	L	05N22N69N6r  c                    t          d| fi |S )Nr  rU  r  r  s     r;   http_getr  w  s    s--f---r=   c                    t          d| fi |S )NPOSTr  r  s     r;   	http_postr  {  s    ..v...r=   workflowc                    t          | t                    sdS d| v rd| v rdS |                                 D ]}t          |t                    rd|v r dS dS )z>API format = top-level dict where each value has `class_type`.Fnodeslinks
class_typeT)r  rH  values)r  rv  s     r;   is_api_formatr    su    h%% u(w(22u__  a 	<1#4#4445r=   payloadrH  c                0   t          | t                    rt          |           r| S t          | t                    r!d| v rt          | d                   r| d         S t          | t                    rd| v rd| v rt          d          t          d          )zQUnwrap common wrapper variants. Returns API-format workflow or raises ValueError.r|   r  r  u   Workflow is in editor format (has top-level 'nodes' and 'links' arrays). Re-export from ComfyUI using 'Workflow → Export (API)' (newer UI) or 'Save (API Format)' (older UI).zSWorkflow is not in API format. Each top-level entry must have a 'class_type' field.)r  rH  r  
ValueError)r  s    r;   unwrap_workflowr    s    '4   ]7%;%; '4   !X%8%8]7S[K\=]=]%8x  '4   
W%7%7Gw<N<N1
 
 	

 ]  r=   r   c                    t          | t                    oHt          |           dk    o5t          | d         t                    ot          | d         t                    S )zHTrue if `value` is a [node_id, output_index] connection (length-2 list).r0  r   rG  )r  r_  r  r6   r  r   s    r;   is_linkr    sV     	5$ 	&JJ!O	&uQx%%	& uQx%%	r=   Iterator[tuple[str, dict]]c              #  |   K   |                                  D ]$\  }}t          |t                    r
d|v r||fV  %dS )z5Yield (node_id, node) for each valid API-format node.r  N)r  r  rH  )r  node_idnodes      r;   
iter_nodesr    sZ      !))    dD!! 	 ld&:&:4-   r=   Iterator[dict]c              #  8  K   t          |           D ]\  }}|d         }|t          vr|                    di           pi }t          |         D ]J\  }}|                    |          }|r.t          |t                    rt          |          s
|||||dV  KdS )zLYield {node_id, class_type, field, value, folder} for each model dependency.r  inputs)r  r  fieldr   r5   N)r  ry   r:   r  r6   r  )r  r  r  clsr  
field_namer5   r  s           r;   iter_model_depsr    s      #H--  < m##(B''-2"/"4 		 		J**Z((C z#s++ GCLL &"%' $    		 r=   Iterator[tuple[str, str]]c              #  D  K   t          |           D ]\  }}|                    di           pi }|                                D ][\  }}|t          vrt	          |t
                    s%t                              |          D ]}||                    d          fV  \dS )zGYield (node_id, embedding_name) for every embedding mention in prompts.r  rG  N)	r  r:   r  PROMPT_FIELDSr  r6   EMBEDDING_REGEXfinditergroup)r  r  r  r  r  r  ms          r;   iter_embedding_refsr    s      #H-- * *(B''-2%||~~ 	* 	*OJ..c3'' $--c22 * *qwwqzz)))))*	** *r=   r   partsc                    |                                  } | j        |                                  }	 |                    |           n(# t          $ r}t          d| d|           |d}~ww xY w|S )zJoin paths, raising if the result escapes `base`.

    Server-supplied filenames may contain `../` etc. This guards against
    path-traversal attacks when downloading outputs.
    zRefusing path traversal: z is outside N)resolvejoinpathrelative_tor  )r   r  base_resolved	candidaterT  s        r;   safe_path_joinr    s     LLNNMu%--//Im,,,,   N	NN}NN
 
	 s   A 
A-A((A-filenamec                    t          |           j                                        }|dv rdS |dv rdS |dv rdS |dv rdS d	S )
N>   .avi.gif.mkv.mov.mp4.webm.webpr   >   .m4a.mp3.ogg.wav.flacr   >   .glb.obj.ply.gltf3d>   .md.txt.jsonr{   r   )r   suffixr   )r  exts     r;   media_type_from_filenamer    se    
x..

%
%
'
'C
HHHw
777w
///t
&&&v7r=   c                    t          |           D ]G\  }}|d         t          v r dS |d                                                             d          r dS HdS )z1Used to bump default timeout for video workflows.r  T)animatediffade_wanvideohunyuanvideoltxvideocogvideoF)r  SLOW_OUTPUT_NODESr   r   )r  _r  s      r;   looks_like_video_workflowr    sn    h''  4!22244##%%001|}} 	44	5r=   l    c                D   | t          j        t          t                    S t	          | t
                    r7|                                 dk    rt          j        t          t                    S | dk    rt          j        t          t                    S t          |           S )zConvert -1 or None to a fresh random seed; otherwise return int(value).

    Accepts numeric -1 OR string "-1" (both treated as "randomize"). Other
    parse failures raise TypeError/ValueError for the caller to surface.
    Nz-1)r2  randintSEED_MINSEED_MAXr  r6   r  r  r  s    r;   coerce_seedr    sx     }~h111% 2%++--4"7"7~h111{{~h111u::r=   c                   t          | t                    st                      S t                      }| D ]}t          |t                    r|                    |           -t          |t
                    ri|                    d          p)|                    d          p|                    d          }t          |t                    r|                    |           |S )zNormalize model-list responses from local ComfyUI vs Comfy Cloud.

    Local: `["a.safetensors", "b.safetensors"]`
    Cloud: `[{"name": "a.safetensors", "pathIndex": 0}, ...]`
    namer  r   )r  r_  setr6   addrH  r:   )r  outitemr
  s       r;   parse_model_listr    s     gt$$ uuEEC  dC   	GGDMMMMd## 	88F##Otxx
';';Otxx?O?OD$$$ Jr=   c                 B    t          t          j                              S r   )r6   uuiduuid4r*  r=   r;   new_client_idr  3  s    tz||r=   dc                d    d                     d |                                 D                       S )zPretty key=value for log lines. c              3  *   K   | ]\  }}| d |V  dS )=Nr*  rt  s      r;   r   zfmt_kv.<locals>.<genexpr>9  s0      77TQqLL1LL777777r=   )joinr  )r  s    r;   fmt_kvr  7  s+    8877QWWYY777777r=   r0  )indentobjr  c               X    t          t          j        | |t                               dS )zKPrint JSON to stdout. Centralised so behavior can be tweaked (e.g., --raw).)r  defaultN)printr$  r  r6   )r  r  s     r;   	emit_jsonr   <  s'    	$*S
5
5
566666r=   r  c                B    t          d|  t          j                   dS )z?stderr log with consistent prefix (so JSON stdout stays clean).z[comfyui-skill] )fileN)r  sysstderr)r  s    r;   logr%  A  s&    	
"S
"
"444444r=   )r5   r6   r7   r8   )r   r6   r7   r   )r   r6   r   r6   r   r   r7   r6   )r   r6   r7   r6   )r   r6   r   r6   r  r   r7   r6   )r	  r
  r7   r
  )r+  r  r   r,  r-  r,  r7   r.  )rA  r6   r  r6   r  rB  r8  r   r9  rC  r:  rD  r;  rD  r<  r,  r=  r   r>  r  r?  r   r@  rE  r7   r  )rA  r6   r  r6   r  r  r8  r   r9  rC  r:  rD  r;  rD  r<  r,  r=  r   r?  r   r@  rE  r7   r  )r  r6   r  r   r7   r  )r  r   r7   r   )r  r   r7   rH  )r   r   r7   r   )r  rH  r7   r  )r  rH  r7   r  )r  rH  r7   r  )r   r   r  r6   r7   r   )r  r6   r7   r6   )r  rH  r7   r   )r   r   r7   r  )r  r   r7   r,   )r7   r6   )r  rH  r7   r6   )r  r   r  r  r7   r.  )r  r6   r7   r.  )Nrk  
__future__r   r$  r  r2  rer#  r4  r  dataclassesr   pathlibr   typingr   r   urllib.parser   r  rJ  ImportErrorurllib.errorr  urllib.requestDEFAULT_LOCAL_HOSTDEFAULT_CLOUD_HOSTr  DEFAULT_HTTP_TIMEOUTDEFAULT_RETRIESRETRY_BASE_DELAYRETRY_MAX_DELAYrN  r  r   r-   r)  r4   r<   ry   r   r  compile
IGNORECASEr  r   r   r   r  r  r  r  r  r7  rU  ra  SessionrZ  rM  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r%  r*  r=   r;   <module>r8     s     # " " " " "  				  				 



   ! ! ! ! ! !                       ! ! ! ! ! !OOOLL   L - . #   ===            2 '(+V4_%%v./( (    0 0 0 0.3!= >.3 != >.3  ">!?	.3
  ">!?.3 != >.3 !7 8.3 !7 8.3 !7 8.3 !4 5.3 !C D.3 !C D.3  !C D!.3$ !6 7%.3& !79O P'.3(  h h h).3* != >+.3. !6 7/.30 "D D!6 7!A B!E F!: ;!G H
 "A A!@ A!E F)M(N"F!G!H I "[.3 .3 .3 . . . .hU. U. U. U U U Up XWW "*jM  ( '( D D D D OS      $   " BF @ @ @ @ @ @    
G 
G 
G 
G 
G 
G 
G 
G 0@o      &*)!"@K @K @K @K @K @KF >       8+;   6ie ie ie ieX. . . ./ / / /	 	 	 	   &             &
* 
* 
* 
*"   "
 
 
 
       (   .   8 8 8 8
 *+ 7 7 7 7 7 7
5 5 5 5 5 5s   A AA