
    }-jh                    X   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	Z	ddl
mZ ddlmZmZmZmZ  ej        e          Zd5d
Zd6dZd6dZd7dZ	 d8d9dZddd:dZd;d<d"Zd=d$Zd>d%Z	 	 d?d@d,ZdAd.Zd8d>d/Zd8d>d0Z 	 	 dBdCd2Z!dDd3Z"ed4k    r e# e"                      dS )Eug  
Session recovery from .bak snapshots — last line of defense against
data-loss bugs like #1558.

``Session.save()`` writes a ``<sid>.json.bak`` snapshot of the previous
state whenever an incoming save would shrink the messages array. This
module reads those snapshots back and restores any session whose live
file has fewer messages than its backup, or whose live file is missing
while a valid backup remains.

Three integration points:

1. ``recover_all_sessions_on_startup()`` — called from server.py at boot,
   scans the session dir, restores any session whose JSON has fewer
   messages than its .bak, and recreates a missing ``<sid>.json`` from an
   orphaned ``<sid>.json.bak`` when the canonical state DB still has that
   session. Idempotent: a clean run is a no-op.

2. ``recover_session(sid)`` — single-session helper backing the
   ``POST /api/session/recover`` endpoint, so users can re-run recovery
   manually if their session was open through a server restart.

3. ``inspect_session_recovery_status(sid)`` — read-only audit returning
   message counts for the live JSON, the .bak, and a recommendation.
    )annotationsN)Path)derive_turn_journal_statesis_terminal_turn_eventiter_turn_journal_session_idsread_turn_journalpr   returnintc                @   	 t          j        |                     d                    }n## t          t           j        t
          f$ r Y dS w xY wt          |t                    sdS |                    d          }t          |t                    rt          |          ndS )u  Return the number of messages in a session JSON file, or -1 on read/parse error.

    Returns -1 for any non-session-shape file:
    - File can't be read (OSError)
    - Top-level isn't valid JSON or is invalid (JSONDecodeError, ValueError)
    - Top-level isn't a dict (AttributeError on .get) — e.g. ``_index.json``
      which is a top-level list of session metadata, not a session itself.
      The startup recovery scanner globs ``*.json`` and would otherwise
      crash on the first non-dict file it encounters.
    utf-8encodingmessages)jsonloads	read_textOSErrorJSONDecodeError
ValueError
isinstancedictgetlistlen)r	   datamsgss      */root/hermes-webui/api/session_recovery.py
_msg_countr    /   s    z!++w+7788T):6   rrdD!! r88JD"4..63t999B6s   (+ A
Asession_pathr   c                    |                      d          }t          |           }|                                s| j        |dddS t          |          }||k    r| j        ||ddS | j        ||ddS )a   Return a status dict describing whether recovery is recommended.

    {
      "session_id": "...",
      "live_messages": int,    # -1 if live file unreadable
      "bak_messages": int,     # -1 if no .bak or unreadable
      "recommend": "restore" | "no_action" | "no_backup",
    }
    	.json.bakr   	no_backup)
session_idlive_messagesbak_messages	recommendrestore	no_action)with_suffixr    existsstem)r!   bak_path
live_count	bak_counts       r   inspect_session_recovery_statusr1   D   s     ''44HL))J?? 
&+'$	
 
 	
 8$$I:&+'%"	
 
 	
 #'#! 	      c                B   t          |           }|d         dk    ri |ddiS |                     d          }|                     d          }	 t          j        ||           |                    |            nq# t
          $ rd}t                              d| |           	 |                    d	           n# t
          $ r Y nw xY wi |dt          |          d
cY d}~S d}~ww xY wt                              d| j
        |d         |d                    i |ddiS )zRestore session_path from its .bak when the bak has more messages.

    Returns a status dict identical to ``inspect_session_recovery_status``
    plus a "restored" boolean.
    r(   r)   restoredFr#   z.json.recover.tmpz'recover_session: copy failed for %s: %sT
