
    PL
j<                        d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 dZ
dZdZd!dZd"dZd#dZd$dZd%dZd&dZd'dZdd gZdS )(u  CLI subcommand: ``hermes send`` — pipe text from shell scripts to any
configured messaging platform (Telegram, Discord, Slack, Signal, SMS, etc.).

This is a thin wrapper around ``tools.send_message_tool.send_message_tool``
that exposes its functionality as a standalone CLI entry point so ops
scripts, cron jobs, CI hooks, and monitoring daemons can reuse the gateway's
already-configured credentials without having to reimplement each platform's
REST API client.

Design notes:

* No LLM, no agent loop — the subcommand just resolves arguments, reads the
  message body, calls the shared tool function, and prints/returns the
  result. It is intentionally fast, cheap, and side-effect-only.
* For platforms that send via bot token (Telegram, Discord, Slack, Signal,
  SMS, WhatsApp-CloudAPI, …) no running gateway is required. The tool
  talks directly to each platform's REST endpoint. For platforms that rely
  on a persistent adapter connection (plugin platforms, Matrix in some
  modes, …) a live gateway is needed; the underlying tool surfaces that
  error to the caller.
* Exit codes follow the classic Unix convention:
    0 — delivery (or list) succeeded
    1 — delivery failed at the platform level
    2 — usage / argument / config error (argparse already uses 2)
    )annotationsN)Path)Optional      
positionalOptional[str]	file_pathreturnc                   | r| S |r|dk    rt           j                                        S 	 t          |                              d          S # t
          t          f$ rD}t          d| d| t           j                   t          j	        t                     Y d}~nd}~ww xY wt           j                                        s"t           j                                        }|r|S dS )u8  Resolve the message body from (in order):

    1. An explicit positional message argument.
    2. ``--file PATH`` or ``--file -`` (where ``-`` means stdin).
    3. Piped stdin when it is not attached to a TTY.

    Returns ``None`` when nothing is available — callers must treat that as
    a usage error.
    -utf-8encodingzhermes send: cannot read z: fileN)sysstdinreadr   	read_textOSErrorUnicodeDecodeErrorprintstderrexit_USAGE_EXITisatty)r   r
   excdatas       7/home/kuhnn/.hermes/hermes-agent/hermes_cli/send_cmd.py_read_message_bodyr!   )   s       "9>>###	"	??,,g,>>>+, 	" 	" 	"@i@@3@@szRRRRH[!!!!!!!!	" 9 y~~ 	K4s   "A B$ :BB$arg_toc                Z    | r(|                                  r|                                  S dS )zAReturn a cleaned ``--to`` value, or ``None`` when nothing is set.N)strip)r"   s    r    _resolve_targetr%   M   s,     &,,.. ||~~4    result_jsonstr	json_modeboolquietintc                  	 | rt          j        |           ni }n# t           j        $ r d| d}Y nw xY w|r$t          t          j        |d                     n|rn|                    d          r%t          d|d          t          j                   no|                    d          r7|                    d	          }|rt          |           n3t          d
           n#t          t          j        |d                     |                    d          rt          S |                    d          rt          S |                    d          rt          S t          S )zPrint the tool result in the requested format and return the exit code.

    The underlying ``send_message_tool`` always returns a JSON string. We
    parse it, decide success/failure, and format accordingly.
    z#invalid JSON from send_message_tool)errorrawr   )indentr.   zhermes send: r   successnotesentskipped)
jsonloadsJSONDecodeErrorr   dumpsgetr   r   _FAILURE_EXIT_SUCCESS_EXIT)r'   r)   r+   payloadr2   s        r    _emit_resultr=   T   s   W-8@$*[)))b W W W B+VVW
  1dj+++,,,,	 1;;w 
	14''"2443:FFFFF[[## 	1;;v&&D df $*WQ///000{{7 {{9 {{9 s    22platform_filterc          
     h   	 ddl m}m} n<# t          $ r/}t	          d| t
          j                   t          cY d}~S d}~ww xY w	  |            }n<# t          $ r/}t	          d| t
          j                   t          cY d}~S d}~ww xY wt          |	                    d          pi           }| r| 
                                                                fd|                                D             }|sJt	          d	|  d
d                    t          |                    pd t
          j                   t          S |}|r2t	          t          j        d|idt"                               t$          S t'          |                                          s4t	          d           t	          d           t	          d           t$          S | t	           |                       t$          S t          |          D ]}||         }	t	          | d           |	st	          d           .|	D ]k}
