
    BEjj                     6   S r SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
r
SSKrSSKJr  SSKJr  \" \5      R                   R#                  5       r\S-  r\S-  rSrSrS	rS
SSSSS/S.S.SSSSSS	S/ SQSS.S.SSSSSS/ SQS.S .S!S"S#SSS$S%S&/\" \5      S.S.S'S(SSS)S	SS*S+/ S,QS.S-.	S.S/SSS)S	SS0S+/ S1QS.S-.	S2S3SSS)S	SS4S+/ S5QS.S-.	S6S7S8SSS9S:/ S;QS<S=S>0S?.S.S@SASBSSSCSSD/\" \5      S.S./	r\R4                  " \R6                  SE\R8                  " \SFSG9\R:                  " \R<                  5      /SH9  \R>                  " SI5      r Sq!0 r"0 r#0 r$S$r%SXSJ jr&SK r'SL r(SM r)SN r*SO r+SYSP jr,SQ r-SR r.SS r/ST r0SU r1SV r2\3SW:X  a  \2" 5         gg)Zu  
正方元智能广告工作台 - 服务守护进程
功能：
1. 启动全部后端服务（proxy_server 最后启动）
2. 每 10 秒检测各服务端口健康状态
3. 发现崩溃自动重启
4. 日志记录到 guardian.log
5. 打开门户页面

用法：
    python service_guardian.py          # 启动守护 + 自动启动所有服务
    python service_guardian.py --watch  # 仅守护已运行的服务（不自动启动）
    N)datetime)Pathzguardian.logzguardian.pidz%http://192.168.5.250:9000/portal.html
   x   Ollamai,  z	/api/tagsTollamaserve)exeargs)nameport
check_pathrequiredstartupComfyUIi  z/system_statsGETz8C:\AI\ComfyUI_windows_portable\python_embeded\python.exe)z-szComfyUI\main.pyz--windows-standalone-buildz--listen0.0.0.0--port8188z--enable-cors-headerzC:\AI\ComfyUI_windows_portable)r
   r   cwd)r   r   r   check_methodr   cooldownr   IOPainti  /z%C:\Program Files\Python311\python.exe)z-miopaintstartz--modelcv2z--enable-remove-bgz--remove-bg-devicecudaz--devicer   r   8080z--hostr   )r   r   r   r   r   r      数字人v2i>"  z/api/digital-human/v2/status<   zEC:\Users\liuyu\.workbuddy\binaries\python\versions\3.13.12\python.exezdh_v2_startup.pyzDuix.Avatari   Fduix-avatarwsl)-dr"   --bashz(/mnt/d/AI-Ad-Agent/scripts/run-avatar.sh)	r   r   r   r   r   r   is_wsl
