chore: initial commit

This commit is contained in:
gameloader
2026-03-18 22:07:19 +08:00
commit e3a28050df
13 changed files with 51831 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.env
.venv/
.claude/
__pycache__/
*.py[cod]

95
README.md Normal file
View File

@@ -0,0 +1,95 @@
# gptplus_auto
ChatGPT Plus 自动注册、订阅工具,以及 Codex CLI OAuth 登录工具。
## 功能
- **register** — 自动注册新 ChatGPT 账号,并可选完成 Plus 订阅Stripe 支付)
- **codex-login** — 对已有 ChatGPT 账号执行 Codex CLI OAuth 登录,纯 HTTP 实现,无需浏览器
## 安装
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
## 配置
复制并编辑 `.env`
```env
# 代理(推荐美国 IP
SOCKS5_PROXY=socks5://user:pass@host:port
# YesCaptcha注册功能需要
YESCAPTCHA_API_KEY=your_key_here
# 支付信息(订阅功能需要,可留空跳过支付)
CARD_NUMBER=4111111111111111
CARD_EXP_MONTH=12
CARD_EXP_YEAR=2028
CARD_CVC=123
BILLING_NAME=John Smith
BILLING_EMAIL=john@example.com
BILLING_ADDRESS_LINE1=123 Main St
BILLING_ADDRESS_CITY=New York
BILLING_ADDRESS_STATE=NY
BILLING_ADDRESS_POSTAL_CODE=10001
COUNTRY=US
CURRENCY=usd
```
## 使用
所有功能通过 `src/main.py` 统一入口调用:
### 注册账号(+ 可选 Plus 订阅)
```bash
.venv/bin/python src/main.py register
```
- 自动申请临时邮箱、注册账号、解 hCaptcha
-`.env` 中配置了信用卡信息,注册完成后自动订阅 ChatGPT Plus
- 输出邮箱、邮箱密码、ChatGPT 密码、access token
### Codex CLI OAuth 登录
```bash
.venv/bin/python src/main.py codex-login --email user@example.com --password yourpassword
```
可选参数:
| 参数 | 说明 |
|------|------|
| `--email` | 账号邮箱(不传则交互式输入)|
| `--password` | 账号密码(不传则交互式输入)|
| `--otp` | 邮箱 OTP如需要|
| `--workspace-id` | 指定 workspace ID |
| `--authorize-url` | 自定义 OAuth authorize URL |
成功后输出 `localhost:1455/auth/callback?code=...` 回调 URL可直接交给 Codex CLI 完成登录。
## 项目结构
```
src/
├── main.py # 统一入口register / codex-login
├── config.py # 配置pydantic-settings读取 .env
├── http_client.py # HTTP 客户端curl_cffi Chrome 模拟)
├── vmail_client.py # 临时邮箱mail.tm
├── captcha_solver.py # YesCaptcha hCaptcha 解决
├── chatgpt_register_http_reverse.py # 完整注册流程
├── chatgpt_payment.py # 完整 Stripe 支付流程
└── codex_oauth_http_flow.py # Codex CLI OAuth 登录(纯 HTTP
```
## 注意事项
1. **仅供学习研究**:请遵守 OpenAI 服务条款
2. **代理建议**:建议使用美国 IP 代理,避免触发风控
3. **API 可能变化**OpenAI/Stripe 可能随时更改接口
4. **避免频繁调用**:同 IP 短时间内多次注册可能被封

49618
nodatadog.js Normal file

File diff suppressed because one or more lines are too long

14
pyproject.toml Normal file
View File

@@ -0,0 +1,14 @@
[project]
name = "gptplus-auto"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"curl-cffi>=0.14.0",
"httpx[socks]>=0.27.0",
"playwright>=1.58.0",
"pydantic>=2.0.0",
"pydantic-settings>=2.0.0",
]
[tool.uv]
dev-dependencies = []

50
src/captcha_solver.py Normal file
View File

@@ -0,0 +1,50 @@
import time
import httpx
# YesCaptcha international endpoint
YESCAPTCHA_BASE = "https://api.yescaptcha.com"
class CaptchaSolver:
def __init__(self, api_key: str):
self.api_key = api_key
def solve_hcaptcha(self, site_key: str, page_url: str, timeout: int = 180) -> str:
"""Submit hCaptcha task via YesCaptcha and wait for solution token."""
# createTask
r = httpx.post(
f"{YESCAPTCHA_BASE}/createTask",
json={
"clientKey": self.api_key,
"task": {
"type": "HCaptchaTaskProxyless",
"websiteURL": page_url,
"websiteKey": site_key,
},
},
timeout=30,
)
r.raise_for_status()
resp = r.json()
if resp.get("errorId", 0) != 0:
raise RuntimeError(f"YesCaptcha createTask error: {resp}")
task_id = resp["taskId"]
print(f" YesCaptcha task: {task_id}")
# getTaskResult polling
deadline = time.time() + timeout
while time.time() < deadline:
time.sleep(5)
r2 = httpx.post(
f"{YESCAPTCHA_BASE}/getTaskResult",
json={"clientKey": self.api_key, "taskId": task_id},
timeout=30,
)
r2.raise_for_status()
res = r2.json()
if res.get("errorId", 0) != 0:
raise RuntimeError(f"YesCaptcha error: {res}")
if res.get("status") == "ready":
return res["solution"]["gRecaptchaResponse"]
# status == "processing" -> keep polling
raise TimeoutError(f"hCaptcha not solved within {timeout}s")

227
src/chatgpt_payment.py Normal file
View File

