
    &<iWv              	          d 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ZddlmZm	Z	 ddl
mZ ddlmZ ddlZddlZdZeej"                  vrej"                  j%                  de       ddlmZmZ ddlmZ dd	lmZ dd
lmZ dZej"                  j;                  ed      Zej"                  j;                  ed      Zej"                  j;                  ed      Z ej"                  j;                  ed      Z!ej"                  j;                  ed      Z"ej"                  j;                  ed      Z#ej"                  j;                  ed      Z$ej"                  j;                  ed      Z%dZ&ej"                  j;                  ed      Z' ejP                  ejR                  d        ejT                  e+      Z,g dZ-dee.   fdZ/de.ddfdZ0de1de2fdZ3de2de4fd Z5d!e1d"e1de6e1   fd#Z7d$e1dee2   fd%Z8d$e1d&e2ddfd'Z9d(e2d$e1d&e2de4fd)Z:d:d*e.d+e.d,e2dz  ddfd-Z;d.e1d/e1de4fd0Z<de6e2   fd1Z=d;d2Z>d<d$e1d3e4ddfd4Z?d;d5Z@d;d6ZAde1ddfd7ZBd;d8ZCe+d9k(  r eC        yy)=u|  auto_orch.py — Phase 3 오케스트레이터 코어 메인 모듈.

