Add Maria WhatsApp+RAG bridge as a service (LXC 171)
Move the /tmp prototype (Baileys bridge + RAG consumer) into git as a proper sibling project to discord-bridge/: own systemd --user units (whatsapp bridge, rag consumer, dashboard, periodic Drive sync timer), a filesystem document store with a stdlib control dashboard (start/ stop/restart, document CRUD, reindex, Google Drive sync via rclone), and an idempotent ops/install.sh following the same conventions. Co-Authored-By: Claude Agent <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# Configurarea puntii WhatsApp+RAG a lui Maria.
|
||||
#
|
||||
# install.sh copiaza acest fisier in ~/.maria-bridge/env cu drepturi 0600.
|
||||
# Format KEY=value, fara `export`, fara expandare de variabile.
|
||||
|
||||
# --- Puntea WhatsApp (whatsapp/index.js) -----------------------------------
|
||||
BRIDGE_HOST=127.0.0.1
|
||||
BRIDGE_PORT=8099
|
||||
# true = raspunde DOAR in chatul cu tine insuti (self-chat). Pune "false" abia
|
||||
# dupa ce esti multumit de calitatea raspunsurilor — altfel Maria raspunde la
|
||||
# oricine iti scrie pe numarul legat.
|
||||
TEST_MODE_SELF_CHAT_ONLY=true
|
||||
|
||||
# --- Backend LLM + embeddings -----------------------------------------------
|
||||
# Modelul de chat folosit pentru raspunsuri (format compatibil OpenAI
|
||||
# /v1/chat/completions). Implicit presupune un tunel/proxy local catre acelasi
|
||||
# backend folosit de Echo (LXC 110) — vezi docs/maria-whatsapp-rag-prototype.md.
|
||||
LLM_URL=http://127.0.0.1:8091
|
||||
# Ollama, pentru embeddings (nomic-embed-text).
|
||||
OLLAMA_URL=http://127.0.0.1:11434
|
||||
EMBED_MODEL=nomic-embed-text
|
||||
TOP_K=3
|
||||
MAX_TOKENS=250
|
||||
POLL_INTERVAL_S=2
|
||||
|
||||
# --- Depozit de documente + sincronizare Google Drive -----------------------
|
||||
# Tinta rclone pentru dosarul de documente (partajat de pe Windows,
|
||||
# D:\GoogleDrive\romfast\document_store), format `<remote>:<cale>`.
|
||||
# Gol = sincronizare dezactivata; documentele se administreaza doar manual din
|
||||
# dashboard. Vezi README.md, "Sincronizare cu Google Drive", pentru configurarea
|
||||
# remote-ului `rclone` (cont de serviciu, fara OAuth interactiv pe un container
|
||||
# headless).
|
||||
DRIVE_REMOTE=
|
||||
|
||||
# --- dashboard de control (dashboard/api.py) --------------------------------
|
||||
# Token de acces. Generat automat de ops/install.sh daca lipseste.
|
||||
DASHBOARD_TOKEN=
|
||||
# `off` scoate complet login-ul. De pus DOAR daca panoul ramane strict pe
|
||||
# 127.0.0.1 / tailnet (acelasi motiv ca la discord-bridge).
|
||||
DASHBOARD_AUTH=
|
||||
DASHBOARD_BIND=127.0.0.1
|
||||
DASHBOARD_PORT=18792
|
||||
158
proxmox/lxc171-claude-agent/maria-whatsapp-bridge/ops/install.sh
Executable file
158
proxmox/lxc171-claude-agent/maria-whatsapp-bridge/ops/install.sh
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env bash
|
||||
# install.sh — instaleaza puntea WhatsApp+RAG a lui Maria pe LXC 171 (claude-agent).
|
||||
#
|
||||
# IDEMPOTENT: se poate rula de cate ori vrei. Nu suprascrie niciodata
|
||||
# ~/.maria-bridge/env (acolo sta configuratia completata de om).
|
||||
#
|
||||
# ./ops/install.sh # instaleaza / actualizeaza, NU porneste serviciile
|
||||
# ./ops/install.sh --start # in plus porneste bridge+rag (bridge cere QR scanat)
|
||||
#
|
||||
# Ce face:
|
||||
# 1. ~/.maria-bridge/ cu 0700, subdirectoare (logs/, documents/, whatsapp-auth/)
|
||||
# 2. env 0600 din ops/env.example (doar daca lipseste)
|
||||
# 3. npm install pentru whatsapp/ (Baileys)
|
||||
# 4. venv + pip install pentru rag/requirements.txt
|
||||
# 5. loginctl enable-linger (serviciile de utilizator supravietuiesc logout-ului)
|
||||
# 6. symlink-uri unit -> ~/.config/systemd/user/ (bridge, rag, dashboard, timer sync)
|
||||
# 7. systemd-analyze verify + enable
|
||||
set -uo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SRC="$(dirname "$HERE")" # .../maria-whatsapp-bridge
|
||||
STATE_DIR="$HOME/.maria-bridge"
|
||||
UNIT_DIR="$HOME/.config/systemd/user"
|
||||
UNITS=(maria-whatsapp.service maria-rag.service maria-sync.service maria-sync.timer)
|
||||
DASH_UNIT="maria-dashboard.service"
|
||||
DO_START=0
|
||||
[ "${1:-}" = "--start" ] && DO_START=1
|
||||
|
||||
info() { printf ' \033[32m*\033[0m %s\n' "$*"; }
|
||||
warn() { printf ' \033[33m!\033[0m %s\n' "$*"; }
|
||||
die() { printf ' \033[31mX\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[ "$(id -u)" -eq 0 ] && die "NU rula ca root. Serviciile Maria sunt servicii de utilizator (claude)."
|
||||
|
||||
echo "== Punte WhatsApp+RAG Maria: instalare =="
|
||||
echo " sursa: $SRC"
|
||||
|
||||
# --- 1. director de stare --------------------------------------------------
|
||||
mkdir -p "$STATE_DIR/logs" "$STATE_DIR/documents" "$STATE_DIR/whatsapp-auth"
|
||||
chmod 0700 "$STATE_DIR"
|
||||
info "director de stare: $STATE_DIR (0700)"
|
||||
|
||||
# --- 2. env ----------------------------------------------------------------
|
||||
if [ -f "$STATE_DIR/env" ]; then
|
||||
chmod 0600 "$STATE_DIR/env"
|
||||
info "env exista deja, nu se atinge"
|
||||
else
|
||||
cp "$HERE/env.example" "$STATE_DIR/env"
|
||||
chmod 0600 "$STATE_DIR/env"
|
||||
info "env creat din env.example (0600) — completeaza LLM_URL/DRIVE_REMOTE dupa caz"
|
||||
fi
|
||||
|
||||
# --- 3. dependinte Node (whatsapp/) -----------------------------------------
|
||||
if command -v npm >/dev/null 2>&1; then
|
||||
if [ -d "$SRC/whatsapp/node_modules" ]; then
|
||||
info "node_modules exista deja in whatsapp/"
|
||||
else
|
||||
(cd "$SRC/whatsapp" && npm install --omit=dev) \
|
||||
&& info "npm install: gata (Baileys si dependinte)" \
|
||||
|| warn "npm install a esuat (retea?) — reia cu: cd $SRC/whatsapp && npm install"
|
||||
fi
|
||||
else
|
||||
warn "npm lipseste din PATH — instaleaza Node (nvm) inainte de a porni puntea"
|
||||
fi
|
||||
|
||||
# --- 4. venv + dependinte Python (rag/) -------------------------------------
|
||||
if [ ! -x "$STATE_DIR/venv/bin/python" ]; then
|
||||
python3 -m venv "$STATE_DIR/venv" || die "crearea venv a esuat"
|
||||
info "venv creat: $STATE_DIR/venv"
|
||||
else
|
||||
info "venv exista deja"
|
||||
fi
|
||||
"$STATE_DIR/venv/bin/python" -m pip install --quiet --upgrade pip >/dev/null 2>&1
|
||||
if "$STATE_DIR/venv/bin/python" -m pip install --quiet -r "$SRC/rag/requirements.txt"; then
|
||||
info "dependinte Python instalate din rag/requirements.txt"
|
||||
else
|
||||
warn "instalarea dependintelor Python a esuat (retea?) — reia cu: $STATE_DIR/venv/bin/pip install -r $SRC/rag/requirements.txt"
|
||||
fi
|
||||
|
||||
# --- 5. linger ---------------------------------------------------------------
|
||||
if [ "$(loginctl show-user "$USER" -p Linger --value 2>/dev/null)" = "yes" ]; then
|
||||
info "linger deja activ pentru $USER"
|
||||
else
|
||||
if loginctl enable-linger "$USER" 2>/dev/null; then
|
||||
info "linger activat pentru $USER"
|
||||
elif sudo -n loginctl enable-linger "$USER" 2>/dev/null; then
|
||||
info "linger activat pentru $USER (prin sudo)"
|
||||
else
|
||||
warn "nu am putut activa linger; ruleaza manual: sudo loginctl enable-linger $USER"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 6. unit-uri -------------------------------------------------------------
|
||||
mkdir -p "$UNIT_DIR"
|
||||
for u in "${UNITS[@]}"; do
|
||||
ln -sfn "$HERE/$u" "$UNIT_DIR/$u"
|
||||
info "unit legat: $UNIT_DIR/$u -> $HERE/$u"
|
||||
done
|
||||
ln -sfn "$SRC/dashboard/$DASH_UNIT" "$UNIT_DIR/$DASH_UNIT"
|
||||
info "unit legat: $UNIT_DIR/$DASH_UNIT -> $SRC/dashboard/$DASH_UNIT"
|
||||
|
||||
if grep -qE '^DASHBOARD_TOKEN=.+' "$STATE_DIR/env" 2>/dev/null; then
|
||||
info "DASHBOARD_TOKEN exista deja in env"
|
||||
else
|
||||
printf 'DASHBOARD_TOKEN=%s\n' "$(python3 -c 'import secrets;print(secrets.token_urlsafe(24))')" \
|
||||
>> "$STATE_DIR/env"
|
||||
info "DASHBOARD_TOKEN generat si adaugat in $STATE_DIR/env"
|
||||
fi
|
||||
|
||||
systemctl --user daemon-reload 2>/dev/null || warn "daemon-reload a esuat (sesiune fara systemd de utilizator?)"
|
||||
|
||||
if systemd-analyze verify "$UNIT_DIR/maria-whatsapp.service" 2>&1 | grep -vE 'Unknown key|^$' | grep -q .; then
|
||||
systemd-analyze verify "$UNIT_DIR/maria-whatsapp.service" 2>&1 | sed 's/^/ /'
|
||||
warn "systemd-analyze verify a raportat probleme (vezi mai sus)"
|
||||
else
|
||||
info "systemd-analyze verify: curat"
|
||||
fi
|
||||
|
||||
# --- 7. rclone (optional, pentru sincronizare Drive) ------------------------
|
||||
if command -v rclone >/dev/null 2>&1; then
|
||||
info "rclone gasit: $(command -v rclone)"
|
||||
if ! grep -qE '^DRIVE_REMOTE=.+' "$STATE_DIR/env" 2>/dev/null; then
|
||||
warn "DRIVE_REMOTE necompletat — sincronizarea cu Google Drive ramane dezactivata"
|
||||
warn "vezi README.md, sectiunea 'Sincronizare cu Google Drive'"
|
||||
fi
|
||||
else
|
||||
warn "rclone nu e instalat — sincronizarea automata cu Google Drive nu va functiona"
|
||||
warn "instaleaza cu: sudo apt-get install -y rclone (apoi vezi README.md)"
|
||||
fi
|
||||
|
||||
# --- 8. enable / start -------------------------------------------------------
|
||||
for u in maria-whatsapp.service maria-rag.service; do
|
||||
systemctl --user enable "$u" >/dev/null 2>&1 \
|
||||
&& info "$u enabled (porneste la boot)" \
|
||||
|| warn "enable pentru $u a esuat"
|
||||
done
|
||||
systemctl --user enable --now "$DASH_UNIT" >/dev/null 2>&1 \
|
||||
&& info "dashboard enabled + pornit pe 127.0.0.1:$(grep -E '^DASHBOARD_PORT=' "$STATE_DIR/env" | cut -d= -f2- || echo 18792)" \
|
||||
|| warn "enable pentru $DASH_UNIT a esuat"
|
||||
systemctl --user enable --now maria-sync.timer >/dev/null 2>&1 \
|
||||
&& info "timer de sincronizare Drive activat (la fiecare 10 min)" \
|
||||
|| warn "enable pentru maria-sync.timer a esuat"
|
||||
|
||||
if [ "$DO_START" -eq 1 ]; then
|
||||
systemctl --user restart maria-whatsapp.service && info "punte WhatsApp pornita"
|
||||
echo
|
||||
echo " Deschide dashboard-ul (127.0.0.1:\$DASHBOARD_PORT, tunel SSH) si scaneaza"
|
||||
echo " codul QR din cardul 'Conectare WhatsApp' inainte sa pornesti maria-rag."
|
||||
systemctl --user restart maria-rag.service && info "consumer RAG pornit"
|
||||
systemctl --user --no-pager status maria-whatsapp.service | sed 's/^/ /'
|
||||
else
|
||||
echo
|
||||
echo " Serviciile bridge/rag NU au fost pornite (intentionat: bridge-ul cere"
|
||||
echo " scanarea unui cod QR de asociere WhatsApp, actiune manuala)."
|
||||
echo " Dupa ce completezi $STATE_DIR/env: $0 --start"
|
||||
fi
|
||||
|
||||
echo "== gata =="
|
||||
@@ -0,0 +1,38 @@
|
||||
# Unit systemd de UTILIZATOR pentru dashboard-ul de control al puntii Maria.
|
||||
#
|
||||
# Se instaleaza in ~/.config/systemd/user/maria-dashboard.service (vezi install.sh).
|
||||
# Unit SEPARAT de bridge/rag, ca o repornire a lor sa nu ia si panoul din care
|
||||
# ai apasat butonul (acelasi motiv ca la claude-discord-dashboard.service).
|
||||
#
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now maria-dashboard
|
||||
# journalctl --user -u maria-dashboard -f
|
||||
|
||||
[Unit]
|
||||
Description=Dashboard de control pentru puntea WhatsApp+RAG a lui Maria (LXC 171)
|
||||
Documentation=file:///workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=%h/.maria-bridge
|
||||
EnvironmentFile=%h/.maria-bridge/env
|
||||
Environment=MARIA_BRIDGE_DIR=%h/.maria-bridge
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
|
||||
# Stdlib only, dar are nevoie sa dea `systemctl --user` — acelasi utilizator ca
|
||||
# bridge/rag, deci merge fara sudo.
|
||||
ExecStart=/usr/bin/python3 /workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/dashboard/api.py
|
||||
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
MemoryHigh=192M
|
||||
MemoryMax=384M
|
||||
|
||||
StandardOutput=append:%h/.maria-bridge/logs/dashboard.log
|
||||
StandardError=append:%h/.maria-bridge/logs/dashboard.log
|
||||
SyslogIdentifier=maria-dashboard
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,48 @@
|
||||
# Unit systemd de UTILIZATOR pentru consumer-ul RAG al lui Maria (rag/consumer.py).
|
||||
#
|
||||
# Se instaleaza in ~/.config/systemd/user/maria-rag.service (vezi install.sh).
|
||||
#
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now maria-rag
|
||||
# journalctl --user -u maria-rag -f
|
||||
|
||||
[Unit]
|
||||
Description=Consumer RAG pentru Maria — raspunde la mesajele din puntea WhatsApp
|
||||
Documentation=file:///workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md
|
||||
After=network-online.target maria-whatsapp.service
|
||||
Wants=network-online.target
|
||||
# Nu e o dependinta stricta (poate porni si daca puntea WhatsApp inca nu e sus —
|
||||
# consumer.py doar face polling si tolereaza erori de retea), dar ordinea are sens.
|
||||
Wants=maria-whatsapp.service
|
||||
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag
|
||||
EnvironmentFile=%h/.maria-bridge/env
|
||||
Environment=MARIA_BRIDGE_DIR=%h/.maria-bridge
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
|
||||
ExecStartPre=/bin/mkdir -p %h/.maria-bridge/logs %h/.maria-bridge/documents
|
||||
ExecStart=%h/.maria-bridge/venv/bin/python consumer.py
|
||||
|
||||
KillMode=control-group
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=15
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
RestartSteps=5
|
||||
RestartMaxDelaySec=120
|
||||
|
||||
MemoryHigh=256M
|
||||
MemoryMax=512M
|
||||
|
||||
StandardOutput=append:%h/.maria-bridge/logs/rag.log
|
||||
StandardError=append:%h/.maria-bridge/logs/rag.log
|
||||
SyslogIdentifier=maria-rag
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,21 @@
|
||||
# Unit systemd de UTILIZATOR: o singura trecere de sincronizare Drive + reindexare
|
||||
# conditionata (rag/sync.py). Pornit periodic de maria-sync.timer, sau manual:
|
||||
#
|
||||
# systemctl --user start maria-sync.service
|
||||
# journalctl --user -u maria-sync -f
|
||||
|
||||
[Unit]
|
||||
Description=Sincronizare depozit documente Maria (Drive) + reindexare RAG
|
||||
Documentation=file:///workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag
|
||||
EnvironmentFile=%h/.maria-bridge/env
|
||||
Environment=MARIA_BRIDGE_DIR=%h/.maria-bridge
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
ExecStart=%h/.maria-bridge/venv/bin/python sync.py
|
||||
|
||||
StandardOutput=append:%h/.maria-bridge/logs/sync.log
|
||||
StandardError=append:%h/.maria-bridge/logs/sync.log
|
||||
SyslogIdentifier=maria-sync
|
||||
@@ -0,0 +1,20 @@
|
||||
# Timer de UTILIZATOR: declanseaza maria-sync.service la fiecare 10 minute.
|
||||
#
|
||||
# Interval fix aici (nu citit din env): un timer systemd nu poate parametriza
|
||||
# OnUnitActiveSec dintr-un fisier extern usor — de editat direct daca 10 min nu
|
||||
# se potriveste.
|
||||
#
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now maria-sync.timer
|
||||
# systemctl --user list-timers maria-sync.timer
|
||||
|
||||
[Unit]
|
||||
Description=Sincronizare periodica a depozitului de documente Maria (Drive)
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=10min
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,47 @@
|
||||
# Unit systemd de UTILIZATOR pentru puntea WhatsApp (Baileys) a lui Maria.
|
||||
#
|
||||
# Se instaleaza in ~/.config/systemd/user/maria-whatsapp.service (vezi install.sh).
|
||||
#
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now maria-whatsapp
|
||||
# journalctl --user -u maria-whatsapp -f
|
||||
|
||||
[Unit]
|
||||
Description=Punte WhatsApp (Baileys) pentru Maria (LXC 171 claude-agent)
|
||||
Documentation=file:///workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/whatsapp
|
||||
EnvironmentFile=%h/.maria-bridge/env
|
||||
Environment=MARIA_BRIDGE_DIR=%h/.maria-bridge
|
||||
Environment=NODE_ENV=production
|
||||
# nvm — acelasi PATH ca discord-bridge, pe acelasi container.
|
||||
Environment=PATH=%h/bin:%h/.nvm/versions/node/v20.19.6/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
ExecStartPre=/bin/mkdir -p %h/.maria-bridge/logs %h/.maria-bridge/whatsapp-auth
|
||||
ExecStart=%h/.nvm/versions/node/v20.19.6/bin/node index.js
|
||||
|
||||
KillMode=control-group
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=15
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
RestartSteps=5
|
||||
RestartMaxDelaySec=120
|
||||
|
||||
MemoryHigh=384M
|
||||
MemoryMax=768M
|
||||
|
||||
StandardOutput=append:%h/.maria-bridge/logs/whatsapp.log
|
||||
StandardError=append:%h/.maria-bridge/logs/whatsapp.log
|
||||
SyslogIdentifier=maria-whatsapp
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Reference in New Issue
Block a user