diff --git a/claude_desktop/CHANGELOG.md b/claude_desktop/CHANGELOG.md index 2da9444603..00c950b8b6 100644 --- a/claude_desktop/CHANGELOG.md +++ b/claude_desktop/CHANGELOG.md @@ -1,5 +1,9 @@ -## 1.28 (16-07-2026) -- Minor bugs fixed +## 1.29 (16-07-2026) + +- Point the Headroom MCP server at the persistent Kompress model cache. 1.27 set `HF_HOME` on the `svc-headroom` proxy longrun only, but the MCP server is a separate process spawned by Claude Desktop / Claude Code from the registered `mcpServers` entry, so it never inherited that export and kept resolving the HuggingFace cache to `~/.cache` — symlinked to tmpfs here and wiped on every restart. Its Kompress ML path therefore never found the model, re-downloaded ~270 MB into tmpfs on each boot, and lost it again on the next one; `headroom_compress` fell back to `router:noop` (unchanged output) on prose and other unstructured content. The managed `headroom` entry in both `claude_desktop_config.json` and `~/.claude.json` now carries `env.HF_HOME` pointing at the same `~/.headroom/hf` cache the proxy warms. Rule-based compression (SmartCrusher, structured tool output) was unaffected and worked throughout. +- Fix `~/.gitconfig` being written as `root` and left unreadable by the `abc` runtime user, which broke git for the user that actually runs it: every commit failed with `Author identity unknown` and the `gh` credential helper was invisible to authenticated pushes. `git config --global` ran as root during init and rewrites the file on every start, so `20-folders.sh`'s earlier recursive chown never stuck to it (`.config/gh` survived abc-owned only because the "already authenticated" branch skips rewriting it). The git/gh setup now runs as `abc` via `s6-setuidgid`, matching `81-tokensave_repositories.sh`, and reclaims any root-owned copies left by an earlier version before writing. +- Fix `~/.bashrc` accumulating stale `HOME`/`FM_HOME` exports when `data_location` changes. The idempotency guard only tested for the *current* `$LOCATION`, so changing the option and later changing it back appended a second block while leaving the first, and the last one written won for every interactive shell — leaving `$HOME` pointing at a directory the add-on no longer manages. Any tool that resolves config through `$HOME` then read the wrong path (`headroom doctor` reported `claude: not routed (no ~/.claude/settings.json)` against a correctly routed install, and bare `headroom` invocations created a stray `.headroom` tree under the old location). The block is now marker-delimited and rewritten from scratch on every boot, so it is idempotent across any number of `data_location` changes. + ## 1.27 (15-07-2026) - Route Claude Desktop cowork/local-agent-mode sessions through the Headroom proxy. Desktop spawns its bundled Claude Code binary at an absolute path (bypassing the add-on's PATH wrapper) with `ANTHROPIC_BASE_URL` pinned to the production endpoint, so those sessions never produced proxy savings. The add-on now manages `env.ANTHROPIC_BASE_URL` in `~/.claude/settings.json` — settings `env` entries replace inherited environment values at CLI startup — gated on `headroom_wrap_claude_code` and never overwriting a user-customized endpoint. diff --git a/claude_desktop/config.yaml b/claude_desktop/config.yaml index 7cd8b5630a..bbfd56b2da 100644 --- a/claude_desktop/config.yaml +++ b/claude_desktop/config.yaml @@ -109,5 +109,5 @@ slug: claude_desktop tmpfs: true udev: true url: https://github.com/alexbelgium/hassio-addons -version: "1.28" +version: "1.29" video: true diff --git a/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh b/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh index 6ef48206a9..845c9aa3d9 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/20-folders.sh @@ -85,10 +85,23 @@ printf "%s" "$LOCATION" > "$S6_ENVDIR/HOME" printf "%s" "$LOCATION" > "$S6_ENVDIR/FM_HOME" printf "%s" "/tmp/cache" > "$S6_ENVDIR/XDG_CACHE_HOME" printf "%s" "$XDG_RUNTIME_DIR" > "$S6_ENVDIR/XDG_RUNTIME_DIR" -grep -qxF "export HOME=\"$LOCATION\"" ~/.bashrc 2>/dev/null || { +# Re-derived on every boot rather than gated on a "does it already say $LOCATION" grep: that +# guard only ever recognized the CURRENT $LOCATION, so a user who changed data_location and +# later changed it back left two stale HOME/FM_HOME exports in ~/.bashrc, with the last one +# (not necessarily the correct one) winning for every interactive shell. The marker makes this +# idempotent regardless of how many times $LOCATION has changed: strip any previously managed +# block, then append one that reflects the current value. +BASHRC_HOME_BEGIN="# --- BEGIN ADDON HOME (managed) ---" +BASHRC_HOME_END="# --- END ADDON HOME (managed) ---" +if [ -f ~/.bashrc ]; then + sed -i "/^${BASHRC_HOME_BEGIN}\$/,/^${BASHRC_HOME_END}\$/d" ~/.bashrc +fi +{ + printf "%s\n" "$BASHRC_HOME_BEGIN" printf "%s\n" "export HOME=\"$LOCATION\"" printf "%s\n" "export FM_HOME=\"$LOCATION\"" printf "%s\n" "export XDG_CACHE_HOME=\"/tmp/cache\"" + printf "%s\n" "$BASHRC_HOME_END" } >> ~/.bashrc bashio::log.info "Creating $LOCATION" diff --git a/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh index 8f01b01db2..4cf1a023b2 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/82-claude_tools.sh @@ -76,6 +76,7 @@ if bashio::config.true 'enable_ha_mcp'; then fi HEADROOM_ENABLED="$HEADROOM_ENABLED" HEADROOM_BIN="$(command -v headroom || echo headroom)" \ + HEADROOM_HF_HOME="${HOME}/.headroom/hf" \ TOKENSAVE_ENABLED="$TOKENSAVE_ENABLED" TOKENSAVE_BIN="$(command -v tokensave || echo tokensave)" \ HA_MCP_ENABLED="$HA_MCP_ENABLED" HA_MCP_URL="$HA_MCP_URL" HA_MCP_TOKEN="$HA_MCP_TOKEN" \ MCP_PROXY_BIN="$(command -v mcp-proxy || echo mcp-proxy)" \ @@ -96,6 +97,11 @@ if os.environ["HEADROOM_ENABLED"] == "true": desired["headroom"] = { "command": os.environ["HEADROOM_BIN"], "args": ["mcp", "serve", "--proxy-url", "http://127.0.0.1:8787"], + # The MCP server is a separate process from the svc-headroom proxy longrun and does + # not inherit its HF_HOME export, so Kompress falls back to the default (tmpfs, wiped + # every restart) cache dir, never finds the model, and silently no-ops every + # compression request. Point it at the same persistent cache the proxy warms. + "env": {"HF_HOME": os.environ["HEADROOM_HF_HOME"]}, } if os.environ["TOKENSAVE_ENABLED"] == "true": desired["tokensave"] = {"command": os.environ["TOKENSAVE_BIN"], "args": ["serve"]} diff --git a/claude_desktop/rootfs/etc/cont-init.d/83-github_cli.sh b/claude_desktop/rootfs/etc/cont-init.d/83-github_cli.sh index a836bef8b1..3df1f2f9ee 100755 --- a/claude_desktop/rootfs/etc/cont-init.d/83-github_cli.sh +++ b/claude_desktop/rootfs/etc/cont-init.d/83-github_cli.sh @@ -17,25 +17,54 @@ if ! command -v gh > /dev/null 2>&1; then exit 0 fi +# Everything below writes into the abc runtime user's HOME, so it must run AS abc. cont-init +# runs as root with HOME already pointing at the persistent data location, so plain +# `git config --global` recreated ~/.gitconfig owned by root:root on every start — and because +# that file is rewritten each boot, 20-folders.sh's earlier recursive chown never stuck to it. +# The user who actually runs git, gh and Claude was then unable to read its own committer +# identity or the gh credential helper, so every commit failed with "Author identity unknown" +# and authenticated pushes fell back to prompting. 20-folders.sh already remapped abc to the +# effective runtime identity (never root in bypass mode), so follow abc rather than re-reading +# the raw PUID/PGID options here. +RUNTIME_UID="$(id -u abc)" +RUNTIME_GID="$(id -g abc)" + +run_as_runtime_user() { + s6-setuidgid abc env HOME="$HOME" "$@" +} + +# Reclaim any root-owned copies left by an earlier add-on version before writing as abc: +# these paths are not covered by 82-claude_tools.sh's ownership pass, and a root-owned +# ~/.gitconfig would make the first `git config` below fail outright under `set -e`. + +mkdir -p "$HOME/.config" +chown -- "${RUNTIME_UID}:${RUNTIME_GID}" "$HOME/.config" + +for managed_path in "$HOME/.gitconfig" "$HOME/.config/gh"; do + if [ -e "$managed_path" ]; then + chown -R -- "${RUNTIME_UID}:${RUNTIME_GID}" "$managed_path" || bashio::log.warning "Unable to set ownership on $managed_path" + fi +done + if bashio::config.has_value 'github_username'; then - git config --global user.name "$(bashio::config 'github_username')" + run_as_runtime_user git config --global user.name "$(bashio::config 'github_username')" fi if bashio::config.has_value 'github_email'; then - git config --global user.email "$(bashio::config 'github_email')" + run_as_runtime_user git config --global user.email "$(bashio::config 'github_email')" fi if bashio::config.has_value 'github_token'; then token="$(bashio::config 'github_token')" - mkdir -p "$HOME/.config/gh" - chmod 700 "$HOME/.config/gh" - if env -u GH_TOKEN -u GITHUB_TOKEN gh auth status --hostname github.com > /dev/null 2>&1; then + run_as_runtime_user mkdir -p "$HOME/.config/gh" + run_as_runtime_user chmod 700 "$HOME/.config/gh" + if run_as_runtime_user env -u GH_TOKEN -u GITHUB_TOKEN gh auth status --hostname github.com > /dev/null 2>&1; then bashio::log.info "GitHub CLI already authenticated for github.com" else bashio::log.info "Configuring GitHub CLI authentication for github.com" - printf '%s\n' "$token" | env -u GH_TOKEN -u GITHUB_TOKEN gh auth login --hostname github.com --with-token || bashio::log.warning "GitHub CLI authentication failed" + printf '%s\n' "$token" | run_as_runtime_user env -u GH_TOKEN -u GITHUB_TOKEN gh auth login --hostname github.com --with-token || bashio::log.warning "GitHub CLI authentication failed" fi - env -u GH_TOKEN -u GITHUB_TOKEN gh auth setup-git --hostname github.com || bashio::log.warning "GitHub CLI git credential setup failed" + run_as_runtime_user env -u GH_TOKEN -u GITHUB_TOKEN gh auth setup-git --hostname github.com || bashio::log.warning "GitHub CLI git credential setup failed" else bashio::log.info "GitHub CLI available. Set github_token to authenticate gh and git operations." fi