CLI 진입점 + 핵심 함수들:
  - acquire_global_lock / release_global_lock : 중복 실행 방지 (fcntl.flock)
  - load_pipeline  : YAML 파이프라인 파일 로드
  - scan_events    : incoming/ 디렉터리에서 미처리 이벤트 수집
  - get_pipeline_state / save_pipeline_state : state/*.json 관리
  - dispatch_step  : dispatch.py 서브프로세스 호출 (shell=False)
  - update_health  : health.json 갱신
  - cmd_scan / cmd_run / cmd_status / cmd_list / cmd_validate : CLI 커맨드

작성자: 토르 (dev2-team backend)
날짜: 2026-03-24
    N)datetimetimezone)Path)Optionalz/home/jay/workspace)consume_eventscan_done_events)validate_pipeline)TeamLock)TokenLedgerz/tmp/auto-orch.lockzorchestrator/state	pipelineszorchestrator/health.jsonzorchestrator/incomingzorchestrator/processedzmemory/eventsz#orchestrator/state/alerts_sent.jsonzmemory/task-timers.jsoni   $orchestrator/state/token_ledger.jsonz1%(asctime)s [%(levelname)s] %(name)s: %(message)s)levelformat)acquire_global_lockrelease_global_lockload_pipelineis_pipeline_enabledscan_eventsget_pipeline_statesave_pipeline_statedispatch_stepupdate_healthsend_telegram_alertcheck_stale_taskscmd_scancmd_run
cmd_statuscmd_listcmd_validatereturnc                     	 t        j                  t        t         j                  t         j                  z        } t        j                  | t
        j                  t
        j                  z         t        j                  d| t               | S # t        $ rC t        j                  dt               	 t        j                          Y y# t        $ r Y Y yw xY wt        $ r }t        j                  d|       Y d}~yd}~ww xY w)u   GLOBAL_LOCK_PATH에 LOCK_EX|LOCK_NB flock을 획득한다.

    Returns:
        성공 시 파일 디스크립터(int), 이미 잠겨 있으면 None.
    u'   전역 락 획득 성공: fd=%d path=%su3   전역 락 획득 실패 (이미 잠겨 있음): %sNu#   전역 락 획득 중 OS 오류: %s)osopenGLOBAL_LOCK_PATHO_CREATO_RDWRfcntlflockLOCK_EXLOCK_NBloggerdebugBlockingIOErrorclose	ExceptionOSErrorerror)fdexcs     G/home/jay/workspace/.worktrees/task-2057-dev2/orchestrator/auto_orch.pyr   r   ^   s    WW%rzzBII'=>B56>DTU	 JL\]	HHRL   		  :C@s6   B
B #D 1C	CD CD  C;;D r2   c                     	 t        j                  | t         j                         t        j	                  d|        t        j                  |        y# t        j                  |        w xY w)u   fd에 대한 flock을 해제하고 파일 디스크립터를 닫는다.

    Args:
        fd: acquire_global_lock()이 반환한 파일 디스크립터.
    u   전역 락 해제 성공: fd=%dN)r'   r(   LOCK_UNr+   r,   r"   r.   )r2   s    r4   r   r   u   s?    B&6;
s   :A A)	yaml_pathc                     t         j                  d|        t        | dd      5 }t        j                  |      }ddd       t        t              st        dt        |             |S # 1 sw Y   2xY w)u%  YAML 파이프라인 파일을 로드하여 dict를 반환한다.

    Args:
        yaml_path: 파이프라인 YAML 파일의 절대/상대 경로.

    Returns:
        파이프라인 dict.

    Raises:
        yaml.YAMLError: YAML 구문 오류.
        OSError: 파일 읽기 오류.
    u   파이프라인 로드: %srutf-8encodingNu.   파이프라인 YAML이 dict가 아닙니다: )	r+   r,   r#   yaml	safe_load
isinstancedict
ValueErrortype)r7   fhdatas      r4   r   r      sj     LL-y9	iw	/ "2~~b!"dD!I$t*VWWK	" "s   A,,A5pipelinec                 8    t        | j                  dd            S )u   파이프라인 dict에서 enabled 여부를 반환한다.

    Args:
        pipeline: 파이프라인 dict (YAML 로드 결과).

    Returns:
        enabled 필드가 있으면 그 bool 값, 없으면 True (후방호환).
    enabledT)boolget)rE   s    r4   r   r      s     Y-..    incoming_dirprocessed_dirc                 r   t        |       }t        |      }|j                         st        j                  d|        g S t	               }|j                         r8|j                         D ch c]  }|j                         s|j                  ! }}g }|j                         D ]m  }|j                         s|j                  dk(  s$|j                  |vs3|j                  |j                         t        j                  d|j                         o t        j                  dt        |             |S c c}w )u  incoming_dir에서 미처리 .done 이벤트 파일 목록을 반환한다.

    이미 processed_dir에 존재하는 파일은 제외한다.

    Args:
        incoming_dir: 미처리 이벤트 파일 디렉터리.
        processed_dir: 처리 완료된 이벤트 파일 디렉터리.

    Returns:
        미처리 .done 파일명 리스트 (순수 파일명, 경로 제외).
    u)   incoming 디렉터리가 없습니다: %sz.doneu   미처리 이벤트 발견: %su    scan_events 결과: %d개 파일)r   existsr+   warningsetiterdiris_filenamesuffixappendr,   len)rK   rL   incoming_pathprocessed_pathprocessed_filesfresultss          r4   r   r      s     &M-(N!BLQ	 #O+9+A+A+CSaqyy{166SSG""$ G99;188w.vv_,qvv&=qvvF	G LL3S\BN Ts   +D4D4pipeline_idc                    t         j                  j                  t        |  d      }t         j                  j	                  |      st
        j                  d|       y	 t        |dd      5 }|j                         }ddd       j                         st
        j                  d|       yt        j                  |      }t        |t              st
        j                  d|       y|S # 1 sw Y   nxY w# t        j                  t         f$ r!}t
        j                  d	||       Y d}~yd}~wt"        $ r!}t
        j%                  d
||       Y d}~yd}~ww xY w)u   STATE_DIR/<pipeline_id>.json을 로드한다.

    Args:
        pipeline_id: 파이프라인 식별자.

    Returns:
        상태 dict, 파일 없음 또는 파싱 실패 시 None.
    .jsonu(   파이프라인 상태 파일 없음: %sNr9   r:   r;   u2   파이프라인 상태 파일이 비어 있음: %su,   파이프라인 상태가 dict가 아님: %su2   파이프라인 상태 JSON 파싱 실패 (%s): %su4   파이프라인 상태 파일 읽기 오류 (%s): %s)r"   pathjoin	STATE_DIRrN   r+   r,   r#   readstriprO   jsonloadsr?   r@   JSONDecodeErrorrA   r0   r1   )r\   
state_filerC   contentrD   r3   s         r4   r   r      s    iK=)>?J77>>*%?L*cG4 	 ggiG	 }}NNOQ[\zz'"$%NNI:V	  	    *- KZY\] KZY\]sH   C7 -C+>.C7 -;C7 )C7 +C40C7 7ED,,E8EEstatec                 ,   t        j                  t        d       t         j                  j	                  t        |  d      }t        |dd      5 }t        j                  ||dd	       d
d
d
       t        j                  d|       y
# 1 sw Y    xY w)u   STATE_DIR/<pipeline_id>.json에 상태를 저장한다.

    Args:
        pipeline_id: 파이프라인 식별자.
        state: 저장할 상태 dict.
    T)exist_okr^   wr:   r;   F   ensure_asciiindentNu!   파이프라인 상태 저장: %s)
r"   makedirsra   r_   r`   r#   rd   dumpr+   r,   )r\   ri   rg   rC   s       r4   r   r      sq     KK	D)iK=)>?J	j#	0 ;B		%%:;
LL4jA; ;s   B

Bstepc           	         | j                  dd| j                  dd       d      }| j                  dd      }dt        j                  j                  t        d	      d
|d|g}t
        j                  d|| j                  d      ||       	 t        j                  |ddd      }|j                  dk(  r't
        j                  d|| j                  d             yt
        j                  d|| j                  d      |j                  |j                  r|j                  dd        yd       y# t        $ r }t
        j                  d|       Y d}~yd}~ww xY w)uS  dispatch.py를 서브프로세스로 호출하여 스텝을 실행한다.

    Args:
        step: 실행할 스텝 dict (id, target_team, task_file_template 등).
        pipeline_id: 파이프라인 식별자.
        state: 현재 파이프라인 상태 dict.

    Returns:
        성공(returncode==0) 시 True, 실패 시 False.
    task_file_templatezpipelines/templates/idunknownz.mdtarget_teamzunknown-teamz/usr/bin/python3zdispatch.pyz--teamz--task-fileu>   dispatch_step 호출: pipeline_id=%s step_id=%s team=%s cmd=%sFT)shellcapture_outputtextr   u$   dispatch 성공: pipeline=%s step=%su<   dispatch 실패: pipeline=%s step=%s returncode=%d stderr=%sN    u   dispatch 실행 오류: %s)rI   r"   r_   r`   WORKSPACE_ROOTr+   r,   
subprocessrun
returncodeinforO   stderrr0   r1   )rs   r\   ri   	task_filerx   cmdresultr3   s           r4   r   r     sH    -1EdhhtU^F_E``c/deI((=.9K 	
^]3C LLH	
 !KK>TXXVZ^\NNN!!'-}}ds#  ;=  137s&   AD. AD. 'D. .	E7EEactive_pipelineserrorstoken_usagec                    t        j                  t        j                        j	                  d      j                         | ||dndd}|||d<   t        t              }|j                  j                  dd       |j                  t        j                  |d	d
      d       t        j                  d| ||       y)u   HEALTH_PATH의 health.json을 갱신한다.

    Args:
        active_pipelines: 현재 활성 파이프라인 수.
        errors: 최근 1시간 내 에러 수.
        token_usage: 토큰 사용량 dict (옵션). 있으면 version "1.1"로 기록.
    Ntzinfoz1.1z1.0)	last_tickr   errors_last_hourversionr   Tparentsrk   Frm   rn   r:   r;   u@   health.json 갱신: active_pipelines=%d errors=%d token_usage=%s)r   nowr   utcreplace	isoformatr   HEALTH_PATHparentmkdir
write_textrd   dumpsr+   r,   )r   r   r   health_datahealth_files        r4   r   r   G  s     \\(,,/77t7DNNP,"'35	K %0M"{#KTD94::kaP[bc
LLJ	rJ   message
alert_typec                    t        t              }	 |j                         r&t        j                  |j                  d            }ni }t        j                  t        j                        j                         j                         }|j                  |g       }||v rt        j!                  d||       yt"        j$                  j                  d      }|st        j'                  d       yt"        j$                  j                  d      }|st        j'                  d       y	 t)        j*                  d	| d
|| dd      }|j,                  s/t        j'                  d|j.                  |j0                  dd        y	 |j7                  |       |||<   	 |j8                  j;                  dd       |j=                  t        j>                  |dd      d       t        jA                  d||       y# t        j                  t        f$ r i }Y w xY w# t(        j2                  $ r }	t        j5                  d|	       Y d}	~	yd}	~	ww xY w# t        $ r }	t        j'                  d|	       Y d}	~	d}	~	ww xY w)u  Telegram Direct API로 경고 발송 (토큰 0, 중복 방지).

    Args:
        message: 발송할 메시지 텍스트
        alert_type: 경고 유형 (예: "token_warning", "stale_task")

    Returns:
        True: 발송 성공
        False: 이미 발송됨 또는 실패
    r:   r;   u.   경고 이미 발송됨 (중복 방지): %s/%sFANU_BOT_TOKENuQ   ANU_BOT_TOKEN 환경변수가 설정되지 않음. Telegram 경고 발송 불가.AUTO_ORCH_CHAT_IDuU   AUTO_ORCH_CHAT_ID 환경변수가 설정되지 않음. Telegram 경고 발송 불가.zhttps://api.telegram.org/botz/sendMessage)chat_idr{   
   )rd   timeoutu0   Telegram 경고 발송 실패: status=%d body=%sNr|   u%   Telegram 경고 발송 중 오류: %sTr   rm   rn   u"   alerts_sent.json 저장 실패: %su$   Telegram 경고 발송 성공: %s/%s)!r   ALERTS_SENT_PATHrN   rd   re   	read_textrf   r0   r   r   r   r   dater   rI   r+   r,   r"   environrO   requestspostokstatus_coder{   RequestExceptionr1   rU   r   r   r   r   r   )
r   r   alerts_pathalerts_data	today_keytoday_alertstokenr   respr3   s
             r4   r   r   g  s)    '(K $

;+@+@'+@+R SKK
 X\\*//1;;=I$B7L \!EyR\] JJNN?+Ejk jjnn01Gno}}*5'>$g6

 wwNNMtO_O_aeajajkoloapq  
