From 82050d1103c732e385f724c75480ec78e4680839 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 25 May 2026 13:51:51 +0400 Subject: [PATCH] =?UTF-8?q?v3.10.4=20=E2=80=94=20Fix=20OAuth=20Secrets=20d?= =?UTF-8?q?ialog:=20use=20correct=20error=20dialog=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/codex-launcher-gui | 71 ++++++++++++++++++++++++++++++++++++++++-- src/translate-proxy.py | 10 +++--- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/codex-launcher-gui b/src/codex-launcher-gui index e0b8292..dfe59b4 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -26,6 +26,10 @@ model_catalog_json = "" """ CHANGELOG = [ + ("3.10.4", "2026-05-25", [ + "OAuth Secrets editor in GUI — update client ID/secret without editing files", + "Secrets stored in ~/.config/codex-launcher/oauth-secrets.json (not in repo)", + ]), ("3.10.3", "2026-05-25", [ "Fix Antigravity 404: map display names to verified REST API model IDs", "REST API uses slugs (gemini-3-flash) not display names (Gemini 3.5 Flash)", @@ -1776,7 +1780,7 @@ class LauncherWin(Gtk.Window): # header row hdr = Gtk.Box(spacing=8) vbox.pack_start(hdr, False, False, 0) - lbl = Gtk.Label(label="Codex Launcher v3.10.3") + lbl = Gtk.Label(label="Codex Launcher v3.10.4") lbl.set_use_markup(True) hdr.pack_start(lbl, False, False, 0) changelog_btn = Gtk.Button(label="Changelog") @@ -1800,6 +1804,9 @@ class LauncherWin(Gtk.Window): mgr_btn = Gtk.Button(label="Manage Endpoints") mgr_btn.connect("clicked", lambda b: self._open_mgr()) hdr.pack_end(mgr_btn, False, False, 0) + oauth_btn = Gtk.Button(label="OAuth Secrets") + oauth_btn.connect("clicked", lambda b: self._edit_oauth_secrets()) + hdr.pack_end(oauth_btn, False, False, 0) # verification status bar self._cli_info = _detect_codex_cli() @@ -3260,6 +3267,64 @@ class EditEndpointDialog(Gtk.Dialog): else: self._lbl_reasoning.set_markup('OFF') + def _edit_oauth_secrets(self): + secrets_path = os.path.expanduser("~/.config/codex-launcher/oauth-secrets.json") + try: + with open(secrets_path) as f: + data = json.load(f) + except Exception: + data = {"antigravity": {"client_id": "", "client_secret": ""}, + "gemini_cli": {"client_id": "", "client_secret": ""}} + + dlg = Gtk.Dialog(title="OAuth 2.0 Client Secrets", parent=self, modal=True) + dlg.add_button("Cancel", Gtk.ResponseType.CANCEL) + dlg.add_button("Save", Gtk.ResponseType.OK) + dlg.set_default_size(520, 340) + area = dlg.get_content_area() + area.set_margin_start(16) + area.set_margin_end(16) + area.set_margin_top(12) + area.set_margin_bottom(12) + area.set_spacing(6) + + area.pack_start(Gtk.Label(label="Google OAuth 2.0 credentials\nStored locally in ~/.config/codex-launcher/oauth-secrets.json", use_markup=True, xalign=0), False, False, 4) + + fields = {} + for section_key, section_label in [("antigravity", "Antigravity (CloudCode)"), ("gemini_cli", "Gemini CLI")]: + area.pack_start(Gtk.Label(label=f"\n{section_label}", use_markup=True, xalign=0), False, False, 2) + sec = data.get(section_key, {}) + for fk, fl in [("client_id", "Client ID"), ("client_secret", "Client Secret")]: + row = Gtk.Box(spacing=6) + lbl = Gtk.Label(label=fl + ":", xalign=0) + lbl.set_size_request(100, -1) + entry = Gtk.Entry() + entry.set_text(sec.get(fk, "")) + entry.set_size_request(380, -1) + if fk == "client_secret": + entry.set_visibility(False) + entry.set_invisible_char("*") + row.pack_start(lbl, False, False, 0) + row.pack_start(entry, True, True, 0) + area.pack_start(row, False, False, 2) + fields[(section_key, fk)] = entry + + area.pack_start(Gtk.Label(label="\nGet credentials from console.cloud.google.com → APIs & Services → Credentials", use_markup=True, xalign=0), False, False, 4) + area.show_all() + + if dlg.run() == Gtk.ResponseType.OK: + for (sk, fk), entry in fields.items(): + if sk not in data: + data[sk] = {} + data[sk][fk] = entry.get_text().strip() + try: + os.makedirs(os.path.dirname(secrets_path), exist_ok=True) + with open(secrets_path, "w") as f: + json.dump(data, f, indent=2) + os.chmod(secrets_path, 0o600) + except Exception as e: + self._show_error_dialog("Save failed", str(e)) + dlg.destroy() + def _do_oauth_login(self): preset_name = self._combo_preset.get_active_text() or "Custom" preset = PROVIDER_PRESETS.get(preset_name, {}) @@ -3589,7 +3654,7 @@ class EditEndpointDialog(Gtk.Dialog): auth_url = "https://codebuff.com/api/auth/cli/code" body = json.dumps({"fingerprintId": fingerprint_id}).encode() req = urllib.request.Request(auth_url, data=body, - headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.3"}) + headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.4"}) resp = urllib.request.urlopen(req, timeout=30) data = json.loads(resp.read()) login_url = data.get("loginUrl", "") or data.get("login_url", "") @@ -3614,7 +3679,7 @@ class EditEndpointDialog(Gtk.Dialog): time.sleep(2) try: poll_req = urllib.request.Request(poll_url, - headers={"User-Agent": "codex-launcher/3.10.3"}) + headers={"User-Agent": "codex-launcher/3.10.4"}) poll_resp = urllib.request.urlopen(poll_req, timeout=10) poll_data = json.loads(poll_resp.read()) user = poll_data.get("user") diff --git a/src/translate-proxy.py b/src/translate-proxy.py index ab37c02..ec5d06d 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -335,7 +335,7 @@ def _codebuff_get_session(token, model): req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}", - "User-Agent": "codex-launcher/3.10.3", + "User-Agent": "codex-launcher/3.10.4", "x-codebuff-model": model, }) try: @@ -383,7 +383,7 @@ def _codebuff_start_run(token, agent_id): req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}", - "User-Agent": "codex-launcher/3.10.3", + "User-Agent": "codex-launcher/3.10.4", }) try: resp = urllib.request.urlopen(req, timeout=15) @@ -416,7 +416,7 @@ def _codebuff_finish_run(token, run_id, status="completed"): req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {token}", - "User-Agent": "codex-launcher/3.10.3", + "User-Agent": "codex-launcher/3.10.4", }) try: urllib.request.urlopen(req, timeout=10) @@ -5342,7 +5342,7 @@ class Handler(http.server.BaseHTTPRequestHandler): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}", - "User-Agent": "codex-launcher/3.10.3", + "User-Agent": "codex-launcher/3.10.4", "x-codebuff-model": model, } if instance_id: @@ -5508,7 +5508,7 @@ class Handler(http.server.BaseHTTPRequestHandler): if body.get("tool_choice"): chat_body["tool_choice"] = body["tool_choice"] target = f"{_CODEBUFF_API_URL}/api/v1/chat/completions" - headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.10.3", "x-codebuff-model": model} + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.10.4", "x-codebuff-model": model} if instance_id: headers["x-codebuff-instance-id"] = instance_id print(f"[codebuff] retry POST {target} model={model} stream={stream} run={run_id} (thinking disabled via DeepSeek native)", file=sys.stderr)