Ë
    À7jâ   ã                  óÆ   — d Z ddlm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  ed«      ZdZ G d	„ d
e«      Z G d„ d«      Z G d„ de«      Z G d„ de«      Zdd„Zy)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                  ó&   ‡ — e Zd ZdZddˆ fd„Zˆ xZS )ÚApiErrorzBRaised for non-recoverable API errors (4xx / malformed responses).c                ó2   •— t         ‰| �  |«       || _        y ©N)ÚsuperÚ__init__Ústatus_code)ÚselfÚmessager   Ú	__class__s      €úD/var/www/downloads.wolfpanel.net/public_html/agent/src/api_client.pyr   zApiError.__init__$   s   ø€ Ü‰Ñ˜Ô!Ø&ˆÕó    r   )r   Ústrr   z
int | NoneÚreturnÚNone)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ú__classcell__)r   s   @r   r   r   !   s   ø„ ÙL÷'ò 'r   r   c                  óH   — e Zd ZdZd
d„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
y	)Ú	ApiClientz0Interface every client implementation satisfies.c                ó   — t         ‚r   ©ÚNotImplementedError©r   Úpayloads     r   ÚregisterzApiClient.register,   ó   € Ü!Ð!r   c                ó   — t         ‚r   r!   ©r   r$   Útokens      r   Ú	heartbeatzApiClient.heartbeat/   r&   r   c                ó   — t         ‚r   r!   r(   s      r   Úopen_discovery_sessionz ApiClient.open_discovery_session2   r&   r   c                ó   — t         ‚r   r!   r(   s      r   Úupload_discovery_itemsz ApiClient.upload_discovery_items5   r&   r   c                ó   — t         ‚r   r!   r(   s      r   Úcomplete_discoveryzApiClient.complete_discovery8   r&   r   c                ó   — t         ‚r   r!   r(   s      r   Úfail_discoveryzApiClient.fail_discovery;   r&   r   c                ó   — t         ‚r   r!   ©r   Úurls     r   Úfetch_manifestzApiClient.fetch_manifest>   r&   r   N©r$   údict[str, Any]r   r8   ©r$   r8   r)   ú
str | Noner   r   ©r$   r8   r)   r:   r   r8   ©r5   r   r   r8   ©r   r   r   r   r%   r*   r,   r.   r0   r2   r6   © r   r   r   r   )   s(   „ Ù:ó"ó"ó"ó"ó"ó"ô"r   r   c                  óX   — e Zd ZdZdd„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
dd	„Zdd
„Zy)ÚRealApiClientz?Talks to the live Central API over HTTPS using only the stdlib.c                ó0   — |j                  d«      | _        y )Nú/)ÚrstripÚbase_url)r   rD   s     r   r   zRealApiClient.__init__E   s   € Ø Ÿ™¨Ó,ˆ�r   c                ó¶  — | j                   › |› �}t        j                  |«      j                  d«      }dddœ}|r||d<   t        j
                  j                  |||d¬«      }	 t        j
                  j                  |t        ¬«      5 }|j                  «       j                  d«      }	|	rt        j                  |	«      ni cd d d «       S # 1 sw Y   y xY w# t        j                  j                  $ r,}
