+
    jHàj�`  ã                   ó  a € R: tI0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RIHtHtHtHt ^ RIHt ^ RIHt ^ RIHt ]P6                  ! R4      t ]P:                  P=                  R4      '       d
   ^ RIH t!H"t" M^ R	I#H!t!H"t" R
t$]
PN                  ! R4      t(Rt)Rt*]! R;^R<])R7      t+R R lt,R R lt-^ RI.H/t/ R R lt0R R lt1R R lt2R R lt3R R  lt4^ R!I.H5t5H6t6H7t7 R" R# lt8R$ R% lt9R& R' lt:R=R( R) llt;]<! R*R+04      t=R, R- lt>R. R/ lt?R0 R1 lt@R2 R3 ltAR=R4 R5 lltBR6 R7 ltC^tDRsE] ^ k ]PŒ                  ! 4       tGR8 R9 ltHR#   ]% d    Rt!Rt$ ! R R]&4      t" Lßi ; i)>z–Chat/terminal WebSocket plumbing: PTY bridge selection and registry, WS
client/origin/auth gates, chat argv resolution, gateway/sidecar URL building.
N)ÚOptional)ÚFastAPIÚRequestÚ	WebSocketÚWebSocketDisconnect)ÚPath)ÚPtySessionRegistryzhermes_cli.web_serverÚwin)ÚWinPtyBridgeÚPtyUnavailableError)Ú	PtyBridger   TFc                   ó   € ] tR t^)tRtRtR# )r   z5Stub when the platform PTY bridge cannot be imported.© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   ó    Ú?/home/ubuntu/.hermes/hermes-agent/hermes_cli/web_server_chat.pyr   r   )   s   † ÝCr   r   s   \x1b\[RESIZE:(\d+);(\d+)\]gš™™™™™É?gš™™™™™©?)ÚttlÚmax_sessionsÚ
buffer_capÚread_timeoutc                ó4   € V ^8„  d   QhR\         R\        RR/# )é   ÚwsÚpathÚreturnN)r   Ústr)Úformats   "r   Ú__annotate__r"   6   s!   € ÷ ñ ¤yð ¼3ð À4ñ r   c             ƒ  óš   "  € \         P                  RV4        V P                  RRR7      G Rj  x€L
  R#  L  \         d     R# i ; i5i)zDClose only the terminal socket when its child stops accepting input.z5pty input stalled path=%s; recycling terminal sessioniõ  zPTY input stalled)ÚcodeÚreasonN)Ú_logÚwarningÚcloseÚ	Exception)r   r   s   &$r   Ú_close_stalled_pty_inputr*   6   sA   é € ä‡L�LÐHÈ$ÔOðØ�h‰h˜DÐ)<ˆhÓ=×=Ô=øÜô Úðüs1   ‚Aš9 °7±9 µA·9 ¹AÁAÁAÁAc                ó(   € V ^8„  d   QhR\         RR/# )r   r   r   N)r   )r!   s   "r   r"   r"   ?   s   € ÷ D.ñ D.œ9ð D.°ñ D.r   c           	   ƒ  ó¼  a aa"  € \         P                  ! 4       oR VVV 3R llp\         P                  ! V! 4       4      p   S P                  4       G Rj  x€L
 pTP                  R4      R8X  d   EMTP                  R4      pTf;   TP                  R4      p\        T\        4      '       d   TP                  R4      MR	pT'       g   K‹  \        P                  T4      pT'       de   TP                  4       \        T4      8X  dG   SP                  \        TP                  ^4      4      \        TP                  ^4      4      R
7       EK  SP!                  T4      G Rj  x€L
 '       d   EK.  \#        S RR7      G Rj  x€L
   TP'                  4        \(        P*                  ! \         P,                  \.        4      ;_uu_ 4        TG Rj  x€L
  RRR4       \         P0                  ! SP2                  4      G Rj  x€L
  R#  EL­  \         d     K�  i ; i L¶ Lš  \$         d     L¤i ; i L`  + '       g   i     Lf; i LE  TP'                  4        \(        P*                  ! \         P,                  \.        4      ;_uu_ 4        TG Rj  x€L 
  RRR4       M  + '       g   i     M; i\         P0                  ! SP2                  4      G Rj  x€L 
  i ; i5i)u‘  Original 1:1 socket<->PTY pump: stream until disconnect, then close the
bridge. Used when no ``?attach=`` token is supplied (keep-alive opt-in).