missing_ok)r4   errorNu|   recover_session: restored %s from .bak (live=%d → bak=%d messages). See #1558 for the data-loss class this guards against.r&   r'   )r1   r+   shutilcopyfilereplacer   loggerwarningunlinkstrname)r!   statusr.   tmp_pathexcs        r   recover_sessionrC   g   s{    -\::Fki'',&,*e,,,''44H ''(;<<H	@(+++&&&& @ @ @@,PSTTT	OOtO,,,, 	 	 	D	?&?ec#hh?????????@ NN	A6/2F>4J  
 (f'j$'''sB   *A: :
C(C#!B87C#8
CC#CC#C(#C(r%   r>   state_db_pathPath | Noneboolc                   ||                                 sdS 	 t          j        d| dd          5 }|                    d          }|                                	 ddd           dS |                    d| f          }|                                ducddd           S # 1 swxY w Y   dS # t
          $ r'}t                              d| |           Y d}~dS d}~ww xY w)	a!  Return whether state.db still knows this session.

    The check is deliberately fail-open: recovery must not be prevented by a
    locked, absent, or older-schema state DB. When a DB is readable and has no
    row, treat the orphan backup as a tombstoned/deleted session and skip it.
    NTfile:?mode=rourizBselect 1 from sqlite_master where type='table' and name='sessions'z+select 1 from sessions where id = ? limit 1z2state_db session tombstone check failed for %s: %s)r,   sqlite3connectexecutefetchone	Exceptionr;   debug)r%   rD   conncurrB   s        r   _state_db_has_sessionrT      sf    M$8$8$:$:t_<]<<<$GGG 	.4,,T C ||~~%	. 	. 	. 	. 	. 	. 	. 	. ,,Lzm\\C<<>>-	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	.    I:WZ[[[tttttsF   B2 +B%B2 ,,B%B2 %B))B2 ,B)-B2 2
C#<CC#session_dir
list[Path]c                   g }t          |                     d                    D ]}|                    d          }|j                            d          s|                                rFt          |          dk     rZ|j        }t          ||          s!t          
                    d|j                   |                    |           |S )aH  Return live ``<sid>.json`` paths whose ``<sid>.json.bak`` exists.

    ``Path.glob('*.json')`` does not see orphan backups because their suffix is
    ``.bak``. Existing startup recovery only handled shrunken live files; this
    helper covers the crash shape where the live sidecar is gone but the rescue
    copy remains.
    
*.json.bak _r   z[recover_all_sessions_on_startup: skipped orphan backup %s; state.db has no live session row)sortedglobr+   r?   
startswithr,   r    r-   rT   r;   infoappend)rU   rD   pathsr.   	live_pathr%   s         r   _orphaned_backup_live_pathsrb      s     E;++L99::    ((,,	>$$S)) 	Y-=-=-?-? 	h!##^
$Z?? 	KK3  
 YLr2   Finclude_emptyrd   
list[dict]c               \   ||                                 sg S 	 t          j        d| dd          5 }t          j        |_        d |                    d                                          D             }d |                    d	                                          D             }d
dh                    |          sg cddd           S t          d|          }t          d|          }t          d|d          }t          d|          }	t          d|d          }
t          d|          }t          d|          }t          d|          }t          d|          }t          d|          }g }|                    d| d| d| d|	 d|
 d| d| d| d| d| d                                          D ]%}t          |          }t          |                    d
          pd                                          }|r| | dz                                   reg }h d                    |          rd|v rd
|v rdnd }d|v rdnd!}|                    d"| d#| |f                                          D ]=}|d$         |d%         pdd&}|d         |d         |d<   |                    |           >|s|s||d'<   | |d(<   |                    |           '|cddd           S # 1 swxY w Y   dS # t          $ r(}t                              d)||           g cY d}~S d}~ww xY w)*z@Return WebUI-origin state.db rows whose JSON sidecar is missing.NrH   rI   TrJ   c                    h | ]
}|d          S     .0rows     r   	<setcomp>z6_read_state_db_missing_sidecar_rows.<locals>.<setcomp>       eeesCFeeer2   zPRAGMA table_info(sessions)c                    h | ]
}|d          S rh   rj   rk   s     r   rn   z6_read_state_db_missing_sidecar_rows.<locals>.<setcomp>   ro   r2   zPRAGMA table_info(messages)idsourcetitlemodel
started_at0parent_session_idmessage_count	workspaceworktree_pathworktree_branchworktree_repo_rootworktree_created_atz$
                SELECT id, source, z, z,
                       z
                FROM sessions
                WHERE source = 'webui'
                ORDER BY COALESCE(started_at, 0) DESC
                rY   .json>   rolecontentr%   	timestampztimestamp, idrowidzNULL AS timestampzSELECT role, content, z- FROM messages WHERE session_id = ? ORDER BY r   r   )r   r   r   _state_db_empty_messagesz6state_db sidecar reconciliation scan failed for %s: %s)r,   rL   rM   Rowrow_factoryrN   fetchallissubset_sql_optional_colr   r>   r   stripr_   rP   r;   rQ   )rU   rD   rd   rR   session_colsmessage_cols
