#!/bin/sh
# hermes — canonical hal0 CLI entry point on PATH (locked decision #3).
#
# This is the env-injecting wrapper around the hal0-managed Hermes venv.
# Unlike the legacy `hal0-hermes` wrapper, it DOES NOT pin HERMES_HOME:
# the hermes binary's own default (`~/.hermes`) resolves to
# /var/lib/hal0/.hermes for the hal0 service account, which is now the
# canonical Hermes home. Dropping the pin keeps a single source of truth
# (the hermes default) and lets `hermes` Just Work for any invoking user
# whose `~/.hermes` is the intended tree.
#
# Its sole job is to set:
#
#   HAL0_AGENT_ID  — read by `MCPIdentityMiddleware` via the
#                    `X-hal0-Agent` header, then echoed back as the
#                    `client_id` for Cognee's `private:<client_id>`
#                    namespace. Defaults to `hermes`.
#
# It then execs the hal0-managed venv's `hermes` binary. The venv lives
# at /var/lib/hal0/venvs/hermes/ (hard-pinned in
# installer/agents/hermes/requirements.txt). HAL0_HERMES_BIN overrides
# when the venv lives elsewhere.
#
# `--hal0-ready` is the smoke-test sentinel the installer pings to verify
# the wrapper is on PATH WITHOUT spawning upstream `hermes`.
#
# An optional outbound-credentials env file at
# /var/lib/hal0/secrets/agents/hermes.env (mode 0600) is sourced when
# present. Post-ADR-0012 this holds HF token + external MCP tokens only —
# never a hal0 Bearer.
#
# `hal0-hermes` is a back-compat symlink to this wrapper.

set -eu

# run-as-hal0 guard: if launched as root, re-exec as the unprivileged hal0
# service user (correct HOME, HERMES_HOME stripped) BEFORE we touch any state.
# Otherwise hermes would resolve ~/.hermes to /root/.hermes and write root:root
# files the hal0 service can't read — the root-clobber regression (#843).
# Sourced defensively: a dev/uninstalled box without the lib runs unguarded.
HAL0_GUARD_LIB="${HAL0_GUARD_LIB:-/usr/lib/hal0/guards/run-as-hal0.sh}"
if [ -r "$HAL0_GUARD_LIB" ]; then
    # shellcheck disable=SC1090
    . "$HAL0_GUARD_LIB"
    hal0_ensure_runas hal0 "$0" "$@" || exit $?
fi

HAL0_AGENT_ID="${HAL0_AGENT_ID:-hermes}"
HAL0_HERMES_BIN="${HAL0_HERMES_BIN:-/var/lib/hal0/venvs/hermes/bin/hermes}"
HAL0_HERMES_SECRETS="${HAL0_HERMES_SECRETS:-/var/lib/hal0/secrets/agents/hermes.env}"

# Deliberately NOT exporting HERMES_HOME — the hermes default ~/.hermes
# (== /var/lib/hal0/.hermes for the hal0 user) is now canonical.
export HAL0_AGENT_ID

# Source the outbound-credentials file when present (HF / external MCP
# tokens per ADR-0013). Optional — bootstrap may run without one.
if [ -r "$HAL0_HERMES_SECRETS" ]; then
    set -a
    # shellcheck disable=SC1090
    # (path is operator-controlled by design)
    . "$HAL0_HERMES_SECRETS"
    set +a
fi

case "${1:-}" in
    --hal0-ready) exit 0 ;;
esac

# root-home fallback (#2063): reaching this point as root means the guard
# above could NOT drop privileges — the guard lib is missing/unreadable
# (partial or dev install) or the hal0 user is absent — or the operator
# forced root via HAL0_ALLOW_ROOT. Upstream hermes resolves its state dir
# from $HOME (`~/.hermes`; the CLI-side euid-aware fix belongs upstream),
# so an unguarded root run silently reads /root/.hermes — a tree the hal0
# service never writes — and `hermes mcp list` falsely reports "No MCP
# servers configured" on a healthy install. Point HOME at the hal0 service
# home instead (and strip any inherited HERMES_HOME, matching the guard's
# `env -u` posture), and ALWAYS name the tree this invocation resolves so
# an empty result is diagnosable rather than a false "broken install".
# HAL0_SERVICE_HOME bypasses the getent lookup (tests / nonstandard
# service accounts; set-but-empty means "no service user resolvable").
# HAL0_RUNAS_TEST_UID is the guard's documented test-only euid seam.
_hal0_euid="${HAL0_RUNAS_TEST_UID:-$(id -u 2>/dev/null || echo 0)}"
if [ "$_hal0_euid" = "0" ]; then
    case "${HAL0_ALLOW_ROOT:-}" in
        1 | true | yes | TRUE | YES)
            # Deliberate root debug: env left alone, resolved path named.
            printf 'hermes: HAL0_ALLOW_ROOT set — reading %s/.hermes (root debug; NOT the hal0 service home)\n' \
                "${HOME:-/root}" >&2
            ;;
        *)
            _hal0_svc_home="${HAL0_SERVICE_HOME-$(getent passwd hal0 2>/dev/null | cut -d: -f6)}"
            if [ -n "$_hal0_svc_home" ]; then
                HOME="$_hal0_svc_home"
                export HOME
                unset HERMES_HOME
                printf 'hermes: running as root without the run-as-hal0 guard — reading %s/.hermes (hal0 service home)\n' \
                    "$_hal0_svc_home" >&2
            else
                printf 'hermes: running as root and no hal0 service user found — reading %s/.hermes\n' \
                    "${HOME:-/root}" >&2
            fi
            ;;
    esac
fi

if [ ! -x "$HAL0_HERMES_BIN" ]; then
    printf 'hermes: venv binary missing at %s\n' "$HAL0_HERMES_BIN" >&2
    printf '  Run `hal0 agent bootstrap hermes` to provision.\n' >&2
    exit 127
fi

# cwd-safety guard: hermes probes for a git/workspace root by walking UP from
# $PWD. If the invoking cwd is not traversable by the effective user -- e.g.
# launched as the hal0 service user from root's 0700 /root via `h0 hermes`, or a
# systemd unit with an unreadable WorkingDirectory -- that probe EACCESes
# ('[Errno 13] Permission denied: /root/.git'; Python 3.12 Path.exists() re-raises
# EACCES). Step into a guaranteed-traversable dir first; a no-op when cwd is
# already fine. The bashrc hermes() fn cds to a workspace itself, so this only
# covers the other entry paths (h0 hermes, systemd, bare invocations).
if ! { cwd=$(pwd 2>/dev/null) && [ -n "$cwd" ] && [ -r "$cwd" ] && [ -x "$cwd" ]; }; then
    cd "${HOME:-/var/lib/hal0}" 2>/dev/null || cd /var/lib/hal0 2>/dev/null || cd /
fi

exec "$HAL0_HERMES_BIN" "$@"