|
	                    dd          }|
	                    d          p|
	                    d          pd}|r||k    rd| dnd}t	          d| d| |            lt	                       t$          S )u4  Print the channel directory (all configured targets across platforms).

    Uses ``load_directory()`` for structured JSON output and
    ``format_directory_for_display()`` for the human-readable rendering that
    the send_message tool itself shows to the model — keeps the two surfaces
    identical.
    r   )format_directory_for_displayload_directoryz/hermes send: failed to load channel directory: r   Nz/hermes send: failed to read channel directory: 	platformsc                L    i | ] \  }}|                                 k    ||!S  )lower).0kvkeys      r    
<dictcomp>z!_list_targets.<locals>.<dictcomp>   s1    KKKTQ!''))s:J:JAq:J:J:Jr&   z,hermes send: no targets found for platform 'z'. Configured: z, z(none)r   )r0   defaultz@No messaging platforms configured or no channels discovered yet.zBSet one up with `hermes gateway setup`, or run the gateway once soz@channel discovery can populate ~/.hermes/channel_directory.json.:z  (no channels discovered yet)name?idchat_id z  []z  )gateway.channel_directoryr@   rA   	Exceptionr   r   r   r:   dictr9   r$   rE   itemsjoinsortedr5   r8   r(   r;   anyvalues)r>   r)   r@   rA   r   r/   rB   filtered	plat_namechannelschrM   rP   suffixrI   s                 @r    _list_targetsr`      s\   	
 	
 	
 	
 	
 	
 	
 	
 	
    EEECJWWWWn   EEECJWWWW SWW[))/R00I 
##%%++--KKKKY__%6%6KKK 	!J J J#yy	):):;;GxJ JZ   
 ! 	 dj+y1!SIIIJJJy!!"" PQQQRSSSPQQQ **,,---I&&  	Y'ooo 	2333 	3 	3B66&#&&DffTll=bffY&7&7=2G)0LW__%7%%%%"F1y1141112222s7    
A$A A A	
A 
B$BBBNonec                    	 ddl m}  n# t          $ r d} Y nw xY w	 ddlm}  |            }n# t          $ r Y dS w xY w|dz  }| ry|                                re	  | t          |          dd           nH# t          $ r0 	  | t          |          dd	           n# t          $ r Y nw xY wY nt          $ r Y nw xY wddl}|d
z  }|                                sdS 	 ddl	}n# t          $ r Y dS w xY w	 t          |dd          5 }|                    |          pi }ddd           n# 1 swxY w Y   n# t          $ r Y dS w xY w	 ddlm}	  |	|          }n# t          $ r Y nw xY wt          |t                    sdS |                                D ]O\  }
}t          |t          t           t"          t$          f          s.|
|j        v r8t          |          |j        |
<   PdS )u  Populate ``os.environ`` from ``~/.hermes/.env`` AND bridge top-level
    ``config.yaml`` keys into the environment so the underlying gateway
    config loader sees platform credentials and home channel IDs.

    ``send_message_tool`` reads tokens and home-channel IDs via
    ``os.getenv(...)`` on each call. The gateway process does two things at
    startup that ``hermes send`` must replicate when invoked standalone:

    1. ``load_dotenv(~/.hermes/.env)`` — brings bot tokens into the env.
    2. Bridge top-level simple values from ``~/.hermes/config.yaml`` into
       ``os.environ`` (without overriding existing env vars). This is where
       ``TELEGRAM_HOME_CHANNEL`` and friends live when the user saved them
       via ``hermes config set``.

    See ``gateway/run.py`` for the canonical version of this bridge — we
    intentionally reimplement the minimum needed here so ``hermes send``
    doesn't pull in the full gateway module just to resolve a home channel.
    r   )load_dotenvN)get_hermes_homez.envTr   )overrider   zlatin-1zconfig.yamlrr   )_expand_env_vars)dotenvrc   rT   hermes_cli.configrd   existsr(   r   osyamlopen	safe_loadrg   
isinstancerU   rV   r,   floatr*   environ)rc   rd   homeenv_pathrk   config_pathrl   fhr/   rg   rI   vals               r    _load_hermes_envrw      s   (&&&&&&&   555555      f}H 	x(( 		KHwGGGGG! 	 	 	CMMD9MMMMM    	 	 	D	 III&K    +sW555 	+..$$*C	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+   666666s##    c4   IIKK # #S#S%677 	"*c((
3# #s   	 - 
;;A6 6
B;BB;
B*'B;)B**B;/	B;:B;C# #
C10C15D7 D+D7 +D//D7 2D/3D7 7
EE	E 
E('E(argsargparse.Namespacec           	     t   t                       t          | dd          rEt          | dd          }t          |t          | dd                    }t          j        |           t          t          | dd                    }|s4t          dt          j        	           t          j        t                     t          t          | dd          t          | d
d                    }||
                                s4t          dt          j        	           t          j        t                     t          | dd          }|r| d|                                 }ddlm} d||d} ||          }t          |t          | dd          t          | dd                    }t          j        |           dS )z9Entry point wired into the top-level argparse dispatcher.list_targetsFmessageNr5   )r)   tozhermes send: --to PLATFORM[:channel[:thread]] is required
Examples:
  hermes send --to telegram "hello"
  hermes send --to discord:#ops --file report.md
  hermes send --list      # list available targetsr   r   znhermes send: no message provided. Pass text as a positional argument, use --file PATH, or pipe data via stdin.subjectz

r   )send_message_toolsend)actiontargetr|   r+   )r)   r+   )rw   getattrr`   r   r   r%   r   r   r   r!   r$   lstriptools.send_message_toolr   r=   )	rx   r>   	exit_coder   r|   r~   r   	tool_argsresults	            r    cmd_sendr     s     t^U++  "$	488!/WT6SX=Y=YZZZ	WT46677F 	A
 	
 	
 	
 	
 	 i&&fd## G gmmooA	
 	
 	
 	

 	 dIt,,G 544'.."2"244 :99999  I y))F$..dGU++  I
 HYr&   argparse.ArgumentParserc                   |                      ddddt          j                  }|                    dddd	d
           |                    ddd	d           |                    dddd	d           |                    dddd	d           |                    dddddd           |                    dd ddd!"           |                    d#ddd$"           |                    t
          %           |S )&zCreate the ``send`` subparser and return it.

    Kept as a standalone function so the top-level parser builder can wire
    it in next to the other messaging subcommands without cluttering
    ``_parser.py`` or ``main.py``.
    r   zASend a message to a configured platform (scripts, cron jobs, CI).u!  Pipe text from any shell script to any messaging platform Hermes is already configured for. Reuses the gateway's platform credentials (~/.hermes/.env + ~/.hermes/config.yaml) — no LLM, no agent loop, no running gateway required for bot-token platforms like Telegram/Discord/Slack/Signal.a  Examples:
  hermes send --to telegram "deploy finished"
  echo "RAM 92%" | hermes send --to telegram:-1001234567890
  hermes send --to discord:#ops --file /tmp/report.md
  hermes send --to slack:#eng --subject "[CI]" --file build.log
  hermes send --list                  # all platforms
  hermes send --list telegram         # filter by platform

