
    +j>&                         d 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m	Z	m
Z
mZ ddlmZmZmZmZmZ  ej        e          Z G d d          ZdS )u  
LightClaw native WebSocket connection loop + pending ACK table.
Mirrors: src/socket/native-socket.ts

协议层职责（"哑"传输）：
    - 连接 / 重连 / 心跳 / 收发
    - 收到帧 → _handle_raw() 同步派发，绝不 await 业务
    - 内置 pending ACK 表：_ws_emit_with_timeout() → callback
    - 断线 → _flush_pending_acks(error) 一次性回调所有挂起
    N)CallableDictOptionalTuple   )API_PATH_TICKETEMIT_ACK_TIMEOUTRECONNECT_DELAY_BASERECONNECT_DELAY_MAXSOCKET_PATHc            
           e Zd ZU dZeeeej        e	f         f         e
d<   ddZdededdfdZdeded	ed
e	ee         gdf         ddf
dZdeddfdZdeddfdZdefdZdedefdZddZdeddfdZdS )NativeSocketMixinu  
    Mixin providing the WebSocket transport layer.
    Mirrors: NativeSocketClient in native-socket.ts

    Provides to upper layers:
        _ws_emit(event, data)                              — fire-and-forget
        _ws_emit_with_timeout(event, data, timeout_ms, cb) — ACK + timeout
        _on_ws_ack(related_msg_id)                         — dispatch ACK
        _flush_pending_acks(err)                           — disconnect cleanup

    Requires (set by LightClawAdapter.__init__):
        self._ws_base_url: str
        self._api_base_url: str
        self._api_keys: list[str]
        self._session: aiohttp.ClientSession
        self._reliable: ReliableEmitter
        self._stopped: bool
        self._socket_id: str
        self._first_connect_event: asyncio.Event

    Must implement (in LightClawAdapter):
        async _handle_raw(raw: str) -> None
        _mark_connected() -> None
        _mark_disconnected() -> None
    _pending_acksreturnNc                     i | _         dS )zMust be called from __init__.N)r   )selfs    H/home/agentuser/.hermes/plugins/lightclawbot/src/socket/native_socket.py_init_pending_acksz$NativeSocketMixin._init_pending_acks?   s        eventdatac                     | j         }|t          |dd          rdS 	 t          j        |                    ||d                     dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)u   Fire-and-forget send.  Synchronous, never blocks.

        Mirrors TS ``socket.emit(event, data)``  — send and return
        immediately without registering an ACK callback.
        NclosedTr   r   z$[lightclaw] _ws_emit send failed: %s)_wsgetattrasyncioensure_future	send_json	Exceptionloggerdebug)r   r   r   wsexcs        r   _ws_emitzNativeSocketMixin._ws_emitC   s     X:Xt44:F	F !",,/M/M"N"NOOOOO 	F 	F 	FLL?EEEEEEEEE	Fs   *A
 

A:A55A:
timeout_mscallbackc                      j         }|t          |dd          r t          d                     dS |                    d          pdt	          |            j                                      }||d                                          t          j                    }d fd	}|	                    |d
z  |          }	|	f j        <   	 t          j
        |                    ||d                     dS # t          $ rU}
|	                                  j                            d            t          d|
                      Y d}
~
dS d}
~
ww xY w)a  Send + register ACK callback with timeout.  Synchronous return.

        Mirrors TS ``socket.timeout(ms).emit(event, data, callback)``.

        The *callback* receives ``None`` on ACK success, or an ``Exception``
        on timeout / disconnect.  It is invoked exactly once.
        Nr   Tznot connectedmsgId_ack_r   r   c                  v    j                             d           } |  t          d                     d S d S )NzACK timeout)r   popTimeoutError)entryr'   pending_keyr   s    r   _on_timeoutz<NativeSocketMixin._ws_emit_with_timeout.<locals>._on_timeoutp   sG    &**;==E m4455555 ! r   g     @@r   zsend failed: r   N)r   r   ConnectionErrorgetidr   cancelr   get_running_loop
call_laterr   r   r    r,   )r   r   r   r&   r'   r#   existingloopr0   timerr$   r/   s   `   `      @r   _ws_emit_with_timeoutz'NativeSocketMixin._ws_emit_with_timeoutS   s    X:Xt44:H__55666F hhw''=+=2d88+=+= %))+66QK   '))	6 	6 	6 	6 	6 	6 	6 	6
 
V 3[AA+0(*;;'	=!",,/M/M"N"NOOOOO 	= 	= 	=LLNNN"";555H_%:S%:%:;;<<<<<<<<<		=s   *D 
E!A
EE!related_msg_idc                     | j                             |d          }|t                              d|           dS |\  }}|                                  |d           dS )zDispatch an incoming ACK.  Mirrors TS ``_handleMessage`` ACK branch.

        Called from ``_handle_raw`` when ``event == "message:ack"``.
        Looks up the pending table and invokes the callback with ``None``.
        Nz7[lightclaw] ACK for unknown msgId=%s (already resolved))r   r,   r!   r"   r5   )r   r<   r.   r:   r'   s        r   
_on_ws_ackzNativeSocketMixin._on_ws_ack   sk     "&&~t<<=LLI   Fxr   errc                     | j         }i | _         |                                D ]5\  }}|                                 	  ||           &# t          $ r Y 2w xY wdS )ut   Disconnect cleanup — resolve all pending callbacks with *err*.

        Mirrors TS ``_flushPendingAcks``.
        N)r   valuesr5   r    )r   r?   pendingr:   r'   s        r   _flush_pending_acksz%NativeSocketMixin._flush_pending_acks   s|    
 $&~~// 	 	OE8LLNNN   		 	s   A		