@@ -0,0 +1,227 @@
"""ChatGPT Plus payment flow."""
import uuid
from http_client import HTTPClient
from captcha_solver import CaptchaSolver
class ChatGPTPayment:
def __init__(self, http: HTTPClient, captcha: CaptchaSolver):
self.http = http
self.captcha = captcha
self.base = "https://chatgpt.com"
def subscribe_plus(
self, access_token: str, country: str, currency: str, card_info: dict
) -> bool:
"""Subscribe to ChatGPT Plus."""
checkout_currency = currency.upper()
# 0. Get account info and promo campaign
print("[0/6] Fetching account promo campaign...")
r = self.http.request(
"GET",
f"{self.base}/backend-api/accounts/check/v4-2023-04-27",
headers={"Authorization": f"Bearer {access_token}"},
)
accounts_data = r.json()
# Extract promo campaign from first account
account_id = list(accounts_data.get("accounts", {}).keys())[0] if accounts_data.get("accounts") else None
promo_campaign_id = ""
plan_name = "chatgptplusplan"
if account_id:
account = accounts_data["accounts"][account_id]
eligible_promos = account.get("eligible_promo_campaigns", {})
plus_promo = eligible_promos.get("plus", {})
if plus_promo:
promo_campaign_id = plus_promo.get("id", "")
plan_name = plus_promo.get("metadata", {}).get("plan_name", "chatgptplusplan")
print(f" Promo campaign: {promo_campaign_id}")
print(f" Plan name: {plan_name}")
# 1. Create checkout session
print("[1/6] Creating checkout session...")
r = self.http.request(
"POST",
f"{self.base}/backend-api/payments/checkout",
json={
"plan_name": plan_name,
"billing_details": {"country": country, "currency": checkout_currency},
"promo_campaign": {"promo_campaign_id": promo_campaign_id, "is_coupon_from_query_param": False},
"prefetch": True,
"checkout_ui_mode": "custom",
},
headers={"Authorization": f"Bearer {access_token}"},
)
checkout = r.json()
session_id = checkout["checkout_session_id"]
publishable_key = checkout["publishable_key"]
processor_entity = checkout["processor_entity"]
print(f" Session: {session_id}")
# 2. Initialize Stripe elements session
print("[2/6] Initializing Stripe elements...")
stripe_js_id = str(uuid.uuid4())
r = self.http.request(
"GET",
f"https://api.stripe.com/v1/elements/sessions",
params={
"client_betas[0]": "custom_checkout_server_updates_1",
"client_betas[1]": "custom_checkout_manual_approval_1",
"deferred_intent[mode]": "subscription",
"deferred_intent[amount]": "0",
"deferred_intent[currency]": currency.lower(),
"deferred_intent[setup_future_usage]": "off_session",
"deferred_intent[payment_method_types][0]": "card",
"currency": currency.lower(),
"key": publishable_key,
"_stripe_version": "2025-03-31.basil; checkout_server_update_beta=v1; checkout_manual_approval_preview=v1",
"elements_init_source": "custom_checkout",
"referrer_host": "chatgpt.com",
"stripe_js_id": stripe_js_id,
"locale": "en",
"type": "deferred_intent",
"checkout_session_id": session_id,
},
headers={
"Origin": "https://js.stripe.com",
"Referer": "https://js.stripe.com/",
},
)
elements = r.json()
captcha_site_key = elements.get("passive_captcha", {}).get("site_key", "")
elements_session_id = elements.get("session_id", "")
elements_config_id = elements.get("config_id", "")
print(f" Elements session: {elements_session_id}")
# 3. Solve hCaptcha (Stripe passive captcha via YesCaptcha)
print("[3/6] Solving Stripe passive captcha...")
print(f" Site key: {captcha_site_key}")
if captcha_site_key:
captcha_token = self.captcha.solve_hcaptcha(captcha_site_key, "https://js.stripe.com")
print(f" Token: {captcha_token[:30]}...")
else:
captcha_token = ""
print(" No captcha required")
# 4. Create Stripe payment method
print("[4/6] Creating payment method...")
billing_name = card_info.get("billing_name") or card_info.get("name") or "John Doe"
billing_email = card_info.get("billing_email") or card_info.get("email") or ""
billing_line1 = card_info.get("billing_address_line1") or "100 Main St"
billing_city = card_info.get("billing_address_city") or "San Francisco"
billing_state = card_info.get("billing_address_state") or "CA"
billing_postal_code = card_info.get("billing_address_postal_code") or "94105"
guid = str(uuid.uuid4()) + str(uuid.uuid4())[:6]
muid = str(uuid.uuid4()) + str(uuid.uuid4())[:6]
sid = str(uuid.uuid4()) + str(uuid.uuid4())[:6]
r = self.http.request(
"POST",
"https://api.stripe.com/v1/payment_methods",
data={
"billing_details[name]": billing_name,
"billing_details[email]": billing_email,
"billing_details[address][country]": country,
"billing_details[address][line1]": billing_line1,
"billing_details[address][city]": billing_city,
"billing_details[address][postal_code]": billing_postal_code,
"billing_details[address][state]": billing_state,
"type": "card",
"card[number]": card_info["number"],
"card[cvc]": card_info["cvc"],
"card[exp_year]": card_info["exp_year"],
"card[exp_month]": card_info["exp_month"],
"allow_redisplay": "unspecified",
"pasted_fields": "number,exp,cvc",
"payment_user_agent": "stripe.js/5e596c82e6; stripe-js-v3/5e596c82e6; payment-element; deferred-intent",
"referrer": "https://chatgpt.com",
"time_on_page": "60000",
"client_attribution_metadata[client_session_id]": stripe_js_id,
"client_attribution_metadata[checkout_session_id]": session_id,
"client_attribution_metadata[merchant_integration_source]": "elements",
"client_attribution_metadata[merchant_integration_subtype]": "payment-element",
"client_attribution_metadata[merchant_integration_version]": "2021",
"client_attribution_metadata[payment_intent_creation_flow]": "deferred",
"client_attribution_metadata[payment_method_selection_flow]": "automatic",
"client_attribution_metadata[elements_session_id]": elements_session_id,
"client_attribution_metadata[elements_session_config_id]": elements_config_id,
"client_attribution_metadata[merchant_integration_additional_elements][0]": "payment",
"client_attribution_metadata[merchant_integration_additional_elements][1]": "address",
"guid": guid,
"muid": muid,
"sid": sid,
"key": publishable_key,
"_stripe_version": "2025-03-31.basil; checkout_server_update_beta=v1; checkout_manual_approval_preview=v1",
"radar_options[hcaptcha_token]": captcha_token,
},
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://js.stripe.com",
"Referer": "https://js.stripe.com/",
},
)
if r.status_code != 200:
raise RuntimeError(f"Create payment method failed: {r.text}")
payment_method_id = r.json()["id"]
print(f" Payment method: {payment_method_id}")
# 5. Confirm payment with Stripe
print("[5/6] Confirming payment with Stripe...")
return_url = f"https://checkout.stripe.com/c/pay/{session_id}?returned_from_redirect=true&ui_mode=custom&return_url=https%3A%2F%2Fchatgpt.com%2Fcheckout%2Fverify%3Fstripe_session_id%3D{session_id}%26processor_entity%3D{processor_entity}%26plan_type%3Dplus"
r = self.http.request(
"POST",
f"https://api.stripe.com/v1/payment_pages/{session_id}/confirm",
data={
"guid": guid,
"muid": muid,
"sid": sid,
"payment_method": payment_method_id,
"expected_amount": "0",
"passive_captcha_token": captcha_token,
"passive_captcha_ekey": "",
"expected_payment_method_type": "card",
"return_url": return_url,
"elements_session_client[client_betas][0]": "custom_checkout_server_updates_1",
"elements_session_client[client_betas][1]": "custom_checkout_manual_approval_1",
"elements_session_client[elements_init_source]": "custom_checkout",
"elements_session_client[referrer_host]": "chatgpt.com",
"elements_session_client[session_id]": elements_session_id,
"elements_session_client[stripe_js_id]": stripe_js_id,
"elements_session_client[locale]": "en",
"elements_session_client[is_aggregation_expected]": "false",
"client_attribution_metadata[client_session_id]": stripe_js_id,
"client_attribution_metadata[checkout_session_id]": session_id,
"client_attribution_metadata[merchant_integration_source]": "checkout",
"client_attribution_metadata[merchant_integration_version]": "custom",
"client_attribution_metadata[merchant_integration_subtype]": "payment-element",
"client_attribution_metadata[merchant_integration_additional_elements][0]": "payment",
"client_attribution_metadata[merchant_integration_additional_elements][1]": "address",
"client_attribution_metadata[payment_intent_creation_flow]": "deferred",
"client_attribution_metadata[payment_method_selection_flow]": "automatic",
"client_attribution_metadata[elements_session_id]": elements_session_id,
"client_attribution_metadata[elements_session_config_id]": elements_config_id,
"key": publishable_key,
"_stripe_version": "2025-03-31.basil; checkout_server_update_beta=v1; checkout_manual_approval_preview=v1",
},
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://js.stripe.com",
"Referer": "https://js.stripe.com/",
},
)
if r.status_code != 200:
raise RuntimeError(f"Stripe confirm failed: {r.text}")
print(f" Stripe confirm status: {r.status_code}")
# 6. Verify subscription
print("[6/6] Verifying subscription...")
r = self.http.request(
"GET",
f"{self.base}/backend-api/payments/checkout/{processor_entity}/{session_id}",
headers={"Authorization": f"Bearer {access_token}"},
)
return r.status_code == 200

View File

