mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-07-27 16:30:32 +02:00
Merge effaf6e211 into c824a5e0fb
This commit is contained in:
@@ -1,3 +1,14 @@
|
|||||||
|
## 1.13 (10-07-2026)
|
||||||
|
|
||||||
|
- Add the official Claude Code stable package, `tmux`, `ripgrep`, and a pinned upstream `ttyd` binary for both supported architectures.
|
||||||
|
- Add an optional authenticated Claude Code web terminal on port `7681`; reconnecting clients attach to the same persistent tmux session.
|
||||||
|
- Validate and canonicalize `terminal_workspace`, restrict it to supported storage roots, and never re-own existing directories.
|
||||||
|
- Order the terminal after service initialization and start tmux with an explicit Bash shell and workspace.
|
||||||
|
- Add `claude-headroom` for Headroom's supported wrapper, reusing the supervised proxy with `--no-proxy` while leaving RTK management to the add-on.
|
||||||
|
- Keep Claude Desktop on the MCP-only Headroom integration and remove the ineffective `ANTHROPIC_BASE_URL` export from its launch environment.
|
||||||
|
- Preserve ownership of Claude settings for the configured runtime user and remove only the exact add-on-managed RTK hook when RTK is disabled.
|
||||||
|
- Document the shared-home architecture, separate Desktop/CLI sessions, direct HTTP terminal security, shared concurrent sessions, and configurable data location.
|
||||||
|
|
||||||
## 1.12 (09-07-2026)
|
## 1.12 (09-07-2026)
|
||||||
- Minor bugs fixed
|
- Minor bugs fixed
|
||||||
## 1.11 (09-07-2026)
|
## 1.11 (09-07-2026)
|
||||||
|
|||||||
@@ -42,26 +42,48 @@ RUN chmod 744 /ha_lsio.sh && if grep -qr "lsio" /etc; then /ha_lsio.sh "$CONFIGL
|
|||||||
|
|
||||||
# Add rootfs
|
# Add rootfs
|
||||||
COPY rootfs/ /
|
COPY rootfs/ /
|
||||||
RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \;
|
RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \; && \
|
||||||
|
chmod +x /usr/local/bin/claude-direct /usr/local/bin/claude-headroom /usr/local/bin/claude-terminal-shell
|
||||||
|
|
||||||
# Uses /bin for compatibility purposes
|
# Uses /bin for compatibility purposes
|
||||||
# hadolint ignore=DL4005
|
# hadolint ignore=DL4005
|
||||||
RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \
|
RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \
|
||||||
if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi
|
if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi
|
||||||
|
|
||||||
# Install Claude Desktop and Python tooling
|
# Install Claude Desktop, Claude Code, browser-terminal tooling, and Python tooling
|
||||||
RUN curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc && \
|
RUN install -d -m 0755 /etc/apt/keyrings && \
|
||||||
|
curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc && \
|
||||||
|
curl -fsSLo /etc/apt/keyrings/claude-code.asc https://downloads.claude.ai/keys/claude-code.asc && \
|
||||||
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" > /etc/apt/sources.list.d/claude-desktop.list && \
|
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/claude-desktop-archive-keyring.asc] https://downloads.claude.ai/claude-desktop/apt/stable stable main" > /etc/apt/sources.list.d/claude-desktop.list && \
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/claude-code.asc] https://downloads.claude.ai/claude-code/apt/stable stable main" > /etc/apt/sources.list.d/claude-code.list && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
claude-desktop \
|
claude-desktop \
|
||||||
|
claude-code \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
libsecret-1-0 \
|
libsecret-1-0 \
|
||||||
dbus-x11 \
|
dbus-x11 \
|
||||||
git gh && \
|
git \
|
||||||
|
gh \
|
||||||
|
ripgrep \
|
||||||
|
tmux && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# ttyd is not packaged in Debian bookworm. Install the pinned upstream static binary.
|
||||||
|
ARG TTYD_VERSION="1.7.7"
|
||||||
|
RUN set -eux; \
|
||||||
|
case "$(dpkg --print-architecture)" in \
|
||||||
|
amd64) ttyd_arch="x86_64" ;; \
|
||||||
|
arm64) ttyd_arch="aarch64" ;; \
|
||||||
|
*) echo "Unsupported architecture for ttyd: $(dpkg --print-architecture)" >&2; exit 1 ;; \
|
||||||
|
esac; \
|
||||||
|
curl -fsSL --retry 3 --retry-delay 2 \
|
||||||
|
-o /usr/local/bin/ttyd \
|
||||||
|
"https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.${ttyd_arch}"; \
|
||||||
|
chmod 0755 /usr/local/bin/ttyd; \
|
||||||
|
/usr/local/bin/ttyd --version
|
||||||
|
|
||||||
# Install token-optimization tool prerequisites and binaries at build time
|
# Install token-optimization tool prerequisites and binaries at build time
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends nodejs && \
|
apt-get install -y --no-install-recommends nodejs && \
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
![Supports amd64 Architecture][amd64-shield]
|
![Supports amd64 Architecture][amd64-shield]
|
||||||
![Project Maintenance][maintenance-shield]
|
![Project Maintenance][maintenance-shield]
|
||||||
|
|
||||||
Run the Claude Desktop Linux app inside a LinuxServer.io Selkies container and stream it through Home Assistant ingress.
|
Run Claude Desktop and an optional persistent Claude Code web terminal in one
|
||||||
|
LinuxServer.io Selkies add-on.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -13,60 +14,177 @@ Run the Claude Desktop Linux app inside a LinuxServer.io Selkies container and s
|
|||||||
3. Start the add-on and open the web UI from the sidebar.
|
3. Start the add-on and open the web UI from the sidebar.
|
||||||
4. Sign in with your Claude account from the Desktop app.
|
4. Sign in with your Claude account from the Desktop app.
|
||||||
|
|
||||||
Claude Desktop sign-in requires a claude.ai plan that supports the Desktop app. API keys are not accepted by the Desktop application. Anthropic's Linux beta does not include Computer Use or dictation.
|
Claude Desktop sign-in requires a claude.ai plan that supports the Desktop app.
|
||||||
|
API keys are not accepted by the Desktop application. Anthropic's Linux beta
|
||||||
|
currently does not include Computer Use or dictation.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Claude Desktop and Claude Code run as separate clients inside the same add-on.
|
||||||
|
They share the configured persistent home directory, Git credentials,
|
||||||
|
repositories, Claude Code configuration, Headroom storage, and RTK
|
||||||
|
configuration, but they do not share or hand off a conversation.
|
||||||
|
|
||||||
|
- **Claude Desktop** uses Headroom through its MCP tools.
|
||||||
|
- **Claude Code** uses Headroom's supported `headroom wrap claude` integration.
|
||||||
|
- **RTK** filters Claude Code Bash output through its `PreToolUse` hook.
|
||||||
|
- **tmux** keeps the terminal session running when the browser disconnects.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Claude Desktop in single-app Selkies mode.
|
- Claude Desktop in single-app Selkies mode.
|
||||||
- Home Assistant ingress support.
|
- Home Assistant ingress support for Claude Desktop.
|
||||||
- Persistent `$HOME` under `/data/data`, preserving Claude Desktop and Claude Code sign-in state across restarts.
|
- Official Claude Code stable package installed in the same image.
|
||||||
|
- Optional authenticated `ttyd` web terminal on port `7681`.
|
||||||
|
- Persistent `tmux` session shared by reconnecting terminal clients.
|
||||||
|
- Persistent `$HOME` at the configured `data_location` (default `/data/data`),
|
||||||
|
preserving Desktop and Claude Code state across restarts.
|
||||||
- Optional runtime Claude Desktop updates from Anthropic's apt repository.
|
- Optional runtime Claude Desktop updates from Anthropic's apt repository.
|
||||||
- Optional extra apt and pip package installation.
|
- Optional extra apt and pip package installation.
|
||||||
- Baked-in `git` and GitHub CLI (`gh`) with optional startup credential configuration.
|
- Baked-in `git`, GitHub CLI (`gh`), `ripgrep`, and terminal tooling.
|
||||||
- Custom script support through the repository standard `claude_desktop.sh` script.
|
- Custom script support through the repository standard `claude_desktop.sh`.
|
||||||
- Optional bundled Claude Code optimization tools: headroom, rtk, and caveman.
|
- Optional bundled Claude Code optimization tools: Headroom, RTK, and Caveman.
|
||||||
- Headroom dashboard exposed on mapped port `8787` when `install_headroom` is enabled.
|
- Headroom dashboard exposed on mapped port `8787` when enabled.
|
||||||
- Low-power defaults: GPU device mapping, `AUTO_GPU=1`, `SELKIES_FRAMERATE=30`, `/tmp` tmpfs, and `$HOME/.cache` redirected to `/tmp/cache`.
|
- Low-power defaults for GPU mapping, Selkies frame rate, and volatile caches.
|
||||||
|
|
||||||
|
## Claude Code terminal setup
|
||||||
|
|
||||||
|
The terminal service is enabled in the add-on configuration but remains
|
||||||
|
unavailable until authentication is configured. Port `7681` is not mapped by
|
||||||
|
default.
|
||||||
|
|
||||||
|
1. Set a unique `terminal_password`. The existing `PASSWORD` option is accepted
|
||||||
|
only as a compatibility fallback.
|
||||||
|
2. Optionally set `terminal_username` and `terminal_workspace`.
|
||||||
|
3. Map container port `7681` to a host port in the add-on **Network** section.
|
||||||
|
4. Restart the add-on.
|
||||||
|
5. Reach `http://<home-assistant-host>:7681` only through an encrypted VPN or an
|
||||||
|
HTTPS reverse proxy, then sign in with the configured terminal credentials.
|
||||||
|
|
||||||
|
The terminal opens in a persistent tmux session. Closing the browser detaches
|
||||||
|
from tmux rather than terminating commands that are already running.
|
||||||
|
|
||||||
|
Start the optimized Claude Code path with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
claude-headroom
|
||||||
|
```
|
||||||
|
|
||||||
|
This reuses the supervised Headroom proxy on `127.0.0.1:8787` and launches
|
||||||
|
Claude Code with the required routing. Headroom is told not to install RTK
|
||||||
|
because the add-on already maintains the RTK hook in
|
||||||
|
`~/.claude/settings.json`.
|
||||||
|
|
||||||
|
To bypass Headroom for troubleshooting, run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
claude-direct
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `claude` directly is equivalent to the direct path. The first Claude
|
||||||
|
Code launch may require its own account authentication; Desktop and Claude Code
|
||||||
|
store separate client credentials even though both use the configured
|
||||||
|
persistent home directory.
|
||||||
|
|
||||||
|
### Multiple concurrent clients
|
||||||
|
|
||||||
|
Every browser connection attaches to the same tmux session. Concurrent clients
|
||||||
|
therefore see the same terminal, keystrokes, and resize events. This is useful
|
||||||
|
for reconnecting to one long-running session, but it is not an isolated
|
||||||
|
multi-user terminal.
|
||||||
|
|
||||||
|
### Terminal user and permissions
|
||||||
|
|
||||||
|
The service drops privileges to the LinuxServer `abc` account before starting
|
||||||
|
ttyd. The effective numeric UID and GID follow the configured `PUID` and `PGID`.
|
||||||
|
Using `PUID: 0` can provide root-equivalent access inside the add-on; use a
|
||||||
|
non-zero UID/GID where your storage permissions allow it.
|
||||||
|
|
||||||
|
The configured workspace must resolve to the persistent home directory or a
|
||||||
|
subdirectory of `/share`, `/media`, `/mnt`, `/data`, or `/config`. Existing
|
||||||
|
directories are never re-owned by the terminal service and must already be
|
||||||
|
readable, writable, and searchable by `abc`.
|
||||||
|
|
||||||
|
### Terminal security
|
||||||
|
|
||||||
|
The direct ttyd endpoint uses HTTP Basic Authentication without TLS.
|
||||||
|
Credentials and terminal traffic are unencrypted on the network. ttyd also
|
||||||
|
receives its Basic Authentication credential as a process argument, so it is
|
||||||
|
visible to processes with sufficient access inside the container.
|
||||||
|
|
||||||
|
Do not expose port `7681` directly to the public internet. Use a VPN such as
|
||||||
|
WireGuard or Tailscale, or place the endpoint behind an HTTPS reverse proxy.
|
||||||
|
Use a unique `terminal_password` rather than reusing the Selkies `PASSWORD`.
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
| ------ | ------- | ----------- |
|
| ------ | ------- | ----------- |
|
||||||
| `PUID` / `PGID` | `0` / `0` | User and group used for persistent data ownership. |
|
| `PUID` / `PGID` | `0` / `0` | Numeric user and group applied by the LinuxServer initialization. |
|
||||||
| `TZ` | | Optional timezone, for example `America/New_York`. |
|
| `TZ` | | Optional timezone, for example `Europe/Brussels`. |
|
||||||
| `KEYBOARD` | | Optional Selkies keyboard layout. |
|
| `KEYBOARD` | | Optional Selkies keyboard layout. |
|
||||||
| `PASSWORD` | | Optional password for direct Selkies ports. Set this before exposing ports `3000` or `3001`. |
|
| `PASSWORD` | | Optional password for direct Selkies ports and compatibility fallback for terminal authentication. |
|
||||||
| `DRINODE` | | Optional GPU device override for Selkies. |
|
| `DRINODE` | | Optional GPU device override for Selkies. |
|
||||||
| `DNS_server` | `8.8.8.8` | DNS server used by the standard DNS module. |
|
| `DNS_server` | `8.8.8.8` | DNS server used by the standard DNS module. |
|
||||||
| `auto_update` | `true` | Check Anthropic's apt repository and upgrade `claude-desktop` at add-on startup. |
|
| `auto_update` | `true` | Upgrade `claude-desktop` from Anthropic's apt repository at startup. |
|
||||||
| `install_headroom` | `true` | Register the baked-in `headroom` MCP server in Claude Desktop, start the Headroom proxy backend, and expose the Headroom dashboard on mapped port `8787` at `/dashboard` when that port is open. This exposes the `headroom_compress`/`headroom_retrieve`/`headroom_stats` context-compression tools inside the app. (Claude Desktop overrides `ANTHROPIC_BASE_URL`, so transparent proxy compression is not possible — MCP is the supported path; see [headroom #869](https://github.com/headroomlabs-ai/headroom/issues/869).) Disabling removes the MCP entry and stops the backend/dashboard service. |
|
| `enable_terminal` | `true` | Enable the supervised Claude Code web-terminal service. |
|
||||||
| `install_rtk` | `true` | Configure the rtk Claude Code `PreToolUse` hook in the persistent Claude Code settings. |
|
| `terminal_username` | `claude` | Username used by ttyd Basic Authentication. |
|
||||||
| `install_caveman` | `true` | Install the caveman Claude Code plugin into the persistent Claude Code home. |
|
| `terminal_password` | | Dedicated terminal password. The service idles when this and `PASSWORD` are empty. |
|
||||||
| `install_github_cli` | `true` | Enable first-start checks and setup for the baked-in `git` and `gh` commands. |
|
| `terminal_workspace` | | Initial directory; defaults to `<data_location>/workspace`. |
|
||||||
| `github_token` | | Optional GitHub personal access token used to authenticate `gh` and configure Git credentials for GitHub. |
|
| `install_headroom` | `true` | Enable Headroom MCP for Desktop and the supervised local proxy reused by `claude-headroom`. |
|
||||||
|
| `install_rtk` | `true` | Configure RTK's Claude Code `PreToolUse` hook. |
|
||||||
|
| `install_caveman` | `true` | Install the Caveman Claude Code plugin in the persistent Claude home. |
|
||||||
|
| `install_github_cli` | `true` | Enable setup checks for the baked-in `git` and `gh` commands. |
|
||||||
|
| `github_token` | | Optional GitHub token used to authenticate `gh` and Git operations. |
|
||||||
| `github_username` | | Optional global Git author name. |
|
| `github_username` | | Optional global Git author name. |
|
||||||
| `github_email` | | Optional global Git author email. |
|
| `github_email` | | Optional global Git author email. |
|
||||||
| `ha_smart_context` | `true` | Enable Home Assistant smart context support for Claude tooling. |
|
| `ha_smart_context` | `true` | Enable Home Assistant smart context support for Claude tooling. |
|
||||||
| `enable_ha_mcp` | `true` | Enable Home Assistant MCP support for Claude tooling. |
|
| `enable_ha_mcp` | `true` | Enable Home Assistant MCP support for Claude tooling. |
|
||||||
| `dangerously_skip_permissions` | `false` | Expose Claude Code's dangerous permission skip mode option. Only enable if you understand the risk. |
|
| `dangerously_skip_permissions` | `false` | Reserved compatibility option; it is not applied by the terminal launcher. |
|
||||||
| `additional_apps` | | Comma-separated Debian apt packages to install at startup, for example `htop,git`. |
|
| `additional_apps` | | Comma-separated Debian apt packages to install at startup. |
|
||||||
| `additional_pip` | | Comma-separated pip packages to install at startup. Installs use `--break-system-packages`. |
|
| `additional_pip` | | Comma-separated pip packages installed with `--break-system-packages`. |
|
||||||
| `data_location` | `/data/data` | Persistent home directory location. Keep this persistent so Claude sign-in survives restarts. |
|
| `data_location` | `/data/data` | Persistent home directory for both Claude clients and tooling. |
|
||||||
| `networkdisks`, `cifsusername`, `cifspassword`, `cifsdomain` | | Standard SMB mount options. |
|
| `env_vars` | `[]` | Additional environment variables exported inside the container. |
|
||||||
| `localdisks` | | Standard local disk mount option. |
|
|
||||||
| `env_vars` | `[]` | Extra environment variables to export into the container. This can override `SELKIES_*` defaults. |
|
## Headroom behavior
|
||||||
|
|
||||||
|
When `install_headroom` is enabled, the add-on registers `headroom mcp serve` in
|
||||||
|
Claude Desktop and starts a supervised local Headroom backend. Desktop can use
|
||||||
|
`headroom_compress`, `headroom_retrieve`, and `headroom_stats` through MCP.
|
||||||
|
|
||||||
|
Claude Desktop overrides `ANTHROPIC_BASE_URL`, so it is deliberately launched
|
||||||
|
without proxy injection. The web terminal instead provides `claude-headroom`,
|
||||||
|
which reuses the supervised proxy through Headroom's `--no-proxy` mode. RTK
|
||||||
|
setup remains owned by the add-on through Headroom's `--no-rtk` mode.
|
||||||
|
|
||||||
|
The Headroom dashboard remains available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://<home-assistant-host>:8787/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
when the `8787/tcp` port is mapped. Treat this endpoint as sensitive and do not
|
||||||
|
expose it directly to the public internet.
|
||||||
|
|
||||||
## Custom scripts
|
## Custom scripts
|
||||||
|
|
||||||
The add-on includes the repository standard custom-script executor. On first start, it seeds a `claude_desktop.sh` file in the add-on config directory from the shared template. Commands in that script run during startup, allowing local customization without rebuilding the image.
|
The add-on includes the repository standard custom-script executor. On first
|
||||||
|
start, it seeds `claude_desktop.sh` in the add-on config directory. Commands in
|
||||||
|
that script run during startup, allowing local customization without rebuilding
|
||||||
|
the image.
|
||||||
|
|
||||||
## Data and cache locations
|
## Data and cache locations
|
||||||
|
|
||||||
Persistent state is stored in the configured `data_location`. Claude Desktop stores sign-in data below `~/.config/Claude`, and Claude Code/tool configuration is stored below `~/.claude`. Volatile cache data is redirected to `/tmp/cache` through `$XDG_CACHE_HOME` and `$HOME/.cache`.
|
Persistent state is stored in the configured `data_location` (default
|
||||||
|
`/data/data`):
|
||||||
|
|
||||||
## Headroom dashboard
|
- Claude Desktop sign-in: `~/.config/Claude`
|
||||||
|
- Claude Code settings, hooks, sessions, and plugins: `~/.claude`
|
||||||
|
- Default terminal workspace: `~/workspace`
|
||||||
|
- Headroom and RTK user state: their standard paths below the shared home
|
||||||
|
|
||||||
When `install_headroom` is enabled, the add-on starts the local Headroom proxy backend and maps port `8787` by default. Open `http://<home-assistant-host>:8787/dashboard` to view the Headroom live savings dashboard. If you disable the `8787/tcp` port mapping, the backend binds to localhost for MCP use only and the dashboard is not externally exposed.
|
Volatile cache data is redirected to `/tmp/cache` through `$XDG_CACHE_HOME` and
|
||||||
|
`$HOME/.cache`.
|
||||||
|
|
||||||
[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg
|
[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg
|
||||||
[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
|
[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ arch:
|
|||||||
- aarch64
|
- aarch64
|
||||||
- amd64
|
- amd64
|
||||||
audio: true
|
audio: true
|
||||||
description: Claude Desktop streamed through a browser with LinuxServer Selkies
|
description: Claude Desktop and a persistent Claude Code terminal streamed from one add-on
|
||||||
devices:
|
devices:
|
||||||
- /dev/dri
|
- /dev/dri
|
||||||
- /dev/dri/card0
|
- /dev/dri/card0
|
||||||
@@ -44,6 +44,10 @@ options:
|
|||||||
ha_smart_context: true
|
ha_smart_context: true
|
||||||
enable_ha_mcp: true
|
enable_ha_mcp: true
|
||||||
dangerously_skip_permissions: false
|
dangerously_skip_permissions: false
|
||||||
|
enable_terminal: true
|
||||||
|
terminal_username: claude
|
||||||
|
terminal_password: ""
|
||||||
|
terminal_workspace: ""
|
||||||
github_token: ""
|
github_token: ""
|
||||||
github_username: ""
|
github_username: ""
|
||||||
install_caveman: true
|
install_caveman: true
|
||||||
@@ -54,9 +58,11 @@ panel_admin: false
|
|||||||
panel_icon: mdi:robot-happy
|
panel_icon: mdi:robot-happy
|
||||||
ports:
|
ports:
|
||||||
3001/tcp: null
|
3001/tcp: null
|
||||||
|
7681/tcp: null
|
||||||
8787/tcp: 8787
|
8787/tcp: 8787
|
||||||
ports_description:
|
ports_description:
|
||||||
3001/tcp: Web interface https
|
3001/tcp: Claude Desktop web interface
|
||||||
|
7681/tcp: HTTP Basic-auth Claude Code terminal (no TLS)
|
||||||
8787/tcp: Headroom dashboard and proxy
|
8787/tcp: Headroom dashboard and proxy
|
||||||
privileged:
|
privileged:
|
||||||
- SYS_ADMIN
|
- SYS_ADMIN
|
||||||
@@ -80,6 +86,10 @@ schema:
|
|||||||
ha_smart_context: bool?
|
ha_smart_context: bool?
|
||||||
enable_ha_mcp: bool?
|
enable_ha_mcp: bool?
|
||||||
dangerously_skip_permissions: bool?
|
dangerously_skip_permissions: bool?
|
||||||
|
enable_terminal: bool?
|
||||||
|
terminal_username: match(^[A-Za-z0-9_.-]+$)?
|
||||||
|
terminal_password: password?
|
||||||
|
terminal_workspace: str?
|
||||||
github_token: password?
|
github_token: password?
|
||||||
github_username: str?
|
github_username: str?
|
||||||
install_caveman: bool
|
install_caveman: bool
|
||||||
@@ -90,5 +100,5 @@ slug: claude_desktop
|
|||||||
tmpfs: true
|
tmpfs: true
|
||||||
udev: true
|
udev: true
|
||||||
url: https://github.com/alexbelgium/hassio-addons
|
url: https://github.com/alexbelgium/hassio-addons
|
||||||
version: "1.12"
|
version: "1.13"
|
||||||
video: true
|
video: true
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ if [ -s "$CLAUDE_DESKTOP_COMMAND_FILE" ]; then
|
|||||||
else
|
else
|
||||||
CLAUDE_DESKTOP_COMMAND="$DEFAULT_CLAUDE_DESKTOP_COMMAND"
|
CLAUDE_DESKTOP_COMMAND="$DEFAULT_CLAUDE_DESKTOP_COMMAND"
|
||||||
fi
|
fi
|
||||||
if bashio::config.true 'install_headroom'; then
|
|
||||||
export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
|
# Headroom is intentionally not injected into the Desktop process. Claude Desktop overrides
|
||||||
fi
|
# ANTHROPIC_BASE_URL, so Desktop uses the registered Headroom MCP tools instead. The Claude Code
|
||||||
|
# terminal uses the supported `headroom wrap claude` integration through claude-headroom.
|
||||||
|
|
||||||
# Launch the configured command. If a custom/wrapped command fails to start, fall back to
|
# Launch the configured command. If a custom/wrapped command fails to start, fall back to
|
||||||
# the plain Claude Desktop launch so the desktop app still comes up for the user.
|
# the plain Claude Desktop launch so the app always comes up for the user.
|
||||||
if ! sh -c "$CLAUDE_DESKTOP_COMMAND"; then
|
if ! sh -c "$CLAUDE_DESKTOP_COMMAND"; then
|
||||||
if [ "$CLAUDE_DESKTOP_COMMAND" != "$DEFAULT_CLAUDE_DESKTOP_COMMAND" ]; then
|
if [ "$CLAUDE_DESKTOP_COMMAND" != "$DEFAULT_CLAUDE_DESKTOP_COMMAND" ]; then
|
||||||
echo "autostart: '$CLAUDE_DESKTOP_COMMAND' failed; falling back to default Claude Desktop launch" >&2
|
echo "autostart: '$CLAUDE_DESKTOP_COMMAND' failed; falling back to default Claude Desktop launch" >&2
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
set -e
|
set -e
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
|
PUID="$(if bashio::config.has_value 'PUID'; then bashio::config 'PUID'; else echo '0'; fi)"
|
||||||
|
PGID="$(if bashio::config.has_value 'PGID'; then bashio::config 'PGID'; else echo '0'; fi)"
|
||||||
mkdir -p "$HOME/.claude"
|
mkdir -p "$HOME/.claude"
|
||||||
|
|
||||||
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
|
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
|
||||||
@@ -138,24 +141,66 @@ PY
|
|||||||
else
|
else
|
||||||
bashio::log.warning "rtk is not available"
|
bashio::log.warning "rtk is not available"
|
||||||
fi
|
fi
|
||||||
elif [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk' "$HOME/.claude/settings.json"; then
|
elif [ -f "$HOME/.claude/settings.json" ]; then
|
||||||
bashio::log.info "Removing rtk Claude Code hook"
|
bashio::log.info "Removing the add-on-managed rtk Claude Code hook"
|
||||||
python3 - <<'PY' || bashio::log.warning "Unable to remove rtk hook automatically"
|
python3 - <<'PY' || bashio::log.warning "Unable to remove rtk hook automatically"
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
path = Path.home() / ".claude" / "settings.json"
|
path = Path.home() / ".claude" / "settings.json"
|
||||||
data = json.loads(path.read_text())
|
data = json.loads(path.read_text())
|
||||||
hooks = data.get("hooks", {})
|
if not isinstance(data, dict):
|
||||||
for event, entries in list(hooks.items()):
|
raise TypeError("Claude settings must contain a JSON object")
|
||||||
if isinstance(entries, list):
|
|
||||||
hooks[event] = [entry for entry in entries if "rtk" not in json.dumps(entry)]
|
hooks = data.get("hooks")
|
||||||
if not hooks[event]:
|
if not isinstance(hooks, dict):
|
||||||
hooks.pop(event, None)
|
raise SystemExit(0)
|
||||||
if hooks:
|
|
||||||
data["hooks"] = hooks
|
entries = hooks.get("PreToolUse")
|
||||||
else:
|
if not isinstance(entries, list):
|
||||||
data.pop("hooks", None)
|
raise SystemExit(0)
|
||||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
|
||||||
|
changed = False
|
||||||
|
filtered_entries = []
|
||||||
|
for entry in entries:
|
||||||
|
if not isinstance(entry, dict) or entry.get("matcher") != "Bash":
|
||||||
|
filtered_entries.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
|
commands = entry.get("hooks")
|
||||||
|
if not isinstance(commands, list):
|
||||||
|
filtered_entries.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
|
filtered_commands = [
|
||||||
|
command
|
||||||
|
for command in commands
|
||||||
|
if not (
|
||||||
|
isinstance(command, dict)
|
||||||
|
and command.get("type") == "command"
|
||||||
|
and command.get("command") == "rtk hook claude"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if len(filtered_commands) == len(commands):
|
||||||
|
filtered_entries.append(entry)
|
||||||
|
continue
|
||||||
|
|
||||||
|
changed = True
|
||||||
|
if filtered_commands:
|
||||||
|
updated_entry = dict(entry)
|
||||||
|
updated_entry["hooks"] = filtered_commands
|
||||||
|
filtered_entries.append(updated_entry)
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
if filtered_entries:
|
||||||
|
hooks["PreToolUse"] = filtered_entries
|
||||||
|
else:
|
||||||
|
hooks.pop("PreToolUse", None)
|
||||||
|
if hooks:
|
||||||
|
data["hooks"] = hooks
|
||||||
|
else:
|
||||||
|
data.pop("hooks", None)
|
||||||
|
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||||
PY
|
PY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -170,3 +215,11 @@ else
|
|||||||
bashio::log.info "Disabling caveman Claude Code plugin"
|
bashio::log.info "Disabling caveman Claude Code plugin"
|
||||||
find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -exec rm -rf {} + 2> /dev/null || true
|
find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -exec rm -rf {} + 2> /dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Startup configuration runs as root, while Claude Desktop and the web terminal run as abc.
|
||||||
|
# Return managed persistent files to the configured runtime UID/GID after all writes complete.
|
||||||
|
for managed_path in "$HOME/.claude" "$HOME/.config/Claude"; do
|
||||||
|
if [ -e "$managed_path" ]; then
|
||||||
|
chown -R -- "${PUID}:${PGID}" "$managed_path" || bashio::log.warning "Unable to set ownership on $managed_path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
declare port=7681
|
||||||
|
declare username
|
||||||
|
declare password=""
|
||||||
|
declare workspace
|
||||||
|
declare canonical_workspace
|
||||||
|
|
||||||
|
export PATH="${HOME:-/data/data}/.local/bin:/usr/local/bin:/usr/bin:/bin:${PATH:-}"
|
||||||
|
|
||||||
|
if bashio::config.has_value 'enable_terminal' && ! bashio::config.true 'enable_terminal'; then
|
||||||
|
bashio::log.info "svc-claude-terminal: terminal disabled; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${HOME:-}" ]; then
|
||||||
|
bashio::log.error "svc-claude-terminal: HOME is not initialized; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v ttyd >/dev/null 2>&1 || ! command -v tmux >/dev/null 2>&1 || ! command -v claude >/dev/null 2>&1; then
|
||||||
|
bashio::log.error "svc-claude-terminal: ttyd, tmux, or Claude Code is missing; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
username="claude"
|
||||||
|
if bashio::config.has_value 'terminal_username'; then
|
||||||
|
username="$(bashio::config 'terminal_username')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if bashio::config.has_value 'terminal_password'; then
|
||||||
|
password="$(bashio::config 'terminal_password')"
|
||||||
|
elif bashio::config.has_value 'PASSWORD'; then
|
||||||
|
bashio::log.warning "svc-claude-terminal: using PASSWORD as fallback for terminal authentication; prefer a unique terminal_password"
|
||||||
|
password="$(bashio::config 'PASSWORD')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$password" ]; then
|
||||||
|
bashio::log.warning "svc-claude-terminal: set terminal_password (or PASSWORD) before mapping port ${port}; terminal will remain disabled"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
workspace="${HOME}/workspace"
|
||||||
|
if bashio::config.has_value 'terminal_workspace'; then
|
||||||
|
workspace="$(bashio::config 'terminal_workspace')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$workspace" != /* ]]; then
|
||||||
|
bashio::log.error "svc-claude-terminal: terminal_workspace must be an absolute path; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -L "$workspace" ]; then
|
||||||
|
bashio::log.error "svc-claude-terminal: terminal_workspace must not be a symbolic link; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! canonical_workspace="$(realpath -m -- "$workspace")"; then
|
||||||
|
bashio::log.error "svc-claude-terminal: unable to resolve terminal_workspace '$workspace'; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
workspace="$canonical_workspace"
|
||||||
|
|
||||||
|
case "$workspace" in
|
||||||
|
"$HOME" | "$HOME"/* | /share/* | /media/* | /mnt/* | /data/* | /config/*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
bashio::log.error "svc-claude-terminal: terminal_workspace must be the configured data_location or a subdirectory of /share, /media, /mnt, /data, or /config; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ ! -e "$workspace" ]; then
|
||||||
|
if ! install -d -m 0750 -o abc -g abc -- "$workspace"; then
|
||||||
|
bashio::log.error "svc-claude-terminal: failed to create workspace '$workspace'; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
elif [ ! -d "$workspace" ]; then
|
||||||
|
bashio::log.error "svc-claude-terminal: terminal_workspace '$workspace' is not a directory; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! s6-setuidgid abc test -r "$workspace" ||
|
||||||
|
! s6-setuidgid abc test -w "$workspace" ||
|
||||||
|
! s6-setuidgid abc test -x "$workspace"; then
|
||||||
|
bashio::log.error "svc-claude-terminal: workspace '$workspace' must be readable, writable, and searchable by user abc; idling"
|
||||||
|
exec sleep infinity
|
||||||
|
fi
|
||||||
|
|
||||||
|
export CLAUDE_TERMINAL_WORKSPACE="$workspace"
|
||||||
|
|
||||||
|
bashio::log.info "svc-claude-terminal: starting authenticated ttyd terminal on port ${port}; workspace=${workspace}"
|
||||||
|
exec s6-setuidgid abc ttyd \
|
||||||
|
-p "$port" \
|
||||||
|
-W \
|
||||||
|
-O \
|
||||||
|
-c "${username}:${password}" \
|
||||||
|
/usr/local/bin/claude-terminal-shell
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
#!/usr/bin/with-contenv bashio
|
||||||
# Headroom optimization proxy — runs ONLY as a local backend for the headroom MCP tools
|
# Headroom optimization proxy — local backend for Claude Desktop MCP and Claude Code.
|
||||||
# (headroom_compress / headroom_retrieve, registered in 82-claude_tools.sh). It is NOT a
|
|
||||||
# traffic router: no client's ANTHROPIC_BASE_URL is pointed at it, so the Claude Desktop
|
|
||||||
# Electron app (which force-overrides ANTHROPIC_BASE_URL — headroom #869) and every other
|
|
||||||
# client are unaffected. Without this backend the MCP tools cannot store/retrieve compressed
|
|
||||||
# content and no savings are ever recorded.
|
|
||||||
declare port=8787
|
declare port=8787
|
||||||
declare host=127.0.0.1
|
declare host=127.0.0.1
|
||||||
|
|
||||||
if bashio::config.true 'install_headroom' && command -v headroom >/dev/null 2>&1; then
|
if bashio::config.true 'install_headroom' && command -v headroom >/dev/null 2>&1; then
|
||||||
bashio::log.info "svc-headroom: starting headroom proxy on ${host}:${port}; dashboard available at /dashboard when the port is mapped (MCP backend only; no client routing)"
|
bashio::log.info "svc-headroom: starting local Headroom proxy on ${host}:${port}"
|
||||||
exec headroom proxy --host "${host}" --port "${port}" --code-aware
|
exec s6-setuidgid abc headroom proxy --host "${host}" --port "${port}" --code-aware
|
||||||
fi
|
fi
|
||||||
|
|
||||||
bashio::log.info "svc-headroom: install_headroom disabled or headroom not found; idling"
|
bashio::log.info "svc-headroom: install_headroom disabled or headroom not found; idling"
|
||||||
|
|||||||
4
claude_desktop/rootfs/usr/local/bin/claude-direct
Normal file
4
claude_desktop/rootfs/usr/local/bin/claude-direct
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
exec claude "$@"
|
||||||
21
claude_desktop/rootfs/usr/local/bin/claude-headroom
Normal file
21
claude_desktop/rootfs/usr/local/bin/claude-headroom
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if ! command -v claude >/dev/null 2>&1; then
|
||||||
|
echo "Claude Code is not installed or is not on PATH." >&2
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v headroom >/dev/null 2>&1; then
|
||||||
|
echo "Headroom is unavailable; start Claude Code directly with claude-direct." >&2
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! curl -fsS --max-time 3 "http://127.0.0.1:8787/readyz" >/dev/null; then
|
||||||
|
echo "The supervised Headroom proxy is not ready on 127.0.0.1:8787. Ensure install_headroom is enabled and check the add-on log." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reuse the s6-supervised proxy instead of starting a competing proxy. RTK is already managed
|
||||||
|
# through the persistent Claude Code PreToolUse hook, so Headroom must not reinstall it.
|
||||||
|
exec headroom wrap claude --port 8787 --no-proxy --no-rtk -- "$@"
|
||||||
24
claude_desktop/rootfs/usr/local/bin/claude-terminal-shell
Normal file
24
claude_desktop/rootfs/usr/local/bin/claude-terminal-shell
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ -z "${HOME:-}" ]; then
|
||||||
|
echo "Claude terminal: HOME is not initialized." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export SHELL="/bin/bash"
|
||||||
|
export PATH="${HOME}/.local/bin:/usr/local/bin:/usr/bin:/bin:${PATH:-}"
|
||||||
|
|
||||||
|
workspace="${CLAUDE_TERMINAL_WORKSPACE:-${HOME}/workspace}"
|
||||||
|
session_name="${CLAUDE_TMUX_SESSION:-claude}"
|
||||||
|
|
||||||
|
if [ ! -d "$workspace" ]; then
|
||||||
|
echo "Claude terminal: workspace does not exist: $workspace" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd -- "$workspace"
|
||||||
|
|
||||||
|
# Reattach every browser connection to the same terminal session. Closing the browser detaches
|
||||||
|
# the client but leaves Claude Code and other commands running inside tmux.
|
||||||
|
exec tmux new-session -A -s "$session_name" -c "$workspace"
|
||||||
Reference in New Issue
Block a user