AAc                   K   ddl }| j        d         }| j         t           }d| dd}| j                            |||                    d                    4 d{V 	 }|j        d	k    rt          d
|j                   |	                                 d{V }|
                    d          dk    r%t          d|
                    d                     |
                    di           
                    dd          }|st                              d           |cddd          d{V  S # 1 d{V swxY w Y   dS )z?Fetch a fresh connection ticket. Called before each WS connect.r   NzBearer channel)authorizationz	x-product   )total)headerstimeout   zPOST /cgi/ticket HTTP codezPOST /cgi/ticket error: messager   ticket zD[lightclaw] /cgi/ticket returned empty ticket, connecting without it)aiohttp	_api_keys_api_base_urlr   _sessionpostClientTimeoutstatusRuntimeErrorjsonr3   r!   warning)r   rP   keyurlrI   respr   rN   s           r   _fetch_ticketzNativeSocketMixin._fetch_ticket   s     nQ#6_66$3cOO)LL=%%)))33 & 
 
 	 	 	 	 	 	 	 	 {c!!"#IDK#I#IJJJ$$$$$$Dxx1$$"#Sdhhy>Q>Q#S#STTTXXfb))--h;;F gefff	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   $CD99
EErN   c                     | j                             d          }t          }|r%dt          j                            |d           dnd}| | | S )z.Construct full WS URL with ticket query param./z?ticket=rO   )safez&enableMultiLogin=false)_ws_base_urlrstripr   urllibparsequote)r   rN   basepathquerys        r   _build_ws_urlzNativeSocketMixin._build_ws_url   sd    !((--[aiW6<--f2->>WWWWgi%%e%%%r   c                   K   d}| j         sY	 |                                  d{V }|                     |          }t                              d|dd                    |                     |           d{V  d}nC# t          j        $ r Y dS t          $ r%}t          	                    d|           Y d}~nd}~ww xY w| j         rdS |dz  }t          t          d|dz
  z  z  t                    }|dt          j                    d	z  z   z  }t                              d
||           	 t          j        |           d{V  n# t          j        $ r Y dS w xY w| j         WdS dS )z(Reconnect loop with exponential backoff.r   Nz[lightclaw] Connecting to %sP   z([lightclaw] Connection session ended: %s   r   g?g?z.[lightclaw] Reconnecting in %.1fs (attempt %d))_stoppedr]   ri   r!   info	_run_oncer   CancelledErrorr    rY   minr
   r   randomsleep)r   attemptsrN   ws_urlr$   
base_delaydelays          r   _connection_loopz"NativeSocketMixin._connection_loop   s     - 		P#1133333333++F33:F3B3KHHHnnV,,,,,,,,,)    P P PI3OOOOOOOOP } MH1Q8a<5HIK^__J##(="=>EKKH%QYZZZmE**********)   + - 	 	 	 	 	s0   A/A> >B>	B>B99B>+E EEru   c                 H  K   ddl }| j                            |d|                    dd                     d{V }|| _        	 d| _        |                                  | j                                         | j	        r| j	        
                                 |2 3 d{V }| j        r n|j        |j        j        k    rT	 |                     |j                   d{V  G# t"          $ r%}t$                              d|           Y d}~qd}~ww xY w|j        |j        j        |j        j        fv r n6 d| _        |                     t/          d	                     | j	        r| j	                                         |j        s,	 |                                 d{V  n# t"          $ r Y nw xY w|                                  t$                              d
| j        pd           dS # d| _        |                     t/          d	                     | j	        r| j	                                         |j        s,	 |                                 d{V  n# t"          $ r Y nw xY w|                                  t$                              d
| j        pd           w xY w)z=Single WebSocket session: connect -> receive loop -> cleanup.r   N   rG   )rH   connect)	heartbeatrJ   rO   z [lightclaw] handle_raw error: %s
disconnectz'[lightclaw] Disconnected (socket_id=%s)?)rP   rS   
ws_connectrU   r   
_socket_id_mark_connected_first_connect_eventset	_reliableresumerm   type	WSMsgTypeTEXT_handle_rawr   r    r!   errorCLOSEDERRORrC   r2   pauser   close_mark_disconnectedrn   )r   ru   rP   r#   raw_msgr$   s         r   ro   zNativeSocketMixin._run_once   sE     =++))b)AA , 
 
 
 
 
 
 
 

 	[ DO  """%))+++~ (%%'''!# 	 	 	 	 	 	 	g= E<7#4#999N"..w|<<<<<<<<<<$ N N N%GMMMMMMMMN\g&7&>@Q@W%XXXE Y "$ DH$$_\%B%BCCC~ '$$&&&9 ((**$$$$$$$$    D##%%%KKA4?CYVYZZZZZ DH$$_\%B%BCCC~ '$$&&&9 ((**$$$$$$$$    D##%%%KKA4?CYVYZZZZsz   AG- D8#G-  C$#G- $
D.D	G- D&G- 
F% %
F21F2-AJ!?IJ!
I'$J!&I'':J!r1   )__name__
__module____qualname____doc__r   strr   r   TimerHandler   __annotations__r   dictr%   intr   r    r;   r>   rC   r]   ri   rx   ro    r   r   r   r      s         < U7#6#@AABBBB       Fc F F$ F F F F ,=,= ,= 	,=
 HY/0$67,= 
,= ,= ,= ,=\     "y T    $S    ,&C &C & & & &   6([c ([d ([ ([ ([ ([ ([ ([r   r   )r   r   rX   loggingrr   urllib.parserc   typingr   r   r   r   configr   r	   r
   r   r   	getLoggerr   r!   r   r   r   r   <module>r      s  	 	         2 2 2 2 2 2 2 2 2 2 2 2              
	8	$	$j[ j[ j[ j[ j[ j[ j[ j[ j[ j[r   