Exit codes: 0 ok, 1 delivery/backend error, 2 usage error.)helpdescriptionepilogformatter_classz-tz--toTARGETNzDelivery target. Format: 'platform' (home channel), 'platform:chat_id', 'platform:chat_id:thread_id', or 'platform:#channel-name'. Examples: telegram, telegram:-1001234567890:17585, discord:#ops, slack:C0123ABCD, signal:+15551234567.)metavarrK   r   r|   rN   z4Message text. If omitted, read from --file or stdin.)nargsrK   r   z-fz--filePATHz4Read message body from PATH. Use '-' to force stdin.z-sz	--subjectLINEz6Prepend a subject/header line before the message body.z-lz--listr{   
store_trueFzRList available targets. Optional positional filter: `hermes send --list telegram`.)destr   rK   r   z-qz--quietz,Suppress stdout on success (exit code only).)r   rK   r   z--jsonz6Emit raw JSON result instead of human-readable output.)func)
add_parserargparseRawDescriptionHelpFormatteradd_argumentset_defaultsr   )
subparsersparsers     r    register_send_subparserr   [  s    ""P<I !<+ #  F0 #     C	     C     E     a     ;     E	     X&&&Mr&   r   r   )r   r	   r
   r	   r   r	   )r"   r	   r   r	   )r'   r(   r)   r*   r+   r*   r   r,   )r>   r	   r)   r*   r   r,   )r   ra   )rx   ry   r   ra   )r   r   )__doc__
__future__r   r   r5   r   pathlibr   typingr   r   r:   r;   r!   r%   r=   r`   rw   r   r   __all__rD   r&   r    <module>r      s&   4 # " " " " "   



             ! ! ! !H   * * * *ZB B B BJL# L# L# L#^C C C CL_ _ _ _D 0
1r&   