v2.1.0: add Codex auth status detection and re-login

This commit is contained in:
admin
2026-05-19 16:45:41 +04:00
Unverified
parent ac24fe6372
commit 772255a188
4 changed files with 106 additions and 1 deletions

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## v2.1.0 (2026-05-19)
- Added Codex auth status detection (reads `codex login status`)
- Auth status bar shows logged-in provider or warning if auth missing/expired
- "Re-login" button opens `codex login` in a terminal for re-authentication
- Auto re-checks auth 30s after re-login flow starts
- Pre-launch auth check warns before launching Codex Default mode if auth is invalid
- Auth status checked asynchronously at startup (non-blocking)
## v2.0.1 (2026-05-19) ## v2.0.1 (2026-05-19)
- Added Codex CLI/Desktop installation verifier to main page - Added Codex CLI/Desktop installation verifier to main page

Binary file not shown.

Binary file not shown.

View File

@@ -24,6 +24,12 @@ model_catalog_json = ""
""" """
CHANGELOG = [ CHANGELOG = [
("2.1.0", "2026-05-19", [
"Added Codex auth status detection (codex login status)",
"Added Re-login button to re-authenticate via codex login",
"Auto-checks auth before launching Codex Default mode",
"Warns if OAuth token expired or missing before launch",
]),
("2.0.1", "2026-05-19", [ ("2.0.1", "2026-05-19", [
"Added Codex CLI/Desktop installation verifier to main page", "Added Codex CLI/Desktop installation verifier to main page",
"Disables Desktop/CLI launch buttons when corresponding tool is missing", "Disables Desktop/CLI launch buttons when corresponding tool is missing",
@@ -450,6 +456,25 @@ def _detect_codex_desktop():
return str(START_SH) return str(START_SH)
return None return None
def _check_codex_auth():
try:
out = subprocess.run(
["codex", "login", "status"],
capture_output=True, text=True, timeout=10,
)
text = (out.stdout or "").strip()
if not text:
text = (out.stderr or "").strip()
if out.returncode == 0 and text:
return ("logged_in", text)
if text:
return ("error", text)
return ("unknown", "No output from codex login status")
except FileNotFoundError:
return ("not_installed", "codex not found")
except Exception as e:
return ("error", str(e))
# ═══════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════
# Main window # Main window
# ═══════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════
@@ -469,7 +494,7 @@ class LauncherWin(Gtk.Window):
# header row # header row
hdr = Gtk.Box(spacing=8) hdr = Gtk.Box(spacing=8)
vbox.pack_start(hdr, False, False, 0) vbox.pack_start(hdr, False, False, 0)
lbl = Gtk.Label(label="<b>Codex Launcher v2.0.1</b>") lbl = Gtk.Label(label="<b>Codex Launcher v2.1.0</b>")
lbl.set_use_markup(True) lbl.set_use_markup(True)
hdr.pack_start(lbl, False, False, 0) hdr.pack_start(lbl, False, False, 0)
changelog_btn = Gtk.Button(label="Changelog") changelog_btn = Gtk.Button(label="Changelog")
@@ -522,6 +547,19 @@ class LauncherWin(Gtk.Window):
if not self._desktop_info: if not self._desktop_info:
self._missing.append("desktop") self._missing.append("desktop")
auth_box = Gtk.Box(spacing=12)
vbox.pack_start(auth_box, False, False, 0)
self._auth_label = Gtk.Label()
self._auth_label.set_markup("<span foreground='#888'>Checking auth…</span>")
self._auth_label.set_use_markup(True)
self._auth_label.set_ellipsize(3)
auth_box.pack_start(self._auth_label, False, False, 0)
self._relogin_btn = Gtk.Button(label="Re-login")
self._relogin_btn.set_sensitive(False)
self._relogin_btn.connect("clicked", lambda b: self._codex_relogin())
auth_box.pack_end(self._relogin_btn, False, False, 0)
threading.Thread(target=self._check_auth_async, daemon=True).start()
ops_box = Gtk.Box(spacing=8) ops_box = Gtk.Box(spacing=8)
vbox.pack_start(ops_box, False, False, 0) vbox.pack_start(ops_box, False, False, 0)
self._refresh_all_btn = Gtk.Button(label="Refresh Models") self._refresh_all_btn = Gtk.Button(label="Refresh Models")
@@ -634,6 +672,50 @@ class LauncherWin(Gtk.Window):
else: else:
self.log("All dependencies OK.") self.log("All dependencies OK.")
def _check_auth_async(self):
status, msg = _check_codex_auth()
GLib.idle_add(self._update_auth_status, status, msg)
def _update_auth_status(self, status, msg):
if status == "logged_in":
self._auth_label.set_markup(f"<span foreground='#2ea043'>✔ Auth: {msg}</span>")
self._relogin_btn.set_sensitive("cli" not in self._missing)
elif status == "not_installed":
self._auth_label.set_markup("<span foreground='#888'>Auth: N/A (CLI not installed)</span>")
else:
self._auth_label.set_markup(f"<span foreground='#d29922'>⚠ Auth: {msg}</span>")
self._relogin_btn.set_sensitive("cli" not in self._missing)
return False
def _codex_relogin(self):
self.log("Opening codex login in terminal…")
terms = [
("x-terminal-emulator", ["-e"]),
("kgx", ["--"]),
("gnome-terminal", ["--"]),
("konsole", ["-e"]),
("xterm", ["-e"]),
]
term = None
term_args = None
for t in terms:
if shutil.which(t[0]):
term = t[0]
term_args = t[1]
break
if not term:
self.log("ERROR: no terminal emulator found for re-login")
return
cmd_parts = [term] + term_args + ["codex", "login"]
subprocess.Popen(cmd_parts, preexec_fn=os.setsid)
self.log("Login flow started in terminal. Re-checking auth in 30s…")
self._auth_label.set_markup("<span foreground='#888'>Auth: waiting for login…</span>")
threading.Thread(target=self._delayed_auth_check, daemon=True).start()
def _delayed_auth_check(self):
time.sleep(30)
self._check_auth_async()
def _set_busy(self, busy): def _set_busy(self, busy):
def _update(): def _update():
has_cli = "cli" not in self._missing has_cli = "cli" not in self._missing
@@ -883,6 +965,20 @@ class LauncherWin(Gtk.Window):
threading.Thread(target=self._run, args=(ep, model, target), daemon=True).start() threading.Thread(target=self._run, args=(ep, model, target), daemon=True).start()
def _launch_codex_default(self, target): def _launch_codex_default(self, target):
if "cli" not in self._missing:
status, msg = _check_codex_auth()
if status != "logged_in":
d = Gtk.MessageDialog(
self, 0, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO,
f"Codex auth check: {msg}\n\n"
"Launch may fail without valid authentication.\n"
"Continue anyway?"
)
r = d.run()
d.destroy()
if r != Gtk.ResponseType.YES:
self._set_busy(False)
return
self._set_busy(True) self._set_busy(True)
self.log(f"=== Codex Default (OAuth) → {'Desktop' if target == 'desktop' else 'CLI'} ===") self.log(f"=== Codex Default (OAuth) → {'Desktop' if target == 'desktop' else 'CLI'} ===")
threading.Thread(target=self._run_codex_default, args=(target,), daemon=True).start() threading.Thread(target=self._run_codex_default, args=(target,), daemon=True).start()