feat(cli): support github-copilot in provider logout

Logout previously claimed to support github-copilot in --help text but had
no registered handler, so `provider logout github-copilot` failed with
"Logout not implemented". Add the handler, sharing token deletion with the
codex flow via `_delete_oauth_files`. Tighten handler-table types, fix the
codex test fixture filename, and cover github-copilot plus the unknown
provider path.
This commit is contained in:
chengyongru
2026-05-04 12:10:06 +08:00
committed by Xubin Ren
parent 807b8188e3
commit 3ceabdecd5
3 changed files with 105 additions and 16 deletions
+43 -11
View File
@@ -5,6 +5,7 @@ import os
import select
import signal
import sys
from collections.abc import Callable
from contextlib import nullcontext, suppress
from pathlib import Path
from typing import Any
@@ -1487,8 +1488,13 @@ provider_app = typer.Typer(help="Manage providers")
app.add_typer(provider_app, name="provider")
_LOGIN_HANDLERS: dict[str, Any] = {}
_LOGOUT_HANDLERS: dict[str, Any] = {}
_LOGIN_HANDLERS: dict[str, Callable[[], None]] = {}
_LOGOUT_HANDLERS: dict[str, Callable[[], None]] = {}
_PROVIDER_DISPLAY: dict[str, str] = {
"openai_codex": "OpenAI Codex",
"github_copilot": "GitHub Copilot",
}
def _register_login(name: str):
@@ -1587,20 +1593,46 @@ def _logout_openai_codex() -> None:
raise typer.Exit(1)
storage = FileTokenStorage(token_filename=OPENAI_CODEX_PROVIDER.token_filename)
_delete_oauth_files(storage.get_token_path(), _PROVIDER_DISPLAY["openai_codex"])
@_register_logout("github_copilot")
def _logout_github_copilot() -> None:
"""Clear local OAuth credentials for GitHub Copilot."""
try:
from nanobot.providers.github_copilot_provider import get_storage
except ImportError:
console.print("[red]GitHub Copilot provider unavailable. Ensure oauth-cli-kit is installed.[/red]")
raise typer.Exit(1)
storage = get_storage()
_delete_oauth_files(storage.get_token_path(), _PROVIDER_DISPLAY["github_copilot"])
def _delete_oauth_files(token_path: Path, provider_label: str) -> None:
"""Delete OAuth token and lock files, reporting the result."""
removed_paths: list[Path] = []
for path in (storage.get_token_path(), storage.get_token_path().with_suffix(".lock")):
if path.exists():
skipped: list[tuple[Path, OSError]] = []
for path in (token_path, token_path.with_suffix(".lock")):
try:
path.unlink()
removed_paths.append(path)
except FileNotFoundError:
continue
except OSError as exc:
skipped.append((path, exc))
continue
removed_paths.append(path)
if not removed_paths:
console.print("[yellow]! No local OAuth credentials found for OpenAI Codex[/yellow]")
if not removed_paths and not skipped:
console.print(f"[yellow]! No local OAuth credentials found for {provider_label}[/yellow]")
return
console.print("[green]✓ Logged out from OpenAI Codex[/green]")
for path in removed_paths:
console.print(f"[dim]Removed: {path}[/dim]")
if removed_paths:
console.print(f"[green]✓ Logged out from {provider_label}[/green]")
for path in removed_paths:
console.print(f"[dim]Removed: {path}[/dim]")
for path, exc in skipped:
console.print(f"[yellow]! Could not remove {path}: {exc}[/yellow]")
@_register_login("github_copilot")