title_expr
model_exprstarted_exprparent_exprmsg_count_exprworkspace_exprworktree_path_exprworktree_branch_exprworktree_repo_root_exprworktree_created_at_exprrowsrm   r   sidmessage_rowsorderts_exprmsgmessagerB   s                             r   #_read_state_db_missing_sidecar_rowsr      s    M$8$8$:$:	8_<]<<<$GGG 4	4&{Deedll;X.Y.Y.b.b.d.deeeLeedll;X.Y.Y.b.b.d.deeeL(#,,\:: 4	 4	 4	 4	 4	 4	 4	 4	 +7LAAJ*7LAAJ,\<MML+,?NNK.cRRN.{LIIN!2?L!Q!Q#45F#U#U &78Ll&[&[#'89NP\']']$D||$. 2< @L # '5 9G  +  /C  0	  4L	  
 
 hjj"" "" Cyy$((4...B//5577 {]]]:BBDD +-444==lKK 5/:l/J/JtWcOcOcOOipE-8L-H-HkkNaG#||nnnglnn    hjj
5 
5
 %(K'*9~';# # {+736{3CGK0$++G4444# M #/Z 7C3C/0D!!!!i4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	 4	j    M}^abbb						sN   K9 BK, K9 HK,K9 ,K00K9 3K04K9 9
L+L& L+&L+NULLr?   columnsset[str]fallbackc                    | |v r| n| d|  S )Nz AS rj   )r?   r   r   s      r   r   r      s"    7??448(?(?(?(??r2   rm   c                r   	 ddl m} n# t          $ r d }Y nw xY wt          |                     d          pd                                                                          }|r ||          n|pd |pd |r|                                nd d}|                     d          pd}t          |                     d          t                    r|                     d          ng }|r6t          |d         t                    r|d                             d	          n|}|                     d
          pd}i d|                     d          d|                     d          pdd
