
    %jz              	       x   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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mZ d dlmZ  ed      Z ej*                  edz  j-                               Z eed         Zd Z e ej6                  dej9                  d	d
                  Z e ej6                  dej9                  dd                  Z e ej6                  dej9                  dd                  Zedz  Z dZ!edz  Z"edz  Z#dZ$d Z% G d d      Z&d Z'd Z(d Z) ejT                  dejV                        Z,d Z-d Z.d Z/d  Z0d! Z1d" Z2d# Z3d$ Z4d% Z5d& Z6d' Z7d( Z8d) Z9d* Z:d+ Z;d, Z<d- Z=d. Z>d/ Z?d0 Z@d1 ZAd2 ZBd3 ZCdEd4ZDd5 ZEd6 ZFeGd7k(  r e j                         ZIeIj                  d8d9:       eIj                  d;d9:       eIj                  d<d9:       eIj                  d=d9:       eIj                  d>eKd?@       eIj                  dA       eIj                  dB       eIj                  dCd9:       eIj                         ZMeMj                  r/eMj                  j                  dD      D  cg c]
  }  eK|        c} ndZNeMj                  r/eMj                  j                  dD      D  cg c]
  }  eK|        c} ng ZQeMj                  xs eNdu ZR e	j                  eMj                  r eF       n2eMj                  r eE       n eDeMj                  eNeReQeMj                               yyc c} w c c} w )F    N)datetimetimezone	timedeltaPathz&/opt/struktur/obsidian-graphiti-importzconfig.jsondbc                  d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     /tmp/aa043-staging/importer.py<lambda>r      sJ      HP  HT  HT  U]  Ua  Ua  Hb  Hl  Hl  Hn r   !GRAPHITI_REQUEST_DEADLINE_SECONDS!graphiti_request_deadline_secondsiJ  IMPORTER_HTTP_TIMEOUT_SECONDSimporter_http_timeout_secondsih  OUTER_PROCESS_TIMEOUT_SECONDSouter_process_timeout_seconds  zimport.lock   zprovider_state.jsonzimport_run_block.jsoniQ c                    	 t        d|  d      j                         }|j                  dd      d   j                         }t	        |d         }t        d t        d      j                         j                         D              }t        j                  t        j                  d         }| |t        j                  |||z  z   t        j                        j                         d	S # t        t         t"        f$ r	 | d d d	cY S w xY w)
Nz/proc/z/statz)       c              3   x   K   | ]2  }|j                  d       st        |j                         d          4 yw)zbtime r   N)
startswithintsplit).0xs     r   	<genexpr>z)process_start_metadata.<locals>.<genexpr>   s.     n1WXWcWcdlWmS1ns   :!:z
/proc/stat
SC_CLK_TCK)pidprocess_start_ticksprocess_start_time)r   	read_textrsplitr    r   next
splitlinesossysconfsysconf_namesr   fromtimestampr   r   r   FileNotFoundError
ValueErrorStopIteration)r%   rawtailticksbtimehzs         r   process_start_metadatar8      s   ~
VC5))+##**T!2DQ2G2M2M2OTWZ[_`b[cWdQVn\(:(D(D(F(Q(Q(Snn%sus}s}  A  O  O  P\  ]  t^pr%XE[E[\abghjbj\jkskwkwEx  FC  FC  FE  
F  F	:m4}C^bx|=}6}}s   C&C) )DDc                   0    e Zd Zd Zd Zd Zd Zd Zd Zy)
ImportLockc                     || _         d | _        d | _        t        j                         | _        d | _        i | _        i | _        y r
   )	run_idfdinode	threadingEventstopthreadold_handlersmetadata)selfr<   s     r   __init__zImportLock.__init__   s;    $+ddgtDJy?PTY^bRVR]vxdhdu$-r   c                    t               | j                  d<   t        j                  | j                  dd      dz   j	                         }t        j                  | j                  dt
        j                         t        j                  | j                  d       t        j                  | j                  |       t        j                  | j                         y )Nheartbeat_atFTensure_ascii	sort_keys
r   )NOWrD   jsondumpsencoder,   lseekr=   SEEK_SET	ftruncatewritefsync)rE   datas     r   _writezImportLock._write   s     #$--TZZSXcg-him-m,u,u,wt((4771R[[!2<<#:BHHTWWT<RTVT\T\]a]d]dTer   c                     | j                   j                  t              s1| j                          | j                   j                  t              s0y y # t        $ r Y y w xY wr
   )rA   waitLOCK_HEARTBEAT_SECONDSrW   OSError)rE   s    r   
_heartbeatzImportLock._heartbeat   s<    IINN12 IINN12
6s   A 	AAc                    t        j                  t        t         j                  t         j                  z  d      }t        j                  |t
        j                  t
        j                  z         || _        t        j*                  |      j,                  | _        t1        t        j2                               }t        j2                         |d
   |d   | j4                  t7        j8                         t;               t;               dj=                  t>        j@                        t%        tB              | j.                  d
| _"        | jG                          tI        jJ                  | jL                  dd      | _'        | jN                  jQ                          tI        jR                         tI        jT                         u rgtV        jX                  tV        jZ                  fD ]D  }tW        j\                  |      | j^                  |<   tW        jV                  || j`                         F y# t        $ r 	 t        j                  |dt         j                         t        j                  |d      j                  d      j                         }t        t!        j"                  dt%        t              |dd	             t        j&                  |       Y y# t        j&                  |       w xY ww xY w)Nr   r   i    replace)errorsT)already_running	lock_pathlock_metadataFrJ   r'   r&    )