@@ -0,0 +1,427 @@
"""Pure HTTP registration experiment with detailed auth-state diagnostics."""
import os
import random
import re
import time
import uuid
from typing import Optional
from urllib.parse import unquote, urlencode, urljoin, urlparse
from http_client import HTTPClient
from vmail_client import MailClient
class ChatGPTRegisterHTTPReverse:
def __init__(self, http: HTTPClient, mail: MailClient):
self.http = http
self.mail = mail
self.base = "https://chatgpt.com"
self.auth_base = "https://auth.openai.com"
self.root_dir = os.path.dirname(os.path.dirname(__file__))
def _delay(self, lo=0.5, hi=1.5):
time.sleep(random.uniform(lo, hi))
def _cookie_rows(self) -> list[tuple[str, str, str]]:
rows = []
for cookie in self.http.session.cookies.jar:
rows.append(((cookie.domain or "").lstrip("."), cookie.name, cookie.value))
rows.sort()
return rows
def _cookie_value(self, name: str, domain_suffix: str = "") -> str:
matches = []
for domain, cookie_name, value in self._cookie_rows():
if cookie_name != name:
continue
if domain_suffix and not domain.endswith(domain_suffix):
continue
matches.append((domain, value))
return matches[-1][1] if matches else ""
def _print_cookie_summary(self, label: str):
interesting = [
"__Host-next-auth.csrf-token",
"__Secure-next-auth.callback-url",
"__Secure-next-auth.state",
"oai-login-csrf_dev_3772291445",
"rg_context",
"auth_provider",
"login_session",
"hydra_redirect",
"oai-client-auth-session",
"unified_session_manifest",
"auth-session-minimized",
"cf_clearance",
"__cf_bm",
"__cflb",
"_cfuvid",
"oai-sc",
]
present = []
for name in interesting:
value = self._cookie_value(name)
if value:
present.append(name)
print(f" {label}: {', '.join(present) if present else 'no relevant cookies'}")
@staticmethod
def _response_excerpt(response, limit: int = 500) -> str:
text = response.text or ""
text = text.replace("\n", " ").replace("\r", " ")
return text[:limit]
def _json_or_none(self, response):
try:
return response.json()
except Exception:
return None
def _load_captured_sentinel(self, flow_name: str) -> str:
path = os.path.join(self.root_dir, "nodatadog.js")
try:
with open(path, "r", encoding="utf-8") as handle:
content = handle.read()
except FileNotFoundError:
return ""
pattern = re.compile(r'"openai-sentinel-token": "((?:[^"\\]|\\.)*flow\\"\\"%s(?:[^"\\]|\\.)*)"' % re.escape(flow_name))
match = pattern.search(content)
if not match:
return ""
# Keep the captured literal as-is. The request header in the capture uses this exact string form.
return match.group(1)
def _bootstrap_chatgpt(self):
print("[2/9] Bootstrapping chatgpt.com cookies...")
response = self.http.request(
"GET",
f"{self.base}/",
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"sec-fetch-site": "none",
"sec-fetch-mode": "navigate",
"sec-fetch-dest": "document",
},
)
print(f" Home status: {response.status_code}")
self._print_cookie_summary("After home")
response = self.http.request(
"GET",
f"{self.base}/api/auth/csrf",
headers={
"Accept": "*/*",
"Referer": f"{self.base}/",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
},
)
data = self._json_or_none(response) or {}
csrf_token = data.get("csrfToken", "")
print(f" CSRF status: {response.status_code}")
print(f" CSRF token present: {bool(csrf_token)}")
self._print_cookie_summary("After csrf")
if not csrf_token:
raise RuntimeError(f"Failed to get csrf token: {self._response_excerpt(response)}")
return csrf_token
def _signin_auth0(self, email: str, csrf_token: str) -> str:
print("[3/9] Calling next-auth signin endpoint...")
callback_cookie = self._cookie_value("__Secure-next-auth.callback-url", "chatgpt.com")
callback_url = unquote(callback_cookie) if callback_cookie else f"{self.base}/"
auth_logging_id = str(uuid.uuid4())
params = {
"prompt": "login",
"ext-oai-did": self.http.device_id,
"auth_session_logging_id": auth_logging_id,
"ext-passkey-client-capabilities": "1111",
"screen_hint": "login_or_signup",
"login_hint": email,
}
response = self.http.request(
"POST",
f"{self.base}/api/auth/signin/openai?{urlencode(params)}",
data=urlencode({
"callbackUrl": callback_url,
"csrfToken": csrf_token,
"json": "true",
}),
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "*/*",
"Origin": self.base,
"Referer": f"{self.base}/",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
},
)
data = self._json_or_none(response) or {}
auth_url = data.get("url", "")
print(f" Signin status: {response.status_code}")
print(f" Auth URL present: {bool(auth_url)}")
self._print_cookie_summary("After signin/auth0")
if not auth_url:
raise RuntimeError(f"Failed to get auth URL: {self._response_excerpt(response)}")
return auth_url
def _follow_auth_redirects(self, auth_url: str) -> str:
print("[4/9] Following auth.openai.com redirect chain...")
current_url = auth_url
referer = f"{self.base}/"
final_url = ""
for step in range(10):
response = self.http.request(
"GET",
current_url,
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"Referer": referer,
},
)
print(f" Redirect step {step + 1}: {response.status_code} {current_url}")
self._print_cookie_summary(f"Cookies after auth step {step + 1}")
if response.status_code in (301, 302, 303, 307, 308):
location = response.headers.get("location", "")
if not location:
raise RuntimeError(f"Redirect missing location at {current_url}")
referer = current_url
current_url = urljoin(current_url, location)
continue
final_url = current_url
break
if not final_url:
raise RuntimeError("Failed to land on auth page")
print(f" Final auth page: {final_url}")
return final_url
def _accounts_api_base(self, auth_url: str) -> str:
path = urlparse(auth_url).path.rstrip("/")
if path.endswith("/authorize"):
return f"{self.auth_base}{path.rsplit('/', 1)[0]}"
return f"{self.auth_base}/api/accounts"
def _register_endpoint(self, auth_url: str) -> str:
return f"{self._accounts_api_base(auth_url)}/user/register"
def _attempt_register(self, register_url: str, auth_page_url: str, email: str, password: str, sentinel: str = ""):
headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": self.auth_base,
"Referer": auth_page_url,
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "cors",
"sec-fetch-dest": "empty",
}
if sentinel:
headers["openai-sentinel-token"] = sentinel
response = self.http.request(
"POST",
register_url,
json={"username": email, "password": password},
headers=headers,
)
data = self._json_or_none(response)
print(f" Register status: {response.status_code} sentinel={bool(sentinel)}")
if data is not None:
print(f" Register json keys: {sorted(data.keys())}")
else:
print(f" Register body: {self._response_excerpt(response)}")
self._print_cookie_summary("After register attempt")
return response, data
def _open_continue_url(self, continue_url: str, referer: str, label: str) -> str:
print(f"{label} Opening continue URL...")
response = self.http.request(
"GET",
continue_url,
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"Referer": referer,
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "navigate",
"sec-fetch-dest": "document",
},
)
print(f" Continue status: {response.status_code}")
page_url = continue_url
location = response.headers.get("location", "")
if location:
page_url = urljoin(continue_url, location)
if page_url != continue_url:
page_response = self.http.request(
"GET",
page_url,
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"Referer": referer,
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "navigate",
"sec-fetch-dest": "document",
},
)
print(f" Page status: {page_response.status_code}")
print(f" Page URL: {page_url}")
self._print_cookie_summary("After opening continue URL")
return page_url
def _validate_otp(self, validate_url: str, otp: str, referer: str) -> str:
print("[7/9] Validating OTP via pure HTTP...")
response = self.http.request(
"POST",
validate_url,
json={"code": otp},
headers={
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": self.auth_base,
"Referer": referer,
},
)
data = self._json_or_none(response) or {}
print(f" Validate status: {response.status_code}")
if response.status_code != 200 or not data.get("continue_url"):
raise RuntimeError(f"OTP validate failed: {self._response_excerpt(response)}")
self._print_cookie_summary("After validate")
return data["continue_url"]
def _attempt_create_account(self, create_account_url: str, name: str, referer: str, sentinel: str = ""):
print("[8/9] Creating account profile via pure HTTP...")
headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": self.auth_base,
"Referer": referer,
}
if sentinel:
headers["openai-sentinel-token"] = sentinel
response = self.http.request(
"POST",
create_account_url,
json={"name": name, "birthdate": "1998-03-18"},
headers=headers,
)
data = self._json_or_none(response)
print(f" Create-account status: {response.status_code} sentinel={bool(sentinel)}")
if data is not None:
print(f" Create-account json keys: {sorted(data.keys())}")
else:
print(f" Create-account body: {self._response_excerpt(response)}")
self._print_cookie_summary("After create_account")
return response, data
def _finalize_auth_callback(self, continue_url: str):
print("[9/9] Finalizing OAuth callback on chatgpt.com...")
current_url = continue_url
referer = f"{self.auth_base}/"
for step in range(5):
response = self.http.request(
"GET",
current_url,
headers={
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"Referer": referer,
},
)
print(f" Callback step {step + 1}: {response.status_code} {current_url}")
if response.status_code not in (301, 302, 303, 307, 308):
break
location = response.headers.get("location", "")
if not location:
break
referer = current_url
current_url = urljoin(current_url, location)
self._print_cookie_summary("After callback")
def register(self, password: str, name: str) -> dict:
print("[1/9] Creating temporary mailbox...")
mailbox = self.mail.create_mailbox()
email = mailbox["address"]
print(f" Email: {email}")
self._delay()
csrf_token = self._bootstrap_chatgpt()
self._delay()
auth_url = self._signin_auth0(email, csrf_token)
self._delay()
auth_page_url = self._follow_auth_redirects(auth_url)
self._delay()
accounts_api_base = self._accounts_api_base(auth_url)
register_url = self._register_endpoint(auth_url)
print(f"[5/9] Pure HTTP register endpoint: {register_url}")
response, data = self._attempt_register(register_url, auth_page_url, email, password)
if response.status_code != 200 or not data or not data.get("continue_url"):
captured = self._load_captured_sentinel("username_password_create")
if not captured:
raise RuntimeError(
f"Register failed without sentinel and no captured sentinel found: {self._response_excerpt(response)}"
)
print("[6/9] Retrying register with captured sentinel token...")
response, data = self._attempt_register(register_url, auth_page_url, email, password, sentinel=captured)
if response.status_code != 200 or not data or not data.get("continue_url"):
raise RuntimeError(f"Register blocked: {self._response_excerpt(response)}")
continue_url1 = data["continue_url"]
print(f" Continue URL: {continue_url1}")
email_verification_url = self._open_continue_url(continue_url1, auth_page_url, "[6/9]")
print("[7/9] Waiting for OTP...")
otp = self.mail.wait_for_otp(mailbox, timeout=120)
print(f" OTP: {otp}")
continue_url2 = self._validate_otp(
f"{accounts_api_base}/email-otp/validate",
otp,
email_verification_url,
)
about_you_url = self._open_continue_url(continue_url2, email_verification_url, "[8/9]")
create_response, create_data = self._attempt_create_account(
f"{accounts_api_base}/create_account",
name,
about_you_url,
)
if create_response.status_code != 200 or not create_data or not create_data.get("continue_url"):
captured = self._load_captured_sentinel("oauth_create_account")
if captured:
print("[9/9] Retrying create_account with captured sentinel token...")
create_response, create_data = self._attempt_create_account(
f"{accounts_api_base}/create_account",
name,
about_you_url,
sentinel=captured,
)
if create_response.status_code != 200 or not create_data or not create_data.get("continue_url"):
raise RuntimeError(f"Create account blocked: {self._response_excerpt(create_response)}")
continue_url3 = create_data["continue_url"]
print(f" Final continue URL: {continue_url3}")
self._finalize_auth_callback(continue_url3)
session_response = self.http.request(
"GET",
f"{self.base}/api/auth/session",
headers={"Referer": f"{self.base}/"},
)
session_data = self._json_or_none(session_response) or {}
print(f" Session status: {session_response.status_code}")
return {
"email": email,
"mailbox_password": mailbox.get("password", ""),
"access_token": session_data.get("accessToken", ""),
"user_id": session_data.get("user", {}).get("id", ""),
"continue_url": continue_url3,
}

View File