#)K	B   =tzz+ERST_fg KK6	:Na   '* D $$ <cB  B;SAABsC   8G5 %AH AI 5HHI
*II
	I6I11I6c                     t        t              } 	 | j                         sg S | j                  d      }t	        j
                  |      }t        |t              r
d|v r|d   }nt        |t              r|}ni }g }t        j                  t        j                        }|j                         D ]  \  }}t        |t              s|j                  d      dk7  r,|j                  d      }|s@	 t        j                   |      }	|	j"                   |	j%                  t        j                        }	||	z
  j'                         }
|
t0        kD  s|j                  d	d
      }|j3                  |||
d       t,        j/                  d|||
        |S # t        j                  t        f$ r g cY S w xY w# t(        t*        f$ r t,        j/                  d||       Y ,w xY w)u   task-timers.json에서 STALE_TASK_RUNNING_SECONDS 초과 running 태스크 목록 반환.

    Returns:
        [{"task_id": str, "team_id": str, "duration_seconds": float}, ...]
    r:   r;   tasksstatusrunning
start_timer   u2   start_time 파싱 실패: task_id=%s start_time=%steam_idrw   )task_idr   duration_secondsu@   스텝 타임아웃 감지: task_id=%s team_id=%s duration=%.1fs)r   TASK_TIMERS_PATHrN   r   rd   re   rf   r0   r?   r@   r   r   r   r   itemsrI   fromisoformatr   r   total_secondsrA   	TypeErrorr+   r,   STALE_TASK_RUNNING_SECONDSrU   )timers_pathrawtimers_datar   staler   r   	task_infostart_time_strr   r   r   s               r4   r   r     s    '(K!!#I##W#5 JJsO
 +t$K)?!'*	K	&E
