
    a
i9                       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	m
Z
mZmZ e
rddlmZmZ  ej        e          ZdZdZdZdZdZd	ZddZddZ G d d          ZdZddZd Z G d d          Z G d d          Z dS ) a  TCP tunnel for accessing services running inside sandboxes.

Establishes a WebSocket connection to the daemon's ``/tunnel`` endpoint,
runs a yamux multiplexing session on top, and forwards local TCP connections
through yamux streams to the target port inside the sandbox.
    )annotationsN)TYPE_CHECKINGAnyOptional)YamuxSessionYamuxStream         z>BHstreamr   portintreturnNonec                l    |                      t          j        t          t          |                     dS )zAWrite the 3-byte connect header on a freshly opened yamux stream.N)writestructpack_CONNECT_HEADER_FMTPROTOCOL_VERSION)r   r   s     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\langsmith/sandbox/_tunnel.py_write_connect_headerr   %   s*    
LL02BDIIJJJJJ    c                ^    |                      d          }|st          d          |d         S )z0Read the 1-byte status response from the daemon.r	   z'tunnel: connection closed before statusr   )readConnectionError)r   datas     r   _read_statusr   *   s2    ;;q>>D IGHHH7Nr   c                  2    e Zd ZdZddZdd
ZddZddZdS )
_WSAdaptera
  Adapts the ``websockets`` message API to a byte-stream interface.

    yamux requires a plain read/write/close byte stream.  WebSocket is
    message-based, so this adapter buffers partially consumed messages on
    reads and sends one binary message per write.
    wsr   r   r   c                j    || _         t                      | _        t          j                    | _        d S N)_ws	bytearray_buf	threadingLock_write_lock)selfr!   s     r   __init__z_WSAdapter.__init__?   s*    KK	$>++r   nr   bytesc                j   t          | j                  |k     rt| j                                        }t	          |t
                    r|                                }| j                            |           t          | j                  |k     tt          | j        d |                   }| j        d |= |S r#   )	lenr&   r$   recv
isinstancestrencodeextendr-   )r*   r,   msgresults       r   r   z_WSAdapter.readD   s    $)nnq  (--//C#s## #jjllIS!!!	 $)nnq   ty!}%%IbqbMr   r   c                    | j         5  | j                            |           d d d            n# 1 swxY w Y   t          |          S r#   )r)   r$   sendr/   )r*   r   s     r   r   z_WSAdapter.writeO   s     	  	 HMM$	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 4yys   /33c                \    	 | j                                          d S # t          $ r Y d S w xY wr#   )r$   close	Exceptionr*   s    r   r:   z_WSAdapter.closeT   sA    	HNN 	 	 	DD	s    
++N)r!   r   r   r   )r,   r   r   r-   )r   r-   r   r   r   r   )__name__
__module____qualname____doc__r+   r   r   r:    r   r   r    r    7   sn         , , , ,
	 	 	 	   
     r   r    i @  tcp_connsocket.socketc                t    t          j                    d
 fd}d
 fd}t          j        |d          }t          j        |d          }|                                 |                                                                  	                                   n# t          $ r Y nw xY w	                     t          j	                   n# t          $ r Y nw xY w	                                  n# t          $ r Y nw xY w|                    d           |                    d           d	S )z:Copy data bidirectionally until one side closes or errors.r   r   c                     	 	                      t                    } | sn                    |            3n# t          $ r Y nw xY w                                 d S #                                  w xY wr#   )r   _BRIDGE_BUF_SIZEsendallr;   setr   doner   rC   s    r   _stream_to_tcpz_bridge.<locals>._stream_to_tcpf   s    		'{{#344   &&&	'  	 	 	D	 HHJJJJJDHHJJJJ&   48 A 
