返回提交历史
Modified
g4f/Provider/needs_auth/GeminiCLI.py
+573
-2
Modified
g4f/Provider/qwen/QwenCode.py
+162
-2
Modified
setup.py
+2
-0
XFEstudio/gpt4free
feat: add CLI entry points for GeminiCLI and QwenCode providers
6472b477
代码差异
3 个文件
+737
-4
@@ -1,10 +1,18 @@
1
1
from __future__ import annotations
2
2
3
import sys
4
import json
5
import time
6
import asyncio
7
from pathlib import Path
8
from typing import Optional
9
3
10
from ...typing import Messages, AsyncResult
4
11
from ..template import OpenaiTemplate
5
12
from .qwenContentGenerator import QwenContentGenerator
6
13
from .qwenOAuth2 import QwenOAuth2Client
7
from .sharedTokenManager import TokenManagerError
14
from .sharedTokenManager import TokenManagerError, SharedTokenManager
15
from .oauthFlow import launch_browser_for_oauth
8
16
9
17
class QwenCode(OpenaiTemplate):
10
18
label = "Qwen Code 🤖"
@@ -70,4 +78,156 @@ class QwenCode(OpenaiTemplate):
70
78
else:
71
79
yield chunk
72
80
except:
73
raise
81
raise
82
83
@classmethod
84
async def login(cls, credentials_path: Optional[Path] = None) -> SharedTokenManager:
85
"""
86
Perform interactive OAuth login and save credentials.
87
88
Args:
89
credentials_path: Path to save credentials (default: g4f cache)
90
91
Returns:
92
SharedTokenManager with active credentials
93
94
Example:
95
>>> import asyncio
96
>>> from g4f.Provider.qwen import QwenCode
97
>>> asyncio.run(QwenCode.login())
98
"""
99
print("\n" + "=" * 60)
100
print("QwenCode OAuth Login")
101
print("=" * 60)
102
103
await launch_browser_for_oauth()
104
105
shared_manager = SharedTokenManager.getInstance()
106
print("=" * 60 + "\n")
107
108
return shared_manager
109
110
@classmethod
111
def has_credentials(cls) -> bool:
112
"""Check if valid credentials exist."""
113
shared_manager = SharedTokenManager.getInstance()
114
path = shared_manager.getCredentialFilePath()
115
return path.exists()
116
117
@classmethod
118
def get_credentials_path(cls) -> Optional[Path]:
119
"""Get path to credentials file if it exists."""
120
shared_manager = SharedTokenManager.getInstance()
121
path = shared_manager.getCredentialFilePath()
122
if path.exists():
123
return path
124
return None
125
126
127
async def main():
128
"""CLI entry point for QwenCode authentication."""
129
import argparse
130
131
parser = argparse.ArgumentParser(
132
description="QwenCode OAuth Authentication for gpt4free",
133
formatter_class=argparse.RawDescriptionHelpFormatter,
134
epilog="""
135
Examples:
136
%(prog)s login # Interactive device code login
137
%(prog)s status # Check authentication status
138
%(prog)s logout # Remove saved credentials
139
"""
140
)
141
142
subparsers = parser.add_subparsers(dest="command", help="Commands")
143
144
# Login command
145
subparsers.add_parser("login", help="Authenticate with Qwen")
146
147
# Status command
148
subparsers.add_parser("status", help="Check authentication status")
149
150
# Logout command
151
subparsers.add_parser("logout", help="Remove saved credentials")
152
153
args = parser.parse_args()
154
155
if args.command == "login":
156
try:
157
await QwenCode.login()
158
except KeyboardInterrupt:
159
print("\n\nLogin cancelled.")
160
sys.exit(1)
161
except Exception as e:
162
print(f"\n❌ Login failed: {e}")
163
sys.exit(1)
164
165
elif args.command == "status":
166
print("\nQwenCode Authentication Status")
167
print("=" * 40)
168
169
if QwenCode.has_credentials():
170
creds_path = QwenCode.get_credentials_path()
171
print(f"✓ Credentials found at: {creds_path}")
172
173
try:
174
with creds_path.open() as f:
175
creds = json.load(f)
176
177
expiry = creds.get("expiry_date")
178
if expiry:
179
expiry_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(expiry / 1000))
180
if expiry / 1000 > time.time():
181
print(f" Token expires: {expiry_time}")
182
else:
183
print(f" Token expired: {expiry_time} (will auto-refresh)")
184
185
if creds.get("resource_url"):
186
print(f" Endpoint: {creds['resource_url']}")
187
except Exception as e:
188
print(f" (Could not read credential details: {e})")
189
else:
190
print("✗ No credentials found")
191
print(f"\nRun 'g4f-qwencode login' to authenticate.")
192
193
print()
194
195
elif args.command == "logout":
196
print("\nQwenCode Logout")
197
print("=" * 40)
198
199
removed = False
200
201
shared_manager = SharedTokenManager.getInstance()
202
path = shared_manager.getCredentialFilePath()
203
204
if path.exists():
205
path.unlink()
206
print(f"✓ Removed: {path}")
207
removed = True
208
209
# Also try the default location
210
default_path = Path.home() / ".qwen" / "oauth_creds.json"
211
if default_path.exists() and default_path != path:
212
default_path.unlink()
213
print(f"✓ Removed: {default_path}")
214
removed = True
215
216
if removed:
217
print("\n✓ Credentials removed successfully.")
218
else:
219
print("No credentials found to remove.")
220
221
print()
222
223
else:
224
parser.print_help()
225
226
227
def cli_main():
228
"""Synchronous CLI entry point for setup.py console_scripts."""
229
asyncio.run(main())
230
231
232
if __name__ == "__main__":
233
cli_main()
@@ -120,6 +120,8 @@ setup(
120
120
'g4f=g4f.cli:main',
121
121
'g4f-mcp=g4f.mcp.server:main',
122
122
'g4f-antigravity=g4f.Provider.needs_auth.Antigravity:cli_main',
123
'g4f-geminicli=g4f.Provider.needs_auth.GeminiCLI:cli_main',
124
'g4f-qwencode=g4f.Provider.qwen.QwenCode:cli_main',
123
125
],
124
126
},
125
127
url='https://github.com/xtekky/gpt4free', # Link to your GitHub repository