process_idr'   r&   r<   hostname
created_atrH   commanddatabase_path
lock_inodezimport-lock-heartbeat)targetnamedaemon)1r,   open	LOCK_PATHO_RDWRO_CREATfcntlflockLOCK_EXLOCK_NBBlockingIOErrorrQ   rR   readdecodestripprintrN   rO   strcloser=   fstatst_inor>   r8   getpidr<   socketgethostnamerM   joinsysargvDBrD   rW   r?   Threadr\   rB   startcurrent_threadmain_threadsignalSIGTERMSIGINT	getsignalrC   _signal_handler)rE   r=   r3   r   sigs        r   acquirezImportLock.acquire   s   WWYryy+E2"{{2emmEMM12 $'",,dj4J299;4We iikuEY?Zqv  xM  rN  X\  Xc  Xc  ou  oA  oA  oC  QT  QV  fi  fk  vy  v~  v~  B  G  G  vH  Y\  ]_  Y`  nr  nx  nx  y$-++-Y--T__Jaimnptp{p{  qB  qB  qD9#8#8#::~~fmm, Zs!++C0Dc&--DDXDX2YZ	 
 HHR"++BGGBt$4$;$;9$;$M$S$S$UW\]a]g]g{  MP  QZ  M[  lo  ip  ~C  ^D  XEHHRL
 HHRLs$   5H# #	K)-B
K7K)K%%K)c                 >    | j                          t        d|z         )N   )release
SystemExit)rE   signumframes      r   r   zImportLock._signal_handler,   s    ,,.
3v:..r   c                    | j                   y | j                  j                          | j                  r| j                  j	                  d       	 t        j                  t              j                  | j                  k(  rt        j                  t               | j                  j                         D ]  \  }}t        j                  ||        t        j                   | j                   t        j"                         t        j$                  | j                          d | _         y # t        $ r Y w xY w# | j                  j                         D ]  \  }}t        j                  ||        t        j                   | j                   t        j"                         t        j$                  | j                          d | _         w xY w)N   timeout)r=   rA   setrB   r   r,   statro   r~   r>   unlinkr0   rC   itemsr   rr   rs   LOCK_UNr|   )rE   r   handlers      r   r   zImportLock.release.   s   	WW_f))--/	[[$++""1"-Gggi+RYYy-A ''--/K{s7s71KK;;twwu}}%rxx'8$$' 
 D ''--/K{s7s71KK;;twwu}}%rxx'8$$'s&   A	D' '	D30D6 2D33D6 6BGN)	__name__
__module____qualname__rF   rW   r\   r   r   r   r   r   r   r:   r:      s"    f/	Gr   r:   c                     t        t        |       j                  dd            j                         j	                  d      S )N\/)r   r{   r^   as_posixlstrip)rels    r   normalized_relative_pathr   8   s2    $s3x/?/?S/I*J*S*S*U*\*\]`*a#ar   c                     t        |       } |rt        |      dk7  rt        d      t        j                  d| z   dz   |z   j                               j                         S )N@   zinvalid content_hashz	obsidian||)r   lenr1   hashlibsha256rP   	hexdigest)r   hs     r   
import_keyr   9   sS    c"	SVRZz*@AAC+A-5578BBDDr   c                 *   | xs dj                         } |xs dj                         j                         }| rt        j                  d|      st	        d      t        j                  d| z   dz   |z   j                  d            j                         S )N z[0-9a-f]{64}z invalid formal identity materialzv1
rL   utf-8)	ry   lowerre	fullmatchr1   r   r   rP   r   )source_identityversion_hashs     r   formal_import_identityr   =   s    !'R..0!r((*002r||OLIQ[\~QK.t3L@HHQR\\^^r   zD(?:youtube\.com/(?:watch\?v=|shorts/)|youtu\.be/)([A-Za-z0-9_-]{6,})c                 |    t         j                  | xs d      }|rd|j                  d      z   S dt        |      z   S )Nr   youtube:r   	obsidian:)YOUTUBE_URL_REsearchgroupr   )r3   r   ms      r   source_identity_forr   C   s;    #!"
1771:Q4LS4Q(QQr   c                     | xs dj                  dd      j                  dd      }dj                  d |j                  d      D              j                  d      S )Nr   z
rL   c              3   >   K   | ]  }|j                  d         yw)z 	N)rstrip)r!   lines     r   r#   z*canonical_content_bytes.<locals>.<genexpr>H   s     B$++e$Bs   r   )r^   r   r    rP   )r3   texts     r   canonical_content_bytesr   F   sN    
yb&&..tD9		BD1ABBII'RRr   c                 Z    t        j                  t        |             j                         S r
   )r   r   r   r   )r3   s    r   content_version_hashr   I   s    gnn5LS5Q&R&\&\&^^r   c                     t        j                  d| xs d      }|si S 	 t        j                  |j	                  d            }t        |t              r|S i S # t        $ r i cY S w xY w)N\|\s*(\{.*\})$r   r   )r   r   rN   loadsr   
isinstancedict	Exception)sdr   r"   s      r   !_metadata_from_source_descriptionr   J   s[    99rxR(	"9JJqwwqz!
1T(:1BB"9s   5A A A&%A&c                     t        t        |       dz         }|j                  t        j                  |ddd      dz          t        j                  ||        y )Nz.tmpFTr   )rJ   rK   indentrL   )r   r{   
write_textrN   rO   r,   r^   )pathpayloadtmps      r   _atomic_jsonr   R   sU    	#d)F
S^^DJJwE\`hi,jko,oprtr|r|  ~A  BF  sGr   c                     t        j                  t        |       j                               S # t        t         j
                  t        f$ r Y y w xY wr
   )rN   r   r   r(   r0   JSONDecodeErrorr[   )r   s    r   
_read_jsonr   T   s8    ZZT
,,.//	4//8EEs   +- AAc                 >   t        j                  d| xs dt         j                        }|sy t        |j	                  d            }|dkD  r|dz  n|}t        j                  |t        j                        j                         S # t        t        t        f$ r Y y w xY w)Nz!X-RateLimit-Reset[^0-9]*(\d{10,})r   r   l    h]   )r   r   Ifloatr   r   r/   r   r   r   OverflowErrorr[   r1   )r   r   values      r   provider_reset_atr   W   sx    991$*"RTTB	$QWWQZeFld
%##E(,,7AACC	wz*747s   1B BBc                 J    | xs dj                         }d|v sd|v sd|v rdS d S )Nr   429z
rate limit	ratelimitprovider_rate_limit)r   )r   ts     r   r   r   ]   s7    JB%1*XYHY]hlm]m 5xtxxr   c                  <   t        t              } | r| j                  d      sd| fS | j                  d      xs | j                  d      }|r;t        j                  t
        j                        t        j                  |      k  | fS d| fS # t        $ r Y d| fS w xY w)NblockedFnext_allowed_atreset_atT)	r   PROVIDER_STATE_PATHgetr   r   r   r   fromisoformatr1   )stateuntils     r   provider_block_activer   _   s    %&UYYy)%++=yy"#<uyy'<	ll8<<(8+A+A%+HHNNU
 
TU
 s   :B 	BBc           
      N   t        j                  t        j                        }t	        |       }|rt        j
                  |      nd }|r||kD  r|n |t        t              z   j                         }dd|j                         |||t        |      ddd	}t        t        |       |S )N)secondsTr   
OpenRouterz&nvidia/nemotron-3-super-120b-a12b:free)	r   reason
blocked_atr   r   r<   queue_idprovidermodel)r   r   r   r   r   r   r   #CONSERVATIVE_PROVIDER_BLOCK_SECONDSr   r{   r   r   )
error_textr<   r   r   resetreset_dtnext_allowedr   s           r   set_provider_blockr  g   s    \\(,,'8'DpuxOeOefkOl{h  X`  em  nq  eq  OT  x{  |E  Nq  |r  xr  w}  w}  w  BN 53==?^c  wC  MS  _b  ck  _l  xD  Mu  v!%(,r   c           
      R    t        t        d| t               |t        |      d       y )NT)r   r   r   r<   r   )r   RUN_BLOCK_PATHrM   r{   )r   r<   r   s      r   set_run_blockr  k   s;    <RV`ftwty  DJ  VY  Zb  Vc  Hd  ,er   c                  `    t        t              } t        | xr | j                  d            | fS )Nr   )r   r  boolr   )r   s    r   run_block_activer	  l   s)    .!$u/M99M*Nu*T#Tr   c                      t        j                  t        d      } t         j                  | _        | j                  d       | S )N   r   zpragma busy_timeout=30000)sqlite3connectr   Rowrow_factoryexecute)cs    r   connr  o   s.    
