fix: auto-discover local gateway url

This commit is contained in:
2026-06-30 09:57:17 +08:00
parent 99194a8f7d
commit fc88744c50
4 changed files with 115 additions and 13 deletions
+60 -9
View File
@@ -19,10 +19,13 @@ from typing import Any
APP_NAME = "codex-retry-gateway-tui"
FALLBACK_VERSION = "0.1.0"
DEFAULT_GATEWAY_ADMIN_PATH = "/__codex_retry_gateway"
DEFAULT_GATEWAY_URL = "http://127.0.0.1:4610/__codex_retry_gateway"
DEFAULT_API_URL = DEFAULT_GATEWAY_URL
DEFAULT_CONFIG_FILE = "~/.config/codex-retry-gateway-tui/api-url"
DEFAULT_STATUS_CONFIG_FILE = "~/.config/codex-retry-gateway-tui/status-url"
DEFAULT_GATEWAY_STATE_FILE = "~/.codex-retry-gateway/state.json"
DEFAULT_GATEWAY_JSON_CONFIG_FILE = "~/.codex-retry-gateway/config/config.json"
DEFAULT_VERSION_CHECK_URL = "https://gitea.shujk.top/shujakuin/codex-retry-gateway-tui/raw/branch/main/pyproject.toml"
DEFAULT_REFRESH_SECONDS = 10
DEFAULT_TIMEOUT_SECONDS = 5
@@ -60,14 +63,61 @@ def configured_url(env_names: tuple[str, ...], config_path: str, default: str =
return default
def read_json_object(path: str) -> dict[str, Any]:
try:
data = json.loads(Path(path).expanduser().read_text(encoding="utf-8"))
except Exception:
return {}
return data if isinstance(data, dict) else {}
def gateway_admin_url(base_url: str) -> str:
normalized = normalize_gateway_url(base_url)
if not normalized:
return ""
parsed = urllib.parse.urlparse(normalized)
path = parsed.path.rstrip("/")
if not path:
path = DEFAULT_GATEWAY_ADMIN_PATH
elif not path.endswith(DEFAULT_GATEWAY_ADMIN_PATH):
path = f"{path}{DEFAULT_GATEWAY_ADMIN_PATH}"
parsed = parsed._replace(path=path, params="", query="", fragment="")
return urllib.parse.urlunparse(parsed).rstrip("/")
def normalize_listen_host(value: Any) -> str:
host = str(value or "").strip()
if host in {"", "0.0.0.0", "::", "[::]", "*"}:
return "127.0.0.1"
return host
def discover_gateway_url() -> str:
state = read_json_object(DEFAULT_GATEWAY_STATE_FILE)
gateway_base_url = str(state.get("gateway_base_url") or "").strip()
if gateway_base_url:
return gateway_admin_url(gateway_base_url)
config = read_json_object(DEFAULT_GATEWAY_JSON_CONFIG_FILE)
listen_host = normalize_listen_host(config.get("listen_host"))
listen_port = as_int(config.get("listen_port"))
if listen_host and listen_port > 0:
return gateway_admin_url(f"http://{listen_host}:{listen_port}")
return ""
def default_api_url() -> str:
return normalize_gateway_url(
configured_url(
("CODEX_RETRY_GATEWAY_TUI_API_URL",),
os.environ.get("CODEX_RETRY_GATEWAY_TUI_API_URL_FILE", DEFAULT_CONFIG_FILE),
DEFAULT_API_URL,
)
configured = configured_url(
("CODEX_RETRY_GATEWAY_TUI_API_URL",),
os.environ.get("CODEX_RETRY_GATEWAY_TUI_API_URL_FILE", DEFAULT_CONFIG_FILE),
"",
)
if configured:
return gateway_admin_url(configured)
discovered = discover_gateway_url()
if discovered:
return discovered
return gateway_admin_url(DEFAULT_API_URL)
def config_file_path() -> Path:
@@ -128,7 +178,7 @@ def build_api_url(gateway_url: str, suffix: str, params: dict[str, Any] | None =
def write_api_url_config(api_url: str) -> Path:
value = str(api_url or "").strip()
value = gateway_admin_url(api_url)
if not value:
raise ValueError("api url is empty")
path = config_file_path()
@@ -639,7 +689,7 @@ def fetch_dashboard_snapshot(
filter_text: str = "",
current_view: str = "overview",
) -> dict[str, Any]:
gateway_root = normalize_gateway_url(gateway_url)
gateway_root = gateway_admin_url(gateway_url)
endpoints = {
"status": status_url or gateway_status_url(gateway_root),
"logs": build_api_url(gateway_root, "/api/logs", {"limit": 200}),
@@ -695,7 +745,7 @@ def run_textual(api_url: str, status_url: str, refresh_seconds: int, timeout: in
print("Textual is required. Run with: uv run --with textual python codex_retry_gateway_tui.py", file=sys.stderr)
return 2
gateway_base_url = normalize_gateway_url(api_url)
gateway_base_url = gateway_admin_url(api_url)
status_api_url = status_url or gateway_status_url(gateway_base_url)
class CodexRetryGatewayTui(App[None]):
@@ -1193,6 +1243,7 @@ def print_once(snapshot: dict[str, Any], filter_text: str = "") -> None:
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
args.api_url = gateway_admin_url(args.api_url)
status_url = str(args.status_url or "").strip() or default_status_url()
if args.save_config or args.install:
config_path = write_api_url_config(args.api_url)