t        |› d	|
j                  › �|
j                  ¬
«      |
‚d }
~
wt        j                  j                   t"        f$ r}
t        |› d|
› �«      |
‚d }
~
ww xY w)Núutf-8zapplication/jsonúwolfpanel-agent)zContent-Typeú
User-AgentzX-Agent-TokenÚPOST)ÚdataÚheadersÚmethod©Útimeoutz	 -> HTTP )r   z -> transport error: )rD   ÚjsonÚdumpsÚencodeÚurllibÚrequestÚRequestÚurlopenÚ_TIMEOUTÚreadÚdecodeÚloadsÚerrorÚ	HTTPErrorr   ÚcodeÚURLErrorÚTimeoutError)r   Úpathr$   r)   r5   rJ   rK   rS   ÚresponseÚbodyÚexcs              r   Ú_postzRealApiClient._postH   s3  € Ø—‘� ˜vÐ&ˆÜ�z‰z˜'Ó"×)Ñ)¨'Ó2ˆØ#5ÐEVÑWˆÙð
 (-ˆG�OÑ$ä—.‘.×(Ñ(¨°4ÀÐQWÐ(ÓXˆð		IÜ—‘×'Ñ'¨¼Ð'ÓBð 8ÀhØ—}‘}“×-Ñ-¨gÓ6�Ù+/”t—z‘z $Ô'°R÷8÷ 8ò 8ûô �|‰|×%Ñ%ò 	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 Â
8CÃ	C ÃCÃC ÃC ÃEÃ5'DÄ&EÅEÅEc                ó*   — | j                  d|d ¬«      S )Nz/api/v1/agent/register)r)   ©rc   r#   s     r   r%   zRealApiClient.register_   s   € Ø�z‰zÐ2°GÀ4ˆzÓHÐHr   c                ó*   — | j                  d||«       y )Nz/api/v1/agent/heartbeatre   r(   s      r   r*   zRealApiClient.heartbeatb   s   € Ø�
‰
Ð,¨g°uÕ=r   c                ó(   — | j                  d||«      S )Nz /api/v1/agent/discovery/sessionsre   r(   s      r   r,   z$RealApiClient.open_discovery_sessione   ó   € Ø�z‰zÐ<¸gÀuÓMÐMr   c                ó(   — | j                  d||«      S )Nz/api/v1/agent/discovery/itemsre   r(   s      r   r.   z$RealApiClient.upload_discovery_itemsh   s   € Ø�z‰zÐ9¸7ÀEÓJÐJr   c                ó(   — | j                  d||«      S )Nz /api/v1/agent/discovery/completere   r(   s      r   r0   z RealApiClient.complete_discoveryk   rh   r   c                ó(   — | j                  d||«      S )Nz/api/v1/agent/discovery/failedre   r(   s      r   r2   zRealApiClient.fail_discoveryn   s   € Ø�z‰zÐ:¸GÀUÓKÐKr   c                ó¢  — t         j                  j                  |ddi¬«      }	 t         j                  j                  |t        ¬«      5 }t        j                  |j                  «       j                  d«      «      cd d d «       S # 1 sw Y   y xY w# t         j                  j                  t        t        f$ r}t        d|› �«      |‚d }~ww xY w)NrH   rG   )rK   rM   rF   zmanifest fetch failed: )rR   rS   rT   rU   rV   rO   rY   rW   rX   rZ   r]   r^   Ú
ValueErrorr   )r   r5   rS   r`   rb   s        r   r6   zRealApiClient.fetch_manifestq   s­   € Ü—.‘.×(Ñ(¨°|ÐEVÐ6WÐ(ÓXˆð	EÜ—‘×'Ñ'¨¼Ð'ÓBð CÀhÜ—z‘z (§-¡-£/×"8Ñ"8¸Ó"AÓB÷C÷ Cò Cûä—‘×%Ñ%¤|´ZÐ@ò 	EÜÐ4°S°EÐ:Ó;ÀÐDûð	Eús5   ¥%B Á
2BÁ<	B ÂBÂB ÂB Â(CÂ:C	Ã	CN)rD   r   r   r   )r_   r   r$   r8   r)   r:   r   r8   r7   r9   r;   r<   )r   r   r   r   r   rc   r%   r*   r,   r.   r0   r2   r6   r>   r   r   r@   r@   B   s9   „ ÙIó-óIó.Ió>óNóKóNóLôEr   r@   c                  óH   — e Zd ZdZd
d„Zdd„Zdd„Zdd„Zdd„Zdd„Z	dd„Z
y	)ÚMockApiClientz•Development stand-in. Logs payloads and returns canned responses.

    Lets the whole agent be exercised end-to-end with no Central API present.
    c                ó˜   — t         j                  d|j                  d«      |j                  d«      |j                  d«      «       ddddœS )	Nz0[mock] register hostname=%s os=%s fingerprint=%sÚhostnameÚos_nameÚfingerprintÚwp_agent_dev_tokené   Úactive)Úagent_tokenÚ	server_idÚstatus©ÚlogÚinfoÚgetr#   s     r   r%   zMockApiClient.register€   sJ   € Ü�‰Ø>Ø�K‰K˜
Ó#Ø�K‰K˜	Ó"Ø�K‰K˜Ó&ô		
ð 0ØØñ
ð 	
r   c                ó‚   — t         j                  d|j                  d«      |j                  d«      t        |«      «       y )Nz0[mock] heartbeat server_id=%s status=%s token=%srx   ry   )r{   r|   r}   r   r(   s      r   r*   zMockApiClient.heartbeatŽ   s0   € Ü�‰Ø>Ø�K‰K˜Ó$Ø�K‰K˜Ó!Ü�5‹Mõ		
r   c                óv   — t         j                  d|j                  d«      |j                  d«      «       dddœS )Nz8[mock] discovery session open scan_type=%s categories=%sÚ	scan_typeÚexpected_categoriesru   Úrunning©Ú
session_idry   rz   r(   s      r   r,   z$MockApiClient.open_discovery_session–   s7   € Ü�‰ØFØ�K‰K˜Ó$Ø�K‰KÐ-Ó.ô	
ð
  ¨9Ñ5Ð5r   c                óò   — |j                  dg «      }t        j                  d|j                  d«      |j                  d«      t        |«      «       |j                  d«      t        |«      t        |«      dœS )NÚitemsz9[mock] discovery items session_id=%s category=%s count=%dr„   Úcategory)r„   ÚacceptedÚ
item_count)r}   r{   r|   Úlen)r   r$   r)   r†   s       r   r.   z$MockApiClient.upload_discovery_itemsž   sf   € Ø—‘˜G RÓ(ˆÜ�‰ØGØ�K‰K˜Ó%Ø�K‰K˜
Ó#Ü�‹Jô		
ð "Ÿ+™+ lÓ3Ü˜E›
Ü˜e›*ñ
ð 	
r   c                óî   — t         j                  d|j                  d«      |j                  d«      |j                  d«      «       |j                  d«      xs g }|j                  d«      |rdddœS dddœS )	Nz>[mock] discovery complete session_id=%s succeeded=%s failed=%sr„   Úsucceeded_categoriesÚfailed_categoriesÚpartialÚ	completedr   )r„   ry   r‰   rz   )r   r$   r)   Úfaileds       r   r0   z MockApiClient.complete_discovery¬   s{   € Ü�‰ØLØ�K‰K˜Ó%Ø�K‰KÐ.Ó/Ø�K‰KÐ+Ó,ô		
ð —‘Ð0Ó1Ò7°Rˆà!Ÿ+™+ lÓ3Ù#)�iØñ
ð 	
à/:Øñ
ð 	
r   c                ót   — t         j                  d|j                  d«      «       |j                  d«      ddœS )Nz%[mock] discovery failed session_id=%sr„   r�   rƒ   rz   r(   s      r   r2   zMockApiClient.fail_discoveryº   s3   € Ü�‰Ø3Ø�K‰K˜Ó%ô	
ð &Ÿk™k¨,Ó7À8ÑLÐLr   c                ó:   — t         j                  d|«       ddddœS )Nz[mock] fetch manifest %sz0.1.2zMhttps://agent.wolfpanel.net/releases/0.1.2/wolfpanel-agent-linux-amd64.tar.gzÚ@0000000000000000000000000000000000000000000000000000000000000000)Úversionr5   Úsha256)r{   r|   r4   s     r   r6   zMockApiClient.fetch_manifestÁ   s$   € Ü�‰Ð+¨SÔ1àØbØñ
ð 	
r   Nr7   r9   r;   r<   r=   r>   r   r   ro   ro   z   s+   „ ñó

ó
ó6ó
ó
óMô
r   ro   c                ó‚   — | j                   rt        j                  d«       t        «       S t	        | j
                  «      S )z4Factory: mock in development, real client otherwise.z0using MockApiClient (WOLFPANEL_API_MOCK enabled))Úapi_mockr{   r|   ro   r@   Úapi_url)Úconfigs    r   Úget_api_clientrš   Ê   s-   € à‡‚Ü�‰ÐCÔDÜ‹ÐÜ˜Ÿ™Ó(Ð(r   )r™   r   r   r   )r   Ú
__future__r   rO   Úurllib.errorrR   Úurllib.requestÚtypingr   r™   r   Úloggerr   r   r{   rV   Ú	Exceptionr   r   r@   ro   rš   r>   r   r   ú<module>r¡      si   ðñõ" #ã Û Û Ý å ß %á�Ó€à€ô'ˆyô '÷"ñ "ô25E�Iô 5EôpM
�Iô M
ô`)r   