Compare commits
205 Commits
fix/elasti
...
feat/claud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d11bfe8e1 | ||
|
|
587121cb1b | ||
|
|
29f2cfd198 | ||
|
|
3329c4b40d | ||
|
|
9471dffcca | ||
|
|
215327e439 | ||
|
|
8ecbfccdcd | ||
|
|
5a2efc4f9e | ||
|
|
ef0aecbde6 | ||
|
|
7a6ad72617 | ||
|
|
b5985f230e | ||
|
|
d48d1f4d8d | ||
|
|
6bf379bffc | ||
|
|
5d577ad954 | ||
|
|
85dab2ae6e | ||
|
|
ad50abc365 | ||
|
|
dc280a5caf | ||
|
|
c0c1df8c27 | ||
|
|
a38bc75f95 | ||
|
|
8073317150 | ||
|
|
15505a265f | ||
|
|
d1ceafebe8 | ||
|
|
c76b257f5d | ||
|
|
321f2fde73 | ||
|
|
45f88307d4 | ||
|
|
7cd82b2758 | ||
|
|
66d3886b80 | ||
|
|
bdd56b8057 | ||
|
|
9c55193c38 | ||
|
|
6b6233e1fa | ||
|
|
22951ac4f5 | ||
|
|
006f3052ac | ||
|
|
0ff2e8f783 | ||
|
|
3c8a32199e | ||
|
|
c8d706c9de | ||
|
|
c04f3e288c | ||
|
|
f6c2bef0be | ||
|
|
f8c447ed19 | ||
|
|
bd82e72ffa | ||
|
|
a3ecb38fea | ||
|
|
eada7a0ba0 | ||
|
|
e7921b822e | ||
|
|
aa1486c1d8 | ||
|
|
b4008c4db9 | ||
|
|
fa2328f741 | ||
|
|
e8bb55682b | ||
|
|
7fdf95940b | ||
|
|
583c5e655a | ||
|
|
b56be1f57d | ||
|
|
50150a4775 | ||
|
|
6814088369 | ||
|
|
2951b04f57 | ||
|
|
245c52fcf2 | ||
|
|
2b6c9eace7 | ||
|
|
fd268d0e6d | ||
|
|
5792d84336 | ||
|
|
bf97abdb4f | ||
|
|
7de4789196 | ||
|
|
34e8a75bf8 | ||
|
|
652be8b13c | ||
|
|
78f5289518 | ||
|
|
12384ee606 | ||
|
|
df0dacf6b9 | ||
|
|
2cbf2a6f08 | ||
|
|
adcd892e62 | ||
|
|
980be49d9b | ||
|
|
9cff4f83b9 | ||
|
|
688d1cbdcf | ||
|
|
02bbfa86c3 | ||
|
|
a5916d9236 | ||
|
|
371c206fc0 | ||
|
|
75ce8e94e4 | ||
|
|
0b7b0ac1df | ||
|
|
2f4ab956db | ||
|
|
08a7bd35cb | ||
|
|
a313475d92 | ||
|
|
087d23eeaf | ||
|
|
a9a6b22a62 | ||
|
|
b16305a9e6 | ||
|
|
34135ba471 | ||
|
|
ab02e92ad1 | ||
|
|
6f93187535 | ||
|
|
a90fe9ee4a | ||
|
|
6bb4cb735d | ||
|
|
299c97c83f | ||
|
|
ad5eba5657 | ||
|
|
bd6daa32b7 | ||
|
|
7df08e4f84 | ||
|
|
b8421be1e9 | ||
|
|
30f99d738b | ||
|
|
d44b38981e | ||
|
|
c6c5197eb8 | ||
|
|
a966369e96 | ||
|
|
0363265d84 | ||
|
|
bb2146866f | ||
|
|
21b9783fe2 | ||
|
|
8162d42e41 | ||
|
|
4b848c9054 | ||
|
|
731f099c8c | ||
|
|
e56dcf7abd | ||
|
|
e25b4ec1d3 | ||
|
|
9161c28e27 | ||
|
|
82af311c37 | ||
|
|
85dd1669fa | ||
|
|
98962ac296 | ||
|
|
6d199b146b | ||
|
|
b0750a8a7f | ||
|
|
ef9f76e91c | ||
|
|
8bc048b993 | ||
|
|
26533b6697 | ||
|
|
3eafa3e028 | ||
|
|
9173829335 | ||
|
|
6b50abe96d | ||
|
|
9a4f384d5a | ||
|
|
521d555615 | ||
|
|
a5bba509ef | ||
|
|
246a7ce157 | ||
|
|
44f576699d | ||
|
|
afe45b7a75 | ||
|
|
796a4132bf | ||
|
|
e17cc922f9 | ||
|
|
cedccabef2 | ||
|
|
c8a52ba915 | ||
|
|
2468d11400 | ||
|
|
3ad3b1d1ba | ||
|
|
3c5212b4ef | ||
|
|
289b3b53f1 | ||
|
|
19c3cf8252 | ||
|
|
dc206213ca | ||
|
|
5755df5181 | ||
|
|
0b6c410001 | ||
|
|
2a498af497 | ||
|
|
b620907a6a | ||
|
|
98629f7214 | ||
|
|
769cfc278e | ||
|
|
9d21c49fa5 | ||
|
|
49d78a32d5 | ||
|
|
5208dd3ce9 | ||
|
|
055f6e58f3 | ||
|
|
e2bec544cb | ||
|
|
23f756b4db | ||
|
|
efa5ed9b59 | ||
|
|
82c784ce6e | ||
|
|
83b791e6c3 | ||
|
|
c3a8e1d57b | ||
|
|
382de98a9b | ||
|
|
ca6c6a54d3 | ||
|
|
57d8edb445 | ||
|
|
d9252da5a3 | ||
|
|
deb51a0bed | ||
|
|
dbaa8a8a98 | ||
|
|
4cf79224f7 | ||
|
|
90fd373c2c | ||
|
|
e930e36e0d | ||
|
|
35c3077bec | ||
|
|
06e0a32f81 | ||
|
|
22f9490b77 | ||
|
|
72851eea95 | ||
|
|
2bc142b5fe | ||
|
|
3e5649f00f | ||
|
|
8c7a43b5be | ||
|
|
e7a6add76f | ||
|
|
0d56b4dded | ||
|
|
7ee213406d | ||
|
|
e6210d484d | ||
|
|
71c67e7eef | ||
|
|
2aaf940b73 | ||
|
|
842c237965 | ||
|
|
8788211f8a | ||
|
|
ea3e5e425b | ||
|
|
554d7dc04f | ||
|
|
29ca739709 | ||
|
|
e8260efe37 | ||
|
|
682a96fc8e | ||
|
|
22bbbfdf75 | ||
|
|
481094574d | ||
|
|
534a526185 | ||
|
|
b98ae69a6c | ||
|
|
af68eece53 | ||
|
|
c8f5ec8f20 | ||
|
|
aa77249314 | ||
|
|
6e02e611f4 | ||
|
|
91ed6c3f46 | ||
|
|
b21ce72e0e | ||
|
|
839cfd1382 | ||
|
|
ae22240269 | ||
|
|
91429643ef | ||
|
|
507ea9fb8c | ||
|
|
434aa76c2a | ||
|
|
397a6eed88 | ||
|
|
859e47772a | ||
|
|
8153a944d8 | ||
|
|
6f143deb49 | ||
|
|
0fc95bfe88 | ||
|
|
0c651312ab | ||
|
|
6b4c11cbd8 | ||
|
|
424573548e | ||
|
|
effd7f4319 | ||
|
|
69458eb13d | ||
|
|
2bbc48f7ab | ||
|
|
94135b9b05 | ||
|
|
7b3eb17efd | ||
|
|
5d7d6ff334 | ||
|
|
bd9f4f3d23 | ||
|
|
918b9477b0 |
10
.github/stargazer_countries.csv
vendored
@@ -162,6 +162,7 @@ DARKNAGAN,France
|
||||
DDanii,
|
||||
DMurzNN,
|
||||
DUC750,
|
||||
DY-hub,
|
||||
DaFlowah,
|
||||
DaJonas94,
|
||||
Daafip,Netherlands
|
||||
@@ -238,6 +239,7 @@ EtienneMD,
|
||||
Evel270,
|
||||
Everestlion,
|
||||
EvertJob,
|
||||
Exlatis,
|
||||
Extrunder,
|
||||
F0264,
|
||||
F4bsi,Germany
|
||||
@@ -563,6 +565,7 @@ PhoenixTwoFive,Germany
|
||||
PhysShell,
|
||||
PierreNa,France
|
||||
PietroSpina,
|
||||
Pingmin,
|
||||
PiotrKrzyzek,United States
|
||||
PiratesGhost,
|
||||
Pixelzeus,
|
||||
@@ -897,10 +900,12 @@ antorimba,
|
||||
antx-code,
|
||||
anyezhe,
|
||||
aorosora,
|
||||
araminimichael,
|
||||
arbal,United States
|
||||
ardemk,
|
||||
ared469,
|
||||
arethefreshest,
|
||||
arozoire,
|
||||
arpit-mehra,
|
||||
artemave,France
|
||||
artemdanielov,
|
||||
@@ -1156,6 +1161,7 @@ danbruno,
|
||||
danctrl,Germany
|
||||
danez,United States
|
||||
danieldotnl,Netherlands
|
||||
danishru,
|
||||
dannybeeckman,
|
||||
dannybloomfield,United States
|
||||
danveitch76,
|
||||
@@ -1744,6 +1750,7 @@ ljsquare,
|
||||
llabourdeth,
|
||||
llewy,
|
||||
llfjahn,
|
||||
llugo,
|
||||
lmalmoreno,Brazil
|
||||
lnrdmx,
|
||||
loc4t3llix,
|
||||
@@ -2050,6 +2057,7 @@ pedrolicassali,
|
||||
pedromfa,
|
||||
pedrware,Portugal
|
||||
peeetek,
|
||||
peggleg,
|
||||
pejannl,
|
||||
pem884,United States
|
||||
pepelatc,
|
||||
@@ -2273,6 +2281,7 @@ skalingclouds,United States
|
||||
skamaleo,
|
||||
skavieller,
|
||||
skipper00,
|
||||
skoducks,United States
|
||||
skylidefr,
|
||||
skynet-network,
|
||||
slimehands,
|
||||
@@ -2532,6 +2541,7 @@ williamcorsel,Netherlands
|
||||
willigenburggihaux,
|
||||
willnewcombe,United Kingdom
|
||||
wimb0,
|
||||
wingerasc,
|
||||
witold-gren,Poland
|
||||
wonderfulhuber,
|
||||
wonkygecko,United States
|
||||
|
||||
|
BIN
.github/stargazer_map.png
vendored
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 60 KiB |
BIN
.github/stats.png
vendored
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.8 KiB |
BIN
.github/stats_addons.png
vendored
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 4.4 KiB |
2
.github/workflows/onpr_check-pr.yaml
vendored
@@ -35,6 +35,7 @@ jobs:
|
||||
git fetch origin "${{ github.event.before }}" || true
|
||||
changed_changelog_files=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" | grep -iE '^([^/]+/)?changelog\.(md|txt|ya?ml|json)$' || true)
|
||||
echo "$changed_changelog_files"
|
||||
echo "changelogs_files=$changed_changelog_files" >> "$GITHUB_OUTPUT"
|
||||
changed_config_files=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" | grep -E '^[^/]+/config\.(json|ya?ml)$' || true)
|
||||
echo "$changed_config_files"
|
||||
all_changed_files=$(echo -e "$changed_config_files\n$changed_changelog_files" | sort -u)
|
||||
@@ -208,6 +209,7 @@ jobs:
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ${{ matrix.addon }}
|
||||
platforms: linux/arm64
|
||||
push: false
|
||||
load: true
|
||||
file: ${{ matrix.addon }}/Dockerfile
|
||||
|
||||
3
.github/workflows/onpush_builder.yaml
vendored
@@ -300,7 +300,7 @@ jobs:
|
||||
|
||||
- name: Build ${{ matrix.addon }} add-on
|
||||
if: steps.info.outputs.build_arch == 'true' && steps.info.outputs.has_dockerfile == 'true'
|
||||
uses: home-assistant/builder/actions/build-image@2026.03.2
|
||||
uses: home-assistant/builder/actions/build-image@2026.06.0
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
cache-gha: "false"
|
||||
@@ -433,4 +433,3 @@ jobs:
|
||||
done
|
||||
|
||||
git push origin HEAD:master
|
||||
|
||||
|
||||
51
.github/workflows/pr2841-diagnostic.yml
vendored
@@ -1,51 +0,0 @@
|
||||
---
|
||||
name: Claude Add-on Build Diagnostic
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
diagnose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Gather add-on information
|
||||
id: information
|
||||
uses: frenck/action-addon-information@v1.4
|
||||
with:
|
||||
path: ./claude_desktop/
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Build aarch64 and capture tail
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
BUILD_FROM="$(jq -r '.build_from.aarch64 // empty' '${{ steps.information.outputs.build }}')"
|
||||
docker buildx build \
|
||||
--platform linux/arm64 \
|
||||
--progress=plain \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--file claude_desktop/Dockerfile \
|
||||
claude_desktop >build.log 2>&1
|
||||
status=$?
|
||||
tail -n 300 build.log >build-tail.log
|
||||
printf 'build_status=%s\n' "$status" >build-status.txt
|
||||
exit 0
|
||||
|
||||
- name: Upload diagnostic log
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: claude-aarch64-build-log
|
||||
path: |
|
||||
build-tail.log
|
||||
build-status.txt
|
||||
retention-days: 1
|
||||
36
README.md
@@ -56,19 +56,19 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
|
||||
### Number of addons
|
||||
|
||||
- In the repository : 135
|
||||
- Installed : 508490
|
||||
- In the repository : 136
|
||||
- Installed : 626324
|
||||
|
||||
### Top 3
|
||||
|
||||
1. Arpspoof (59486x)
|
||||
2. Sponsorblockcast (55582x)
|
||||
3. Flaresolverr (44486x)
|
||||
1. Arpspoof (86665x)
|
||||
2. Sponsorblockcast (82801x)
|
||||
3. Jellyfin (71957x)
|
||||
|
||||
### Architectures used
|
||||
|
||||
- amd64: 88%
|
||||
- aarch64: 12%
|
||||
- amd64: 90%
|
||||
- aarch64: 10%
|
||||
|
||||
### Stars evolution
|
||||
|
||||
@@ -77,7 +77,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
## Add-ons provided by this repository
|
||||
|
||||
%%ADDONS_LIST%%
|
||||
✓ [Arpspoof (59486x)](arpspoof/) : block internet connection for local network devices
|
||||
✓ [Arpspoof (86665x)](arpspoof/) : block internet connection for local network devices
|
||||
|
||||
  
