
     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)RealApiClientN   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&zz'"))'2#5EVW
 (-O$..((QW(X		I'''Bh}}--g6+/tzz$'R CBB ||%% 	XdV9SXXJ7SXXNTWW%%|4 	I dV#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'''Bh}}--g6+/tzz$'R CBB ||%% 	XdV9SXXJ7SXXNTWW%%|4 	IdV#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    zz2G4z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       zz<gMMr   c                &    U R                  SX5      $ )Nz/api/v1/agent/discovery/itemsr   r/   s      r   r7   $RealApiClient.upload_discovery_items   s    zz97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    zz: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    ..((|EV6W(X	E'''Bhzz(--/"8"8"AB CBB%%|Z@ 	E4SE:;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    yy95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    zz3J<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zz3F87CW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    zz0	A7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   zDevelopment 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    >KK
#KK	"KK&		
 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    >KK$KK!5M		
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KK$KK-.	

  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    GR(GKK%KK
#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KK%KK./KK+,		
 017R!++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KK%	
 &kk,78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    BG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   