AA AA A4c                     	 	                      t                    } | sn                    |            3n# t          $ r Y nw xY w                                 d S #                                  w xY wr#   )r0   rG   r   r;   rI   rJ   s    r   _tcp_to_streamz_bridge.<locals>._tcp_to_streamr   s    		#}}%566 T"""	#  	 	 	D	 HHJJJJJDHHJJJJrM   T)targetdaemon   )timeoutNr=   )r'   EventThreadstartwaitr:   r;   shutdownsocket	SHUT_RDWROSErrorjoin)r   rC   rL   rO   t1t2rK   s   ``    @r   _bridger_   b   s   ?D
 
 
 
 
 
 
 

 
 
 
 
 
 
 
 
		=	=	=B			=	=	=BHHJJJHHJJJIIKKK   &*++++       GGAGGGAGs6   B% %
B21B26C 
C#"C#'C< <
D	D	c                 P    	 ddl m}  | S # t          $ r t          d          dw xY w)z5Import websockets sync client or raise a clear error.r   )connectz_TCP tunnel requires the 'websockets' package. Install it with: pip install 'langsmith[sandbox]'N)websockets.sync.clientra   ImportError)
ws_connects    r   _ensure_websocketsre      sW    @@@@@@   @
 
 	s   
 %c                      e Zd ZdZdZdZdddd$dZed%d            Ze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d*d!Zd+d"Zd#S ),Tunnela  TCP tunnel to a port inside a sandbox.

    Opens a local TCP listener and forwards each accepted connection through
    a yamux-multiplexed WebSocket to the daemon, which dials the target port
    inside the sandbox.

    Typically used as a context manager::

        with sandbox.tunnel(remote_port=5432) as t:
            conn = psycopg2.connect(host="127.0.0.1", port=t.local_port)

    Or with explicit lifecycle::

        t = sandbox.tunnel(remote_port=5432)
        # ... use tunnel ...
        t.close()
          ?g       @r   r   
local_portmax_reconnectsdataplane_urlr2   api_keyOptional[str]remote_portr   rj   rk   r   r   c                   || _         || _        || _        |p|| _        | j        | _        || _        d | _        d | _        d | _        d | _	        t          j                    | _        d| _        d| _        d S )NF)_dataplane_url_api_key_remote_port_requested_local_port_local_port_max_reconnectsr$   _yamux_server_socket_accept_threadr'   r(   _reconnect_lock_closed_startedr*   rl   rm   ro   rj   rk   s         r   r+   zTunnel.__init__   s{     ,'%/%>;"5-.27;:>(~//r   c                    | j         S )z&Local port the tunnel is listening on.)ru   r<   s    r   rj   zTunnel.local_port   s     r   c                    | j         S )z4Port inside the sandbox that the tunnel connects to.)rs   r<   s    r   ro   zTunnel.remote_port   s       r   c                    | S r#   rB   r<   s    r   	__enter__zTunnel.__enter__   s    r   argsobjectc                .    |                                   d S r#   )r:   )r*   r   s     r   __exit__zTunnel.__exit__   s    

r   c                    | j         rd S d| _         	 |                                  d S # t          $ r |                                   w xY w)NT)r|   	_do_startr;   r:   r<   s    r   _startzTunnel._start   s^    = 	F	NN 	 	 	JJLLL	s	   (  Ac                   |                                   t          j        t          j        t          j                  | _        | j                            t          j        t          j        d           | j        }|dk    r&t          j        t          j        t          j                  }	 |	                    d           |
                    d|f           |                                 t          d| d          # t          $ r Y nMt          $ rA}dt          |          v rn%dt          |                                          v r 	 Y d }~nd }~ww xY w	 |                                 n:# t          $ r Y n.w xY w# 	 |                                 w # t          $ r Y w w xY wxY w| j                            d| j        f           | j                            d	           | j                                        d         | _        t)          j        | j        d
