
    i5*                         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Z G d d      Zdej                  fd	Zd
 Zedk(  r e        yy)u   
Pending Actions Manager
- CLI와 모듈 import 양쪽으로 사용 가능
- fcntl.flock 기반 파일 잠금으로 동시 쓰기 충돌 방지
- Atomic write (임시 파일 → os.replace)
- JSON 읽기/쓰기 실패 시 .bak 백업 복구 로직
    N)datetimetimezone)Optionalz//home/jay/workspace/memory/pending-actions.jsonc            	           e Zd ZddefdZd ZdefdZdefdZdedefd	Z		 	 dd
ede
e   dedefdZdede
e   fdZdefdZdefdZddedefdZy)PendingActionsNactions_filec                 j    |xs t         | _        | j                  dz   | _        | j                          y )Nz.bak)DEFAULT_ACTIONS_FILEr   backup_file_ensure_actions_file)selfr   s     G/home/jay/workspace/.worktrees/task-2116-dev1/memory/pending_actions.py__init__zPendingActions.__init__   s/    (@,@,,v5!!#    c                     t        j                  t         j                  j                  | j                        d       t         j                  j                  | j                        s| j                  g g d       yy)u9   액션 파일이 없으면 초기 구조로 생성한다.Texist_okactionsresolvedN)osmakedirspathdirnamer   exists_write_datar   s    r   r   z#PendingActions._ensure_actions_file!   sQ    
BGGOOD$5$56Fww~~d//0<= 1r   returnc                    	 t        | j                  dd      5 }t        j                  |t        j                         	 |j                         }t        j                  |      }t        j                  |t        j                         	 ddd       j                  dg        |j                  dg        |S # t        j                  |t        j                         w xY w# 1 sw Y   XxY w# t        j                  t        f$ r}t        j                  j                  | j                        r	 t        | j                  dd      5 }t        j                   |      }ddd       n# 1 sw Y   nxY wj                  dg        |j                  dg        | j#                  |       |cY d}~S # t        j                  t        f$ r Y nw xY wt%        d| d      d}~ww xY w)	uI   JSON 파일을 읽는다. 실패 시 백업에서 복구를 시도한다.rutf-8encodingNr   r   u   액션 파일 읽기 실패: u   . 백업도 사용 불가.)openr   fcntlflockLOCK_SHreadjsonloadsLOCK_UN
setdefaultJSONDecodeErrorOSErrorr   r   r   r   loadr   RuntimeError)r   fcontentdataprimary_errbfs         r   
_read_datazPendingActions._read_data'   s~   	d''w? 21Au}}-2ffhG::g.DKK5==12 OOIr*OOJ+K	 KK5==12 2 $$g. 	ww~~d../	d..gF -"#yy}- - -OOIr2OOJ3$$T*K,,g6 /}<VW 	s   C- %C! %B8%$C!
-C- 8&CC!!C*&C- -G")G0F/E'	F/'E0	,=F/)G"/GG
GGG"r3   c                    t         j                  j                  | j                        }t        j                  |d       t         j                  j                  | j                        r+	 t        j                  | j                  | j                         t        j                  |d      \  }}	 t        j                  |dd      5 }t        j                  |t        j                         	 t!        j"                  ||dd	
       |j%                          t        j&                  |j)                                t        j                  |t        j*                         	 ddd       t        j,                  || j                         y# t        $ r Y w xY w# t        j                  |t        j*                         w xY w# 1 sw Y   bxY w# t.        $ r' 	 t        j0                  |        # t        $ r Y  w xY ww xY w)uB   Atomic write: 임시 파일에 쓴 뒤 os.replace로 교체한다.Tr   z.tmp)dirsuffixwr!   r"   F   ensure_asciiindentN)r   r   r   r   r   r   shutilcopy2r   r.   tempfilemkstempfdopenr%   r&   LOCK_EXr)   dumpflushfsyncfilenor+   replace	Exceptionunlink)r   r3   dir_pathfdtmp_pathtfs         r   r   zPendingActions._write_dataF   su   77??4#4#45