??2b!1=aiiHc>dmnfnr   c                    | j                  d       | j                  d       dD ]  \  }}| j                  d| d|         | j                  d       | j                  d       | j                  d      j                         D ]  }|d	   }|d
   }|d   }	 t	        |d         }|j                         r|j                  dd      nd}|xs t        ||d         }|xs t        |      }|xs |r|rt        ||      nd }| j                  dt        |d   |d         |||t               |d   f        | j                          y # t        j                  $ r Y :w xY w# t        t        t        f$ r Y vw xY w)Na  create table if not exists files(id integer primary key, relative_path text not null unique, absolute_path text not null, source_area text, document_type text, filename text, size integer, modified_at text, content_hash text, discovered_at text, status text not null, attempts integer default 0, last_attempt text, imported_at text, episode_ref text, last_error text, importer_version text, metadata_json text, supersedes_id integer, unique(relative_path,content_hash))zcreate table if not exists runs(id integer primary key, started_at text, finished_at text, scanned integer, allowed integer, excluded integer, done integer, errors integer)))r   r   )r   r   )r   r   )import_identityr   )
updated_atr   )graphiti_episode_namer   )graphiti_episode_uuidr   )graphiti_statusr   )graphiti_created_atr   )last_verified_atr   )last_error_phaser   zalter table files add column rd   zcreate table if not exists status_history(id integer primary key,queue_id integer,old_status text,new_status text,timestamp text,phase text,run_id text,message text)zhcreate unique index if not exists idx_files_import_key on files(import_key) where import_key is not nullzrselect id,relative_path,absolute_path,content_hash,source_identity,content_version_hash,import_identity from filesr   r   r  absolute_pathr   ignoreencodingr_   r   relative_patha  update files set import_key=coalesce(import_key,?),source_identity=coalesce(source_identity,?),content_version_hash=coalesce(content_version_hash,?),import_identity=coalesce(import_identity,?),graphiti_status=coalesce(graphiti_status,status),updated_at=coalesce(updated_at,?) where id=?content_hashid)r  r  OperationalErrorfetchallr   existsr(   r   r   r   r[   UnicodeErrorr1   r   rM   commit)	r  coltyprowsidcvhiidpr3   s	            r   initr/  q   s$     g  h  ~   H (WSyy0Qse<=(   w  xuvII  K  L  U  U  W AS	
##&<"=c3GXCYs1	#o
1VWV^V^V`Q[['[%Rfh
;!#c/&:;3AaH\]`HaSgj  hi  SV  [^  pF  GJ  KN  pO  dhcf))  m  oy  z}  ~M  zN  OR  Sa  Ob  oc  dg  hk  lo  ps  pu  vy  z~  v  n@  AA  
	!	!'4' ,z	*0D0s$   E	A+E#	E E #E:9E:c           
          | j                  d|f      j                         d   }||k7  r!| j                  d|||t               |||f       | j                  d|||f       y )Nz#select status from files where id=?statusoinsert into status_history(queue_id,old_status,new_status,timestamp,phase,run_id,message) values(?,?,?,?,?,?,?)z6update files set status=?,graphiti_status=? where id=?)r  fetchonerM   )r  qidnewphasemessager<   olds          r   
transitionr9     s    YY4cV<EEGQHaii  J  LO  PS  TW  X[  X]  ^c  dj  kr  Ks  tCSSMRr   c                 0   | j                  d      si S i }| j                  dd      d   j                         D ]  }t        j                  d|      } r@|j                  d      j                         j                  d      ||j                  d      <   |S )Nz---r   r   z^([\w-]+):\s*(.*)$")r   r    r+   r   matchr   ry   )r   outr   r   s       r   frontr>     s    	UBYWWU1a ++- )THH"4(!)qwwqz'')//4s1771:r   c                    t        d      }|j                         sy	 t        j                  |      }|j	                  dt        |       f      j                         d   }|j                          |dkD  S # t        $ r Y yw xY w)N//opt/struktur/graphiti/request-data/requests.dbFz^select count(*) from graphiti_requests where queue_id=? and status in ('started','processing')r   )	r   r%  r  r  r  r{   r3  r|   r   )r   r.  r  ns       r   active_requestrB     s    9:	
5 OOA!!))  %E  GJ  KS  GT  FV  W  `  `  b  cd  e  gh  gn  gn  gp  yz  {|  y|  r|%s   AA5 5	B Bc                 .   t        d      }|j                         sy 	 t        j                  |      }t        j                  |_        |j                  d| f      j                         }|j                          |rt        |      S d S # t        $ r Y y w xY w)Nr@  zselect request_id,status,server_completed,episode_uuid,terminal_reason,recovery_result from graphiti_requests where request_id=?)r   r%  r  r  r  r  r  r3  r|   r   r   )
request_idr.  r  r*  s       r   request_staterE     s    9:	
4OOA!gkkqyy  BD  FP  ER  8S  8\  8\  8^3  `a  `g  `g  `i  B  rv  wz  r{  kL  HL  kL$s   A'B B 	BBc                    | j                  t        |d               }t        |      j                         | j                  j                         |d   vs3t        fd|d   D cg c]  }|j                          c}D              ry|d   r|j                  d   |d   vry| j                  d	d
      }t        |j                               |d   k  st        j                  d|      ryd|fS c c}w )Nvault