,,x||
$C $kkm  A)T* =="i/"|4		!//?J  ('//x||/D
 #j 0??A
 88mmIy9GLL&&(8 LL[]dfmo  AA AD La   '* 	B I& 	LLMwXfg	s)   F 'F /AF1F.-F.1&GGc            	         t               } | t        j                  d       yd}d}	 t        t        t
        t               t        t
        t              }|D ]F  }t        t
        t        |      }|rt        j                  d|       1t        j                  d|       H t        t              }|j                         s|j                  dd       |j                  d      D ]V  }|j                   }t#        |      }	|	|	j%                  d	d
      }
|
dvr5|dz  }t&        j(                  j+                  t,        | d      }t&        j(                  j                  |      st        j                  d|       	 t/        |      }t5        |      st        j                  d|       |j%                  dg       }|	j%                  d      }|D ]j  }|j%                  d      |k7  r|j%                  dd
      }t7        j8                  |      rt;        |||	      }|rO|dz  }Ut        j                  d|       l Y t=        t>              }|jA                         }tC        |||       |jE                         r-|d   }|d   }|d   }d|dd|dd|dd| d	}tG        |d        tI               }|D ]0  }|d!   }|d"   }|d#   }d$| d%| d&|d'z  d(d)}tG        |d*|        2 	 tK        |        y# t0        $ r'}t        j3                  d||       |dz  }Y d}~;d}~ww xY w# t0        $ r2}t        j3                  d+|       |dz  }tC        ||,       Y d}~ud}~ww xY w# tK        |        w xY w)-ui   --scan: 전역 락 획득 → 이벤트 소비 → 파이프라인 스텝 디스패치 → health 갱신.NuP   다른 auto_orch 프로세스가 실행 중입니다. 스캔을 건너뜁니다.r   u   이벤트 소비 완료: %su3   이벤트 소비 실패 또는 이미 처리됨: %sTr   *.jsonr   r}   )r   pending   .yamlu   파이프라인 YAML 없음: %su+   파이프라인 YAML 로드 실패 (%s): %su2   파이프라인 비활성화 상태 — 스킵: %sstepscurrent_steprv   rx   u3   팀 락 보유 중 — 디스패치 대기: team=%s)r   r   r   todaylimit	remainingu2   ⚠️ 일일 토큰 사용량 80% 도달
