Dashboard: indicate when a newer Serena version is available

This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-07-21 14:51:32 +02:00
1 parent 4903da5ac0
commit 4c82b2bf19
5 files changed
+83 -1

No files matched your search

+1
View File
@@ -70,6 +70,7 @@ Status of the `main` branch. Changes prior to the next official version change w
when using plugin version 2023.3.3+
* Dashboard:
- The version display now indicates when a newer Serena version is available
- Fix: the "Last Execution" panel stayed on "Loading..." forever when there was no logged execution
(e.g. a fresh server / no tool run yet), because `loadLastExecution()` only rendered the panel when
the backend returned a non-null execution; the empty state is now rendered via the existing
+26
View File
@@ -10,6 +10,7 @@ import urllib.error
import urllib.request
from collections.abc import Callable
from dataclasses import dataclass
from functools import cached_property
from html import escape
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional, Self
@@ -26,7 +27,9 @@ from serena.config.serena_config import SerenaConfig, SerenaPaths
from serena.constants import SERENA_DASHBOARD_DIR, SerenaPorts
from serena.task_executor import TaskExecutor
from serena.util.logging import MemoryLogHandler
from serena.util.pypi import PyPIPackageInfo
from serena.util.pywebview import WebViewWithTray
from serena.util.version import Version
if TYPE_CHECKING:
from serena.agent import SerenaAgent
@@ -71,6 +74,7 @@ class ResponseConfigOverview(BaseModel):
encoding: str | None
current_client: str | None
serena_version: str
newer_serena_version: str | None
class ResponseAvailableLanguages(BaseModel):
@@ -224,6 +228,27 @@ class SerenaDashboardAPI:
def memory_log_handler(self) -> MemoryLogHandler:
return self._memory_log_handler
@cached_property
def _newer_serena_version(self) -> str | None:
"""The version of a newer serena-agent package release on PyPI, if any.
The result is computed only once (on first access) and cached thereafter.
:return: the latest version available on PyPI if it is newer than the running version, else None
"""
try:
# query PyPI for the latest released version
latest_serena_version = PyPIPackageInfo("serena-agent").get_latest_version(timeout_secs=3)
# compare against the running version
latest_version = Version(latest_serena_version)
current_version = Version(self._agent.version)
if not current_version.is_at_least(*latest_version.components):
return latest_serena_version
except Exception as e:
log.warning("Failed to check for newer Serena version on PyPI: %s", e)
return None
def _setup_routes(self) -> None:
@self._app.route("/")
def redirect_to_dashboard() -> Response:
@@ -606,6 +631,7 @@ class SerenaDashboardAPI:
encoding=encoding,
current_client=Tool.get_last_tool_call_client_str(),
serena_version=self._agent.version,
newer_serena_version=self._newer_serena_version,
)
def _on_agent_config_changed(self) -> None:
@@ -556,6 +556,24 @@ code, pre, kbd, samp,
color: var(--text-primary);
}
.newer-version-badge {
display: inline-flex;
align-items: center;
padding: 2px 10px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 12px;
font-weight: 500;
text-decoration: none;
cursor: pointer;
}
.newer-version-badge:hover {
background: var(--btn-secondary-hover);
}
.config-list {
list-style: none;
padding: 0;
+6 -1
View File
@@ -618,7 +618,12 @@ class Dashboard {
let html = '<div class="config-grid">';
html += '<div class="config-label">Version:</div><div class="config-value">' + config.serena_version + '</div>';
html += '<div class="config-label">Version:</div><div class="config-value">' + config.serena_version;
if (config.newer_serena_version) {
html += ' <a class="newer-version-badge" href="https://github.com/oraios/serena/releases" target="_blank" rel="noopener">'
+ 'newer version available: ' + config.newer_serena_version + '</a>';
}
html += '</div>';
// Project info
html += '<div class="config-label">Active Project:</div>';
+32
View File
@@ -0,0 +1,32 @@
"""Utilities for querying package information from the Python Package Index (PyPI)."""
import requests
class PyPIPackageInfo:
"""Provider of information on a package hosted on the Python Package Index (PyPI).
Information is retrieved on demand via PyPI's JSON API.
"""
JSON_API_URL_TEMPLATE = "https://pypi.org/pypi/%s/json"
def __init__(self, package_name: str) -> None:
"""
:param package_name: the name of the package on PyPI (e.g. "requests")
"""
self.package_name = package_name
def get_latest_version(self, timeout_secs: int = 5) -> str:
"""Retrieves the latest released version of the package from PyPI.
:return: the latest version string (e.g. "2.33.0")
:raises requests.RequestException: if the HTTP request fails or PyPI responds with an error status
"""
# query PyPI's JSON API for the package metadata
url = self.JSON_API_URL_TEMPLATE % self.package_name
response = requests.get(url, timeout=timeout_secs)
response.raise_for_status()
# extract the latest version from the metadata
return response.json()["info"]["version"]