|
||||

|
||||
@@ -143,6 +143,17 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![amd64][amd64-badge]
|
||||
![ingress][ingress-badge]
|
||||
|
||||
✓  [BirdNET-Pi (zach7036)](birdnet-pi-zach/) : Realtime acoustic bird classification system
|
||||
|
||||
  
|
||||

|
||||
![aarch64][aarch64-badge]
|
||||
![amd64][amd64-badge]
|
||||
![ingress][ingress-badge]
|
||||
![mqtt][mqtt-badge]
|
||||
![smb][smb-badge]
|
||||
![localdisks][localdisks-badge]
|
||||
|
||||
✓  [BirdNET-PiPy](birdnet-pipy/) : BirdNET-PiPy bird detection with a modern web dashboard
|
||||
|
||||
  
|
||||
@@ -247,7 +258,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![amd64][amd64-badge]
|
||||
![ingress][ingress-badge]
|
||||
|
||||
✓  [Claude Desktop](claude_desktop/) : Claude Desktop streamed through a browser with LinuxServer Selkies
|
||||
✓  [Claude Desktop](claude_desktop/) : Claude Desktop with Headroom MCP context compression and RTK acceleration
|
||||
|
||||
  
|
||||

|
||||
@@ -302,6 +313,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
✓ [Elasticsearch server](elasticsearch/) : Free and Open, Distributed, RESTful Search Engine
|
||||
|
||||
  
|
||||

|
||||
![aarch64][aarch64-badge]
|
||||
![amd64][amd64-badge]
|
||||
|
||||
@@ -508,7 +520,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![smb][smb-badge]
|
||||
![localdisks][localdisks-badge]
|
||||
|
||||
✓  [Jellyfin NAS](jellyfin/) : A free Software Media System that puts you in control of managing and streaming your media
|
||||
✓  [Jellyfin (71957x) NAS](jellyfin/) : A free Software Media System that puts you in control of managing and streaming your media
|
||||
|
||||
  
|
||||

|
||||
@@ -571,7 +583,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![aarch64][aarch64-badge]
|
||||
![amd64][amd64-badge]
|
||||
|
||||
✓  [Maintainerr](maintainerr/) : Rule-based media cleanup tool for Plex, Jellyfin and Emby. Creates collections and optionally deletes unwatched content.
|
||||
✓  [Maintainerr](maintainerr/) : Rule-based media cleanup tool for Plex, Jellyfin (71957x) and Emby. Creates collections and optionally deletes unwatched content.
|
||||
|
||||
  
|
||||

|
||||
@@ -885,7 +897,7 @@ If you want to do add the repository manually, please follow the procedure highl
|
||||
![smb][smb-badge]
|
||||
![localdisks][localdisks-badge]
|
||||
|
||||
✓  [Seerr](seerr/) : Open-source media request and discovery manager for Jellyfin, Plex, and Emby
|
||||
✓  [Seerr](seerr/) : Open-source media request and discovery manager for Jellyfin (71957x), Plex, and Emby
|
||||
|
||||
  
|
||||