사용: , / u	   
잔여: u     토큰
활성 파이프라인:    개token_warningr   r   r   u-   ⚠️ 스텝 타임아웃 감지
태스크: u   
팀: u	   
경과: i  z.1fu   시간stale_task_u%   cmd_scan 실행 중 예외 발생: %s)r   r   )&r   r+   rO   r   
EVENTS_DIRINCOMING_DIRPROCESSED_DIRr   r   r   r,   r   ra   rN   r   globstemr   rI   r"   r_   r`   PIPELINES_DIRr   r/   r1   r   r
   is_team_availabler   r   LEDGER_PATHget_daily_usage_summaryr   check_warning_thresholdr   r   r   )r2   errors_countactive_count
new_events
event_fileconsumed
state_pathrg   r\   ri   r   pipeline_yamlrE   r3   r   current_step_idrs   rx   successledgerusage_summaryr   r   r   msgstale_tasksr   r   r   r   s                                 r4   r   r     s   		B	zijLLe \=A !}=
$ 	`J$\=*MH9:FRT^_	` )_
  "TD9$//(3 *	eJ$//K&{3E}YYx,F33AL GGLL;-u8MNM77>>-0>N(7 'x0QS^_ (Wb 9E#ii7O 	e88D>_4"hh}b9--k:+D+uEG"$)LL!VXcd	eC*	eZ [)668 	|LVcd ))+!'*E!'*E%k2I )3uQi 0$Q- (++7.=   _5 ()  
	>EI&GI&G$%78%Y 'y !+d237v?   {7)%<=
	>" 	B  JM[^_!t  J<cB|LIIJ
 	Bsb   EL& ;K3BL& C
L& &M$ 3	L#<LL& L##L& &	M!/(MM$ M!!M$ $M1dry_runc                    t         j                  j                  t        |  d      }t        j                  d|        	 t        |      }t        |      }|r!t        d       |D ]  }t        d|         y|j                  dd	      }t         j                  j                  t        d
      }t        |      }	|	j                  | |      st        d|         y|j                  dg       }
|rt        d|  d       t        d       t        d|d       t        dt        |
       d       t                t!        |
