
    `gj{'                       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
 ddlmZmZmZ ddlmZ ddZddZdd	Zdd
ZddZddZdddddddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZdd dZd!dZddd"dZy)#u  Subprocess lifecycle manager for the google_meet bot.

Single active meeting at a time. Stores the running pid + out_dir in a
session-scoped state file under ``$HERMES_HOME/workspace/meetings/.active.json``
so tool calls across turns can find the bot, and ``on_session_end`` can clean
it up.

The bot runs as a detached subprocess — we don't hold file descriptors open,
so the parent agent loop can't block on it. We communicate via files only.
    )annotationsN)Path)AnyDictOptional)get_hermes_homec                 4    t        t                     dz  dz  S )N	workspacemeetings)r   r        V/root/.hermes/venv/lib/python3.12/site-packages/plugins/google_meet/process_manager.py_rootr   '   s    !"[0:==r   c                     t               dz  S )Nz.active.json)r   r   r   r   _active_filer   +   s    7^##r   c                     t               } | j                         sy 	 t        j                  | j	                  d            S # t
        $ r Y y w xY w)Nutf-8encoding)r   is_filejsonloads	read_text	Exception)ps    r   _read_activer   /   sF    A99;zz!++w+788 s   $A 	AAc                    t               }|j                  j                  dd       |j                  d      }|j	                  t        j                  | d      d       |j                  |       y )NTparentsexist_okz	.json.tmp   )indentr   r   )r   parentmkdirwith_suffix
write_textr   dumpsreplace)datar   tmps      r   _write_activer+   9   sQ    AHHNN4$N/
--
$CNN4::d1-N@KKNr   c                 T    	 t               j                          y # t        $ r Y y w xY wN)r   unlinkFileNotFoundErrorr   r   r   _clear_activer0   A   s'     s    	''c                    ddl m}  ||       S )Nr   )_pid_exists)gateway.statusr2   )pidr2   s     r   
_pid_aliver5   H   s     +sr   FzHermes Agent
transcribe)out_dirheaded
auth_state
guest_nameduration
session_idmoderealtime_modelrealtime_voicerealtime_instructionsrealtime_api_keyc          	        ddl m}m}  ||       sddt        |       z   dS t	               }|r0t        t        |j                  dd                  rt        d        ||       }|xs t               |z  }|j                  d	d	
       dD ])  }||z  }|j                         s	 |j                          + t        j                  j!                         }| |d<   t#        |      |d<   ||d<   |rd|d<   |r||d<   |r||d<   |r||d<   |r||d<   |	r|	|d<   |
r|
|d<   |r||d<   |dz  }t%        |dd      }	 t'        j(                  t*        j,                  ddgt&        j.                  |t&        j0                  |d	d	      }|j3                          |j4                  |t#        |      | t7        j6                         |t#        |      |d}t9        |       dd	i|S # t        $ r Y ]w xY w# |j3                          w xY w) u   Spawn the meet_bot subprocess for *url*.

    If a bot is already running for this hermes install, leave it first —
    we enforce single-active-meeting semantics.

    Returns a dict summarizing the started bot.
    r   )_is_safe_meet_url_meeting_id_from_urlFz?refusing: only https://meet.google.com/ URLs are allowed. got: )okerrorr4   zreplaced by new meet_joinreasonTr   )transcript.txtstatus.jsonHERMES_MEET_URLHERMES_MEET_OUT_DIRHERMES_MEET_GUEST_NAME1HERMES_MEET_HEADEDHERMES_MEET_AUTH_STATEHERMES_MEET_DURATIONHERMES_MEET_MODEHERMES_MEET_REALTIME_MODELHERMES_MEET_REALTIME_VOICE!HERMES_MEET_REALTIME_INSTRUCTIONSHERMES_MEET_REALTIME_KEYzbot.logab)	bufferingz-mzplugins.google_meet.meet_bot)stdinstdoutstderrenvstart_new_session	close_fds)r4   
meeting_idr7   url
started_atr<   log_pathr=   rE   )plugins.google_meet.meet_botrC   rD   reprr   r5   intgetstopr   r$   existsr.   OSErrorosenvironcopystropen
subprocessPopensys
executableDEVNULLSTDOUTcloser4   timer+   )r`   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rC   rD   existingr_   outnamefr\   rb   log_fhprocrecords                           r   startr~   T   s4   , US!s)$
 	
 ~HJs8<<q#9:;/0%c*J

+eg
*CIIdTI* 2 $J88:
	 **//