d          | _        | j                                         d S )Nr	   r   rh   z	127.0.0.1zPort zE is already in use by another service. Choose a different local_port.zConnection refusedzalready in use   Tztunnel-accept)rP   rQ   name)_connectrY   AF_INETSOCK_STREAMrx   
setsockopt
SOL_SOCKETSO_REUSEADDRrt   
settimeoutra   r:   r[   ConnectionRefusedErrorr2   lowerbindlistengetsocknameru   r'   rU   _accept_loopry   rV   )r*   r   probees       r   r   zTunnel._do_start   sH   $mFNF<NOO&&v'8&:MqQQQ
 )199M&.&2DEEE  %%%{D12226D 6 6 6   *      '3q6611%Q77KKMMMM   DKKMMMM   D 	  +t/I!JKKK""3'''.::<<Q?'.$T
 
 
 	!!#####sm   +AC> >
EE= 
	E7E
E= EE= E- -
E:9E:=F$?FF$
F!F$ F!!F$c                L   ddl m} | j        }|r&	 |                                 n# t          $ r Y nw xY wt                      }|                                 }i }| j        r
| j        |d<    |||ddd          | _        t          | j                  } ||          | _        dS )z:Establish (or re-establish) the WebSocket + yamux session.r   )r   z	X-Api-Key   rR   N)additional_headersopen_timeoutclose_timeoutping_interval)
langsmith.sandbox._yamuxr   rw   r:   r;   re   _build_ws_urlrr   r$   r    )r*   r   	old_yamuxrd   ws_urlheadersadapters          r   r   zTunnel._connect!  s    999999K	 	!!!!    ())
##%%"$= 	1#'=GK :&
 
 
 TX&&"l7++s   & 
33r   c                X   ddl m} | j        r| j        j        s| j        S | j        5  | j        r| j        j        s| j        cddd           S d}t          | j                  D ]}	 |                                  t          	                    d|dz              | j        c cddd           S # t          $ rN}|}|| j        dz
  k     r4t          | j        d|z  z  | j                  }t          j        |           Y d}~d}~ww xY w |d| j         d          |# 1 swxY w Y   dS )	z4Return a live yamux session, reconnecting if needed.r   )TunnelErrorNz tunnel: reconnected (attempt %d)r	   r
   ztunnel: reconnect failed after z	 attempts)langsmith.sandbox._exceptionsr   rw   	is_closedrz   rangerv   r   loggerdebugr;   min_BACKOFF_BASE_BACKOFF_MAXtimesleep)r*   r   last_errattemptexcdelays         r   _ensure_sessionzTunnel._ensure_session=  s   ======; 	t{4 	;! 	 	{ #4;#8 #{	 	 	 	 	 	 	 	 -1H !566 * **MMOOOLL!CWq[QQQ;&&	 	 	 	 	 	 	 	 ! * * *"H!5!999 # .!W*= -! ! 
5)))* +Q$2FQQQ '	 	 	 	 	 	 	 	 	 	sB   DD(8B/ D/
D9AD=DDDD#&D#c                    | j         rdS d| _         | j        r+	 | j                                         n# t          $ r Y nw xY w| j        r| j                                         dS dS )z.Shut down the tunnel, closing all connections.NT)r{   rx   r:   r[   rw   r<   s    r   r:   zTunnel.close[  s    < 	F 	#))++++    ; 	 K	  	 s   3 
A A c                    | j         sh	 | j                                        \  }}n# t          $ r Y d S w xY wt	          j        | j        |fdd                                           | j         fd S d S )NTztunnel-bridge)rP   r   rQ   r   )r{   rx   acceptr[   r'   rU   _handle_connrV   )r*   conn_s      r   r   zTunnel._accept_loopl  s    , 
	-4466aa   (W$	  
 eggg , 
	 
	 
	 
	 
	s   & 
44rC   rD   c                ,   	 |                                  }|                                }t          || j                   t	          |          }|t
          k    rt          ||           d S |                                 |                                 |t          k    r"t          
                    d| j                   d S |t          k    r"t          
                    d| j                   d S |t          k    r"t          
                    dt                     d S t          
                    d|           d S # t          $ rR}t                              d|           	 |                                 n# t           $ r Y n