Behavior is identical to the pre-keep-alive ``pty_ws`` body, including the 54028 half-open-socket
protection (reader EOF â†’ close the WS so the writer's ``ws.receive()`` unparks) and the #53227
``to_thread`` offloads for the blocking ``bridge.close()``.
c                ó   € V ^8„  d   QhRR/# ©r   r   Nr   )r!   s   "r   r"   Ú"_legacy_pump.<locals>.__annotate__I   s   € ÷ !ñ ! $ñ !r   c            	   “   ó¼  <"  €   SP                  RSP                  \        4      G Rj  x€L
 p V fš    \        P                  ! \
        4      ;_uu_ 4        \        P                  ! SP                  4      G Rj  x€L
  RRR4       \        P                  ! \
        4      ;_uu_ 4        SP                  4       G Rj  x€L
  RRR4       R# V '       g%   \        P                  ! \        4      G Rj  x€L
  Kó   SP                  V 4      G Rj  x€L
  EK   Lê Lœ  + '       g   i     L¢; i Ln  + '       g   i     R# ; i LP L6  \
         dÅ     \        P                  ! \
        4      ;_uu_ 4        \        P                  ! SP                  4      G Rj  x€L 
  RRR4       M  + '       g   i     M; i\        P                  ! \
        4      ;_uu_ 4        SP                  4       G Rj  x€L 
  RRR4       R#   + '       g   i     R# ; ii ; i  \        P                  ! \
        4      ;_uu_ 4        \        P                  ! SP                  4      G Rj  x€L 
  RRR4       M  + '       g   i     M; i\        P                  ! \
        4      ;_uu_ 4        SP                  4       G Rj  x€L 
  RRR4       i   + '       g   i     i ; i; i5i)TN)Úrun_in_executorÚreadÚ_PTY_READ_CHUNK_TIMEOUTÚ
contextlibÚsuppressr)   ÚasyncioÚ	to_threadr(   ÚsleepÚ_PTY_IDLE_BACKOFFÚ
send_bytes)ÚchunkÚbridgeÚloopr   s    €€€r   Úpump_pty_to_wsÚ$_legacy_pump.<locals>.pump_pty_to_wsI   sÚ  øé € ð	!ØØ"×2Ñ2°4¸¿¹ÔF]Ó^×^�Ø’=Øô ×$Ò$¤Y×/Õ/ô ×'Ò'¨¯©Ó5×5Ð5÷ 0ô ×$Ò$¤Y×/Õ/Ø—h‘h“j× Ð ÷ 0Ñ/÷# Ü!Ÿ-š-Ô(9Ó:×:Ð:ÙðØŸ-™-¨Ó.×.Ó.ñ _ñ& 6÷ 0×/úñ
 !÷ 0×/Ð/úñ! ;ñ /øÜ ô Øô ×$Ò$¤Y×/Õ/ô ×'Ò'¨¯©Ó5×5Ñ5÷ 0×/×/Ð/úô ×$Ò$¤Y×/Õ/Ø—h‘h“j× Ñ ÷ 0×/×/Ò/úðûô ×$Ò$¤Y×/Õ/ô ×'Ò'¨¯©Ó5×5Ñ5÷ 0×/×/Ð/úô ×$Ò$¤Y×/Õ/Ø—h‘h“j× Ñ ÷ 0×/×/Ð/ÿsc  ƒK…%H ªD«H ´"KÁ$DÁ:DÁ;DÁ?*KÂ)D.Â=D,Â>D.Ã
KÃH ÃH Ã1EÃ2H Ã9E ÄEÄE ÄH ÄDÄD)	Ä$KÄ,D.Ä.D?	Ä9	KÅH ÅE ÅHÅH Å"KÅ4$F'ÆFÆF'Æ	KÆ'F7	Æ2*KÇH Ç0G3Ç1H Ç6
KÈ H	È	KÈHÈH È#KÈ;$I.	ÉI"É I.	É%	KÉ.I>É9*KÊ#K	Ê7J:Ê8K	Ê=	KËKËKËKNÚtypezwebsocket.disconnectÚbytesÚtextzutf-8r   )ÚcolsÚrowsÚlegacy)r   )r6   Úget_running_loopÚcreate_taskÚreceiveÚRuntimeErrorÚgetÚ
isinstancer    ÚencodeÚ
_RESIZE_REÚmatchÚendÚlenÚresizeÚintÚgroupÚwriter*   r   Úcancelr4   r5   ÚCancelledErrorr)   r7   r(   )	r   r<   r>   Úreader_taskÚmsgÚrawrB   rN   r=   s	   ff      @r   Ú_legacy_pumprZ   ?   s  úé € ô ×#Ò#Ó%€D÷!ò !ô4 ×%Ò%¡nÓ&6Ó7€Kð.ØðØŸJ™J›L×(�ð
 �w‰w�v‹Ð"8Ô8ÙØ—'‘'˜'Ó"ˆCØŠ{Ø—w‘w˜v“�Ü.8¸¼s×.CÒ.C�d—k‘k 'Ô*È�ßÙä×$Ñ$ SÓ)ˆEß˜Ÿ™›¬¨C«Ô0Ø—‘¤3 u§{¡{°1£~Ó#6¼SÀÇÁÈQÃÓ=P�ÔQÚØŸ™ cÓ*×*×*Ñ*Ü.¨r¸ÔA×AÐAØð 	×ÑÔÜ× Ò ¤×!7Ñ!7¼×CÕCØ×Ð÷ Dä×Ò §¡Ó-×-Ò-ò7 )øÜô ò ðúñ" +ÙAøäô Ùðúñ
 ÷ D×Cúá-øð 	×ÑÔÜ× Ò ¤×!7Ñ!7¼×CÕCØ×Ñ÷ D×C×CÐCúä×Ò §¡Ó-×-Ò-üs  …<KÁH ÁH ÁHÁH ÁH Á2I Á4AH ÃH Ã*A8H Å"HÅ#H Å0H Æ HÆH ÆI ÆAKÇH0ÇH.ÇH0Ç+KÇ=IÇ>KÈH ÈHÈH ÈI ÈHÈH ÈH ÈH+È(I È*H+È+I È.H0È0I 	È;	KÉAKÊJ	ÊJÊJ	Ê	KÊJ,Ê'+KËKËKËK)Ú_LOOPBACK_HOSTSc                óF   € V ^8„  d   QhR\         R\        \        ,          /# ©r   r   r   ©r   r   r    )r!   s   "r   r"   r"   ‰   s   € ÷ +ñ +œ)ð +¬´­ñ +r   c                ó>   € ^ RI Hp ^ RIHp V! WP                  4      # )z›Return a rejection reason token for the peer IP, or None when allowed
