mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-07-20 04:50:31 +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)
|
||||
- Minor bugs fixed
|
||||
## 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
|
||||
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
|
||||
# hadolint ignore=DL4005
|
||||
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
|
||||
|
||||
# Install Claude Desktop and Python tooling
|
||||
RUN curl -fsSLo /usr/share/keyrings/claude-desktop-archive-keyring.asc https://downloads.claude.ai/claude-desktop/key.asc && \
|
||||
# Install Claude Desktop, Claude Code, browser-terminal tooling, and Python tooling
|
||||
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 [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 install -y --no-install-recommends \
|
||||
claude-desktop \
|
||||
claude-code \
|
||||
python3-pip \
|
||||
libsecret-1-0 \
|
||||
dbus-x11 \
|
||||
git gh && \
|
||||
git \
|
||||
gh \
|
||||
ripgrep \
|
||||
tmux && \
|
||||
apt-get clean && \
|
||||
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
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends nodejs && \
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
![Supports amd64 Architecture][amd64-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
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
- Claude Desktop in single-app Selkies mode.
|
||||
- Home Assistant ingress support.
|
||||
- Persistent `$HOME` under `/data/data`, preserving Claude Desktop and Claude Code sign-in state across restarts.
|
||||
- Home Assistant ingress support for Claude Desktop.
|
||||
- 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 extra apt and pip package installation.
|
||||
- Baked-in `git` and GitHub CLI (`gh`) with optional startup credential configuration.
|
||||
- Custom script support through the repository standard `claude_desktop.sh` script.
|
||||
- Optional bundled Claude Code optimization tools: headroom, rtk, and caveman.
|
||||
- Headroom dashboard exposed on mapped port `8787` when `install_headroom` is enabled.
|
||||
- Low-power defaults: GPU device mapping, `AUTO_GPU=1`, `SELKIES_FRAMERATE=30`, `/tmp` tmpfs, and `$HOME/.cache` redirected to `/tmp/cache`.
|
||||
- Baked-in `git`, GitHub CLI (`gh`), `ripgrep`, and terminal tooling.
|
||||
- Custom script support through the repository standard `claude_desktop.sh`.
|
||||
- Optional bundled Claude Code optimization tools: Headroom, RTK, and Caveman.
|
||||
- Headroom dashboard exposed on mapped port `8787` when enabled.
|
||||
- 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
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------ | ------- | ----------- |
|
||||
| `PUID` / `PGID` | `0` / `0` | User and group used for persistent data ownership. |
|
||||
| `TZ` | | Optional timezone, for example `America/New_York`. |
|
||||
| `PUID` / `PGID` | `0` / `0` | Numeric user and group applied by the LinuxServer initialization. |
|
||||
| `TZ` | | Optional timezone, for example `Europe/Brussels`. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `install_rtk` | `true` | Configure the rtk Claude Code `PreToolUse` hook in the persistent Claude Code settings. |
|
||||
| `install_caveman` | `true` | Install the caveman Claude Code plugin into the persistent Claude Code home. |
|
||||
| `install_github_cli` | `true` | Enable first-start checks and setup for the baked-in `git` and `gh` commands. |
|
||||
| `github_token` | | Optional GitHub personal access token used to authenticate `gh` and configure Git credentials for GitHub. |
|
||||
| `auto_update` | `true` | Upgrade `claude-desktop` from Anthropic's apt repository at startup. |
|
||||
| `enable_terminal` | `true` | Enable the supervised Claude Code web-terminal service. |
|
||||
| `terminal_username` | `claude` | Username used by ttyd Basic Authentication. |
|
||||
| `terminal_password` | | Dedicated terminal password. The service idles when this and `PASSWORD` are empty. |
|
||||
| `terminal_workspace` | | Initial directory; defaults to `<data_location>/workspace`. |
|
||||
| `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_email` | | Optional global Git author email. |
|
||||
| `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. |
|
||||
| `dangerously_skip_permissions` | `false` | Expose Claude Code's dangerous permission skip mode option. Only enable if you understand the risk. |
|
||||
| `additional_apps` | | Comma-separated Debian apt packages to install at startup, for example `htop,git`. |
|
||||
| `additional_pip` | | Comma-separated pip packages to install at startup. Installs use `--break-system-packages`. |
|
||||
| `data_location` | `/data/data` | Persistent home directory location. Keep this persistent so Claude sign-in survives restarts. |
|
||||
| `networkdisks`, `cifsusername`, `cifspassword`, `cifsdomain` | | Standard SMB mount options. |
|
||||
| `localdisks` | | Standard local disk mount option. |
|
||||
| `env_vars` | `[]` | Extra environment variables to export into the container. This can override `SELKIES_*` defaults. |
|
||||
| `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. |
|
||||
| `additional_pip` | | Comma-separated pip packages installed with `--break-system-packages`. |
|
||||
| `data_location` | `/data/data` | Persistent home directory for both Claude clients and tooling. |
|
||||
| `env_vars` | `[]` | Additional environment variables exported inside the container. |
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
|
||||
|
||||
@@ -2,7 +2,7 @@ arch:
|
||||
- aarch64
|
||||
- amd64
|
||||
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:
|
||||
- /dev/dri
|
||||
- /dev/dri/card0
|
||||
@@ -44,6 +44,10 @@ options:
|
||||
ha_smart_context: true
|
||||
enable_ha_mcp: true
|
||||
dangerously_skip_permissions: false
|
||||
enable_terminal: true
|
||||
terminal_username: claude
|
||||
terminal_password: ""
|
||||
terminal_workspace: ""
|
||||
github_token: ""
|
||||
github_username: ""
|
||||
install_caveman: true
|
||||
@@ -54,9 +58,11 @@ panel_admin: false
|
||||
panel_icon: mdi:robot-happy
|
||||
ports:
|
||||
3001/tcp: null
|
||||
7681/tcp: null
|
||||
8787/tcp: 8787
|
||||
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
|
||||
privileged:
|
||||
- SYS_ADMIN
|
||||
@@ -80,6 +86,10 @@ schema:
|
||||
ha_smart_context: bool?
|
||||
enable_ha_mcp: 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_username: str?
|
||||
install_caveman: bool
|
||||
@@ -90,5 +100,5 @@ slug: claude_desktop
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.12"
|
||||
version: "1.13"
|
||||
video: true
|
||||
|
||||
@@ -20,12 +20,13 @@ if [ -s "$CLAUDE_DESKTOP_COMMAND_FILE" ]; then
|
||||
else
|
||||
CLAUDE_DESKTOP_COMMAND="$DEFAULT_CLAUDE_DESKTOP_COMMAND"
|
||||
fi
|
||||
if bashio::config.true 'install_headroom'; then
|
||||
export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
|
||||
fi
|
||||
|
||||
# Headroom is intentionally not injected into the Desktop process. Claude Desktop overrides
|
||||
# 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
|
||||
# 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 [ "$CLAUDE_DESKTOP_COMMAND" != "$DEFAULT_CLAUDE_DESKTOP_COMMAND" ]; then
|
||||
echo "autostart: '$CLAUDE_DESKTOP_COMMAND' failed; falling back to default Claude Desktop launch" >&2
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
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"
|
||||
|
||||
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
|
||||
@@ -138,24 +141,66 @@ PY
|
||||
else
|
||||
bashio::log.warning "rtk is not available"
|
||||
fi
|
||||
elif [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk' "$HOME/.claude/settings.json"; then
|
||||
bashio::log.info "Removing rtk Claude Code hook"
|
||||
elif [ -f "$HOME/.claude/settings.json" ]; then
|
||||
bashio::log.info "Removing the add-on-managed rtk Claude Code hook"
|
||||
python3 - <<'PY' || bashio::log.warning "Unable to remove rtk hook automatically"
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path.home() / ".claude" / "settings.json"
|
||||
data = json.loads(path.read_text())
|
||||
hooks = data.get("hooks", {})
|
||||
for event, entries in list(hooks.items()):
|
||||
if isinstance(entries, list):
|
||||
hooks[event] = [entry for entry in entries if "rtk" not in json.dumps(entry)]
|
||||
if not hooks[event]:
|
||||
hooks.pop(event, None)
|
||||
if hooks:
|
||||
data["hooks"] = hooks
|
||||
else:
|
||||
data.pop("hooks", None)
|
||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||
if not isinstance(data, dict):
|
||||
raise TypeError("Claude settings must contain a JSON object")
|
||||
|
||||
hooks = data.get("hooks")
|
||||
if not isinstance(hooks, dict):
|
||||
raise SystemExit(0)
|
||||
|
||||
entries = hooks.get("PreToolUse")
|
||||
if not isinstance(entries, list):
|
||||
raise SystemExit(0)
|
||||
|
||||
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
|
||||
fi
|
||||
|
||||
@@ -170,3 +215,11 @@ else
|
||||
bashio::log.info "Disabling caveman Claude Code plugin"
|
||||
find "$HOME/.claude" -maxdepth 4 -iname '*caveman*' -exec rm -rf {} + 2> /dev/null || true
|
||||
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
|
||||
# Headroom optimization proxy — runs ONLY as a local backend for the headroom MCP tools
|
||||
# (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.
|
||||
# Headroom optimization proxy — local backend for Claude Desktop MCP and Claude Code.
|
||||
declare port=8787
|
||||
declare host=127.0.0.1
|
||||
|
||||
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)"
|
||||
exec headroom proxy --host "${host}" --port "${port}" --code-aware
|
||||
bashio::log.info "svc-headroom: starting local Headroom proxy on ${host}:${port}"
|
||||
exec s6-setuidgid abc headroom proxy --host "${host}" --port "${port}" --code-aware
|
||||
fi
|
||||
|
||||
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