mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-09 16:00:45 +00:00
## 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 -->
150 lines
4.5 KiB
Python
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
|