chore: bump honcho-cli to 0.1.4 (#1080)

* feat(cli): fix API key typing

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: bump honcho-cli to 0.1.4

Ship the masked --setup API key prompt plus the openai-compatible embedding base URL already on main.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cli): check for newer version

* docs: nit

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ajspig 2026-08-26 13:05:25 -04:00 committed by GitHub
parent dea2917fa9
commit 2ddd819a28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 162 additions and 12 deletions

View File

@ -1170,7 +1170,17 @@ Welcome to the Honcho changelog! This section documents all notable changes to t
</Tab>
<Tab title="Honcho CLI">
[Honcho CLI](https://pypi.org/project/honcho-cli/)
<Update label="v0.1.3 (Current)">
<Update label="v0.1.4 (Current)">
### Added
- 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
</Update>
<Update label="v0.1.3">
### Added
- `honcho start`, `honcho stop`, and `honcho status` — run a personal Honcho stack in Docker (API, deriver, Postgres, Redis). Profiles live under `~/.honcho/profiles/`. First start pins `ghcr.io/plastic-labs/honcho:latest` by digest and copies the image `config.toml`. Optional `--setup basic` / `--setup advanced` wizard writes LLM overrides to `.env` (#1029)

View File

@ -69,6 +69,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 |
| Local stack | — | `HONCHO_PROFILE` | `--profile` | No |
### Persisted config
@ -124,6 +125,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

@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## [0.1.4] - 2026-08-26
### Added
- 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`
- `--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

@ -185,6 +185,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

@ -1,6 +1,6 @@
[project]
name = "honcho-cli"
version = "0.1.3"
version = "0.1.4"
description = "A terminal for Honcho — memory that reasons."
readme = "README.md"
requires-python = ">=3.11"

View File

@ -1,3 +1,3 @@
"""Honcho CLI — a terminal for Honcho."""
__version__ = "0.1.3"
__version__ = "0.1.4"

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

@ -7,6 +7,7 @@ only — the start command rejects ``--setup`` in JSON / non-TTY mode.
from __future__ import annotations
import sys
import tomllib
from dataclasses import dataclass
from pathlib import Path
@ -445,13 +446,7 @@ def _prompt_secret(label: str, current: str | None) -> str:
if choice != "2":
return current
_console.print(f" [dim]{label}[/dim]")
raw = typer.prompt(
f" {label}",
default="",
show_default=False,
hide_input=True,
prompt_suffix=": ",
).strip()
raw = _prompt_masked(f" {label}: ").strip()
if not raw or is_placeholder_key(raw):
print_error(
"MISSING_LLM_KEY",
@ -461,6 +456,69 @@ def _prompt_secret(label: str, current: str | None) -> str:
return raw
def _prompt_masked(prompt: str) -> str:
"""Read a secret, echoing ``*`` per character so paste is visibly received."""
stream = sys.stderr
stream.write(prompt)
stream.flush()
chars: list[str] = []
def _write(text: str) -> None:
stream.write(text)
stream.flush()
def _feed(ch: str) -> bool:
"""Return True when input is complete."""
if not ch or ch in ("\n", "\r", "\x04"):
_write("\n")
return True
if ch in ("\x7f", "\x08"):
if chars:
chars.pop()
_write("\b \b")
return False
if ch == "\x1b":
return False
if ch.isprintable():
chars.append(ch)
_write("*")
return False
if sys.platform == "win32":
import msvcrt
while True:
ch = msvcrt.getwch()
if ch in ("\x00", "\xe0"):
msvcrt.getwch()
continue
if _feed(ch):
return "".join(chars)
import termios
import tty
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
try:
tty.setcbreak(fd)
while True:
ch = sys.stdin.read(1)
if ch == "\x1b":
nxt = sys.stdin.read(1)
if nxt == "[":
while True:
seq = sys.stdin.read(1)
if not seq or "@" <= seq <= "~":
break
continue
if _feed(ch):
return "".join(chars)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return "".join(chars)
def _redact(key: str) -> str:
if len(key) <= 4:
return "***"

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))

View File

@ -1170,7 +1170,7 @@ dev = [{ name = "ruff", specifier = ">=0.11.13" }]
[[package]]
name = "honcho-cli"
version = "0.1.3"
version = "0.1.4"
source = { editable = "honcho-cli" }
dependencies = [
{ name = "click" },