simbiot-harness/harness/paths.py

160 lines
7.6 KiB
Python
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.

"""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