@@ -0,0 +1,575 @@
"""Replay the captured Codex OAuth login flow over HTTP only."""
from __future__ import annotations
import base64
import json
import os
from pathlib import Path
from typing import Any
from urllib.parse import urljoin, urlparse
from http_client import HTTPClient
try:
from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
from playwright.sync_api import sync_playwright
except Exception: # pragma: no cover - optional runtime dependency path
PlaywrightTimeoutError = Exception
sync_playwright = None
DEFAULT_AUTHORIZE_URL = (
"https://auth.openai.com/oauth/authorize?"
"response_type=code&client_id=app_EMoamEEZ73f0CkXaXp7hrann&"
"redirect_uri=http%3A%2F%2Flocalhost%3A1455%2Fauth%2Fcallback&"
"scope=openid+profile+email+offline_access&"
"code_challenge=DdqIRKaga8whKtumU863MbnpEA74P4YITGhM2l7VLuU&"
"code_challenge_method=S256&state=dfa46133d0bb07e60420e058c3a4e8ea&"
"id_token_add_organizations=true&codex_cli_simplified_flow=true&originator=pi"
)
class FlowError(RuntimeError):
pass
class BrowserSentinelHelper:
chrome_executable = Path("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")
def __init__(self, proxy: str) -> None:
self.proxy = proxy
self.playwright = None
self.browser = None
self.context = None
self.page = None
@classmethod
def is_available(cls) -> bool:
return sync_playwright is not None and cls.chrome_executable.exists()
def start(self) -> None:
if self.page is not None:
return
if sync_playwright is None:
raise FlowError("Playwright is not available in this environment")
launch_kwargs: dict[str, Any] = {
"headless": True,
"executable_path": str(self.chrome_executable),
}
proxy_config = self._playwright_proxy()
if proxy_config:
launch_kwargs["proxy"] = proxy_config
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(**launch_kwargs)
self.context = self.browser.new_context(ignore_https_errors=True)
self.page = self.context.new_page()
def close(self) -> None:
if self.context is not None:
self.context.close()
if self.browser is not None:
self.browser.close()
if self.playwright is not None:
self.playwright.stop()
self.page = None
self.context = None
self.browser = None
self.playwright = None
def open_page(self, url: str) -> str:
self.start()
self.page.goto(url, wait_until="domcontentloaded", timeout=60_000)
try:
self.page.wait_for_load_state("networkidle", timeout=15_000)
except PlaywrightTimeoutError:
pass
print(f"[browser] page -> {self.page.url}")
return self.page.url
def get_sentinel_token(self, flow: str) -> str:
self.start()
token = self.page.evaluate(
"""
async (flow) => {
async function loadScript(src) {
await new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
script.async = true;
script.defer = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load ${src}`));
document.head.appendChild(script);
});
}
if (!window.SentinelSDK) {
try {
await loadScript("https://sentinel.openai.com/backend-api/sentinel/sdk.js");
} catch (error) {
await loadScript("https://chatgpt.com/backend-api/sentinel/sdk.js");
}
}
try {
if (window.SentinelSDK && typeof window.SentinelSDK.init === "function") {
await window.SentinelSDK.init(flow);
}
} catch (error) {
// Match the web app: init failures are tolerated.
}
if (!window.SentinelSDK || typeof window.SentinelSDK.token !== "function") {
return JSON.stringify({ e: "q2n8w7x5z1" });
}
try {
return await window.SentinelSDK.token(flow);
} catch (error) {
return JSON.stringify({ e: "k9d4s6v3b2" });
}
}
""",
flow,
)
if not isinstance(token, str) or not token:
raise FlowError(f"Sentinel token generation failed for flow {flow!r}: {token!r}")
print(f"[browser] sentinel token ready for {flow}")
return token
def cookies(self) -> list[dict[str, Any]]:
self.start()
return self.context.cookies()
def set_cookies(self, cookies: list[dict[str, Any]]) -> None:
self.start()
if cookies:
self.context.add_cookies(cookies)
def _playwright_proxy(self) -> dict[str, str] | None:
if not self.proxy:
return None
parsed = urlparse(self.proxy)
if not parsed.hostname or not parsed.port:
return {"server": self.proxy}
scheme = parsed.scheme
if scheme in {"socks5", "socks5h"}:
# Match HTTPClient's fallback: some proxy providers expose an HTTP proxy
# behind a socks5-looking URL, and Chromium cannot do authenticated socks5 here.
scheme = "http"
proxy: dict[str, str] = {"server": f"{scheme}://{parsed.hostname}:{parsed.port}"}
if parsed.username:
proxy["username"] = parsed.username
if parsed.password:
proxy["password"] = parsed.password
return proxy
def load_proxy() -> str:
proxy = os.getenv("SOCKS5_PROXY", "").strip()
if proxy:
return proxy
env_path = Path(__file__).resolve().parent.parent / ".env"
if not env_path.exists():
return ""
for raw_line in env_path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
if key.strip() != "SOCKS5_PROXY":
continue
return value.strip().strip("\"'")
return ""
class CodexOAuthHTTPFlow:
def __init__(
self,
authorize_url: str,
email: str,
password: str,
otp: str = "",
workspace_id: str = "",
use_browser: bool = False,
) -> None:
self.authorize_url = authorize_url
self.email = email
self.password = password
self.otp = otp
self.workspace_id = workspace_id
self.auth_base = "https://auth.openai.com"
self.proxy = load_proxy()
self.http = HTTPClient(self.proxy)
self.browser = (
BrowserSentinelHelper(self.proxy)
if use_browser and BrowserSentinelHelper.is_available()
else None
)
def close(self) -> None:
self.http.close()
if self.browser is not None:
self.browser.close()
def run(self) -> str:
login_page_url = self._bootstrap_login_page()
self._sync_browser_to_http()
response = self._api_json(
"POST",
f"{self.auth_base}/api/accounts/authorize/continue",
referer=login_page_url,
payload={"username": {"kind": "email", "value": self.email}},
sentinel_flow="authorize_continue",
)
current_page = login_page_url
while True:
continue_url = self._absolute_continue_url(response)
page = response.get("page") or {}
page_type = page.get("type")
print(f"[flow] page_type={page_type!r}")
if continue_url and "/api/oauth/oauth2/auth" in continue_url:
return self._finish_oauth_chain(continue_url, current_page)
if page_type == "login_password":
current_page = self._open_page(continue_url, current_page, browser_sync=True)
response = self._api_json(
"POST",
f"{self.auth_base}/api/accounts/password/verify",
referer=current_page,
payload={"password": self.password},
sentinel_flow="password_verify",
)
continue
if page_type in {"contact_verification", "email_otp_send"}:
response = self._send_email_otp(current_page, continue_url)
continue
if page_type == "email_otp_verification":
current_page = continue_url or f"{self.auth_base}/email-verification"
response = self._validate_email_otp(current_page)
continue
if page_type == "sign_in_with_chatgpt_codex_consent":
current_page = continue_url or f"{self.auth_base}/sign-in-with-chatgpt/codex/consent"
workspace_id = self._pick_workspace_id()
response = self._api_json(
"POST",
f"{self.auth_base}/api/accounts/workspace/select",
referer=current_page,
payload={"workspace_id": workspace_id},
)
continue
raise FlowError(
f"Unsupported page type: {page_type!r}, continue_url={continue_url!r}, "
f"response={json.dumps(response, ensure_ascii=False)}"
)
def _bootstrap_login_page(self) -> str:
if self.browser is not None:
return self.browser.open_page(self.authorize_url)
current_url = self.authorize_url
referer = ""
for step in range(1, 10):
response = self._page_request(current_url, referer=referer)
if response.status_code in {301, 302, 303, 307, 308}:
location = response.headers.get("location")
if not location:
raise FlowError(f"Redirect from {current_url} missing Location header")
next_url = urljoin(current_url, location)
print(f"[bootstrap:{step}] {response.status_code} {current_url} -> {next_url}")
referer = current_url
current_url = next_url
continue
print(f"[bootstrap:{step}] {response.status_code} {current_url}")
return current_url
raise FlowError("Authorize bootstrap exceeded redirect limit")
def _send_email_otp(self, referer: str, continue_url: str) -> dict[str, Any]:
send_url = continue_url or f"{self.auth_base}/api/accounts/email-otp/send"
print(f"[otp] sending email code via {send_url}")
return self._api_json("GET", send_url, referer=referer)
def _validate_email_otp(self, referer: str) -> dict[str, Any]:
validate_url = f"{self.auth_base}/api/accounts/email-otp/validate"
resend_url = f"{self.auth_base}/api/accounts/email-otp/resend"
while True:
code = (self.otp or input("Email OTP (or type 'resend'): ").strip()).strip()
self.otp = ""
if not code:
continue
if code.lower() == "resend":
resend_response = self.http.request(
"POST",
resend_url,
headers={
"Accept": "application/json",
"Origin": self.auth_base,
"Referer": referer,
},
)
print(f"[otp] resend -> {resend_response.status_code}")
if resend_response.status_code == 429:
raise FlowError("Email OTP resend rate-limited")
if resend_response.status_code not in {200, 204}:
raise FlowError(
f"Resend OTP failed: {resend_response.status_code} "
f"{self._response_excerpt(resend_response)}"
)
continue
response = self.http.request(
"POST",
validate_url,
json={"code": code},
headers={
"Accept": "application/json",
"Content-Type": "application/json",
"Origin": self.auth_base,
"Referer": referer,
},
)
print(f"[otp] validate -> {response.status_code}")
if response.status_code == 401:
print("[otp] incorrect code")
continue
if response.status_code == 429:
raise FlowError("Email OTP validation rate-limited")
if response.status_code not in {200, 201}:
raise FlowError(
f"Validate OTP failed: {response.status_code} {self._response_excerpt(response)}"
)
return self._json_from_response(response)
def _pick_workspace_id(self) -> str:
session_data = self._decode_cookie_json("oai-client-auth-session")
workspaces = session_data.get("workspaces") or []
if not workspaces:
raise FlowError(f"No workspaces found in oai-client-auth-session: {session_data}")
if self.workspace_id:
for workspace in workspaces:
if workspace.get("id") == self.workspace_id:
print(f"[workspace] using explicit workspace_id={self.workspace_id}")
return self.workspace_id
raise FlowError(f"workspace_id {self.workspace_id!r} not found in {workspaces}")
if len(workspaces) == 1:
workspace_id = workspaces[0]["id"]
print(f"[workspace] auto-selected {workspace_id}")
return workspace_id
print("[workspace] available workspaces:")
for index, workspace in enumerate(workspaces, start=1):
label = workspace.get("name") or workspace.get("profile_picture_alt_text") or workspace["id"]
print(f" {index}. {label} ({workspace['id']}) [{workspace.get('kind', 'unknown')}]")
while True:
raw = input("Choose workspace number: ").strip()
if not raw.isdigit():
continue
selected = int(raw)
if 1 <= selected <= len(workspaces):
workspace_id = workspaces[selected - 1]["id"]
print(f"[workspace] selected {workspace_id}")
return workspace_id
def _finish_oauth_chain(self, url: str, referer: str) -> str:
current_url = url
current_referer = referer
for step in range(1, 10):
response = self._page_request(current_url, referer=current_referer)
location = response.headers.get("location")
print(
f"[oauth:{step}] {response.status_code} {current_url}"
+ (f" -> {location}" if location else "")
)
if response.status_code not in {301, 302, 303, 307, 308}:
raise FlowError(
f"Expected redirect during oauth finish, got {response.status_code}: "
f"{self._response_excerpt(response)}"
)
if not location:
raise FlowError(f"Redirect from {current_url} missing Location header")
next_url = urljoin(current_url, location)
if next_url.startswith("http://localhost:"):
return next_url
current_referer = current_url
current_url = next_url
raise FlowError("OAuth finish exceeded redirect limit")
def _api_json(
self,
method: str,
url: str,
referer: str,
payload: dict[str, Any] | None = None,
sentinel_flow: str = "",
) -> dict[str, Any]:
headers = {
"Accept": "application/json",
"Origin": self.auth_base,
"Referer": referer,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
}
if payload is not None:
headers["Content-Type"] = "application/json"
if sentinel_flow:
headers["OpenAI-Sentinel-Token"] = self._sentinel_token(sentinel_flow)
request_kwargs: dict[str, Any] = {"headers": headers}
if payload is not None:
request_kwargs["json"] = payload
response = self.http.request(method, url, **request_kwargs)
print(f"[api] {method} {url} -> {response.status_code}")
if response.status_code not in {200, 201}:
raise FlowError(
f"API request failed: {method} {url} -> {response.status_code} "
f"{self._response_excerpt(response)}"
)
return self._json_from_response(response)
def _page_request(self, url: str, referer: str = ""):
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
}
if referer:
headers["Referer"] = referer
return self.http.request("GET", url, headers=headers)
def _open_page(self, url: str, referer: str, browser_sync: bool = False) -> str:
if not url:
raise FlowError("Missing continue_url for page navigation")
if browser_sync and self.browser is not None:
self._sync_http_to_browser()
page_url = self.browser.open_page(url)
self._sync_browser_to_http()
return page_url
response = self._page_request(url, referer=referer)
print(f"[page] GET {url} -> {response.status_code}")
if response.status_code in {301, 302, 303, 307, 308}:
location = response.headers.get("location")
if not location:
raise FlowError(f"Page redirect from {url} missing Location header")
return urljoin(url, location)
if response.status_code != 200:
raise FlowError(f"Page load failed: {url} -> {response.status_code}")
return url
def _absolute_continue_url(self, payload: dict[str, Any]) -> str:
continue_url = payload.get("continue_url") or ""
if not continue_url:
return ""
if urlparse(continue_url).scheme:
return continue_url
return urljoin(self.auth_base, continue_url)
def _decode_cookie_json(self, name: str) -> dict[str, Any]:
raw_value = self._cookie_value(name)
if not raw_value:
return {}
candidates = raw_value.split(".")
for part in candidates[:2]:
padded = part + "=" * (-len(part) % 4)
try:
decoded = base64.urlsafe_b64decode(padded.encode()).decode()
parsed = json.loads(decoded)
except Exception:
continue
if isinstance(parsed, dict):
return parsed
return {}
def _cookie_value(self, name: str) -> str:
for cookie in self.http.session.cookies.jar:
if cookie.name == name:
return cookie.value
return ""
def _sentinel_token(self, flow: str) -> str:
if self.browser is None:
# Fallback: use error token that browser SDK returns when unavailable
print(f"[sentinel] using fallback error token for {flow}")
return '{"e":"q2n8w7x5z1"}'
self._sync_http_to_browser()
return self.browser.get_sentinel_token(flow)
def _sync_browser_to_http(self) -> None:
if self.browser is None:
return
for cookie in self.browser.cookies():
self.http.session.cookies.set(
cookie["name"],
cookie["value"],
domain=cookie.get("domain", ""),
path=cookie.get("path", "/"),
)
def _sync_http_to_browser(self) -> None:
if self.browser is None:
return
cookies: list[dict[str, Any]] = []
for cookie in self.http.session.cookies.jar:
domain = cookie.domain or ""
if not domain:
continue
if "openai.com" not in domain and "chatgpt.com" not in domain:
continue
item: dict[str, Any] = {
"name": cookie.name,
"value": cookie.value,
"domain": domain,
"path": cookie.path or "/",
"secure": bool(cookie.secure),
}
if cookie.expires:
item["expires"] = float(cookie.expires)
cookies.append(item)
self.browser.set_cookies(cookies)
@staticmethod
def _json_from_response(response) -> dict[str, Any]:
content_type = response.headers.get("content-type", "")
if "application/json" not in content_type:
raise FlowError(
f"Expected JSON response, got {content_type}: {CodexOAuthHTTPFlow._response_excerpt(response)}"
)
data = response.json()
if not isinstance(data, dict):
raise FlowError(f"Expected JSON object, got: {data!r}")
return data
@staticmethod
def _response_excerpt(response, limit: int = 500) -> str:
text = response.text or ""
text = text.replace("\r", " ").replace("\n", " ")
return text[:limit]

