
    Jj̍                        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Zddl	Z
ddlmZ ddlmZ ddlmZ ddlmZ ddlmZ  ej        e          ZdZ ej        d	          Zd
ZdZdZdZdefdZde e!         fdZ"de e!         ddfdZ#efde e$         de%ddfdZ&dKdee'         de(e%e%f         fdZ)dLdZ*dZ+dZ,de$dee$         fdZ-de$de.fdZ/efde e$         de%fdZ0de$de$fd Z1dMde$d"e%de$fd#Z2dMde$d"e%de$fd$Z3e G d% d&                      Z4d'e$dee         fd(Z5d'e$dee         fd)Z6d*e$de$fd+Z7ed,d-d'e$d.e%d/e%d0e.de4f
d1Z8d,d2d3e%d0e.de!e$e4f         fd4Z9de$fd5Z:d6d7dd8d3e%d9e$d:ee!e$e4f                  de$fd;Z;d<Z<	 	 dNd3e%d0e.de!e$e$f         fd=Z=dOd>e!e$e$f         d0e.de>fd?Z?e G d@ dA                      Z@d6d!d,dBd3e%dCe%d0e.de@fdDZAde.fdEZBdF ZCdGZDd3e%d0e.ddfdHZEdI ZFdJ ZGdS )Pa  ``hermes debug`` debug tools for Hermes Agent.