wsl_distror   zDuix.TTSiG  	duix-fish)r$   r)   r%   r&   z&/mnt/d/AI-Ad-Agent/scripts/run-fish.shzDuix.ASRio'  duix-asr)r$   r*   r%   r&   z%/mnt/d/AI-Ad-Agent/scripts/run-asr.shz
GPT-SoVITSi&  z/docs   z&C:\AI\gptsovits_env\Scripts\python.exe)z	api_v2.pyz-ar   z-p9880zD:\AI-Ad-Agent\GPT-SoVITSPYTORCH_CUDA_ALLOC_CONFzmax_split_size_mb:256)r
   r   r   	env_extra   代理服务器(#  z/api/health   zproxy_server.pyz'%(asctime)s [%(levelname)s] %(message)szutf-8)encoding)levelformathandlersGuardianc           
      >   SSK nSSKnSSKnSU  U 3n UR                  R	                  XRS9nUR                  R                  USS9 nUR                  S:  sSSS5        $ ! , (       d  f       g= f! UR                  R                   aG  nUR                  S:  n	U	(       d%  [        R                  SU  S	UR                   35        U	s SnA$ SnAfUR                  R                  [        [        [        [         4 a  n
 UR                  " UR"                  UR$                  5      nUR'                  S
5        UR)                  SU 45      nUR+                  5         US:X  a   Sn
A
g[        R                  SU  SU S35         Sn
A
g! [,         a?  n[        R                  SU  S[/        U5      R0                   SU 35         SnA Sn
A
gSnAff = fSn
A
f[,         a;  n
[        R                  SU  S[/        U
5      R0                   SU
 35         Sn
A
gSn
A
ff = f)u   检测端口是否响应 HTTP 请求（超时10秒，适应大模型加载场景）

特殊处理：
- HTTP 4xx (如 404): 服务在线（只是路径不对）→ 返回 True
- HTTP 5xx: 服务异常 → 返回 False  
- 连接错误: TCP 回退检测（兼容 WebSocket 服务如 ASR）
r   Nzhttp://127.0.0.1:methodr   timeouti  zcheck_port(z) FAIL: HTTP    	127.0.0.1Tz!) FAIL: TCP connect failed (code=)Fz) FAIL: z: )urllib.requesturllib.errorsocketrequestRequesturlopenstatuserror	HTTPErrorcodeloggerdebugURLErrorConnectionRefusedErrorConnectionResetErrorOSErrorTimeoutErrorAF_INETSOCK_STREAM
settimeout
connect_exclose	Exceptiontype__name__)r   r   r9   urllibrA   urlreqrespheokesockresulte2s                 )D:/AI-Ad-Agent/portal/service_guardian.py
check_portrc      s    dVJ<
0Cnn$$S$8^^##C#4;;$ 544<<!! WWs]LL;tfM"''CD	LL!!#9;O\# 	==1C1CDDOOA__k4%89FJJL{{4&0QRXQYYZ[\ 	LL;tfHT"X5F5F4Gr"NO5	  {4&a1A1A0B"QCHIsx   3A2 A!	A2 !
A/+A2 /A2 2H<CH2HA!F'F
G1GGGGH!1HHc                       SSK n SnU R                  R                  USS9nU R                  R                  USS9 nUR                  S:H  sSSS5        $ ! , (       d  f       g= f! [
         a     g	f = f)
u   Ollama 特殊检查r   Nzhttp://127.0.0.1:11434/api/tagsr   r8      r:      F)r?   rB   rC   rD   rE   rU   )rX   rY   rZ   r[   s       rb   check_ollamarg      sj    /nn$$S$7^^##C#3t;;#% 433 s.   :A& A	A& 
A#A& #A& &
A32A3c                    [         R                   " [         R                  [         R                  5       n UR                  S5        UR	                  SU 45         SSS5        g!    SSS5        g= f! , (       d  f       g= f)u   检测端口是否被占用   r=   NTF)rA   rP   rQ   rR   connect)r   ss     rb   is_port_in_userl      sa    	v~~v'9'9	:a	LLOII{D)*	 
;	:
	 
;	:	:	:s"   A3$A$$A0&A30A33
Bc           
         U S   nU S   n[        U5      (       a  [        R                  SU SU S35        gU S   nUS   nUR                  S	/ 5      nUR                  S
S5      nU R                  SS5      (       a  SSKnUR                  S5      nU(       d  [        R                  SU S    S35        gU R                  SS5      n	 [        R                  " / SQSSSS9n
U	(       a/  XR                  ;  a   [        R                  SU S    SU	 S35        gOU S   S:X  a  SSKnUR                  S5      nU(       d  [        R                  SU S    S35        g [        R                  " / SQSSSS9n
SU
R                  ;  a  [        R                  SU S    S35        g [        R                  R                  U5      (       d>  SSKnUR                  U5      nU(       a  UnO[        R                  SU S    SU 35        gU/U-   n[        R                  SU S    S S!R                  U5       35         [        R                   R#                  5       nU S   S":X  a  S#US$'   U R                  SS5      (       a  SnSn[$        R&                  S%:X  a`  [        R(                  S&-  n[        R*                  " 5       nU=R,                  [        R.                  -  sl        [        R0                  Ul        [        R4                  " UU[        R6                  [        R6                  UUUS'9nOY[        R4                  " UU[        R6                  [        R6                  U[$        R&                  S%:X  a  [        R(                  OSS(9nU[8        U S   '   [:        R:                  " 5       [<        U S   '   U$ ! [         a(  n[        R                  SU S    SU 35         SnAgSnAff = f! [         a)  n[        R                  SU S    SU 35         SnAGNqSnAff = f! [         a(  n[        R                  SU S    S)U 35         SnAgSnAff = f)*u   启动单个服务。

启动前会检查端口是否已被监听，避免重复启动。
如果端口已被占用（无论服务是否健康），都跳过启动，
交由健康检查循环中的 restart 逻辑处理。
r   r   [u	   ] 端口 u    已被占用，跳过启动skippedr   r
   r   r   Nr'   Fr   wsl.exeu"   ] wsl.exe 未找到，跳过启动r(    )rp   z--listz--quietTz	utf-16-ler<   )capture_outputr2   r;   u   ] WSL 发行版 'u   ' 未找到，跳过启动u   ] 无法检查 WSL 发行版: r    u6   ] wsl.exe 未找到，跳过启动（WSL2 未安装）r"   u6   ] WSL 发行版 'duix-avatar' 未找到，跳过启动u   ] 可执行文件不存在: u
   ] 启动:  r   zhttps://hf-mirror.comHF_ENDPOINTwin32i   )r   stdoutstderrenvcreationflagsstartupinfo)r   rv   rw   rx   ry   u   ] 启动失败: )rl   rI   infogetshutilwhichwarning
subprocessrunrv   rU   ospathexistsrF   joinenvironcopysysplatformCREATE_NO_WINDOWSTARTUPINFOdwFlagsSTARTF_USESHOWWINDOWSW_HIDEwShowWindowPopenDEVNULL	processestimeservice_start_times)svcr   r   r   r
   r   r   r}   wsl_exedistror`   r^   foundcmdrx   flagsrz   procs                     rb   start_servicer      s    v;Dv;D davYtf,HIJ)nG
%.C;;vr"D
++eT
"C
 wwx,,y)NNQs6{m+MNOr*
	^^0#k1F &53v;-/@Hbcd 
V	%,,y)NNQs6{m+abc		O^^0#k1F FMM13v;-/efg 2 77>>#S!CLL1S[M)EcUKL%$,C
KK!CK=
388C=/:;)jjoov;)#!8C 778U## EK||w&"33j@(446##z'F'FF#*4*<*<'##!))!))#'D ##!))!))=@\\W=Tj99Z[D "&	#f++/99;CK(S  	NNQs6{m+I!MN	$  	ONNQs6{m+I!MNN	Op  qV%5aS9:sK   	AN1 AO& 9E7P 1
O#;OO#&
P0PP
Q&Q		Qc                     [         R                  U 5      nU(       aA   UR                  5         UR                  SS9  [         U 	 [
        R                  SU  S35        gg!    UR	                  5          N7!     N<= f= f)u   停止服务进程r<   r:   rn   u   ] 已停止N)r   r|   	terminatewaitkillrI   r{   )r   r   s     rb   stop_servicer   v  sq    ==D	NNIIaI  dOav[)* 			s#   A A<"A44A96A<9A<c                    U S   n[         R                  US5      S-   [         U'   [         U   n[        R                  SU SU S35        [	        U5        [
        R                  " S5        [        R                  S:X  GaE  [
        R                  " S5        SS	K	n [        R                  " S
U S    3SSSSS9n[        5       nUR                  R                  5       R                  S5       H  nSU S    3U;   d  M  SUR!                  5       ;   d  M'  UR                  5       R                  5       n[#        U5      S:  d  MV  US   nUR%                  5       (       d  Mr  X;  d  My  [        R                  " SU 3SSSS9  UR'                  U5        [        R)                  SU SU 35        M     [
        R                  " S5        [+        U 5      n	U	(       a*  U	S:w  a$  S[         U'   [        R)                  SU S35        U	$ U	S:X  a$  [        R)                  SU S35        S[         U'   U	$ [        R-                  SU S35        U	$ !    N= f)u   重启服务r   r   ri   rn   u   ] 第 u    次重启...   ru   Nznetstat -ano | findstr :r   Tr   )shellrr   textr;   
:	LISTENINGr<   ztaskkill /F /PID )r   rr   r;   u%   ] 已终止占用端口的进程 PID=ro   u   ] 重启成功)   ] 端口已被占用，视为已在运行u   ] 重启失败！)crash_countsr|   rI   r   r   r   sleepr   r   rer   r   setrv   stripsplitupperlenisdigitaddr{   r   rF   )
r   r   countr   r`   killed_pidslinepartspidr   s
             rb   restart_servicer     s   v;D%))$2Q6LE
NNQtfF5'78 JJqM ||w

1	^^*3v;-84dBF %K++-33D9s6{m$,

1L JJL..0E5zQ#Bi;;==S-C&NN->se+DD9=qJ'OOC0"KK!D61VWZV[(\] : 	

1 D	!Tav^,- K 
	avFGHT K 	q/01K	s,   AI >I +I I I &A
I Ic           	      p	   [          Vs/ s H  oS   S:w  d  M  UPM     nn[        S [          5       S5      nU (       a  U Vs/ s H  oS   (       d  M  UPM     nnOU Vs/ s H  oPM     nn[        R                  S5        [        R                  S5        [        R                  S[        R
                  " 5       R                  S	5       35        [        R                  S
[        U5       S35        [        R                  S5        Sn/ nU GH(  nUS   nUS:X  a  [        5       n	O#UR                  SS5      n
[        US   US   U
S9n	U	(       a&  [        R                  SU SUS    S35        US-  nMj  [        R                  SU S35        [        U5      nUc  UR                  U5        M  US:X  a   [        R                  SU S35        US-  nM  UR                  SS5      (       a  SnO[        n[        R                  SU SU S35        Sn[        U5       H  n[        R                   " S5        US:X  a  [        5       nO#UR                  SS5      n
[        US   US   U
S9nU(       a&  [        R                  SU SUS-    S 35        US-  n  O1US-   S!-  S:X  d  M  [        R                  SU S"US-    S 35        M     U(       a  GM  UR                  SS#5      (       a0  [        R#                  SU S$U S 35        UR                  U5        GM  [        R                  SU S%35        GM+     U(       a  U (       a
  US   (       a  US   nUR                  SS5      n
[        US   US   U
S9(       a   [        R                  SU SUS    S35        O[        R                  SU S35        [        U5      nU(       a]  [        S!5       HN  n[        R                   " S5        [        US   S&5      (       d  M/  [        R                  SU SUS-    S 35          O   [        R                  S5        [        R                  S'U S([        U5       S)35        U(       a'  [        R#                  S*S+R%                  U5       35        [        R                  S5        XV4$ s  snf s  snf s  snf ),u   按依赖顺序启动所有服务

启动顺序：Ollama -> ComfyUI -> IOPaint -> GPT-SoVITS -> 数字人v2 -> Duix服务 -> 代理服务器
代理服务器必须最后启动，因为需要其他服务已经在线
r   r/   c              3   :   #    U  H  oS    S:X  d  M  Uv   M     g7f)r   r/   N ).0rk   s     rb   	<genexpr>%start_all_services.<locals>.<genexpr>  s     LAvY:K-Kaas   	Nr   z<============================================================u/     正方元智能广告工作台 - 服务启动u
     时间: z%Y-%m-%d %H:%M:%Su     核心服务: u    个r   r   r   r   r   r   r8   rn   u   ] 已在运行 (端口 r>   ri   u   ] 启动中...ro   r   r'   Fi,  u   ] 等待就绪 (最多 u   秒)...u   ] 已就绪 (u   秒)r   u   ] 仍在等待... (Tu   ] 启动超时 (u,   ] 未就绪（可选服务，继续执行）/portal.htmlu     启动完成: r   u    核心服务在线u     失败服务: z, )SERVICESnextrI   r{   r   nowstrftimer   rg   r|   rc   r   appendSTARTUP_TIMEOUTranger   r   r   r   )only_requiredrk   	non_proxy	proxy_svcto_startstarted_countfailed_servicesr   r   already_runningr9   r   wait_secondsreadyis                  rb   start_all_servicesr     s>    %GHq&	5F(FHIGLLdSI (:y!jMAy:()y!Ay)
KK
KKAB
KK*X\\^445HIJKL
KK"3x=/67
KKMO6{ 8*nOWW^U3F(Vc,6GPVWOKK!D6!8VQGHQM 	av^,-S!<""4(9KK!D6!JKLQM 778U##L*Lav4\N'JK|$AJJqMx$7"3v;L0A&Qav]1Q3%t<="A|q av%81TBC %  uwwz4((4&(8dKL&&t,av%QRSs x -9Z+@ ~u5i'<)@PKK!D6!869J8K1MNKK!D601 +DrAJJqM!)F"3^DDav]1Q3%t$DE	 # KK
KK"=/3x=/ATUV)$))O*D)EFG
KK))I H
 ;)s   R)R)R.R.!R3c                      / n [          HV  nUS   nUS:X  a  [        5       nO#UR                  SS5      n[        US   US   US9nU(       a  ME  U R	                  U5        MX     U $ )uB   对所有服务进行健康检查，返回不健康的服务列表r   r   r   r   r   r   r8   )r   rg   r|   rc   r   )	unhealthyr   r   healthyr9   s        rb   health_checkr   &  sm    I6{ 8"nGWW^U3F Vc,.?OGwS!      c                  L   [         R                  S5        [         R                  S[         S35        0 n   [        R                  " [        5        [        5       nU(       d  U R                  5         M?  U H  nUS   n[        R                  US5      nUR                  S[        5      nU[        R                  " 5       U-
  -
  nUS:  a  [         R                  SU S	US
 S35        Mt  U R                  US5      S-   X'   US:X  a  SOSnX   U:  d  M  [         R                  SU SX    S35        US   (       a  [        U5        SX'   M  [         R                  SU S35        M     GM8  ! [         a    e [         a!  n[         R                  SU 3SS9   SnAN3SnAff = f)u   主守护循环u3   守护进程已启动，开始监控服务状态...u   检查间隔: u    秒Tr   r   r   rn   u   ] 冷却中 (z.0fu   秒后开始健康检查)ri   r       r   u	   ] 连续 u    次无响应，正在重启...r   u   ] (可选服务) 已离线u   守护循环异常: )exc_infoN)rI   r{   CHECK_INTERVALr   r   r   clearr   r|   COOLDOWN_SECONDSr   r   KeyboardInterruptrU   rF   )	consecutive_failuresr   r   r   
start_timesvc_cooldowncooldown_remainingrestart_thresholdr^   s	            rb   guardian_loopr   ;  s   
KKEF
KK. 056
'	DJJ~&$I$**, 6{ 144T1=
"wwz3CD%1TYY[:5M%N"%)KK!D67I#6NNg hi-A-E-EdA-NQR-R$*
 *.)>AA!'-1BBNNQtfI6J6P5QQp#qr:',56,2av-G$HI1 ! J ! 	 	DLL/s3dLC	Ds%   ;E. 4BE. AE. .F#FF#c                      [         R                  S5        [        [        R	                  5       5       H  n [        U 5        M     [         R                  S5        g)u   退出时清理所有子进程u   正在停止所有服务...u   所有服务已停止N)rI   r{   listr   keysr   )r   s    rb   cleanupr   m  s:    
KK-.Y^^%&T '
KK'(r   c                  n    [        S5       Ha  n [        SSSS9(       a8  [        R                  " [        5        [
        R                  S[         35          g[        R                  " S5        Mc     [
        R                  S	5        g
! [         a"  n[
        R                  SU 35         SnAg
SnAff = f)u   打开门户页面   r0   r   r   r8   u   门户页面已打开: Tri   u-   代理服务器未就绪，无法打开门户Fu   打开门户失败: N)r   rc   
webbrowseropen
PORTAL_URLrI   r{   r   r   r   rU   rF   )r   r^   s     rb   open_portalr   u  s    rA$u=
+5j\BCJJqM  	FH +A3/0s   AB /B 
B4B//B4c                  X    [        [        [        5      S5      q[        R
                  S:X  a5  SSKn  U R                  [        R                  5       U R                  S5        O@SSKn UR!                  [        R                  5       UR"                  UR$                  -  5        [        R'                  S5        [        R)                  5         [        R+                  [        [,        R.                  " 5       5      5        [        R1                  5         S n[2        R4                  " U5        S n[6        R8                  " US	S
9nUR;                  5         g	! [         aB    [        R                  5         [        R                  S5        [        R                  " S5         Nf = f! [         aC    [        R                  5         [        R                  S5        [        R                  " S5         GNKf = f! [<         a    e [>         a"  n[        RA                  SU 35         SnAg	SnAff = f)u   获取 PID 文件锁，防止多个守护进程同时运行。

使用文件锁 (Windows: msvcrt, Linux: fcntl) 确保原子性，
避免竞态条件导致多个守护进程同时启动。
za+ru   r   Nri   u9   已有守护进程运行（文件锁被占用），退出c                  ~     [         (       a*  [        R                  SS9  [         R                  5         S q g g !    g = f)NT)
missing_ok)_lock_fdPID_FILEunlinkrT   r   r   rb   cleanup_lock&acquire_pid_lock.<locals>.cleanup_lock  s8    8OOtO4NN$#H s   48 <c                       [         R                  " S5         [        b  [        R                  (       a+  [        R                  S5        [        R                  " S5        M`  !    N= f)Nr1   u   PID 锁丢失，退出守护ri   )r   r   r   closedrI   rF   r   _exitr   r   rb   periodic_lock_check-acquire_pid_lock.<locals>.periodic_lock_check  sH    

2'8??%CD s   AA" "A&Ttargetdaemonu   PID 锁获取异常: )!r   strr   r   r   r   msvcrtlockingfilenoLK_NBLCKrN   rT   rI   rF   exitfcntlflockLOCK_EXLOCK_NBseektruncatewriter   getpidflushatexitregister	threadingThreadr   
SystemExitrU   r   )r  r  r   r   tr^   s         rb   acquire_pid_lockr    s   =Ht,<<7"x0&//1E HOO-u}}u}}/LM 	as299;'(	 	%		 $7E		a   XY	   XYP   .qc23sa   1G3 /E #G3 );F# $B/G3 A	F G3 F  G3 #A	G0,G3 /G00G3 3H)H$$H)c                     SS K n U R                  SS9nUR                  SSSS9  UR                  SSS	S9  UR                  S
SSS9  UR                  5       n[	        5         [
        R                  " [        5        [        R                  " [        R                  S 5        [        R                  " [        R                  S 5        UR                  (       a  [        R                  S5        OUR                  (       + n[        US9  UR                   (       d'  ["        R$                  " [&        SS9R)                  5          [+        5         g ! [,         a    [        R                  S5         g f = f)Nr   u3   正方元智能广告工作台 - 服务守护进程)descriptionz--watch
store_trueu0   仅守护已运行的服务（不自动启动）)actionhelpz--no-browseru   不自动打开浏览器z--allu*   启动所有服务（包括可选服务）c                 .    [         R                  " S5      $ Nr   r   r  sigframes     rb   <lambda>main.<locals>.<lambda>  s    CHHQKr   c                 .    [         R                  " S5      $ r  r  r  s     rb   r!  r"    s    SXXa[r   u'   守护模式：监控已运行的服务)r   Tr   u%   
收到中断信号，正在退出...)argparseArgumentParseradd_argument
parse_argsr  r  r  r   signalSIGINTSIGTERMwatchrI   r{   allr   
no_browserr  r  r   r   r   r   )r$  parserr   r   s       rb   mainr/    s   $$1f$gF
	,=op
|B\]
;ghD  OOG
MM&--!?@
MM&.."@Azz=> !HH7 ??D9??A> ><=>s   
E E.-E.__main__)r   r   )F)4__doc__r   r   r   jsonrA   r(  r  loggingr  r   r   r   pathlibr   __file__parentresolve
PORTAL_DIRLOG_FILEr   r   r   r   r   r   basicConfigINFOFileHandlerStreamHandlerrv   	getLoggerrI   r   r   r   r   r   rc   rg   rl   r   r   r   r   r   r   r   r   r  r/  rW   r   r   rb   <module>r?     s   
 
            (^""**,
&&4
 !I
	 %N- 5
  ;P
" 4['(z?
 #c
  !_
   ]
  <@/)+B	
" "#;&'z?
kBJ   
,,4Hw7cjj) 
		:	& 	  (V	sl+ /dk*\*/Dd)"DN >F zF r   