28
src/config.py Normal file
View File

@@ -0,0 +1,28 @@
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
vmail_api_key: str = Field(default="", env="VMAIL_API_KEY")
yescaptcha_api_key: str = Field(default="", env="YESCAPTCHA_API_KEY")
socks5_proxy: str = Field(default="", env="SOCKS5_PROXY")
# Payment info
card_number: str = Field(default="", env="CARD_NUMBER")
card_exp_month: str = Field(default="", env="CARD_EXP_MONTH")
card_exp_year: str = Field(default="", env="CARD_EXP_YEAR")
card_cvc: str = Field(default="", env="CARD_CVC")
billing_name: str = Field(default="", env="BILLING_NAME")
billing_email: str = Field(default="", env="BILLING_EMAIL")
billing_address_line1: str = Field(default="", env="BILLING_ADDRESS_LINE1")
billing_address_city: str = Field(default="", env="BILLING_ADDRESS_CITY")
billing_address_state: str = Field(default="", env="BILLING_ADDRESS_STATE")
billing_address_postal_code: str = Field(default="", env="BILLING_ADDRESS_POSTAL_CODE")
country: str = Field(default="US", env="COUNTRY")
currency: str = Field(default="usd", env="CURRENCY")
class Config:
env_file = ".env"
settings = Settings()

69
src/http_client.py Normal file
View File

@@ -0,0 +1,69 @@
import uuid
from curl_cffi import requests as curl_requests
class HTTPClient:
def __init__(self, proxy: str = ""):
self.proxy_candidates = self._proxy_candidates(proxy)
self.proxy = self.proxy_candidates[0] if self.proxy_candidates else None
self.session = curl_requests.Session(impersonate="chrome")
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"sec-ch-ua": '"Not A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
})
self.device_id = str(uuid.uuid4())
@staticmethod
def _proxy_candidates(proxy: str) -> list[str]:
if not proxy:
return []
candidates = []
normalized = proxy
if normalized.startswith("socks5://"):
# curl_cffi prefers socks5h when the proxy really is SOCKS.
candidates.append("socks5h://" + normalized[len("socks5://"):])
# Some providers label HTTP proxies as socks5:// in dashboards.
candidates.append("http://" + normalized[len("socks5://"):])
elif normalized.startswith("socks5h://"):
candidates.append(normalized)
candidates.append("http://" + normalized[len("socks5h://"):])
else:
candidates.append(normalized)
seen = set()
ordered = []
for item in candidates:
if item not in seen:
ordered.append(item)
seen.add(item)
return ordered
def request(self, method: str, url: str, **kwargs):
kwargs.setdefault("timeout", 30)
kwargs.setdefault("allow_redirects", False)
# This environment/proxy setup does not expose a usable CA bundle to curl_cffi.
kwargs.setdefault("verify", False)
if not self.proxy_candidates:
return self.session.request(method, url, **kwargs)
last_exc = None
for index, proxy in enumerate(self.proxy_candidates):
request_kwargs = dict(kwargs)
request_kwargs.setdefault("proxy", proxy)
try:
if proxy != self.proxy:
print(f" Retrying HTTP request with proxy scheme fallback: {proxy.split('://', 1)[0]}")
response = self.session.request(method, url, **request_kwargs)
self.proxy = proxy
return response
except Exception as exc:
last_exc = exc
if index == len(self.proxy_candidates) - 1:
raise
raise last_exc
def close(self):
self.session.close()

173
src/main.py Normal file
View File

@@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""Entry point: ChatGPT Plus auto-registration + subscription, or Codex OAuth login."""
import argparse
import random
import string
import sys
from config import settings
from vmail_client import MailClient
from captcha_solver import CaptchaSolver
from http_client import HTTPClient
from chatgpt_register_http_reverse import ChatGPTRegisterHTTPReverse
from chatgpt_payment import ChatGPTPayment
from codex_oauth_http_flow import CodexOAuthHTTPFlow, DEFAULT_AUTHORIZE_URL
def generate_password(length=16):
chars = string.ascii_letters + string.digits + "!@#$%"
while True:
pwd = "".join(random.choice(chars) for _ in range(length))
if (any(c.islower() for c in pwd) and any(c.isupper() for c in pwd)
and any(c.isdigit() for c in pwd) and any(c in "!@#$%" for c in pwd)):
return pwd
def generate_name():
first = random.choice(["John", "Jane", "Mike", "Sarah", "David", "Emma", "Chris", "Lisa"])
last = random.choice(["Smith", "Johnson", "Brown", "Davis", "Wilson", "Moore", "Taylor", "Lee"])
return f"{first} {last}"
def cmd_register(args):
"""Register a new ChatGPT account and optionally subscribe to Plus."""
password = generate_password()
name = generate_name()
print("Generated credentials:")
print(f" Password: {password}")
print(f" Name: {name}")
mail = MailClient()
http = HTTPClient(proxy=settings.socks5_proxy)
try:
print("\n=== ChatGPT Registration ===")
register = ChatGPTRegisterHTTPReverse(http, mail)
session = register.register(password, name)
print("\n Registration complete!")
print(f" Email: {session['email']}")
print(f" Mailbox password: {session['mailbox_password']}")
print(f" ChatGPT password: {password}")
if session["access_token"]:
print(f" Access Token: {session['access_token'][:50]}...")
if settings.card_number and session.get("access_token"):
print("\n=== ChatGPT Plus Subscription ===")
captcha = CaptchaSolver(settings.yescaptcha_api_key)
payment = ChatGPTPayment(http, captcha)
success = payment.subscribe_plus(
access_token=session["access_token"],
country=settings.country,
currency=settings.currency,
card_info={
"number": settings.card_number,
"exp_month": settings.card_exp_month,
"exp_year": settings.card_exp_year,
"cvc": settings.card_cvc,
"billing_name": settings.billing_name or name,
"billing_email": settings.billing_email or session["email"],
"billing_address_line1": settings.billing_address_line1,
"billing_address_city": settings.billing_address_city,
"billing_address_state": settings.billing_address_state,
"billing_address_postal_code": settings.billing_address_postal_code,
},
)
if success:
print("\n Plus subscription complete!")
print(f" Email: {session['email']}")
print(f" Mailbox password: {session['mailbox_password']}")
print(f" ChatGPT password: {password}")
else:
print("\n No card info configured, skipping Plus subscription")
except Exception as e:
print(f"\n Error: {e}")
import traceback
traceback.print_exc()
return 1
finally:
http.close()
return 0
def cmd_codex_login(args):
"""Run Codex OAuth login and print the callback URL with authorization code."""
email = args.email
password = args.password
if not email:
email = input("Email: ").strip()
if not password:
import getpass
password = getpass.getpass("Password: ")
authorize_url = args.authorize_url or DEFAULT_AUTHORIZE_URL
print(f"Starting Codex OAuth flow for {email}")
flow = CodexOAuthHTTPFlow(
authorize_url=authorize_url,
email=email,
password=password,
otp=args.otp or "",
workspace_id=args.workspace_id or "",
)
try:
callback_url = flow.run()
print("\n" + "=" * 60)
print("[SUCCESS] callback_url:")
print(callback_url)
print("=" * 60)
return 0
except Exception as e:
print(f"\n[ERROR] {e}")
import traceback
traceback.print_exc()
return 1
finally:
flow.close()
def build_parser():
parser = argparse.ArgumentParser(
description="ChatGPT Plus auto-registration + subscription, or Codex OAuth login",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Commands:
register Auto-register a new ChatGPT account and subscribe to Plus
codex-login Run Codex CLI OAuth login and obtain the callback URL
Examples:
python src/main.py register
python src/main.py codex-login --email user@example.com --password mypass
"""
)
sub = parser.add_subparsers(dest="command")
# register sub-command
sub.add_parser("register", help="Register new ChatGPT account + optional Plus subscription")
# codex-login sub-command
p_codex = sub.add_parser("codex-login", help="Codex CLI OAuth login (HTTP-only, no browser)")
p_codex.add_argument("--email", default="", help="OpenAI account email")
p_codex.add_argument("--password", default="", help="OpenAI account password")
p_codex.add_argument("--otp", default="", help="Email OTP code (if already known)")
p_codex.add_argument("--workspace-id", dest="workspace_id", default="", help="Workspace ID to select")
p_codex.add_argument("--authorize-url", dest="authorize_url", default="", help="Custom authorize URL")
return parser
def main():
parser = build_parser()
args = parser.parse_args()
if args.command == "register":
return cmd_register(args)
elif args.command == "codex-login":
return cmd_codex_login(args)
else:
parser.print_help()
return 0
if __name__ == "__main__":
sys.exit(main())

