#!/bin/bash # # Purpose : Run Hermes inside its sandbox (sandbox/agent runs this there) and # connect its terminal to its gateway. # # Hermes' gateway runs the agent; `hermes` in a terminal only talks to it, over # the gateway's control socket and then a WebSocket on 127.0.0.1:. Each # sandbox has its own network, so that port can't be reached from another one. # The gateway's sandbox relays the port to API_SOCKET, and a terminal's sandbox # listens on the same port and relays to API_SOCKET, so `hermes` reaches the # gateway as if they shared a network. # # Usage: hermes-bridge [arguments] real="$1" shift HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}" API_SOCKET="$HERMES_HOME/coredesktop-api.sock" # Ask the gateway, through its control socket, where its API listens; prints # the port, or nothing if no gateway is ready api_port() { local control="$HERMES_HOME/gateway.sock" origin [ -S "$control" ] || control="$(cat "$HERMES_HOME/gateway.sock.path" 2> /dev/null)" [ -S "$control" ] || return 1 origin="$(printf '{"protocol":1,"verb":"identify","id":1}\n' \ | socat -t 5 - "UNIX-CONNECT:$control" 2> /dev/null \ | jq -r 'select(.result.state == "ready") | .result.api_origin // empty' 2> /dev/null)" # Only an IPv4 loopback origin is relayed, which is what Hermes uses by default [[ "$origin" =~ ^http://127\.0\.0\.1:([0-9]+)$ ]] || return 1 echo "${BASH_REMATCH[1]}" } if [ "$1" = "gateway" ] && [ "$2" = "run" ]; then # The relay waits for the gateway, which runs in this process once exec'd; # the sandbox ends both together ( for _ in $(seq 120); do if port="$(api_port)"; then exec socat "UNIX-LISTEN:$API_SOCKET,unlink-early,mode=600,fork" "TCP:127.0.0.1:$port" fi sleep 1 done echo "hermes-bridge: the gateway's API didn't come up; terminals can't reach it" >&2 ) & exec "$real" "$@" fi # A terminal: relay the gateway's port, if a gateway is running. Without one, # hermes starts its own gateway inside this sandbox, which needs no relay. if [ -S "$API_SOCKET" ] && port="$(api_port)"; then socat "TCP-LISTEN:$port,bind=127.0.0.1,reuseaddr,fork" "UNIX-CONNECT:$API_SOCKET" & # Wait for the listener (state 0A, LISTEN, in /proc/net/tcp) so hermes' # first connection doesn't fail hex="$(printf ':%04X ' "$port")" for _ in $(seq 50); do grep -q "$hex.* 0A " /proc/net/tcp && break sleep 0.05 done fi exec "$real" "$@"