Currently supports:
    hermes debug share    Upload debug report (system info + logs) to a
                          paste service and print a shareable URL.
                          By default, log content is run through
                          ``agent.redact.redact_sensitive_text`` with
                          ``force=True`` before upload so credentials in
                          ``~/.hermes/logs/*.log`` are not leaked into
                          the public paste service. Pass ``--no-redact``
                          to disable.
                          Pass ``--nous`` to upload instead to Nous-internal
                          storage (AWS S3) via a signed URL minted by the
                          Nous account service: the bundle is private
                          (viewable only by Nous staff / allowlisted mods via
                          a Google-login-gated viewer) and auto-deletes after
                          14 days, rather than going to a public paste.
    N)	dataclass)Path)Optionalget_hermes_home)atomic_replacez[[hermes debug share: log content redacted at upload time. run with --no-redact to disable]
zW(?<![A-Za-z0-9._%+-])[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}(?![A-Za-z0-9._%+-])https://paste.rs/zhttps://dpaste.com/api/i  i`T  returnc                  *    t                      dz  dz  S )aZ  Path to ``~/.hermes/pastes/pending.json``.

    Each entry: ``{"url": "...", "expire_at": <unix_ts>}``.  Scheduled
    DELETEs used to be handled by spawning a detached Python process per
    paste that slept for 6 hours; those accumulated forever if the user
    ran ``hermes debug share`` repeatedly.

    Deletion is now driven by the gateway's cron ticker
    (``gateway/run.py::_start_cron_ticker``) which calls
    ``_sweep_expired_pastes`` once per hour.  ``hermes debug share`` also
    runs an opportunistic sweep on entry as a fallback for CLI-only users
    who never start the gateway.
    pasteszpending.jsonr        6/home/rurouni/.hermes/hermes-agent/hermes_cli/debug.py_pending_filer   H   s     x'.88r   c                  (   t                      } |                                 sg S 	 t          j        |                     d                    }t          |t                    rd |D             S n"# t          t          t          j	        f$ r Y nw xY wg S )Nutf-8encodingc                 L    g | ]!}t          |t                    r
d |v d|v |"S url	expire_at)
isinstancedict.0es     r   
<listcomp>z!_load_pending.<locals>.<listcomp>a   sG       a&&+0A::+:J:J :J:J:Jr   )
r   existsjsonloads	read_textr   listOSError
ValueErrorJSONDecodeError)pathdatas     r   _load_pendingr)   Y   s    ??D;;== 		z$..'.::;;dD!! 	    	 Z!56   Is   AA0 0BBentriesc                    t                      }	 |j                            dd           |                    d          }|                    t          j        | d          d           t          ||           d S # t          $ r Y d S w xY w)NT)parentsexist_okz	.json.tmp   )indentr   r   )	r   parentmkdirwith_suffix
write_textr    dumpsr   r$   )r*   r'   tmps      r   _save_pendingr6   j   s    ??D$666{++tz'!444wGGGsD!!!!!    	s   A,A> >
BBurlsdelay_secondsc                 8   d | D             }|sdS t                      }d |D             }t          j                    |z   }|D ])}t          ||                    |d                    ||<   *d |                                D             }t          |           dS )zRecord *urls* for deletion at ``now + delay_seconds``.

    Only paste.rs URLs are recorded (dpaste.com auto-expires).  Entries
    are merged into any existing pending.json.
    c                 0    g | ]}t          |          |S r   )_extract_paste_id)r   us     r   r   z#_record_pending.<locals>.<listcomp>}   s&    ===1(9!(<(<=Q===r   Nc                 F    i | ]}|d          t          |d                   S r   )floatr   s     r   
<dictcomp>z#_record_pending.<locals>.<dictcomp>   s*    QQQA%%+*?*?QQQr   g        c                     g | ]
\  }}||d S )r   r   )r   r<   tss      r   r   z#_record_pending.<locals>.<listcomp>   s$    FFFeaab))FFFr   )r)   timemaxgetitemsr6   )r7   r8   paste_rs_urlsr*   by_urlr   r<   mergeds           r   _record_pendingrI   w   s     >====M ooGQQQQQF	m+I 7 7	6::a#5#566q		FFv||~~FFFF&r   nowc                    t                      }|sdS | t          j                    n| }d}g }|D ]}	 t          |                    dd                    }n# t          t
          f$ r Y :w xY w||k    r|                    |           Z|                    dd          }	 t          |          r|dz  }n# t          $ r Y nw xY w|dz   |k    r|                    |           |dz  }|rt          |           |t          |          fS )	u9  Synchronously DELETE any pending pastes whose ``expire_at`` has passed.

    Returns ``(deleted, remaining)``.  Best-effort: failed deletes stay in
    the pending file and will be retried on the next sweep.  Silent —
    intended to be called from every ``hermes debug`` invocation with
    minimal noise.
    )r   r   Nr   r   r       iQ )r)   rB   r>   rD   	TypeErrorr%   appenddelete_paste	Exceptionr6   len)rJ   r*   currentdeleted	remainingentryr   r   s           r   _sweep_expired_pastesrW      sg    ooG v [dikkkcGGI  	eiiQ7788II:& 	 	 	H	wU###iir""	C   1  	 	 	 D	 uw&&U####qLGG !i   S^^$$s#   #AA+*A+!B77
CCc                  F    	 t                       dS # t          $ r Y dS w xY w)zBAttempt pending-paste cleanup without letting /debug fail offline.N)rW   rQ   r   r   r   !_best_effort_sweep_expired_pastesrY      s;       s    
  un  ⚠️  This will upload system info + logs to a PUBLIC paste service.

Cryptographic secrets (API keys, tokens, passwords) are redacted before
upload, but the following personal data is NOT redacted and will be public:
  • Your display name and persistent platform user ID
  • Verbatim content of your recent messages (prompts, responses, tool output)
  • Local filesystem paths
  • Any other PII present in the logs

The resulting URL is public to anyone who has the link. Pastes auto-delete
after 6 hours, but may be archived by third parties in the meantime.

Use --local to view the report without uploading.
u  ⚠️ **Privacy notice:** This uploads system info + recent log tails (may contain conversation fragments) to a public paste service. Full logs are NOT included from the gateway — use `hermes debug share` from the CLI for full log uploads.
Pastes auto-delete after 6 hours.r   c                     |                                                      d          } dD ]0}|                     |          r| t          |          d         c S 1dS )zExtract the paste ID from a paste.rs or dpaste.com URL.

    Returns the ID string, or None if the URL doesn't match a known service.
    /)r	   zhttp://paste.rs/N)striprstrip
startswithrR   )r   prefixs     r   r;   r;      sg    
 ))++

S
!
!C; % %>>&!! 	%s6{{||$$$$	%4r   c                 F   t          |           }|st          d|            t           | }t          j                            |dddi          }t          j                            |d          5 }d|j        cxk    od	k     nc cd
d
d
           S # 1 swxY w Y   d
S )zDelete a paste from paste.rs.  Returns True on success.

    Only paste.rs supports unauthenticated DELETE.  dpaste.com pastes
    expire automatically but cannot be deleted via API.
    z7Cannot delete: only paste.rs URLs are supported.  Got: DELETE
User-Agenthermes-agent/debug-share)methodheaders   timeout   i,  N)r;   r%   _PASTE_RS_URLurllibrequestRequesturlopenstatus)r   paste_idtargetreqresps        r   rP   rP      s    !%%H 
KcKK
 
 	
 )x))F
.
 
 x9: !  C 
		R		0	0 (Ddk''''C''''( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (s   4BBBc                 (    t          | |           dS )u  Record *urls* for deletion ``delay_seconds`` from now.

    Previously this spawned a detached Python subprocess per call that slept
    for 6 hours and then issued DELETE requests.  Those subprocesses leaked —
    every ``hermes debug share`` invocation added ~20 MB of resident Python
    interpreters that never exited until the sleep completed.

    The replacement is stateless: we append to ``~/.hermes/pastes/pending.json``
    and the gateway's cron ticker sweeps expired entries once per hour.
    ``hermes debug share`` also runs an opportunistic sweep as a fallback
    for CLI-only users.  If neither runs again, paste.rs's own retention
    policy handles cleanup.
    )r8   N)rI   )r7   r8   s     r   _schedule_auto_deleteru      s     D666666r   contentc                    |                      d          }t          j                            t          |dddd          }t          j                            |d          5 }|                                                    d                                          }d	d	d	           n# 1 swxY w Y   |	                    d
          st          d|d	d                    |S )zvUpload to paste.rs.  Returns the paste URL.

    paste.rs accepts a plain POST body and returns the URL directly.
    r   POSTztext/plain; charset=utf-8rc   zContent-Typerb   r(   rd   re   rf   rg   Nhttpz#Unexpected response from paste.rs: ri   )encoderk   rl   rm   rj   rn   readdecoder\   r^   r%   )rv   r(   rr   rs   r   s        r   _upload_paste_rsr     s   
 >>'""D
.
 
 D74
 
 !  C 
		R		0	0 2Diikk  ))//112 2 2 2 2 2 2 2 2 2 2 2 2 2 2>>&!! LJs4C4yJJKKKJs   ":B((B,/B,   expiry_daysc                 z   ddt           dt           dt           ffd} |d|            |dd          z    |d	t          |                    z   d
 dz                       d          }t          j                            t
          |dd dd          }t          j                            |d          5 }|                                                    d          	                                }ddd           n# 1 swxY w Y   |
                    d          st          d|dd                    |S )z\Upload to dpaste.com.  Returns the paste URL.

    dpaste.com uses multipart form data.
    z----HermesDebugBoundary9f3cnamevaluer
   c                     d d|  d| dS )N--z(
Content-Disposition: form-data; name="z"

z
r   )r   r   boundarys     r   _fieldz"_upload_dpaste_com.<locals>._field,  s:      59    	
r   rv   syntaxtextr   r   z--
r   rx   zmultipart/form-data; boundary=rc   ry   rz   rf   rg   Nr{   z%Unexpected response from dpaste.com: ri   )strr|   rk   rl   rm   _DPASTE_COM_URLrn   r}   r~   r\   r^   r%   )rv   r   r   bodyrr   rs   r   r   s          @r   _upload_dpaste_comr   %  s   
 -H
S 
 
 
 
 
 
 
 
 	y'""
&6
"
"	#
&K 0 0
1
1	2  x


	  fWoo 	 .
 
 d6GXGG4
 
 !  C 
		R		0	0 2Diikk  ))//112 2 2 2 2 2 2 2 2 2 2 2 2 2 2>>&!! NLTcTLLMMMJs   ;:DDDc                 P   g }	 t          |           S # t          $ r"}|                    d|            Y d}~nd}~ww xY w	 t          | |          S # t          $ r"}|                    d|            Y d}~nd}~ww xY wt	          dd                    |          z             )zUpload *content* to a paste service, trying paste.rs then dpaste.com.

    Returns the paste URL on success, raises on total failure.
    z
paste.rs: Nr   zdpaste.com: z)Failed to upload to any paste service:
  z
  )r   rQ   rO   r   RuntimeErrorjoin)rv   r   errorsexcs       r   upload_to_pastebinr   I  s    
 F*((( * * *(3(())))))))*,!'{CCCC , , ,*S**++++++++, 4v{{67J7JJ  s(    
?:?A 
B A;;B c                   J    e Zd ZU dZee         ed<   eed<   ee         ed<   dS )LogSnapshotz7Single-read snapshot of a log file used by debug-share.r'   	tail_text	full_textN)__name__
__module____qualname____doc__r   r   __annotations__r   r   r   r   r   r   f  sA         AA
4.NNN}r   r   log_namec                 h    ddl m} |                    |           }|rt                      dz  |z  ndS )z@Where *log_name* would live if present. Doesn't check existence.r   )	LOG_FILESlogsN)hermes_cli.logsr   rD   r   )r   r   filenames      r   _primary_log_pathr   o  sE    ))))))}}X&&H6>HO&11DHr   c                    t          |           }|dS |                                r|                                j        dk    r|S |j        |j         dz  }|                                r|                                j        dk    r|S dS )zFind the log file for *log_name*, falling back to the .1 rotation.

    Returns the first non-empty candidate (primary, then .1), or None.
    Callers distinguish 'empty primary' from 'truly missing' via
    :func:`_primary_log_path`.
    Nr   z.1)r   r   statst_sizer0   r   )r   primaryrotateds      r   _resolve_log_pathr   w  s      ))Gt~~ GLLNN2Q66n',2222G~~ GLLNN2Q664r   r   c                 f    | s| S ddl m}  || d          } t                              d|           S )a  Run ``redact_sensitive_text`` with ``force=True`` over upload-bound text.

    Uses ``force=True`` so redaction fires regardless of the operator's
    ``security.redact_secrets`` setting. The local on-disk log file is
    not modified; only the in-memory copy headed for the public paste
    service is sanitized. Returns the redacted text (or the original
    when empty / non-string).
    r   )redact_sensitive_textT)forcez[REDACTED_EMAIL])agent.redactr   _EMAIL_ADDRESS_REsub)r   r   s     r   _redact_log_textr     sO      222222  T222D  !3T:::r   T)	max_bytesredact
tail_linesr   r   c                   t          |           }|;t          |           }|r|                                rdnd}t          d|d          S 	 |                                j        }|dk    rt          |dd          S t          |d          5 }||k    r|                                }	d}
nd}|}g }d}d}|dk    r||k     s	||d	z   k    r||d
z  k     rt          ||          }||z  }|	                    |           |                    |          }|
                    d|           |t          |          z  }||                    d          z  }t          |d
z  d          }|dk    r||k     s	||d	z   k    r	||d
z  k     d                    |          }	|dk    }
ddd           n# 1 swxY w Y   |	}|
rht          |          |k    rUt          |          |z
  }|dk    o||d	z
  |         dk    }||d         }|s d|v r|                    dd	          d	         }|	                    dd          }d                    |                    d          | d                                       d          }|                    dd          }|
rd|dz   d| }|rt%          |          }t%          |          }t          |||          S # t&          $ r }t          |d| dd          cY d}~S d}~ww xY w)aM  Capture a log once and derive summary/full-log views from it.

    The report tail and standalone log upload must come from the same file
    snapshot. Otherwise a rotation/truncate between reads can make the report
    look newer than the uploaded ``agent.log`` paste.

    When ``redact`` is True (the default), both ``tail_text`` and
    ``full_text`` are run through ``_redact_log_text`` so the snapshot
    returned is upload-safe. The on-disk log file is never modified.
    Pass ``redact=False`` to capture original log content (used by
    ``hermes debug share --no-redact``).
    Nz(file empty)z(file not found))r'   r   r   r   rbFi    rM   r.      
i   r   r   replace)r   rL   T)keepends
u!   [... truncated — showing last ~i   zKB ...]
z(error reading: ))r   r   r   r   r   r   openr}   minseekinsertrR   countr   splitr~   
splitlinesr]   r   rQ   )r   r   r   r   log_pathr   tailsizefraw	truncated
chunk_sizeposchunkstotalnewline_count	read_sizechunkfull_rawcuton_boundaryall_textr   r   r   s                            r   _capture_log_snapshotr     s   & !**H#H--!(UW^^-=-=U~~CUEEEE:_}}&199HRVWWWW(D!! 	$Qy  ffhh!		
 "
&( !Agg59#4#4VW8W8W]benqrer]r]r #J 4 4I9$CFF3KKKFF9--EMM!U+++SZZ'E!U[[%7%77M!$Z!^U!;!;J Agg59#4#4VW8W8W]benqrer]r]r hhv&&!G	3	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$6  		7X22h--)+C
 'FhsQw}&=&FK~H 75H#4#4#>>%33A6::gi:88GGH///>>
{||LMMTTUYZZ	OOGIO>>	 	dcI<MccXaccI 	4(33I(33IISSSS _ _ _4Ms4M4M4MY]^^^^^^^^^_sJ   0K ?K DF, K ,F00K 3F04D K 
K?K:4K?:K?r   	log_linesc          	          t          | d          }t          d| |          t          d||          t          d||          t          d||          t          d||          dS )	zCapture all logs used by debug-share exactly once.

    ``redact`` is forwarded to each ``_capture_log_snapshot`` call so all
    captured logs share the same redaction policy for a given run.
    d   agent)r   r   r   gatewayguidesktop)r   r   r   r   r   )r   r   )r   r   errors_liness      r   _capture_default_log_snapshotsr     s     y#&&L&	&
 
 
 (f
 
 
 ),v
 
 
 %l6
 
 
 ),v
 
 
  r   c                  ,   ddl m}   G d d          }t          j        }t	          j                    xt          _        }	  |  |                       n# t          $ r Y nw xY w|t          _        n# |t          _        w xY w|                                S )z6Run ``hermes dump`` and return its stdout as a string.r   )run_dumpc                       e Zd ZdZdS ) _capture_dump.<locals>._FakeArgsFN)r   r   r   	show_keysr   r   r   	_FakeArgsr     s        			r   r   )hermes_cli.dumpr   sysstdoutioStringIO
SystemExitgetvalue)r   r   
old_stdoutcaptures       r   _capture_dumpr     s    ((((((        J;==(CJ      

Z
s*    A A1 
A!A1  A!!A1 1A?ri   rL   r   	dump_textlog_snapshotsr   r   c                    t          j                    }|st                      }|                    |           |t	          |           }|                    d           |                    d|  d           |                    |d         j                   |                    d           t          | d          }|                    d| d           |                    |d         j                   |                    d           |                    d	| d           |                    |d
         j                   |                    d           |                    d| d           |                    |d         j                   |                    d           |                    d| d           |                    |d         j                   |                    d           |                                S )aI  Build the summary debug report: system dump + log tails.

    Parameters
    ----------
    log_lines
        Number of recent lines to include per log file.
    dump_text
        Pre-captured dump output.  If empty, ``hermes dump`` is run
        internally.

    Returns the report as a plain-text string ready for upload.
    N

z--- agent.log (last z lines) ---
r   r   z--- errors.log (last r   z--- gateway.log (last r   z--- gui.log (last r   z--- desktop.log (last r   r   )r   r   r   writer   r   r   r   )r   r   r   bufr   s        r   collect_debug_reportr   &  s   $ +--C $!OO	IIi6yAA IIfII=Y===>>>IImG$.///IIfy#&&LIIAlAAABBBIImH%/000IIfIIB|BBBCCCIImI&0111IIfII><>>>???IImE",---IIfIIB|BBBCCCIImI&0111IIdOOO<<>>r   zhermes-debug-share/1c                    t                      }t          | |          }t          | ||          }|d         j        }|d         j        }|d         j        }|d         j        }|r|dz   |z   }|r|dz   |z   }|r|d	z   |z   }|r|d
z   |z   }|r:t          |z   }|r
t          |z   }|r
t          |z   }|r
t          |z   }|r
t          |z   }d|i}	|r||	d<   |r||	d<   |r||	d<   |r||	d<   |	S )u  Collect the debug report + full logs as a label→text mapping.

    Returns ``{"report": ..., "agent.log": ..., "gateway.log": ...,
    "desktop.log": ...}`` where each value is the already-redacted (when
    ``redact`` is True) text that would be uploaded.  Keys for logs that are
    absent/empty are simply omitted.

    This is the single source of collection + redaction shared by both
    destinations: the paste.rs path (:func:`build_debug_share`) and the
    Nous-S3 path (``--nous``).  Centralising it guarantees the Nous bundle is
    built from the *same* force-redacted snapshots as the public paste path —
    redaction is the safety boundary, so the Nous path must never see raw
    logs.

    The dump header is prepended to each full log (mirroring the historical
    paste behaviour) so every file is self-contained, and the redaction
    banner is prepended when ``redact`` is True.
    r   r   r   r   r   r   z

--- full agent.log ---
z

--- full gateway.log ---
z

--- full gui.log ---
z

--- full desktop.log ---
report	agent.loggateway.loggui.logdesktop.log)r   r   r   r   _REDACTION_BANNER)
r   r   r   r   r   	agent_loggateway_loggui_logdesktop_logbundles
             r   collect_share_bundler  d  s   , I29VLLLM!#  F
 g&0I	*4KE",G	*4K  K >>J	 Q"BB[P E::WD Q"BB[P  	:"V+ 	6)I5I 	:+k9K 	2''1G 	:+k9K&/F ('{ , +} $#y , +}Mr   r  c                 &   t           j                             t           j        j                                                  }t
          t          |          || d}t          j        t          j
        |                              d                    S )a<  Gzip-compress a :func:`collect_share_bundle` mapping into the Nous envelope.

    The JSON shape is what the discord-support viewer (Repo 3) parses::

        {"format": "hermes-debug-share/1",
         "redacted": <bool>,
         "created": <iso8601>,
         "files": {"report": ..., "agent.log": ..., ...}}
    )formatredactedcreatedfilesr   )datetimerJ   timezoneutc	isoformat_NOUS_BUNDLE_FORMATboolgzipcompressr    r4   r|   )r  r   r  envelopes       r   build_nous_bundler    st     ##H$5$9::DDFFG%LL	 H =H--44W==>>>r   c                   J    e Zd ZU dZeed<   eed<   eed<   eed<   dZ	e
ed<   dS )	DebugShareResultzStructured outcome of a ``debug share`` upload.

    Returned by :func:`build_debug_share` so non-CLI callers (the dashboard
    web server, gateway) can render the uploaded paste URLs as real links
    instead of scraping printed text.
    r7   failuresr  auto_delete_secondsrL   r   N)r   r   r   r   r   r   r#   r  intr   r   r   r   r   r  r    sU           JJJNNNNNNFCr   r  r   expiryr   r  c                    t                       t          | |          }|rt                              d           |d         }i }g }t	          ||          |d<   dD ]`}|                    |          }|s	 t	          ||          ||<   0# t          $ r$}	|                    | d|	            Y d}	~	Yd}	~	ww xY wt          t          |
                                                     t          |||t          |	          S )
u  Collect the debug report + full logs, upload each, return the URLs.

    This is the shared core behind ``hermes debug share`` (CLI) and the
    dashboard ``POST /api/ops/debug-share`` endpoint. It performs blocking
    network I/O (paste uploads) — callers inside an event loop must run it in
    a worker thread.

    The summary report upload is required: on failure this raises
    ``RuntimeError``. Full-log uploads are best-effort; their errors are
    collected into ``failures`` rather than raised.
    r   r   zOhermes debug share: applied force-mode redaction to log snapshots before uploadr   r   Report)r   r   r   r   : N)r7   r  r  r  r   )rY   r  loggerinfor   rD   rQ   rO   ru   r#   valuesr  _AUTO_DELETE_SECONDS)
r   r  r   r  r   r7   r  labelrv   r   s
             r   build_debug_sharer$    sR   " &''' "IfEEEF 
]	
 	
 	
 HFDH (FCCCDN H / /**U## 		/,W&IIIDKK 	/ 	/ 	/OOu----........	/ $t{{}}--...0   s   9B
B<B77B<c                    t          t          | dd                    rdS t          j                                        s/t          dt          j                   t          j        d           	 t          d          	                                
                                }n# t          t          f$ r d}Y nw xY w|d	vrt          d
           dS dS )u  Require explicit consent before any debug-share upload.

    The privacy notice is printed by the caller. This gates the actual
    upload: with ``--yes`` (or ``-y``) we proceed unprompted; otherwise we
    ask an interactive ``[y/N]`` question. In a non-interactive context
    (no TTY on stdin — scripts, CI, piped input) we refuse rather than
    hang or upload silently, so debug data can't be exposed without a
    deliberate ``--yes``.

    Returns True to proceed with the upload, False to abort.
    yesFTzERROR: Non-interactive mode requires --yes to confirm upload.
       This prevents accidental exposure of personal data.
       Use --local to view the report without uploading.filerM   zUpload debug report? [y/N] rL   )yr&  zAborted.)r  getattrr   stdinisattyprintstderrexitinputr\   lowerEOFErrorKeyboardInterrupt)argsanswers     r   _confirm_uploadr6    s     GD%''(( t9 G 		
 	
 	
 	
 	455;;==CCEE'(   \!!ju4s   /3B# #B98B9c                 0   t          | dd          }t          | dd          }t          | dd          }t          | dd          }t          | dd           }|rt                       t          d	           t          ||
          }t          |d                    dD ]^\  }}|                    |          }	|	rBt          dd            t          |           t          d d           t          |	           _dS |rt          | ||
           dS t          t                     t          |           sdS t          d	           t          d           	 t          |||          }
nX# t          $ rK}t          d| t          j                   t          d           t          j        d           Y d}~nd}~ww xY wt          d |
j        D                       }t          d           |
j                                        D ]\  }}t          d|d| d|            |
j        r+t          dd                    |
j                   d           |
j        dz  }t          d| d            t          d!           t          d"           dS )#z:Collect debug report + full logs, upload each, print URLs.linesri   expirer   localFnous	no_redactCollecting debug report...r  r   ))zFULL agent.logr   )zFULL gateway.logr   )zFULL gui.logr   )zFULL desktop.logr   r   z<============================================================r   NzUploading...r  z
Upload failed: r'  z?
Run `hermes debug share --local` to print the report instead.
rM   c              3   4   K   | ]}t          |          V  d S N)rR   )r   ks     r   	<genexpr>z"run_debug_share.<locals>.<genexpr>a  s(      22c!ff222222r   z
Debug report uploaded:z  <z
  (failed to upload: z, r   i  u!   
⏱  Pastes will auto-delete in z hours.z)To delete now:  hermes debug delete <url>z4
Share these links with the Hermes team for support.)r*  rY   r-  r  rD   _run_debug_share_nous_PRIVACY_NOTICEr6  r$  r   r   r.  r/  rC   r7   rE   r  r   r  )r4  r   r  
local_onlyr;  r   r  titler#  r   resultr   label_widthr   hourss                  r   run_debug_sharerJ  -  s   gs++IT8Q''Fw..J4''D{E222F  	*+++*+++%	&IIIfX
 	 	LE5 ::e$$D 'X''(((eooo&&&d diGGGG	/4   	
&'''	.	"
 
 

    '#''cj9999QRRR 22fk22222K	
$%%%k'')) 2 2
s050;00003001111 GE		&/(B(BEEEFFF&$.E	
=u
=
=
=>>> 

5666	
ABBBBBs   E, ,
G6AF<<Guw  ⚠️  --nous: This uploads your debug bundle to Nous-INTERNAL storage (AWS S3),
    NOT a public paste service. The following is included:
  • System info (OS, Python/Hermes version, provider, which API keys are
    configured — NOT the actual keys)
  • Full agent.log, gateway.log, and desktop.log (up to 512 KB each — likely
    contains conversation content, tool outputs, and file paths)

  • The bundle is viewable only by Nous staff (and allowlisted Discord mods)
    via a Google-login-gated viewer.
  • It is NOT a public paste — there is no public URL to the contents.
  • It auto-deletes after 14 days.
c                   ddl m} t          t                     t	          |           sdS |st          d           t          d           t                       t          ||          }|rt                              d           t          ||          }t          d	           	  ||          }nJ# t          $ r=}t          d
| dt          j                   t          j        d           Y d}~nd}~ww xY w|                    d          p|                    d          }t          d           |rt          d|            n't          d|                    dd           d           |                    d          p|                    d          }	|	rt          d|	 d           nt          d           t          d           dS )aH  Handle ``hermes debug share --nous``: upload the bundle to Nous-S3.

    Collects the same force-redacted bundle as the paste path, gzips it into
    the Nous envelope, requests a signed URL from NAS, uploads, and prints the
    private viewer link. On any failure falls back to a clear error that
    suggests ``--local``.
    r   )share_to_nousNuU   ⚠️  --no-redact is set: secrets in your logs will NOT be redacted before upload.
r=  r  zEhermes debug share --nous: applied force-mode redaction before uploadr   z(Uploading to Nous diagnostics storage...z
Nous upload failed: z

The Nous diagnostics service may be unavailable or not yet provisioned.
Run `hermes debug share --local` to print the report instead, or `hermes debug share` to upload to a public paste service.
r'  rM   viewUrlview_urlz)
Debug bundle uploaded to Nous (private):z  View URL  z$  (no view URL returned; upload id: id?r   	expiresAt
expires_atu   
⏱  Auto-deletes at z (14-day retention).u!   
⏱  Auto-deletes after 14 days.u_   
Share this private link with the Nous team — only Nous staff (via Google login) can open it.)hermes_cli.diagnostics_uploadrL  r-  _NOUS_PRIVACY_NOTICEr6  rY   r  r  r   r  rQ   r   r.  r/  rD   )
r4  r   r   rL  r  blobresr   rN  rR  s
             r   rC  rC    sG    <;;;;;	
4    
	
 	
 	
 

&'''%'''!IfEEEF 
S	
 	
 	
 VF333D	
4555mD!! 	 	 	MS M M M
 	
 	
 	
 	
 		 wwy!!8SWWZ%8%8H	
6777 L'X''((((JSWWT35G5GJJJKKK%%>)>)>J 4H
HHHIIII2333		*    s   (B4 4
C;>3C66C;c                    t          | dg           }|s t          d           t          d           dS |D ]}	 t          |          }|rt          d|            nt          d| d           ;# t          $ r}t          d|            Y d}~\d}~wt          $ r}t          d	| d
|            Y d}~d}~ww xY wdS )z1Delete one or more paste URLs uploaded by /debug.r7   z,Usage: hermes debug delete <url> [<url> ...]z;  Deletes paste.rs pastes uploaded by 'hermes debug share'.Nu     ✓ Deleted: u     ✗ Failed to delete: z (unexpected response)u     ✗ u     ✗ Could not delete r  )r*  r-  rP   r%   rQ   )r4  r7   r   okr   s        r   run_debug_deleterY    s-   4$$D <===KLLL 
: 
:		:c""B N---....LLLLMMM 	" 	" 	".3..!!!!!!!! 	: 	: 	:8C8838899999999	:
: 
:s#   7A11
B>;BB>B99B>c                    	 t                       n# t          $ r Y nw xY wt          | dd          }|dk    rt          |            dS |dk    rt	          |            dS t          d           t                       t          d           t          d           t          d           t                       t          d	           t          d
           t          d           t          d           t          d           t          d           t          d           t                       t          d           t          d           dS )zRoute debug subcommands.debug_commandNsharedeletezUsage: hermes debug <command>z	Commands:z?  share    Upload debug report to a paste service and print URLz-  delete   Delete a previously uploaded pastezOptions (share):z<  --lines N    Number of log lines to include (default: 200)z0  --expire N   Paste expiry in days (default: 7)z8  --local      Print report locally instead of uploadingzD  --nous       Upload to Nous-internal storage (private, staff-only,zA               auto-deletes in 14 days) instead of a public pastezE  --no-redact  Disable upload-time secret redaction (default: redact)zOptions (delete):z/  <url> ...    One or more paste URLs to delete)rW   rQ   r*  rJ  rY  r-  )r4  subcmds     r   	run_debugr_    sl       T?D11F	8		 	-...kOPPP=>>> !!!LMMM@AAAHIIITUUUQRRRUVVV!"""?@@@@@s    
r?  )r
   N)r   )ri   T)T)Hr   r
  r  r   r    loggingrer   rB   urllib.requestrk   dataclassesr   pathlibr   typingr   hermes_constantsr   utilsr   	getLoggerr   r  r   compiler   rj   r   _MAX_LOG_BYTESr"  r   r#   r   r)   r6   r   r  rI   r>   tuplerW   rY   rD  _GATEWAY_PRIVACY_NOTICEr;   r  rP   ru   r   r   r   r   r   r   r   r   r   r   r   r  r  bytesr  r  r$  r6  rJ  rT  rC  rY  r_  r   r   r   <module>rn     sp   &   				   				 



      ! ! ! ! ! !             , , , , , ,            		8	$	$) 
 BJ   $+   9t 9 9 9 9"tDz    "
4: 
$ 
 
 
 
 ;O  $s) C SW    (,% ,%x ,%%S/ ,% ,% ,% ,%^   "( 	3 	8C= 	 	 	 	(c (d ( ( ( (* AU 7 7S	 7# 7 7 7 7"c c    (! ! !# !c ! ! ! !H  # c    :        I I I I I I     *;3 ;3 ; ; ; ;* $S_ S_ S_S_ S_ 	S_
 S_ S_ S_ S_ S_n '+  #	#{
   @s    * 6:	2 2 22 2 Dk!123	2
 	2 2 2 2v -  B BBB 
#s(^B B B BJ? ?d38n ?d ?e ? ? ? ?0        " 	9 9 99 9 	9
 9 9 9 9xT    @BC BC BCJ :c :4 :D : : : :z: : :*"A "A "A "A "Ar   