ó
    K«jÊ  ã                   ór   • S r SSKrSSKrSSKrSSKrSSKrSrSrSrSr	S r
S r\\\	4S	 jrS
 rS rSS jrg)a  
What the Mac sends Dealer OS so nobody ever needs this Mac's screen to see
why something went wrong (Steven 2026-09-05: the Mac Studio stays behind
the office router, and if Dealer OS needs to see something, the Mac sends
it). One document, POSTed to /api/bidbrain/diagnostics:

  {at, bidbrain_version, logs: [{name, size, modified_at, lines}],
   run_status, site_health, progress, process}

`logs` is the tail of every log file under data/logs (the last LINES lines,
each cut at LINE_CHARS, newest file first, at most FILES of them).
`run_status` and `site_health` are the full records the heartbeat only
carries the first line of. `progress` is the live progress file.

`fingerprint()` is what decides whether anything has changed since the
last send: the size and stamp of every log, plus every run's own "at". The
server sends when that differs (every few minutes at most), when a run has
just finished, and at once when Dealer OS asks (diagnostics.pull).

Pure file reads, no browser, importable by serve.py and the tests.
é    Niô  i�  é   i   c                 óÈ   • [         R                   R                  U [         R                  R                  5      R	                  SS9R                  5       R	                  SS5      $ )Nr   )Úmicrosecondz+00:00ÚZ)ÚdatetimeÚfromtimestampÚtimezoneÚutcÚreplaceÚ	isoformat)Útss    Ú5/Users/stevendouglas/BidBrain/bidbrain/diagnostics.pyÚ_isor   )   sN   € Ü×Ñ×*Ñ*¨2¬x×/@Ñ/@×/DÑ/DÓE×MÑMÐZ[ÐMÐ\×fÑfÓh×pÑpÐqyÐ{~ÓÐó    c           
      ó8  •  [         R                  " U 5      n/ nU H�  n[         R                  R	                  X5      nUR                  S5      (       d$  [         R                  R                  U5      (       d  M^   [         R                  " U5      nUR                  UR                  X4UR                  45        MŸ     UR                  SS9  US[          VVVVs/ s H
  u  pcpGX4Xv4PM     snnnn$ ! [         a    / s $ f = f! [         a     Mô  f = fs  snnnnf )z}Every plain file under the logs folder (no recursion), newest
modified first, at most FILES. Missing folder reads as no logs.Ú.T)ÚreverseN)ÚosÚlistdirÚOSErrorÚpathÚjoinÚ
startswithÚisfileÚstatÚappendÚst_mtimeÚst_sizeÚsortÚFILES)Úlogs_dirÚnamesÚrowsÚnamer   ÚstÚmtimeÚsizes           r   Ú	log_filesr(   -   sî   € ðÜ—
’
˜8Ó$ˆð €DÛˆÜ�w‰w�|‰|˜HÓ+ˆØ�?‰?˜3×Ñ¤r§w¡w§~¡~°d×';Ñ';Ùð	Ü—’˜“ˆBð 	�‰�R—[‘[ $¨b¯j©jÐ9Ö:ñ ð 	‡I�I�d€IÑØEIÈ&Ì5Á\ÖRÂ\Ñ*A¨%°tˆT˜Ó%Á\ÔRÐRøô ó ØŠ	ðûô ó 	Úð	üõ Ss)   ‚C1 Á;DÃD
Ã1D Ã?D Ä
DÄDc                 ó>  •  [        U S5       nUR                  S[        R                  5        UR	                  5       nUR                  [        SXS-
  5      5        UR                  5       nSSS5        WR                  SS5      nUR                  5       nWU:”  a  U(       a  USS nX�* S nU V	s/ s H  n	[        U	5      PM     nn	U V	s/ s H  n	[        U	5      U::  a  U	OU	SU S-   PM     sn	$ ! , (       d  f       NŒ= f! [         a    / s $ f = fs  sn	f s  sn	f )z‹The last `lines` lines of a file, each cut to `line_chars`, read from
the end only. Bytes that are not UTF-8 are replaced, never raised on.Úrbr   Núutf-8r   é   z ...)ÚopenÚseekr   ÚSEEK_ENDÚtellÚmaxÚreadr   ÚdecodeÚ
splitlinesÚ
_printableÚlen)
r   ÚlinesÚ
line_charsÚ
tail_bytesÚfr'   ÚrawÚtextr#   Úrs
             r   Ú
tail_linesr>   B   s  € ðÜ�$˜Ô Ø�F‰F�1”b—k‘kÔ"Ø—6‘6“8ˆDØ�F‰F”3�q˜$Ñ+Ó,Ô-Ø—&‘&“(ˆC÷	 ð �:‰:�g˜yÓ)€DØ�?‰?Ó€DØˆjÓžTØ�A�BˆxˆØ��ˆ=€Dñ
 $(Ó(¢4˜aŒJ�qŽM¡4€DÐ(ÙLPÓQÊDÀq”�Q“˜:Ó%‰A¨1¨[¨j¨>¸FÑ+BÒBÉDÑQÐQ÷# Õûô
 ó ØŠ	ðüò )ùÚQs;   ‚D ŽAC2Á,D Â1DÃ#DÃ2
D Ã<D Ä D ÄDÄDc                 ó2   • SR                  S U  5       5      $ )zGThe line with every control character bar a tab removed (pure, tested).Ú c              3   óR   #   • U  H  oS :X  d  [        U5      S:¼  d  M  Uv •  M     g7f)Ú	é    N)Úord)Ú.0Úchs     r   Ú	<genexpr>Ú_printable.<locals>.<genexpr>\   s    é € ÐA¢˜"¨4£Z´3°r³7¸b±=—2‘2¢ùs   ‚'ž	')r   )Úss    r   r5   r5   Z   s   € à�7‰7ÑA¡ÓAÓAÐAr   c                 ó¢   • [        S [        U 5       5       5      n[        [        S U=(       d    0 R                  5        5       5      5      nX#4$ )z<Small, hashable, changes when a log grows or a run finishes.c              3   óB   #   • U  H  u  pp4X[        U5      4v •  M     g 7f©N)Úint)rE   r$   Ú_pr'   r&   s        r   rG   Úfingerprint.<locals>.<genexpr>a   s    é € Ð[ÒGZÑ.C¨d¸�$œc %›jÕ)ÒGZùs   ‚c              3   ón   #   • U  H+  u  pX=(       d    0 R                  S 5      =(       d    S4v •  M-     g7f)Úatr@   N)Úget)rE   ÚkÚvs      r   rG   rO   b   s)   é € Ð^ÒC]¹4¸1˜ŸW "ŸM™M¨$Ó/×5°2Õ6ÒC]ùs   ‚35)Útupler(   ÚsortedÚitems)r!   Ú
run_statusÚlogsÚrunss       r   Úfingerprintr[   _   sD   € äÑ[ÄyÐQYÔGZÓ[Ó[€DÜ”Ñ^ÀJ×DTÐRT×C[ÑC[ÔC]Ó^Ó^Ó_€DØˆ:Ðr   c                 ó¸  • [         R                  R                  U SS5      n/ n[        U5       H-  u  p‰p«UR	                  XŠ[        U5      [        U	5      S.5        M/     Sn [        USS9 n[        R                  " U5      nSSS5        [        R                  " 5       n[        U5      UU[        U=(       d    0 5      [        U=(       d    0 5      U[         R                  " 5       [        R                   R#                  5       S   U(       a  [        U5      OSU(       a  [%        Xå-
  5      OSS.S	.$ ! , (       d  f       N·= f! [        [        4 a     NÌf = f)
zwThe whole document. Never raises: a log that cannot be read is
simply not in the list, a missing progress file is null.ÚdatarY   )r$   r'   Úmodified_atr7   Nr+   )Úencodingr   )ÚpidÚpythonÚ
started_atÚuptime_seconds)rQ   Úbidbrain_versionrY   rX   Úsite_healthÚprogressÚprocess)r   r   r   r(   r   r   r>   r-   ÚjsonÚloadr   Ú
ValueErrorÚtimeÚdictÚgetpidÚsysÚversionÚsplitrM   )Úhererd   rX   re   Úprogress_pathrb   r!   rY   r$   r   r'   r&   rf   r:   Únows                  r   Úcollectrt   f   s  € ô �w‰w�|‰|˜D &¨&Ó1€HØ€DÜ#,¨XÖ#6Ñˆ�DØ�‰˜TÄÀUÃÔV`ÐaeÓVfÑgÖhñ $7à€HðÜ�-¨'Ò2°aÜ—y’y “|ˆH÷ 3ô �)Š)‹+€Cä�3‹iØ,ØÜ˜:×+¨Ó,Ü˜K×-¨2Ó.Øä—9’9“;Ü—k‘k×'Ñ'Ó)¨!Ñ,Þ.8œ$˜zÔ*¸dÞ7Aœc #Ñ"2Ô3Àtñ	
ñð ÷ 3Õ2ûä”ZÐ ó Ùðús0   Á#
E Á-D5ÂE Ä5
EÄ?E ÅE ÅEÅErL   )Ú__doc__r   rh   r   rn   rk   ÚLINESÚ
LINE_CHARSr    Ú
TAIL_BYTESr   r(   r>   r5   r[   rt   © r   r   Ú<module>rz      s\   ðñó, Û Û 	Û 
Û à€Ø€
Ø	€ð €
ò@òSð* !¨ZÀJô Rò0Bò
õr   