
    `gj                        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	m
Z
mZ ddlmZ ddlmZ ddZ G d	 d
      Zy)aV  Remote node server.

Runs on the machine that will host the Meet bot (typically the user's
Mac laptop with a signed-in Chrome). Exposes a WebSocket endpoint that
accepts signed RPC requests and dispatches them to the existing
``plugins.google_meet.process_manager`` module.

Launched by ``hermes meet node run``.

Token handling
--------------
On first boot we mint 32 hex chars of entropy and persist them at
``$HERMES_HOME/workspace/meetings/node_token.json``. Subsequent boots
reuse the same token so previously-approved gateways don't need to be
re-paired. The operator copies this token out-of-band to the gateway
via ``hermes meet node approve <name> <url> <token>``.

Dependencies
------------
``websockets`` is an optional dep. We import it lazily inside
:meth:`serve` so installing the plugin doesn't require it unless you
actually host a node.
    )annotationsN)Path)AnyDictOptional)get_hermes_home)protocolc                 :    t        t                     dz  dz  dz  S )N	workspacemeetingsznode_token.json)r   r        R/root/.hermes/venv/lib/python3.12/site-packages/plugins/google_meet/node/server.py_default_token_pathr   %   s     !"[0:=@QQQr   c                  T    e Zd ZdZ	 	 	 	 d	 	 	 	 	 	 	 	 	 d	dZd
dZd
dZddZddZy)
NodeServerz5WebSocket server that executes meet bot RPCs locally.Nc                t    || _         || _        || _        |t        |      n	t	               | _        d | _        y )N)hostportdisplay_namer   r   
token_path_token)selfr   r   r   r   s        r   __init__zNodeServer.__init__,   s8     		(.8.D$z*J]J_%)r   c                   | j                   r| j                   S | j                  j                         r\	 t        j                  | j                  j                  d            }|j                  d      }t        |t              r|r	|| _         |S t        j                  d      }| j                  j                  j                  dd       | j                  j                  d      }|j!                  t        j"                  |t%        j$                         dd	
      d       	 |j'                  d       |j+                  | j                         || _         |S # t        t        j                  f$ r Y w xY w# t        t(        f$ r Y Tw xY w)z@Return the persisted shared secret, generating one on first use.utf-8encodingtoken   Tparentsexist_okz	.json.tmp)r   generated_at   )indenti  )r   r   is_filejsonloads	read_textget
isinstancestrOSErrorJSONDecodeErrorsecrets	token_hexparentmkdirwith_suffix
write_textdumpstimechmodNotImplementedErrorreplace)r   datatoktmps       r   ensure_tokenzNodeServer.ensure_token;   sE   ;;;;??""$zz$//";";W";"MNhhw'c3'C"%DKJ #$$TD$Aoo))+6JJdiikB1M 	 	
	IIe 	DOO$
% T112  ,- 		s$   AE $E8 E54E58F
	F
c                "    | j                         S )zDAlias for :meth:`ensure_token`; does not mutate on subsequent calls.)r>   )r   s    r   	get_tokenzNodeServer.get_tokenZ   s      ""r   c                
  K   | j                         }t        j                  ||      \  }}|s2t        j                  t	        |j                  d      xs d      |      S |d   }|d   }|d   }ddlm} 	 |dk(  r%d|| j                  t        j                         d	d
S |dk(  rYdD 	ci c]  }	|	|v r|	||	    }
}	d|
vrt        j                  |d      S  |j                  d'i |
}t        j                  ||      S |dk(  r:|j                  dd      }|j                  |      }t        j                  ||      S |dk(  r$t        j                  ||j                               S |dk(  r9|j                  d      }|j                  |      }t        j                  ||      S |dk(  r|j                  dd      }|j                         }d}|r|j                  d      rt!        |d         dz  }	 |j"                  j%                  dd       |j'                  dd       5 }|j)                  t+        j,                  |t        j                         d!      d"z          d#d#d#       d}t        j                  |d||d$      S 	 t        j                  |d&|      S c c}	w # 1 sw Y   DxY w# t.        $ r d}Y Rw xY w# t0        $ r8}t        j                  |t3        |      j4                   d%|       cY d#}~S d#}~ww xY ww)(u  Validate + dispatch a single decoded request envelope.

        Always returns a response envelope (success or error); never
        raises. Errors from inside the process_manager are wrapped into
        the response payload's ``ok``/``error`` keys (which pm already
        does) rather than being re-encoded as error envelopes — the
        envelope-level error channel is reserved for auth / protocol
        failures.
        id typepayloadr   )process_managerpingpong)r   ts)rD   rB   rE   	start_bot)url
guest_namedurationheaded
auth_state
session_idout_dirrK   zmissing 'url' in payloadstopreason	requested)rS   status
transcriptlast)rW   saytextFrQ   zsay_queue.jsonlTr!   ar   r   )rY   rI   
N)okenqueuedrY   z: zunhandled type: r   )r>   _protovalidate_request
make_errorr-   r+   plugins.google_meetrF   r   r7   startmake_responserR   rU   rV   _read_activer   r2   r3   openwriter(   r6   r.   	ExceptionrD   __name__)r   msgexpectedr\   rS   req_idtrE   pmkkwargsresult
reason_argrW   rY   activer]   queuefhexcs                       r   _handle_requestzNodeServer._handle_request`   s     $$&,,S(;
F$$S)<"%=vFFTKi. 	>0	MF{ &f484E4E*.))+$78 8 KEG| wqzM  &!,,V5OPP!+F+++FF;;F{$[[;?

