ó
     ¥Jj=.  ã                  óÈ   • S r SSKJr  SSKrSSKrSSKrSSKJr  SSK	J
r
  SSKJrJr  \" S5      rSr " S	 S
\5      r " S S5      r " S S\5      r " S S\5      rSS jrg)a  Central API client.

The rest of the agent talks to the control plane only through ApiClient, so the
real HTTP implementation can be swapped for the development mock without
touching call sites. Choose the implementation with get_api_client(config):
WOLFPANEL_API_MOCK=1 selects the mock.

Endpoints (frozen v1 contract):
  POST /api/v1/agent/register             (no auth; install token in body)
  POST /api/v1/agent/heartbeat            (X-Agent-Token header)
  POST /api/v1/agent/discovery/sessions   (X-Agent-Token header)
  POST /api/v1/agent/discovery/items      (X-Agent-Token header)
  POST /api/v1/agent/discovery/complete   (X-Agent-Token header)
  POST /api/v1/agent/discovery/failed     (X-Agent-Token header)
  GET  <manifest_url>                     (update check)
é    )ÚannotationsN)ÚAny)ÚConfig)Ú
get_loggerÚredactÚapié   c                  ó4   ^ • \ rS rSrSrSSU 4S jjjrSrU =r$ )ÚApiErroré!   zBRaised for non-recoverable API errors (4xx / malformed responses).c                ó0   >• [         TU ]  U5        X l        g ©N)ÚsuperÚ__init__Ústatus_code)ÚselfÚmessager   Ú	__class__s      €Ú4D:\Programs\laragon\www\wolf\agent\src\api_client.pyr   ÚApiError.__init__$   s   ø€ Ü‰Ñ˜Ô!Ø&Õó    ©r   r   )r   Ústrr   z
int | NoneÚreturnÚNone)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   Ú__static_attributes__Ú__classcell__)r   s   @r   r   r   !   s   ø† ÙL÷'÷ 'r   r   c                  ó†   • \ rS rSrSrSS jrSS jrSS jrSS jrSS jr	SS jr
SS	 jrSS
 jrSS jrSS jrSS jrSrg)Ú	ApiClienté)   z0Interface every client implementation satisfies.c                ó   • [         er   ©ÚNotImplementedError©r   Úpayloads     r   ÚregisterÚApiClient.register,   ó   € Ü!Ð!r   c                ó   • [         er   r'   ©r   r*   Útokens      r   Ú	heartbeatÚApiClient.heartbeat/   r-   r   c                ó   • [         er   r'   r/   s      r   Úopen_discovery_sessionÚ ApiClient.open_discovery_session2   r-   r   c                ó   • [         er   r'   r/   s      r   Úupload_discovery_itemsÚ ApiClient.upload_discovery_items5   r-   r   c                ó   • [         er   r'   r/   s      r   Úcomplete_discoveryÚApiClient.complete_discovery8   r-   r   c                ó   • [         er   r'   r/   s      r   Úfail_discoveryÚApiClient.fail_discovery;   r-   r   c                ó   • [         er   r'   ©r   Úurls     r   Úfetch_manifestÚApiClient.fetch_manifest>   r-   r   c                ó   • [         er   r'   ©r   r0   s     r   Úget_pending_commandsÚApiClient.get_pending_commandsA   r-   r   c                ó   • [         er   r'   ©r   Ú
command_idr*   r0   s       r   Úpost_command_resultÚApiClient.post_command_resultD   r-   r   c                ó   • [         er   r'   ©r   Úrun_idÚstatusÚlogsr0   s        r   Úupdate_pipeline_runÚApiClient.update_pipeline_runG   r-   r   c                ó   • [         er   r'   ©r   Úsite_idr*   r0   s       r   Úupdate_site_statusÚApiClient.update_site_statusJ   r-   r   © N©r*   údict[str, Any]r   r[   ©r*   r[   r0   ú
str | Noner   r   ©r*   r[   r0   r]   r   r[   ©rA   r   r   r[   ©r0   r]   r   úlist[dict[str, Any]]©rJ   Úintr*   r[   r0   r]   r   r[   ©
rO   rc   rP   r   rQ   ra   r0   r]   r   r[   ©rV   rc   r*   r[   r0   r]   r   r[   ©r   r   r   r   r    r+   r1   r4   r7   r:   r=   rB   rF   rK   rR   rW   r!   rY   r   r   r$   r$   )   s<   † Ù:ô"ô"ô"ô"ô"ô"ô"ô"ô"ô"÷"r   r$   c                  ó¤   • \ rS rSrSrSS jrSS jrSS jrSS jrSS jr	SS jr
SS	 jrSS
 jrSS jrSS jrSS jrSS jrSS jrSS jrSrg)ÚRealApiClientéN   z?Talks to the live Central API over HTTPS using only the stdlib.c                ó0   • UR                  S5      U l        g )NÚ/)ÚrstripÚbase_url)r   rm   s     r   r   ÚRealApiClient.__init__Q   s   € Ø Ÿ™¨Ó,ˆ�r   c                óÆ  • U R                    U 3n[        R                  " U5      R                  S5      nSSS.nU(       a  X6S'   [        R
                  R                  XEUSS9n [        R
                  R                  U[        S9 nUR                  5       R                  S5      n	U	(       a  [        R                  " U	5      O0 sS S S 5        $ ! , (       d  f       g = f! [        R                  R                   a)  n
[        U S	U
R                   3U
R                  S
9U
eS n
A
f[        R                  R                   ["        4 a  n
[        U SU
 35      U
eS n
A
ff = f)Núutf-8zapplication/jsonúwolfpanel-agent)zContent-Typeú
User-AgentúX-Agent-TokenÚPOST)ÚdataÚheadersÚmethod©Útimeoutú	 -> HTTP r   ú -> transport error: )rm   ÚjsonÚdumpsÚencodeÚurllibÚrequestÚRequestÚurlopenÚ_TIMEOUTÚreadÚdecodeÚloadsÚerrorÚ	HTTPErrorr   ÚcodeÚURLErrorÚTimeoutError)r   Úpathr*   r0   rA   ru   rv   r€   ÚresponseÚbodyÚexcs              r   Ú_postÚRealApiClient._postT   s'  € Ø—‘� ˜vÐ&ˆÜ�zŠz˜'Ó"×)Ñ)¨'Ó2ˆØ#5ÐEVÑWˆÞð
 (-�OÑ$ä—.‘.×(Ñ(¨ÀÐQWÐ(ÐXˆð		IÜ—‘×'Ñ'¨¼Ð'ÑBÀhØ—}‘}“×-Ñ-¨gÓ6�Þ+/”t—z’z $Ô'°R÷ C×B×Bûô �|‰|×%Ñ%ó 	XÜ˜d˜V 9¨S¯X©X¨JÐ7ÀSÇXÁXÑNÐTWÐWûÜ—‘×%Ñ%¤|Ð4ó 	Iô ˜d˜VÐ#8¸¸Ð>Ó?ÀSÐHûð	IúsB   Á&"C! Â>CÃ	C! Ã
CÃC! ÃC! Ã!E Ã?$D#Ä#'E Å
EÅE c                óx  • U R                    U 3nSS0nU(       a  X$S'   [        R                  R                  X4SS9n [        R                  R	                  U[
        S9 nUR                  5       R                  S5      nU(       a  [        R                  " U5      O0 sS S S 5        $ ! , (       d  f       g = f! [        R                  R                   a)  n[        U SUR                   3UR                  S	9UeS nAf[        R                  R                  [        4 a  n[        U S
U 35      UeS nAff = f)Nrr   rq   rs   ÚGET)rv   rw   rx   rp   rz   r   r{   )rm   r   r€   r�   r‚   rƒ   r„   r…   r|   r†   r‡   rˆ   r   r‰   rŠ   r‹   )	r   rŒ   r0   rA   rv   r€   r�   rŽ   r�   s	            r   Ú_getÚRealApiClient._getk   s  € Ø—‘� ˜vÐ&ˆØÐ!2Ð3ˆÞØ',�OÑ$ä—.‘.×(Ñ(¨ÀeÐ(ÐLˆð	IÜ—‘×'Ñ'¨¼Ð'ÑBÀhØ—}‘}“×-Ñ-¨gÓ6�Þ+/”t—z’z $Ô'°R÷ C×B×Bûô �|‰|×%Ñ%ó 	XÜ˜d˜V 9¨S¯X©X¨JÐ7ÀSÇXÁXÑNÐTWÐWûÜ—‘×%Ñ%¤|Ð4ó 	IÜ˜d˜VÐ#8¸¸Ð>Ó?ÀSÐHûð	IúsA   ¿"B: Á!>B)Â	B: Â)
B7Â3B: Â7B: Â:D9Ã$C<Ã<'D9Ä#D4Ä4D9c                ó$   • U R                  SUS S9$ )Nz/api/v1/agent/register)r0   ©r�   r)   s     r   r+   ÚRealApiClient.register{   s   € Ø�z‰zÐ2°GÀ4ˆzÐHÐHr   c                ó(   • U R                  SX5        g )Nz/api/v1/agent/heartbeatr—   r/   s      r   r1   ÚRealApiClient.heartbeat~   s   € Ø�
‰
Ð,¨gÕ=r   c                ó&   • U R                  SX5      $ )Nz /api/v1/agent/discovery/sessionsr—   r/   s      r   r4   Ú$RealApiClient.open_discovery_session�   ó   € Ø�z‰zÐ<¸gÓMÐMr   c                ó&   • U R                  SX5      $ )Nz/api/v1/agent/discovery/itemsr—   r/   s      r   r7   Ú$RealApiClient.upload_discovery_items„   s   € Ø�z‰zÐ9¸7ÓJÐJr   c                ó&   • U R                  SX5      $ )Nz /api/v1/agent/discovery/completer—   r/   s      r   r:   Ú RealApiClient.complete_discovery‡   r�   r   c                ó&   • U R                  SX5      $ )Nz/api/v1/agent/discovery/failedr—   r/   s      r   r=   ÚRealApiClient.fail_discoveryŠ   s   € Ø�z‰zÐ:¸GÓKÐKr   c                ó¤  • [         R                  R                  USS0S9n [         R                  R                  U[        S9 n[
        R                  " UR                  5       R                  S5      5      sS S S 5        $ ! , (       d  f       g = f! [         R                  R                  [        [        4 a  n[        SU 35      UeS nAff = f)Nrr   rq   )rv   rx   rp   zmanifest fetch failed: )r   r€   r�   r‚   rƒ   r|   r†   r„   r…   r‡   rŠ   r‹   Ú
ValueErrorr   )r   rA   r€   r�   r�   s        r   rB   ÚRealApiClient.fetch_manifest�   s¡   € Ü—.‘.×(Ñ(¨°|ÐEVÐ6WÐ(ÐXˆð	EÜ—‘×'Ñ'¨¼Ð'ÑBÀhÜ—z’z (§-¡-£/×"8Ñ"8¸Ó"AÓB÷ C×B×Bûä—‘×%Ñ%¤|´ZÐ@ó 	EÜÐ4°S°EÐ:Ó;ÀÐDûð	Eús5   ¢"B Á3BÁ7	B Â
BÂB ÂB Â)CÂ;C
Ã
Cc                ó&   • U R                  SU5      $ )Nz/api/v1/agent/commands/pending)r”   rE   s     r   rF   Ú"RealApiClient.get_pending_commands•   s   € Ø�y‰yÐ9¸5ÓAÐAr   c                ó.   • U R                  SU S3X#5      $ )Nz/api/v1/agent/commands/z/resultr—   rI   s       r   rK   Ú!RealApiClient.post_command_result˜   s   € Ø�z‰zÐ3°J°<¸wÐGÈÓXÐXr   c                ó6   • X#S.nU R                  SU S3XT5      $ )N)rP   rQ   z/api/v1/agent/pipeline/z/updater—   )r   rO   rP   rQ   r0   r*   s         r   rR   Ú!RealApiClient.update_pipeline_run›   s%   € Ø#Ñ2ˆØ�z‰zÐ3°F°8¸7ÐCÀWÓTÐTr   c                ó.   • U R                  SU S3X#5      $ )Nz/api/v1/agent/sites/z/statusr—   rU   s       r   rW   Ú RealApiClient.update_site_statusŸ   s   € Ø�z‰zÐ0°°	¸ÐAÀ7ÓRÐRr   )rm   N)rm   r   r   r   )rŒ   r   r*   r[   r0   r]   r   r[   )rŒ   r   r0   r]   r   r   rZ   r\   r^   r_   r`   rb   rd   re   )r   r   r   r   r    r   r�   r”   r+   r1   r4   r7   r:   r=   rB   rF   rK   rR   rW   r!   rY   r   r   rh   rh   N   sW   † ÙIô-ôIô.Iô Iô>ôNôKôNôLôEôBôYôU÷Sr   rh   c                  ó†   • \ rS rSrSrSS jrSS jrSS jrSS jrSS jr	SS jr
SS	 jrSS
 jrSS jrSS jrSS jrSrg)ÚMockApiClienté£   z�Development stand-in. Logs payloads and returns canned responses.

Lets the whole agent be exercised end-to-end with no Central API present.
c                ó˜   • [         R                  SUR                  S5      UR                  S5      UR                  S5      5        SSSS.$ )	Nz0[mock] register hostname=%s os=%s fingerprint=%sÚhostnameÚos_nameÚfingerprintÚwp_agent_dev_tokené   Úactive)Úagent_tokenÚ	server_idrP   ©ÚlogÚinfoÚgetr)   s     r   r+   ÚMockApiClient.register©   sJ   € Ü�‰Ø>Ø�K‰K˜
Ó#Ø�K‰K˜	Ó"Ø�K‰K˜Ó&ô		
ð 0ØØñ
ð 	
r   c                ó‚   • [         R                  SUR                  S5      UR                  S5      [        U5      5        g )Nz0[mock] heartbeat server_id=%s status=%s token=%srº   rP   )r¼   r½   r¾   r   r/   s      r   r1   ÚMockApiClient.heartbeat·   s0   € Ü�‰Ø>Ø�K‰K˜Ó$Ø�K‰K˜Ó!Ü�5‹Mõ		
r   c                óv   • [         R                  SUR                  S5      UR                  S5      5        SSS.$ )Nz8[mock] discovery session open scan_type=%s categories=%sÚ	scan_typeÚexpected_categoriesr·   Úrunning©Ú
session_idrP   r»   r/   s      r   r4   Ú$MockApiClient.open_discovery_session¿   s7   € Ü�‰ØFØ�K‰K˜Ó$Ø�K‰KÐ-Ó.ô	
ð
  ¨9Ñ5Ð5r   c                óò   • UR                  S/ 5      n[        R                  SUR                  S5      UR                  S5      [        U5      5        UR                  S5      [        U5      [        U5      S.$ )NÚitemsz9[mock] discovery items session_id=%s category=%s count=%drÇ   Úcategory)rÇ   ÚacceptedÚ
item_count)r¾   r¼   r½   Úlen)r   r*   r0   rÊ   s       r   r7   Ú$MockApiClient.upload_discovery_itemsÇ   sf   € Ø—‘˜G RÓ(ˆÜ�‰ØGØ�K‰K˜Ó%Ø�K‰K˜
Ó#Ü�‹Jô		
ð "Ÿ+™+ lÓ3Ü˜E›
Ü˜e›*ñ
ð 	
r   c                ó  • [         R                  SUR                  S5      UR                  S5      UR                  S5      5        UR                  S5      =(       d    / nUR                  S5      U(       a  SSS.$ SSS.$ )	Nz>[mock] discovery complete session_id=%s succeeded=%s failed=%srÇ   Úsucceeded_categoriesÚfailed_categoriesÚpartialÚ	completedr   )rÇ   rP   rÍ   r»   )r   r*   r0   Úfaileds       r   r:   Ú MockApiClient.complete_discoveryÕ   s{   € Ü�‰ØLØ�K‰K˜Ó%Ø�K‰KÐ.Ó/Ø�K‰KÐ+Ó,ô		
ð —‘Ð0Ó1×7°Rˆà!Ÿ+™+ lÓ3Þ#)�iØñ
ð 	
à/:Øñ
ð 	
r   c                ót   • [         R                  SUR                  S5      5        UR                  S5      SS.$ )Nz%[mock] discovery failed session_id=%srÇ   rÕ   rÆ   r»   r/   s      r   r=   ÚMockApiClient.fail_discoveryã   s3   € Ü�‰Ø3Ø�K‰K˜Ó%ô	
ð &Ÿk™k¨,Ó7À8ÑLÐLr   c           	     óD   • [         R                  SU5        SSSSSSSS	S
.$ )Nz[mock] fetch manifest %sr·   Ústablez0.1.2zwolfpanel-agent-0.1.2.tar.gzÚ@0000000000000000000000000000000000000000000000000000000000000000Ú r   z2026-07-04T00:00:00Z)Úmanifest_versionÚchannelÚversionÚfileÚsha256Ú	signatureÚ
size_bytesÚpublished_at©r¼   r½   r@   s     r   rB   ÚMockApiClient.fetch_manifestê   s3   € Ü�‰Ð+¨SÔ1à !ØØØ2ØØØØ2ñ	
ð 		
r   c                ó0   • [         R                  S5        / $ )Nz[mock] get pending commandsrå   rE   s     r   rF   Ú"MockApiClient.get_pending_commands÷   s   € Ü�‰Ð.Ô/Øˆ	r   c                ór   • [         R                  SXR                  S5      5        SUR                  S5      0$ )Nz*[mock] post command result id=%s status=%srP   r»   rI   s       r   rK   Ú!MockApiClient.post_command_resultû   s/   € Ü�‰Ð=¸zÏ;É;ÐW_ÓK`ÔaØ˜'Ÿ+™+ hÓ/Ð0Ð0r   c                ó6   • [         R                  SX5        SU0$ )Nz*[mock] update pipeline run id=%s status=%srP   rå   rN   s        r   rR   Ú!MockApiClient.update_pipeline_runÿ   s   € Ü�‰Ð=¸vÔNØ˜&Ð!Ð!r   c                ó6   • [         R                  SX5        SS0$ )Nz/[mock] update site status site_id=%s payload=%srP   Úsuccessrå   rU   s       r   rW   Ú MockApiClient.update_site_status  s   € Ü�‰ÐBÀGÔUØ˜)Ð$Ð$r   rY   NrZ   r\   r^   r_   r`   rb   rd   re   rf   rY   r   r   r°   r°   £   s?   † ñô

ô
ô6ô
ô
ôMô
ôô1ô"÷%r   r°   c                óŒ   • U R                   (       a  [        R                  S5        [        5       $ [	        U R
                  5      $ )z4Factory: mock in development, real client otherwise.z0using MockApiClient (WOLFPANEL_API_MOCK enabled))Úapi_mockr¼   r½   r°   rh   Úapi_url)Úconfigs    r   Úget_api_clientrô     s-   € à‡‡Ü�‰ÐCÔDÜ‹ÐÜ˜Ÿ™Ó(Ð(r   )ró   r   r   r$   )r    Ú
__future__r   r|   Úurllib.errorr   Úurllib.requestÚtypingr   ró   r   Úloggerr   r   r¼   rƒ   Ú	Exceptionr   r$   rh   r°   rô   rY   r   r   Ú<module>rû      sl   ðñõ" #ã Û Û Ý å ß %á�Ó€à€ô'ˆyô '÷""ñ ""ôJRS�Iô RSôjb%�Iô b%õJ)r   