feat: add admin panel

This commit is contained in:
h
2026-05-20 13:00:08 +02:00
parent 7970d4be9b
commit 0128191ac3
26 changed files with 2985 additions and 115 deletions
+35 -4
View File
@@ -12,6 +12,7 @@ from beaver_gateway.agents.base import ExposedMcp
from beaver_gateway.agents.claude import ClaudeAgent
from beaver_gateway.agents.raycast import RaycastAgent, RemoteTool, UserPreferences
from beaver_gateway.core.registry import Gateway
from beaver_gateway.frontends.admin import AdminFrontend
from beaver_gateway.frontends.anthropic import AnthropicMessagesFrontend
from beaver_gateway.frontends.mcp_server import McpServerFrontend
from beaver_gateway.mcp.types import McpServer
@@ -28,6 +29,7 @@ def current_time() -> str:
return datetime.now().astimezone().isoformat()
gateway = Gateway(
agents=[
# Phase 2.2 — ClaudeCodeBackendAdapter routes this agent's
@@ -98,7 +100,7 @@ gateway = Gateway(
# ClaudeCode adapter forwards that URL into
# ``BackendOptions.mcp_servers``. Phase 3's ``McpServerFrontend``
# reverse-proxies the same internal URL out to external clients.
McpServer.python_tool(name="time", tools=[current_time]),
McpServer.python_tool(name="time", tools=[current_time])
# Phase 3 — illustrates the ``lenient`` flag. Real-world stdio MCPs
# sometimes print "Processing..." or other chatter to stdout before
# their actual JSON-RPC frames; the default mcp client forwards
@@ -124,12 +126,41 @@ gateway = Gateway(
# Phase 1.4 — expose the agents as `model=<name>` on an
# Anthropic-compatible Messages endpoint. Auth comes from
# `BOOTSTRAP_TOKENS` in the env (`name1:value1,name2:value2`).
#
# Behind a reverse proxy (Caddy / nginx / Cloudflare) pass
# `public_base_url=` so the admin dashboard advertises the
# outside URL instead of `host:port`. Caddy strips its own
# prefix and the frontend's internal paths (`/v1/messages`,
# `/v1/models`) get appended:
# Caddy: handle_path /ai/* { reverse_proxy localhost:8000 }
# Config: AnthropicMessagesFrontend(
# port=8000,
# public_base_url="https://domain.com/ai")
# Result: https://domain.com/ai/v1/messages
AnthropicMessagesFrontend(host="0.0.0.0", port=8000),
# Phase 3 — re-exposes every declared `McpServer` outside the
# gateway with bearer auth + audit log. Per-namespace endpoints
# at `/mcp/<name>/`; flat bundle at `/mcp/all/`. Discovery page
# (HTML, auth-gated) at `/` with copy-pastable Cursor / Claude
# gateway with bearer auth + audit log. Each namespace lives
# at `/<name>/` on this port (the port itself disambiguates
# MCP traffic — no extra `/mcp` segment in the route); a flat
# bundle is published at `/all/`. Discovery page (HTML,
# auth-gated) at `/` with copy-pastable Cursor / Claude
# Desktop snippets. Auth re-uses `BOOTSTRAP_TOKENS`.
#
# Same `public_base_url=` knob as above. Caddy strips its
# prefix; the frontend's `/<name>/` segment gets appended:
# Caddy: handle_path /mcp/* { reverse_proxy localhost:8001 }
# Config: McpServerFrontend(
# port=8001,
# public_base_url="https://domain.com/mcp")
# Result: https://domain.com/mcp/<name>/ (and /mcp/all/)
McpServerFrontend(host="0.0.0.0", port=8001),
# Phase 4.3 — browser admin UI. Creds come from
# `ADMIN_USER`/`ADMIN_PASS`; the session cookie is signed with
# `SESSION_SECRET`. Use it to mint tokens (Argon2-hashed in
# the DB), revoke them, and watch the audit log. Scope is
# enforced on the bearer frontends: tokens minted with scope
# `messages` only work on `/v1/messages`; `mcp` only on
# `/mcp/<name>`; `*` works everywhere.
AdminFrontend(host="0.0.0.0", port=8002),
],
)
+5
View File
@@ -24,6 +24,11 @@ services:
# config.py declares one, so set these (or remove the agent)
# before exposing port 8000.
ports:
# /v1/messages frontend
- "8000:8000"
# MCP server frontend
- "8001:8001"
# Admin UI (Phase 4.3) — change ADMIN_USER/ADMIN_PASS/SESSION_SECRET
- "8002:8002"
volumes:
- ./config.py:/config/config.py:ro