Merge pull request #2 from roman-ryzenadvanced/cleanup/remove-windows-folder
chore: merge windows/ into src/ and remove duplicate folder
This commit is contained in:
@@ -30,7 +30,8 @@ from codex_launcher_lib import (
|
|||||||
IS_WINDOWS, HOME, CONFIG, CONFIG_BAK, CONFIG_TXN,
|
IS_WINDOWS, HOME, CONFIG, CONFIG_BAK, CONFIG_TXN,
|
||||||
ENDPOINTS_FILE, BGP_POOLS_FILE, LAUNCH_LOG, LOG_DIR,
|
ENDPOINTS_FILE, BGP_POOLS_FILE, LAUNCH_LOG, LOG_DIR,
|
||||||
PROXY_CONFIG_DIR, BIN_DIR, PROXY, CLEANUP, PID_REGISTRY,
|
PROXY_CONFIG_DIR, BIN_DIR, PROXY, CLEANUP, PID_REGISTRY,
|
||||||
PROVIDER_PRESETS, CHANGELOG, DEFAULT_CONFIG,
|
PROVIDER_PRESETS, CHANGELOG, DEFAULT_CONFIG, OAUTH_SECRETS_PATH,
|
||||||
|
ANTIGRAVITY_MODELS,
|
||||||
safe_name, label_for_backend, normalize_model_id, normalize_base_url,
|
safe_name, label_for_backend, normalize_model_id, normalize_base_url,
|
||||||
parse_model_list, now_utc_iso, apply_provider_preset,
|
parse_model_list, now_utc_iso, apply_provider_preset,
|
||||||
load_endpoints, save_endpoints, load_bgp_pools, save_bgp_pools,
|
load_endpoints, save_endpoints, load_bgp_pools, save_bgp_pools,
|
||||||
@@ -48,6 +49,7 @@ from codex_launcher_lib import (
|
|||||||
load_incident_store, save_incident_store, load_usage_stats,
|
load_incident_store, save_incident_store, load_usage_stats,
|
||||||
monitoring_log,
|
monitoring_log,
|
||||||
IncidentStore, AIDiagnosticAgent, HealthWatcher,
|
IncidentStore, AIDiagnosticAgent, HealthWatcher,
|
||||||
|
load_oauth_secrets, save_oauth_secrets,
|
||||||
_usage_theme, UA,
|
_usage_theme, UA,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -378,9 +380,11 @@ class EditEndpointDialog:
|
|||||||
is_antigravity = oauth_provider == "google-antigravity"
|
is_antigravity = oauth_provider == "google-antigravity"
|
||||||
token_path = str(PROXY_CONFIG_DIR / ("google-antigravity-oauth-token.json" if is_antigravity else "google-cli-oauth-token.json"))
|
token_path = str(PROXY_CONFIG_DIR / ("google-antigravity-oauth-token.json" if is_antigravity else "google-cli-oauth-token.json"))
|
||||||
|
|
||||||
|
_sec = load_oauth_secrets().get("antigravity" if is_antigravity else "gemini_cli", {})
|
||||||
|
CLIENT_ID = _sec.get("client_id", "")
|
||||||
|
CLIENT_SECRET = _sec.get("client_secret", "")
|
||||||
|
|
||||||
if is_antigravity:
|
if is_antigravity:
|
||||||
CLIENT_ID = ""
|
|
||||||
CLIENT_SECRET = ""
|
|
||||||
SCOPES = [
|
SCOPES = [
|
||||||
"https://www.googleapis.com/auth/cloud-platform",
|
"https://www.googleapis.com/auth/cloud-platform",
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
"https://www.googleapis.com/auth/userinfo.email",
|
||||||
@@ -393,8 +397,6 @@ class EditEndpointDialog:
|
|||||||
callback_path = "/oauth-callback"
|
callback_path = "/oauth-callback"
|
||||||
provider_kind = "antigravity"
|
provider_kind = "antigravity"
|
||||||
else:
|
else:
|
||||||
CLIENT_ID = ""
|
|
||||||
CLIENT_SECRET = ""
|
|
||||||
SCOPES = [
|
SCOPES = [
|
||||||
"https://www.googleapis.com/auth/cloud-platform",
|
"https://www.googleapis.com/auth/cloud-platform",
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
"https://www.googleapis.com/auth/userinfo.email",
|
||||||
@@ -436,7 +438,7 @@ class EditEndpointDialog:
|
|||||||
oauth_dlg.grab_set()
|
oauth_dlg.grab_set()
|
||||||
|
|
||||||
tk.Label(oauth_dlg, text="Sign in with Google", font=("Segoe UI", 11, "bold")).pack(padx=16, pady=(12, 0), anchor="w")
|
tk.Label(oauth_dlg, text="Sign in with Google", font=("Segoe UI", 11, "bold")).pack(padx=16, pady=(12, 0), anchor="w")
|
||||||
tk.Label(oauth_dlg, text="Emulating Gemini CLI OAuth -- no client_secret.json needed.").pack(padx=16, anchor="w")
|
tk.Label(oauth_dlg, text=f"Using OAuth credentials from {OAUTH_SECRETS_PATH}").pack(padx=16, anchor="w")
|
||||||
|
|
||||||
link_lbl = tk.Label(oauth_dlg, text="Click here to open Google authorization", fg="blue", cursor="hand2")
|
link_lbl = tk.Label(oauth_dlg, text="Click here to open Google authorization", fg="blue", cursor="hand2")
|
||||||
link_lbl.pack(padx=16, pady=(8, 0), anchor="w")
|
link_lbl.pack(padx=16, pady=(8, 0), anchor="w")
|
||||||
@@ -535,12 +537,7 @@ class EditEndpointDialog:
|
|||||||
|
|
||||||
found_models = []
|
found_models = []
|
||||||
if is_antigravity:
|
if is_antigravity:
|
||||||
found_models = [
|
found_models = list(ANTIGRAVITY_MODELS)
|
||||||
"gemini-2.5-flash", "gemini-2.5-pro",
|
|
||||||
"gemini-3-flash-preview", "gemini-3-pro-preview", "gemini-3.1-pro-preview",
|
|
||||||
"antigravity-gemini-3-flash", "antigravity-gemini-3-pro",
|
|
||||||
"antigravity-claude-sonnet-4-6", "antigravity-claude-opus-4-6-thinking",
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
found_models = ["gemini-2.5-flash", "gemini-2.5-pro"]
|
found_models = ["gemini-2.5-flash", "gemini-2.5-pro"]
|
||||||
if found_models:
|
if found_models:
|
||||||
@@ -1944,21 +1941,20 @@ class BenchmarkWindow:
|
|||||||
class LauncherWin:
|
class LauncherWin:
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self._root = root
|
self._root = root
|
||||||
self._root.title("Codex Launcher")
|
|
||||||
self._root.geometry("580x520")
|
|
||||||
self._root.minsize(480, 400)
|
|
||||||
self._proc = None
|
self._proc = None
|
||||||
self._endpoints_data = load_endpoints()
|
self._endpoints_data = load_endpoints()
|
||||||
self._refresh_running = False
|
self._refresh_running = False
|
||||||
recover_config_if_needed()
|
recover_config_if_needed()
|
||||||
|
|
||||||
main = ttk.Frame(root, padding=12)
|
main = ttk.Frame(root, padding=16)
|
||||||
main.pack(fill="both", expand=True)
|
main.pack(fill="both", expand=True)
|
||||||
|
main.pack_propagate(False)
|
||||||
|
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
hdr = ttk.Frame(main)
|
hdr = ttk.Frame(main)
|
||||||
hdr.pack(fill="x")
|
hdr.pack(fill="x")
|
||||||
ttk.Label(hdr, text="Codex Launcher v3.8.1", font=("Segoe UI", 13, "bold")).pack(side="left")
|
ttk.Label(hdr, text=f"Codex Launcher v{CHANGELOG[0][0]}", font=("Segoe UI", 13, "bold")).pack(side="left")
|
||||||
|
|
||||||
# Toolbar — two rows to fit all buttons
|
# Toolbar — two rows to fit all buttons
|
||||||
tb1 = ttk.Frame(main)
|
tb1 = ttk.Frame(main)
|
||||||
@@ -1969,6 +1965,7 @@ class LauncherWin:
|
|||||||
ttk.Button(tb1, text="Usage", command=self._open_usage).pack(side="left", padx=(6, 0))
|
ttk.Button(tb1, text="Usage", command=self._open_usage).pack(side="left", padx=(6, 0))
|
||||||
ttk.Button(tb1, text="Benchmark", command=self._open_benchmark).pack(side="left", padx=(6, 0))
|
ttk.Button(tb1, text="Benchmark", command=self._open_benchmark).pack(side="left", padx=(6, 0))
|
||||||
ttk.Button(tb1, text="History", command=self._open_history).pack(side="left", padx=(6, 0))
|
ttk.Button(tb1, text="History", command=self._open_history).pack(side="left", padx=(6, 0))
|
||||||
|
ttk.Button(tb1, text="OAuth Secrets", command=self._edit_oauth_secrets).pack(side="left", padx=(6, 0))
|
||||||
ttk.Button(tb1, text="Changelog", command=self._show_changelog).pack(side="right")
|
ttk.Button(tb1, text="Changelog", command=self._show_changelog).pack(side="right")
|
||||||
|
|
||||||
# Detection status — one row per item so long paths don't truncate
|
# Detection status — one row per item so long paths don't truncate
|
||||||
@@ -2054,7 +2051,7 @@ class LauncherWin:
|
|||||||
self._btn_codex_cli.pack(side="left", fill="x", expand=True)
|
self._btn_codex_cli.pack(side="left", fill="x", expand=True)
|
||||||
|
|
||||||
# Log area
|
# Log area
|
||||||
self._log_text = scrolledtext.ScrolledText(main, height=8, state="disabled", wrap="word",
|
self._log_text = scrolledtext.ScrolledText(main, height=10, state="disabled", wrap="word",
|
||||||
font=("Consolas", 9))
|
font=("Consolas", 9))
|
||||||
self._log_text.pack(fill="both", expand=True, pady=(8, 0))
|
self._log_text.pack(fill="both", expand=True, pady=(8, 0))
|
||||||
|
|
||||||
@@ -2199,6 +2196,92 @@ class LauncherWin:
|
|||||||
if Path(assist_path).exists():
|
if Path(assist_path).exists():
|
||||||
subprocess.Popen([sys.executable, assist_path], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if IS_WINDOWS else 0)
|
subprocess.Popen([sys.executable, assist_path], creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if IS_WINDOWS else 0)
|
||||||
|
|
||||||
|
def _edit_oauth_secrets(self):
|
||||||
|
data = load_oauth_secrets()
|
||||||
|
if not data:
|
||||||
|
data = {"antigravity": {"client_id": "", "client_secret": ""},
|
||||||
|
"gemini_cli": {"client_id": "", "client_secret": ""}}
|
||||||
|
|
||||||
|
dlg = tk.Toplevel(self._root)
|
||||||
|
dlg.title("OAuth 2.0 Client Secrets")
|
||||||
|
dlg.geometry("600x450")
|
||||||
|
dlg.transient(self._root)
|
||||||
|
dlg.grab_set()
|
||||||
|
|
||||||
|
frame = ttk.Frame(dlg, padding=16)
|
||||||
|
frame.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
ttk.Label(frame, text="Google OAuth 2.0 credentials", font=("Segoe UI", 10, "bold")).pack(anchor="w")
|
||||||
|
ttk.Label(frame, text=f"Stored locally in {OAUTH_SECRETS_PATH}", foreground="gray").pack(anchor="w", pady=(0, 8))
|
||||||
|
|
||||||
|
fields = {}
|
||||||
|
nf = ttk.Frame(frame)
|
||||||
|
nf.pack(fill="x")
|
||||||
|
row = 0
|
||||||
|
for section_key, section_label in [("antigravity", "Antigravity (CloudCode)"), ("gemini_cli", "Gemini CLI")]:
|
||||||
|
ttk.Label(nf, text=f"\n{section_label}", font=("Segoe UI", 9, "bold")).grid(row=row, column=0, columnspan=3, sticky="w", pady=(8, 2))
|
||||||
|
row += 1
|
||||||
|
sec = data.get(section_key, {})
|
||||||
|
import_btn = ttk.Button(nf, text="Import JSON",
|
||||||
|
command=lambda sk=section_key: self._import_oauth_json(fields, sk))
|
||||||
|
import_btn.grid(row=row, column=2, padx=(4, 0), pady=2, sticky="e")
|
||||||
|
for fk, fl in [("client_id", "Client ID"), ("client_secret", "Client Secret")]:
|
||||||
|
ttk.Label(nf, text=fl + ":").grid(row=row, column=0, sticky="w", padx=(8, 4), pady=2)
|
||||||
|
entry = ttk.Entry(nf, width=60)
|
||||||
|
entry.insert(0, sec.get(fk, ""))
|
||||||
|
entry.grid(row=row, column=1, sticky="ew", pady=2)
|
||||||
|
if fk == "client_secret":
|
||||||
|
entry.configure(show="*")
|
||||||
|
fields[(section_key, fk)] = entry
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
nf.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
ttk.Label(frame, text="\nImport a client_secret_*.json from Google Cloud Console\nconsole.cloud.google.com → Credentials", foreground="gray").pack(anchor="w")
|
||||||
|
|
||||||
|
btnf = ttk.Frame(frame)
|
||||||
|
btnf.pack(fill="x", pady=(12, 0))
|
||||||
|
ttk.Button(btnf, text="Cancel", command=dlg.destroy).pack(side="right", padx=(4, 0))
|
||||||
|
save_btn = ttk.Button(btnf, text="Save")
|
||||||
|
save_btn.pack(side="right", padx=(4, 0))
|
||||||
|
|
||||||
|
def _save():
|
||||||
|
for (sk, fk), entry in fields.items():
|
||||||
|
if sk not in data:
|
||||||
|
data[sk] = {}
|
||||||
|
data[sk][fk] = entry.get().strip()
|
||||||
|
try:
|
||||||
|
save_oauth_secrets(data)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Save failed", str(e), parent=dlg)
|
||||||
|
return
|
||||||
|
dlg.destroy()
|
||||||
|
|
||||||
|
save_btn.configure(command=_save)
|
||||||
|
|
||||||
|
def _import_oauth_json(self, fields, section_key):
|
||||||
|
path = filedialog.askopenfilename(
|
||||||
|
title="Import Google OAuth Client Secret JSON",
|
||||||
|
filetypes=[("JSON files", "*.json")])
|
||||||
|
if not path:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
raw = json.load(f)
|
||||||
|
creds = raw.get("installed") or raw.get("web") or raw
|
||||||
|
cid = creds.get("client_id", "")
|
||||||
|
csec = creds.get("client_secret", "")
|
||||||
|
if not cid or not csec:
|
||||||
|
raise ValueError("JSON does not contain client_id and client_secret")
|
||||||
|
if (section_key, "client_id") in fields:
|
||||||
|
fields[(section_key, "client_id")].delete(0, "end")
|
||||||
|
fields[(section_key, "client_id")].insert(0, cid)
|
||||||
|
if (section_key, "client_secret") in fields:
|
||||||
|
fields[(section_key, "client_secret")].delete(0, "end")
|
||||||
|
fields[(section_key, "client_secret")].insert(0, csec)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Import failed", str(e))
|
||||||
|
|
||||||
# ── Watcher ──────────────────────────────────────────────────────
|
# ── Watcher ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _start_watcher(self):
|
def _start_watcher(self):
|
||||||
@@ -2696,5 +2779,8 @@ if __name__ == "__main__":
|
|||||||
create_default_endpoints()
|
create_default_endpoints()
|
||||||
|
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
|
root.title("Codex Launcher")
|
||||||
|
root.geometry("800x680")
|
||||||
|
root.minsize(640, 520)
|
||||||
app = LauncherWin(root)
|
app = LauncherWin(root)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ CONFIG_TXN = CONFIG_DIR / "config.toml.launcher-txn.json"
|
|||||||
ENDPOINTS_FILE = CONFIG_DIR / "endpoints.json"
|
ENDPOINTS_FILE = CONFIG_DIR / "endpoints.json"
|
||||||
BGP_POOLS_FILE = CONFIG_DIR / "bgp-pools.json"
|
BGP_POOLS_FILE = CONFIG_DIR / "bgp-pools.json"
|
||||||
LAUNCH_LOG = LOG_DIR / "launcher.log"
|
LAUNCH_LOG = LOG_DIR / "launcher.log"
|
||||||
|
OAUTH_SECRETS_PATH = HOME / ".config" / "codex-launcher" / "oauth-secrets.json"
|
||||||
|
|
||||||
if IS_WINDOWS:
|
if IS_WINDOWS:
|
||||||
PROXY = BIN_DIR / "translate-proxy.py"
|
PROXY = BIN_DIR / "translate-proxy.py"
|
||||||
@@ -82,6 +83,69 @@ model_catalog_json = ""
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
("3.10.5", "2026-05-25", [
|
||||||
|
"Context compaction for Antigravity/Gemini OAuth — prevents token limit errors",
|
||||||
|
"Aggressive compaction policies at 60% of model context limit",
|
||||||
|
"Compaction for cloudcode-pa and googleapis provider policies",
|
||||||
|
"REST model IDs added to context size map (gemini-3-flash, etc.)",
|
||||||
|
"OAuth Secrets editor in GUI — update client ID/secret without editing files",
|
||||||
|
"Secrets stored in ~/.config/codex-launcher/oauth-secrets.json (not in repo)",
|
||||||
|
"Import JSON button — import client_secret_*.json from Google Cloud Console",
|
||||||
|
"All hardcoded OAuth secrets removed from source code",
|
||||||
|
"Antigravity model IDs fixed: display names → slug model IDs for REST API",
|
||||||
|
"Git history scrubbed of leaked credentials; pre-push hook installed",
|
||||||
|
"Antigravity REST API model IDs verified with live API testing",
|
||||||
|
"Gemini 3.5 Flash, 3.1 Pro, Claude 4.6, GPT-OSS 120B all working",
|
||||||
|
]),
|
||||||
|
("3.9.9", "2026-05-25", [
|
||||||
|
"Refresh Antigravity preset: Gemini 3.5 Flash, Gemini 3.1 Pro, Claude 4.6, GPT-OSS",
|
||||||
|
"Fix Antigravity alias map for tiered model IDs (high/medium/low/thinking)",
|
||||||
|
"Model context sizes for Gemini 3.5 Flash, 3.1 Pro, Claude 4.6, GPT-OSS 120B",
|
||||||
|
]),
|
||||||
|
("3.9.8", "2026-05-25", [
|
||||||
|
"Fix Desktop model leak — remap gpt-5.4-mini to user-selected model",
|
||||||
|
"send_json() catches BrokenPipeError globally — no crashes on disconnect",
|
||||||
|
"Proxy remaps Desktop forced models via CODEX_LAUNCHER_MODEL env",
|
||||||
|
]),
|
||||||
|
("3.9.7", "2026-05-25", [
|
||||||
|
"Forward real Codebuff error messages instead of generic 429",
|
||||||
|
"Return HTTP 200 with Responses API format for rate limits",
|
||||||
|
"Extract retryAfterMs from Codebuff 429 responses for cooldown",
|
||||||
|
"RateLimitError carries upstream message through all paths",
|
||||||
|
"BrokenPipeError fix on 'all accounts exhausted' response",
|
||||||
|
"Fix 3 SyntaxWarnings for invalid escape sequences",
|
||||||
|
"_codebuff_start_run returns actual error body",
|
||||||
|
]),
|
||||||
|
("3.9.6", "2026-05-25", [
|
||||||
|
"Fix Gemini follow-up turns: enforce latest user instruction as final turn",
|
||||||
|
"Edit-intent detection with tool-use nudge for file modifications",
|
||||||
|
"Thought signature preservation for Gemini 3 tool-call continuity",
|
||||||
|
"Smart tool output compaction: old=3000, recent=20000 chars",
|
||||||
|
"Multi-account rotation for codebuff, Google OAuth, API keys",
|
||||||
|
"/v1/accounts endpoint for account pool status",
|
||||||
|
]),
|
||||||
|
("3.9.0", "2026-05-24", [
|
||||||
|
"Multi-account rotation for OAuth providers (codebuff, Google, API keys)",
|
||||||
|
"Automatic failover on rate limit — next account used",
|
||||||
|
"Codebuff: accounts[] array in credentials.json",
|
||||||
|
"Google OAuth: multiple token files (google-*-oauth-token-N.json)",
|
||||||
|
"API keys: comma-separated keys rotate on 429 errors",
|
||||||
|
"/v1/accounts endpoint shows account pool status",
|
||||||
|
"x-codebuff-model and x-codebuff-instance-id headers",
|
||||||
|
]),
|
||||||
|
("3.8.4", "2026-05-24", [
|
||||||
|
"Codebuff streaming — SSE events reach Codex client",
|
||||||
|
"stream_buffered_events now called for codebuff",
|
||||||
|
"Codebuff OAuth built-in login flow (no external CLI)",
|
||||||
|
"Codebuff API: reverse-engineered www.codebuff.com endpoints",
|
||||||
|
"Codebuff session management with instance ID",
|
||||||
|
"Codebuff agent run lifecycle (start/finish) with model routing",
|
||||||
|
"Free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7",
|
||||||
|
"Reasoning mode works with codebuff (thinking tokens supported)",
|
||||||
|
"GUI: Sandbox mode selector (Read-only / Workspace / Full Access)",
|
||||||
|
"GUI: Approval mode selector (Untrusted / On Request / Full Auto)",
|
||||||
|
"GUI: Codebuff Login button in endpoint editor",
|
||||||
|
]),
|
||||||
("3.8.1", "2026-05-24", [
|
("3.8.1", "2026-05-24", [
|
||||||
"Freebuff integration — free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7",
|
"Freebuff integration — free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7",
|
||||||
"Freebuff backend: auto agent-run lifecycle, credential detection, model routing",
|
"Freebuff backend: auto agent-run lifecycle, credential detection, model routing",
|
||||||
@@ -974,6 +1038,9 @@ def endpoint_model_headers(endpoint):
|
|||||||
|
|
||||||
|
|
||||||
def fetch_models_for_endpoint(endpoint, timeout=10):
|
def fetch_models_for_endpoint(endpoint, timeout=10):
|
||||||
|
bt = endpoint.get("backend_type", "")
|
||||||
|
if bt == "gemini-oauth-antigravity":
|
||||||
|
return list(ANTIGRAVITY_MODELS), None
|
||||||
url = endpoint_models_url(endpoint)
|
url = endpoint_models_url(endpoint)
|
||||||
if not url:
|
if not url:
|
||||||
return None, "Base URL is empty"
|
return None, "Base URL is empty"
|
||||||
@@ -1006,6 +1073,40 @@ def refresh_endpoint_models(endpoint):
|
|||||||
updated["default_model"] = ids[0]
|
updated["default_model"] = ids[0]
|
||||||
return updated, None
|
return updated, None
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
# Antigravity model list (static — no /v1/models REST endpoint)
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
ANTIGRAVITY_MODELS = [
|
||||||
|
"Gemini 3.5 Flash (High)", "Gemini 3.5 Flash (Medium)", "Gemini 3.5 Flash (Low)",
|
||||||
|
"Gemini 3.1 Pro (High)", "Gemini 3.1 Pro (Low)",
|
||||||
|
"Claude Sonnet 4.6 (Thinking)",
|
||||||
|
"Claude Opus 4.6 (Thinking)",
|
||||||
|
"GPT-OSS 120B (Medium)",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
# OAuth secrets (local, never in repo)
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
def load_oauth_secrets():
|
||||||
|
try:
|
||||||
|
with open(OAUTH_SECRETS_PATH, encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def save_oauth_secrets(data):
|
||||||
|
os.makedirs(os.path.dirname(OAUTH_SECRETS_PATH), exist_ok=True)
|
||||||
|
tmp = str(OAUTH_SECRETS_PATH) + ".tmp"
|
||||||
|
with open(tmp, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
os.replace(tmp, OAUTH_SECRETS_PATH)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
# Doctor checks
|
# Doctor checks
|
||||||
# ═══════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user