|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.76.52 (2026-07-11)
|
||||
- Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases)
|
||||
|
||||
## 1.76.51 (2026-06-17)
|
||||
- Update to latest version from lklynet/aurral (changelog : https://github.com/lklynet/aurral/releases)
|
||||
## 1.76.49 (2026-06-05)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Aurral
|
||||
version: "1.76.51"
|
||||
version: "1.76.52"
|
||||
slug: aurral
|
||||
description: >-
|
||||
Self-hosted music discovery, request management, flows, and playlist
|
||||
|
||||
BIN
aurral/stats.png
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"last_update": "2026-06-17",
|
||||
"last_update": "2026-07-11",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "aurral",
|
||||
"source": "github",
|
||||
"upstream_repo": "lklynet/aurral",
|
||||
"upstream_version": "1.76.51",
|
||||
"upstream_version": "1.76.52",
|
||||
"github_beta": false
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
baikal/stats.png
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
bazarr/stats.png
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -1,3 +1,5 @@
|
||||
## source-20260714 (14-07-2026)
|
||||
- Minor bugs fixed
|
||||
## source-20260709 (09-07-2026)
|
||||
- Minor bugs fixed
|
||||
## source-20260708-4 (08-07-2026)
|
||||
|
||||
@@ -127,5 +127,5 @@ slug: birdnet-go-dev
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
usb: true
|
||||
version: "source-20260709"
|
||||
version: "source-20260714"
|
||||
video: true
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +1,6 @@
|
||||
|
||||
## 20260712 (2026-07-13)
|
||||
- Update to latest version from tphakala/birdnet-go (changelog : https://github.com/tphakala/birdnet-go/releases)
|
||||
## nightly-20260615-4 (09-07-2026)
|
||||
- Minor bugs fixed
|
||||
## nightly-20260615-3 (05-07-2026)
|
||||
|
||||
@@ -128,4 +128,4 @@ slug: birdnet-go
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-go
|
||||
usb: true
|
||||
version: "nightly-20260615-4"
|
||||
version: "20260712"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -2,10 +2,10 @@
|
||||
"github_beta": true,
|
||||
"github_exclude": "-4",
|
||||
"github_fulltag": true,
|
||||
"last_update": "2026-06-17",
|
||||
"last_update": "2026-07-13",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "birdnet-go",
|
||||
"source": "github",
|
||||
"upstream_repo": "tphakala/birdnet-go",
|
||||
"upstream_version": "nightly-20260615"
|
||||
"upstream_version": "20260712"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -1,4 +1,10 @@
|
||||
|
||||
## 0.8.4 (2026-07-13)
|
||||
- Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases)
|
||||
|
||||
## 0.8.3 (2026-07-11)
|
||||
- Update to latest version from Suncuss/BirdNET-PiPy (changelog : https://github.com/Suncuss/BirdNET-PiPy/releases)
|
||||
|
||||
## 0.8.2.1 (2026-07-05)
|
||||
- Re-tag of 0.8.2-1 with no content change. Home Assistant compares add-on versions with semver semantics, where a `-N` suffix counts as a *pre-release* and sorts **below** the base version — so users already on 0.8.2 saw the 0.8.2-1 nginx fix as "Up-to-date" with the Update button disabled. Four-segment `0.8.2.1` sorts above both `0.8.2` and `0.8.2-1` (and below the next upstream `0.8.3`), so the update becomes installable everywhere.
|
||||
|
||||
|
||||
@@ -96,4 +96,4 @@ schema:
|
||||
ssl: bool?
|
||||
slug: birdnet-pipy
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/birdnet-pipy
|
||||
version: "0.8.2.1"
|
||||
version: "0.8.4"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-07-13",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "birdnet-pipy",
|
||||
"source": "github",
|
||||
"upstream_repo": "Suncuss/BirdNET-PiPy",
|
||||
"upstream_version": "0.8.2"
|
||||
"upstream_version": "0.8.4"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,4 +1,7 @@
|
||||
|
||||
## 1.92.139-ls112 (2026-07-11)
|
||||
- Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases)
|
||||
|
||||
## 1.92.134-ls109 (2026-07-04)
|
||||
- Update to latest version from linuxserver/docker-brave (changelog : https://github.com/linuxserver/docker-brave/releases)
|
||||
|
||||
|
||||
@@ -69,5 +69,5 @@ slug: brave
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.92.134-ls109"
|
||||
version: "1.92.139-ls112"
|
||||
video: true
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_fulltag": "true",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-07-11",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "brave",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-brave",
|
||||
"upstream_version": "1.92.134-ls109"
|
||||
"upstream_version": "1.92.139-ls112"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## version-30a7c401 (2026-07-11)
|
||||
- Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases)
|
||||
|
||||
## version-7148c2a3 (2026-07-04)
|
||||
- Update to latest version from linuxserver/docker-chromium (changelog : https://github.com/linuxserver/docker-chromium/releases)
|
||||
|
||||
|
||||
@@ -71,5 +71,5 @@ slug: chromium
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "version-7148c2a3"
|
||||
version: "version-30a7c401"
|
||||
video: true
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_fulltag": "true",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-07-11",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "chromium",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-chromium",
|
||||
"upstream_version": "version-7148c2a3"
|
||||
"upstream_version": "version-30a7c401"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,3 +1,58 @@
|
||||
## 1.23 (15-07-2026)
|
||||
|
||||
- Expose `SUDO_PASSWORD`, per LinuxServer.io's base-image convention: setting it grants the `abc` user sudo access gated by that password. Sudo access stays disabled by default when left unset. Passed straight through by the existing option-to-env-var mechanism; no rootfs changes needed.
|
||||
|
||||
## 1.21 (15-07-2026)
|
||||
|
||||
- Fix Claude Code bypass permissions being rejected when the add-on uses its default root `PUID`.
|
||||
- In `permission_mode: bypass`, remap the shared `abc` Desktop runtime to an unused non-root UID before storage ownership and Selkies startup, while retaining its configured primary group for mounted-path access.
|
||||
- Make folder setup and final Claude configuration ownership follow the effective `abc` identity instead of the configured root UID.
|
||||
- Drop root console invocations of the add-on's `/usr/local/bin/claude` wrapper to the non-root `abc` runtime before passing `--dangerously-skip-permissions`.
|
||||
- Extend `claude-tools-doctor.sh` with configured/effective UID and GID checks for bypass mode.
|
||||
|
||||
## 1.20 (15-07-2026)
|
||||
|
||||
- Complete the TokenSave Claude Code integration at startup: install its MCP server, permissions, PreToolUse/UserPromptSubmit/Stop hooks, global guidance, and Git synchronization hooks instead of registering only `tokensave serve`.
|
||||
- Add `tokensave_project_paths` for explicit per-repository initialization and incremental synchronization; no repositories are scanned or indexed unless listed.
|
||||
- Route PATH-based Claude Code launches through the already-supervised Headroom proxy by default with a recursion-safe `/usr/local/bin/claude` wrapper; fall back to the official binary when the proxy is unavailable.
|
||||
- Pass the local proxy URL explicitly to the Headroom MCP server, while retaining MCP-only integration for the Desktop Electron application.
|
||||
- Keep the unauthenticated Headroom dashboard container-local by default; add `expose_headroom_dashboard` and leave port `8787/tcp` unmapped until explicitly enabled.
|
||||
- Fix the hourly gains report so Headroom no longer suppresses RTK output, add TokenSave gains, and gate each tool on its actual add-on option.
|
||||
- Add `claude-tools-doctor.sh` to inspect binaries, redacted MCP registrations, hooks, proxy health, routing, project indexes, and gains.
|
||||
- Install local validation tools (`jq`, `shellcheck`, `yamllint`, current `hadolint`, and current `actionlint`) to reduce avoidable CI round-trips.
|
||||
- Disable the unpinned third-party Caveman startup installer by default; it remains opt-in.
|
||||
|
||||
## 1.19 (14-07-2026)
|
||||
- Minor bugs fixed
|
||||
## 1.18 (14-07-2026)
|
||||
|
||||
- **Breaking:** remove the standalone Claude Code web terminal (ttyd/tmux service, port `7681`, and the `enable_terminal`, `terminal_username`, `terminal_password`, `terminal_workspace` options). The add-on is now built purely around Claude Desktop; Claude Code remains installed and powers Desktop cowork/dispatch sessions with the RTK hook, Caveman, and MCP servers intact. If the add-on refuses to start after the update, open its Configuration tab and re-save to drop the removed options.
|
||||
- Remove the `claude-direct` and `claude-headroom` terminal wrapper scripts and the unused `ha_smart_context` and `dangerously_skip_permissions` options.
|
||||
- Fix the Headroom dashboard being unreachable at `http://<host>:8787/dashboard`: the supervised proxy only listened on `127.0.0.1`; it now binds `0.0.0.0` so the mapped port works.
|
||||
- Fix dispatch/remote sessions and sign-in persistence: install the missing `gnome-keyring` package. The existing keyring bootstrap silently no-oped without it, leaving Electron `safeStorage` unavailable ("cannot store allowlist cache"), so auth tokens and dispatch permission grants were lost on restart.
|
||||
- Add the tokensave code-intelligence MCP server (pinned 7.2.0, built from source like RTK), registered for both Claude Desktop and Claude Code; disable with `install_tokensave: false`.
|
||||
- Implement the Home Assistant MCP bridge for real: `enable_ha_mcp` plus new `ha_mcp_url`/`ha_mcp_token` options register Home Assistant's MCP Server integration in Claude through `mcp-proxy`, using the integration's stateless Streamable HTTP endpoint (`/api/mcp`).
|
||||
- Write the Claude configuration files with `0600` permissions, since they hold the Home Assistant access token in clear text.
|
||||
- Restrict the build-time `chmod +x` pass to the directories the add-on actually ships scripts in instead of traversing the whole image.
|
||||
- Register add-on-managed MCP servers in Claude Code's `~/.claude.json` as well as Claude Desktop's config, without clobbering user-customized entries.
|
||||
- Install `uv` and use it for the `additional_pip` option for much faster package installs.
|
||||
|
||||
## 1.16 (14-07-2026)
|
||||
- Minor bugs fixed
|
||||
## 1.15 (13-07-2026)
|
||||
- Minor bugs fixed
|
||||
|
||||
## ubunturesolute-version-6dc44b0e (2026-07-13)
|
||||
- Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases)
|
||||
## 1.14 (10-07-2026)
|
||||
|
||||
- Build pinned RTK 0.43.0 source on Debian Bookworm for both architectures instead of installing the upstream arm64 release binary, which requires GLIBC 2.39 and cannot run in the add-on image.
|
||||
- Execute `rtk --version` inside the final image during the Docker build so future ABI incompatibilities fail CI instead of surfacing at runtime.
|
||||
- Validate the final Bookworm-built RTK binary in a native aarch64 image build.
|
||||
- Correct the repository PR checks so changed changelog paths are exported and aarch64 images are built explicitly for `linux/arm64`.
|
||||
|
||||
## debiantrixie-version-c55d3809 (2026-07-11)
|
||||
- Update to latest version from linuxserver/docker-baseimage-selkies (changelog : https://github.com/linuxserver/docker-baseimage-selkies/releases)
|
||||
## 1.13 (10-07-2026)
|
||||
|
||||
- Add the official Claude Code stable package, `tmux`, `ripgrep`, and a pinned upstream `ttyd` binary for both supported architectures.
|
||||
|
||||
@@ -9,7 +9,32 @@
|
||||
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
ARG RTK_VERSION="v0.43.0"
|
||||
ARG RTK_COMMIT="5a7880d404db8364d602f2ecdc41dd790f64013f"
|
||||
ARG TOKENSAVE_VERSION="7.2.0"
|
||||
|
||||
# The upstream aarch64 release is cross-built on ubuntu-latest and requires
|
||||
# GLIBC 2.39. Build the pinned source on Bookworm instead so it is compatible
|
||||
# with the add-on runtime on both supported architectures.
|
||||
FROM rust:1.91-bookworm AS rtk-builder
|
||||
ARG RTK_VERSION
|
||||
ARG RTK_COMMIT
|
||||
RUN git clone --depth 1 --branch "${RTK_VERSION}" https://github.com/rtk-ai/rtk.git /src/rtk && \
|
||||
test "$(git -C /src/rtk rev-parse HEAD)" = "${RTK_COMMIT}" && \
|
||||
cd /src/rtk && \
|
||||
cargo build --release --locked && \
|
||||
install -D -m 0755 target/release/rtk /out/rtk && \
|
||||
/out/rtk --version
|
||||
|
||||
# tokensave ships no Bookworm-compatible prebuilt binary either; build the pinned
|
||||
# crates.io release from source so GLIBC matches the add-on runtime.
|
||||
FROM rust:1.91-bookworm AS tokensave-builder
|
||||
ARG TOKENSAVE_VERSION
|
||||
RUN cargo install tokensave --version "${TOKENSAVE_VERSION}" --locked --root /out && \
|
||||
/out/bin/tokensave --version
|
||||
|
||||
FROM ${BUILD_FROM}
|
||||
ARG BUILD_ARCH
|
||||
|
||||
##################
|
||||
# 2 Modify Image #
|
||||
@@ -45,17 +70,21 @@ RUN curl -fsSL --retry 3 --retry-delay 2 \
|
||||
# 3 Install apps #
|
||||
##################
|
||||
|
||||
# Add rootfs
|
||||
# Add rootfs. Only the directories this add-on ships scripts in are traversed, so the chmod
|
||||
# cannot alter executables elsewhere in the image.
|
||||
COPY rootfs/ /
|
||||
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
|
||||
RUN find /etc/cont-init.d /etc/s6-overlay /defaults /usr/local/bin -type f \
|
||||
\( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \; && \
|
||||
chmod +x /usr/local/bin/claude
|
||||
|
||||
# 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, Claude Code, browser-terminal tooling, and Python tooling
|
||||
# Install Claude Desktop, Claude Code, Python tooling, and lightweight local validators.
|
||||
# gnome-keyring provides the Secret Service backend Electron safeStorage needs to persist
|
||||
# sign-in and dispatch grants.
|
||||
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 && \
|
||||
@@ -71,33 +100,53 @@ RUN install -d -m 0755 /etc/apt/keyrings && \
|
||||
git \
|
||||
gh \
|
||||
ripgrep \
|
||||
tmux && \
|
||||
jq \
|
||||
shellcheck \
|
||||
yamllint && \
|
||||
test -x /usr/bin/claude && \
|
||||
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"
|
||||
# Install the current upstream hadolint and actionlint releases for both supported
|
||||
# architectures. The GitHub release API resolves the latest asset at build time, so these
|
||||
# developer tools are intentionally not version-pinned.
|
||||
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 ;; \
|
||||
case "${BUILD_ARCH}" in \
|
||||
amd64) hadolint_arch="x86_64"; actionlint_arch="amd64" ;; \
|
||||
aarch64) hadolint_arch="arm64"; actionlint_arch="arm64" ;; \
|
||||
*) echo "Unsupported validation-tools architecture: ${BUILD_ARCH}" >&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
|
||||
hadolint_name="hadolint-linux-${hadolint_arch}"; \
|
||||
hadolint_url="$(curl -fsSL https://api.github.com/repos/hadolint/hadolint/releases/latest \
|
||||
| jq -r --arg name "${hadolint_name}" '.assets[] | select(.name == $name) | .browser_download_url' \
|
||||
| head -n 1)"; \
|
||||
test -n "${hadolint_url}"; \
|
||||
curl -fsSL --retry 3 --retry-delay 2 -o /usr/local/bin/hadolint "${hadolint_url}"; \
|
||||
chmod 0755 /usr/local/bin/hadolint; \
|
||||
actionlint_suffix="_linux_${actionlint_arch}.tar.gz"; \
|
||||
actionlint_url="$(curl -fsSL https://api.github.com/repos/rhysd/actionlint/releases/latest \
|
||||
| jq -r --arg suffix "${actionlint_suffix}" '.assets[] | select(.name | endswith($suffix)) | .browser_download_url' \
|
||||
| head -n 1)"; \
|
||||
test -n "${actionlint_url}"; \
|
||||
curl -fsSL --retry 3 --retry-delay 2 -o /tmp/actionlint.tar.gz "${actionlint_url}"; \
|
||||
tar -xzf /tmp/actionlint.tar.gz -C /tmp actionlint; \
|
||||
install -m 0755 /tmp/actionlint /usr/local/bin/actionlint; \
|
||||
rm -f /tmp/actionlint /tmp/actionlint.tar.gz; \
|
||||
hadolint --version; \
|
||||
actionlint -version
|
||||
|
||||
# Install only the Headroom proxy, code-compression, and MCP features used by this add-on.
|
||||
# Copy the pinned Bookworm-built RTK and tokensave binaries and execute them in the final
|
||||
# image. This makes an ABI mismatch fail the image build instead of surfacing at runtime.
|
||||
COPY --from=rtk-builder /out/rtk /usr/local/bin/rtk
|
||||
COPY --from=tokensave-builder /out/bin/tokensave /usr/local/bin/tokensave
|
||||
RUN /usr/local/bin/rtk --version && /usr/local/bin/tokensave --version
|
||||
|
||||
# Install only the Headroom proxy, code-compression, and MCP features used by this add-on,
|
||||
# plus mcp-proxy (stdio->HTTP bridge for the Home Assistant MCP server) and uv (fast
|
||||
# installer used for the additional_pip option).
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends nodejs && \
|
||||
pip3 install --break-system-packages "headroom-ai[proxy,code,mcp]" && \
|
||||
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh -o /tmp/rtk-install.sh && \
|
||||
HOME=/root sh /tmp/rtk-install.sh && \
|
||||
rm /tmp/rtk-install.sh && \
|
||||
if [ -x /root/.local/bin/rtk ] && [ ! -x /usr/local/bin/rtk ]; then mv /root/.local/bin/rtk /usr/local/bin/rtk; fi && \
|
||||
if [ -x /usr/local/bin/rtk ]; then chmod +x /usr/local/bin/rtk; else echo "rtk binary was not installed on PATH"; exit 1; fi && \
|
||||
pip3 install --break-system-packages "headroom-ai[proxy,code,mcp]" mcp-proxy uv && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /root/.cache
|
||||
|
||||
@@ -145,7 +194,6 @@ CMD [ "/ha_entrypoint.sh" ]
|
||||
# 5 Labels #
|
||||
############
|
||||
|
||||
ARG BUILD_ARCH
|
||||
ARG BUILD_DATE
|
||||
ARG BUILD_DESCRIPTION
|
||||
ARG BUILD_NAME
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
![Supports amd64 Architecture][amd64-shield]
|
||||
![Project Maintenance][maintenance-shield]
|
||||
|
||||
Run Claude Desktop and an optional persistent Claude Code web terminal in one
|
||||
LinuxServer.io Selkies add-on.
|
||||
Run Claude Desktop in a LinuxServer.io Selkies add-on, with Headroom context
|
||||
compression, RTK Bash-output acceleration, and TokenSave semantic code
|
||||
intelligence wired in by default.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -20,151 +21,201 @@ 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.
|
||||
Everything is built around the Claude Desktop app. Claude Code is installed in
|
||||
the same image but is not exposed as a standalone service: Claude Desktop's
|
||||
cowork and dispatch sessions run it internally, and they pick up the shared
|
||||
Claude Code configuration (`~/.claude`), hooks, MCP servers, permissions, and
|
||||
PATH tools.
|
||||
|
||||
- **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.
|
||||
- **Claude Code sessions inside Desktop** get the same MCP servers, permission
|
||||
mode, and RTK/TokenSave hooks through the shared Claude Code configuration.
|
||||
- PATH-based Claude Code launches are routed through the supervised Headroom
|
||||
proxy when `headroom_wrap_claude_code` is enabled. If a Desktop release calls
|
||||
`/usr/bin/claude` directly, the session remains functional and still has the
|
||||
shared permission mode and Headroom MCP tools, but transparent proxy
|
||||
compression cannot be injected.
|
||||
- When `permission_mode: bypass` is selected while `PUID` is `0`, the add-on
|
||||
automatically remaps the shared `abc` desktop account to an unused non-root
|
||||
UID before Selkies and Claude Desktop start. Claude Code refuses bypass mode
|
||||
under an effective root UID.
|
||||
- **gnome-keyring** provides the Secret Service backend Electron needs to
|
||||
persist sign-in and dispatch permission grants across restarts.
|
||||
|
||||
## Optimization layers
|
||||
|
||||
The three bundled optimization tools are complementary:
|
||||
|
||||
- **RTK** rewrites supported Bash commands so Claude receives compact output.
|
||||
- **TokenSave** builds a local semantic graph for explicitly selected code
|
||||
repositories and steers Claude away from repeated Explore/Grep/Read fan-out.
|
||||
- **Headroom** transparently compresses proxied Claude Code traffic and also
|
||||
exposes on-demand compress/retrieve/statistics MCP tools to Claude Desktop.
|
||||
|
||||
TokenSave's complete Claude integration is installed at startup: MCP server,
|
||||
permissions, PreToolUse/UserPromptSubmit/Stop hooks, global prompt rules, and
|
||||
Git synchronization hooks. A repository is indexed only when it is listed in
|
||||
`tokensave_project_paths`; no automatic filesystem scan is performed.
|
||||
|
||||
## Features
|
||||
|
||||
- Claude Desktop in single-app Selkies mode.
|
||||
- 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.
|
||||
- Claude Desktop in single-app Selkies mode with Home Assistant ingress.
|
||||
- Official Claude Code stable package powering Desktop cowork/dispatch
|
||||
sessions.
|
||||
- Persistent `$HOME` at the configured `data_location` (default `/data/data`),
|
||||
preserving Desktop and Claude Code state across restarts.
|
||||
- Persistent sign-in through a bundled, auto-unlocked gnome-keyring.
|
||||
- Configurable Claude Code permissions: strict prompts, automatic safe-action
|
||||
approval, or explicit full bypass for trusted installations.
|
||||
- Automatic non-root runtime enforcement for bypass mode, including root-console
|
||||
wrapper launches.
|
||||
- Optional runtime Claude Desktop updates from Anthropic's apt repository.
|
||||
- Optional extra apt and pip package installation.
|
||||
- Baked-in `git`, GitHub CLI (`gh`), `ripgrep`, and terminal tooling.
|
||||
- Optional extra apt and pip package installation (pip installs use `uv`).
|
||||
- Baked-in `git`, GitHub CLI (`gh`), `ripgrep`, `jq`, `shellcheck`, `yamllint`,
|
||||
`hadolint`, and `actionlint`.
|
||||
- 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.
|
||||
- Bundled optimization tools: Headroom, RTK, and TokenSave; Caveman remains
|
||||
available as an opt-in plugin.
|
||||
- Optional Home Assistant MCP bridge so Claude can query and control Home
|
||||
Assistant.
|
||||
- Independent hourly savings reports for Headroom, RTK, and TokenSave.
|
||||
- `claude-tools-doctor.sh` diagnostics for binaries, routing, hooks, MCP
|
||||
registrations, project indexes, proxy health, permissions, runtime identity,
|
||||
and gains.
|
||||
- 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` | Numeric user and group applied by the LinuxServer initialization. |
|
||||
| `PUID` / `PGID` | `0` / `0` | Numeric user and group applied by LinuxServer initialization. In bypass mode, a root `PUID` is automatically replaced at runtime by an unused non-root UID while the configured group is retained. |
|
||||
| `TZ` | | Optional timezone, for example `Europe/Brussels`. |
|
||||
| `KEYBOARD` | | Optional Selkies keyboard layout. |
|
||||
| `PASSWORD` | | Optional password for direct Selkies ports and compatibility fallback for terminal authentication. |
|
||||
| `PASSWORD` | | Optional password for direct Selkies ports. |
|
||||
| `SUDO_PASSWORD` | | LinuxServer.io convention: grants the `abc` user sudo access gated by this password. Sudo access is disabled by default when left unset. |
|
||||
| `DRINODE` | | Optional GPU device override for Selkies. |
|
||||
| `DNS_server` | `8.8.8.8` | DNS server used by the standard DNS module. |
|
||||
| `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. |
|
||||
| `permission_mode` | `auto` | Claude Code permission policy: `strict`, `auto`, or `bypass`. |
|
||||
| `install_headroom` | `true` | Register Headroom MCP and run the supervised local proxy. |
|
||||
| `headroom_wrap_claude_code` | `true` | Route PATH-based Claude Code launches through the already-running Headroom proxy. |
|
||||
| `expose_headroom_dashboard` | `false` | Bind Headroom to all interfaces. Port `8787/tcp` must also be mapped manually. |
|
||||
| `install_rtk` | `true` | Configure RTK's Claude Code `PreToolUse` Bash hook. |
|
||||
| `install_tokensave` | `true` | Install TokenSave's complete global Claude integration. |
|
||||
| `tokensave_project_paths` | `[]` | Explicit absolute Git repository paths to initialize or sync at startup. |
|
||||
| `install_caveman` | `false` | Install the third-party Caveman Claude Code plugin at startup. |
|
||||
| `enable_tools_health_report` | `true` | Write independent Headroom, RTK, and TokenSave gains to the add-on log hourly. |
|
||||
| `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` | Reserved compatibility option; it is not applied by the terminal launcher. |
|
||||
| `enable_ha_mcp` | `false` | Register Home Assistant's MCP server in Claude (requires `ha_mcp_token`). |
|
||||
| `ha_mcp_url` | `http://homeassistant:8123/api/mcp` | Streamable HTTP endpoint of Home Assistant's MCP Server integration. |
|
||||
| `ha_mcp_token` | | Home Assistant long-lived access token used by the MCP bridge. |
|
||||
| `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. |
|
||||
| `additional_pip` | | Comma-separated pip packages installed at startup (via `uv`). |
|
||||
| `data_location` | `/data/data` | Persistent home directory for Claude and tooling. |
|
||||
| `env_vars` | `[]` | Additional environment variables exported inside the container. |
|
||||
|
||||
### Permission modes
|
||||
|
||||
```yaml
|
||||
permission_mode: auto
|
||||
```
|
||||
|
||||
- `strict` keeps Claude Code's normal interactive permission prompts.
|
||||
- `auto` asks Claude Code's automatic permission classifier to approve safe
|
||||
operations while retaining prompts for risky actions. This is the default.
|
||||
- `bypass` disables Claude Code permission checks by using
|
||||
`bypassPermissions` in the shared settings and
|
||||
`--dangerously-skip-permissions` for wrapper-launched sessions.
|
||||
|
||||
Claude Code does not permit bypass mode when its effective UID is `0`. If the
|
||||
add-on is configured with `PUID: 0`, selecting `bypass` remaps only the shared
|
||||
`abc` runtime account to an available non-root UID (preferring `1000`, then
|
||||
`911`) before storage ownership and Desktop startup. Its configured primary
|
||||
GID is retained, so group-based access to mounted Home Assistant paths remains
|
||||
available. Strict and auto modes keep the configured identity unchanged.
|
||||
|
||||
A root shell invoking `/usr/local/bin/claude` in bypass mode is also dropped to
|
||||
the remapped `abc` account. Directly invoking `/usr/bin/claude` as root still
|
||||
bypasses the add-on wrapper and will be rejected by Claude Code.
|
||||
|
||||
`bypass` gives Claude broad authority over all mounted writable data and every
|
||||
command or credential available inside the add-on. Enable it only in a trusted
|
||||
installation with trusted repositories and mounts. Mounted paths must remain
|
||||
accessible to the effective non-root UID or its retained group.
|
||||
|
||||
### TokenSave project example
|
||||
|
||||
Only repositories listed here are indexed. Paths must be absolute, mounted in
|
||||
the add-on, and resolve to a Git working tree:
|
||||
|
||||
```yaml
|
||||
tokensave_project_paths:
|
||||
- /share/projects/hassio-addons
|
||||
- /share/projects/birdnet-go
|
||||
```
|
||||
|
||||
At startup, an uninitialized repository receives `tokensave init`; an existing
|
||||
index receives an incremental `tokensave sync`. Removing a path from the option
|
||||
stops automatic synchronization but does not delete its `.tokensave` database.
|
||||
Configured repositories are added to Git's `safe.directory` list for the shared
|
||||
runtime user before TokenSave performs repository discovery.
|
||||
|
||||
## 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.
|
||||
When `install_headroom` is enabled, the add-on registers `headroom mcp serve`
|
||||
with the explicit local proxy URL in Claude Desktop and Claude Code, then starts
|
||||
a supervised Headroom backend on `127.0.0.1:8787`.
|
||||
|
||||
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.
|
||||
Claude Desktop overrides `ANTHROPIC_BASE_URL`, so Desktop chat deliberately uses
|
||||
the MCP integration. The `/usr/local/bin/claude` wrapper routes PATH-based Claude
|
||||
Code sessions through `headroom wrap claude --no-proxy`, reusing the supervised
|
||||
backend without starting a second proxy.
|
||||
|
||||
The Headroom dashboard remains available at:
|
||||
The dashboard is disabled externally by default. To expose it:
|
||||
|
||||
```text
|
||||
http://<home-assistant-host>:8787/dashboard
|
||||
1. Set `expose_headroom_dashboard: true`.
|
||||
2. Map `8787/tcp` in the add-on **Network** section.
|
||||
3. Open `http://<home-assistant-host>:8787/dashboard`.
|
||||
|
||||
The dashboard is unauthenticated. Do not publish this port to the public
|
||||
internet.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Run the following inside the add-on through a custom script or container console:
|
||||
|
||||
```bash
|
||||
claude-tools-doctor.sh
|
||||
```
|
||||
|
||||
when the `8787/tcp` port is mapped. Treat this endpoint as sensitive and do not
|
||||
expose it directly to the public internet.
|
||||
The report checks the tool binaries, configuration switches, configured and
|
||||
effective runtime identities, redacted MCP registrations, Claude hooks,
|
||||
permission mode, Headroom health, TokenSave indexes, routing, and recorded
|
||||
savings. It never prints MCP environment values because the Home Assistant MCP
|
||||
entry can contain a long-lived token.
|
||||
|
||||
The hourly report can also be invoked manually:
|
||||
|
||||
```bash
|
||||
claude-gains-report.sh
|
||||
```
|
||||
|
||||
## Home Assistant MCP bridge
|
||||
|
||||
To let Claude query and control Home Assistant:
|
||||
|
||||
1. In Home Assistant, add the **Model Context Protocol Server** integration
|
||||
(Settings → Devices & services → Add integration).
|
||||
2. Create a long-lived access token (your profile → Security).
|
||||
3. Set `enable_ha_mcp: true` and paste the token into `ha_mcp_token` in the
|
||||
add-on configuration, then restart the add-on.
|
||||
|
||||
The add-on bridges Claude to the integration's stateless Streamable HTTP
|
||||
endpoint (`/api/mcp`) with `mcp-proxy`. Override `ha_mcp_url` only if your Home
|
||||
Assistant instance is not reachable as `homeassistant:8123` from add-ons.
|
||||
|
||||
## Custom scripts
|
||||
|
||||
@@ -178,10 +229,14 @@ the image.
|
||||
Persistent state is stored in the configured `data_location` (default
|
||||
`/data/data`):
|
||||
|
||||
- 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
|
||||
- Claude Desktop sign-in: `~/.config/Claude` (token encrypted via
|
||||
gnome-keyring; keyring DB in `~/.local/share/keyrings`)
|
||||
- Claude Code settings, hooks, sessions, plugins, and permission mode:
|
||||
`~/.claude`
|
||||
- Headroom, RTK, and TokenSave user state: their standard paths below the
|
||||
shared home
|
||||
- TokenSave repository indexes: `.tokensave/` inside each explicitly configured
|
||||
project
|
||||
|
||||
Volatile cache data is redirected to `/tmp/cache` through `$XDG_CACHE_HOME` and
|
||||
`$HOME/.cache`.
|
||||
|
||||
@@ -4,7 +4,10 @@ Two related sign-in problems when Claude Desktop runs inside the LinuxServer Sel
|
||||
streamed desktop.
|
||||
|
||||
**Status:**
|
||||
- **Shipped:** Problem B (keyring persistence) is implemented in v1.4 (Dockerfile + `rootfs/defaults/autostart`).
|
||||
- **Shipped:** Problem B (keyring persistence) — the `autostart` bootstrap landed in v1.4, but
|
||||
the `gnome-keyring` package itself was missing from the image until v1.17 (the bootstrap
|
||||
silently no-oped and Electron logged "safeStorage encryption is not available"). Fixed in
|
||||
v1.17: the Dockerfile now installs `gnome-keyring`.
|
||||
- **Planned only:** Problem A (in-desktop browser for OAuth) is intentionally not implemented.
|
||||
The image ships no browser; complete the login with the user-side workaround below.
|
||||
|
||||
@@ -50,8 +53,9 @@ magic link into the in-session Chromium (not a phone).
|
||||
### User-side workaround (no rebuild)
|
||||
- Add-on Configuration → `additional_apps: chromium`, restart (installed by
|
||||
`rootfs/etc/cont-init.d/80-configuration.sh`).
|
||||
- Run the two `xdg-settings`/`xdg-mime` commands once in an in-session terminal, or add them
|
||||
to the custom script `/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`.
|
||||
- Add the two `xdg-settings`/`xdg-mime` commands to the custom script
|
||||
`/addon_configs/db21ed7f_claude-desktop/claude_desktop.sh` (the image ships no standalone
|
||||
terminal).
|
||||
|
||||
---
|
||||
|
||||
@@ -94,14 +98,14 @@ Claude Desktop uses. No extra `dbus-launch` is needed.
|
||||
then exposes the Secret Service and exports `GNOME_KEYRING_CONTROL`/`SSH_AUTH_SOCK`.
|
||||
- `--password-store=gnome-libsecret` forces Electron to use the libsecret backend instead
|
||||
of falling back to plaintext.
|
||||
3. Persistence: the keyring DB lives in `$HOME/.local/share/keyrings/` and `HOME=/config/data`
|
||||
3. Persistence: the keyring DB lives in `$HOME/.local/share/keyrings/` and `HOME=/data/data`
|
||||
(persistent add-on storage), so the empty-password login keyring survives restarts and is
|
||||
re-unlocked automatically each boot by the same `autostart` line — the sign-in then sticks.
|
||||
|
||||
### User-side workaround (no rebuild)
|
||||
- Add-on Configuration → `additional_apps: gnome-keyring, libsecret-1-0, dbus-x11`, restart.
|
||||
- Add the keyring-start lines above to the custom script
|
||||
`/addon_configs/db21ed7f_claude-desktop/claude-desktop.sh`, and relaunch Claude Desktop
|
||||
`/addon_configs/db21ed7f_claude-desktop/claude_desktop.sh`, and relaunch Claude Desktop
|
||||
with `--password-store=gnome-libsecret` (e.g. edit the in-session openbox autostart).
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@ arch:
|
||||
- aarch64
|
||||
- amd64
|
||||
audio: true
|
||||
description: Claude Desktop with a persistent Claude Code web terminal in one add-on
|
||||
description: "Claude Desktop with Headroom, RTK, and TokenSave optimization"
|
||||
devices:
|
||||
- /dev/dri
|
||||
- /dev/dri/card0
|
||||
@@ -13,8 +13,8 @@ environment:
|
||||
AUTO_GPU: "1"
|
||||
FM_HOME: /data/data
|
||||
HOME: /data/data
|
||||
PGID: "0"
|
||||
PUID: "0"
|
||||
PGID: "1000"
|
||||
PUID: "1000"
|
||||
SELKIES_FRAMERATE: "30"
|
||||
START_DOCKER: "false"
|
||||
TITLE: Claude Desktop
|
||||
@@ -35,35 +35,33 @@ options:
|
||||
env_vars: []
|
||||
DNS_server: 8.8.8.8
|
||||
data_location: /data/data
|
||||
PGID: 0
|
||||
PUID: 0
|
||||
additional_apps: ""
|
||||
additional_pip: ""
|
||||
auto_update: true
|
||||
github_email: ""
|
||||
ha_smart_context: true
|
||||
enable_ha_mcp: true
|
||||
dangerously_skip_permissions: false
|
||||
enable_terminal: true
|
||||
terminal_username: claude
|
||||
terminal_password: ""
|
||||
terminal_workspace: ""
|
||||
enable_ha_mcp: false
|
||||
ha_mcp_url: http://homeassistant:8123/api/mcp
|
||||
ha_mcp_token: ""
|
||||
github_token: ""
|
||||
github_username: ""
|
||||
install_caveman: true
|
||||
enable_tools_health_report: true
|
||||
expose_headroom_dashboard: false
|
||||
headroom_wrap_claude_code: true
|
||||
install_caveman: false
|
||||
install_github_cli: true
|
||||
install_headroom: true
|
||||
install_rtk: true
|
||||
install_tokensave: true
|
||||
permission_mode: auto
|
||||
tokensave_project_paths: []
|
||||
panel_admin: false
|
||||
panel_icon: mdi:robot-happy
|
||||
ports:
|
||||
3001/tcp: null
|
||||
7681/tcp: null
|
||||
8787/tcp: 8787
|
||||
8787/tcp: null
|
||||
ports_description:
|
||||
3001/tcp: Claude Desktop web interface
|
||||
7681/tcp: HTTP Basic-auth Claude Code terminal (no TLS)
|
||||
8787/tcp: Headroom dashboard and proxy
|
||||
8787/tcp: Optional Headroom dashboard and proxy
|
||||
privileged:
|
||||
- SYS_ADMIN
|
||||
- DAC_READ_SEARCH
|
||||
@@ -76,29 +74,35 @@ schema:
|
||||
DRINODE: list(/dev/dri/card0|/dev/dri/card1|/dev/dri/card2|/dev/dri/renderD128|/dev/dri/renderD129|)?
|
||||
KEYBOARD: list(da-dk-qwerty|de-de-qwertz|en-gb-qwerty|en-us-qwerty|es-es-qwerty|fr-ch-qwertz|fr-fr-azerty|it-it-qwerty|ja-jp-qwerty|pt-br-qwerty|sv-se-qwerty|tr-tr-qwerty)?
|
||||
PASSWORD: str?
|
||||
PGID: int
|
||||
PUID: int
|
||||
SUDO_PASSWORD: password?
|
||||
TZ: match([A-Z][a-z]*./[A-Z][a-z]*.)?
|
||||
additional_apps: str?
|
||||
additional_pip: str?
|
||||
auto_update: bool?
|
||||
cifsdomain: str?
|
||||
cifspassword: str?
|
||||
cifsusername: str?
|
||||
localdisks: str?
|
||||
networkdisks: str?
|
||||
github_email: str?
|
||||
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?
|
||||
ha_mcp_url: str?
|
||||
ha_mcp_token: password?
|
||||
github_token: password?
|
||||
github_username: str?
|
||||
enable_tools_health_report: bool
|
||||
expose_headroom_dashboard: bool
|
||||
headroom_wrap_claude_code: bool
|
||||
install_caveman: bool
|
||||
install_github_cli: bool
|
||||
install_headroom: bool
|
||||
install_rtk: bool
|
||||
install_tokensave: bool
|
||||
permission_mode: list(strict|auto|bypass)
|
||||
tokensave_project_paths:
|
||||
- str
|
||||
slug: claude_desktop
|
||||
tmpfs: true
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "1.13"
|
||||
version: "1.23"
|
||||
video: true
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -22,8 +22,7 @@ else
|
||||
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.
|
||||
# ANTHROPIC_BASE_URL, so Desktop uses the registered Headroom MCP tools instead.
|
||||
|
||||
# Launch the configured command. If a custom/wrapped command fails to start, fall back to
|
||||
# the plain Claude Desktop launch so the app always comes up for the user.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Hourly rtk + headroom token-savings report to the add-on log (heartbeat + gains).
|
||||
# Hourly RTK + Headroom + TokenSave savings report to the add-on log.
|
||||
# Seeded to /data/data/crontabs/root by init-crontab-config and run by svc-cron; edit the
|
||||
# persistent copy to customize. Output goes to /proc/1/fd/1 so it shows in the add-on log.
|
||||
0 * * * * /usr/local/bin/claude-gains-report.sh > /proc/1/fd/1 2>&1
|
||||
|
||||
48
claude_desktop/rootfs/etc/cont-init.d/19-claude_bypass_runtime.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
# Claude Code deliberately refuses bypass-permissions mode when its effective UID is 0.
|
||||
# The add-on historically defaults PUID to 0, so switch the shared `abc` desktop user to
|
||||
# an unused non-root UID before storage ownership and Selkies runtime directories are set up.
|
||||
# Keep abc's configured primary group (commonly group 0) so existing group-based access to
|
||||
# Home Assistant mounts is preserved. Strict and auto permission modes are unchanged.
|
||||
if [ "$(bashio::config 'permission_mode')" != "bypass" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CURRENT_UID="$(id -u abc)"
|
||||
if [ "$CURRENT_UID" -ne 0 ]; then
|
||||
bashio::log.info "Claude bypass runtime already uses non-root UID ${CURRENT_UID}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
find_available_uid() {
|
||||
local candidate owner
|
||||
for candidate in 1000 911 $(seq 1001 1099); do
|
||||
owner="$(getent passwd "$candidate" | cut -d: -f1 || true)"
|
||||
if [ -z "$owner" ] || [ "$owner" = "abc" ]; then
|
||||
printf '%s' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
TARGET_UID="$(find_available_uid || true)"
|
||||
if [ -z "$TARGET_UID" ]; then
|
||||
bashio::exit.nok "Claude bypass mode requires a non-root runtime user, but no free fallback UID was found"
|
||||
fi
|
||||
|
||||
usermod --uid "$TARGET_UID" abc
|
||||
|
||||
if [ "$(id -u abc)" -eq 0 ]; then
|
||||
bashio::exit.nok "Unable to switch the Claude Desktop runtime away from root for bypass mode"
|
||||
fi
|
||||
|
||||
mkdir -p /run/s6/container_environment
|
||||
printf '%s' "$TARGET_UID" > /run/s6/container_environment/CLAUDE_RUNTIME_UID
|
||||
printf '%s' "$(id -g abc)" > /run/s6/container_environment/CLAUDE_RUNTIME_GID
|
||||
|
||||
bashio::log.warning "Claude bypass mode cannot run as root; remapped abc from UID 0 to UID ${TARGET_UID} (GID $(id -g abc))"
|
||||
@@ -3,9 +3,10 @@
|
||||
# shellcheck disable=SC2046
|
||||
set -e
|
||||
|
||||
# Define user
|
||||
PUID=$(bashio::config "PUID")
|
||||
PGID=$(bashio::config "PGID")
|
||||
# Use the effective shared desktop user identity. In bypass mode an earlier init script may
|
||||
# remap abc away from UID 0 because Claude Code rejects bypass permissions when run as root.
|
||||
PUID="$(id -u abc)"
|
||||
PGID="$(id -g abc)"
|
||||
|
||||
# Check data location
|
||||
LOCATION="$(bashio::config 'data_location')"
|
||||
|
||||
@@ -24,7 +24,12 @@ fi
|
||||
if bashio::config.has_value 'additional_pip'; then
|
||||
for p in $(bashio::config 'additional_pip' | tr ',' ' '); do
|
||||
bashio::log.green "... pip: $p"
|
||||
pip3 install --break-system-packages "$p" || bashio::log.fatal "Error: pip package $p failed"
|
||||
# Prefer uv (much faster resolver/installer); fall back to pip3 when unavailable.
|
||||
if command -v uv &> /dev/null; then
|
||||
uv pip install --system --break-system-packages "$p" || bashio::log.fatal "Error: pip package $p failed"
|
||||
else
|
||||
pip3 install --break-system-packages "$p" || bashio::log.fatal "Error: pip package $p failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
|
||||
if bashio::config.true 'auto_update'; then
|
||||
bashio::log.info "Checking for Claude Desktop updates..."
|
||||
if apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null && apt-get install -y --only-upgrade claude-desktop &> /dev/null; then
|
||||
bashio::log.info "Claude Desktop version: $(dpkg-query -W -f='${Version}' claude-desktop)"
|
||||
else
|
||||
bashio::log.warning "Update check failed (offline?), keeping current version"
|
||||
fi
|
||||
bashio::log.info "Checking for Claude Desktop updates..."
|
||||
if apt-get update -o Acquire::http::Timeout=10 -o Acquire::https::Timeout=10 &> /dev/null && apt-get install -y --only-upgrade claude-desktop &> /dev/null; then
|
||||
bashio::log.info "Claude Desktop version: $(dpkg-query -W -f='${Version}' claude-desktop)"
|
||||
else
|
||||
bashio::log.warning "Update check failed (offline?), keeping current version"
|
||||
fi
|
||||
|
||||
36
claude_desktop/rootfs/etc/cont-init.d/81-tokensave_repositories.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
if ! bashio::config.true 'install_tokensave' || ! command -v git > /dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
declare -A REPOS_SEEN=()
|
||||
while IFS= read -r configured_path; do
|
||||
configured_path="${configured_path#"${configured_path%%[![:space:]]*}"}"
|
||||
configured_path="${configured_path%"${configured_path##*[![:space:]]}"}"
|
||||
[ -n "$configured_path" ] || continue
|
||||
|
||||
case "$configured_path" in
|
||||
/*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
[ -d "$configured_path" ] || continue
|
||||
|
||||
# The one-shot safe.directory override is used only to discover the repository root.
|
||||
# Persist the resolved root in the shared runtime user's Git config before 82-claude_tools.sh
|
||||
# performs normal repository detection, avoiding Git's dubious-ownership rejection.
|
||||
repo_root="$(s6-setuidgid abc env HOME="$HOME" \
|
||||
git -c safe.directory='*' -C "$configured_path" rev-parse --show-toplevel 2> /dev/null || true)"
|
||||
[ -n "$repo_root" ] && [ "$repo_root" != "/" ] || continue
|
||||
[[ -z "${REPOS_SEEN[$repo_root]:-}" ]] || continue
|
||||
REPOS_SEEN[$repo_root]=1
|
||||
|
||||
if ! s6-setuidgid abc env HOME="$HOME" git config --global --get-all safe.directory \
|
||||
| grep -Fxq -- "$repo_root"; then
|
||||
s6-setuidgid abc env HOME="$HOME" git config --global --add safe.directory "$repo_root"
|
||||
bashio::log.info "Marked TokenSave repository as safe for Git: ${repo_root}"
|
||||
fi
|
||||
done < <(bashio::config.array 'tokensave_project_paths')
|
||||
@@ -7,69 +7,210 @@ PUID="$(if bashio::config.has_value 'PUID'; then bashio::config 'PUID'; else ech
|
||||
PGID="$(if bashio::config.has_value 'PGID'; then bashio::config 'PGID'; else echo '0'; fi)"
|
||||
mkdir -p "$HOME/.claude"
|
||||
|
||||
run_as_runtime_user() {
|
||||
s6-setuidgid abc env HOME="$HOME" "$@"
|
||||
}
|
||||
|
||||
CLAUDE_DESKTOP_COMMAND_FILE="/tmp/claude-desktop-command"
|
||||
DEFAULT_CLAUDE_DESKTOP_COMMAND='claude-desktop --no-sandbox --disable-dev-shm-usage --password-store=gnome-libsecret'
|
||||
printf '%s\n' "$DEFAULT_CLAUDE_DESKTOP_COMMAND" > "$CLAUDE_DESKTOP_COMMAND_FILE"
|
||||
|
||||
# headroom's "wrap"/proxy routing works by setting ANTHROPIC_BASE_URL, which the Claude Desktop
|
||||
# Electron app force-overrides to the production endpoint (headroom #869), so transparent
|
||||
# compression cannot be applied to the desktop launch. The integration that does work with
|
||||
# Claude Desktop is headroom's MCP server, which exposes the headroom_compress/headroom_retrieve/
|
||||
# headroom_stats tools inside the app. Register it in Claude Desktop's MCP config, leaving the
|
||||
# plain launch untouched. The merge is idempotent and preserves any other MCP servers.
|
||||
# Headroom's proxy routing works by setting ANTHROPIC_BASE_URL, which the Claude Desktop
|
||||
# Electron app force-overrides to the production endpoint (headroom #869). Desktop therefore
|
||||
# uses Headroom's MCP tools. Claude Code launches that resolve `claude` through PATH use the
|
||||
# add-on's /usr/local/bin/claude wrapper and can be transparently proxied when enabled.
|
||||
#
|
||||
# Register the add-on-managed MCP servers (headroom, tokensave, homeassistant) in both Claude
|
||||
# Desktop's config and Claude Code's user config (used by Desktop cowork/dispatch sessions).
|
||||
# The merge is idempotent, preserves any other MCP servers, never overwrites a user-customized
|
||||
# entry with a different command, and removes only add-on-managed entries when disabled.
|
||||
CLAUDE_DESKTOP_CONFIG="$HOME/.config/Claude/claude_desktop_config.json"
|
||||
CLAUDE_CODE_CONFIG="$HOME/.claude.json"
|
||||
|
||||
HEADROOM_ENABLED=false
|
||||
if bashio::config.true 'install_headroom'; then
|
||||
if command -v headroom &> /dev/null; then
|
||||
bashio::log.info "headroom $(headroom --version 2> /dev/null || true) available; registering the headroom MCP server for Claude Desktop"
|
||||
HEADROOM_BIN="$(command -v headroom)" CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" python3 - <<'PY' || bashio::log.warning "Unable to register the headroom MCP server automatically"
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
path = Path(os.environ["CLAUDE_DESKTOP_CONFIG"])
|
||||
try:
|
||||
data = json.loads(path.read_text()) if path.exists() else {}
|
||||
if not isinstance(data, dict):
|
||||
data = {}
|
||||
except Exception:
|
||||
if path.exists():
|
||||
path.rename(path.with_suffix(path.suffix + ".bak"))
|
||||
data = {}
|
||||
servers = data.get("mcpServers")
|
||||
if not isinstance(servers, dict):
|
||||
servers = {}
|
||||
data["mcpServers"] = servers
|
||||
servers["headroom"] = {"command": os.environ.get("HEADROOM_BIN", "headroom"), "args": ["mcp", "serve"]}
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||
PY
|
||||
HEADROOM_ENABLED=true
|
||||
bashio::log.info "headroom $(headroom --version 2> /dev/null || true) available; registering the headroom MCP server"
|
||||
else
|
||||
bashio::log.warning "headroom is not available"
|
||||
fi
|
||||
elif [ -f "$CLAUDE_DESKTOP_CONFIG" ]; then
|
||||
bashio::log.info "Removing the headroom MCP server from Claude Desktop"
|
||||
CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" python3 - <<'PY' || bashio::log.warning "Unable to remove the headroom MCP server automatically"
|
||||
fi
|
||||
|
||||
TOKENSAVE_ENABLED=false
|
||||
if bashio::config.true 'install_tokensave'; then
|
||||
if command -v tokensave &> /dev/null; then
|
||||
TOKENSAVE_ENABLED=true
|
||||
bashio::log.info "tokensave $(tokensave --version 2> /dev/null || true) available; configuring the complete Claude Code integration"
|
||||
# The upstream installer adds the MCP entry, PreToolUse/UserPromptSubmit/Stop hooks,
|
||||
# MCP permissions, global CLAUDE.md rules, and the global post-commit/checkout sync hook.
|
||||
run_as_runtime_user tokensave install --agent claude --git-hook yes \
|
||||
|| bashio::log.warning "tokensave Claude Code integration setup failed"
|
||||
else
|
||||
bashio::log.warning "tokensave is not available"
|
||||
fi
|
||||
elif command -v tokensave &> /dev/null; then
|
||||
bashio::log.info "Removing the tokensave Claude Code integration"
|
||||
run_as_runtime_user tokensave uninstall --agent claude \
|
||||
|| bashio::log.warning "tokensave Claude Code integration removal failed"
|
||||
fi
|
||||
|
||||
HA_MCP_ENABLED=false
|
||||
HA_MCP_URL=""
|
||||
HA_MCP_TOKEN=""
|
||||
if bashio::config.true 'enable_ha_mcp'; then
|
||||
HA_MCP_URL="$(bashio::config 'ha_mcp_url' 'http://homeassistant:8123/api/mcp')"
|
||||
if bashio::config.has_value 'ha_mcp_token'; then
|
||||
HA_MCP_TOKEN="$(bashio::config 'ha_mcp_token')"
|
||||
fi
|
||||
if [ -z "$HA_MCP_TOKEN" ]; then
|
||||
bashio::log.warning "enable_ha_mcp is on but ha_mcp_token is empty; set a Home Assistant long-lived access token (Profile -> Security) and enable the 'Model Context Protocol Server' integration"
|
||||
elif ! command -v mcp-proxy &> /dev/null; then
|
||||
bashio::log.warning "mcp-proxy is not available; cannot register the Home Assistant MCP server"
|
||||
else
|
||||
HA_MCP_ENABLED=true
|
||||
bashio::log.info "Registering the Home Assistant MCP server (${HA_MCP_URL})"
|
||||
fi
|
||||
fi
|
||||
|
||||
HEADROOM_ENABLED="$HEADROOM_ENABLED" HEADROOM_BIN="$(command -v headroom || echo headroom)" \
|
||||
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)" \
|
||||
CLAUDE_DESKTOP_CONFIG="$CLAUDE_DESKTOP_CONFIG" CLAUDE_CODE_CONFIG="$CLAUDE_CODE_CONFIG" \
|
||||
python3 - <<'PY' || bashio::log.warning "Unable to update the MCP server registrations automatically"
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
path = Path(os.environ["CLAUDE_DESKTOP_CONFIG"])
|
||||
data = json.loads(path.read_text())
|
||||
if isinstance(data, dict):
|
||||
MANAGED_BASENAMES = {
|
||||
"headroom": "headroom",
|
||||
"tokensave": "tokensave",
|
||||
"homeassistant": "mcp-proxy",
|
||||
}
|
||||
|
||||
desired = {}
|
||||
if os.environ["HEADROOM_ENABLED"] == "true":
|
||||
desired["headroom"] = {
|
||||
"command": os.environ["HEADROOM_BIN"],
|
||||
"args": ["mcp", "serve", "--proxy-url", "http://127.0.0.1:8787"],
|
||||
}
|
||||
if os.environ["TOKENSAVE_ENABLED"] == "true":
|
||||
desired["tokensave"] = {"command": os.environ["TOKENSAVE_BIN"], "args": ["serve"]}
|
||||
if os.environ["HA_MCP_ENABLED"] == "true":
|
||||
# Home Assistant's MCP Server integration speaks stateless Streamable HTTP on /api/mcp;
|
||||
# mcp-proxy defaults to SSE, so the transport flags are required.
|
||||
desired["homeassistant"] = {
|
||||
"command": os.environ["MCP_PROXY_BIN"],
|
||||
"args": ["--transport=streamablehttp", "--stateless", os.environ["HA_MCP_URL"]],
|
||||
"env": {"API_ACCESS_TOKEN": os.environ["HA_MCP_TOKEN"]},
|
||||
}
|
||||
|
||||
# An entry is add-on-managed when its command is one of our binaries living outside the
|
||||
# persistent home. Matching on the basename (rather than the exact path recorded at write
|
||||
# time) keeps entries updatable when a base-image upgrade moves the binary, while commands
|
||||
# under $HOME stay untouched because those are user-installed.
|
||||
HOME_PREFIX = os.path.expanduser("~") + os.sep
|
||||
|
||||
|
||||
def is_managed(name, entry):
|
||||
if not isinstance(entry, dict):
|
||||
return False
|
||||
command = entry.get("command")
|
||||
if not isinstance(command, str) or command.startswith(HOME_PREFIX):
|
||||
return False
|
||||
return os.path.basename(command) == MANAGED_BASENAMES[name]
|
||||
|
||||
|
||||
for config_var, stdio_type in (("CLAUDE_DESKTOP_CONFIG", False), ("CLAUDE_CODE_CONFIG", True)):
|
||||
path = Path(os.environ[config_var])
|
||||
try:
|
||||
data = json.loads(path.read_text()) if path.exists() else {}
|
||||
if not isinstance(data, dict):
|
||||
data = {}
|
||||
except Exception:
|
||||
if path.exists():
|
||||
path.rename(path.with_suffix(path.suffix + ".bak"))
|
||||
data = {}
|
||||
servers = data.get("mcpServers")
|
||||
if isinstance(servers, dict) and servers.pop("headroom", None) is not None:
|
||||
if not servers:
|
||||
data.pop("mcpServers", None)
|
||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||
if not isinstance(servers, dict):
|
||||
servers = {}
|
||||
changed = False
|
||||
for name in MANAGED_BASENAMES:
|
||||
existing = servers.get(name)
|
||||
if name in desired:
|
||||
entry = dict(desired[name])
|
||||
if stdio_type:
|
||||
entry["type"] = "stdio"
|
||||
if existing is None or is_managed(name, existing):
|
||||
if existing != entry:
|
||||
servers[name] = entry
|
||||
changed = True
|
||||
elif existing is not None and is_managed(name, existing):
|
||||
del servers[name]
|
||||
changed = True
|
||||
if not changed:
|
||||
continue
|
||||
if servers:
|
||||
data["mcpServers"] = servers
|
||||
else:
|
||||
data.pop("mcpServers", None)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||
# The Home Assistant long-lived access token is stored here in clear text.
|
||||
path.chmod(0o600)
|
||||
PY
|
||||
|
||||
# Initialize or incrementally sync only explicitly configured repositories. TokenSave deliberately
|
||||
# requires one-time per-project opt-in; an empty list therefore has no startup or storage cost.
|
||||
if $TOKENSAVE_ENABLED; then
|
||||
declare -A TOKENSAVE_REPOS_SEEN=()
|
||||
while IFS= read -r configured_path; do
|
||||
# Trim surrounding whitespace while preserving spaces inside paths.
|
||||
configured_path="${configured_path#"${configured_path%%[![:space:]]*}"}"
|
||||
configured_path="${configured_path%"${configured_path##*[![:space:]]}"}"
|
||||
[ -n "$configured_path" ] || continue
|
||||
|
||||
case "$configured_path" in
|
||||
/*) ;;
|
||||
*)
|
||||
bashio::log.warning "Skipping non-absolute tokensave_project_paths entry: ${configured_path}"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
if [ ! -d "$configured_path" ]; then
|
||||
bashio::log.warning "Skipping missing TokenSave project path: ${configured_path}"
|
||||
continue
|
||||
fi
|
||||
|
||||
repo_root="$(git -C "$configured_path" rev-parse --show-toplevel 2> /dev/null || true)"
|
||||
if [ -z "$repo_root" ] || [ "$repo_root" = "/" ]; then
|
||||
bashio::log.warning "Skipping TokenSave path that is not a supported Git repository: ${configured_path}"
|
||||
continue
|
||||
fi
|
||||
if [[ -n "${TOKENSAVE_REPOS_SEEN[$repo_root]:-}" ]]; then
|
||||
continue
|
||||
fi
|
||||
TOKENSAVE_REPOS_SEEN[$repo_root]=1
|
||||
|
||||
if [ -f "$repo_root/.tokensave/tokensave.db" ]; then
|
||||
bashio::log.info "Synchronizing TokenSave index: ${repo_root}"
|
||||
run_as_runtime_user tokensave sync "$repo_root" \
|
||||
|| bashio::log.warning "TokenSave sync failed for ${repo_root}"
|
||||
else
|
||||
bashio::log.info "Initializing TokenSave index: ${repo_root}"
|
||||
run_as_runtime_user tokensave init "$repo_root" \
|
||||
|| bashio::log.warning "TokenSave initialization failed for ${repo_root}"
|
||||
fi
|
||||
done < <(bashio::config.array 'tokensave_project_paths')
|
||||
fi
|
||||
|
||||
# Guide Claude to actually use the headroom compression tools so the MCP integration produces
|
||||
# real savings (otherwise the tools sit unused and `headroom savings` stays empty). Managed,
|
||||
# idempotent block appended to the user's global CLAUDE.md; removed when headroom is disabled.
|
||||
# Guide Claude to actually use the Headroom compression tools so the MCP integration produces
|
||||
# real savings when transparent proxying is unavailable. Managed, idempotent block appended to
|
||||
# the user's global CLAUDE.md; removed when Headroom is disabled.
|
||||
CLAUDE_MD="$HOME/.claude/CLAUDE.md"
|
||||
HEADROOM_GUIDE_BEGIN="<!-- BEGIN headroom (managed by claude_desktop addon) -->"
|
||||
if bashio::config.true 'install_headroom'; then
|
||||
if $HEADROOM_ENABLED; then
|
||||
mkdir -p "$(dirname "$CLAUDE_MD")"
|
||||
if ! { [ -f "$CLAUDE_MD" ] && grep -qF "$HEADROOM_GUIDE_BEGIN" "$CLAUDE_MD"; }; then
|
||||
bashio::log.info "Adding headroom usage guidance to CLAUDE.md"
|
||||
@@ -112,14 +253,13 @@ fi
|
||||
|
||||
if bashio::config.true 'install_rtk'; then
|
||||
if command -v rtk &> /dev/null; then
|
||||
if [ -f "$HOME/.claude/settings.json" ] && grep -q 'rtk hook claude' "$HOME/.claude/settings.json"; then
|
||||
bashio::log.info "rtk Claude Code hook already configured"
|
||||
else
|
||||
bashio::log.info "Configuring rtk Claude Code hook"
|
||||
RTK_NONINTERACTIVE=1 rtk init -g || bashio::log.warning "rtk global files configuration failed"
|
||||
python3 - <<'PY' || bashio::log.warning "Unable to configure rtk hook automatically"
|
||||
bashio::log.info "Configuring rtk Claude Code integration"
|
||||
run_as_runtime_user env RTK_NONINTERACTIVE=1 rtk init -g \
|
||||
|| bashio::log.warning "rtk global files configuration failed"
|
||||
python3 - <<'PY' || bashio::log.warning "Unable to configure rtk hook automatically"
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path.home() / ".claude" / "settings.json"
|
||||
try:
|
||||
data = json.loads(path.read_text()) if path.exists() else {}
|
||||
@@ -137,7 +277,6 @@ if not any("rtk hook claude" in json.dumps(entry) for entry in pre if isinstance
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, indent=2) + "\n")
|
||||
PY
|
||||
fi
|
||||
else
|
||||
bashio::log.warning "rtk is not available"
|
||||
fi
|
||||
@@ -209,16 +348,17 @@ if bashio::config.true 'install_caveman'; then
|
||||
bashio::log.info "caveman Claude Code plugin already configured"
|
||||
else
|
||||
bashio::log.info "Installing caveman Claude Code plugin"
|
||||
curl --connect-timeout 10 --max-time 60 -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash >/dev/null || bashio::log.warning "caveman install failed (offline?)"
|
||||
curl --connect-timeout 10 --max-time 60 -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/main/install.sh | bash > /dev/null \
|
||||
|| bashio::log.warning "caveman install failed (offline?)"
|
||||
fi
|
||||
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
|
||||
# Startup configuration runs as root, while Claude Desktop runs as abc. Return managed
|
||||
# persistent files to the configured runtime UID/GID after all writes complete.
|
||||
for managed_path in "$HOME/.claude" "$HOME/.claude.json" "$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
|
||||
|
||||
92
claude_desktop/rootfs/etc/cont-init.d/83-claude_permissions.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# 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)"
|
||||
PERMISSION_MODE="$(bashio::config 'permission_mode')"
|
||||
SETTINGS_PATH="$HOME/.claude/settings.json"
|
||||
STATE_PATH="$HOME/.claude/.addon-permission-mode.json"
|
||||
|
||||
case "$PERMISSION_MODE" in
|
||||
strict|auto|bypass) ;;
|
||||
*)
|
||||
bashio::log.warning "Unknown permission_mode '${PERMISSION_MODE}'; falling back to strict"
|
||||
PERMISSION_MODE="strict"
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$(dirname "$SETTINGS_PATH")"
|
||||
PERMISSION_MODE="$PERMISSION_MODE" SETTINGS_PATH="$SETTINGS_PATH" STATE_PATH="$STATE_PATH" python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
mode = os.environ["PERMISSION_MODE"]
|
||||
settings_path = Path(os.environ["SETTINGS_PATH"])
|
||||
state_path = Path(os.environ["STATE_PATH"])
|
||||
|
||||
try:
|
||||
settings = json.loads(settings_path.read_text()) if settings_path.exists() else {}
|
||||
except (OSError, json.JSONDecodeError):
|
||||
if settings_path.exists():
|
||||
settings_path.rename(settings_path.with_suffix(settings_path.suffix + ".bak"))
|
||||
settings = {}
|
||||
if not isinstance(settings, dict):
|
||||
settings = {}
|
||||
|
||||
try:
|
||||
state = json.loads(state_path.read_text()) if state_path.exists() else None
|
||||
except (OSError, json.JSONDecodeError):
|
||||
state = None
|
||||
if not isinstance(state, dict):
|
||||
state = None
|
||||
|
||||
permissions = settings.get("permissions")
|
||||
if not isinstance(permissions, dict):
|
||||
permissions = {}
|
||||
|
||||
if mode == "strict":
|
||||
# Restore the value that existed before the add-on first managed this setting.
|
||||
if state is not None:
|
||||
if state.get("previous_exists"):
|
||||
permissions["defaultMode"] = state.get("previous_value")
|
||||
else:
|
||||
permissions.pop("defaultMode", None)
|
||||
state_path.unlink(missing_ok=True)
|
||||
else:
|
||||
if state is None:
|
||||
state = {
|
||||
"previous_exists": "defaultMode" in permissions,
|
||||
"previous_value": permissions.get("defaultMode"),
|
||||
}
|
||||
state_path.write_text(json.dumps(state, indent=2) + "\n")
|
||||
state_path.chmod(0o600)
|
||||
permissions["defaultMode"] = "auto" if mode == "auto" else "bypassPermissions"
|
||||
|
||||
if permissions:
|
||||
settings["permissions"] = permissions
|
||||
else:
|
||||
settings.pop("permissions", None)
|
||||
|
||||
settings_path.write_text(json.dumps(settings, indent=2) + "\n")
|
||||
settings_path.chmod(0o600)
|
||||
PY
|
||||
|
||||
case "$PERMISSION_MODE" in
|
||||
strict)
|
||||
bashio::log.info "Claude Code permission mode: strict (normal prompts)"
|
||||
;;
|
||||
auto)
|
||||
bashio::log.info "Claude Code permission mode: auto (safe actions approved automatically)"
|
||||
;;
|
||||
bypass)
|
||||
bashio::log.warning "Claude Code permission mode: bypass (permission checks disabled for mounted data and available tools)"
|
||||
;;
|
||||
esac
|
||||
|
||||
chown -- "${PUID}:${PGID}" "$SETTINGS_PATH" 2> /dev/null || true
|
||||
if [ -e "$STATE_PATH" ]; then
|
||||
chown -- "${PUID}:${PGID}" "$STATE_PATH" 2> /dev/null || true
|
||||
fi
|
||||
17
claude_desktop/rootfs/etc/cont-init.d/84-claude_runtime_ownership.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -e
|
||||
|
||||
# Earlier configuration scripts intentionally run as root and may use the configured PUID/PGID
|
||||
# values when returning files to the runtime user. In bypass mode PUID can still be configured as
|
||||
# 0 even though 19-claude_bypass_runtime.sh remapped abc to a non-root UID. Reconcile ownership
|
||||
# with the effective desktop identity after all Claude configuration writes are complete.
|
||||
RUNTIME_UID="$(id -u abc)"
|
||||
RUNTIME_GID="$(id -g abc)"
|
||||
|
||||
for managed_path in "$HOME/.claude" "$HOME/.claude.json" "$HOME/.config/Claude"; do
|
||||
if [ -e "$managed_path" ]; then
|
||||
chown -R -- "${RUNTIME_UID}:${RUNTIME_GID}" "$managed_path" \
|
||||
|| bashio::log.warning "Unable to set effective runtime ownership on $managed_path"
|
||||
fi
|
||||
done
|
||||
@@ -1,101 +0,0 @@
|
||||
#!/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
|
||||
@@ -1 +0,0 @@
|
||||
longrun
|
||||
@@ -3,7 +3,13 @@
|
||||
declare port=8787
|
||||
declare host=127.0.0.1
|
||||
|
||||
if bashio::config.true 'install_headroom' && command -v headroom >/dev/null 2>&1; then
|
||||
# The dashboard is unauthenticated. Keep it container-local by default and bind all
|
||||
# interfaces only when the user explicitly opts in and maps port 8787.
|
||||
if bashio::config.true 'expose_headroom_dashboard'; then
|
||||
host=0.0.0.0
|
||||
fi
|
||||
|
||||
if bashio::config.true 'install_headroom' && command -v headroom > /dev/null 2>&1; then
|
||||
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
|
||||
|
||||
53
claude_desktop/rootfs/usr/local/bin/claude
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# shellcheck shell=bash
|
||||
set -o pipefail
|
||||
|
||||
REAL_CLAUDE="/usr/bin/claude"
|
||||
HEADROOM_BIN="/usr/local/bin/headroom"
|
||||
HEADROOM_URL="http://127.0.0.1:8787"
|
||||
PERMISSION_MODE="$(bashio::config 'permission_mode')"
|
||||
declare -a CLAUDE_PERMISSION_ARGS=()
|
||||
|
||||
case "$PERMISSION_MODE" in
|
||||
bypass)
|
||||
CLAUDE_PERMISSION_ARGS+=("--dangerously-skip-permissions")
|
||||
;;
|
||||
auto)
|
||||
CLAUDE_PERMISSION_ARGS+=("--permission-mode" "auto")
|
||||
;;
|
||||
strict|"")
|
||||
;;
|
||||
*)
|
||||
echo "claude wrapper: unknown permission_mode '${PERMISSION_MODE}', using strict mode" >&2
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -x "$REAL_CLAUDE" ]; then
|
||||
echo "claude wrapper: ${REAL_CLAUDE} is unavailable" >&2
|
||||
exit 127
|
||||
fi
|
||||
|
||||
# Claude Code rejects bypass mode when the effective UID is 0. Normal Desktop sessions run
|
||||
# as abc, which startup remaps to a non-root UID when bypass is selected. Also handle a user
|
||||
# invoking this wrapper directly from a root container console by dropping to abc here.
|
||||
if [ "$PERMISSION_MODE" = "bypass" ] && [ "$(id -u)" -eq 0 ]; then
|
||||
if command -v s6-setuidgid > /dev/null 2>&1 && [ "$(id -u abc)" -ne 0 ]; then
|
||||
exec s6-setuidgid abc "$0" "$@"
|
||||
fi
|
||||
echo "claude wrapper: bypass mode requires a non-root runtime user, but abc is still UID 0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if bashio::config.true 'install_headroom' && bashio::config.true 'headroom_wrap_claude_code'; then
|
||||
if [ -x "$HEADROOM_BIN" ] && curl -fsS --max-time 2 "${HEADROOM_URL}/health" > /dev/null 2>&1; then
|
||||
# Put /usr/bin before /usr/local/bin while Headroom resolves its upstream `claude`
|
||||
# executable; otherwise it would resolve this wrapper recursively.
|
||||
export HEADROOM_CONTEXT_TOOL="rtk"
|
||||
exec env PATH="/usr/bin:/bin:/usr/local/bin" \
|
||||
"$HEADROOM_BIN" wrap claude --no-proxy -- \
|
||||
"${CLAUDE_PERMISSION_ARGS[@]}" "$@"
|
||||
fi
|
||||
echo "claude wrapper: Headroom proxy is unavailable; launching Claude Code directly" >&2
|
||||
fi
|
||||
|
||||
exec "$REAL_CLAUDE" "${CLAUDE_PERMISSION_ARGS[@]}" "$@"
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
exec claude "$@"
|
||||
@@ -1,34 +1,44 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# Hourly rtk + headroom token-savings snapshot for the add-on log.
|
||||
# Invoked by cron (see /defaults/crontabs/root); its stdout is redirected to /proc/1/fd/1,
|
||||
# so the report appears in the add-on log. Doubles as a heartbeat: if the numbers stop
|
||||
# growing, the corresponding tool has stopped working.
|
||||
# with-contenv supplies HOME from the s6 envdir, so this honors a custom `data_location`
|
||||
# (see 20-folders.sh) instead of hardcoding /data/data; it also makes bashio::config
|
||||
# available for the install_headroom gate below.
|
||||
export NO_COLOR=1 # keep the add-on log free of ANSI color codes
|
||||
# Hourly RTK + Headroom + TokenSave token-savings snapshot for the add-on log.
|
||||
# Invoked by cron (see /defaults/crontabs/root); stdout is redirected to /proc/1/fd/1.
|
||||
# Each tool is reported independently so enabling Headroom cannot hide RTK or TokenSave data.
|
||||
# with-contenv supplies the configured persistent HOME.
|
||||
export NO_COLOR=1
|
||||
export PATH="/lsiopy/bin:/usr/local/bin:/usr/bin:/bin:${PATH}"
|
||||
|
||||
have_rtk=false; command -v rtk >/dev/null 2>&1 && have_rtk=true
|
||||
have_headroom=false; command -v headroom >/dev/null 2>&1 && have_headroom=true
|
||||
|
||||
# headroom is pip-installed unconditionally at build time, so its binary is on PATH even
|
||||
# when install_headroom is off — gate on the same config svc-headroom checks, and only
|
||||
# fall back to have_headroom as a secondary availability guard.
|
||||
headroom_enabled=false
|
||||
if bashio::config.true 'install_headroom' && $have_headroom; then
|
||||
headroom_enabled=true
|
||||
if ! bashio::config.true 'enable_tools_health_report'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Nothing to report if neither tool is active — stay quiet.
|
||||
if ! $have_rtk && ! $headroom_enabled; then exit 0; fi
|
||||
rtk_enabled=false
|
||||
headroom_enabled=false
|
||||
tokensave_enabled=false
|
||||
|
||||
echo "===== claude gains report $(date '+%Y-%m-%d %H:%M:%S') ====="
|
||||
if bashio::config.true 'install_rtk' && command -v rtk > /dev/null 2>&1; then
|
||||
rtk_enabled=true
|
||||
fi
|
||||
if bashio::config.true 'install_headroom' && command -v headroom > /dev/null 2>&1; then
|
||||
headroom_enabled=true
|
||||
fi
|
||||
if bashio::config.true 'install_tokensave' && command -v tokensave > /dev/null 2>&1; then
|
||||
tokensave_enabled=true
|
||||
fi
|
||||
|
||||
if ! $rtk_enabled && ! $headroom_enabled && ! $tokensave_enabled; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "===== claude tools report $(date '+%Y-%m-%d %H:%M:%S') ====="
|
||||
if $headroom_enabled; then
|
||||
echo "--- headroom savings ---"
|
||||
headroom savings 2>&1 || echo "[warn] headroom savings failed"
|
||||
elif $have_rtk; then
|
||||
fi
|
||||
if $rtk_enabled; then
|
||||
echo "--- rtk gain ---"
|
||||
rtk gain 2>&1 || echo "[warn] rtk gain failed"
|
||||
fi
|
||||
echo "===== end gains report ====="
|
||||
if $tokensave_enabled; then
|
||||
echo "--- tokensave gain ---"
|
||||
tokensave gain --all --range 30d 2>&1 || echo "[warn] tokensave gain failed"
|
||||
fi
|
||||
echo "===== end claude tools report ====="
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/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 -- "$@"
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/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"
|
||||
174
claude_desktop/rootfs/usr/local/bin/claude-tools-doctor.sh
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# Diagnose installation, registration, routing, indexing, permissions, and recorded savings without
|
||||
# printing MCP environment values (which may contain the Home Assistant access token).
|
||||
# shellcheck shell=bash
|
||||
set +e
|
||||
set -o pipefail
|
||||
export NO_COLOR=1
|
||||
export PATH="/lsiopy/bin:/usr/local/bin:/usr/bin:/bin:${PATH}"
|
||||
|
||||
section() {
|
||||
printf '\n=== %s ===\n' "$1"
|
||||
}
|
||||
|
||||
section "Installed binaries"
|
||||
for tool in claude claude-desktop headroom rtk tokensave git gh rg jq shellcheck yamllint hadolint actionlint; do
|
||||
resolved="$(command -v "$tool" 2> /dev/null || true)"
|
||||
if [ -n "$resolved" ]; then
|
||||
printf '%-16s %s\n' "$tool" "$resolved"
|
||||
else
|
||||
printf '%-16s %s\n' "$tool" "MISSING"
|
||||
fi
|
||||
done
|
||||
|
||||
section "Configured switches"
|
||||
for option in permission_mode install_headroom headroom_wrap_claude_code expose_headroom_dashboard install_rtk install_tokensave install_caveman enable_tools_health_report; do
|
||||
printf '%-30s %s\n' "$option" "$(bashio::config "$option")"
|
||||
done
|
||||
|
||||
section "Runtime identity"
|
||||
printf '%-30s %s\n' "configured PUID:PGID" "$(bashio::config 'PUID'):$(bashio::config 'PGID')"
|
||||
printf '%-30s %s\n' "effective abc UID:GID" "$(id -u abc):$(id -g abc)"
|
||||
printf '%-30s %s\n' "current process UID:GID" "$(id -u):$(id -g)"
|
||||
if [ "$(bashio::config 'permission_mode')" = "bypass" ]; then
|
||||
if [ "$(id -u abc)" -eq 0 ]; then
|
||||
echo "bypass runtime: ERROR - Claude Code will reject bypass permissions while abc is root"
|
||||
else
|
||||
echo "bypass runtime: OK - Claude Desktop and Cowork run as a non-root UID"
|
||||
fi
|
||||
fi
|
||||
|
||||
section "Claude Code permission state"
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path.home() / ".claude/settings.json"
|
||||
try:
|
||||
data = json.loads(path.read_text())
|
||||
except FileNotFoundError:
|
||||
print("settings: MISSING")
|
||||
except Exception as exc:
|
||||
print(f"settings: INVALID: {exc}")
|
||||
else:
|
||||
permissions = data.get("permissions", {})
|
||||
if isinstance(permissions, dict):
|
||||
print(f"permissions.defaultMode: {permissions.get('defaultMode', '<upstream default>')}")
|
||||
else:
|
||||
print("permissions: INVALID")
|
||||
print(f"managed-state marker: {(Path.home() / '.claude/.addon-permission-mode.json').exists()}")
|
||||
PY
|
||||
|
||||
section "MCP registrations (environment values redacted)"
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
paths = [
|
||||
Path.home() / ".claude.json",
|
||||
Path.home() / ".config/Claude/claude_desktop_config.json",
|
||||
]
|
||||
for path in paths:
|
||||
print(path)
|
||||
try:
|
||||
data = json.loads(path.read_text())
|
||||
except FileNotFoundError:
|
||||
print(" MISSING")
|
||||
continue
|
||||
except Exception as exc:
|
||||
print(f" INVALID: {exc}")
|
||||
continue
|
||||
servers = data.get("mcpServers", {})
|
||||
if not isinstance(servers, dict) or not servers:
|
||||
print(" no MCP servers")
|
||||
continue
|
||||
for name, spec in sorted(servers.items()):
|
||||
if not isinstance(spec, dict):
|
||||
print(f" {name}: invalid entry")
|
||||
continue
|
||||
command = spec.get("command", "?")
|
||||
args = spec.get("args", [])
|
||||
server_type = spec.get("type", "")
|
||||
suffix = f" type={server_type}" if server_type else ""
|
||||
print(f" {name}: {command} {args}{suffix}")
|
||||
if spec.get("env"):
|
||||
print(" env: <redacted>")
|
||||
PY
|
||||
|
||||
section "Claude Code hooks"
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path.home() / ".claude/settings.json"
|
||||
try:
|
||||
data = json.loads(path.read_text())
|
||||
except FileNotFoundError:
|
||||
print("MISSING")
|
||||
raise SystemExit(0)
|
||||
except Exception as exc:
|
||||
print(f"INVALID: {exc}")
|
||||
raise SystemExit(0)
|
||||
|
||||
hooks = data.get("hooks", {})
|
||||
if not isinstance(hooks, dict) or not hooks:
|
||||
print("no hooks")
|
||||
raise SystemExit(0)
|
||||
for event, entries in hooks.items():
|
||||
print(event)
|
||||
if not isinstance(entries, list):
|
||||
print(" invalid entries")
|
||||
continue
|
||||
for entry in entries:
|
||||
matcher = entry.get("matcher", "*") if isinstance(entry, dict) else "?"
|
||||
commands = entry.get("hooks", []) if isinstance(entry, dict) else []
|
||||
rendered = []
|
||||
for command in commands if isinstance(commands, list) else []:
|
||||
if isinstance(command, dict):
|
||||
rendered.append(" ".join([str(command.get("command", "?")), *map(str, command.get("args", []))]))
|
||||
print(f" matcher={matcher}: {', '.join(rendered) or 'no command'}")
|
||||
PY
|
||||
|
||||
section "Headroom"
|
||||
if bashio::config.true 'install_headroom'; then
|
||||
curl -fsS --max-time 2 http://127.0.0.1:8787/health && echo || echo "proxy health: FAILED"
|
||||
headroom mcp status || true
|
||||
headroom savings || true
|
||||
else
|
||||
echo "disabled"
|
||||
fi
|
||||
|
||||
section "RTK"
|
||||
if bashio::config.true 'install_rtk'; then
|
||||
rtk gain || true
|
||||
else
|
||||
echo "disabled"
|
||||
fi
|
||||
|
||||
section "TokenSave"
|
||||
if bashio::config.true 'install_tokensave'; then
|
||||
tokensave doctor --agent claude || true
|
||||
tokensave gain --all --range 30d || true
|
||||
while IFS= read -r configured_path; do
|
||||
[ -n "$configured_path" ] || continue
|
||||
repo_root="$(s6-setuidgid abc env HOME="$HOME" git -c safe.directory='*' -C "$configured_path" rev-parse --show-toplevel 2> /dev/null || true)"
|
||||
if [ -z "$repo_root" ]; then
|
||||
echo "${configured_path}: not a Git repository"
|
||||
elif [ -f "$repo_root/.tokensave/tokensave.db" ]; then
|
||||
s6-setuidgid abc env HOME="$HOME" tokensave status "$repo_root" --short || true
|
||||
else
|
||||
echo "${repo_root}: NOT INITIALIZED"
|
||||
fi
|
||||
done < <(bashio::config.array 'tokensave_project_paths')
|
||||
else
|
||||
echo "disabled"
|
||||
fi
|
||||
|
||||
section "Claude routing"
|
||||
printf 'PATH claude: %s\n' "$(command -v claude 2> /dev/null || true)"
|
||||
printf 'real claude: %s\n' "$([ -x /usr/bin/claude ] && echo /usr/bin/claude || echo MISSING)"
|
||||
if bashio::config.true 'headroom_wrap_claude_code'; then
|
||||
echo "PATH-based Claude Code launches are configured for Headroom wrapping."
|
||||
else
|
||||
echo "Claude Code Headroom wrapping is disabled; Headroom remains available through MCP."
|
||||
fi
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -3,5 +3,7 @@
|
||||
"upstream_repo": "linuxserver/docker-baseimage-selkies",
|
||||
"github_fulltag": true,
|
||||
"slug": "claude_desktop",
|
||||
"paused": false
|
||||
"paused": false,
|
||||
"upstream_version": "ubunturesolute-version-6dc44b0e",
|
||||
"last_update": "2026-07-13"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.9.16 (2026-07-11)
|
||||
- Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases)
|
||||
|
||||
## 2.9.14 (2026-06-20)
|
||||
- Update to latest version from Cleanuparr/Cleanuparr (changelog : https://github.com/Cleanuparr/Cleanuparr/releases)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#=== Home Assistant Addon ===#
|
||||
|
||||
# ARGs used in FROM must be declared before any FROM instruction
|
||||
ARG BUILD_UPSTREAM="2.9.14"
|
||||
ARG BUILD_UPSTREAM="2.9.16"
|
||||
|
||||
#################
|
||||
# 1 Build Image #
|
||||
|
||||
@@ -91,5 +91,5 @@ schema:
|
||||
TZ: str?
|
||||
slug: cleanuparr
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/cleanuparr
|
||||
version: "2.9.14"
|
||||
version: "2.9.16"
|
||||
webui: "[PROTO:ssl]://[HOST]:[PORT:11011]"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"last_update": "2026-06-20",
|
||||
"last_update": "2026-07-11",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "cleanuparr",
|
||||
"source": "github",
|
||||
"upstream_repo": "Cleanuparr/Cleanuparr",
|
||||
"upstream_version": "2.9.14"
|
||||
"upstream_version": "2.9.16"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,4 +1,7 @@
|
||||
|
||||
## 2.1.2 (2026-07-11)
|
||||
- Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases)
|
||||
|
||||
## 2.1.0 (2026-07-04)
|
||||
- Update to latest version from ajslater/codex (changelog : https://github.com/ajslater/codex/releases)
|
||||
|
||||
|
||||
@@ -101,4 +101,4 @@ schema:
|
||||
slug: codex
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons
|
||||
version: "2.1.0"
|
||||
version: "2.1.2"
|
||||
|
||||
BIN
codex/stats.png
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": "true",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-07-11",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "codex",
|
||||
"source": "github",
|
||||
"upstream_repo": "ajslater/codex",
|
||||
"upstream_version": "2.1.0"
|
||||
"upstream_version": "2.1.2"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,3 +1,9 @@
|
||||
## 8.19.18-3 (14-07-2026)
|
||||
- Force a fresh image pull for users left on a stale cached image (some upgrades kept running the old Elasticsearch 7.17.9 image, failing with `mv: cannot move '/data/config' ... Permission denied` and `AccessDeniedException[/usr/share/elasticsearch/data/nodes/0]`). Fully stop and update the add-on so Home Assistant pulls this build.
|
||||
- Replaced the cryptic `Permission denied` failure with a clear message when the add-on is not running as root (the state that caused the failure above).
|
||||
|
||||
## 8.19.18-2 (14-07-2026)
|
||||
- Minor bugs fixed
|
||||
## 8.19.18 (2026-07-14)
|
||||
|
||||
- Upgrade to Elasticsearch 8.19.18 (#2849). Note: despite the previous add-on version reading `8.14.3`, the shipped image was still Elasticsearch 7.17.9 — the Dockerfile upstream version was never bumped. This release actually delivers 8.x, making the add-on compatible with the `homeassistant-elasticsearch` integration (requires 8.14+).
|
||||
@@ -8,9 +14,10 @@
|
||||
- Removed the `ingest-attachment` plugin install: it is a bundled module since Elasticsearch 8.0.
|
||||
- Startup persistence logic rewritten as a proper init script (`/usr/local/bin/addon-init.sh`) instead of line-number-based entrypoint patching.
|
||||
- Added `updater.json` so upstream 8.19.x releases are tracked automatically (pinned to the 8.19 line: 9.x cannot read indices created in 7.x).
|
||||
- The upstream 8.x image ends the build as a non-root user with a read-only entrypoint; the Dockerfile now switches to root for the build steps that patch/install into it. The image stays root at runtime too (unchanged from 7.17.9): the upstream entrypoint no longer drops privileges itself, and `addon-init.sh` needs to chown/move pre-existing `/data` content that may be owned by root from earlier installs.
|
||||
- The upstream 8.x image ends the build as a non-root user with a read-only entrypoint; the Dockerfile now switches to root for the build steps that patch/install into it. The container also starts as root (unchanged from 7.17.9) so `addon-init.sh` can chown/move pre-existing `/data` content that may be owned by root from earlier installs; unlike 7.17.9's own entrypoint, the upstream 8.x entrypoint no longer drops privileges before starting Elasticsearch (which refuses to run as root), so `addon-init.sh` now does that itself via `chroot --userspec=1000:0` once its root-only work is done.
|
||||
- `env_vars` names starting with a digit are now rejected before export instead of crashing the entrypoint.
|
||||
- Fixed a startup failure (`mv: cannot move '/data/config' ... Permission denied`) on upgrade from an existing 7.17.9 install, caused by an earlier fix in this same release that switched the runtime user to non-root before this fix was in place.
|
||||
- Fixed a second regression from that same fix: without a privilege drop before starting Elasticsearch, both fresh installs and upgrades would fail Elasticsearch's own root-check ("can not run elasticsearch as root").
|
||||
|
||||
## 8.14.3-3 (2026-06-19)
|
||||
- Fix startup failing with `chroot: cannot change root directory` by allowing `capability sys_chroot` in the AppArmor profile (#2709)
|
||||
|
||||
@@ -141,9 +141,10 @@ HEALTHCHECK \
|
||||
--timeout=25s \
|
||||
CMD curl -A "HealthCheck: Docker/1.0" -s -f "http://127.0.0.1:${HEALTH_PORT}${HEALTH_URL}" &>/dev/null || exit 1
|
||||
|
||||
# Stay root at runtime: addon-init.sh must chown/mv pre-existing /data
|
||||
# content that may be owned by root from earlier installs, and the upstream
|
||||
# entrypoint no longer drops privileges itself, so a non-root container
|
||||
# can't touch that data at all. This matches the addon's own AppArmor
|
||||
# profile (chown, setuid, setgid, sys_chroot, mount capabilities), which
|
||||
# assumes a root process.
|
||||
# Start as root: addon-init.sh needs it to chown/move pre-existing /data
|
||||
# content that may be owned by root from earlier installs. It drops to
|
||||
# uid 1000 itself (via chroot --userspec) before Elasticsearch actually
|
||||
# starts, since Elasticsearch refuses to run as root and the upstream 8.x
|
||||
# entrypoint no longer does that drop on its own (7.x's did). This matches
|
||||
# the addon's own AppArmor profile (chown, setuid, setgid, sys_chroot,
|
||||
# mount capabilities).
|
||||
|
||||
@@ -90,4 +90,4 @@ slug: elasticsearch
|
||||
startup: services
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/elasticsearch
|
||||
version: 8.19.18
|
||||
version: 8.19.18-3
|
||||
|
||||
47
elasticsearch/rootfs/usr/local/bin/addon-init.sh
Normal file → Executable file
@@ -1,15 +1,25 @@
|
||||
#!/bin/bash
|
||||
# shellcheck shell=bash
|
||||
# Sourced by /usr/local/bin/docker-entrypoint.sh (right after "set -e"),
|
||||
# before Elasticsearch starts. Runs as root (the image stays root at
|
||||
# runtime - see Dockerfile); the official entrypoint does not drop
|
||||
# privileges itself, and Elasticsearch ends up running as root too.
|
||||
# before Elasticsearch starts. The container starts as root (see
|
||||
# Dockerfile) so this script can chown/move pre-existing /data content
|
||||
# that may be owned by root from earlier installs. Elasticsearch itself
|
||||
# refuses to run as root, and unlike 7.x the upstream 8.x entrypoint no
|
||||
# longer drops privileges on its own, so this script does it at the end
|
||||
# (section 6) by re-execing the entrypoint as uid 1000. On that re-exec'd
|
||||
# pass this script just returns immediately (see the guard right below).
|
||||
#
|
||||
# Responsibilities:
|
||||
# 1. Export user env_vars from /data/options.json
|
||||
# 2. Default xpack.security.enabled=false (7.x behavior) unless user overrides
|
||||
# 3. Relocate data & config to /data for persistence (idempotent)
|
||||
# 4. Guard major-version data migrations (7.x -> 8.x is automatic)
|
||||
# 5. Record the running version once Elasticsearch is confirmed healthy
|
||||
# 6. Drop root privileges before Elasticsearch actually starts
|
||||
|
||||
if [ -n "${_ADDON_INIT_REEXEC:-}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "-----------------------------------------------------------"
|
||||
echo " Add-on: Elasticsearch server"
|
||||
@@ -21,6 +31,17 @@ PERSISTENT_HOME="/data"
|
||||
VERSION_MARKER="$PERSISTENT_HOME/.addon-upstream-version"
|
||||
OPTIONS_JSON="/data/options.json"
|
||||
|
||||
# This first pass must be root so it can relocate and take ownership of
|
||||
# pre-existing /data content written by an earlier (root) install. If it
|
||||
# is not root (e.g. an old cached image that pinned USER 1000:0, or the
|
||||
# container being forced to another user), the moves/chowns below fail
|
||||
# with a cryptic "Permission denied"; fail loudly with the real reason.
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "FATAL: the Elasticsearch add-on must start as root (currently uid $(id -u))."
|
||||
echo "If you upgraded from an older version, the running image is likely stale - fully stop and update/reinstall the add-on so Home Assistant pulls the current image."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
############################
|
||||
# 1 Export user env_vars #
|
||||
############################
|
||||
@@ -84,7 +105,11 @@ if [ -n "$data_version" ] && [[ $current_major =~ ^[0-9]+$ ]]; then
|
||||
if [ -d "$PERSISTENT_HOME/config" ] && [ ! -L "$PERSISTENT_HOME/config" ]; then
|
||||
config_backup="$PERSISTENT_HOME/config.bak-$data_version"
|
||||
if [ ! -e "$config_backup" ]; then
|
||||
mv "$PERSISTENT_HOME/config" "$config_backup"
|
||||
if ! mv "$PERSISTENT_HOME/config" "$config_backup"; then
|
||||
echo "FATAL: could not archive the old config to $config_backup."
|
||||
echo "This add-on must run as root to migrate a previous install. Restore a Home Assistant backup and ensure the add-on is not forced to a non-root user."
|
||||
exit 1
|
||||
fi
|
||||
echo "NOTICE: previous config archived to $config_backup. Re-apply any custom settings to the new config."
|
||||
fi
|
||||
fi
|
||||
@@ -141,3 +166,17 @@ if [ "$data_version" != "$current_version" ]; then
|
||||
done
|
||||
) &
|
||||
fi
|
||||
|
||||
############################
|
||||
# 6 Drop privileges #
|
||||
############################
|
||||
|
||||
# Elasticsearch refuses to start as root ("can not run elasticsearch as
|
||||
# root"). 7.x's own entrypoint dropped to uid 1000 via chroot before
|
||||
# launching Elasticsearch; 8.x no longer does that, so do it here instead,
|
||||
# then let the entrypoint continue as uid 1000 (matches the sys_chroot /
|
||||
# setuid / setgid capabilities already granted in the AppArmor profile).
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
export _ADDON_INIT_REEXEC=1
|
||||
exec chroot --userspec=1000:0 / /usr/local/bin/docker-entrypoint.sh "$@"
|
||||
fi
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
emby/stats.png
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,4 +1,10 @@
|
||||
|
||||
## 4.10.0.19 (2026-07-13)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
## 4.10.0.18 (2026-07-11)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
## 4.10.0.17 (2026-07-04)
|
||||
- Update to latest version from linuxserver/docker-emby (changelog : https://github.com/linuxserver/docker-emby/releases)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_VERSION
|
||||
ARG BUILD_UPSTREAM="4.10.0.17"
|
||||
ARG BUILD_UPSTREAM="4.10.0.19"
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
##################
|
||||
|
||||
@@ -122,5 +122,5 @@ schema:
|
||||
slug: emby_nas
|
||||
udev: true
|
||||
url: https://github.com/alexbelgium/hassio-addons/tree/master/emby
|
||||
version: "4.10.0.17"
|
||||
version: "4.10.0.19"
|
||||
video: true
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"github_beta": "true",
|
||||
"last_update": "2026-07-04",
|
||||
"last_update": "2026-07-13",
|
||||
"repository": "alexbelgium/hassio-addons",
|
||||
"slug": "emby",
|
||||
"source": "github",
|
||||
"upstream_repo": "linuxserver/docker-emby",
|
||||
"upstream_version": "4.10.0.17"
|
||||
"upstream_version": "4.10.0.19"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
ente/stats.png
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.5 KiB |