v3.11.12: dynamic version fetch with probe validation
This commit is contained in:
Binary file not shown.
@@ -320,9 +320,10 @@ _active_requests = {}
|
|||||||
_active_requests_lock = threading.Lock()
|
_active_requests_lock = threading.Lock()
|
||||||
|
|
||||||
_pool = uuid.uuid4().hex[:8]
|
_pool = uuid.uuid4().hex[:8]
|
||||||
_antigravity_version = "1.18.3"
|
_antigravity_version = "2.0.1"
|
||||||
_antigravity_version_checked = 0
|
_antigravity_version_checked = 0
|
||||||
_antigravity_version_lock = threading.Lock()
|
_antigravity_version_lock = threading.Lock()
|
||||||
|
_antigravity_version_validated = False
|
||||||
_last_user_urls = collections.deque(maxlen=20)
|
_last_user_urls = collections.deque(maxlen=20)
|
||||||
|
|
||||||
_conn_pool_lock = threading.Lock()
|
_conn_pool_lock = threading.Lock()
|
||||||
@@ -798,49 +799,137 @@ _ANTIGRAVITY_LOOP_TRACKER_LOCK = threading.Lock()
|
|||||||
def _antigravity_loop_key(session_id):
|
def _antigravity_loop_key(session_id):
|
||||||
return f"ag:{session_id}"
|
return f"ag:{session_id}"
|
||||||
|
|
||||||
|
def _validate_antigravity_version(version, access_token=None, project_id=None):
|
||||||
|
if not version or not re.match(r"^\d+\.\d+\.\d+$", version):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
if not access_token:
|
||||||
|
access_token = _refresh_oauth_token()
|
||||||
|
if not project_id:
|
||||||
|
token_path = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy", "google-antigravity-oauth-token.json")
|
||||||
|
try:
|
||||||
|
with open(token_path) as f:
|
||||||
|
project_id = json.load(f).get("project_id", "")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not access_token or not project_id:
|
||||||
|
return True
|
||||||
|
import platform as _plat
|
||||||
|
_os_name = _plat.system().lower()
|
||||||
|
_os_arch = _plat.machine().lower().replace("x86_64", "x64").replace("aarch64", "arm64")
|
||||||
|
ua = f"antigravity/{version} {_os_name}/{_os_arch}"
|
||||||
|
body = {
|
||||||
|
"project": project_id,
|
||||||
|
"model": "gemini-3-flash",
|
||||||
|
"requestType": "agent",
|
||||||
|
"userAgent": ua,
|
||||||
|
"requestId": f"probe-{uuid.uuid4().hex[:8]}",
|
||||||
|
"request": {
|
||||||
|
"contents": [{"role": "user", "parts": [{"text": "hi"}]}],
|
||||||
|
"sessionId": f"probe{int(time.time()*1000)}",
|
||||||
|
"safetySettings": [
|
||||||
|
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "OFF"},
|
||||||
|
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "OFF"},
|
||||||
|
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "OFF"},
|
||||||
|
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
|
||||||
|
{"category": "HARM_CATEGORY_CIVIC_INTEGRITY", "threshold": "OFF"},
|
||||||
|
],
|
||||||
|
"generationConfig": {"maxOutputTokens": 32, "stopSequences": ["\n\nHuman:", "[DONE]"]},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url = "https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
|
"User-Agent": ua,
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(url, data=json.dumps(body).encode(), headers=headers)
|
||||||
|
resp = urllib.request.urlopen(req, timeout=15)
|
||||||
|
data = resp.read().decode()
|
||||||
|
if "no longer supported" in data.lower():
|
||||||
|
print(f"[antigravity-version] version {version} rejected (deprecated)", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
if e.code == 404:
|
||||||
|
print(f"[antigravity-version] version {version} rejected (404)", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[antigravity-version] probe error for {version}: {e}", file=sys.stderr)
|
||||||
|
return True
|
||||||
|
|
||||||
def _fetch_antigravity_version():
|
def _fetch_antigravity_version():
|
||||||
cache_path = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy", "antigravity-version.json")
|
cache_path = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy", "antigravity-version.json")
|
||||||
try:
|
try:
|
||||||
with open(cache_path) as f:
|
with open(cache_path) as f:
|
||||||
cached = json.load(f)
|
cached = json.load(f)
|
||||||
if cached.get("version") and cached.get("checked_at", 0) > time.time() - 6 * 3600:
|
if cached.get("version") and cached.get("validated") and cached.get("checked_at", 0) > time.time() - 6 * 3600:
|
||||||
return cached["version"]
|
return cached["version"]
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
urls = [
|
|
||||||
|
access_token = None
|
||||||
|
project_id = None
|
||||||
|
try:
|
||||||
|
access_token = _refresh_oauth_token()
|
||||||
|
token_path = os.path.join(os.path.expanduser("~"), ".cache", "codex-proxy", "google-antigravity-oauth-token.json")
|
||||||
|
with open(token_path) as f:
|
||||||
|
project_id = json.load(f).get("project_id", "")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
sources = [
|
||||||
("https://antigravity-auto-updater-974169037036.us-central1.run.app", None),
|
("https://antigravity-auto-updater-974169037036.us-central1.run.app", None),
|
||||||
("https://antigravity.google/changelog", 5000),
|
("https://antigravity.google/changelog", 5000),
|
||||||
]
|
]
|
||||||
for url, limit in urls:
|
|
||||||
|
candidates = []
|
||||||
|
for url, limit in sources:
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||||||
resp = urllib.request.urlopen(req, timeout=5)
|
resp = urllib.request.urlopen(req, timeout=5)
|
||||||
text = resp.read().decode(errors="replace")
|
text = resp.read().decode(errors="replace")
|
||||||
if limit:
|
if limit:
|
||||||
text = text[:limit]
|
text = text[:limit]
|
||||||
m = re.search(r"\d+\.\d+\.\d+", text)
|
for m in re.finditer(r"\d+\.\d+\.\d+", text):
|
||||||
if m:
|
ver = m.group(0)
|
||||||
version = m.group(0)
|
if ver not in candidates:
|
||||||
|
candidates.append(ver)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for ver in candidates:
|
||||||
|
if _validate_antigravity_version(ver, access_token, project_id):
|
||||||
|
print(f"[antigravity-version] fetched version {ver} validated", file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
|
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
|
||||||
with open(cache_path, "w", encoding="utf-8") as f:
|
with open(cache_path, "w", encoding="utf-8") as f:
|
||||||
json.dump({"version": version, "checked_at": time.time()}, f)
|
json.dump({"version": ver, "validated": True, "checked_at": time.time()}, f)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return version
|
return ver
|
||||||
|
|
||||||
|
fallback = "2.0.1"
|
||||||
|
print(f"[antigravity-version] all candidates failed, using fallback {fallback}", file=sys.stderr)
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
|
||||||
|
with open(cache_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"version": fallback, "validated": False, "checked_at": time.time()}, f)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return _antigravity_version
|
return fallback
|
||||||
|
|
||||||
def _ensure_antigravity_version():
|
def _ensure_antigravity_version():
|
||||||
global _antigravity_version, _antigravity_version_checked
|
global _antigravity_version, _antigravity_version_checked, _antigravity_version_validated
|
||||||
if time.time() - _antigravity_version_checked < 6 * 3600:
|
if _antigravity_version_validated and time.time() - _antigravity_version_checked < 6 * 3600:
|
||||||
return _antigravity_version
|
return _antigravity_version
|
||||||
with _antigravity_version_lock:
|
with _antigravity_version_lock:
|
||||||
if time.time() - _antigravity_version_checked < 6 * 3600:
|
if _antigravity_version_validated and time.time() - _antigravity_version_checked < 6 * 3600:
|
||||||
return _antigravity_version
|
return _antigravity_version
|
||||||
_antigravity_version = _fetch_antigravity_version()
|
_antigravity_version = _fetch_antigravity_version()
|
||||||
_antigravity_version_checked = time.time()
|
_antigravity_version_checked = time.time()
|
||||||
|
_antigravity_version_validated = True
|
||||||
return _antigravity_version
|
return _antigravity_version
|
||||||
|
|
||||||
_antigravity_client_version = "1.110.0"
|
_antigravity_client_version = "1.110.0"
|
||||||
@@ -5643,10 +5732,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
_os_name = _plat.system().lower()
|
_os_name = _plat.system().lower()
|
||||||
_os_arch = _plat.machine().lower().replace("x86_64", "x64").replace("aarch64", "arm64")
|
_os_arch = _plat.machine().lower().replace("x86_64", "x64").replace("aarch64", "arm64")
|
||||||
_fetched_ver = _ensure_antigravity_version()
|
_fetched_ver = _ensure_antigravity_version()
|
||||||
_fallback_ver = "1.15.8"
|
_ag_ua = f"antigravity/{_fetched_ver} {_os_name}/{_os_arch}"
|
||||||
_versions = [_fetched_ver, _fallback_ver] if _fetched_ver != _fallback_ver else [_fallback_ver]
|
|
||||||
_version_404s = set()
|
|
||||||
_ag_ua = f"antigravity/{_versions[0]} {_os_name}/{_os_arch}"
|
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": f"Bearer {access_token}",
|
"Authorization": f"Bearer {access_token}",
|
||||||
@@ -5739,12 +5825,18 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
|
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if _all_404 and upstream is None and len(_versions) > 1:
|
if _all_404 and upstream is None:
|
||||||
_fallback_ver = _versions[1]
|
print(f"[{self._session_id}] [antigravity-v2] all endpoints 404, invalidating version cache and re-fetching", file=sys.stderr)
|
||||||
print(f"[{self._session_id}] [antigravity-v2] all 404, retrying with fallback version {_fallback_ver}", file=sys.stderr)
|
global _antigravity_version_validated
|
||||||
_ag_ua_fb = f"antigravity/{_fallback_ver} {_os_name}/{_os_arch}"
|
with _antigravity_version_lock:
|
||||||
headers["User-Agent"] = _ag_ua_fb
|
_antigravity_version_validated = False
|
||||||
wrapped["userAgent"] = _ag_ua_fb
|
_antigravity_version_checked = 0
|
||||||
|
_new_ver = _ensure_antigravity_version()
|
||||||
|
if _new_ver != _fetched_ver:
|
||||||
|
print(f"[{self._session_id}] [antigravity-v2] version changed {_fetched_ver} -> {_new_ver}, retrying", file=sys.stderr)
|
||||||
|
_ag_ua_new = f"antigravity/{_new_ver} {_os_name}/{_os_arch}"
|
||||||
|
headers["User-Agent"] = _ag_ua_new
|
||||||
|
wrapped["userAgent"] = _ag_ua_new
|
||||||
body_b = json.dumps(wrapped).encode()
|
body_b = json.dumps(wrapped).encode()
|
||||||
for ep in ordered:
|
for ep in ordered:
|
||||||
action = "streamGenerateContent" if stream else "generateContent"
|
action = "streamGenerateContent" if stream else "generateContent"
|
||||||
@@ -5759,17 +5851,13 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
|||||||
break
|
break
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
err_body = e.read().decode()
|
err_body = e.read().decode()
|
||||||
err_class = _classify_antigravity_error(e.code, err_body)
|
print(f"[{self._session_id}] [antigravity-v2-retry] {ep.replace('https://','')} {e.code}", file=sys.stderr)
|
||||||
print(f"[{self._session_id}] [antigravity-v2-fallback] {ep.replace('https://','')} {e.code} class={err_class}", file=sys.stderr)
|
|
||||||
if e.code == 400:
|
if e.code == 400:
|
||||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
||||||
if err_class in ("auth_permanent", "service_disabled", "forbidden", "account_banned", "validation_required"):
|
|
||||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
|
||||||
if ep == ordered[-1]:
|
if ep == ordered[-1]:
|
||||||
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}})
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[{self._session_id}] [antigravity-v2-fallback] {ep.replace('https://','')} conn failed: {e}", file=sys.stderr)
|
|
||||||
if ep == ordered[-1]:
|
if ep == ordered[-1]:
|
||||||
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
|
return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}})
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user