#!/usr/bin/env bash # ============================================================================ # Simbiot — перенос памяти инстанса из репо в SIMBIOT_HOME (t-261004113351). # # Репозиторий становится чистым кодом: ES-база, .env, ключи аудита, балансы, # выжимки, штампы каналов переезжают в один каталог-хом (по умолчанию # /var/lib/simbiot-harness). Идемпотентен: повторный запуск — SKIP уже # перенесённого. # # БЕЗОПАСНОСТЬ: скрипт сам ждёт «тихого окна» (нет живых LLM-воркеров — # паттерн scripts/safe-restart.sh), останавливает три сервиса харнеса, # переносит файлы, добавляет SIMBIOT_HOME в systemd-юниты и обёртки, # поднимает сервисы и проверяет здоровье. Откат: см. конец файла. # # scripts/migrate_to_home.sh [HOME] # по умолчанию /var/lib/simbiot-harness # ============================================================================ set -u REPO="$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)" HOME_DIR="${1:-/var/lib/simbiot-harness}" SERVICES="simbiot-harness.service simbiot-harness-tg.service simbiot-dashboard.service" LOG="${SIMBIOT_MIGRATION_LOG:-/tmp/simbiot-migrate-to-home.log}" MAX_WAIT="${SIMBIOT_MIGRATION_WAIT:-1800}" PAT='/harness/_llm_worker\.py$' log() { echo "[$(date -u '+%F %T')] $*" >> "$LOG"; echo "$*"; } # ── 0) тихое окно: мигрировать можно только когда ни один тик не пишет в БД ─ deadline=$(( $(date +%s) + MAX_WAIT )) while :; do if ! pgrep -f "$PAT" > /dev/null; then sleep 3 if ! pgrep -f "$PAT" > /dev/null; then log "тихое окно поймано — начинаю миграцию в $HOME_DIR" break fi fi if [ "$(date +%s)" -ge "$deadline" ]; then log "тихого окна нет ${MAX_WAIT}s — отмена (повторить позже)" exit 0 fi sleep 10 done # ── 1) структура хома (audit НЕ создаём: туда переедет каталог целиком) ───── mkdir -p "$HOME_DIR"/{db,env,inbox,digests,run,voice,docs/aum} log "home создан: $HOME_DIR" moved() { # mv если источник есть и цель отсутствует [ -e "$1" ] || { [ -L "$1" ] && rm -f "$1"; return 0; } if [ -e "$2" ]; then log "SKIP (цель есть): $2"; return 0; fi mkdir -p "$(dirname "$2")" mv "$1" "$2" && log "moved: $1 → $2" } copied() { # cp если источник есть (переносить нельзя: git-трекается) [ -e "$1" ] || return 0 if [ -e "$2" ]; then log "SKIP (цель есть): $2"; return 0; fi mkdir -p "$(dirname "$2")" cp -a "$1" "$2" && log "copied: $1 → $2" } # ── 2) стоп сервисов (писателей больше нет — mv БД с WAL безопасен) ───────── systemctl stop $SERVICES log "сервисы остановлены: $SERVICES" # ── 3) перенос памяти ─────────────────────────────────────────────────────── moved "$REPO/harness/harness.db" "$HOME_DIR/db/harness.db" moved "$REPO/harness/harness.db-wal" "$HOME_DIR/db/harness.db-wal" moved "$REPO/harness/harness.db-shm" "$HOME_DIR/db/harness.db-shm" moved "$REPO/.env" "$HOME_DIR/env/.env" moved "$REPO/.audit" "$HOME_DIR/audit" # ключи ed25519 copied "$REPO/docs/aum/balances.json" "$HOME_DIR/docs/aum/balances.json" copied "$REPO/out" "$HOME_DIR/digests/out" # выжимки-файлы copied "$REPO/portal/inbox/.channel-last" "$HOME_DIR/inbox/.channel-last" for f in "$REPO"/harness/channel_*.json; do [ -e "$f" ] && { cp -a "$f" "$HOME_DIR/run/" && log "copied: $f"; } done [ -f "$HOME_DIR/env/.env" ] && chmod 600 "$HOME_DIR/env/.env" # ── 4) SIMBIOT_HOME в юнитах и обёртках ───────────────────────────────────── # RAG-корпус кооператива (/root/rag/rag.db) — общая инфраструктура хоста, # его индексируют и другие инструменты (board hook): основному инстансу # оставляем точечное переопределение, иначе SIMBIOT_HOME уведёт поиск на # пустой /rag/rag.db (t-261004113351). RAG_KEEP="" if [ -e /root/rag/rag.db ]; then RAG_KEEP="/root/rag/rag.db" fi for svc in $SERVICES; do f="/etc/systemd/system/$svc" [ -f "$f" ] || continue if ! grep -q "^Environment=SIMBIOT_HOME=" "$f"; then sed -i "/^\[Service\]/a Environment=SIMBIOT_HOME=$HOME_DIR" "$f" log "юнит обновлён: $f (+SIMBIOT_HOME)" fi if [ -n "$RAG_KEEP" ] && ! grep -q "^Environment=SIMBIOT_RAG_DB=" "$f"; then sed -i "/^Environment=SIMBIOT_HOME=/a Environment=SIMBIOT_RAG_DB=$RAG_KEEP" "$f" log "юнит обновлён: $f (+SIMBIOT_RAG_DB=$RAG_KEEP)" fi done systemctl daemon-reload for w in "$REPO/bin/simbiot" /root/.local/bin/simbiot; do [ -f "$w" ] || continue if ! grep -q "SIMBIOT_HOME" "$w"; then sed -i "3a export SIMBIOT_HOME=\"\${SIMBIOT_HOME:-$HOME_DIR}\"" "$w" log "обёртка обновлена: $w" fi done # ── 5) подъём и проверка здоровья ─────────────────────────────────────────── systemctl start simbiot-harness.service simbiot-harness-tg.service simbiot-dashboard.service sleep 8 health="FAIL" if systemctl is-active --quiet simbiot-harness.service \ && [ -s "$HOME_DIR/db/harness.db" ] \ && ! ls "$REPO/harness/harness.db" >/dev/null 2>&1; then health="OK" fi log "health: $health (db: $(du -h "$HOME_DIR/db/harness.db" 2>/dev/null | cut -f1))" # контрольный прогон CLI из хома (не должен пересоздавать репо-базу) SIMBIOT_HOME="$HOME_DIR" "$REPO/venv/bin/python" "$REPO/cli.py" status >/dev/null 2>&1 \ && log "cli status: OK" || log "cli status: FAIL" [ -e "$REPO/harness/harness.db" ] && log "ВНИМАНИЕ: репо-база пересоздана чем-то!" # отчёт + идемпотентное уведомление (не curl — защита от дублей в cli.py) cat > "$HOME_DIR/migration-report.json" </dev/null || true log "миграция завершена: $health" # ОТКАТ (если что-то не так): systemctl stop $SERVICES; # mv "$HOME_DIR"/db/harness.db* "$REPO/harness/"; mv "$HOME_DIR/env/.env" "$REPO/.env"; # убрать Environment=SIMBIOT_HOME из юнитов; systemctl daemon-reload; start exit 0