extensionsc              3   &   K   | ]  }|v  
 y wr
   r   )r!   r"   ss     r   r#   zallowed.<locals>.<genexpr>   s     4oQ!V4os   deny_patterns)Fdenylistallow_rootsr   )Fznot-allowlistedr   r  r  	min_charszM(?im)^\s*(draft|private|no-graphiti|exclude-from-graphiti)\s*:\s*(true|yes)\b)FcontentT)relative_tor   r{   r   suffixanypartsr(   r   ry   r   r   )r.  cfgr   dr   rJ  s        @r   allowedrV     s    ]]4G%&#c(..*:HHNNC--4oY\]lYmEnTUaggiEn4o1o  yIMciil#m2DDMd;;x;0	N3{##ryy  2B  CD  (E  N]Q	 Fos   'C2
c                    t        t        d         }dx}x}}t               }t               }|j	                  d      D ]  }|j                         s|dz  }t        |j                  |            }	t        |t              \  }
}|
s|dz  }O|dz  }t        |	      }	|j                  |	       |}t        j                  |j                               j                         }t        |      }t!        ||	      }t#        ||      }t%        |	|      }|j'                         }| j)                  d|	f      j+                         }|r$|d   |k(  r| j)                  d|||||d   f       )|r|d	   d
v xr |d    xr |d    xr t-        |d          }|r| j)                  dt        |      |j.                  t1        j2                  |j4                  t6        j8                        j;                         |||||||d   f
       | j)                  d|d   |d	   d|d|d|d    d| d|d    d| d	f       t=        |      }|sdnd}|r
|d	   dk(  rd}| j)                  d|	t        |      |j                  |      j>                  d   |jA                  dd      |jB                  |j.                  t1        j2                  |j4                  t6        j8                        j;                         ||||||||dt        d   tE        jF                  |d      |r|d   nd f        | j)                  d      jI                         D ]7  }|d   |vs||d   z  jK                         r"| j)                  d |d   f       9 | jM                          |||fS )!NrG  r   *r   zBselect * from files where relative_path=? order by id desc limit 1r!  zupdate files set source_identity=coalesce(source_identity,?),content_version_hash=coalesce(content_version_hash,?),import_identity=coalesce(import_identity,?),updated_at=coalesce(updated_at,?) where id=?r"  r1  )
discoveredqueuedretryfailedr  episode_refzupdate files set absolute_path=?,size=?,modified_at=?,content_hash=?,content_version_hash=?,source_identity=?,import_identity=?,import_key=?,updated_at=?,status='queued',graphiti_status='queued',last_error_phase=null,last_error=null where id=?r2  rZ  content_changed_before_importzhash z -> z; import_key r   z; no episode present
supersededdonea3  insert into files(relative_path,absolute_path,source_area,document_type,filename,size,modified_at,content_hash,content_version_hash,source_identity,import_identity,import_key,discovered_at,updated_at,status,attempts,importer_version,metadata_json,supersedes_id) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)typemarkdownimporter_versionFrc   zOselect id,relative_path from files where status not in ("deleted","superseded")r   z,update files set status="deleted" where id=?)'r   CFGrM   r   rglobis_filer{   rP  rV  r   addr   r   rP   r   r   r   r   r   r   r  r3  rB  st_sizer   r/   st_mtimer   r   r   r>  rS  r   rl   rN   rO   r$  r%  r'  )r  r<   rootscanned	allowed_nexcludedr   seenr.  r   okvalr3   r   r,  r+  r-  keystr8  safemetar1  r*  s                           r   scanru     s   
3w<Q6'6)H#%	e
**S/ N	Q	
h	1*'#ammD)*c71S>FBs	Xq[X(Q,),S1488C=c#W^^\_\f\f\hMiMsMsMu1  |P  QT  |Uwz  [n  or  sv  [w  WZ  }S  TW  X[  }\  y|  bl  mp  qr  bs  ^a  xy  x~  x~  x@  uw  FG  FO  FO  PT  VY  U[  F\  Fe  Fe  Fg  BES !#99  [  ]`  ad  eh  ij  kn  os  kt  \u  v  x@HAA  	d#NeJfFf  	dor  tA  pB  lB  	d  KY  Z]  ^b  Zc  Kd  Gd4
II  D  FI  JK  FL  MO  MW  MW  X`  Xn  Xn  oq  oz  oz  {C  {G  {G  XH  XR  XR  XT  UV  WZ  [^  _b  cf  gh  il  mq  ir  Es  tII  BE  FJ  BK  LO  PX  LY  Zb  cd  eD  EK  NS  TW  Xf  Tg  Sh  hl  mn  lo  o|  }@  AM  }N  |O  OS  TW  SX  Xl  Lm  An  oSz$#(<6S]F"8F))  B  DG  HK  LM  HN  OP  O\  O\  ]a  Ob  Oh  Oh  ij  Ok  lp  lt  lt  u{  |F  lG  HI  HN  HN  OQ  OY  OY  Zb  Zp  Zp  qs  q|  q|  }E  }I  }I  ZJ  ZT  ZT  ZV  WX  Y\  ]`  ad  eh  ij  kl  ms  tu  vy  zL  vM  NR  NX  NX  Y]  kp  Nq  B	  ru  vz  r{  H	L	  CM	  N	#N	$ IIghqqs [S%tC4H/H.P.P.RTUT]T]  _M  OR  SW  OX  NZ  U[[GIh..r   c                    dd l }dd l}dd l}ddlm}m} ddlm}  || d         }|j                  dd      }t        j                  d|      }	|	r|	j                  d	      j                         n|j                  }	 |j                  | d
   xs d      }