w xY wY d }~d S Y d }~d S d }~ww xY w)Nz%tunnel: port %d not allowed by daemonz3tunnel: nothing listening on port %d inside sandboxz.tunnel: protocol version mismatch (client v%d)ztunnel: unknown status %dz$tunnel: connection handler error: %s)r   open_streamr   rs   r   	STATUS_OKr_   r:   STATUS_PORT_NOT_ALLOWEDr   warningSTATUS_DIAL_FAILEDSTATUS_UNSUPPORTED_VERSIONr   r;   r   r[   )r*   rC   sessionr   statusr   s         r   r   zTunnel._handle_conny  s   $	**,,G((**F!&$*;<<<!&))F"")))LLNNNNN000;%     ---I%     555D$    
 :FCCCCC 	 	 	LL?EEE        !     	sU   A'D7 +AD7  +D7 -+D7 D7 7
FFE21F2
E?<F>E??FFc                    | j                             d          }|                    dd                              dd          }| dS )N/zhttps://zwss://zhttp://zws://z/tunnel)rq   rstripreplace)r*   urls     r   r   zTunnel._build_ws_url  sG    !((--kk*h//77	7KKr   Nrl   r2   rm   rn   ro   r   rj   r   rk   r   r   r   r   r   )r   rg   r   r   r   r   r=   )r   r   )rC   rD   r   r   )r   r2   )r>   r?   r@   rA   r   r   r+   propertyrj   ro   r   r   r   r   r   r   r:   r   r   r   rB   r   r   rg   rg      sZ        $ ML      0       X  ! ! ! X!      
	 	 	 	*$ *$ *$ *$X, , , ,8   <       "   % % % %N     r   rg   c                  j    e Zd ZdZdddddZedd            Zedd            ZddZddZ	ddZ
dS )AsyncTunnelaw  Async wrapper around :class:`Tunnel`.

    The underlying tunnel runs in background threads (TCP listener + bridges);
    async context-manager methods delegate to the sync tunnel via the event
    loop's executor.

    Usage::

        async with await sandbox.tunnel(remote_port=5432) as t:
            conn = await asyncpg.connect(host="127.0.0.1", port=t.local_port)
    r   r   ri   rl   r2   rm   rn   ro   r   rj   rk   r   r   c               8    t          |||||          | _        d S )Nri   )rg   _tunnelr}   s         r   r+   zAsyncTunnel.__init__  s-     !)
 
 
r   c                    | j         j        S r#   )r   rj   r<   s    r   rj   zAsyncTunnel.local_port  s    |&&r   c                    | j         j        S r#   )r   ro   r<   s    r   ro   zAsyncTunnel.remote_port  s    |''r   c                |   K   t          j                    }|                    d | j        j                   d {V  | S r#   )asyncioget_running_looprun_in_executorr   r   )r*   loops     r   
__aenter__zAsyncTunnel.__aenter__  sF      '))""4)<=========r   r   r   c                |   K   t          j                    }|                    d | j        j                   d {V  d S r#   )r   r   r   r   r:   )r*   r   r   s      r   	__aexit__zAsyncTunnel.__aexit__  sG      '))""4);<<<<<<<<<<<r   c                8    | j                                          dS )z;Shut down the tunnel (sync, safe to call from any context).N)r   r:   r<   s    r   r:   zAsyncTunnel.close  s    r   Nr   r   )r   r   r   r=   )r>   r?   r@   rA   r+   r   rj   ro   r   r   r:   rB   r   r   r   r     s        
 
$ 
 
 
 
 
 
" ' ' ' X' ( ( ( X(   
= = = =     r   r   )r   r   r   r   r   r   )r   r   r   r   )r   r   rC   rD   r   r   )!rA   
__future__r   r   loggingrY   r   r'   r   typingr   r   r   r   r   r   	getLoggerr>   r   r   r   r   r   r   r   r   r   r    rG   r_   re   rg   r   rB   r   r   <module>r      s    # " " " " "          / / / / / / / / / / CBBBBBBBB		8	$	$  	  !  K K K K
   ! ! ! ! ! ! ! !P  1 1 1 1r
 
 
{ { { { { { { {F1 1 1 1 1 1 1 1 1 1r   