(:func:`hermes_cli.web_server_boundary.ws_client_reason` on the dashboard app state).©Úapp)Úws_client_reason)Úhermes_cli.web_serverra   Úhermes_cli.web_server_boundaryrb   Ústate)r   ra   rb   s   &  r   Ú_ws_client_reasonrf   ‰   s   € õ *Ý?Ù˜B§	¡	Ó*Ð*r   c                ó0   € V ^8„  d   QhR\         R\        /# r]   ©r   Úbool)r!   s   "r   r"   r"   ‘   s   € ÷ )ñ )œið )¬Dñ )r   c                ó   € \        V 4      RJ # )z7True when the peer IP passes :func:`_ws_client_reason`.N)rf   ©r   s   &r   Ú_ws_client_is_allowedrl   ‘   s   € ä˜RÓ  DÐ(Ð(r   c                óF   € V ^8„  d   QhR\         R\        \        ,          /# r]   r^   )r!   s   "r   r"   r"   –   s   € ÷ 0ñ 0œyð 0¬X´c­]ñ 0r   c                ó>   € ^ RI Hp ^ RIHp V! WP                  4      # )u§   Return ``host_mismatch â€¦`` / ``origin_mismatch â€¦``, or None when allowed
(:func:`hermes_cli.web_server_boundary.ws_host_origin_reason` on the dashboard app state).r`   )Úws_host_origin_reason)rc   ra   rd   ro   re   )r   ra   ro   s   &  r   Ú_ws_host_origin_reasonrp   –   s   € õ *ÝDÙ  §Y¡YÓ/Ð/r   c                ó0   € V ^8„  d   QhR\         R\        /# r]   rh   )r!   s   "r   r"   r"   ž   s   € ÷ .ñ .¤9ð .´ñ .r   c                ó   € \        V 4      RJ # )z=True when the upgrade passes the dashboard Host/Origin guard.N)rp   rk   s   &r   Ú_ws_host_origin_is_allowedrs   ž   s   € ä! "Ó%¨Ð-Ð-r   c                ó0   € V ^8„  d   QhR\         R\        /# r]   rh   )r!   s   "r   r"   r"   £   s   € ÷ Hñ Hœyð H¬Tñ Hr   c                ó>   € \        V 4      ;'       d    \        V 4      # )zDReturn True when the WebSocket upgrade matches dashboard boundaries.)rs   rl   rk   s   &r   Ú_ws_request_is_allowedrv   £   s   € ä% bÓ)×GÐGÔ.CÀBÓ.GÐGr   )Ú_GATEWAY_WS_PROTOCOLÚ"_GATEWAY_WS_TICKET_PROTOCOL_PREFIXÚ#_gateway_ws_ticket_from_subprotocolc                ó0   € V ^8„  d   QhR\         R\        /# r]   )r   r   )r!   s   "r   r"   r"   ¯   s   € ÷ ñ œð ¤wñ r   c                óŠ  € ^ RI Hp T! RRR\        V P                  R\        4      ! 4        UUu. uF5  w  r#VP                  4       P                  R4      VP                  R4      3NK7  	  uppRV P                  '       d   V P                  P                  ^ 3MRR	RR
RRRRV P                  P                  RRRR/	4      # u uppi )u­  A ``Request`` facade over a ``WebSocket`` for the auth helpers.

``_verify_access_token`` only touches ``request.headers`` (X-Forwarded-For
via ``client_ip``) and ``request.client`` on this path (``audit=False``,
no cookie/redirect work), so a lightweight duck-typed stand-in avoids
constructing a real ASGI request inside the upgrade. ``scan_session_providers``
itself never sees the request â€” only the provider callbacks do.
)r   r@   ÚhttpÚheadersÚitemszlatin-1ÚclientNÚserverÚschemer   ÚmethodÚGETr   Úquery_stringr   Ú	root_pathÚ )Úfastapir   Úgetattrr}   ÚdictÚlowerrL   r   ÚhostÚurlr   )r   r   ÚkÚvs   &   r   Ú_ws_request_viewr�   ¯   s·   € õ  áØ�ØÜ '¨¯
©
°G¼TÔ BÔ DôFÙ D™˜ð —W‘W“Y×%Ñ% iÓ0°!·(±(¸9Ó2EÓFÙ DòFà¨¯¯¨�2—9‘9—>‘> 1Ñ%¸Ø�$Ø�$Ø�%Ø�—‘—‘Ø˜Ø�Rðó ð ùóFs   ¯;B?c                óh   € V ^8„  d   QhR\         R\        \        \        ,          \        3,          /# r]   )r   Útupler   r    )r!   s   "r   r"   r"   È   s,   € ÷ L%ñ L%œ	ð L%¤e¬H´S­M¼3Ð,>Õ&?ñ L%r   c           
     óœ  a aa€ ^ RI HpHp ^ RIHpHp VS P                  9   d#   V! S 4      pVf   R# RVR,          RR/S n        R # \        \        VP                  RR	4      4      pV'       Ed   ^ R
IHoHo ^ RIHpHpHp	 R VVV 3R llp
R V 3R llpS P$                  P'                  RR4      pV'       d    V! V! V4      4       R!# \)        S 4      w  rïVR8X  d   R## T;'       g    S P$                  P'                  RR4      pV'       dG    V	! V4      pVP'                  R4      R8X  d	   V! R4      hV! V4       V'       d   \*        S n        R$# R%# S P$                  P'                  RR4      pV'       Ed   ^ RIHp ^ RIHp  V! \9        S 4      VR	R7      pTeŽ   T! R\        TRR4      R\        TRR4      /4       S! SP:                  \        TRR4      \        TRR4      S P<                  '       d   S P<                  P>                  MRS P@                  PB                  R7       R(# S! SPD                  RS P<                  '       d   S P<                  P>                  MRS P@                  PB                  R7       R)# R*# S P$                  P'                  RR4      pV'       g   R*# \F        PH                  ! VPK                  4       VPK                  4       4      '       d   R(# R+#   T d   pT
! RT 24       R"u Rp?# Rp?ii ; i  T d   pT
! \/        T4      4       R&u Rp?# Rp?ii ; i  T d   pT
! RT 24       R'u Rp?# Rp?ii ; i),u9  Validate WS-upgrade auth; return ``(reason, credential)``.

``reason`` is None when accepted, else a short token (``no_credential``,
``token_mismatch``, ``ticket_invalid``, ``internal_invalid``);
``credential`` names what was presented so the accept path can log *how*.

Loopback / ``--insecure``: legacy ``?token=`` (constant-time compared).
Gated: ``?ticket=`` (browser-minted, single-use, 30s TTL) or ``?internal=``
(process-lifetime, multi-use, only for server-spawned WS clients so the PTY
child can reconnect; never injected into the SPA), or ``?token=`` holding a
user session access token (verified against the dashboard auth session
providers â€” the same ``verify_session`` seam the native bearer REST leg
uses â€” e.g. a token-mode Remote desktop connection). The legacy in-process
``_SESSION_TOKEN`` is rejected in gated mode: a leaked ``_SESSION_TOKEN``
must not grant access.

Either mode: a local Desktop (no static token) presents the private one-use
``X-Hermes-Gateway-Ticket`` grant its REST calls use (``authenticate_native_ws``).
A socket that sends that header is judged by it alone: on a core socket
(``/api/ws``, ``/api/pty``, the audio streams) the grant is refused
(``native_invalid``) even when a valid ``token`` query credential rides along.
That is intended; only the plugin WebSocket gate accepts the grant.
)ÚHEADERÚauthenticate_native_ws©Ú_SESSION_TOKENra   NÚuser_idÚsubjectÚproviderÚlocalÚauth_requiredF)Ú
AuditEventÚ	audit_log)ÚTicketInvalidÚconsume_internal_credentialÚconsume_ticketc                ó(   € V ^8„  d   QhR\         RR/# )r   r%   r   N)r    )r!   s   "r   r"   Ú%_ws_auth_reason.<locals>.__annotate__ð   s   € ÷ 	Lñ 	LœCð 	L Dñ 	Lr   c                 ó¬   <€ S! SP                   T SP                  '       d   SP                  P                  MR SP                  P                  R7       R# )r†   ©r%   Úipr   N)ÚWS_TICKET_REJECTEDr   r‹   rŒ   r   )r%   rœ   r�   r   s   &€€€r   Ú_rejectÚ _ws_auth_reason.<locals>._rejectð   s6   ø€ ÙØ×-Ñ-°fØ&(§i§i i�B—I‘I—N’N°R¸r¿v¹v¿{¹{÷Lr   c                ó   € V ^8„  d   QhRR/# r.   r   )r!   s   "r   r"   r¢   õ   s   € ÷ 		Dñ 		D Tñ 		Dr   c                 ó¸   <€ R V P                  R 4      RV P                  R4      /Sn        V P                  R4      '       d   V R,          SP                  R&   R# R# )r—   r™   ÚissuerN)rJ   Ú_hermes_auth_identity)Úinfor   s   &€r   Ú_stamp_identityÚ(_ws_auth_reason.<locals>._stamp_identityõ   sS   ø€ ð ˜4Ÿ8™8 IÓ.°
¸D¿H¹HÀZÓ<Pð(RˆBÔ$à�x‰x˜×!Ò!Ø59¸(µ^�×(Ñ(¨Ó2ñ "r   Úinternalr†   z
internal: ÚinvalidÚticketzbot-desktopz+display ticket presented as a gateway loginÚtoken)ÚProviderError)Ú_verify_access_token)Úaccess_tokenÚauditz"session token verify unavailable: )r™   r—   r¥   r   Úsession_token_invalidr¤   )Únative_invalidÚnative)Nrº   )Nr°   )Úinternal_invalidr°   )Úticket_invalidúticket-subprotocol)Nr½   )Nr²   )r¼   r²   )Útoken_unavailabler³   )Nr³   )Útoken_invalidr³   )Úno_credentialÚnone)Útoken_mismatchr³   )&Ú%hermes_cli.dashboard_auth.native_httpr“   r”   rc   r–   ra   r}   r¬   ri   rˆ   re   Úhermes_cli.dashboard_auth.auditrœ   r�   Ú$hermes_cli.dashboard_auth.ws_ticketsrž   rŸ   r    Úquery_paramsrJ   ry   rw   Ú_hermes_ws_subprotocolr    Úhermes_cli.dashboard_auth.baser´   Ú$hermes_cli.dashboard_auth.middlewarerµ   r�   ÚTOKEN_AUTH_SUCCESSr   r‹   rŒ   r   ÚTOKEN_AUTH_FAILUREÚhmacÚcompare_digestrL   )r   r“   r”   r–   ra   Úgrantr›   rž   rŸ   r    r§   r®   r°   ÚexcÚprotocol_ticketÚprotocol_reasonr²   r­   r³   r´   rµ   Úsessionrœ   r�   s   f                     @@r   Ú_ws_auth_reasonrÓ   È   s  ú€ ÷0 Uß9Ø�—‘ÔÙ& rÓ*ˆØŠ=Ø-Ð-Ø$-¨u°YÕ/?ÀÈWÐ#UˆÔ ØÐÜœ §¡¨O¸UÓCÓD€Mß€}÷ 	J÷	Hñ 	H÷	Lò 	L÷
		Dð 		Dð —?‘?×&Ñ& z°2Ó6ˆßð6ÙÑ ;¸HÓ EÔFØ'Ð'ô
 ,OÈrÓ+RÑ(ˆØ˜iÔ'Ø9Ð9Ø ×EÐE B§O¡O×$7Ñ$7¸À"Ó$Eˆßð2Ù% fÓ-�Ø—8‘8˜JÓ'¨=Ô8ñ (Ð(UÓVÐVÙ Ô%ß"ô 1E�BÔ-Ø5Ð5Ø%Ð%ð —‘×#Ñ# G¨RÓ0ˆßˆ5ÝDÝQð4Ù.Ü$ RÓ(°uÀEôK�ð Ò"ÙØœw w°	¸4Ó@Ø¤¨°¸TÓ Bð!ô ñ Ø×1Ñ1Ü$ W¨j¸$Ó?Ü# G¨Y¸Ó=Ø*,¯)¯)¨)˜Ÿ	™	Ÿš¸ØŸ™Ÿ™õð %Ð$ÙØ×-Ñ-Ø.Ø&(§i§i i�B—I‘I—N’N°RØ—V‘V—[‘[õ	ð ,Ð+à&Ð&à�O‰O×Ñ ¨Ó,€EßØ&Ð&Ü×Ò˜5Ÿ<™<›>¨>×+@Ñ+@Ó+B×CÒCØÐØ$Ð$øð_ !ô 6Ù˜* S EÐ*Ô+Ø5Õ5ûð6ûð. !ô 2Ùœ˜C›Ô!Ø1Õ1ûð2ûð, !ô 4ñ Ð<¸S¸EÐBÔCØ3Õ3ûð4úsf   Â;K% Ä5L ÅL ÅL ÆL- Ë%LË,K>Ë8LË>LÌL*ÌL%ÌL*Ì%L*Ì-MÌ4MÍ MÍMc                ó0   € V ^8„  d   QhR\         R\        /# r]   rh   )r!   s   "r   r"   r"   W  s   € ÷ *ñ *”Ið *¤$ñ *r   c                ó*   € \        V 4      ^ ,          RJ # )zETrue when the WS-upgrade credential is accepted. See _ws_auth_reason.N)rÓ   rk   s   &r   Ú_ws_auth_okrÖ   W  s   € ä˜2Ó˜qÕ! TÐ)Ð)r   c                ó<  € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\        \        \        ,          \         \        ,          \         \        ,          3,          /# ©r   ÚresumeÚsidecar_urlÚprofileÚactive_session_fileÚworkspace_cwdr   ©r   r    r‘   Úlistr‰   )r!   s   "r   r"   r"   \  sv   € ÷ Y6ñ Y6Ü”S�MðY6Ü/7¼­}ðY6ÜNVÔWZÍmðY6ä!¤#�ðY6ô œC•=ðY6ô -2´$´sµ)¼XÄc½]ÌHÔUYÍNÐ2ZÕ,[ñY6r   c                óº  € ^ RI HpHp ^ RIHpHp ^ RIHp	 ^ RIH	p
H
p RpT;'       g    RP                  4       pV'       d   VP                  4       R8w  d	   V! V4      pT! V	R,          R	R
7      w  rï^ RIHp V! R	RR7      pVe   \        V4      VR&    ^ RIHpHpHp Ve[   V! 4       P)                  R4      pV! V4       F  pVP+                  VR4       K  	  V! V4      ;_uu_ 4        V! VR7       RRR4       M
V! VR7        V'       d   VVR&   VVR&   V
! V4       VP3                  RR4       VP3                  RR4       VP3                  RR4       VP3                  RR4       RVR&   V '       d=   T! Ve   TMRRR7      p V! V V4      w  ppVP5                  4        V'       d   Tp V VR&   V'       d   VVR &   V'       d   VVR!&   Vf   \7        4       ;p'       d   VVR"&   \9        V4      V'       d   \        V4      V3# RV3#   + '       g   i     EL; i  \,         d    \.        P1                  RRR7        EL<i ; i  TP5                  4        i ; i)#aß  Resolve the argv + cwd + env for the chat PTY (what ``hermes --tui`` runs).

Tests monkeypatch this with a tiny fake command.  Env contract: resume goes
through ``HERMES_TUI_RESUME`` (``ui-tui`` does not parse argv), resolved to
the newest descendant; ``HERMES_TUI_GATEWAY_URL`` attaches to this process's
in-memory gateway but is SKIPPED for profile-scoped chats (that gateway runs
under the dashboard's own profile, so a scoped chat attaches to its profile's gateway);
``profile`` scopes the ENTIRE chat by pointing ``HERMES_HOME`` at the profile
dir, the same propagation ``hermes -p <name>`` performs. ``workspace_cwd``
(an already-validated host directory, ``chat_workspaces.resolve_chat_cwd``)
is the workspace the user picked for a FRESH chat: it becomes ``HERMES_CWD``
(where a self-spawned gateway starts) and ``HERMES_TUI_CWD`` (what the TUI
passes as the explicit ``cwd`` of ``session.create`` when attached to the
in-memory gateway, whose own cwd is the dashboard's launch dir).
)Ú_config_profile_scopeÚ_resolve_profile_dir)Ú_open_session_db_for_profileÚ_session_latest_descendant)ÚPROJECT_ROOT)Ú_apply_tui_python_envÚ_make_tui_argvNr†   Úcurrentzui-tuiF)Útui_dev)Úbuild_subprocess_envT)Úscrub_secretsÚinherit_profile_homeÚHERMES_HOME)Úapply_terminal_config_to_envÚread_raw_configÚterminal_config_owned_env_varsÚterminal)Úenvz9Failed to apply terminal config bridge for dashboard chat)Úexc_infoÚ
HERMES_CWDÚHERMES_TUI_CWDÚNODE_ENVÚ
productionÚHERMES_TUI_DISABLE_MOUSEÚ1ÚHERMES_TUI_INLINEÚ	COLORTERMÚ	truecolorÚHERMES_TUI_DASHBOARD)Ú	read_onlyÚHERMES_TUI_RESUMEÚHERMES_TUI_SIDECAR_URLÚHERMES_TUI_ACTIVE_SESSION_FILEÚHERMES_TUI_GATEWAY_URL)Úhermes_cli.web_server_profilesrá   râ   Úhermes_cli.web_server_sessionsrã   rä   Úhermes_cli.mainrå   Úhermes_cli.main_tui_launchræ   rç   ÚstriprŠ   Útools.environments.localrê   r    Úhermes_cli.configrî   rï   rð   rJ   Úpopr)   r&   r'   Ú
setdefaultr(   Ú_build_gateway_ws_urlrß   )rÙ   rÚ   rÛ   rÜ   rÝ   rá   râ   rã   rä   rå   ræ   rç   Úprofile_dirÚ	requestedÚargvÚcwdrê   rò   rî   rï   rð   Úraw_launch_terminalÚenv_varÚ
_resume_dbÚlatest_resumeÚ_latest_pathÚgateway_ws_urls   &&&&&                      r   Ú_resolve_chat_argvr  \  sJ  € ÷& [ßgÝ,ßPà"&€KØ—�˜B×%Ñ%Ó'€Iß�Y—_‘_Ó&¨)Ô3Ù*¨9Ó5ˆá˜|¨hÕ6ÀÔF�I€Dõ >Ù
¨UÈÔ
N€CØÒÜ  Ó-ˆˆMÑða÷	[ñ 	[ð Ò"ñ #2Ó"3×"7Ñ"7¸
Ó"CÐÙ9Ð:MÖN�Ø—‘˜ Ö&ñ Oá& y×1Õ1Ù,°Õ5÷ 2Ð1ñ )¨SÖ1÷ Ø)ˆˆLÑØ -ˆÐÑÙ˜#ÔØ‡N�N�:˜|Ô,ð ‡N�NÐ-¨sÔ3Ø‡N�NÐ&¨Ô,ð ‡N�N�; Ô,Ø"%€CÐÑçÙ1Ø$Ò0‰I°dÀdôLˆ
ð	Ù*DÀVÈZÓ*XÑ'ˆM˜<à×ÑÔßØ"ˆFØ#)ˆÐÑ çØ(3ˆÐ$Ñ%çØ0CˆÐ,Ñ-ð
 ÒÔ2GÓ2IÐ I Ö IØ(6ˆÐ$Ñ%ä�‹:§3”s˜3“x°#Ð5Ð5¨D°#Ð5Ð5÷W 2×1Ð1ûô ô aÜ�‰ÐPÐ[_ˆ×`ðaûð, ×ÑÕúsC   ÂAH Ã+
