feat(cli): check for newer version

This commit is contained in:
ajspig 2026-08-26 11:32:31 -04:00
parent a6676355d8
commit 9cb6ec1233
6 changed files with 78 additions and 2 deletions

View File

@ -59,6 +59,7 @@ The CLI resolves config in this order: **flag → env var → config file → de
| Peer | — | `HONCHO_PEER_ID` | `-p` / `--peer` | No |
| Session | — | `HONCHO_SESSION_ID` | `-s` / `--session` | No |
| JSON output | — | `HONCHO_JSON` | `--json` | No |
| Update nag | — | `HONCHO_NO_UPDATE_CHECK` | — | No |
### Persisted config
@ -110,6 +111,8 @@ Every command adapts its output to the context:
- **Piped or redirected** — JSON automatically (detected via `isatty`).
- **`--json` flag / `HONCHO_JSON=1`** — force JSON regardless of terminal.
Interactive sessions may print a one-line upgrade hint on stderr at most once a day when a newer `honcho-cli` is on PyPI (`uv tool upgrade honcho-cli`). JSON/piped output skips it; set `HONCHO_NO_UPDATE_CHECK=1` to disable it.
Collection commands emit JSON arrays; single-resource commands emit JSON objects. Errors are always structured:
```json

View File

@ -9,13 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.1.4] - 2026-08-26
### Changed
### Added
- `--setup` API key prompts echo `*` per character so a paste is visibly received instead of a blank getpass field
- A TTY notice when a newer `honcho-cli` is on PyPI (`uv tool upgrade honcho-cli`). Skipped in JSON mode; disable with `HONCHO_NO_UPDATE_CHECK`
### Fixed
- `--setup` for openai-compatible writes `EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL` into the profile `.env` alongside `LLM_OPENAI_BASE_URL` (#1068)
- `--setup` API key prompts echo `*` per character so a paste is visibly received instead of a blank getpass field
## [0.1.3] - 2026-08-25

View File

@ -186,6 +186,7 @@ Precedence (highest first): **flag → env var → config file → default**.
| `HONCHO_PEER_ID` | `-p` / `--peer` | Peer scope |
| `HONCHO_SESSION_ID` | `-s` / `--session` | Session scope |
| `HONCHO_JSON` | `--json` | Force JSON output (`1` / `true`) |
| `HONCHO_NO_UPDATE_CHECK` | — | Disable the once-a-day upgrade notice (`1` / `true`) |
| `HONCHO_PROFILE` | `--profile` (start/stop/status) | Local stack profile (default: `local`) |
| `LLM_OPENAI_API_KEY` | — | Provider key for `honcho start` (also `LLM_ANTHROPIC_API_KEY`, `LLM_GEMINI_API_KEY`) |

View File

@ -24,6 +24,7 @@ from typer.core import TyperGroup
from honcho_cli import __version__
from honcho_cli.branding import BANNER, BRAND
from honcho_cli.output import use_json
from honcho_cli.update_check import maybe_print_update_nag
# Theme Typer's rich help renderer. Module-level side effect limited to
@ -113,6 +114,7 @@ def print_welcome(console: Console) -> None:
console.print(_welcome_panel("memory", memory_rows))
console.print(_welcome_panel("options", option_rows))
console.print()
maybe_print_update_nag()
class HonchoTyperGroup(TyperGroup):

View File

@ -15,6 +15,7 @@ from honcho_cli import __version__
from honcho_cli._help import HonchoTyperGroup, print_welcome
from honcho_cli.branding import BANNER
from honcho_cli.output import set_json_mode
from honcho_cli.update_check import maybe_print_update_nag
app = typer.Typer(
@ -60,6 +61,7 @@ def main(
if ctx.invoked_subcommand is None:
print_welcome(Console())
raise typer.Exit()
maybe_print_update_nag()
# Register top-level commands

View File

@ -0,0 +1,67 @@
"""Once-a-day stderr notice when a newer honcho-cli is on PyPI.
Fail-open: any error is swallowed. Cache is ``update-check.json`` beside
config, not ``config.json``.
"""
from __future__ import annotations
import json
import os
import sys
import time
import httpx
from honcho_cli import __version__
from honcho_cli.branding import ICON_RUN
from honcho_cli.config import _config_dir
from honcho_cli.output import console, use_json
_INTERVAL_S = 24 * 60 * 60
_PYPI_URL = "https://pypi.org/pypi/honcho-cli/json"
def maybe_print_update_nag() -> None:
if use_json() or "--json" in sys.argv:
return
if os.environ.get("HONCHO_NO_UPDATE_CHECK", "").lower() in ("1", "true"):
return
try:
path = _config_dir() / "update-check.json"
now = time.time()
try:
cache = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError, UnicodeDecodeError):
cache = {}
if isinstance(cache, dict) and now - float(cache.get("t") or 0) < _INTERVAL_S:
return
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps({"t": now}), encoding="utf-8")
latest = httpx.get(_PYPI_URL, timeout=1.0).json()["info"]["version"]
if not isinstance(latest, str) or not _is_newer(latest, __version__):
return
console.print(f" {ICON_RUN} honcho-cli {latest} is available (you have {__version__})")
console.print(" [dim]uv tool upgrade honcho-cli[/dim]")
except Exception:
return
def _is_newer(latest: str, current: str) -> bool:
def parts(version: str) -> tuple[int, ...]:
out: list[int] = []
for segment in version.lstrip("v").split("."):
num = ""
for ch in segment:
if ch.isdigit():
num += ch
else:
break
if not num:
break
out.append(int(num))
return tuple(out) or (0,)
a, b = parts(latest), parts(current)
n = max(len(a), len(b))
return a + (0,) * (n - len(a)) > b + (0,) * (n - len(b))