Ht, 77>>$++,T..0@0@A
  ''HVDH	2sW5 3B.3IIdBU1EHHJHHRYY[)KKEMM23 JJx!2!23   KKEMM23 3  			(#   		ss   +*F 0G %G .AF:$G (G 	FF&F==G  G	G 	G<G,+G<,	G85G<7G88G<c                    d}|j                  dg       |j                  dg       z   }|D ]<  }|j                  dd      }|j                  d      s'	 t        |dd       }||kD  r|}> d|d	z   d
S # t        $ r Y Sw xY w)uL   actions + resolved 전체에서 최대 시퀀스 번호를 찾아 +1 반환.r   r   r   id zpa-   N   03d)get
startswithint
ValueError)r   r3   max_seqall_actionsactionaidseqs          r   _next_action_idzPendingActions._next_action_idg   s    hhy"-R0HH! 	F**T2&C~~e$c!"g,CW}"%	 Wq[%&& " s   A22	A>=A>descriptiontrigger_task_idtrigger_typec           	      R   | j                   dz   }t        |dd      5 }t        j                  |t        j                         	 | j                         }| j                  |      }||||dt        j                  t        j                        j                  d      dd}|d	   j                  |       | j                  |       t        j                  |t        j                         	 ddd       |S # t        j                  |t        j                         w xY w# 1 sw Y   S xY w)
uO   새 약속/후속 작업을 추가하고 추가된 action dict를 반환한다..lockr:   r!   r"   pending%Y-%m-%dT%H:%M:%S+00:00N)rQ   r`   ra   rb   status
created_atresolved_atr   )r   r$   r%   r&   rD   r6   r_   r   nowr   utcstrftimeappendr   r+   )	r   r`   ra   rb   	lock_pathlock_fr3   	action_idr\   s	            r   
add_actionzPendingActions.add_actionz   s     %%/	)S73 	3vKK.3( 006	##.'6$0'"*,,x||"<"E"E1# $(
 Y&&v.  &FEMM2'	3*  FEMM2'	3* s$   %DB C3$D3&DDD&rp   c                    | j                   dz   }t        |dd      5 }t        j                  |t        j                         	 | j                         }d}g }|d   D ]*  }|j                  d      |k(  r|}|j                  |       , |.	 t        j                  |t        j                         ddd       yd|d	<   t        j                  t        j                        j                  d
      |d<   ||d<   |d   j                  |       | j                  |       t        j                  |t        j                         	 ddd       |S # t        j                  |t        j                         w xY w# 1 sw Y   S xY w)uH   action_id 액션을 actions에서 제거하고 resolved로 이동한다.rd   r:   r!   r"   Nr   rQ   r   rg   rf   ri   )r   r$   r%   r&   rD   r6   rV   rm   r+   r   rj   r   rk   rl   r   )r   rp   rn   ro   r3   targetnew_actionsr\   s           r   resolve_actionzPendingActions.resolve_action   sO   %%/	)S73 	3vKK.3( "9o 3Fzz$'94!'#**62	3 > FEMM2/	3 	3 $.x (0X\\(B(K(K-)}% #.YZ ''/  &FEMM2/	32  FEMM2/	32 s1   %E6AE$E6:A$E$E6&E33E66F c                 |    | j                         }|d   D cg c]  }|j                  d      dk(  s| c}S c c}w )u:   pending 상태인 모든 action 리스트를 반환한다.r   rg   re   )r6   rV   )r   r3   as      r   get_pendingzPendingActions.get_pending   s5     	?KaaeeHo.JKKKs   99c                 4    t        | j                               S )u#   pending action 수를 반환한다.)lenrx   r   s    r   count_pendingzPendingActions.count_pending   s    4##%&&r   include_resolvedc                 J    | j                         }|r|d   |d   dS d|d   iS )u   전체 목록을 반환한다.r   r   r   )r6   )r   r|   r3   s      r   list_allzPendingActions.list_all   s;     	? ,  4	?++r   )N)Nmanual)F)__name__
__module____qualname__strr   r   dictr6   r   r_   r   rq   ru   listrx   rX   r{   boolr~    r   r   r   r      s    $S $>D > B'D 'S ', *.$	 "# 	
 
>  <LT L
's ', ,$ ,r   r   r   c                     t        j                  dd      } | j                  dd      }|j                  dd	      }|j	                  d
dd       |j	                  dd dd       |j	                  dddd       |j	                  dd d       |j                  dd	      }|j	                  dd	       |j	                  dd        |j                  dd	      }|j	                  dd        |j                  d d!	      }|j	                  d"d#d$d%&       |j	                  dd        |j                  d'd(	      }|j	                  dd        | S ))Nzpending-actions.pyzPending Actions Manager)progr`   commandCOMMAND)destmetavaraddu(   새 약속/후속 작업을 추가한다)helpz--descTu   약속/후속 작업 설명)requiredr   z--trigger-task-idra   u(   트리거 태스크 ID (예: task-123.1))defaultr   r   z--trigger-typer   rb   u+   트리거 타입 (예: on_complete, manual)z--actions-fileu.   액션 파일 경로 (기본값 사용 가능))r   r   resolveu"   액션을 resolved로 이동한다rp   u   액션 ID (예: pa-001))r   re   u7   pending 상태 액션 목록을 JSON으로 출력한다r   u   액션 목록을 조회한다z--allr[   
store_trueu   actions + resolved 모두)r   r\   r   countu.   pending 액션 수를 출력한다 (숫자만))argparseArgumentParseradd_subparsers
add_parseradd_argument)parser
subparsersp_add	p_resolve	p_pendingp_listp_cnts          r   build_parserr      s   $$!-F &&Iy&IJ !!%.X!YE	x$5RS	*D?P  XB  C	'  VC  D	'<lm %%i6Z%[I;-FG+T: %%i6o%pI+T: ""60O"PF
mLOjk
($7 !!'0`!aE	'6Mr   c                  x   t               } | j                         }|j                  %| j                          t	        j
                  d       t        |dd       }t        |      }|j                  dk(  rT|j                  |j                  |j                  |j                        }t        t        j                  |dd             y |j                  d	k(  r|j                  |j                         }|Ft        t        j                  d
d|j                    idd             t	        j
                  d       y t        t        j                  |dd             y |j                  dk(  r2|j#                         }t        t        j                  |dd             y |j                  dk(  rAt        |dd      }|j%                  |      }t        t        j                  |dd             y |j                  dk(  rt        |j'                                y | j                          t	        j
                  d       y )Nr   r   )r   r   )r`   ra   rb   Fr;   r<   r   erroru#   액션을 찾을 수 없습니다: rT   re   r   r[   )r|   r   )r   
parse_argsr   
print_helpsysexitgetattrr   rq   descra   rb   printr)   dumpsru   rp   rx   r~   r{   )r   argsr   paresultr|   s         r   mainr      s   ^FD||46L	\	2B||u		 00**  

 	djjeA>?		"""4>>2>

 CDNNCSTU!& HHQK$**V%BC		"!djjeA>?		"4>.>?djjeA>?		 b ! 	r   __main__)__doc__r   r%   r)   r   r?   r   rA   r   r   typingr   r
   r   r   r   r   r   r   r   r   <module>r      sg       	  
  ' H q, q,p h--  F/d zF r   