d      D ]  \  }}|j                  dd      }|j                  d|      }|j                  dd      }|j                  dg       }|j                  dd      }t        d| d|        t        d|        t        d |        t        d!|        t        d"|         t                t        d#       y|
r|
d	   j                  d      nd}| d$|t#        j$                  t&        j(                        j+                  d%      j-                         g d&}t/        | |       t        j                  d'| |       |
r5|
d	   }t1        || |      }|rt        d(|  d)|        yt        d*|        yt        d+|  d,       y# t        $ r t        d|        Y yt        $ r}t        d|        Y d}~yd}~ww xY w)-u   --run: 파이프라인 YAML 로드 → 검증 → 토큰 확인 → 첫 스텝 디스패치.

    Args:
        pipeline_id: 실행할 파이프라인 식별자.
        dry_run: True이면 실제 디스패치 없이 시뮬레이션만 수행.
    r   u!   파이프라인 실행 시작: %su:   ERROR: 파이프라인 파일을 찾을 수 없습니다: Nu&   ERROR: 파이프라인 로드 실패: u%   ERROR: 파이프라인 검증 실패:z  - token_budgetr   r   uL   ERROR: 토큰 예산 초과 또는 동시 실행 한도 초과. pipeline_id=r   u   === DRY-RUN: 파이프라인 'z' ===u   검증: 통과u   토큰 예산: r   u   스텝 수: r   r   rv   z	<unknown>rS   rx   rw   