C C!$SC$.C !$' !(2$%&."# "&,:(),:()3H/0*:&'YH (DA.F^^T#AB$$$$"
 	 xx s8iik M	F &$!&!!o  T 	s   &G:A	G- 	G*)G*-G?c            	        t               } | sdddS t        | j                  dd            }|rt        |      nd}t	        | j                  dd            dz  }i }|j                         r&	 t        j                  |j                  d	
            }d||| j                  d      | j                  d      | j                  d      | j                  d      d|S # t        $ r Y Sw xY w)zFReturn the current meeting state, or ``{"ok": False, "reason": ...}``.Fno active meetingrE   rH   r4   r   r7    rJ   r   r   Tr_   r`   ra   )rE   aliver4   	meetingIdr`   	startedAtoutDir)
r   re   rf   r5   r   r   r   r   r   r   )activer4   r   status_path
bot_statuss        r   statusr      s    ^F':;;
fjj"
#C"JsOEvzz)R01MAK!#J	K$9$97$9$KLJ
 ZZ-zz% ZZ-**Y'	 	 	  		s   -%C 	C&%C&c                   t               }|sdddS t        |j                  dd            dz  }|j                         s d|j                  d      g d	t	        |      d
S |j                  dd      }|j                         D cg c]  }|j                         s| }}| r||  d n|}d|j                  d      |t        |      t	        |      d
S c c}w )zBRead the current transcript file. Returns ok=False if none exists.Fr   r   r7   r   rI   Tr_   r   )rE   r   linestotalpathr   r(   )r   errorsN)	r   r   rf   r   rm   r   
splitlinesstriplen)lastr   tptextln	all_linesr   s          r   
transcriptr      s    ^F':;;	fjjB'	(+;	;B::<L1G
 	
 <<<;D"oo/>288:>I>!%Itef9EZZ-YB  ?s   CCc                &   ddl }| xs dj                         } | sdddS t               }|sdddS |j                  d      d	k7  rdd
dS t	        |j                  dd            }|j                         sdd| dS |dz  }|j                         j                  dd | d}|j                  dd      5 }|j                  t        j                  |      dz          ddd       d|j                  d      |d   t        |      dS # 1 sw Y   +xY w)a.  Append a ``say`` request to the active bot's JSONL queue.

    Returns ``{"ok": False, "reason": ...}`` when no meeting is active or
    the active bot is in transcribe-only mode. Otherwise writes a line to
    ``<out_dir>/say_queue.jsonl`` that the bot's realtime speaker thread
    will consume.
    r   Nr   Fztext is requiredr   r   r=   realtimeua   active meeting is in transcribe mode — pass mode='realtime' to meet_join to enable agent speechr7   zout_dir missing: zsay_queue.jsonl   )idr   ar   r   
Tr_   r   )rE   r   enqueued_id
queue_path)uuidr   r   rf   r   is_diruuid4hexrn   writer   r'   rm   )r   r   r   r7   r   entryrz   s          r   enqueue_sayr      s'    JBD'9::^F':;;zz&Z'6
 	
 6::i,-G>>):7)'DEE,,J::<##CR($7E	w	/ *1	

5!D()* ZZ-T{*o	 * *s   5(DD	requestedrG   c                b   t               }|sdddS t        |j                  dd            }|j                  d      }|rt        |      dz  nd}|rt	        |      r	 t        j                  |t        j                         t        d	      D ]$  }t	        |      s nt        j                  d
       & t	        |      r%	 t        j                  |t        j                         t                d| |j                  d      |rt        |      dS ddS # t        $ r Y w xY w# t        $ r Y Iw xY w)zSignal the active bot to leave cleanly, then clear the active pointer.

    Sends SIGTERM and waits up to 10s for the bot to exit. Falls back to
    SIGKILL if the bot doesn't respond.
    Fr   r   r4   r   r7   rI   N   g      ?Tr_   )rE   rH   r   transcriptPath)r   re   rf   r   r5   rj   killsignalSIGTERMProcessLookupErrorrangerv   sleepSIGKILLr0   rm   )rH   r   r4   r7   transcript_path_s         r   rg   rg      s    ^F':;;
fjj"
#Cjj#G:Ad7m&66tO
z#	GGC( r 	Ac?JJsO	 c?V^^, OZZ-2A#o.	  HL	  " 		 & s$   $D  $D" 	DD"	D.-D.)returnr   )r   zOptional[Dict[str, Any]])r)   Dict[str, Any]r   None)r   r   )r4   re   r   bool)r`   rm   r7   zOptional[Path]r8   r   r9   Optional[str]r:   rm   r;   r   r<   r   r=   rm   r>   r   r?   r   r@   r   rA   r   r   r   )r   r   r-   )r   zOptional[int]r   r   )r   rm   r   r   )rH   rm   r   r   )__doc__
__future__r   r   rj   r   ro   rq   rv   pathlibr   typingr   r   r   hermes_constantsr   r   r   r   r+   r0   r5   r~   r   r   r   rg   r   r   r   <module>r      s  	 #  	   
   & & , >$ # $$" $$($(+/&*g"	g" g" 	g"
 g" g" g" g" g" "g" "g" )g" $g" g"T:6'T & #r   