diff --git a/CHANGELOG.md b/CHANGELOG.md index 3511a55..ed7a3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## v3.10.0 (2026-05-25) + +**Provider Model Editor + Antigravity Model Refresh** + +### Provider Editor +- **Remove Selected** button to remove highlighted model(s) from provider +- **Clear All** button to empty model list +- **Sync from Preset** button to refresh model list from current preset definition +- Preset sync now replaces (not appends) models — fixes stale saved model lists + +### Antigravity Models Updated +- **Gemini 3.5 Flash** (High / Medium) +- **Gemini 3.1 Pro** (High / Low) +- **Claude Sonnet 4.6 Thinking** +- **Claude Opus 4.6 Thinking** +- **GPT-OSS 120B Medium** + ## v3.9.9 (2026-05-25) **Antigravity Model Refresh** diff --git a/codex-launcher_3.10.0_all.deb b/codex-launcher_3.10.0_all.deb new file mode 100644 index 0000000..668df2a Binary files /dev/null and b/codex-launcher_3.10.0_all.deb differ diff --git a/install.sh b/install.sh index 79ad396..f1e7266 100755 --- a/install.sh +++ b/install.sh @@ -3,11 +3,11 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$SCRIPT_DIR/codex-launcher_3.9.9_all.deb" ]; then - echo "Installing codex-launcher_3.9.9_all.deb ..." - sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.9.9_all.deb" +if [ -f "$SCRIPT_DIR/codex-launcher_3.10.0_all.deb" ]; then + echo "Installing codex-launcher_3.10.0_all.deb ..." + sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.10.0_all.deb" echo "" - echo "Installed v3.9.9 via .deb package." + echo "Installed v3.10.0 via .deb package." echo " translate-proxy.py -> /usr/bin/translate-proxy.py" echo " codex-launcher-gui -> /usr/bin/codex-launcher-gui" echo " cleanup-codex-stale -> /usr/bin/cleanup-codex-stale.sh" diff --git a/src/codex-launcher-gui b/src/codex-launcher-gui index bd65e74..9ca1e96 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -26,6 +26,11 @@ model_catalog_json = "" """ CHANGELOG = [ + ("3.10.0", "2026-05-25", [ + "Provider editor: Remove Selected, Clear All, Sync from Preset buttons for model list", + "Sync from Preset replaces model list with current preset models", + "Stale saved Antigravity models auto-refreshed on preset sync", + ]), ("3.9.9", "2026-05-25", [ "Refresh Antigravity preset: Gemini 3.5 Flash, Gemini 3.1 Pro, Claude Sonnet/Opus 4.6, GPT-OSS 120B", "Fix Antigravity alias map for new tiered model IDs (high/medium/low/thinking)", @@ -1752,7 +1757,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.9.9") + lbl = Gtk.Label(label="Codex Launcher v3.10.0") lbl.set_use_markup(True) hdr.pack_start(lbl, False, False, 0) changelog_btn = Gtk.Button(label="Changelog") @@ -3138,6 +3143,18 @@ class EditEndpointDialog(Gtk.Dialog): sw.add(self._model_tree) self._model_tree.connect("row-activated", lambda t, p, c: self._remove_model(p)) + model_btn_box = Gtk.Box(spacing=6) + area.pack_start(model_btn_box, False, False, 0) + self._remove_model_btn = Gtk.Button(label="Remove Selected") + self._remove_model_btn.connect("clicked", lambda b: self._remove_selected_model()) + model_btn_box.pack_start(self._remove_model_btn, False, False, 0) + self._clear_models_btn = Gtk.Button(label="Clear All") + self._clear_models_btn.connect("clicked", lambda b: self._clear_all_models()) + model_btn_box.pack_start(self._clear_models_btn, False, False, 0) + self._sync_preset_btn = Gtk.Button(label="Sync from Preset") + self._sync_preset_btn.connect("clicked", lambda b: self._apply_selected_preset()) + model_btn_box.pack_start(self._sync_preset_btn, False, False, 0) + for m in self._data.get("models", []): self._model_store.append([m]) @@ -3207,10 +3224,12 @@ class EditEndpointDialog(Gtk.Dialog): cc_ver = preset.get("cc_version", "") if cc_ver and not self._entry_cc_ver.get_text().strip(): self._entry_cc_ver.set_text(cc_ver) - if preset.get("models") and len(self._model_store) == 0: - for mid in preset["models"]: - self._model_store.append([mid]) - self._refresh_default_combo(preset["models"][0]) + if preset.get("models") and (not initial or len(self._model_store) == 0): + current = self._combo_default.get_active_text() + self._model_store.clear() + for mid in preset["models"]: + self._model_store.append([mid]) + self._refresh_default_combo(current or preset["models"][0]) if initial and self._data.get("models"): self._refresh_default_combo(self._data.get("default_model", "")) @@ -3542,7 +3561,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.9.9"}) + headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.10.0"}) resp = urllib.request.urlopen(req, timeout=30) data = json.loads(resp.read()) login_url = data.get("loginUrl", "") or data.get("login_url", "") @@ -3567,7 +3586,7 @@ class EditEndpointDialog(Gtk.Dialog): time.sleep(2) try: poll_req = urllib.request.Request(poll_url, - headers={"User-Agent": "codex-launcher/3.9.9"}) + headers={"User-Agent": "codex-launcher/3.10.0"}) poll_resp = urllib.request.urlopen(poll_req, timeout=10) poll_data = json.loads(poll_resp.read()) user = poll_data.get("user") @@ -3632,6 +3651,21 @@ class EditEndpointDialog(Gtk.Dialog): self._model_store.remove(self._model_store.get_iter(path)) self._refresh_default_combo(current) + def _remove_selected_model(self): + sel = self._model_tree.get_selection() + model, paths = sel.get_selected_rows() + if not paths: + return + current = self._combo_default.get_active_text() + for p in reversed(paths): + self._model_store.remove(self._model_store.get_iter(p)) + self._refresh_default_combo(current) + + def _clear_all_models(self): + current = self._combo_default.get_active_text() + self._model_store.clear() + self._refresh_default_combo(current) + def _refresh_default_combo(self, active=None): if active is None: active = self._combo_default.get_active_text() diff --git a/src/translate-proxy.py b/src/translate-proxy.py index ede42a8..0015378 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.9.9", + "User-Agent": "codex-launcher/3.10.0", "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.9.9", + "User-Agent": "codex-launcher/3.10.0", }) 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.9.9", + "User-Agent": "codex-launcher/3.10.0", }) try: urllib.request.urlopen(req, timeout=10) @@ -5314,7 +5314,7 @@ class Handler(http.server.BaseHTTPRequestHandler): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token}", - "User-Agent": "codex-launcher/3.9.9", + "User-Agent": "codex-launcher/3.10.0", "x-codebuff-model": model, } if instance_id: @@ -5480,7 +5480,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.9.9", "x-codebuff-model": model} + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.10.0", "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)