t          |t                    r|nddt          |                     d          t                    r|                     d          nt          |          d|                     d          pd d|                     d          pd d|                     d          pd d|                     d          pd d|                     d          pddd d|d|p|dddddd dd ddi ddd d d!d d"d d#d d$g d%d d&d d'd d(d d)d d*d d+d d,d d-g d.dd/|                     d/          d|pd d0|d i |g d1d2S )3Nr   )normalize_agent_session_sourcerr   rY   )
raw_sourcesession_sourcesource_labelru   r   r   r   ry   r%   rq   rs   zRecovered WebUI Sessionrx   rz   r{   r|   r}   rt   unknownmodel_provider
created_at
updated_atpinnedFarchived
project_idprofileinput_tokensoutput_tokensestimated_costpersonalityactive_stream_idpending_user_messagepending_attachmentspending_started_atcompression_anchor_visible_idxcompression_anchor_message_keycompression_anchor_summarycontext_lengththreshold_tokenslast_prompt_tokensgateway_routinggateway_routing_historyllm_title_generatedrw   )is_cli_session
source_tagT)enabled_toolsetscomposer_draftr   
tool_calls_recovered_from_state_db)api.agent_sessionsr   rP   r>   r   r   lowerrs   r   r   r   r   r   )rm   r   rr   source_metaru   r   last_tsworkspace_values           r   _state_db_row_to_sidecarr     s"   .EEEEEEE . . .)-&&&.""(b))//117799F<Z 00888n .D*0:da aK
 &&+!J&01D1Dd&K&KSswwz"""QSH/7jJxPR|UY<Z<Zjhrl{+++`jGggk**0bO+cggdmm+!!>%>+ 	
?C(H(HP__b+ 	Z@X@XZ]5^5^q111dghpdqdq	+
 	119T+ 	377#455=+ 	cgg&:;;Ct+ 	sww'<==E+ 	!!.Y+ 	$+ 	j+ 	g++ 	%+ 	E+ 	d+  	4!+" 	#+ +$ 	%+& 	$'+( 	t)+* 	D++, 	-+. 	r/+0 	d1+2 	)$3+4 	)$5+6 	%d7+8 	$9+: 	D;+< 	d=+> 	4?+@ 	"2A+B 	uC+D 	SWW%899E+ +F  nI+ + +J K+L !$(U+ + + +s   	 c           
     n   t          | |          }d}g }|                     dd           |D ]t}t          |                    d          pd                                          }|s<| | dz  }|                                rYt          |          }dt          j                     dt          j
                    j         }	|                    |	          }
d	}	 |
                    t          j        |d	d
          d           nf# t           $ rY}	 |
                    d           n# t           $ r Y nw xY w|                    |d	t          |          d           Y d}~:d}~ww xY wd	}	 t          j        t          |
          t          |                     d}nJ# t(          $ r Y n>t           $ r2}|                    |d	t          |          d           d}Y d}~nd}~ww xY w	 |
                    d           n<# t           $ r Y n0w xY w# 	 |
                    d           w # t           $ r Y w w xY wxY w|rB|dz  }|                    |dt+          |                    d          pg           d           Y|s|                    |d	dd           vt+          |          ||dS )zEMaterialize missing WebUI JSON sidecars from canonical state.db rows.r   T)parentsexist_okrq   rY   r~   z.json.reconcile.tmp..F   )ensure_asciiindentr   r   r5   )r%   materializedr7   Nri   r   )r%   r   r   !sidecar_appeared_during_reconcile)r%   r   skipped)scannedr   details)r   mkdirr>   r   r   r,   r   osgetpid	threadingcurrent_threadidentr+   
write_textr   dumpsr   r=   r_   linkFileExistsErrorr   )rU   rD   r   r   r   rm   r   targetpayload
tmp_suffixtmpdetail_recordedrB   materialized_nows                 r   &recover_missing_sidecars_from_state_dbr   @  sI   .{MJJDLGdT222 .w .w#''$--%2&&,,.. 	#}}},==?? 	*3// ]BIKK\\):R:T:T:Z\\
  ,,	NN4:gE!LLLW^N____ 	 	 	

d
++++   NN#usSVxxXXYYYHHHH	 !	GCHHc&kk***# 	 	 	D 	# 	# 	#NN#usSVxxXXYYY"OOOOOO	#

d
++++   

d
++++    	wALNN#tQTU\U`U`akUlUlUrprQsQsttuuuu  	wNN#uQtuuvvv4yy,7SSSs   ,D


E-D,+E(,
D96E(8D99)E((E-31F%$H%
G,/H1	G,:(G'"H'G,,H0H
HHI H0/I 0
H=	:I <H=	=I r   kindcategoryrecommendationr&   r'   c                B    | |||||d}|                     |           |S )N)r%   r   r   r   r&   r'   )update)r%   r   r   r   r&   r'   extraitems           r   _new_audit_itemr   x  s;     !(&$ D 	KKKr2   
index_pathc                   	 t          j        |                     d                    }n0# t          t           j        t
          f$ r t                      cY S w xY wt          |t                    st                      S t                      }|D ]Z}t          |t                    rCt          |
                    d          t                    r|                    |d                    [|S )Nr   r   r%   )r   r   r   r   r   r   setr   r   r   r   r>   add)r   r   idsentrys       r   _read_index_session_idsr     s    z*...@@AAT):6   uudD!! uuEEC ) )eT"" 	)z%))L2I2I3'O'O 	)GGE,'(((Js   (+ *AAc                n   |                                  s
dddddg dS g }t          d |                     d          D                       }d |D             }|D ]}t          |          }|                    d          d	k    rU|                    t          |d
         ddd|                    dd          |                    dd                               t          |                     d                    D ]}|                    d          }|                                 s|j        	                    d          rFt          |          }|j        }	|dk     r(|                    t          |	dddd|                     t          |	|          r(|                    t          |	dddd|                     |                    t          |	dddd|                     | dz  }
|
                                 rt          |
          }t          ||z
            D ]'}	|                    t          |	ddd                     (t          ||z
            D ]<}	|                    t          |	dddt          | |	 dz            d                     =t          | |d          D ]}t          |                    d           pd          }|                    d!          r(|                    t          |d"dddd                     c|                    t          |d#dd$dd                     t!          |           D ]}	t#          |	| %          }t%          |                    d&          pg           \  }}| |	 dz  }t          |          }t'                      }	 t)          j        |                    d'(                    }t/          |t0                    r|                    d)          pg D ]y}t/          |t0                    rb|                    d*          d+k    rI|                    t          |                    d,          pd                                                     zn"# t6          t(          j        t:          f$ r Y nw xY wt          |                                          D ]\  }}t?          |          rt          |                    d,          pd                                          }|r||v rR|                    t          |	d-dd.|d|t          |                    d/          pd          0                     tA          |          ddd}|D ]+}|                    d1          }||v r||xx         d2z  cc<   ,|d         rd3}n|d         rd4}nd}|||dS )5a3  Read-only audit of session recovery state.

    The audit intentionally does not mutate files. It classifies only the safe
    recovery primitives this module knows how to perform: backup restores and
    derived index rebuilds. Call ``recover_all_sessions_on_startup`` separately
    for safe repairs.
    okr   )r   
repairableunsafe_to_repair)r@   summaryitemsc              3  N   K   | ] }|j                             d           |V  !dS )rZ   Nr?   r]   rl   r	   s     r   	<genexpr>z)audit_session_recovery.<locals>.<genexpr>  s8      \\aQVEVEVWZE[E[\\\\\\\r2   *.jsonc                    h | ]	}|j         
S rj   )r-   r  s     r   rn   z)audit_session_recovery.<locals>.<setcomp>  s    +++1+++r2   r(   r)   r%   shrunken_liver   restore_from_bakr&   r   r'   rX   rY   rZ   malformed_orphan_backupr   manual_revieworphan_backuporphan_backup_without_state_rowz_index.jsonindex_missing_filerebuild_indexindex_missing_entryr~   Trc   rq   r   state_db_orphan_webui_rowstate_db_missing_sidecarmaterialize_from_state_db)rU   eventsr   r   r   r   userr   turn_journal_pending_turnaudit_only_pending_turn_journalevent)turn_idr  r   ri   needs_manual_reviewwarn)!r,   r[   r\   r1   r   r_   r   r+   r?   r]   r    r-   rT   r   r   r>   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )rU   rD   r   
live_pathslive_idsra   r@   r.   r'   r%   r   	index_idsrm   r   journalstatesrZ   r&   existing_user_messagesr   r   r  r  r   r   r   r   overalls                               r   audit_session_recoveryr     s     
qaHH
 
 	
 E\\;#3#3H#=#=\\\\\J++
+++H 
 
	0;;::k""i//LL|$"

?B//

>2..     ;++L99::  ((,,	 	!:!:3!?!? 	!(++^
!LL57I?\^`l      #:}== 	LLO\;MrS_      LL1"      },J 
+J77	 X!566 	 	JLL0,      !I!566 	 	JLL1<;J)=)=)==>>     
 3;]abbb  #''$--%2&&77-.. 		LL+"     _&'
 
 	 	 	 	 4K@@  
#JKHHH.w{{8/D/D/JKK	Z"6"6"66	"9--+.55	j!4!4g!4!F!FGGG'4(( ^&{{:66<" ^ ^G!'400 ^W[[5H5HF5R5R.223w{{97M7M7SQS3T3T3Z3Z3\3\]]]-z: 	 	 	D	$V\\^^44 	 	NGU%e,, %))I..4"55;;==G g)???LL+1%))G,,233	 	 	 	 	 	 		" __A1MMG # #88J''wH"!" '		 'EBBBs   %CQ44RRc                   t          | |          }t          | d|          }t          | |          }|                    d          rF	 ddlm}  |d           n2# t          $ r%}t                              d	|           Y d}~nd}~ww xY wt          | |          }t          |                    d
          pi                     d          pd          }t          |                    d
          pi                     d          pd          }	|dk    o|	dk    }
|
|
t          |                    d          pd          t          |                    d          pd          z   ||||dS )a  Run safe, deterministic session recovery repairs.

    This mutates only repairable classes already handled by startup recovery:
    shrunken live sidecars and orphan backups that are not tombstoned by a
    readable state.db. Unsafe audit findings remain for manual review.
    rD   T)r  rD   r   r   _write_session_indexNupdateszTrepair_safe_session_recovery: index rebuild after state.db reconciliation failed: %sr   r   r   r4   )cleanr   repairedbeforebackup_repairsidecar_repairafter)
r   recover_all_sessions_on_startupr   r   
api.modelsr$  rP   r;   r<   r   )rU   rD   r)  r*  r+  r$  rB   r,  unsafe_remainingrepairable_remainingr'  s              r   repair_safe_session_recoveryr1  !  s    $K}MMMF3#  M
 <KWWN.)) x	x777777  ..... 	x 	x 	xNNqsvwwwwwwww	x";mLLLEEIIi006B;;<NOOTSTUU		) 4 4 :??MMRQRSS!?&:a&?E))*55:;;c.BTBTUcBdBdBihi>j>jj&(  s   
A 
B'BBr  c           	        |                                  sdddg dS d}d}g }d t          |                     d                    D             }t          | |          }g ||D ]}|dz  }	 t	          |          }	nK# t
          $ r>}
t                              d|j        t          |
          j
        |
           Y d}
~
[d}
~
ww xY w|	                    d	          r|dz  }|                    |	           |rdt                              d
||           |rF	 ddlm}  |d           n2# t
          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY w||t          |          |dS )zScan session_dir for shrunken/orphaned sessions and restore from .bak.

    Returns {"scanned": N, "restored": M, "orphaned_backups": K, "details": [...]}.
    r   )r   r4   orphaned_backupsr   c                F    g | ]}|j                             d           |S )rZ   r   )rl   paths     r   
<listcomp>z3recover_all_sessions_on_startup.<locals>.<listcomp>R  s.    ggg4TYMaMabeMfMfg$gggr2   r  r"  ri   z9recover_all_sessions_on_startup: skipped %s due to %s: %sNr4   u   recover_all_sessions_on_startup: restored %d/%d sessions from .bak. If you weren't expecting this, check the session list for missing messages — see #1558.r#  r%  z9recover_all_sessions_on_startup: index rebuild failed: %s)r,   r[   r\   rb   rC   rP   r;   r<   r?   type__name__r   r_   r.  r$  r   )rU   r  rD   r   r4   r   r  orphan_pathsr5  resultrB   r$  s               r   r-  r-  D  s     S!rRRRGHGgg6+*:*:8*D*D#E#EgggJ.{-XXXL,*,|, # # 	1		$T**FF 	 	 	 NNK	499-s   HHHH	 ::j!! 	#MHNN6""" a&'/	
 	
 	

  	aa;;;;;;$$T22222 a a aZ\_````````a --	  s0   -A==
