## Agents Added - Alateya: R&D, biotech, innovations - Clan (Spirit): Community spirit agent - Eonarch: Consciousness evolution agent ## Changes - docker-compose.node1.yml: Added tokens for all 3 new agents - gateway-bot/http_api.py: Added configs and webhook endpoints - gateway-bot/clan_prompt.txt: New prompt file - gateway-bot/eonarch_prompt.txt: New prompt file ## Fixes - Fixed ROUTER_URL from :9102 to :8000 (internal container port) - All 9 Telegram agents now working ## Documentation - Created PROJECT-MASTER-INDEX.md - single entry point - Added various status documents and scripts Tokens configured: - Helion, NUTRA, Agromatrix (existing) - Alateya, Clan, Eonarch (new) - Druid, GreenFood, DAARWIZZ (configured)
134 lines
3.9 KiB
Python
134 lines
3.9 KiB
Python
"""
|
|
Brand Registry Service
|
|
- Stores Theme Packs (theme.json + assets refs)
|
|
- Serves immutable theme versions by brand_id
|
|
"""
|
|
|
|
from fastapi import FastAPI, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import Any, Dict, Optional
|
|
from datetime import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DATA_DIR = Path(os.getenv("BRAND_REGISTRY_DATA", "/data/brand-registry"))
|
|
THEMES_DIR = DATA_DIR / "brands"
|
|
|
|
app = FastAPI(
|
|
title="Brand Registry Service",
|
|
description="Single source of truth for brand themes",
|
|
version="0.1.0"
|
|
)
|
|
|
|
|
|
class ThemePublishRequest(BaseModel):
|
|
theme: Dict[str, Any]
|
|
theme_version: Optional[str] = None
|
|
metadata: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
class ThemeResponse(BaseModel):
|
|
brand_id: str
|
|
theme_version: str
|
|
theme: Dict[str, Any]
|
|
metadata: Optional[Dict[str, Any]] = None
|
|
created_at: str
|
|
|
|
|
|
def _ensure_dirs() -> None:
|
|
THEMES_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def _theme_path(brand_id: str, theme_version: str) -> Path:
|
|
return THEMES_DIR / brand_id / "themes" / theme_version / "theme.json"
|
|
|
|
|
|
def _meta_path(brand_id: str, theme_version: str) -> Path:
|
|
return THEMES_DIR / brand_id / "themes" / theme_version / "meta.json"
|
|
|
|
|
|
def _list_versions(brand_id: str) -> list:
|
|
base = THEMES_DIR / brand_id / "themes"
|
|
if not base.exists():
|
|
return []
|
|
versions = [p.name for p in base.iterdir() if p.is_dir()]
|
|
return sorted(versions)
|
|
|
|
|
|
@app.get("/")
|
|
async def root() -> Dict[str, Any]:
|
|
_ensure_dirs()
|
|
return {
|
|
"service": "brand-registry",
|
|
"status": "running",
|
|
"data_dir": str(DATA_DIR),
|
|
"version": "0.1.0"
|
|
}
|
|
|
|
|
|
@app.get("/health")
|
|
async def health() -> Dict[str, Any]:
|
|
_ensure_dirs()
|
|
return {"status": "healthy"}
|
|
|
|
|
|
@app.post("/brands/{brand_id}/themes", response_model=ThemeResponse)
|
|
async def publish_theme(brand_id: str, payload: ThemePublishRequest) -> ThemeResponse:
|
|
_ensure_dirs()
|
|
theme_version = payload.theme_version or f"v1-{uuid.uuid4().hex[:8]}"
|
|
theme_path = _theme_path(brand_id, theme_version)
|
|
theme_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
created_at = datetime.utcnow().isoformat() + "Z"
|
|
meta = {
|
|
"brand_id": brand_id,
|
|
"theme_version": theme_version,
|
|
"created_at": created_at,
|
|
"metadata": payload.metadata or {}
|
|
}
|
|
|
|
theme_path.write_text(json.dumps(payload.theme, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
_meta_path(brand_id, theme_version).write_text(json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
|
|
logger.info("Published theme: %s/%s", brand_id, theme_version)
|
|
return ThemeResponse(
|
|
brand_id=brand_id,
|
|
theme_version=theme_version,
|
|
theme=payload.theme,
|
|
metadata=payload.metadata or {},
|
|
created_at=created_at
|
|
)
|
|
|
|
|
|
@app.get("/brands/{brand_id}/themes/{theme_version}", response_model=ThemeResponse)
|
|
async def get_theme(brand_id: str, theme_version: str) -> ThemeResponse:
|
|
theme_path = _theme_path(brand_id, theme_version)
|
|
if not theme_path.exists():
|
|
raise HTTPException(status_code=404, detail="Theme not found")
|
|
|
|
theme = json.loads(theme_path.read_text(encoding="utf-8"))
|
|
meta_path = _meta_path(brand_id, theme_version)
|
|
meta = json.loads(meta_path.read_text(encoding="utf-8")) if meta_path.exists() else {}
|
|
|
|
return ThemeResponse(
|
|
brand_id=brand_id,
|
|
theme_version=theme_version,
|
|
theme=theme,
|
|
metadata=meta.get("metadata"),
|
|
created_at=meta.get("created_at", "")
|
|
)
|
|
|
|
|
|
@app.get("/brands/{brand_id}/latest", response_model=ThemeResponse)
|
|
async def get_latest(brand_id: str) -> ThemeResponse:
|
|
versions = _list_versions(brand_id)
|
|
if not versions:
|
|
raise HTTPException(status_code=404, detail="No themes for brand")
|
|
return await get_theme(brand_id, versions[-1])
|