free-claude-code/cli/entrypoints.py
Ali Khokhar 51157f91bd
Refactor admin config into catalog-driven package (#926)
## Problem

Admin config was a single responsibility hub with manually duplicated
provider metadata. Provider labels, fields, template loading,
validation, persistence, and status lived in one place.

## Changes

| Before | After |
| --- | --- |
| Admin config lived in one large `api/admin_config.py` module. | Admin
config lives in package modules for manifest, sources, values,
validation, persistence, and status. |
| Provider admin fields and UI labels were manually duplicated. |
Provider admin fields and display names derive from `PROVIDER_CATALOG`
with admin-only help overrides. |
| `fcc-init` and Admin UI loaded `.env.example` separately. | `fcc-init`
and Admin UI use shared `config.env_template` loading. |
| Architecture docs pointed to the old admin config module. |
Architecture docs describe the package owners and catalog-driven
provider manifest. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR refactors admin configuration into a catalog-driven package. The
main changes are:

- Split the former monolithic `api/admin_config.py` into manifest,
source loading, value presentation, validation, persistence, and
provider status modules.
- Generate provider admin fields and display names from
`PROVIDER_CATALOG` with admin-specific help overrides.
- Share `.env.example` loading between `fcc-init` and Admin UI defaults
through `config.env_template`.
- Update admin routes, Admin UI provider labels, architecture docs,
version metadata, and contract/API tests for the new module layout.
</details>

<h3>Confidence Score: 5/5</h3>

The refactor appears merge-safe with no code issues identified in the
reviewed changes.

The package split, catalog-driven provider metadata, shared environment
template loading, route updates, and tests/docs changes are cohesive and
covered by corresponding contract/API/CLI test updates.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- T-Rex ran manifest validation for catalog provider before and after
routes, capturing base and head responses and catalog-alignment checks,
and confirmed the validation completed successfully.
- T-Rex evaluated the shared-env-template scenarios, observing the
before run with no config.env\_template module and the after run with
the module present, with patched loader values and all consistency
checks passing, and the run exited with code 0.
- T-Rex executed the package-admin-workflow validation, verifying the
base and after import paths, the load/validate/write workflow produced
matching outputs, and the run completed with exit code 0.

<a
href="https://app.greptile.com/trex/runs/12529845/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=1"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1"
height="32"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<sub>Reviews (1): Last reviewed commit: ["Refactor admin config into
catalog-drive..."](d6239d7953)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=40315222)</sub>

<!-- /greptile_comment -->
2026-06-27 15:37:29 -07:00

150 lines
4.5 KiB
Python

"""CLI entry points for the installed package."""
from __future__ import annotations
import os
import shutil
import threading
import time
import webbrowser
from pathlib import Path
import uvicorn
from api.admin_urls import local_admin_url, local_proxy_root_url
from api.app import GracefulLifespanApp, create_app
from cli.launchers.common import preflight_proxy
from cli.process_registry import (
kill_all_best_effort,
)
from config.env_template import load_env_template
from config.paths import (
config_dir_path,
legacy_env_paths,
managed_env_path,
)
from config.settings import Settings, get_settings
SERVER_GRACEFUL_SHUTDOWN_SECONDS = 5
def serve() -> None:
"""Start the FastAPI server (registered as `fcc-server` script)."""
opened_admin_browser = False
try:
try:
while True:
_migrate_legacy_env_if_missing()
settings = get_settings()
if not _run_supervised_server(
settings, open_admin_browser=not opened_admin_browser
):
return
opened_admin_browser = True
get_settings.cache_clear()
except KeyboardInterrupt:
return
finally:
kill_all_best_effort()
def _admin_browser_open_enabled() -> bool:
"""Whether to open /admin when the server becomes reachable (FCC_OPEN_BROWSER)."""
raw = os.environ.get("FCC_OPEN_BROWSER", "true").strip().lower()
return raw not in {"", "0", "false", "no"}
def _schedule_open_admin_browser(settings: Settings) -> None:
"""After /health succeeds, open the admin UI in the default browser (daemon thread)."""
if not _admin_browser_open_enabled():
return
admin_url = local_admin_url(settings)
proxy_root_url = local_proxy_root_url(settings)
def open_when_ready() -> None:
deadline = time.monotonic() + 30.0
while time.monotonic() < deadline:
if preflight_proxy(proxy_root_url) is None:
webbrowser.open(admin_url)
return
time.sleep(0.15)
threading.Thread(
target=open_when_ready, name="fcc-open-admin-browser", daemon=True
).start()
def _run_supervised_server(settings: Settings, *, open_admin_browser: bool) -> bool:
"""Run one uvicorn server instance; return whether admin requested restart."""
restart_requested = False
server_holder: dict[str, uvicorn.Server] = {}
def request_restart() -> None:
nonlocal restart_requested
restart_requested = True
if server := server_holder.get("server"):
server.should_exit = True
app = create_app(lifespan_enabled=False)
app.state.admin_restart_callback = request_restart
asgi_app = GracefulLifespanApp(app)
config = uvicorn.Config(
asgi_app,
host=settings.host,
port=settings.port,
log_level="debug",
timeout_graceful_shutdown=SERVER_GRACEFUL_SHUTDOWN_SECONDS,
)
server = uvicorn.Server(config)
server_holder["server"] = server
if open_admin_browser:
_schedule_open_admin_browser(settings)
server.run()
return restart_requested
def init() -> None:
"""Scaffold config at ~/.fcc/.env (registered as `fcc-init`)."""
config_dir = config_dir_path()
env_file = managed_env_path()
migrated_from = _migrate_legacy_env_if_missing()
if migrated_from is not None:
print(f"Config migrated from {migrated_from} to {env_file}")
print(
"Edit it to set your API keys and model preferences, then run: fcc-server"
)
return
if env_file.exists():
print(f"Config already exists at {env_file}")
print("Delete it first if you want to reset to defaults.")
return
config_dir.mkdir(parents=True, exist_ok=True)
template = load_env_template()
env_file.write_text(template, encoding="utf-8")
print(f"Config created at {env_file}")
print("Edit it to set your API keys and model preferences, then run: fcc-server")
def _migrate_legacy_env_if_missing() -> Path | None:
"""Copy a legacy user env into the managed config path when absent."""
env_file = managed_env_path()
if env_file.exists():
return None
# TODO: Remove after the ~/.fcc/.env migration has had a release cycle.
for legacy_env in legacy_env_paths():
if not legacy_env.is_file():
continue
env_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(legacy_env, env_file)
return legacy_env
return None