mirror of
https://github.com/xtekky/gpt4free.git
synced 2026-04-22 15:47:11 +08:00
Refactor CLI authentication commands and improve argument parsing
This commit is contained in:
@@ -25,7 +25,7 @@ class GithubCopilot(OpenaiTemplate):
|
|||||||
allowing users to authenticate via browser without sharing credentials.
|
allowing users to authenticate via browser without sharing credentials.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
1. Run `g4f-github-copilot login` to authenticate
|
1. Run `g4f auth github-copilot` to authenticate
|
||||||
2. Use the provider normally after authentication
|
2. Use the provider normally after authentication
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -270,7 +270,7 @@ class GithubCopilot(OpenaiTemplate):
|
|||||||
usage = await resp.json()
|
usage = await resp.json()
|
||||||
return usage
|
return usage
|
||||||
|
|
||||||
async def main():
|
async def main(args: Optional[List[str]] = None):
|
||||||
"""CLI entry point for GitHub Copilot OAuth authentication."""
|
"""CLI entry point for GitHub Copilot OAuth authentication."""
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ Examples:
|
|||||||
# Logout command
|
# Logout command
|
||||||
subparsers.add_parser("logout", help="Remove saved credentials")
|
subparsers.add_parser("logout", help="Remove saved credentials")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.command == "login":
|
if args.command == "login":
|
||||||
try:
|
try:
|
||||||
@@ -334,7 +334,7 @@ Examples:
|
|||||||
print(f" (Could not read credential details: {e})")
|
print(f" (Could not read credential details: {e})")
|
||||||
else:
|
else:
|
||||||
print("✗ No credentials found")
|
print("✗ No credentials found")
|
||||||
print(f"\nRun 'g4f-github-copilot login' to authenticate.")
|
print(f"\nRun 'g4f auth github-copilot' to authenticate.")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -370,9 +370,9 @@ Examples:
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def cli_main():
|
def cli_main(args: Optional[List[str]] = None):
|
||||||
"""Synchronous CLI entry point for setup.py console_scripts."""
|
"""Synchronous CLI entry point for setup.py console_scripts."""
|
||||||
asyncio.run(main())
|
asyncio.run(main(args))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1499,7 +1499,7 @@ class Antigravity(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
return cache_path
|
return cache_path
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main(args: Optional[List[str]] = None):
|
||||||
"""CLI entry point for Antigravity authentication."""
|
"""CLI entry point for Antigravity authentication."""
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
@@ -1537,7 +1537,7 @@ Examples:
|
|||||||
# Logout command
|
# Logout command
|
||||||
subparsers.add_parser("logout", help="Remove saved credentials")
|
subparsers.add_parser("logout", help="Remove saved credentials")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.command == "login":
|
if args.command == "login":
|
||||||
try:
|
try:
|
||||||
@@ -1616,9 +1616,9 @@ Examples:
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def cli_main():
|
def cli_main(args: Optional[List[str]] = None):
|
||||||
"""Synchronous CLI entry point for setup.py console_scripts."""
|
"""Synchronous CLI entry point for setup.py console_scripts."""
|
||||||
asyncio.run(main())
|
asyncio.run(main(args))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1171,7 +1171,7 @@ class GeminiCLI(AsyncGeneratorProvider, ProviderModelMixin):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main(args: Optional[List[str]] = None):
|
||||||
"""CLI entry point for GeminiCLI authentication."""
|
"""CLI entry point for GeminiCLI authentication."""
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
@@ -1203,7 +1203,7 @@ Examples:
|
|||||||
# Logout command
|
# Logout command
|
||||||
subparsers.add_parser("logout", help="Remove saved credentials")
|
subparsers.add_parser("logout", help="Remove saved credentials")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.command == "login":
|
if args.command == "login":
|
||||||
try:
|
try:
|
||||||
@@ -1241,7 +1241,7 @@ Examples:
|
|||||||
print(f" (Could not read credential details: {e})")
|
print(f" (Could not read credential details: {e})")
|
||||||
else:
|
else:
|
||||||
print("✗ No credentials found")
|
print("✗ No credentials found")
|
||||||
print(f"\nRun 'g4f-geminicli login' to authenticate.")
|
print(f"\nRun 'g4f auth gemini-cli login' to authenticate.")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -1274,9 +1274,9 @@ Examples:
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def cli_main():
|
def cli_main(args: Optional[List[str]] = None):
|
||||||
"""Synchronous CLI entry point for setup.py console_scripts."""
|
"""Synchronous CLI entry point for setup.py console_scripts."""
|
||||||
asyncio.run(main())
|
asyncio.run(main(args))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class QwenCode(OpenaiTemplate):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main(args: Optional[list[str]] = None):
|
||||||
"""CLI entry point for QwenCode authentication."""
|
"""CLI entry point for QwenCode authentication."""
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ Examples:
|
|||||||
# Logout command
|
# Logout command
|
||||||
subparsers.add_parser("logout", help="Remove saved credentials")
|
subparsers.add_parser("logout", help="Remove saved credentials")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.command == "login":
|
if args.command == "login":
|
||||||
try:
|
try:
|
||||||
@@ -188,7 +188,7 @@ Examples:
|
|||||||
print(f" (Could not read credential details: {e})")
|
print(f" (Could not read credential details: {e})")
|
||||||
else:
|
else:
|
||||||
print("✗ No credentials found")
|
print("✗ No credentials found")
|
||||||
print(f"\nRun 'g4f-qwencode login' to authenticate.")
|
print(f"\nRun 'g4f auth qwencode' to authenticate.")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -224,9 +224,9 @@ Examples:
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
def cli_main():
|
def cli_main(args: Optional[list[str]] = None):
|
||||||
"""Synchronous CLI entry point for setup.py console_scripts."""
|
"""Synchronous CLI entry point for setup.py console_scripts."""
|
||||||
asyncio.run(main())
|
asyncio.run(main(args))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+96
-16
@@ -13,6 +13,8 @@ runtime function depending on the CLI arguments.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
# Local imports (within g4f package)
|
# Local imports (within g4f package)
|
||||||
@@ -20,6 +22,10 @@ from .client import get_parser, run_client_args
|
|||||||
from ..requests import BrowserConfig
|
from ..requests import BrowserConfig
|
||||||
from ..gui.run import gui_parser, run_gui_args
|
from ..gui.run import gui_parser, run_gui_args
|
||||||
from ..config import DEFAULT_PORT, DEFAULT_TIMEOUT, DEFAULT_STREAM_TIMEOUT
|
from ..config import DEFAULT_PORT, DEFAULT_TIMEOUT, DEFAULT_STREAM_TIMEOUT
|
||||||
|
from ..Provider.needs_auth.Antigravity import cli_main as antigravity_cli_main
|
||||||
|
from ..Provider.qwen.QwenCode import cli_main as qwen_cli_main
|
||||||
|
from ..Provider.github.GithubCopilot import cli_main as github_cli_main
|
||||||
|
from g4f.Provider.needs_auth.GeminiCLI import cli_main as gemini_cli_main
|
||||||
from .. import Provider
|
from .. import Provider
|
||||||
from .. import cookies
|
from .. import cookies
|
||||||
|
|
||||||
@@ -27,12 +33,12 @@ from .. import cookies
|
|||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
# API PARSER
|
# API PARSER
|
||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
def get_api_parser() -> ArgumentParser:
|
def get_api_parser(exit_on_error: bool = True) -> ArgumentParser:
|
||||||
"""
|
"""
|
||||||
Creates and returns the argument parser used for:
|
Creates and returns the argument parser used for:
|
||||||
g4f api ...
|
g4f api ...
|
||||||
"""
|
"""
|
||||||
api_parser = ArgumentParser(description="Run the API and GUI")
|
api_parser = ArgumentParser(description="Run the API and GUI", exit_on_error=exit_on_error)
|
||||||
|
|
||||||
api_parser.add_argument(
|
api_parser.add_argument(
|
||||||
"--bind",
|
"--bind",
|
||||||
@@ -228,12 +234,12 @@ def run_api_args(args):
|
|||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
# MCP PARSER
|
# MCP PARSER
|
||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
def get_mcp_parser() -> ArgumentParser:
|
def get_mcp_parser(exit_on_error: bool = True) -> ArgumentParser:
|
||||||
"""
|
"""
|
||||||
Parser for:
|
Parser for:
|
||||||
g4f mcp ...
|
g4f mcp ...
|
||||||
"""
|
"""
|
||||||
mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server")
|
mcp_parser = ArgumentParser(description="Run the MCP (Model Context Protocol) server", exit_on_error=exit_on_error)
|
||||||
mcp_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
|
mcp_parser.add_argument("--debug", "-d", action="store_true", help="Enable verbose logging.")
|
||||||
mcp_parser.add_argument("--http", action="store_true", help="Use HTTP instead of stdio.")
|
mcp_parser.add_argument("--http", action="store_true", help="Use HTTP instead of stdio.")
|
||||||
mcp_parser.add_argument("--host", default="0.0.0.0", help="HTTP server host.")
|
mcp_parser.add_argument("--host", default="0.0.0.0", help="HTTP server host.")
|
||||||
@@ -254,6 +260,11 @@ def run_mcp_args(args):
|
|||||||
origin=args.origin
|
origin=args.origin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_auth_parser(exit_on_error: bool = True) -> ArgumentParser:
|
||||||
|
auth_parser = ArgumentParser(description="Manage authentication for providers", exit_on_error=exit_on_error)
|
||||||
|
auth_parser.add_argument("provider", choices=["gemini-cli", "antigravity", "qwencode", "github-copilot"], help="The provider to authenticate with")
|
||||||
|
auth_parser.add_argument("action", nargs="?", choices=["status", "login", "logout"], default="login", help="Action to perform (default: login)")
|
||||||
|
return auth_parser
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
# MAIN ENTRYPOINT
|
# MAIN ENTRYPOINT
|
||||||
@@ -264,31 +275,45 @@ def main():
|
|||||||
Handles selecting: api / gui / client / mcp
|
Handles selecting: api / gui / client / mcp
|
||||||
"""
|
"""
|
||||||
parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
|
parser = argparse.ArgumentParser(description="Run gpt4free", exit_on_error=False)
|
||||||
|
parser.add_argument("--install-autocomplete", action="store_true", help="Install Bash autocompletion for g4f CLI.")
|
||||||
|
args, remaining = parser.parse_known_args()
|
||||||
|
if args.install_autocomplete:
|
||||||
|
generate_autocomplete()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
# Create sub-commands
|
mode_parser = ArgumentParser(description="Select mode to run g4f in.", exit_on_error=False)
|
||||||
subparsers = parser.add_subparsers(dest="mode", help="Mode to run g4f in.")
|
mode_parser.add_argument("mode", nargs="?", choices=["api", "gui", "client", "mcp", "auth"], default="api", help="Mode to run g4f in (default: api).")
|
||||||
subparsers.add_parser("api", parents=[get_api_parser()], add_help=False)
|
|
||||||
subparsers.add_parser("gui", parents=[gui_parser()], add_help=False)
|
args, remaining = mode_parser.parse_known_args(remaining)
|
||||||
subparsers.add_parser("client", parents=[get_parser()], add_help=False)
|
|
||||||
subparsers.add_parser("mcp", parents=[get_mcp_parser()], add_help=False)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
args = parser.parse_args()
|
if args.mode == "auth":
|
||||||
|
parser = get_auth_parser()
|
||||||
# Mode routing
|
args, remaining = parser.parse_known_args(remaining)
|
||||||
if args.mode == "api":
|
print(f"Handling auth for provider: {args.provider}, action: {args.action}")
|
||||||
|
handle_auth(args.provider, args.action, remaining)
|
||||||
|
return
|
||||||
|
elif args.mode == "api":
|
||||||
|
parser = get_api_parser()
|
||||||
|
args = parser.parse_args(remaining)
|
||||||
run_api_args(args)
|
run_api_args(args)
|
||||||
elif args.mode == "gui":
|
elif args.mode == "gui":
|
||||||
|
parser = gui_parser()
|
||||||
|
args = parser.parse_args(remaining)
|
||||||
run_gui_args(args)
|
run_gui_args(args)
|
||||||
elif args.mode == "client":
|
elif args.mode == "client":
|
||||||
|
parser = get_parser()
|
||||||
|
args = parser.parse_args(remaining)
|
||||||
run_client_args(args)
|
run_client_args(args)
|
||||||
elif args.mode == "mcp":
|
elif args.mode == "mcp":
|
||||||
|
parser = get_mcp_parser()
|
||||||
|
args = parser.parse_args(remaining)
|
||||||
run_mcp_args(args)
|
run_mcp_args(args)
|
||||||
else:
|
else:
|
||||||
# No mode provided
|
# No mode provided
|
||||||
raise argparse.ArgumentError(
|
raise argparse.ArgumentError(
|
||||||
None,
|
None,
|
||||||
"No valid mode specified. Use 'api', 'gui', 'client', or 'mcp'."
|
"No valid mode specified. Use 'api', 'gui', 'client', 'mcp', or 'auth'."
|
||||||
)
|
)
|
||||||
|
|
||||||
except argparse.ArgumentError:
|
except argparse.ArgumentError:
|
||||||
@@ -302,3 +327,58 @@ def main():
|
|||||||
except argparse.ArgumentError:
|
except argparse.ArgumentError:
|
||||||
# 2. Try API mode with default arguments
|
# 2. Try API mode with default arguments
|
||||||
run_api_args(get_api_parser().parse_args())
|
run_api_args(get_api_parser().parse_args())
|
||||||
|
|
||||||
|
def generate_autocomplete():
|
||||||
|
# Top-level commands and their subcommands/options
|
||||||
|
commands = ["api", "gui", "client", "mcp", "auth"]
|
||||||
|
auth_providers = ["gemini-cli", "antigravity", "qwencode", "github-copilot"]
|
||||||
|
auth_subcommands = ["status", "login"]
|
||||||
|
# Options for each command
|
||||||
|
api_args = ["--bind", "--port", "--debug", "--gui", "--no-gui", "--model", "--provider", "--media-provider", "--proxy", "--workers", "--disable-colors", "--ignore-cookie-files", "--g4f-api-key", "--ignored-providers", "--cookie-browsers", "--reload", "--demo", "--timeout", "--stream-timeout", "--ssl-keyfile", "--ssl-certfile", "--log-config", "--access-log", "--no-access-log", "--browser-port", "--browser-host"]
|
||||||
|
gui_args = ["--debug"]
|
||||||
|
client_args = ["--debug"]
|
||||||
|
mcp_args = ["--debug", "--http", "--host", "--port", "--origin"]
|
||||||
|
global_args = ["--install-autocomplete"]
|
||||||
|
bash_completion_script = f"""
|
||||||
|
_g4f_completions() {{
|
||||||
|
local cur prev words cword
|
||||||
|
_get_comp_words_by_ref -n : cur prev words cword
|
||||||
|
if [[ $cword -eq 1 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W '{' '.join(commands + global_args)}' -- "$cur"))
|
||||||
|
elif [[ $prev == auth && $cword -eq 2 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W '{' '.join(auth_providers)}' -- "$cur"))
|
||||||
|
elif [[ $prev =~ ^(gemini-cli|antigravity|qwencode|github-copilot)$ && $cword -eq 3 ]]; then
|
||||||
|
COMPREPLY=($(compgen -W '{' '.join(auth_subcommands)}' -- "$cur"))
|
||||||
|
elif [[ $words[1] == api ]]; then
|
||||||
|
local opts="{' '.join(api_args)}"
|
||||||
|
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||||||
|
elif [[ $words[1] == gui ]]; then
|
||||||
|
local opts="{' '.join(gui_args)}"
|
||||||
|
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||||||
|
elif [[ $words[1] == client ]]; then
|
||||||
|
local opts="{' '.join(client_args)}"
|
||||||
|
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||||||
|
elif [[ $words[1] == mcp ]]; then
|
||||||
|
local opts="{' '.join(mcp_args)}"
|
||||||
|
COMPREPLY=($(compgen -W "$opts" -- "$cur"))
|
||||||
|
fi
|
||||||
|
}}
|
||||||
|
complete -F _g4f_completions g4f
|
||||||
|
"""
|
||||||
|
completion_file = os.path.expanduser("~/.g4f_bash_completion")
|
||||||
|
with open(completion_file, "w") as f:
|
||||||
|
f.write(bash_completion_script)
|
||||||
|
print(f"Bash completion script written to {completion_file}. Source it in your .bashrc or .bash_profile.")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_auth(provider, action, remaining):
|
||||||
|
if provider == "gemini-cli":
|
||||||
|
sys.exit(gemini_cli_main([action] + remaining))
|
||||||
|
elif provider == "antigravity":
|
||||||
|
sys.exit(antigravity_cli_main([action] + remaining))
|
||||||
|
elif provider == "qwencode":
|
||||||
|
sys.exit(qwen_cli_main([action] + remaining))
|
||||||
|
elif provider == "github-copilot":
|
||||||
|
sys.exit(github_cli_main([action] + remaining))
|
||||||
|
else:
|
||||||
|
print(f"Provider {provider} not supported yet.")
|
||||||
@@ -3,8 +3,8 @@ from argparse import ArgumentParser
|
|||||||
from ..cookies import BROWSERS
|
from ..cookies import BROWSERS
|
||||||
from .. import Provider
|
from .. import Provider
|
||||||
|
|
||||||
def gui_parser():
|
def gui_parser(exit_on_error: bool = True):
|
||||||
parser = ArgumentParser(description="Run the GUI")
|
parser = ArgumentParser(description="Run the GUI", exit_on_error=exit_on_error)
|
||||||
parser.add_argument("--host", type=str, default="0.0.0.0", help="hostname")
|
parser.add_argument("--host", type=str, default="0.0.0.0", help="hostname")
|
||||||
parser.add_argument("--port", "-p", type=int, default=8080, help="port")
|
parser.add_argument("--port", "-p", type=int, default=8080, help="port")
|
||||||
parser.add_argument("--debug", "-d", "-debug", action="store_true", help="debug mode")
|
parser.add_argument("--debug", "-d", "-debug", action="store_true", help="debug mode")
|
||||||
|
|||||||
-19
@@ -7,25 +7,6 @@ This file is used as the main entry point for building executables with Nuitka
|
|||||||
import g4f.debug
|
import g4f.debug
|
||||||
g4f.debug.enable_logging()
|
g4f.debug.enable_logging()
|
||||||
|
|
||||||
from g4f.client import Client
|
|
||||||
from g4f.errors import ModelNotFoundError
|
|
||||||
|
|
||||||
import g4f.Provider
|
|
||||||
|
|
||||||
try:
|
|
||||||
client = Client(provider=g4f.Provider.PollinationsAI)
|
|
||||||
response = client.chat.completions.create(
|
|
||||||
model="openai",
|
|
||||||
messages=[{"role": "user", "content": "Hello!"}],
|
|
||||||
stream=True,
|
|
||||||
raw=True
|
|
||||||
)
|
|
||||||
for r in response:
|
|
||||||
print(r)
|
|
||||||
except ModelNotFoundError as e:
|
|
||||||
print(f"Successfully")
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
import g4f.cli
|
import g4f.cli
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -121,10 +121,6 @@ setup(
|
|||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'g4f=g4f.cli:main',
|
'g4f=g4f.cli:main',
|
||||||
'g4f-mcp=g4f.mcp.server:main',
|
'g4f-mcp=g4f.mcp.server:main',
|
||||||
'g4f-antigravity=g4f.Provider.needs_auth.Antigravity:cli_main',
|
|
||||||
'g4f-geminicli=g4f.Provider.needs_auth.GeminiCLI:cli_main',
|
|
||||||
'g4f-qwencode=g4f.Provider.qwen.QwenCode:cli_main',
|
|
||||||
'g4f-github-copilot=g4f.Provider.github.GithubCopilot:cli_main',
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
url='https://github.com/xtekky/gpt4free', # Link to your GitHub repository
|
url='https://github.com/xtekky/gpt4free', # Link to your GitHub repository
|
||||||
|
|||||||
Reference in New Issue
Block a user