160 lines
7.6 KiB
Python
160 lines
7.6 KiB
Python
"""Simbiot Harness — единая карта персистентных путей (SIMBIOT_HOME).
|
||
|
||
t-261004113351-2858c1 (мультитенантность): память и знания инстанса — данные,
|
||
а не код. Репозиторий = чистый код; всё, что живёт дольше процесса (ES-база,
|
||
RAG-индекс знаний, входящие каналы, выжимки, секреты, доки инстанса),
|
||
размещается в одном каталоге-хоме.
|
||
|
||
SIMBIOT_HOME (env) — корень данных инстанса. Задан → структура:
|
||
|
||
<home>/db/harness.db — ES-база (события, проекции, delivery-леджер)
|
||
<home>/rag/rag.db — RAG-индекс знаний (векторные чанки)
|
||
<home>/env/.env — секреты инстанса (токены, ключи роутера)
|
||
<home>/inbox/ — входящие каналов (portal/state, legacy outbox)
|
||
<home>/digests/ — файлы-выжимки уровней (summary_ref)
|
||
<home>/audit/ — ключи крипто-аудита (ed25519)
|
||
<home>/docs/ — проектные документы инстанса (aum/balances.json)
|
||
<home>/voice/ — кэш голосовых (если используется ASR)
|
||
<home>/run/ — runtime-штампы каналов (channel_<tg|portal>.json)
|
||
|
||
Не задан (legacy) → всё по-старому из корня репо: harness/harness.db, .env,
|
||
docs/, out/, .audit/ — обратная совместимость существующих установок и
|
||
systemd-юнитов без правки окружения.
|
||
|
||
Приоритет источников (12-фактор): точечная env-переменная (SIMBIOT_DB,
|
||
SIMBIOT_RAG_DB, SIMBIOT_AUDIT_KEY_DIR, ...) сильнее схемы SIMBIOT_HOME,
|
||
SIMBIOT_HOME сильнее legacy-дефолта.
|
||
"""
|
||
import os
|
||
|
||
_HARNESS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
REPO_ROOT = os.path.dirname(_HARNESS_DIR)
|
||
|
||
_HOME = os.environ.get("SIMBIOT_HOME", "").strip()
|
||
IS_ISOLATED = bool(_HOME)
|
||
|
||
|
||
def home() -> str:
|
||
"""Корень данных инстанса (SIMBIOT_HOME или корень репо — legacy)."""
|
||
return _HOME or REPO_ROOT
|
||
|
||
|
||
# ── ES-база: события/проекции/идемпотентная доставка ────────────────────────
|
||
def db_path() -> str:
|
||
env = os.environ.get("SIMBIOT_DB", "").strip()
|
||
if env:
|
||
return env
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "db", "harness.db")
|
||
return os.path.join(_HARNESS_DIR, "harness.db") # legacy: как было
|
||
|
||
|
||
# ── .env: секреты инстанса ──────────────────────────────────────────────────
|
||
def env_path() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "env", ".env")
|
||
return os.path.join(REPO_ROOT, ".env")
|
||
|
||
|
||
# ── RAG-индекс знаний ───────────────────────────────────────────────────────
|
||
def rag_db() -> str:
|
||
env = os.environ.get("SIMBIOT_RAG_DB", "").strip()
|
||
if env:
|
||
return env
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "rag", "rag.db")
|
||
return "/root/rag/rag.db" # legacy (общая база кооп-хоста, как было)
|
||
|
||
|
||
# ── выжимки уровней (файлы summary_ref) ─────────────────────────────────────
|
||
def digests_dir() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "digests")
|
||
return os.path.join(REPO_ROOT, "out") # legacy out/
|
||
|
||
|
||
# ── проектные документы инстанса ────────────────────────────────────────────
|
||
def docs_dir() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "docs")
|
||
return os.path.join(REPO_ROOT, "docs")
|
||
|
||
|
||
# ── ключи крипто-аудита ─────────────────────────────────────────────────────
|
||
def audit_key_dir() -> str:
|
||
env = os.environ.get("SIMBIOT_AUDIT_KEY_DIR", "").strip()
|
||
if env:
|
||
return env
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "audit")
|
||
return os.path.join(REPO_ROOT, ".audit")
|
||
|
||
|
||
# ── входящие каналов: state + legacy-каталоги файлопередачи ─────────────────
|
||
def channel_state_file() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "inbox", ".channel-last")
|
||
return os.path.join(REPO_ROOT, "portal", "inbox", ".channel-last")
|
||
|
||
|
||
def inbox_pending_dir() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "inbox", "pending")
|
||
return os.path.join(REPO_ROOT, "portal", "inbox", "pending")
|
||
|
||
|
||
def inbox_outbox_dir() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "inbox", "outbox")
|
||
return os.path.join(REPO_ROOT, "portal", "inbox", "outbox")
|
||
|
||
|
||
# ── входящие файлы каналов (t-261005135853): documents/photo на диск ────────
|
||
def inbox_files_dir() -> str:
|
||
"""Каталог входящих файлов (documents/photo из TG).
|
||
|
||
Голосовые живут в voice/ (кэш ASR), а документы/фото — данные уровня
|
||
инбокса: выписки, сканы, артефакты Алекса. Не задаём SIMBIOT_HOME
|
||
(legacy) → /var/lib/simbiot-harness/inbox/files — тот же корень, где
|
||
голосовые (voice_dir legacy), рядом с inbox/ каналов портала.
|
||
"""
|
||
env = os.environ.get("SIMBIOT_INBOX_FILES_DIR", "").strip()
|
||
if env:
|
||
return env
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "inbox", "files")
|
||
return "/var/lib/simbiot-harness/inbox/files"
|
||
|
||
|
||
# ── runtime-штампы каналов для дашборда ─────────────────────────────────────
|
||
def stamp_dir() -> str:
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "run")
|
||
return REPO_ROOT # legacy: <repo>/harness/channel_<name>.json
|
||
|
||
|
||
# ── кэш голосовых ───────────────────────────────────────────────────────────
|
||
def voice_dir() -> str:
|
||
env = os.environ.get("SIMBIOT_VOICE_DIR", "").strip()
|
||
if env:
|
||
return env
|
||
if IS_ISOLATED:
|
||
return os.path.join(_HOME, "voice")
|
||
return "/var/lib/simbiot-harness/voice" # legacy
|
||
|
||
|
||
def ensure_instance_dirs() -> list:
|
||
"""Создать структуру хома изолированного инстанса. Идемпотентно.
|
||
|
||
В legacy-режиме ничего не создаёт (там всё уже лежит как лежало).
|
||
Возвращает список созданных каталогов.
|
||
"""
|
||
if not IS_ISOLATED:
|
||
return []
|
||
made = []
|
||
for sub in ("db", "rag", "env", "inbox", "digests", "audit", "docs", "voice", "run"):
|
||
p = os.path.join(_HOME, sub)
|
||
if not os.path.isdir(p):
|
||
os.makedirs(p, exist_ok=True)
|
||
made.append(p)
|
||
return made
|