C4C  CD, ,
E6EEc                 ,   t          j        d          } |                     ddd           |                     dt          dd	
           |                     dt          d d           |                     ddd           |                                 }|j        rt          |j        |j                  }n8|j	        rt          |j        |j                  }n|                     d           t          t          j        |d                     dS )Nz)Audit Hermes WebUI session recovery state)descriptionz--audit
store_truezrun a read-only recovery audit)actionhelpz--session-dirTz path to WebUI sessions directory)r7  requiredr?  z
--state-dbzoptional Hermes state.db path)r7  defaultr?  z--repair-safez-run safe deterministic repairs after auditingr"  zchoose --audit or --repair-safe)	sort_keysr   )argparseArgumentParseradd_argumentr   
parse_argsrepair_safer1  rU   state_dbauditr   r7   printr   r   )parserargsreports      r   _mainrN  |  s   $1\]]]F
	,=]^^^
dTHjkkk
4Dcddd
CrsssD 8-d.>dm\\\	 8'(8VVV6777	$*Vt
,
,
,---1r2   __main__)r	   r   r
   r   )r!   r   r
   r   )r%   r>   rD   rE   r
   rF   )N)rU   r   rD   rE   r
   rV   )rU   r   rD   rE   rd   rF   r
   re   )r   )r?   r>   r   r   r   r>   r
   r>   )rm   r   r
   r   )rU   r   rD   rE   r
   r   )r   r   )r%   r>   r   r>   r   r>   r   r>   r&   r   r'   r   r
   r   )r   r   r
   r   )FN)rU   r   r  rF   rD   rE   r
   r   )r
   r   )$__doc__
__future__r   rC  r   loggingr   r8   rL   r   pathlibr   api.turn_journalr   r   r   r   	getLoggerr8  r;   r    r1   rC   rT   rb   r   r   r   r   r   r   r   r1  r-  rN  
SystemExitrj   r2   r   <module>rW     s   2 # " " " " "    				                        
	8	$	$7 7 7 7*       F( ( ( (>   2 "&    D  	A A A A A AH@ @ @ @ @: : : :z5T 5T 5T 5Tz     *   CC CC CC CC CCL         J  !%5 5 5 5 5p   " z
*UUWW

 r2   