depends_onru   zN/A  [z] z      name: z      team: z      depends_on: z      task_file: uD   DRY-RUN 완료: 실제 디스패치는 수행되지 않았습니다.r   r   )r\   r   r   
started_at
steps_doneu9   초기 상태 저장 완료: pipeline_id=%s first_step=%su   파이프라인 'u   ' 시작 완료. 첫 스텝: u)   WARNING: 첫 스텝 디스패치 실패: u   WARNING: 파이프라인 'u   '에 스텝이 없습니다.)r"   r_   r`   r   r+   r   r   FileNotFoundErrorprintr/   r	   rI   r~   r   	can_spendrV   	enumerater   r   r   r   r   r   r   r   )r\   r   r   rE   r3   validation_errorserrr   ledger_pathr   r   irs   sidrS   teamdepsr   first_step_idinitial_state
first_stepr   s                         r4   r   r   h  s	    GGLL;-u0EFM
KK3[A / *(357$ 	 CD,	  <<2L'',,~/UVK%FK6\]h\ijk Wb1E .{m5AB Q/01SZL,-c
 * 
	3GAt((4-C88FC(D88M95D88L"-D!5u=ICs"SE"#L'(L'(&tf-.%i[12
	3 	c
TV +0E!HLL&TM #%ll8<<0888EOOQM ]3
KKK[Zgh 1X

KG%k]2OP]_`=m_MN*;-7STUK  J=/Z[ 6se<=s   K L+L3LLc                     t        t              } | j                         st        d       yt	        | j                  d            }|st        d       yt        dt        |       d       t        |      D ]x  }|j                  }t        |      }|t        d| d       ,|j                  dd	      }|j                  d
d      }|j                  dd      }t        d| d| d| d|        z t        t              }|j                         }	t        d       t        d       t        d|	d   dd|	d   dd|	d    d       t        |j                  dz        }
t        d|
dd       y)uV   --status: state/*.json 전체를 읽고 파이프라인 상태 요약을 출력한다.u.   실행 중인 파이프라인이 없습니다.Nr   u   === 파이프라인 상태 (   개) ===r   u   ] (상태 파일 손상)r   rw   r   -r   z	] status=z current_step=z started_at=r}   u   === 토큰 사용량 ===u   오늘: r   r   r   r   z (
percentagez%)g?u   경고 임계값: z (80%))r   ra   rN   r   listr   rV   sortedr   r   rI   r   r   r   intDAILY_HARD_LIMIT)r   state_filesrg   r\   ri   r   r   r   r   summarywarning_limits              r4   r   r     s{   iJ>?zx01K>?	([)9(:(
CD[) 	h
 oo";/=C}$<=>8Y/yy5YY|S1
K=	&~\ZdYefg	h %F,,.G	"I	
$%	HWW%a(GG,<Q+?r',BWAXXZ
[\//#56M	}Q/v
67rJ   c            	         t        t              } | j                         st        d       yt	        | j                  d            }|st        d       yt        dt        |       d       t        |      D ][  }|j                  }	 t        t        |            }|j                  d|      }|j                  dd	      }t        d
| d| d|        ] y# t        $ r}t        d
| d| d       Y d}~d}~ww xY w)uT   --list: PIPELINES_DIR/*.yaml을 스캔하여 파이프라인 목록을 출력한다.u'   pipelines 디렉터리가 없습니다.Nz*.yamlu*   등록된 파이프라인이 없습니다.u   === 파이프라인 목록 (r  rS   schema_version?z  z | name=z | schema_version=u    | (로드 실패: ))r   r   rN   r   r  r   rV   r  r   r   strrI   r/   )pipelines_path
yaml_files	yaml_filer\   rE   rS   r  r3   s           r4   r   r     s    -(N  "78n))(34J:;	(Z(9
BCJ' ?	nn	?$S^4H<<4D%\\*:C@NB{m8D61CNCSTU?  	?B{m#6se1=>>	?s   AC	C;C66C;c                     	 t        |       }t        |      }|r.t        dt	        |       d       |D ]  }t        d|         yt        d       y# t        $ r}t        d|        Y d}~yd}~ww xY w)u   --validate: YAML 파일을 로드하고 파이프라인 스키마를 검증한다.

    Args:
        yaml_path: 검증할 YAML 파일 경로.
    u   ERROR: YAML 로드 실패: Nz	Invalid: u   개 오류 발견z	  ERROR: u$   Valid: 파이프라인 검증 통과)r   r/   r   r	   rV   )r7   rE   r3   r   r   s        r4   r   r     s     + x(F	#f+&789 	%CIcU#$	% 	45  +C512s   A 	A5A00A5c                     t        j                  dd      } | j                  d      }|j                  ddd	       |j                  d
dd       |j                  ddd	       |j                  ddd	       |j                  ddd       | j                  dddd       | j	                         }|j
                  rt                y|j                  r"t        |j                  |j                         y|j                  rt                y|j                  rt                y|j                  rt        |j                         yy)u   argparse 기반 CLI 진입점.zauto_orch.pyu:   Auto Orchestrator — Phase 3 오케스트레이터 코어)progdescriptionT)requiredz--scan
store_trueuU   이벤트 스캔 및 대기 스텝 디스패치 (30초 주기 타이머에서 호출))actionhelpz--runPIPELINE_IDu*   지정한 파이프라인을 수동 실행)metavarr  z--statusu2   현재 실행 중인 파이프라인 상태 출력z--listu'   등록된 파이프라인 목록 출력z
--validate	YAML_PATHu"   파이프라인 YAML 파일 검증z	--dry-runFuV   파이프라인을 검증하고 시뮬레이션만 수행 (실제 디스패치 없음))r  defaultr  )r   N)argparseArgumentParseradd_mutually_exclusive_groupadd_argument
parse_argsscanr   r   r   r   r   r   r  r   validater   )parsergroupargss      r4   mainr,    sG   $$PF
 ///>E	d  
 
9  
 
A  
 
6  
 
1   e	   Dyy
	$,,/		
	T]]# 
rJ   __main__)N)r    N)F)D__doc__r"  r'   rd   loggingr"   r   sysr   r   pathlibr   typingr   r   r=   r~   r_   insertorchestrator.event_busr   r   orchestrator.pipeline_validatorr	   orchestrator.team_lockr
   orchestrator.token_ledgerr   r$   r`   ra   r   r   r   r   r   r   r   r   r   basicConfigDEBUG	getLogger__name__r+   __all__r	  r   r   r  r@   r   rH   r   r  r   r   r   r   r   r   r   r   r   r   r   r   r,   rJ   r4   <module>r>     s       	  
 '    
 '!HHOOA~& B = + 1
 ) GGLL)=>	^[9ggll>+EFww||N,CD^-EFWW\\./:
77<<0UV 77<<0IJ ! ggll>+QR
   
--> 
		8	$
2Xc] .
C 
D 
$S T 4	/$ 	/4 	/"c # $s) NC HTN @BS B B$ B&3 33 3t 3 3vC  4$; Z^ @B B# B$ BT=4: =Jo dRV RVt RV RVj8D?06C 6D 684$n zF rJ   