91
src/vmail_client.py Normal file
View File

@@ -0,0 +1,91 @@
import re
import secrets
import time
import httpx
MAILTM_BASE = "https://api.mail.tm"
class MailClient:
"""mail.tm temporary email client."""
def create_mailbox(self) -> dict:
"""Create a mailbox. Returns dict with address, password, token."""
domains_resp = httpx.get(f"{MAILTM_BASE}/domains", timeout=30)
domains_resp.raise_for_status()
domains = domains_resp.json().get("hydra:member", [])
active = next((d for d in domains if d.get("isActive")), None)
if not active:
raise RuntimeError("mail.tm returned no active domains")
for _ in range(10):
address = f"{secrets.token_hex(6)}@{active['domain']}"
password = secrets.token_urlsafe(18)
r = httpx.post(
f"{MAILTM_BASE}/accounts",
json={"address": address, "password": password},
timeout=30,
)
if r.status_code == 201:
token_resp = httpx.post(
f"{MAILTM_BASE}/token",
json={"address": address, "password": password},
timeout=30,
)
token_resp.raise_for_status()
return {
"id": r.json()["id"],
"address": address,
"password": password,
"token": token_resp.json()["token"],
}
if r.status_code == 422:
time.sleep(0.3)
continue
r.raise_for_status()
raise RuntimeError("Failed to create mail.tm mailbox after 10 attempts")
def wait_for_otp(self, mailbox: dict, timeout: int = 120, poll: float = 5.0) -> str:
"""Poll mailbox until a 6-digit OTP arrives."""
headers = {"Authorization": f"Bearer {mailbox['token']}"}
deadline = time.time() + timeout
while time.time() < deadline:
try:
r = httpx.get(
f"{MAILTM_BASE}/messages",
headers=headers,
timeout=30,
)
r.raise_for_status()
messages = r.json().get("hydra:member", [])
except httpx.HTTPError as exc:
print(f"mail poll error: {exc.__class__.__name__}")
time.sleep(poll)
continue
if messages:
try:
msg_r = httpx.get(
f"{MAILTM_BASE}/messages/{messages[0]['id']}",
headers=headers,
timeout=30,
)
msg_r.raise_for_status()
msg = msg_r.json()
except httpx.HTTPError as exc:
print(f"mail fetch error: {exc.__class__.__name__}")
time.sleep(poll)
continue
html = msg.get("html") or ""
if isinstance(html, list):
html = "\n".join(html)
body = msg.get("text") or html or ""
codes = re.findall(r"\b(\d{6})\b", body)
if codes:
return codes[0]
time.sleep(poll)
raise TimeoutError(f"No OTP received within {timeout}s")

459
uv.lock generated Normal file
View File

