feat: add api key auth, devii agent, openai gateway, and admin service management

This commit introduces a comprehensive set of new features including API key authentication with CLI management commands (get, reset, backfill), a Devii agentic assistant with WebSocket terminal and session bootstrap, an OpenAI-compatible LLM gateway service, and an admin service management panel. It also adds Playwright browser automation for bot support, configures internal gateway URLs, refactors content editing/deletion to support JSON API responses, and updates documentation across AGENTS.md, README.md, and the developer docs site.
This commit is contained in:
2026-06-08 15:38:33 +00:00
parent 921e382cbc
commit e0535bb7c5
270 changed files with 54408 additions and 541 deletions
+84
View File
@@ -0,0 +1,84 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import logging
from typing import Any, Callable
from devplacepy.database import db
from .agentic import LessonStore
from .config import Settings
from .cost.tracker import Pricing
from .llm import LLMClient
from .session import DeviiSession
from .store import ConversationStore, TurnAudit, UsageLedger
from .tasks.store import TaskStore, memory_db
logger = logging.getLogger("devii.hub")
SettingsBuilder = Callable[[str, str], "tuple[Settings, Pricing]"]
class DeviiHub:
def __init__(self, settings_builder: SettingsBuilder) -> None:
self._build = settings_builder
self._stores: dict[str, Any] = {
"conversations": ConversationStore(),
"ledger": UsageLedger(),
"turns": TurnAudit(),
}
self._sessions: dict[tuple[str, str], DeviiSession] = {}
@property
def ledger(self) -> UsageLedger:
return self._stores["ledger"]
def get_or_create(
self, owner_kind: str, owner_id: str, username: str, api_key: str, base_url: str
) -> DeviiSession:
key = (owner_kind, owner_id)
session = self._sessions.get(key)
if session is not None:
return session
settings, pricing = self._build(api_key, base_url)
llm = LLMClient(settings)
# Persistent, owner-isolated stores for signed-in users; ephemeral in-memory for guests.
owned_db = db if owner_kind == "user" else memory_db()
task_store = TaskStore(owned_db, owner_kind, owner_id)
lessons = LessonStore(owned_db, owner_kind, owner_id)
session = DeviiSession(
owner_kind, owner_id, username, settings, llm, lessons, pricing, task_store, self._stores
)
if owner_kind == "user":
saved = self._stores["conversations"].load(owner_kind, owner_id)
if saved:
session.restore_history(saved)
self._sessions[key] = session
logger.info("Created session %s/%s (total %d)", owner_kind, owner_id, len(self._sessions))
return session
def find(self, owner_kind: str, owner_id: str) -> DeviiSession | None:
return self._sessions.get((owner_kind, owner_id))
def active_sessions(self) -> int:
return len(self._sessions)
def connection_count(self) -> int:
return sum(s.connection_count for s in self._sessions.values())
async def gc_idle(self) -> int:
removed = 0
for key, session in list(self._sessions.items()):
if session.connection_count == 0:
await session.aclose()
del self._sessions[key]
removed += 1
if removed:
logger.info("GC removed %d idle session(s)", removed)
return removed
async def aclose(self) -> None:
for session in list(self._sessions.values()):
await session.aclose()
self._sessions.clear()