v2.1.0: add Codex auth status detection and re-login
This commit is contained in:
@@ -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.
BIN
codex-launcher_2.1.0_all.deb
Normal file
BIN
codex-launcher_2.1.0_all.deb
Normal file
Binary file not shown.
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user