#!/usr/bin/env bash # socialmag installer — clone, run this, sign in, done. # # What it does not do: log you in. socialmag holds no passwords, which is the # whole design. The last step is yours and it only happens once. set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VENV="${SOCIALMAG_VENV:-$REPO/.venv}" CDP_ENDPOINT="${SOCIALMAG_CDP_ENDPOINT:-http://127.0.0.1:9336}" UNIT_DIR="$HOME/.config/systemd/user" HERMES_CONFIG="${HERMES_CONFIG:-$HOME/.hermes/config.yaml}" say() { printf '\n\033[1;36m==>\033[0m %s\n' "$*"; } ok() { printf ' \033[32mok\033[0m %s\n' "$*"; } warn() { printf ' \033[33m!!\033[0m %s\n' "$*"; } die() { printf '\n\033[31merror:\033[0m %s\n' "$*" >&2; exit 1; } # ---------------------------------------------------------------- python say "Python" command -v python3 >/dev/null 2>&1 || die "python3 not found on PATH" # Every python3 call below runs from / rather than from here. The repo root is # the package dir, so it ships selectors.py and queue.py — names the stdlib also # uses. Python puts the current directory first on sys.path, so `python3 -m venv` # run from inside a fresh clone dies at `import subprocess` with # "module 'selectors' has no attribute 'SelectSelector'". Running from / is the # whole fix. (This is the same trap cli.py exists to keep off users.) (cd / && python3 - <<'PY') || die "socialmag needs Python 3.10 or newer" import sys sys.exit(0 if sys.version_info >= (3, 10) else 1) PY ok "$(cd / && python3 -V)" # ---------------------------------------------------------------- venv say "Virtualenv + package" if [[ ! -x "$VENV/bin/python" ]]; then (cd / && python3 -m venv "$VENV") || die "could not create a venv at $VENV Two things cause this. Read the traceback above to tell them apart: - 'No module named venv' -> the venv module is not installed. Debian/Ubuntu: sudo apt install python3-venv - anything else -> report it; the installer already runs python from / to keep this repo off sys.path." ok "created $VENV" else ok "reusing $VENV" fi # Editable on purpose. socialmag resolves socialmag.db, state.json and # dashboard.json relative to its own __file__; a non-editable install would put # all three inside site-packages, where nothing else looks for them. (cd / && "$VENV/bin/pip" install --quiet --upgrade pip) >/dev/null (cd / && "$VENV/bin/pip" install --quiet -e "$REPO") || die "pip install failed" ok "socialmag installed (editable), websockets pulled in" for script in socialmag socialmag-refresh socialmag-openbridge; do [[ -x "$VENV/bin/$script" ]] || die "console script $script was not generated — pyproject.toml [project.scripts] did not take" done ok "console scripts: socialmag, socialmag-refresh, socialmag-openbridge" # ---------------------------------------------------------------- systemd say "systemd user units" if ! command -v systemctl >/dev/null 2>&1; then warn "systemctl not found — skipping units" warn "run the three processes by hand: socialmag, socialmag-openbridge, and 'python -m http.server 8321' in $REPO" else mkdir -p "$UNIT_DIR" for src in "$REPO"/deploy/user/*.in; do dest="$UNIT_DIR/$(basename "${src%.in}")" sed -e "s|@REPO@|$REPO|g" \ -e "s|@VENV@|$VENV|g" \ -e "s|@CDP_ENDPOINT@|$CDP_ENDPOINT|g" \ "$src" > "$dest" ok "$(basename "$dest")" done cp "$REPO/deploy/user/socialmag-refresh.timer" "$UNIT_DIR/socialmag-refresh.timer" ok "socialmag-refresh.timer" systemctl --user daemon-reload systemctl --user enable --now socialmag-dash.service socialmag-openbridge.service >/dev/null systemctl --user enable --now socialmag-refresh.timer >/dev/null ok "enabled: dash (:8321), openbridge (:8322), refresh timer (30min)" # These die at logout unless lingering is on. Say so rather than let someone # discover it when the dashboard is empty tomorrow. if ! loginctl show-user "$USER" -p Linger 2>/dev/null | grep -q 'Linger=yes'; then warn "user services stop when you log out. To keep them running:" warn " sudo loginctl enable-linger $USER" fi fi # ---------------------------------------------------------------- hermes say "Hermes MCP registration" HERMES_PY="/usr/local/lib/hermes-agent/venv/bin/python" if ! command -v hermes >/dev/null 2>&1; then warn "hermes not on PATH — skipping. Register manually later:" printf '\n mcp_servers:\n socialmag:\n command: "%s/bin/socialmag"\n args: []\n env:\n SOCIALMAG_CDP_ENDPOINT: "%s"\n\n' "$VENV" "$CDP_ENDPOINT" elif [[ ! -f "$HERMES_CONFIG" ]]; then die "hermes is installed but $HERMES_CONFIG does not exist. Start hermes once so it writes a config, then re-run this installer." else # Hermes treats the mcp python package as optional: without it the entire MCP # subsystem is a no-op that logs at debug level. A server that never loaded # looks exactly like one that loaded and did nothing, so check it here rather # than let that be discovered later. if [[ -x "$HERMES_PY" ]]; then if "$HERMES_PY" -c "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec('mcp') else 1)"; then ok "hermes has the mcp package" else die "hermes cannot load MCP servers: the 'mcp' package is missing from $HERMES_PY Without it hermes silently ignores every mcp_servers entry — no error, only a debug log line. Install it into that interpreter, then re-run: $HERMES_PY -m pip install mcp" fi else warn "could not find hermes's interpreter at $HERMES_PY — cannot check for the mcp package" warn "if socialmag's tools never appear in hermes, that is the first thing to check" fi if grep -qE '^\s*mcp_servers\s*:' "$HERMES_CONFIG"; then warn "$HERMES_CONFIG already has an mcp_servers: key — not touching it." warn "Merging YAML blind is how someone's other servers disappear. Add this by hand:" printf '\n socialmag:\n command: "%s/bin/socialmag"\n args: []\n env:\n SOCIALMAG_CDP_ENDPOINT: "%s"\n\n' "$VENV" "$CDP_ENDPOINT" else backup="$HERMES_CONFIG.bak-pre-socialmag-$(date +%Y%m%d%H%M%S)" cp "$HERMES_CONFIG" "$backup" cat >> "$HERMES_CONFIG" <