HÃ5H Ã>	H ÆI ÈH	ÈH ÈH È"IÉIÉIz0.0.0.0z::c                ó:   € V ^8„  d   QhR\         \        ,          /# ©r   r   ©r   r    )r!   s   "r   r"   r"   ½  s   € ÷ 
<ñ 
<¤¬#¥ñ 
<r   c                 óÞ   € ^ RI Hp  \        P                  P	                  RR4      P                  4       pV'       d   V# \        V P                  RR4      pV'       g   R# V\        9   d   R# T# )zŽHost the in-container WS client should dial: ``HERMES_DASHBOARD_WS_HOST``
wins always; a wildcard bind becomes ``127.0.0.1``; others verbatim.r`   ÚHERMES_DASHBOARD_WS_HOSTr†   Ú
bound_hostNz	127.0.0.1)	rc   ra   ÚosÚenvironrJ   r  rˆ   re   Ú_WILDCARD_HOSTS)ra   Úexplicitr‹   s      r   Ú_resolve_client_ws_hostr"  ½  sW   € õ *Ü�z‰z�~‰~Ð8¸"Ó=×CÑCÓE€HßØˆÜ�3—9‘9˜l¨DÓ1€DßÙØ¤/Ô1ˆ;Ð;°tÐ;r   c                óF   € V ^8„  d   QhR\         R\        \         ,          /# )r   r   r   ©r    r   )r!   s   "r   r"   r"   Ê  s"   € ÷ Qñ Q¤#ð Q´h¼sµmñ Qr   c                óŠ  € ^ RI HpHp \        4       p\	        VP
                  RR4      pV'       d	   V'       g   R# RV9   d    VP                  R4      '       g	   RV RV 2MV RV 2p\	        VP
                  RR4      '       d   ^ R	IHp R
V! 4       /pMRV/pRV V  R\        P                  P                  / VCVC4       2# )a;  ``ws://<host>:<port><path>?<auth>&<extra>`` for server-spawned WS clients,
or None when unbound.

Gated mode uses the process-lifetime internal credential, NOT a single-use
browser ticket: the child reads the URL once and reuses it on every
reconnect, and a 30s-TTL ticket can expire before a slow cold boot dials.
r•   Ú
bound_portNÚ:Ú[z]:r›   F)Úinternal_ws_credentialr°   r³   zws://Ú?)rc   r–   ra   r"  rˆ   re   Ú
startswithrÅ   r)  ÚurllibÚparseÚ	urlencode)	r   Úextra_qsr–   ra   r‹   ÚportÚnetlocr)  Úauths	   &,       r   Ú_server_internal_ws_urlr3  Ê  sÁ   € ÷ :Ü"Ó$€DÜ�3—9‘9˜l¨DÓ1€Dß—tÙØ#&¨$¤;°t·±Às×7KÒ7Kˆq���b˜˜ÑÐTXÐSYÐYZÐ[_ÐZ`ÐQa€FÜˆs�y‰y˜/¨5×1Ò1ÝOàÑ2Ó4Ð5‰à˜Ð(ˆØ�6�(˜4˜& ¤&§,¡,×"8Ñ"8Ð9M¸DÐ9MÀHÐ9MÓ"NÐ!OÐPÐPr   c                ó:   € V ^8„  d   QhR\         \        ,          /# r  r  )r!   s   "r   r"   r"   á  s   € ÷ .ñ .œx¬�}ñ .r   c                 ó   € \        R4      # )zAws:// URL the PTY child attaches to for JSON-RPC gateway traffic.z/api/ws©r3  r   r   r   r  r  á  s   € ä" 9Ó-Ð-r   c                óF   € V ^8„  d   QhR\         R\        \         ,          /# )r   Úchannelr   r$  )r!   s   "r   r"   r"   æ  s"   € ÷ @ñ @¤ð @¬´­ñ @r   c                ó   € \        RV R7      # )zBws:// URL the PTY child publishes events to, or None when unbound.z/api/pub©r8  r6  r:  s   &r   Ú_build_sidecar_urlr;  æ  s   € ä" :°wÔ?Ð?r   c                ó<  € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\        \        \        ,          \         \        ,          \         \        ,          3,          /# rØ   rÞ   )r!   s   "r   r"   r"   ë  sv   € ÷ Eñ EÜ”S�MðEÜ/7¼­}ðEÜNVÔWZÍmðEä!¤#�ðEô œC•=ðEô -2´$´sµ)¼XÄc½]ÌHÔUYÍNÐ2ZÕ,[ñEr   c              ƒ  ó,  "  € ^ RI HpHp RV RVRV/pVe   W7R&   Ve   WGR&   V! V4      ;_uu_4       GRj  x€L
  \        P                  ! \
        3/ VB G Rj  x€L
 uuRRR4      GRj  x€L
  #  L9 L L	  + GRj  x€L 
 '       g   i     R# ; i5i)z�Resolve chat argv off the event loop (it may run ``npm run build``); the
async lock keeps one-build-at-a-time without parking worker threads.)Ú_get_chat_argv_lockra   rÙ   rÚ   rÛ   NrÜ   rÝ   )rc   r>  ra   r6   r7   r  )rÙ   rÚ   rÛ   rÜ   rÝ   r>  ra   Úkwargss   &&&&&   r   Ú_resolve_chat_argv_asyncr@  ë  sz   é € ÷ ?Ø˜ ¨{¸IÀwÐO€FØÒ&Ø(;Ð$Ñ%ØÒ Ø"/ˆÑá" 3×'×'Õ'Ü×&Ò&Ô'9ÑD¸VÑD×D÷ (×'Ô'ÙD÷ (×'×'Ó'üs\   ‚5B·A1¸B»A7ÁA3ÁA7ÁBÁ+A5Á,BÁ3A7Á5BÁ7B	Á=B Á>
B	Â	B	Â	Bc                ó<   € V ^8„  d   QhR\         R\        R\        /# )r   ra   r8  r   )r   r    r   )r!   s   "r   r"   r"   ü  s!   € ÷ ñ ¬'ð ¼Cð ÄDñ r   c                óÎ   € ^ RI Hp V! V 4      pVP                  V4      f>   \        P                  ! RRR7      w  rE\
        P                  ! V4       \        V4      W1&   W1,          # )zHReturn the per-channel file where a dashboard TUI writes its active sid.)Ú_get_pty_active_session_fileszhermes-pty-active-z.json)ÚprefixÚsuffix)rc   rC  rJ   ÚtempfileÚmkstempr  r(   r   )ra   r8  rC  ÚfilesÚfdÚraw_paths   &&    r   Ú _active_session_file_for_channelrK  ü  sQ   € åCÙ)¨#Ó.€EØ‡y�y�ÓÒ!Ü×'Ò'Ð/CÈGÔT‰ˆÜ
�Š�ŒÜ˜h›ˆ‰Ø�>Ðr   c                óL   € V ^8„  d   QhR\         P                  P                  /# r  )Ú
concurrentÚfuturesÚThreadPoolExecutor)r!   s   "r   r"   r"     s   € ÷ ñ œz×1Ñ1×DÑDñ r   c                 ó  € \         fb   \        ;_uu_ 4        \         f=   \        P                  P	                  \
        RR7      s \        P                  ! R 4       RRR4       \         # \         #   + '       g   i     \         # ; i)zALazily create the bounded console worker pool (once per process).Nzhermes-console)Úmax_workersÚthread_name_prefixc                  óL   € \         ;'       d    \         P                  R RR7      # )FT)ÚwaitÚcancel_futures)Ú_console_executorÚshutdownr   r   r   Ú<lambda>Ú'_get_console_executor.<locals>.<lambda>  s*   € Ô-÷ Tð TÜ)×2Ñ2¸ÈdÐ2ÓSðTr   )rV  Ú_console_executor_lockrM  rN  rO  Ú_CONSOLE_EXECUTOR_MAX_WORKERSÚatexitÚregisterr   r   r   Ú_get_console_executorr^    ss   € ô Ò ß#Ö#Ü Ò(Ü$.×$6Ñ$6×$IÑ$IÜ =ÐRbð %Jó %dÐ!ô —’ñTôU÷ $ô ÐÔÐ÷ $Ö#ô Ðús   —AA0Á0B	c                ó~   € V ^8„  d   Qh/ ^ \         9   d)   \        \        P                  P                  ,          ;R&   # )r   rV  )Ú__conditional_annotations__r   rM  rN  rO  )r!   s   "r   r"   r"      s2   € ÷ ñ ÷R JÒ I”8œJ×.Ñ.×AÑAÕBÑ IòSr   i  i   )NNNNN)Jr`  r   Úloggingr6   r\  Úconcurrent.futuresrM  r4   rÌ   r  ÚreÚsysrF  Ú	threadingÚurllib.requestr,  Útypingr   r‡   r   r   r   r   Úpathlibr   Úhermes_cli.pty_sessionr   Ú	getLoggerr&   Úplatformr+  Úhermes_cli.win_pty_bridger
   r   r   Úhermes_cli.pty_bridgeÚ_PTY_BRIDGE_AVAILABLEÚImportErrorrI   ÚcompilerM   r3   r9   ÚPTY_REGISTRYr*   rZ   rd   r[   rf   rl   rp   rs   rv   rw   rx   ry   r�   rÓ   rÖ   r  Ú	frozensetr   r"  r3  r  r;  r@  rK  r[  rV  ÚLockrZ  r^  r"   )r`  s   @r   Ú<module>rt     sƒ  øðöó Û Û Û Û Û Û 	Û 	Û 
Û Û Û Ý ç DÓ DÝ Ý Ý 5ð ×ÒÐ0Ó1€ðDØ
‡|�|×Ñ˜u×%Ò%ß\Ð\çHØ Ðð �ZŠZÐ6Ó7€
ØÐ ð
 Ð Ù!Ø˜b¨_ÐKbôd€õõD.õN ;õ+õ)õ
0õ.õ
H÷
ñ õõ2L%õ^*÷
Y6ñ| ˜Y¨Ð-Ó.€õ
<õQõ..õ
@÷
Eõ"ð !"Ð ØEIÐ Ó IØ"ŸšÓ)Ð ÷øðS ô DØ€IØ!ÐôD˜lö Dð	Dús   Á0 E+ ÂE+ Å+FÆF