simbiot-harness/scripts/install.sh
Simbiot Harness 3dc6ca6ab9 t-261004113351: изоляция памяти инстанса — SIMBIOT_HOME (репо = чистый код)
- harness/paths.py: единая карта персистентных путей; SIMBIOT_HOME → db/ rag/
  env/ docs/ digests/ audit/ inbox/ run/ voice/; без него — legacy-пути без
  изменений; точечные env (SIMBIOT_DB и пр.) сильнее схемы
- es/tg_inbox/voice/portal_channel/aum/audit/runtime/rag_lookup/isolated_
  runner/cli: все записи-чтения памяти через paths
- scripts/init_instance.py: новый инстанс — чистая база + идентичность
  владельца + его доки (+RAG) + селективный импорт выжимок/доков
- tests/test_instance_isolation.py: копия с пустой памятью не вспоминает
  наши проекты (контекст тика/RAG/прод — 14 проверок ALL PASS, регресс 7/7)
- scripts/migrate_to_home.sh: перенос боевого инстанса в хом (тихое окно,
  идемпотентно, RAG-корпус коопа сохраняется точечным SIMBIOT_RAG_DB)
- install.sh/service/README: свежие установки сразу с ~/.simbiot-data
2026-10-04 12:34:45 +00:00

258 lines
No EOL
13 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ============================================================================
# Simbiot Harness — установка одной командой
# ============================================================================
# curl -fsSL https://raw.cil.by/simbiot/install.sh | bash
# # или из клона репо:
# bash scripts/install.sh
#
# Что делает:
# 1. Клонирует (или обновляет) репо в ~/simbiot-harness
# 2. Создаёт Python 3.11+ venv, ставит зависимости (requirements.txt)
# 3. Кладёт обёртку `simbiot` в ~/.local/bin (аналог hermes: чистый env,
# exec venv/bin/python cli.py — вне репо работает отовсюду)
# 4. Флаги: --dir DIR (куда ставить, по умолчанию ~/simbiot-harness),
# --repo URL (по умолчанию https://git.cil.by/symbiot/simbiot-harness.git),
# --wizard (визард первого запуска даже в интерактиве не нужен),
# --no-wizard (тихий режим: без вопросов), -q (минимум вывода).
# Опционально ставит systemd-юниты (если root и systemd), иначе — как
# запустить руками, печатает подсказку симулятивно.
# ============================================================================
set -euo pipefail
REPO_URL="https://git.cil.by/symbiot/simbiot-harness.git"
INSTALL_DIR="$(eval echo '~')/simbiot-harness"
WIZARD_AUTO="" # "", "yes", "no"
QUIET=0
FORCE_SYSTEMD=""
INTERACTIVE=0
[ -t 0 ] && [ -t 1 ] && INTERACTIVE=1
while [ $# -gt 0 ]; do
case "$1" in
--dir) INSTALL_DIR="$2"; shift 2 ;;
--repo) REPO_URL="$2"; shift 2 ;;
--wizard) WIZARD_AUTO="yes"; shift ;;
--no-wizard) WIZARD_AUTO="no"; shift ;;
--systemd) FORCE_SYSTEMD="yes"; shift ;;
-q|--quiet) QUIET=1; shift ;;
-h|--help) grep '^#' "$0" | sed -n '2,20p' | sed 's/^# \{0,1\}//'; exit 0 ;;
*) echo "Неизвестный аргумент: $1" >&2; exit 2 ;;
esac
done
GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[0;33m'; NC='\033[0m'
say() { [ "$QUIET" = 1 ] || echo -e "$1"; }
say "${CYAN}⬢ Simbiot Harness — установка${NC}"
say ""
# ----------------------------------------------------------------------------
# 1. Репозиторий
# ----------------------------------------------------------------------------
if [ -d "$INSTALL_DIR/.git" ]; then
say "${CYAN}→${NC} Обновляю существующий клон: $INSTALL_DIR"
git -C "$INSTALL_DIR" fetch --depth=1 origin >/dev/null 2>&1 || true
git -C "$INSTALL_DIR" reset --hard origin/HEAD >/dev/null 2>&1 || \
git -C "$INSTALL_DIR" reset --hard origin/main >/dev/null 2>&1 || true
# старый PoC-клон мог тащить мусорные/полу-удалённые файлы → чистим трекинг,
# но сохраняем локальное: venv, .env, *.db, токены
git -C "$INSTALL_DIR" clean -qfd -e venv -e .env -e '*.db' -e '.simbiot-portal-token' 2>/dev/null || true
else
say "${CYAN}→${NC} Клонирую репозиторий → $INSTALL_DIR"
mkdir -p "$(dirname "$INSTALL_DIR")"
for i in 1 2 3; do
git clone --depth=1 "$REPO_URL" "$INSTALL_DIR" && break
[ "$i" = 3 ] && { echo "Ошибка: клонирование не удалось ($REPO_URL)" >&2; exit 1; }
sleep 2
done
fi
cd "$INSTALL_DIR"
# ----------------------------------------------------------------------------
# 2. Python venv + зависимости
# ----------------------------------------------------------------------------
if ! command -v python3 >/dev/null 2>&1; then
echo "python3 не найден. Установи Python 3.11+: apt install -y python3 python3-venv python3-pip" >&2
exit 1
fi
PYV=$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])')
PYMAJOR=${PYV%%.*}; PYMINOR=${PYV#*.}
if [ "$PYMAJOR" -lt 3 ] || { [ "$PYMAJOR" = 3 ] && [ "$PYMINOR" -lt 11 ]; }; then
echo "Нужен Python ≥ 3.11, найден $PYV" >&2
exit 1
fi
# venv считается годным только если есть И python, И pip (WSL/долгоживущие
# машины оставляют битые venv: python есть, pip нет → "./venv/bin/pip: No such
# file or directory"). Битый/неполный пересоздаём.
if [ ! -x venv/bin/python ] || [ ! -x venv/bin/pip ]; then
say "${CYAN}→${NC} Создаю venv (python $PYV)…"
rm -rf venv # сносим битый/неполный venv целиком
python3 -m venv venv 2>/dev/null || python3 -m venv --without-pip venv
fi
say "${CYAN}→${NC} Ставлю зависимости…"
if [ -x venv/bin/pip ]; then
./venv/bin/pip install --upgrade pip >/dev/null
./venv/bin/pip install -r requirements.txt >/dev/null 2>&1 || \
./venv/bin/pip install -r requirements.txt
else
# WSL/deadsnakes-кейс: ensurepip недоступен → ставим зависимости системным pip
# прямо в site-packages этого venv (python venv подхватит их по sys.path).
if ! command -v pip3 >/dev/null 2>&1; then
echo "pip3 не найден. Установи: apt install -y python3-venv python3-pip" >&2
exit 1
fi
say "${YELLOW}→${NC} venv без pip (ensurepip отсутствует) — ставлю зависимости системным pip3 в venv"
pip3 install --quiet --target="venv/lib/python$PYV/site-packages" -r requirements.txt
fi
# ----------------------------------------------------------------------------
# 3. Обёртка `simbiot` (аналог /usr/local/bin/hermes)
# ----------------------------------------------------------------------------
BIN_DIR="$(bash -c 'echo -n "${XDG_BIN_HOME:-$HOME/.local/bin}"')"
mkdir -p "$BIN_DIR"
cat > "$BIN_DIR/simbiot" <<EOF
#!/usr/bin/env bash
# Simbiot Harness CLI (установлен scripts/install.sh в $INSTALL_DIR)
unset PYTHONPATH
unset PYTHONHOME
# память инстанса — вне репо (t-261004113351): репо = чистый код
export SIMBIOT_HOME="\${SIMBIOT_HOME:-$HOME/.simbiot-data}"
mkdir -p "\$SIMBIOT_HOME" 2>/dev/null || true
cd "$INSTALL_DIR"
exec ./venv/bin/python cli.py "\$@"
EOF
chmod +x "$BIN_DIR/simbiot"
# 3a. In-repo обёртка bin/simbiot: запуск «simbiot» без установки в ~/.local/bin
# (Alex: харнес, ставящийся баш-командой, должен запускаться и просто
# словом simbiot — из корня репо это ./bin/simbiot).
mkdir -p "$INSTALL_DIR/bin"
cat > "$INSTALL_DIR/bin/simbiot" <<'EOF'
#!/usr/bin/env bash
# Simbiot Harness: запуск без установки обёртки (cli.py + scheduler-демон).
# simbiot help — справка по CLI-командам (как python cli.py -h)
# simbiot — без аргументов: статус + как управлять демоном/CLI
DIR="$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)"
unset PYTHONPATH
unset PYTHONHOME
# память инстанса — вне репо (t-261004113351): репо = чистый код
export SIMBIOT_HOME="${SIMBIOT_HOME:-$HOME/.simbiot-data}"
mkdir -p "$SIMBIOT_HOME" 2>/dev/null || true
cd "$DIR" || exit 1
PY="$DIR/venv/bin/python"
[ -x "$PY" ] || PY="$(command -v python3)"
_flag=0
for a in "$@"; do
case "$a" in
help|--help) _flag=1 ;;
esac
done
if [ "${_flag}" = 1 ]; then
exec "$PY" cli.py -h
fi
if [ $# -eq 0 ]; then
echo "Simbiot Harness CLI — venv: $PY, репо: $DIR"
if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet simbiot-harness; then
echo "Демон : active (systemctl status simbiot-harness)"
elif pgrep -f "harness.scheduler" >/dev/null; then
echo "Демон : жив (pgrep harness.scheduler)"
else
echo "Демон : не запущен — $DIR/venv/bin/python -m harness.scheduler"
fi
echo "Быстрые команды: simbiot status | simbiot wizard | simbiot help"
exit 0
fi
exec "$PY" cli.py "$@"
EOF
chmod +x "$INSTALL_DIR/bin/simbiot"
# PATH: если ~/.local/bin не подхватился (WSL/мак/кастомный профиль) — после
# установки будет «simbiot: команда не найдена» до перелогина. Добавляем и в
# текущую сессию, и в rc-файл, и говорим как перечитать.
_PATH_RC=""
case "${SHELL##*/}" in
zsh) _PATH_RC="$HOME/.zshrc" ;;
*) _PATH_RC="$HOME/.bashrc" ;;
esac
case ":$PATH:" in
*":$BIN_DIR:"*) ;;
*)
export PATH="$BIN_DIR:$PATH"
if [ -f "$_PATH_RC" ] && ! grep -qs '.local/bin' "$_PATH_RC"; then
printf '\n# Simbiot CLI (scripts/install.sh)\nexport PATH="$HOME/.local/bin:$PATH"\n' >> "$_PATH_RC"
say "${YELLOW}→${NC} добавил PATH в ${_PATH_RC} — открой новую вкладку терминала или: source ${_PATH_RC}"
else
say "${YELLOW}→${NC} путь $BIN_DIR добавил только в текущую сессию: если «simbiot: команда не найдена» в новой вкладке — source ${_PATH_RC} или перелогинься."
fi
;;
esac
say "${GREEN}→${NC} Команда simbiot готова: $BIN_DIR/simbiot"
# ----------------------------------------------------------------------------
# 4. Первый запуск: init стора (тихо) + визард (если просили/интерактив)
# ----------------------------------------------------------------------------
if [ ! -f harness.db ]; then
if [ "$WIZARD_AUTO" = "no" ] || { [ -z "$WIZARD_AUTO" ] && [ "$INTERACTIVE" = 0 ]; }; then
say "${CYAN}→${NC} Тихий режим: ./venv/bin/python cli.py init"
./venv/bin/python cli.py init
say "${YELLOW}→${NC} Настройки (токен TG, ключ 9router): ${BIN_DIR}/simbiot wizard — когда будешь готов."
else
./venv/bin/python cli.py wizard || true
fi
fi
# ----------------------------------------------------------------------------
# 5. systemd (только если root и просили явно)
# ----------------------------------------------------------------------------
if [ "${FORCE_SYSTEMD:-}" = "yes" ]; then
if [ "$(id -u)" = "0" ] && command -v systemctl >/dev/null 2>&1 && systemctl is-system-running >/dev/null 2>&1; then
# рендер шаблона: плейсхолдеры __INSTALL_DIR__/__DATA_DIR__ всегда
# подставляются (голый cp клал бы юнит с заглушками — t-261004113351)
sed -e "s|__INSTALL_DIR__|$INSTALL_DIR|g" \
-e "s|__DATA_DIR__|${SIMBIOT_DATA_DIR:-$HOME/.simbiot-data}|g" \
"$INSTALL_DIR/scripts/simbiot-harness.service" > /etc/systemd/system/simbiot-harness.service
systemctl daemon-reload
systemctl enable --now simbiot-harness
say "${GREEN}→${NC} systemd: simbiot-harness запущен (systemctl status simbiot-harness)"
else
say "${YELLOW}→${NC} systemd-юнит пропущен (нужны root и systemd)."
fi
fi
# ----------------------------------------------------------------------------
# 6. Проверка одной проверяемой командой
# ----------------------------------------------------------------------------
say ""
if simbiot status >/dev/null 2>&1; then
say "${GREEN}✅ Установка завершена: simbiot status работает.${NC}"
else
echo "⚠ simbiot status вернул ошибку — см. вывод выше." >&2
exit 1
fi
# ----------------------------------------------------------------------------
# 7. Следующий шаг: подключение API агенту (ключи берутся на портале)
# ----------------------------------------------------------------------------
PORTAL_URL="https://cil.by"
if [ -s .env ] && grep -q '^ROUTER_KEY=..*' .env 2>/dev/null; then
say "${GREEN}→${NC} Ключ API уже задан в .env — агент готов."
say "Дальше: simbiot status (проверка), scripts/install.sh --systemd (автозапуск)."
else
say "${CYAN}→${NC} Остался один шаг — подключить API агенту:"
if [ "$INTERACTIVE" = 1 ]; then
# macOS / десктоп-линукс / WSL: открываем портал в браузере
if grep -qi microsoft /proc/version 2>/dev/null; then
{ command -v wslview >/dev/null 2>&1 && wslview "$PORTAL_URL" >/dev/null 2>&1; } || \
{ command -v explorer.exe >/dev/null 2>&1 && explorer.exe "$PORTAL_URL" >/dev/null 2>&1 || true; }
else
command -v xdg-open >/dev/null 2>&1 && { xdg-open "$PORTAL_URL" >/dev/null 2>&1 || true; }
fi
command -v open >/dev/null 2>&1 && { open "$PORTAL_URL" >/dev/null 2>&1 || true; }
say "→ Открыл портал в браузере (если не открылся — ссылка ниже)."
fi
say "${GREEN} $PORTAL_URL${NC} ${CYAN}← залогинься и возьми ключ API${NC}"
say " Затем: $BIN_DIR/simbiot wizard --router-key <КЛЮЧ>"
say " (или просто $BIN_DIR/simbiot wizard — спросит всё интерактивно)"
fi