@@ -0,0 +1,459 @@
version = 1
revision = 1
requires-python = ">=3.11"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
[[package]]
name = "anyio"
version = "4.12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592 },
]
[[package]]
name = "certifi"
version = "2026.2.25"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684 },
]
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344 },
{ url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560 },
{ url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613 },
{ url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476 },
{ url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374 },
{ url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597 },
{ url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574 },
{ url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971 },
{ url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972 },
{ url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078 },
{ url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076 },
{ url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820 },
{ url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635 },
{ url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271 },
{ url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048 },
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529 },
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097 },
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983 },
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519 },
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572 },
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963 },
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361 },
{ url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932 },
{ url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557 },
{ url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762 },
{ url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 },
{ url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 },
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 },
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 },
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 },
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 },
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 },
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 },
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 },
{ url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 },
{ url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 },
{ url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 },
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 },
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 },
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 },
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 },
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 },
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 },
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 },
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 },
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 },
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 },
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 },
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 },
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 },
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 },
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 },
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 },
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 },
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 },
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 },
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 },
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 },
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 },
]
[[package]]
name = "curl-cffi"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "cffi" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9b/c9/0067d9a25ed4592b022d4558157fcdb6e123516083700786d38091688767/curl_cffi-0.14.0.tar.gz", hash = "sha256:5ffbc82e59f05008ec08ea432f0e535418823cda44178ee518906a54f27a5f0f", size = 162633 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/f0/0f21e9688eaac85e705537b3a87a5588d0cefb2f09d83e83e0e8be93aa99/curl_cffi-0.14.0-cp39-abi3-macosx_14_0_arm64.whl", hash = "sha256:e35e89c6a69872f9749d6d5fda642ed4fc159619329e99d577d0104c9aad5893", size = 3087277 },
{ url = "https://files.pythonhosted.org/packages/ba/a3/0419bd48fce5b145cb6a2344c6ac17efa588f5b0061f212c88e0723da026/curl_cffi-0.14.0-cp39-abi3-macosx_15_0_x86_64.whl", hash = "sha256:5945478cd28ad7dfb5c54473bcfb6743ee1d66554d57951fdf8fc0e7d8cf4e45", size = 5804650 },
{ url = "https://files.pythonhosted.org/packages/e2/07/a238dd062b7841b8caa2fa8a359eb997147ff3161288f0dd46654d898b4d/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c42e8fa3c667db9ccd2e696ee47adcd3cd5b0838d7282f3fc45f6c0ef3cfdfa7", size = 8231918 },
{ url = "https://files.pythonhosted.org/packages/7c/d2/ce907c9b37b5caf76ac08db40cc4ce3d9f94c5500db68a195af3513eacbc/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:060fe2c99c41d3cb7f894de318ddf4b0301b08dca70453d769bd4e74b36b8483", size = 8654624 },
{ url = "https://files.pythonhosted.org/packages/f2/ae/6256995b18c75e6ef76b30753a5109e786813aa79088b27c8eabb1ef85c9/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b158c41a25388690dd0d40b5bc38d1e0f512135f17fdb8029868cbc1993d2e5b", size = 8010654 },
{ url = "https://files.pythonhosted.org/packages/fb/10/ff64249e516b103cb762e0a9dca3ee0f04cf25e2a1d5d9838e0f1273d071/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_i686.whl", hash = "sha256:1439fbef3500fb723333c826adf0efb0e2e5065a703fb5eccce637a2250db34a", size = 7781969 },
{ url = "https://files.pythonhosted.org/packages/51/76/d6f7bb76c2d12811aa7ff16f5e17b678abdd1b357b9a8ac56310ceccabd5/curl_cffi-0.14.0-cp39-abi3-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e7176f2c2d22b542e3cf261072a81deb018cfa7688930f95dddef215caddb469", size = 7969133 },
{ url = "https://files.pythonhosted.org/packages/23/7c/cca39c0ed4e1772613d3cba13091c0e9d3b89365e84b9bf9838259a3cd8f/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:03f21ade2d72978c2bb8670e9b6de5260e2755092b02d94b70b906813662998d", size = 9080167 },
{ url = "https://files.pythonhosted.org/packages/75/03/a942d7119d3e8911094d157598ae0169b1c6ca1bd3f27d7991b279bcc45b/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:58ebf02de64ee5c95613209ddacb014c2d2f86298d7080c0a1c12ed876ee0690", size = 9520464 },
{ url = "https://files.pythonhosted.org/packages/a2/77/78900e9b0833066d2274bda75cba426fdb4cef7fbf6a4f6a6ca447607bec/curl_cffi-0.14.0-cp39-abi3-win_amd64.whl", hash = "sha256:6e503f9a103f6ae7acfb3890c843b53ec030785a22ae7682a22cc43afb94123e", size = 1677416 },
{ url = "https://files.pythonhosted.org/packages/5c/7c/d2ba86b0b3e1e2830bd94163d047de122c69a8df03c5c7c36326c456ad82/curl_cffi-0.14.0-cp39-abi3-win_arm64.whl", hash = "sha256:2eed50a969201605c863c4c31269dfc3e0da52916086ac54553cfa353022425c", size = 1425067 },
]
[[package]]
name = "gptplus-auto"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "curl-cffi" },
{ name = "httpx", extra = ["socks"] },
{ name = "playwright" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
]
[package.metadata]
requires-dist = [
{ name = "curl-cffi", specifier = ">=0.14.0" },
{ name = "httpx", extras = ["socks"], specifier = ">=0.27.0" },
{ name = "playwright", specifier = ">=1.58.0" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "pydantic-settings", specifier = ">=2.0.0" },
]
[package.metadata.requires-dev]
dev = []
[[package]]
name = "greenlet"
version = "3.3.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a3/51/1664f6b78fc6ebbd98019a1fd730e83fa78f2db7058f72b1463d3612b8db/greenlet-3.3.2.tar.gz", hash = "sha256:2eaf067fc6d886931c7962e8c6bede15d2f01965560f3359b27c80bde2d151f2", size = 188267 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/47/16400cb42d18d7a6bb46f0626852c1718612e35dcb0dffa16bbaffdf5dd2/greenlet-3.3.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:c56692189a7d1c7606cb794be0a8381470d95c57ce5be03fb3d0ef57c7853b86", size = 278890 },
{ url = "https://files.pythonhosted.org/packages/a3/90/42762b77a5b6aa96cd8c0e80612663d39211e8ae8a6cd47c7f1249a66262/greenlet-3.3.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ebd458fa8285960f382841da585e02201b53a5ec2bac6b156fc623b5ce4499f", size = 581120 },
{ url = "https://files.pythonhosted.org/packages/bf/6f/f3d64f4fa0a9c7b5c5b3c810ff1df614540d5aa7d519261b53fba55d4df9/greenlet-3.3.2-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a443358b33c4ec7b05b79a7c8b466f5d275025e750298be7340f8fc63dff2a55", size = 594363 },
{ url = "https://files.pythonhosted.org/packages/9c/8b/1430a04657735a3f23116c2e0d5eb10220928846e4537a938a41b350bed6/greenlet-3.3.2-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4375a58e49522698d3e70cc0b801c19433021b5c37686f7ce9c65b0d5c8677d2", size = 605046 },
{ url = "https://files.pythonhosted.org/packages/72/83/3e06a52aca8128bdd4dcd67e932b809e76a96ab8c232a8b025b2850264c5/greenlet-3.3.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2cd90d413acbf5e77ae41e5d3c9b3ac1d011a756d7284d7f3f2b806bbd6358", size = 594156 },
{ url = "https://files.pythonhosted.org/packages/70/79/0de5e62b873e08fe3cef7dbe84e5c4bc0e8ed0c7ff131bccb8405cd107c8/greenlet-3.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:442b6057453c8cb29b4fb36a2ac689382fc71112273726e2423f7f17dc73bf99", size = 1554649 },
{ url = "https://files.pythonhosted.org/packages/5a/00/32d30dee8389dc36d42170a9c66217757289e2afb0de59a3565260f38373/greenlet-3.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45abe8eb6339518180d5a7fa47fa01945414d7cca5ecb745346fc6a87d2750be", size = 1619472 },
{ url = "https://files.pythonhosted.org/packages/f1/3a/efb2cf697fbccdf75b24e2c18025e7dfa54c4f31fab75c51d0fe79942cef/greenlet-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e692b2dae4cc7077cbb11b47d258533b48c8fde69a33d0d8a82e2fe8d8531d5", size = 230389 },
{ url = "https://files.pythonhosted.org/packages/e1/a1/65bbc059a43a7e2143ec4fc1f9e3f673e04f9c7b371a494a101422ac4fd5/greenlet-3.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:02b0a8682aecd4d3c6c18edf52bc8e51eacdd75c8eac52a790a210b06aa295fd", size = 229645 },
{ url = "https://files.pythonhosted.org/packages/ea/ab/1608e5a7578e62113506740b88066bf09888322a311cff602105e619bd87/greenlet-3.3.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ac8d61d4343b799d1e526db579833d72f23759c71e07181c2d2944e429eb09cd", size = 280358 },
{ url = "https://files.pythonhosted.org/packages/a5/23/0eae412a4ade4e6623ff7626e38998cb9b11e9ff1ebacaa021e4e108ec15/greenlet-3.3.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ceec72030dae6ac0c8ed7591b96b70410a8be370b6a477b1dbc072856ad02bd", size = 601217 },
{ url = "https://files.pythonhosted.org/packages/f8/16/5b1678a9c07098ecb9ab2dd159fafaf12e963293e61ee8d10ecb55273e5e/greenlet-3.3.2-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2a5be83a45ce6188c045bcc44b0ee037d6a518978de9a5d97438548b953a1ac", size = 611792 },
{ url = "https://files.pythonhosted.org/packages/5c/c5/cc09412a29e43406eba18d61c70baa936e299bc27e074e2be3806ed29098/greenlet-3.3.2-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9e21c84035c490506c17002f5c8ab25f980205c3e61ddb3a2a2a2e6c411fcb", size = 626250 },
{ url = "https://files.pythonhosted.org/packages/50/1f/5155f55bd71cabd03765a4aac9ac446be129895271f73872c36ebd4b04b6/greenlet-3.3.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43e99d1749147ac21dde49b99c9abffcbc1e2d55c67501465ef0930d6e78e070", size = 613875 },
{ url = "https://files.pythonhosted.org/packages/fc/dd/845f249c3fcd69e32df80cdab059b4be8b766ef5830a3d0aa9d6cad55beb/greenlet-3.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c956a19350e2c37f2c48b336a3afb4bff120b36076d9d7fb68cb44e05d95b79", size = 1571467 },
{ url = "https://files.pythonhosted.org/packages/2a/50/2649fe21fcc2b56659a452868e695634722a6655ba245d9f77f5656010bf/greenlet-3.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6c6f8ba97d17a1e7d664151284cb3315fc5f8353e75221ed4324f84eb162b395", size = 1640001 },
{ url = "https://files.pythonhosted.org/packages/9b/40/cc802e067d02af8b60b6771cea7d57e21ef5e6659912814babb42b864713/greenlet-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:34308836d8370bddadb41f5a7ce96879b72e2fdfb4e87729330c6ab52376409f", size = 231081 },
{ url = "https://files.pythonhosted.org/packages/58/2e/fe7f36ff1982d6b10a60d5e0740c759259a7d6d2e1dc41da6d96de32fff6/greenlet-3.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:d3a62fa76a32b462a97198e4c9e99afb9ab375115e74e9a83ce180e7a496f643", size = 230331 },
{ url = "https://files.pythonhosted.org/packages/ac/48/f8b875fa7dea7dd9b33245e37f065af59df6a25af2f9561efa8d822fde51/greenlet-3.3.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:aa6ac98bdfd716a749b84d4034486863fd81c3abde9aa3cf8eff9127981a4ae4", size = 279120 },
{ url = "https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab0c7e7901a00bc0a7284907273dc165b32e0d109a6713babd04471327ff7986", size = 603238 },
{ url = "https://files.pythonhosted.org/packages/59/0e/4223c2bbb63cd5c97f28ffb2a8aee71bdfb30b323c35d409450f51b91e3e/greenlet-3.3.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d248d8c23c67d2291ffd47af766e2a3aa9fa1c6703155c099feb11f526c63a92", size = 614219 },
{ url = "https://files.pythonhosted.org/packages/94/2b/4d012a69759ac9d77210b8bfb128bc621125f5b20fc398bce3940d036b1c/greenlet-3.3.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ccd21bb86944ca9be6d967cf7691e658e43417782bce90b5d2faeda0ff78a7dd", size = 628268 },
{ url = "https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6997d360a4e6a4e936c0f9625b1c20416b8a0ea18a8e19cabbefc712e7397ab", size = 616774 },
{ url = "https://files.pythonhosted.org/packages/0a/03/996c2d1689d486a6e199cb0f1cf9e4aa940c500e01bdf201299d7d61fa69/greenlet-3.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:64970c33a50551c7c50491671265d8954046cb6e8e2999aacdd60e439b70418a", size = 1571277 },
{ url = "https://files.pythonhosted.org/packages/d9/c4/2570fc07f34a39f2caf0bf9f24b0a1a0a47bc2e8e465b2c2424821389dfc/greenlet-3.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1a9172f5bf6bd88e6ba5a84e0a68afeac9dc7b6b412b245dd64f52d83c81e55b", size = 1640455 },
{ url = "https://files.pythonhosted.org/packages/91/39/5ef5aa23bc545aa0d31e1b9b55822b32c8da93ba657295840b6b34124009/greenlet-3.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:a7945dd0eab63ded0a48e4dcade82939783c172290a7903ebde9e184333ca124", size = 230961 },
{ url = "https://files.pythonhosted.org/packages/62/6b/a89f8456dcb06becff288f563618e9f20deed8dd29beea14f9a168aef64b/greenlet-3.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:394ead29063ee3515b4e775216cb756b2e3b4a7e55ae8fd884f17fa579e6b327", size = 230221 },
{ url = "https://files.pythonhosted.org/packages/3f/ae/8bffcbd373b57a5992cd077cbe8858fff39110480a9d50697091faea6f39/greenlet-3.3.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:8d1658d7291f9859beed69a776c10822a0a799bc4bfe1bd4272bb60e62507dab", size = 279650 },
{ url = "https://files.pythonhosted.org/packages/d1/c0/45f93f348fa49abf32ac8439938726c480bd96b2a3c6f4d949ec0124b69f/greenlet-3.3.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:18cb1b7337bca281915b3c5d5ae19f4e76d35e1df80f4ad3c1a7be91fadf1082", size = 650295 },
{ url = "https://files.pythonhosted.org/packages/b3/de/dd7589b3f2b8372069ab3e4763ea5329940fc7ad9dcd3e272a37516d7c9b/greenlet-3.3.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2e47408e8ce1c6f1ceea0dffcdf6ebb85cc09e55c7af407c99f1112016e45e9", size = 662163 },
{ url = "https://files.pythonhosted.org/packages/cd/ac/85804f74f1ccea31ba518dcc8ee6f14c79f73fe36fa1beba38930806df09/greenlet-3.3.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e3cb43ce200f59483eb82949bf1835a99cf43d7571e900d7c8d5c62cdf25d2f9", size = 675371 },
{ url = "https://files.pythonhosted.org/packages/d2/d8/09bfa816572a4d83bccd6750df1926f79158b1c36c5f73786e26dbe4ee38/greenlet-3.3.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63d10328839d1973e5ba35e98cccbca71b232b14051fd957b6f8b6e8e80d0506", size = 664160 },
{ url = "https://files.pythonhosted.org/packages/48/cf/56832f0c8255d27f6c35d41b5ec91168d74ec721d85f01a12131eec6b93c/greenlet-3.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8e4ab3cfb02993c8cc248ea73d7dae6cec0253e9afa311c9b37e603ca9fad2ce", size = 1619181 },
{ url = "https://files.pythonhosted.org/packages/0a/23/b90b60a4aabb4cec0796e55f25ffbfb579a907c3898cd2905c8918acaa16/greenlet-3.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94ad81f0fd3c0c0681a018a976e5c2bd2ca2d9d94895f23e7bb1af4e8af4e2d5", size = 1687713 },
{ url = "https://files.pythonhosted.org/packages/f3/ca/2101ca3d9223a1dc125140dbc063644dca76df6ff356531eb27bc267b446/greenlet-3.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:8c4dd0f3997cf2512f7601563cc90dfb8957c0cff1e3a1b23991d4ea1776c492", size = 232034 },
{ url = "https://files.pythonhosted.org/packages/f6/4a/ecf894e962a59dea60f04877eea0fd5724618da89f1867b28ee8b91e811f/greenlet-3.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:cd6f9e2bbd46321ba3bbb4c8a15794d32960e3b0ae2cc4d49a1a53d314805d71", size = 231437 },
{ url = "https://files.pythonhosted.org/packages/98/6d/8f2ef704e614bcf58ed43cfb8d87afa1c285e98194ab2cfad351bf04f81e/greenlet-3.3.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:e26e72bec7ab387ac80caa7496e0f908ff954f31065b0ffc1f8ecb1338b11b54", size = 286617 },
{ url = "https://files.pythonhosted.org/packages/5e/0d/93894161d307c6ea237a43988f27eba0947b360b99ac5239ad3fe09f0b47/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b466dff7a4ffda6ca975979bab80bdadde979e29fc947ac3be4451428d8b0e4", size = 655189 },
{ url = "https://files.pythonhosted.org/packages/f5/2c/d2d506ebd8abcb57386ec4f7ba20f4030cbe56eae541bc6fd6ef399c0b41/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8bddc5b73c9720bea487b3bffdb1840fe4e3656fba3bd40aa1489e9f37877ff", size = 658225 },
{ url = "https://files.pythonhosted.org/packages/d1/67/8197b7e7e602150938049d8e7f30de1660cfb87e4c8ee349b42b67bdb2e1/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:59b3e2c40f6706b05a9cd299c836c6aa2378cabe25d021acd80f13abf81181cf", size = 666581 },
{ url = "https://files.pythonhosted.org/packages/8e/30/3a09155fbf728673a1dea713572d2d31159f824a37c22da82127056c44e4/greenlet-3.3.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b26b0f4428b871a751968285a1ac9648944cea09807177ac639b030bddebcea4", size = 657907 },
{ url = "https://files.pythonhosted.org/packages/f3/fd/d05a4b7acd0154ed758797f0a43b4c0962a843bedfe980115e842c5b2d08/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1fb39a11ee2e4d94be9a76671482be9398560955c9e568550de0224e41104727", size = 1618857 },
{ url = "https://files.pythonhosted.org/packages/6f/e1/50ee92a5db521de8f35075b5eff060dd43d39ebd46c2181a2042f7070385/greenlet-3.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:20154044d9085151bc309e7689d6f7ba10027f8f5a8c0676ad398b951913d89e", size = 1680010 },
{ url = "https://files.pythonhosted.org/packages/29/4b/45d90626aef8e65336bed690106d1382f7a43665e2249017e9527df8823b/greenlet-3.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c04c5e06ec3e022cbfe2cd4a846e1d4e50087444f875ff6d2c2ad8445495cf1a", size = 237086 },
]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 },
]
[[package]]
name = "httpcore"
version = "1.0.9"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
]
[package.optional-dependencies]
socks = [
{ name = "socksio" },
]
[[package]]
name = "idna"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008 },
]
[[package]]
name = "playwright"
version = "1.58.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet" },
{ name = "pyee" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/c9/9c6061d5703267f1baae6a4647bfd1862e386fbfdb97d889f6f6ae9e3f64/playwright-1.58.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:96e3204aac292ee639edbfdef6298b4be2ea0a55a16b7068df91adac077cc606", size = 42251098 },
{ url = "https://files.pythonhosted.org/packages/e0/40/59d34a756e02f8c670f0fee987d46f7ee53d05447d43cd114ca015cb168c/playwright-1.58.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:70c763694739d28df71ed578b9c8202bb83e8fe8fb9268c04dd13afe36301f71", size = 41039625 },
{ url = "https://files.pythonhosted.org/packages/e1/ee/3ce6209c9c74a650aac9028c621f357a34ea5cd4d950700f8e2c4b7fe2c4/playwright-1.58.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:185e0132578733d02802dfddfbbc35f42be23a45ff49ccae5081f25952238117", size = 42251098 },
{ url = "https://files.pythonhosted.org/packages/f1/af/009958cbf23fac551a940d34e3206e6c7eed2b8c940d0c3afd1feb0b0589/playwright-1.58.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:c95568ba1eda83812598c1dc9be60b4406dffd60b149bc1536180ad108723d6b", size = 46235268 },
{ url = "https://files.pythonhosted.org/packages/d9/a6/0e66ad04b6d3440dae73efb39540c5685c5fc95b17c8b29340b62abbd952/playwright-1.58.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f9999948f1ab541d98812de25e3a8c410776aa516d948807140aff797b4bffa", size = 45964214 },
{ url = "https://files.pythonhosted.org/packages/0e/4b/236e60ab9f6d62ed0fd32150d61f1f494cefbf02304c0061e78ed80c1c32/playwright-1.58.0-py3-none-win32.whl", hash = "sha256:1e03be090e75a0fabbdaeab65ce17c308c425d879fa48bb1d7986f96bfad0b99", size = 36815998 },
{ url = "https://files.pythonhosted.org/packages/41/f8/5ec599c5e59d2f2f336a05b4f318e733077cd5044f24adb6f86900c3e6a7/playwright-1.58.0-py3-none-win_amd64.whl", hash = "sha256:a2bf639d0ce33b3ba38de777e08697b0d8f3dc07ab6802e4ac53fb65e3907af8", size = 36816005 },
{ url = "https://files.pythonhosted.org/packages/c8/c4/cc0229fea55c87d6c9c67fe44a21e2cd28d1d558a5478ed4d617e9fb0c93/playwright-1.58.0-py3-none-win_arm64.whl", hash = "sha256:32ffe5c303901a13a0ecab91d1c3f74baf73b84f4bedbb6b935f5bc11cc98e1b", size = 33085919 },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 },
]
[[package]]
name = "pydantic"
version = "2.12.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580 },
]
[[package]]
name = "pydantic-core"
version = "2.41.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873 },
{ url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826 },
{ url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869 },
{ url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890 },
{ url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740 },
{ url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021 },
{ url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378 },
{ url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761 },
{ url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303 },
{ url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355 },
{ url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875 },
{ url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549 },
{ url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305 },
{ url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902 },
{ url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990 },
{ url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003 },
{ url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200 },
{ url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578 },
{ url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504 },
{ url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816 },
{ url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366 },
{ url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698 },
{ url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603 },
{ url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591 },
{ url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068 },
{ url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908 },
{ url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145 },
{ url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179 },
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403 },
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206 },
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307 },
{ url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258 },
{ url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917 },
{ url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186 },
{ url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164 },
{ url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146 },
{ url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788 },
{ url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133 },
{ url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852 },
{ url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679 },
{ url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766 },
{ url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005 },
{ url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622 },
{ url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725 },
{ url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040 },
{ url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691 },
{ url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897 },
{ url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302 },
{ url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877 },
{ url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680 },
{ url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960 },
{ url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102 },
{ url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039 },
{ url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126 },
{ url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489 },
{ url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288 },
{ url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255 },
{ url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760 },
{ url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092 },
{ url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385 },
{ url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832 },
{ url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585 },
{ url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078 },
{ url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914 },
{ url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560 },
{ url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244 },
{ url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955 },
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906 },
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607 },
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769 },
{ url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441 },
{ url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291 },
{ url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632 },
{ url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905 },
{ url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495 },
{ url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388 },
{ url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879 },
{ url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017 },
{ url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980 },
{ url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865 },
{ url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256 },
{ url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762 },
{ url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141 },
{ url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317 },
{ url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992 },
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302 },
]
[[package]]
name = "pydantic-settings"
version = "2.13.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "python-dotenv" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929 },
]
[[package]]
name = "pyee"
version = "13.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659 },
]
[[package]]
name = "python-dotenv"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101 },
]
[[package]]
name = "socksio"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/48a7d9495be3d1c651198fd99dbb6ce190e2274d0f28b9051307bdec6b85/socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac", size = 19055 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/37/c3/6eeb6034408dac0fa653d126c9204ade96b819c936e136c5e8a6897eee9c/socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3", size = 12763 },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 },
]