t        j                  d      xs- d |j                   |j"                        j%                         z   }d |j&                  |t)        | d         z    |j                   |j"                        j%                         z   j+                               j-                         d d z   }| d   xs d| d   z   }| d   xs | d   }| d   xs t/        ||      }i d|ddd|d|d| d   ddt)        | d         z   ddddd |d!|d"| d"   d#| d   d$|	d%| d%   d| d   d&| d&   d'| d   d(t0        d)    |j                   |j"                        j%                         |
d*}|d d+ d,d-d.|z    |j2                  |d/d01       |j                   |j"                        j%                         d2} |j2                  |d/d01      } |j&                  |j+                               j-                         }|j5                  d3      rd4}|j7                  d5d	      d	   }nd,}d }t)         |t0        d6               } |j8                  |      }	 d7}|d8z  }|d9z  }|d:z  }|d;z  }|d<z  }|d=z  }|j;                  |||| d   | d   | d>   d?d |j                   |j"                        j%                          |j                   |j"                        j%                          |j                   |j"                        j%                         d|d   d |d@dA|||f       |j=                          |j?                          dB| d   dCS # |j?                          w xY w)DNr   )r   r   r   r  r   r  r  z(?m)^#\\s+(.+)$r   metadata_jsonz{}OBSIDIAN_IMPORT_RUN_IDrun-zreq-r"     r   r   r   r   r!  r  identity_versionv1canonical_obsidian_pathreconciliation_record_idzlegacy-queue-source_systemobsidian-generalsource_typerb  rD  r<   r   obsidian_pathdocument_titledocument_typesource_arear   zobsidian-graphiti-single-writerrc  )importer_namerc  imported_atrD   i@  obsidianzobsidian-single-writeraa043_FTrI   )rO  sourceactorrl   context	timestampr   youtube:r   z#INSERT INTO graphiti_import_queue (zEsource_type, video_id, obsidian_path, content_hash, file_modified_at,z=graphiti_status, graphiti_attempts, graphiti_last_attempt_at,z@created_at, updated_at, attempt_count, reconciliation_record_id,zDsource_version_id, import_identity, knowledge_unit_identity, action,z+payload_hash, payload_json, post_request_idzE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)modified_atpending
__source__CREATErZ  )r1  r   ) r   rN   r  r   r   pathlibr   r(   r   r   r   ry   stemr   r,   getenvr   r   r   r   r{   rP   r   r   rd  rO   r   r    r  r  r'  r|   )r*  r   rN   r  r   r   r   r.  r3   titlert  r<   rD  r   r   formal_identity
provenancer   payload_jsonpayload_hashsource_type_dbvideo_iddb_pathr  sqls                            r   postr     s   !!+S!"A
++wx+
8CII(#.E&+EKKN  "E4::c/*2d3DYY/0eVlhll8<<>X>b>b>d5dF.'..&3s4y>*ALHLLQYQ]Q]D^DhDhDj*j)r)r)tu  B  CF  DF  G  GJ+,Ts??S1SO-.E#n2EL+,e0FXd0eO?D 	? 		
 	"3#7 	#Oc#d)n$D 	+ 	z 	j 	& 	c,' 	_- 	% 	_- 	N+  	s=)!" 	CI#$ ; 23#x||HLL1;;=+J0 u:)?*4::juM!X\\(,,/99;G 4::gETJL!7>>,"5"5"78BBDL!!*-""((a03#$s4y/"G7??7#D3VVNNQQUU<<VVS HLL&002HLL&002HLL&00212'
 	* 	

CI66 	

s   )CO O,c                    t         j                  j                  t        d   dz   d      5 }t	        j
                  |j                               j                  dg       }d d d        g }g }| d   xs d}D ]  }|j                  d      xs d}t        |      }|xr |j                  d      |k(  }	|j                  d	      d