3++FF;;H}++FBIIK@@L {{6*D1++FF;;Ez {{62.* fjj3 	!236GGE)**4$*G"ZZgZ> ["HHTZZTYY[0Q%RUY%YZ[#' ++XtD ! .   +;A5)ABBW>[ [ # )#()  	M$$VS	0B0B/C2cU-KLL	Ms   A3L6)J? L 	J? )J:J? L'J? =L>>J? <L=(J? %L&=J? #L$AJ? 20J. "=J"
J. )J? LJ? "J+'J. .J<9J? ;J<<J? ?	L -K;5L 6L;L  Lc                j   K   	 ddl } j                           fd}|j	                  | j
                   j                        4 d{    ddl}|j                          d{    ddd      d{    y# t        $ r}t        d      |d}~ww xY w7 N7 47 &# 1 d{  7  sw Y   yxY ww)z{Run the WebSocket server until cancelled.

        Blocks forever. Callers typically wrap this in ``asyncio.run``.
        r   Nz[NodeServer.serve requires the 'websockets' package. Install it with: pip install websocketsc                  K   | 2 3 d {   }	 t        j                  t        |t              r|n|j                  d            }j                  |       d {   }| j                  t        j                  |             d {    7 # t        $ rN}| j                  t        j                  t        j                  dd|                    d {  7   Y d }~d }~ww xY w7 7 c6 y w)Nr   rC   zdecode: )	r^   decoder,   r-   
ValueErrorsendencoder`   rv   )wsrawri   ru   replyr   s        r   _handlerz"NodeServer.serve.<locals>._handler   s      4 4c --z#s/CT[I\]C #22377ggfmmE23334 " ''&--0A0A"QTPUFV0W"XYYY 83  sx   C.C,BC,C.6BC.C(+C.C*C.C,	C%>C CC C. C%%C.*C.,C.)	
websocketsImportErrorRuntimeErrorr>   server   r   asyncioFuture)r   r   ru   r   r   s   `    r   r   zNodeServer.serve   s     
	 		4 ##HdiiC 	# 	#.."""	# 	# 	#%  	: 	$	# #	# 	# 	# 	#s|   B3A; A B3	B
B3B%B&B*B35B6B3;	BBBB3BB3B0$B'%B0,B3)z	127.0.0.1ieI  Nzhermes-meet-node)
r   r-   r   intr   zOptional[Path]r   r-   returnNone)r   r-   )ri   Dict[str, Any]r   r   )r   r   )	rh   
__module____qualname____doc__r   r>   r@   rv   r   r   r   r   r   r   )   s_    ?  %).** * #	*
 * 
*>#HCX#r   r   )r   r   )r   
__future__r   r(   r0   r7   pathlibr   typingr   r   r   hermes_constantsr   plugins.google_meet.noder	   r^   r   r   r   r   r   <module>r      s9   0 #     & & , 7R_# _#r   