v2.1.0: add Codex auth status detection and re-login
This commit is contained in:
@@ -24,6 +24,12 @@ model_catalog_json = ""
|
||||
"""
|
||||
|
||||
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", [
|
||||
"Added Codex CLI/Desktop installation verifier to main page",
|
||||
"Disables Desktop/CLI launch buttons when corresponding tool is missing",
|
||||
@@ -450,6 +456,25 @@ def _detect_codex_desktop():
|
||||
return str(START_SH)
|
||||
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
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@@ -469,7 +494,7 @@ class LauncherWin(Gtk.Window):
|
||||
# header row
|
||||
hdr = Gtk.Box(spacing=8)
|
||||
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)
|
||||
hdr.pack_start(lbl, False, False, 0)
|
||||
changelog_btn = Gtk.Button(label="Changelog")
|
||||
@@ -522,6 +547,19 @@ class LauncherWin(Gtk.Window):
|
||||
if not self._desktop_info:
|
||||
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)
|
||||
vbox.pack_start(ops_box, False, False, 0)
|
||||
self._refresh_all_btn = Gtk.Button(label="Refresh Models")
|
||||
@@ -634,6 +672,50 @@ class LauncherWin(Gtk.Window):
|
||||
else:
|
||||
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 _update():
|
||||
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()
|
||||
|
||||
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.log(f"=== Codex Default (OAuth) → {'Desktop' if target == 'desktop' else 'CLI'} ===")
|
||||
threading.Thread(target=self._run_codex_default, args=(target,), daemon=True).start()
|
||||
|
||||
Reference in New Issue
Block a user