| d   z   k(  xsI |j                  d      | d   k(  xs0 |j                  d      | d   k(  xr |j                  d      | d   k(  }
|	s|
r|j                  |       |j                  d      | d   k(  }|j                  d      | d   k(  xs |j                  d      | d   k(  }|s|s|j                  |        |r"|s t        dt        t        |            z         |S # 1 sw Y   WxY w)Ngraphiti_url/episodes/inventoryr  r   episodesr  r   source_descriptionrl   	obsidian_r   r  r   r!  r   r}  z`identity_review: partial or historical Graphiti match without exact formal identity; candidates=)urllibrequesturlopenrd  rN   r   rw   r   r   appendRuntimeErrorr{   r   )r*  r	inventorymatchespartialexpected_identityer   rt  exactlegacysame_source	same_paths                r   checkr    s=   nnS01FFrR  KVWcgcmcmnontntnvcwc{c{  }G  HJ  dKYb  K	RW3/@+A+GR*
 1QUU &B"-Nr-RLdhh'89;LL%
%%-S%66
6  K$((<:PRUVbRc:c  Khlhphp  rA  iB  DG  HW  DX  iX  iJ  ]a  ]e  ]e  ft  ]u  wz  {I  wJ  ]J&
fgnnQ'hh()3/@+AA+HH./_1EExRaIbdghwdxIx)Iw~~a01 Gwx{|  AH  }I  yJ  J  	K  K K  Ks   4F55F?c                     t        |xs g       }|sy | yt        |      dk7  ryt        |       dk7  st        t        |            t        |       vryy )Nforce_id_requires_explicit_idsr   exactly_one_force_id_allowed+force_id_must_match_exactly_one_explicit_id)r   r   r*   iter)ids	force_idss     r   validate_force_selectionr    sQ    yB$K	)	Na	'HMT$y/*#c(:	6r   c                 
   t        ||      }|r?t        t        j                  ||xs g t	        t        |xs g             dd             yt        j                  d      xs dt               z   }t        |      }|j                         syt        |xs g       }t               \  }}	t               \  }
}|r5t        t        j                  dd|	d	d             |j                          y
|
r7|s5t        t        j                  dd|dd             |j                          y
d }	 t               }t        |       t               }t        t        j                  ||xs g t!        t"              | dt$        t&        t(        dt+        |
      d
             |rt-        ||      nd\  }}}dx}}|rydj/                  d |D              }|r.|j1                  d| dt3        |            j5                         }nZ|j1                  d| dg |t6        d         j5                         }n)|j1                  dt6        d   | f      j5                         }|D ]  }|d   |v rX|d   t6        d   k\  rI|j1                  d|d   |d   |d   t               d|dt!        |d         z   f       |j9                          t;        ||d   ddd|       |j9                          	 t=        |      }t?        |      dkD  r)t;        ||d   d dd!|       |dz  }|j9                          t?        |      dk(  r|d   }t;        ||d   d"d#d$|       |j1                  d%|jA                  d&      |jA                  d'      t!        |jA                  d(            t               |jA                  d&      |d   f       t;        ||d   d)d"d*|       |dz  }|j9                          t;        ||d   d+d+d,|       |j9                          tC        |      }|j1                  d-t               |d   f       t;        ||d   d.d.d/|       |j1                  d0|jA                  d&      |jA                  d'      |jA                  d&      |d   f       |j9                          t=        |      }t?        |      dk7  rtE        d1      t        j                  d2      t!        |d         k(  r|d   }|j1                  d3|jA                  d&      |jA                  d'      |jA                  d&      t!        |jA                  d(            |d   f       t;        ||d   d.d4d5|jA                  d&       d6|jA                  d'       d7| |       |j9                          tG        d
      t;        ||d   d"d"d8|       |j1                  d9t               |d   f       t;        ||d   d)d"d:|       |dz  }|j9                          |rtM        dP||r|d   d   ndQ       |j1                  dR|t               |||||f       |j9                           	 ||jO                          |j                          t        t        j                  |||||dS             |dk(  rdS dS # tH        $ r}t!        |      }|jK                  d;      r~t;        ||d   d<d=|d d> |       |j1                  d?|d d> d=|d   f       |j9                          tM        d=||d          |dz  }Y d }~ ||jO                          |j                          y
t!        |      jK                  d@      rt;        ||d   |d   dA|d d> |       |j1                  d?|d d> dB|d   f       |j9                          |dz  }tM        dB||d          Y d }~ ||jO                          |j                          y
tQ        |      rtS        |||d         }	t;        ||d   dCd|d d> |       |j1                  dDt               |d d> d|d   f       |j9                          tM        d||d          |dz  }t        t        j                  d|	dEd             Y d }~ ||jO                          |j                          y
|d   dz   }dF}|jK                  dG      rdHn'|jK                  dI      rdJn|jK                  dK      rdLndM}t;        ||d   |||d d> |       |j1                  dN|t               |d d> ||d   f       |j9                          tM        dO||d          |dz  }Y d }~ ||jO                          |j                          yd }~ww xY w# ||jO                          |j                          w xY w)TN)errorr  r  Frc   r   rx  ry  Tr   )r   r   provider_stateK   import_run_block)r   r   	run_state)
scan_enabledrequested_idsri   batch_limitimport_enabledr   r   r   provider_blockedrun_blocked)r   r   r   r   ,c              3       K   | ]  }d   yw)?Nr   )r!   _s     r   r#   zrun.<locals>.<genexpr>9  s     #!##s   z!select * from files where id in (z?) and status not in ("done","duplicate","verified") order by idz>) and status in ("queued","retry") and attempts< ? order by idmax_attemptszZselect * from files where status in ("queued","retry") and attempts< ? order by id limit ?r"  attemptsr2  r1  manual_retry_overridez8Controlled post-write failpoint test; previous attempts=	preflightreconciliationr   	duplicatezmultiple matching episodesverified	reconcilezexisting episode adoptedzupdate files set graphiti_episode_name=?,graphiti_episode_uuid=?,graphiti_created_at=?,last_verified_at=?,episode_ref=?,last_error=null where id=?rl   uuidrg   r`  zexisting episode verified
processingzPOST /episodesz>update files set attempts=attempts+1,last_attempt=? where id=?graphiti_createdzGraphiti POST completedzYupdate files set graphiti_episode_name=?,graphiti_episode_uuid=?,episode_ref=? where id=?z-verification did not find exactly one episodeOBSIDIAN_IMPORT_FAILPOINT_IDzoupdate files set graphiti_episode_name=?,graphiti_episode_uuid=?,episode_ref=?,graphiti_created_at=? where id=?post_write_failpointztest failpoint; episode_name=z; episode_uuid=z	; run_id=zepisode verifiedz>update files set last_verified_at=?,last_error=null where id=?completezidentity_review:reviewidentity_reviewr   z;update files set last_error=?,last_error_phase=? where id=?zmaintenance_mode:maintenance_deferredmaintenance_modeprovider_limitedzJupdate files set last_attempt=?,last_error=?,last_error_phase=? where id=?)r  r   r\  zserver_timeout:server_timeoutz"request_timeout_before_processing:!request_timeout_before_processingztechnical_review:technical_reviewr  zUupdate files set attempts=?,last_attempt=?,last_error=?,last_error_phase=? where id=?source_failurepilot_completednonezcinsert into runs(started_at,finished_at,scanned,allowed,excluded,done,errors) values(?,?,?,?,?,?,?))rk  rV  rm  r`  r_   )*r  rz   rN   rO   sortedr   r,   r  rM   r:   r   r   r	  r   r  r/  r{   r   r   r   r   r  ru  r   r  tupler$  rd  r'  r9  r  r   r   r  r  r   r   r   r  r|   r   r  )limitr  r  r  hold_after_successvalidation_errorr<   lockr   r   	run_blockblock_stater  startedrk  rl  rm  r`  r_   marksrowsr*  existingr  resultr  
text_errorr  r1  r6  s                                 r   runr  *  s   *3	:

,39"PST]TcacPdIeftyz{  EF
		*+=suJvDVtqyB$&');U4::7L^cdrwxy{  |H  |H  |J  SUitzzTK]ju2v  EJ  (K  "L  NR  NZ  NZ  N\  eg(F!DGSUWE$**\bebkik|  AC  }D  SX  jn  St  Ur  Sp  DI  X\  ]f  Xg  6h  +i  %j  Zf  GK  LM  NU  GV  ls  lF  ls  t}  ~F  AB  uB  uy  z@	#s#	#5aii"CE7  KJ  !K  LQ  RU  LV  W  `  `  bii;E7  CA  B  C]  EH  C]  IL  M[  I\  C]  ^  g  g  i			nqt  vD  rE  FK  qL  	M  	V  	V  	X4 wc	$i9ZC4G!GII  BE  FJ  BK  LO  PX  LY  Z]  ^f  Zg  hk  hm  nE  FM  NH  IL  MP  Q[  M\  I]  N]  A^  _  ab  ai  ai  akaD	+k2B7KQXXZZ	3ZH
8}Q#d)K4PQXY[acd[d[afgfnfnfprz
8}a{QJqT:kB\]deghgpgp  rF  HI  HM  HM  NT  HU  VW  V[  V[  \b  Vc  dg  hi  hm  hm  nz  h{  d|  }@  }B  CD  CH  CH  IO  CP  QT  UY  QZ  G[  h\  ^h  ij  kn  os  kt  u{  |F  Gb  cj  ^k  mq  st  mt  mq  vw  v~  v~  v@  BJqT<5EgNPQPXPXPZ9Faii `bebghklphqarsu  AB  CF  GK  CL  M_  `r  sL  MT  vU  WX  W`  W`  a|  ~D  ~H  ~H  IO  ~P  QW  Q[  Q[  \b  Qc  dj  dn  dn  ou  dv  wz  {  w@  }A  WB  DE  DL  DL  DN3ZH
8}a|,[\\	yy/0CD	NB{QAII  P  RS  RW  RW  X^  R_  `a  `e  `e  fl  `m  no  ns  ns  tz  n{  |  @A  @E  @E  FR  @S  |T  UX  Y]  U^  Q_  `#d)./EHefgfkfklrfset  uD  EF  EJ  EJ  KQ  ER  DS  S\  ]d  \e  Gf  gn  o  qr  qy  qy  q{  CM  NP  CQ  }QqT:j1CGLaii  YY  [^  [`  ad  ei  aj  Zk  Ol  nx  yz  {~  C  {D  EK  LV  Wa  bi  nj  lp  rs  ls  lp 88:-(9'SW$q'$-]cd99rt{|  }B  CJ  KT  U]  ^b  ci  tj  k  mn  mu  mu  mw;w> ]AGGI,,.tzzWyHTXbhijky  BC  zCtu  nJ  IJ  nJ  Z	1vJ/0#d)H%6z%47HQSTS\S\  ^[  ]g  hm  im  ]n  o@  AD  EI  AJ  \K  TL  NO  NV  NV  NX  Zg  hy  zA  BE  FJ  BK  ZL  NT  VW  NW  NT  `b ]AGGI,,. 1v,-#d)CM*@ETARSZ[]^]f]f  he  gq  rw  sw  gx  yK  LO  PT  LU  fV  ^W  YZ  Ya  Ya  Yc  ek  mn  en  ek  p}  ~P  QX  Y\  ]a  Yb  pc  ln ]AGGI,,. :&jT;UZ#d)Tfg|  ~H  IN  JN  ~O  PW  >X  Z[  Zc  Zc  dp  ru  rw  xB  CH  DH  xI  J_  `c  dh  `i  qj  Zk  mn  mu  mu  mw  yF  G\  ]d  eh  im  en  yo  qw  yz  qz  qw  |A  BF  BL  BL  ae  ns  Mt  BG  BH  |I  RT ]AGGI,,. _QHx:K`K`arKs7G  ak  av  av  w[  a\  z]  xB  xM  xM  Na  xb  bt  ho  q{  |}  ~A  BF  ~G  HN  OT  U_  `e  ae  Uf  gn  qo  qr  qz  qz  {R  T\  ]`  ]b  cm  ns  os  ct  uz  {~  C  {D  SE  qF  HI  HP  HP  HR  Ta  br  sz  {~  C	  {D	  TE	  G	M	  O	P	  G	P	  G	M	  Y	Z	
 ]AGGI,,.Z	 ]AGGI,,.s{   *F2e AZe B6Ze HZAe 
eA2ee )A3ee Be!e 	B%e.e ee $fc                     t               } t        |        t        d       | j                  d      D cg c]  }t        |d   |d          c} t        d       | j                  d      D cg c]  }t        |d   |d          c} t        d| j                  d	      j	                                | j                          y c c}w c c}w )
Nzstatus:z3select status,count(*) n from files group by statusr1  rA  zareas:zRselect source_area,count(*) n from files group by source_area order by source_arear  z	last_run:z5select finished_at from runs order by id desc limit 1)r  r/  rz   r  r3  r|   )r  r  s     r   r1  r1  `  sF   647E)$!))  UJ  KK  'LQuQx[3'@  'L  NS  T\  N]  HI  HQ  HQ  Rf  Hg  _h  CD  `e  fg  hu  fv  wx  y|  w}  `~  _h  jo  p{  |}  |E  |E  F}  |~  |G  |G  |I  jJ  LM  LS  LS  LU  'L  _hs   C.C	c                     t               } t        |        ddddddd}g d 	 t        j                  j	                  t
        d   dz   d      5 }t        j                  |j                               j                  dg       d d d        fd| j                  d	      j                         D ]  }|d
   rt        |d
         dk7  r|dxx   dz  cc<   |d   dv r|dxx   dz  cc<    |      ng }|d   dk(  rt        |      dk(  r|dxx   dz  cc<   |d   dk(  rt        |      dkD  r|dxx   dz  cc<   t        |      dkD  s|dxx   dz  cc<    	 rt              }ddddddddddd
}| j                  d      D ch c]  }|d   	 }}|D ]R  }	|	j                  d      xs d}
|	j                  d      xs d}t!        j"                  d|
      }|r$t        j                  |j%                  d            ni }|j                  d      }|dk(  r^|j                  d      |vr|d xx   dz  cc<   |j                  d!      s|d"xx   dz  cc<   |j                  d      r|d#xx   dz  cc<   |d$k(  r!|j                  d%      r|d&xx   dz  cc<   |j'                  d'      r|d(xx   dz  cc<   &|j'                  d)      r|d*xx   dz  cc<   F|d+xx   dz  cc<   U ||d,<   t        |      |d-<   t              nd|d/<   t)        fd0| j                  d	      j                         D              |d1<   t)        fd2| j                  d	      j                         D              |d3<   rd4j+                         v rdnd|d5<   rdnd|d6<   d|d7<   d |d8<   rd9n|j                  dd      rdnd|d:<   t-        t        j.                  |             | j1                          |d:   S # 1 sw Y   {xY w# t        $ r}t        |      Y d }~d }~ww xY wc c}w # t        $ r i }Y 9w xY w# t        $ r}	t        |	      |d.<   Y d }	~	]d }	~	ww xY w);Nr   )done_without_episodeepisode_without_queue_referencemultiple_episodesretry_with_episodebad_hashambiguous_episode_referencer  r  r  r   r  c                    g }D ]  }|j                  d      xs d}t        j                  d|      }i }|r$t        j                  |j                  d            ni }|j                  d      d| d   z   k(  sG|j                  d      | d   k(  s0|j                  d      | d	   k(  s|j                  d
      | d
   k(  s|j                  |        |S # t        $ r Y w xY w)Nr  r   r   r   rl   r  r   r  r   r!  )r   r   r   rN   r   r   r   r  )r*  r=  r  r   r   rt  r  s         r   r  zaudit.<locals>.matchesh  s/   # Vaee !'R22995Fr+JQSD'(TZZ
#beeFm[\!222dhh|6LcR^N_6_dhdldlm|d}  @C  DS  @T  eT  Y]  Ya  Ya  bp  Yq  sv  wE  sF  YF  IL  IS  IS  TU  IV	V
 
* Ts   (C	C C zselect * from filesr!  r   r  r   r]  )r  graphitir  r1  r`  r  r[  r  r  )
obsidian_general_without_queueyoutube_without_source_idnew_schema_without_request_id#obsidian_general_without_import_keymultiple_episodes_per_path_hashunknown_source_system%episode_without_traceable_import_pathlegacy_youtubelegacy_obsidianunknown_legacyz9select import_key from files where import_key is not nullr   r  r   rl   r   r  r  r  rD  r  r  zyoutube-research	source_idr  youtube_r  r  r	  r
  episode_inventoryepisode_totalepisode_inventory_errorcheck_successc              3   :   K   | ]  } |      rd  y wNr   r   r!   r*  inventory_errorr  s     r   r#   zaudit.<locals>.<genexpr>  s>       cMil  `o  `w  @G  HK  @Lcd  cMs   check_not_foundc              3   R   K   | ]  }t         |            dkD  sd   y wr  )r   r  s     r   r#   zaudit.<locals>.<genexpr>  sb       hU  or  et  e|  AD  EL  MP  EQ  AR  ST  AT  ij  hUs   '''check_multiplez	timed outcheck_timeoutcheck_unavailablecheck_internal_erroraudit_duration_secondsr   	exit_code)r  r/  r  r  r  rd  rN   r   rw   r   r   r{   r  r$  r   r  r   r   r   r   sumr   rz   rO   r|   )r  r=  rrexcr*  epsinvr"   keysr  r   rl   r   rt  r  r  r  r  s                  @@@r   auditr#  b  s   647TUjk  BC  OP  op  qCt3~~c.12GGPRS  NWYeieoeoprpwpwpyeze~e~  @J  KM  fN[d  N II+,557 -S	^	C$7 8" <c*oq>Po22C8U4VXY4Y4V%-gcl2#]Fs3x{C0F,G,J,G]GC
C0D,Eq,H,EXaZ()1,)->L99#()ahi  QR  uv  OP  yz  LM  `a  st  u#!"+f!ghA,h$h "aee !'R2aeeFm.ArRYYO`acEd1'(TZZ
#b
((?
#6  xxT)3/O+PRS+S+P88L!3'F#G#J#G88L!3'L#Mq#P#M""88K #&A"BA"E"B
#S)9%:A%=%:$c*;&<a&?&<			!	" #SXO 4(7(?c)nQ__b  cMpqpypy  {P  qQ  qZ  qZ  q\  cM  `ML]H^  eh  hU  vw  v  v  @U  vV  v_  v_  va  hU  eU  OR  Sc  Od  q@  EP  Tc  Ti  Ti  Tk  Ek  lm  qr  WZ  [j  Wk  Ra  MN  gh  tw  xK  tL  FG  jm  nD  jE  gk  IL  Me  If  C	R	  ~  ^	a	  ^	e	  ^	e	  f	y	  z	{	  ^	|	  Y	Z	  B
C
  mp  q|  m}tzz#3{+;$;O N  N2#c(/2( i "T 	=s1v56=s   +P 4O?P /Q P.AQ  (P3A%Q .$Q A2Q ?P	P 	P+P&&P+.Q 3Q>Q QQ 	Q'Q""Q'__main__z--once
store_true)actionz--statusz--auditz--scanz--limit   )ra  defaultz--idsz
--force-idz--hold-after-successr  )NTNF)Vargparserr   r   rN   r,   r   r   r   r  r   r?   timeurllib.errorr  urllib.requestr   r   r   r  r   BASEr   r(   rd  r   rM   r   r  r   r   r   r   ro   rZ   r   r  r   r8   r:   r   r   r   compiler   r   r   r   r   r   r   r   r   r   r   r  r  r	  r  r/  r9  r>  rB  rE  rV  ru  r  r  r  r  r1  r#  r   ArgumentParserapadd_argumentr   
parse_argsar  r    force_idr  r  exitr  r  )r"   s   0r   <module>r6     s   z z z z z z z 2 2 	
23T-EWDbDbDd9eSjnorswoxjygi  @n{~"'			2UVYV]V]  _B  CF  WG  )H  #I !  in  ox  oq  ox  ox  yX  Y\  Y`  Y`  a@  AD  YE  oF  iG  Kh  gl  mv  mo  mv  mv  wV  WZ  W^  W^  _~  B  WC  mD  gE  If
}
	R4.. tD[?[  BJ  ^A~%G %GL bE_
 rzzabdbfbfgRS _GF8y7 eUo S /0X7r	5JlU+<X ZHrx|Lboo^hp|oN}  @B  @O  @O  PY  am  @O  @n  pr  p  p  @H  P\  p  p]  _a  _n  _n  ox  ~A  JK  _n  _L  NP  N]  N]  ^e  Nf  hj  hw  hw  xD  hE  GI  GV  GV  Wm  uA  GV  GB  FH  FS  FS  FU  DE  AB  AF  AF  lm  lq  lq  lw  lw  x{  l|  [}  gh  \_  `a  \b  [}  LP  WZ  GH  GQ  GQ  mn  mw  mw  m}  m}  ~A  mB  \C  hi  ]`  ab  ]c  \C  WY  R[  hi  hn  hn  h}  ru  y}  r}  [g  G	  B	  G	  G	  S	T	  S	Z	  S	Z	  H	M	  H	O	  l	m	  l	t	  l	t	  `	f	  `	h	  z	}	  ~		  ~	E
  ~	E
  F
I
  J
V
  W
`
  a
b
  a
u
  a
u
  z	v
  w
  [}  \Cs   7L24L7