v2.0.1: Add Codex CLI/Desktop installation verifier
- Detects codex CLI (via 'which codex' + --version) and Desktop (via /opt/codex-desktop/start.sh) - Shows green ✔ or yellow ✘ status bar on main page - 'Install' button opens guide dialog with install commands - Desktop/CLI launch buttons disabled when corresponding tool is missing - Dependency status logged on startup - _set_busy respects missing-state (won't re-enable disabled buttons) - Rebuilt .deb as v2.0.1
This commit is contained in:
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.1 (2026-05-19)
|
||||
|
||||
- Added Codex CLI/Desktop installation verifier to main page
|
||||
- Green check (✔) when detected, yellow cross (✘) when missing
|
||||
- "Install" button next to missing tools opens install guide dialog
|
||||
- Desktop/CLI launch buttons disabled with tooltip when tool is missing
|
||||
- Dependency status logged on startup
|
||||
- Buttons respect missing-state after busy/unbusy cycles
|
||||
|
||||
## v2.0.0 (2026-05-19)
|
||||
|
||||
- Initial release: multi-provider Codex Launcher
|
||||
|
||||
Binary file not shown.
BIN
codex-launcher_2.0.1_all.deb
Normal file
BIN
codex-launcher_2.0.1_all.deb
Normal file
Binary file not shown.
@@ -24,6 +24,11 @@ model_catalog_json = ""
|
||||
"""
|
||||
|
||||
CHANGELOG = [
|
||||
("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",
|
||||
"Shows install instructions in status area on startup",
|
||||
]),
|
||||
("2.0.0", "2026-05-19", [
|
||||
"Initial release: multi-provider Codex Launcher",
|
||||
"Translation proxy: Responses API to Chat Completions + Anthropic Messages",
|
||||
@@ -429,6 +434,22 @@ def _last_log_lines(n=15):
|
||||
except Exception:
|
||||
return "(no log file)"
|
||||
|
||||
def _detect_codex_cli():
|
||||
try:
|
||||
path = shutil.which("codex")
|
||||
if not path:
|
||||
return None
|
||||
out = subprocess.run(["codex", "--version"], capture_output=True, text=True, timeout=5)
|
||||
ver = (out.stdout or "").strip() or (out.stderr or "").strip() or "unknown"
|
||||
return (path, ver)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _detect_codex_desktop():
|
||||
if START_SH.exists():
|
||||
return str(START_SH)
|
||||
return None
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Main window
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
@@ -448,7 +469,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.0</b>")
|
||||
lbl = Gtk.Label(label="<b>Codex Launcher v2.0.1</b>")
|
||||
lbl.set_use_markup(True)
|
||||
hdr.pack_start(lbl, False, False, 0)
|
||||
changelog_btn = Gtk.Button(label="Changelog")
|
||||
@@ -458,6 +479,49 @@ class LauncherWin(Gtk.Window):
|
||||
mgr_btn.connect("clicked", lambda b: self._open_mgr())
|
||||
hdr.pack_end(mgr_btn, False, False, 0)
|
||||
|
||||
# verification status bar
|
||||
self._cli_info = _detect_codex_cli()
|
||||
self._desktop_info = _detect_codex_desktop()
|
||||
ver_box = Gtk.Box(spacing=12)
|
||||
vbox.pack_start(ver_box, False, False, 0)
|
||||
|
||||
if self._cli_info:
|
||||
cli_path, cli_ver = self._cli_info
|
||||
cli_lbl = Gtk.Label()
|
||||
cli_lbl.set_markup(f"<span foreground='#2ea043'>✔ Codex CLI</span> <small>{cli_ver} ({cli_path})</small>")
|
||||
cli_lbl.set_use_markup(True)
|
||||
ver_box.pack_start(cli_lbl, False, False, 0)
|
||||
else:
|
||||
cli_lbl = Gtk.Label()
|
||||
cli_lbl.set_markup("<span foreground='#d29922'>✘ Codex CLI — not found</span>")
|
||||
cli_lbl.set_use_markup(True)
|
||||
ver_box.pack_start(cli_lbl, False, False, 0)
|
||||
cli_install_btn = Gtk.Button(label="Install")
|
||||
cli_install_btn.connect("clicked", lambda b: self._show_install_guide("cli"))
|
||||
ver_box.pack_start(cli_install_btn, False, False, 0)
|
||||
|
||||
ver_box.pack_start(Gtk.Label(label=" "), False, False, 0)
|
||||
|
||||
if self._desktop_info:
|
||||
desk_lbl = Gtk.Label()
|
||||
desk_lbl.set_markup(f"<span foreground='#2ea043'>✔ Codex Desktop</span> <small>({self._desktop_info})</small>")
|
||||
desk_lbl.set_use_markup(True)
|
||||
ver_box.pack_start(desk_lbl, False, False, 0)
|
||||
else:
|
||||
desk_lbl = Gtk.Label()
|
||||
desk_lbl.set_markup("<span foreground='#d29922'>✘ Codex Desktop — not found</span>")
|
||||
desk_lbl.set_use_markup(True)
|
||||
ver_box.pack_start(desk_lbl, False, False, 0)
|
||||
desk_install_btn = Gtk.Button(label="Install")
|
||||
desk_install_btn.connect("clicked", lambda b: self._show_install_guide("desktop"))
|
||||
ver_box.pack_start(desk_install_btn, False, False, 0)
|
||||
|
||||
self._missing = []
|
||||
if not self._cli_info:
|
||||
self._missing.append("cli")
|
||||
if not self._desktop_info:
|
||||
self._missing.append("desktop")
|
||||
|
||||
ops_box = Gtk.Box(spacing=8)
|
||||
vbox.pack_start(ops_box, False, False, 0)
|
||||
self._refresh_all_btn = Gtk.Button(label="Refresh Models")
|
||||
@@ -488,18 +552,30 @@ class LauncherWin(Gtk.Window):
|
||||
vbox.pack_start(btn_box, False, False, 8)
|
||||
self._btn_desktop = Gtk.Button(label="Launch Desktop")
|
||||
self._btn_desktop.connect("clicked", lambda b: self._launch("desktop"))
|
||||
if "desktop" in self._missing:
|
||||
self._btn_desktop.set_tooltip_text("Codex Desktop is not installed")
|
||||
self._btn_desktop.set_sensitive(False)
|
||||
btn_box.pack_start(self._btn_desktop, True, True, 0)
|
||||
self._btn_cli = Gtk.Button(label="Launch CLI")
|
||||
self._btn_cli.connect("clicked", lambda b: self._launch("cli"))
|
||||
if "cli" in self._missing:
|
||||
self._btn_cli.set_tooltip_text("Codex CLI is not installed")
|
||||
self._btn_cli.set_sensitive(False)
|
||||
btn_box.pack_start(self._btn_cli, True, True, 0)
|
||||
|
||||
btn_box2 = Gtk.Box(spacing=8, homogeneous=True)
|
||||
vbox.pack_start(btn_box2, False, False, 0)
|
||||
self._btn_codex_desktop = Gtk.Button(label="Codex Default (Desktop)")
|
||||
self._btn_codex_desktop.connect("clicked", lambda b: self._launch_codex_default("desktop"))
|
||||
if "desktop" in self._missing:
|
||||
self._btn_codex_desktop.set_tooltip_text("Codex Desktop is not installed")
|
||||
self._btn_codex_desktop.set_sensitive(False)
|
||||
btn_box2.pack_start(self._btn_codex_desktop, True, True, 0)
|
||||
self._btn_codex_cli = Gtk.Button(label="Codex Default (CLI)")
|
||||
self._btn_codex_cli.connect("clicked", lambda b: self._launch_codex_default("cli"))
|
||||
if "cli" in self._missing:
|
||||
self._btn_codex_cli.set_tooltip_text("Codex CLI is not installed")
|
||||
self._btn_codex_cli.set_sensitive(False)
|
||||
btn_box2.pack_start(self._btn_codex_cli, True, True, 0)
|
||||
|
||||
# status
|
||||
@@ -529,6 +605,7 @@ class LauncherWin(Gtk.Window):
|
||||
|
||||
self.show_all()
|
||||
self._rebuild_combo()
|
||||
self._log_dependency_status()
|
||||
|
||||
# ── helpers ──────────────────────────────────────────────────
|
||||
|
||||
@@ -542,14 +619,31 @@ class LauncherWin(Gtk.Window):
|
||||
self._tv.scroll_to_mark(m, 0.0, True, 0.0, 0.5)
|
||||
self._buf.delete_mark(m)
|
||||
|
||||
def _log_dependency_status(self):
|
||||
if self._cli_info:
|
||||
_, ver = self._cli_info
|
||||
self.log(f"✔ Codex CLI detected ({ver})")
|
||||
else:
|
||||
self.log("✘ Codex CLI NOT found — CLI launch disabled. Click 'Install' above.")
|
||||
if self._desktop_info:
|
||||
self.log(f"✔ Codex Desktop detected ({self._desktop_info})")
|
||||
else:
|
||||
self.log("✘ Codex Desktop NOT found — Desktop launch disabled. Click 'Install' above.")
|
||||
if self._missing:
|
||||
self.log("⚠ Install missing tools before using the launcher.")
|
||||
else:
|
||||
self.log("All dependencies OK.")
|
||||
|
||||
def _set_busy(self, busy):
|
||||
GLib.idle_add(lambda: (
|
||||
self._btn_desktop.set_sensitive(not busy),
|
||||
self._btn_cli.set_sensitive(not busy),
|
||||
self._btn_codex_desktop.set_sensitive(not busy),
|
||||
self._btn_codex_cli.set_sensitive(not busy),
|
||||
self._kill_btn.set_sensitive(busy),
|
||||
))
|
||||
def _update():
|
||||
has_cli = "cli" not in self._missing
|
||||
has_desk = "desktop" not in self._missing
|
||||
self._btn_desktop.set_sensitive(not busy and has_desk)
|
||||
self._btn_cli.set_sensitive(not busy and has_cli)
|
||||
self._btn_codex_desktop.set_sensitive(not busy and has_desk)
|
||||
self._btn_codex_cli.set_sensitive(not busy and has_cli)
|
||||
self._kill_btn.set_sensitive(busy)
|
||||
GLib.idle_add(_update)
|
||||
|
||||
def _rebuild_combo(self):
|
||||
self._endpoints_data = load_endpoints()
|
||||
@@ -746,6 +840,31 @@ class LauncherWin(Gtk.Window):
|
||||
d.run()
|
||||
d.destroy()
|
||||
|
||||
def _show_install_guide(self, which):
|
||||
if which == "cli":
|
||||
title = "Install Codex CLI"
|
||||
guide = (
|
||||
"Codex CLI is required to use CLI launch features.\n\n"
|
||||
"Install with npm:\n"
|
||||
" npm install -g @openai/codex\n\n"
|
||||
"Or download from:\n"
|
||||
" https://github.com/openai/codex\n\n"
|
||||
"After installing, restart the launcher."
|
||||
)
|
||||
else:
|
||||
title = "Install Codex Desktop"
|
||||
guide = (
|
||||
"Codex Desktop is required to use Desktop launch features.\n\n"
|
||||
"Expected location: /opt/codex-desktop/start.sh\n\n"
|
||||
"Download from:\n"
|
||||
" https://codex.desktop.openai.com\n\n"
|
||||
"After installing, restart the launcher."
|
||||
)
|
||||
d = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, guide)
|
||||
d.set_title(title)
|
||||
d.run()
|
||||
d.destroy()
|
||||
|
||||
# ── launch ───────────────────────────────────────────────────
|
||||
|
||||
def _launch(self, target):
|
||||
|
||||
Reference in New Issue
Block a user