60 lines
3.0 KiB
Bash
Executable File
60 lines
3.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# send-message.sh — dispatch text through tmux; application acceptance unknown.
|
|
#
|
|
# Usage: send-message.sh [-L socket] -t target {-m message|-f file|-b base64}
|
|
# With no message option, reads stdin. Requires bash, tmux and base64.
|
|
# -r N is compatibility-only: no automatic retries or extra Enter presses.
|
|
# -v prints transport metadata only, never a captured private transcript.
|
|
# Exit 0: tmux accepted buffer load, paste and one Enter command.
|
|
# Exit 1: target resolution failed. Exit 2: transport failed/partial/uncertain.
|
|
# Exit 3: invalid usage/input. No exit establishes application acknowledgement.
|
|
set -uo pipefail
|
|
SOCKET_NAME=""; TARGET=""; MSG=""; FILE=""; B64=""; VERBOSE=0
|
|
usage() { printf '%s\n' 'Usage: send-message.sh [-L socket] -t target [-m message|-f file|-b base64] [-r N] [-v]' 'Exit 0 = transport dispatched; application acceptance unknown. No automatic retries.'; exit "${1:-3}"; }
|
|
while getopts 'L:t:m:f:b:r:vh' o; do
|
|
case "$o" in
|
|
L) SOCKET_NAME=$OPTARG ;; t) TARGET=$OPTARG ;; m) MSG=$OPTARG ;;
|
|
f) FILE=$OPTARG ;; b) B64=$OPTARG ;;
|
|
r) [[ "$OPTARG" =~ ^[0-9]+$ ]] || usage 3 ;;
|
|
v) VERBOSE=1 ;; h) usage 0 ;; *) usage 3 ;;
|
|
esac
|
|
done
|
|
shift "$((OPTIND - 1))"
|
|
[ "$#" -eq 0 ] && [ -n "$TARGET" ] || usage 3
|
|
if [ -n "$B64" ]; then
|
|
MSG=$(printf '%s' "$B64" | base64 -d) || { echo 'ERROR: invalid base64' >&2; exit 3; }
|
|
elif [ -n "$FILE" ]; then
|
|
MSG=$(cat -- "$FILE") || { echo 'ERROR: cannot read message file' >&2; exit 3; }
|
|
elif [ -z "$MSG" ] && [ ! -t 0 ]; then
|
|
MSG=$(cat) || exit 3
|
|
fi
|
|
[ -n "$MSG" ] || { echo 'ERROR: empty message' >&2; exit 3; }
|
|
tmux_cmd=(tmux)
|
|
[ -z "$SOCKET_NAME" ] || tmux_cmd+=(-L "$SOCKET_NAME")
|
|
EFFECTIVE_TARGET=$TARGET
|
|
if [[ "$TARGET" == =* && "$TARGET" != *:* ]]; then EFFECTIVE_TARGET="${TARGET}:0.0"; fi
|
|
# Pin one pane ID for all subsequent commands rather than resolving a moving
|
|
# session/window target independently at every transport step.
|
|
PANE=$("${tmux_cmd[@]}" display-message -p -t "$EFFECTIVE_TARGET" '#{pane_id}' 2>/dev/null) || {
|
|
echo 'ERROR: tmux target resolution failed' >&2; exit 1;
|
|
}
|
|
[[ "$PANE" =~ ^%[0-9]+$ ]] || { echo 'ERROR: invalid resolved pane identity' >&2; exit 1; }
|
|
BUF="__mosaic_send_$$_$(date +%s%N)"
|
|
cleanup() { "${tmux_cmd[@]}" delete-buffer -b "$BUF" >/dev/null 2>&1 || true; }
|
|
trap cleanup EXIT
|
|
if ! printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b "$BUF" -; then
|
|
echo 'ERROR: buffer load failed; transport incomplete' >&2; exit 2
|
|
fi
|
|
# Do not retry a failed paste: failure may be partial. Bracketed paste is
|
|
# requested once; changing paste mode after failure could duplicate effects.
|
|
if ! "${tmux_cmd[@]}" paste-buffer -d -p -b "$BUF" -t "$PANE"; then
|
|
echo 'ERROR: paste failed; transport uncertain; do not blindly resend' >&2; exit 2
|
|
fi
|
|
sleep 0.5
|
|
if ! "${tmux_cmd[@]}" send-keys -t "$PANE" Enter; then
|
|
echo 'ERROR: submission key failed; transport partial; do not blindly resend' >&2; exit 2
|
|
fi
|
|
[ "$VERBOSE" -eq 0 ] || printf 'transport pane=%s; paste_calls=1; submission_keys=1\n' "$PANE"
|
|
printf '%s\n' 'transport dispatched; application acceptance unknown'
|
|
exit 0
|