From 3fa399e3ade85ea116b29ffb9dbac13a7b3aabcf Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 08:13:24 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Drop=20the=20compiler?= =?UTF-8?q?=20and=20express=20the=20deb=20constraints=20properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build-essential` and `python3-dev` were installed and purged again in the same layer, so they existed purely to compile Python packages while `pip install` ran. Installing them means dpkg unpacks and configures some 50 extra packages, which on aarch64 happens under QEMU emulation and is a large part of the ~290s dpkg phase. Drop both. Their absence is the guard: a dependency that needs to be compiled now fails the build loudly. `--only-binary=:all:` would be the stricter version of the same idea, but it also rejects pure-Python source distributions that install fine without a compiler, so it is left out. `wget` and `git-crypt` lost their version constraints in #1238 because neither the unquoted nor the quoted `pkg>=version` spelling reaches apt. `apt-get satisfy` does take relational constraints, so use it and give them back the lower bounds #1224 asked for — git-crypt carries a different binNMU revision per architecture (+b1 on amd64, +b2 on arm64), which an exact pin cannot express. A second Renovate manager tracks the `"pkg (>= version)"` spelling, so both are back on the dashboard. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Pwumst3pLFGsbbzLSxYQMv --- .github/renovate.json | 11 +++++++++++ code-server/Dockerfile | 14 ++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 98792f2..52cd841 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -29,6 +29,17 @@ "datasourceTemplate": "deb", "depNameTemplate": "{{package}}" }, + { + "customType": "regex", + "managerFilePatterns": ["//Dockerfile$/"], + "matchStringsStrategy": "any", + "matchStrings": [ + "\"(?[a-z0-9][a-z0-9-]+) \\(>= (?[a-z0-9-:_+~.]+)\\)\"" + ], + "versioningTemplate": "deb", + "datasourceTemplate": "deb", + "depNameTemplate": "{{package}}" + }, { "customType": "regex", "managerFilePatterns": ["//Dockerfile$/"], diff --git a/code-server/Dockerfile b/code-server/Dockerfile index a69aa78..ce749e8 100755 --- a/code-server/Dockerfile +++ b/code-server/Dockerfile @@ -121,8 +121,6 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY requirements.txt /tmp/requirements.txt # Setup base system -# renovate: datasource=deb depName=build-essential versioning=deb -ARG build_essential_version="12.12" # renovate: datasource=deb depName=locales versioning=deb ARG locales_version="2.41-12+deb13u4" # renovate: datasource=deb depName=gnupg versioning=deb @@ -140,7 +138,6 @@ RUN \ \ && apt-get install -y --no-install-recommends \ ack=3.8.1-1 \ - build-essential="${build_essential_version}" \ colordiff=1.0.21-1 \ git=1:2.47.3-0+deb13u1 \ iputils-ping=3:20240905-3 \ @@ -151,10 +148,8 @@ RUN \ nmap=7.95+dfsg-3 \ openssh-client="${openssh_client_version}" \ openssl=3.5.7-1~deb13u2 \ - python3-dev=3.13.5-1 \ python3=3.13.5-1 \ unzip=6.0-29+deb13u1 \ - wget \ zip=3.0-15+deb13u1 \ zsh=5.9-8+b24 \ less=668-1 \ @@ -164,13 +159,16 @@ RUN \ cron=3.0pl1-197 \ lsb-release=12.1-1 \ openssh-server="${openssh_server_version}" \ - git-crypt \ php=2:8.4+96 \ php-curl=2:8.4+96 \ netcat-openbsd=1.229-1 \ htop=3.4.1-5 \ shellcheck=0.10.0-1 \ \ + && apt-get satisfy -y --no-install-recommends \ + "git-crypt (>= 0.7.0-0.1+b1)" \ + "wget (>= 1.25.0-2)" \ + \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ && locale-gen \ \ @@ -198,10 +196,6 @@ RUN \ \ && sed -i -e "s#bin/bash#bin/zsh#" /etc/passwd \ \ - && apt-get purge -y --auto-remove \ - build-essential \ - python3-dev \ - \ && find /usr/local \ \( -type d -a -name test -o -name tests -o -name '__pycache__' \) \ -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ From 59c447b2f239affc855b251824bf8bf0b292c53a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 08:25:26 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20Pre-install=20Claude=20Code=20a?= =?UTF-8?q?nd=20pull=20upstream=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code is installed into the image with the official installer and linked into PATH, so `claude` works in every terminal. The service now exports CLAUDE_CONFIG_DIR=/data/claude, and the user init creates that folder and migrates a login left behind in ~/.claude or ~/.claude.json, so logins and settings survive restarts and updates. Both the persistence and the migration follow upstream's app-vscode. Unlike everything else in this image, Claude Code is not version-pinned: the installer is the only documented install path and it does not expose a pinning interface that could be verified. It also updates itself at runtime, into the container filesystem rather than /data, so a build-time pin would say little about what is actually running. Fixes pulled from upstream app-vscode: - The `find` cleanup bound its `-exec` to the last `-o` branch only, so only *.pyo files were ever removed, never the test, tests and __pycache__ directories it lists. - `01-vs-mysql` created /root/.config but writes the client config to /etc/mysql/conf.d/service.cnf, which it never created. - `01-vs-user` linked ~/.ssh with `ln -sn`; when ~/.ssh exists as a real directory the link lands inside it as ~/.ssh/.ssh. It now clears that case, cleans up the nested link and uses `ln -sfn`. - `01-vs-mosquitto` linked with plain `ln -s`, which fails on a re-run and reported nothing. Now `ln -sf` with a warning on failure. - `code-server/finish` treated a stop by signal as a crash and never propagated an exit code to the container. It now reports the code, halts on SIGTERM and lets s6 restart only on a real crash. - `code` is a wrapper so files open in the running editor from a terminal, `ha` gets its zsh completion registered, and the esphome file association matches nested folders. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Pwumst3pLFGsbbzLSxYQMv --- README.md | 6 +++ code-server/DOCS.md | 20 ++++++++++ code-server/Dockerfile | 10 ++++- .../etc/s6-overlay/s6-rc.d/code-server/finish | 26 ++++++++++--- .../etc/s6-overlay/s6-rc.d/code-server/run | 5 +++ .../etc/s6-overlay/scripts/01-vs-mosquitto | 6 ++- .../rootfs/etc/s6-overlay/scripts/01-vs-mysql | 2 +- .../rootfs/etc/s6-overlay/scripts/01-vs-user | 38 ++++++++++++++++++- .../rootfs/root/.code-server/settings.json | 2 +- code-server/rootfs/root/.zshrc | 2 +- code-server/rootfs/usr/local/bin/code | 3 ++ 11 files changed, 106 insertions(+), 14 deletions(-) create mode 100755 code-server/rootfs/usr/local/bin/code diff --git a/README.md b/README.md index f7d049f..c8251eb 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,14 @@ Home Assistant configuration. Compared to the original: | `reboot`, `shutdown`, `restart` | – | ✓ | | Custom s6 services and cron | – | ✓ | | Tailscale, 1Password CLI, git-crypt, yq, PHP | – | ✓ | +| Claude Code, pre-installed | – | ✓ | | Extra extensions (Container Tools, GitHub PRs) | – | ✓ | | `packages`, `init_commands`, `config_path` | ✓ | – | +[Claude Code][claude-code] is installed and ready in every terminal: run +`claude` and log in once. Your login and settings are kept in the app's data +folder, so they survive restarts and updates. + **Warning**: this app runs with the Supervisor `admin` role and can access Docker. Used carelessly, it can break your entire system. @@ -47,6 +52,7 @@ below. [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg +[claude-code]: https://github.com/anthropics/claude-code [code-server]: https://github.com/coder/code-server [commits-shield]: https://img.shields.io/github/commit-activity/y/elcajon/app-code-server.svg [commits]: https://github.com/elcajon/app-code-server/commits/main diff --git a/code-server/DOCS.md b/code-server/DOCS.md index 5b14de2..52652c7 100644 --- a/code-server/DOCS.md +++ b/code-server/DOCS.md @@ -20,6 +20,7 @@ Added: - [Custom services](#custom-services) and a running cron daemon - Tailscale, 1Password CLI (`op`), git-crypt, yq, PHP, ShellCheck, htop, nano, netcat, yamllint and ESPHome +- [Claude Code](#claude-code), pre-installed - An OpenSSH server, installed but not started. Start it from a [custom service](#custom-services) if you want it. - Extensions: Container Tools, GitHub Pull Requests, Ruff, markdownlint, @@ -73,12 +74,30 @@ custom-services/ If a custom service keeps the app from starting, set `log_level` to `debug`. This skips all custom services until you set it back. +## Claude Code + +[Claude Code][claude-code] is installed in the image. Open a terminal in VS +Code, run `claude` and log in once — a Claude subscription or an Anthropic API +key works. + +The login, along with everything else Claude Code stores, is kept in `/data` +instead of the home folder, so it survives restarts and updates of this app. +This works through `CLAUDE_CONFIG_DIR`, which the app exports before starting +the code server; terminals inside VS Code inherit it. A login left over from an +earlier version of this app is copied over on the first start, without +overwriting anything already there. + +Claude Code keeps itself up to date and may replace its own binary at runtime. +Such an update lives in the container's filesystem, not in `/data`, so it is +gone after a restart and is fetched again when needed. + ## Persistent data The following survive restarts and updates: - VS Code settings and extensions you install yourself - `~/.ssh`, `~/.gitconfig` and the zsh history +- The Claude Code login and settings Common folders such as `homeassistant`, `share` and `addon_configs` are linked into the workspace (`/root`). @@ -89,6 +108,7 @@ The app keeps its default settings up to date until you change them. To go back to the defaults, open a terminal in VS Code and run `reset-settings`. +[claude-code]: https://github.com/anthropics/claude-code [hassio-addons]: https://github.com/hassio-addons/app-vscode [ha-addons]: https://github.com/elcajon/ha-repository-edge [my-ha-badge]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg diff --git a/code-server/Dockerfile b/code-server/Dockerfile index ce749e8..b26e103 100755 --- a/code-server/Dockerfile +++ b/code-server/Dockerfile @@ -177,6 +177,10 @@ RUN \ --install /usr/bin/python python /usr/bin/python3 10 \ && pip3 install --no-cache-dir --no-compile -r /tmp/requirements.txt \ \ + && curl -fsSL https://claude.ai/install.sh | bash \ + && test -x /root/.local/bin/claude \ + && ln -s /root/.local/bin/claude /usr/local/bin/claude \ + \ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &> /dev/null \ && echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ @@ -197,8 +201,10 @@ RUN \ && sed -i -e "s#bin/bash#bin/zsh#" /etc/passwd \ \ && find /usr/local \ - \( -type d -a -name test -o -name tests -o -name '__pycache__' \) \ - -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ + \( \ + \( -type d -a \( -name test -o -name tests -o -name '__pycache__' \) \) \ + -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \ + \) \ -exec rm -rf '{}' + \ \ && rm -fr \ diff --git a/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/finish b/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/finish index b04a814..55bbb68 100755 --- a/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/finish +++ b/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/finish @@ -3,9 +3,25 @@ # Home Assistant App: Code Server # Take down the S6 supervision tree when the code server fails # ============================================================================== -if [[ "${1}" -ne 0 ]] && [[ "${1}" -ne 256 ]]; then - bashio::log.warning "code-server crashed, halting app" - /run/s6/basedir/bin/halt -fi +declare exit_code_container +exit_code_container=$( /run/s6-linux-init-container-results/exitcode + fi + [[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt +elif [[ "${exit_code_service}" -ne 0 ]]; then + if [[ "${exit_code_container}" -eq 0 ]]; then + echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode + fi + exec /run/s6/basedir/bin/halt +fi diff --git a/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/run b/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/run index 1aa45f9..14ffb0f 100755 --- a/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/run +++ b/code-server/rootfs/etc/s6-overlay/s6-rc.d/code-server/run @@ -62,5 +62,10 @@ options+=(/root) export HASS_SERVER="http://supervisor/core" export HASS_TOKEN="${SUPERVISOR_TOKEN:-}" +# Keep the Claude Code configuration in the persistent data folder. The +# extension host and the integrated terminals inherit this, so both the +# extension and the claude binary use it. +export CLAUDE_CONFIG_DIR="/data/claude" + # Run the code server exec code-server "${options[@]}" diff --git a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mosquitto b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mosquitto index 71c23ca..2eb8554 100755 --- a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mosquitto +++ b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mosquitto @@ -22,6 +22,8 @@ if bashio::services.available "mqtt"; then echo "--username ${username}" } > /root/.config/mosquitto_sub - ln -s /root/.config/mosquitto_sub /root/.config/mosquitto_pub - ln -s /root/.config/mosquitto_sub /root/.config/mosquitto_rr + ln -sf /root/.config/mosquitto_sub /root/.config/mosquitto_pub \ + || bashio::log.warning 'Failed linking the mosquitto_pub configuration' + ln -sf /root/.config/mosquitto_sub /root/.config/mosquitto_rr \ + || bashio::log.warning 'Failed linking the mosquitto_rr configuration' fi diff --git a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mysql b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mysql index 3e2270d..7d4491e 100755 --- a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mysql +++ b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-mysql @@ -14,7 +14,7 @@ if bashio::services.available "mysql"; then port=$(bashio::services "mysql" "port") username=$(bashio::services "mysql" "username") - mkdir -p /root/.config + mkdir -p /etc/mysql/conf.d { echo "[client]" echo "host=${host}" diff --git a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-user b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-user index 4bd0376..579ad6e 100755 --- a/code-server/rootfs/etc/s6-overlay/scripts/01-vs-user +++ b/code-server/rootfs/etc/s6-overlay/scripts/01-vs-user @@ -4,6 +4,7 @@ # Persists user settings and installs custom user packages. # ============================================================================== readonly -a DIRECTORIES=(addon_configs addons backup homeassistant media share ssl) +readonly CLAUDE_USER_PATH=/data/claude readonly GIT_USER_PATH=/data/git readonly SSH_USER_PATH=/data/.ssh readonly ZSH_HISTORY_FILE=/root/.zsh_history @@ -24,7 +25,15 @@ if ! bashio::fs.directory_exists "${SSH_USER_PATH}"; then || bashio::exit.nok \ 'Failed setting permissions on persistent .ssh folder' fi -ln -sn "${SSH_USER_PATH}" ~/.ssh || bashio::log.warning "Failed linking .ssh" +# The image ships an empty ~/.ssh; without removing it, the link below +# would end up inside it as ~/.ssh/.ssh instead of replacing it. +if [[ -d ~/.ssh ]] && [[ ! -L ~/.ssh ]]; then + # Clean up the nested link left behind by earlier versions + rm -f ~/.ssh/.ssh + rmdir ~/.ssh \ + || bashio::log.warning "Not replacing non-empty ${HOME}/.ssh" +fi +ln -sfn "${SSH_USER_PATH}" ~/.ssh || bashio::log.warning "Failed linking .ssh" # Sets up ZSH shell touch "${ZSH_HISTORY_PERSISTANT_FILE}" \ @@ -51,4 +60,29 @@ if ! bashio::fs.file_exists "${GIT_USER_PATH}/.gitconfig"; then touch "${GIT_USER_PATH}/.gitconfig" \ || bashio::exit.nok 'Failed to create .gitconfig' fi -ln -s "${GIT_USER_PATH}/.gitconfig" ~/.gitconfig || bashio::log.warning "Failed linking .gitconfig" \ No newline at end of file +ln -s "${GIT_USER_PATH}/.gitconfig" ~/.gitconfig || bashio::log.warning "Failed linking .gitconfig" + +# Store Claude Code settings & credentials in app data folder. The service +# points Claude Code here through CLAUDE_CONFIG_DIR, which covers both of the +# locations it would otherwise use: ~/.claude and ~/.claude.json. +if ! bashio::fs.directory_exists "${CLAUDE_USER_PATH}"; then + mkdir -p "${CLAUDE_USER_PATH}" \ + || bashio::exit.nok 'Failed to create a persistent Claude Code folder' + + chmod 700 "${CLAUDE_USER_PATH}" \ + || bashio::exit.nok \ + 'Failed setting permissions on persistent Claude Code folder' +fi + +# Migrate the non-persistent locations left behind by an older app version. +# Existing files are never overwritten, and the originals are left alone; +# they are simply no longer read once CLAUDE_CONFIG_DIR is set. +if bashio::fs.directory_exists ~/.claude; then + cp -a -n ~/.claude/. "${CLAUDE_USER_PATH}/" \ + || bashio::log.warning 'Failed migrating the Claude Code folder' +fi + +if bashio::fs.file_exists ~/.claude.json; then + cp -a -n ~/.claude.json "${CLAUDE_USER_PATH}/.claude.json" \ + || bashio::log.warning 'Failed migrating the Claude Code configuration' +fi diff --git a/code-server/rootfs/root/.code-server/settings.json b/code-server/rootfs/root/.code-server/settings.json index 1625a0f..56565ad 100644 --- a/code-server/rootfs/root/.code-server/settings.json +++ b/code-server/rootfs/root/.code-server/settings.json @@ -1,7 +1,7 @@ { "files.associations": { "*.dash": "yaml", - "**/esphome/*.yaml": "esphome", + "**/esphome/**/*.yaml": "esphome", "**/homeassistant/*.yaml": "home-assistant" }, "files.watcherExclude": { diff --git a/code-server/rootfs/root/.zshrc b/code-server/rootfs/root/.zshrc index 28c6d4c..76cf1ea 100755 --- a/code-server/rootfs/root/.zshrc +++ b/code-server/rootfs/root/.zshrc @@ -99,7 +99,7 @@ source $ZSH/oh-my-zsh.sh alias reset-settings="cp /root/.code-server/settings.json /data/code-server/User/settings.json && echo 'Setting restored!'" # Home Assistant CLI -source <(ha completion zsh) +source <(ha completion zsh) && compdef _ha ha # Change Docker autocompletion # see https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker#settings diff --git a/code-server/rootfs/usr/local/bin/code b/code-server/rootfs/usr/local/bin/code new file mode 100755 index 0000000..ea03034 --- /dev/null +++ b/code-server/rootfs/usr/local/bin/code @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/local/lib/code-server/bin/code-server "${@}" From 3659d8277398c37fe2955f394c413cf7c9b9d2c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 08:43:50 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=A5=20Do=20not=20ship=20Claude=20C?= =?UTF-8?q?ode,=20only=20persist=20its=20configuration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing Claude Code into the image is dropped again; what stays is the persistence, which is what upstream's app-vscode does. Users install the `Anthropic.claude-code` extension themselves from the Extensions view. It is published on Open VSX, the registry code-server uses for extensions installed from inside the editor, and it carries its own `claude` binary, so nothing has to be baked into the image. The service keeps exporting CLAUDE_CONFIG_DIR=/data/claude and the user init keeps creating and migrating that folder, so the login survives restarts and updates either way. The documentation follows: the app no longer claims to ship Claude Code, and DOCS.md explains installing the extension, why signing in has to happen from a terminal behind ingress, and where the bundled binary sits for anyone who wants it on PATH. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Pwumst3pLFGsbbzLSxYQMv --- README.md | 6 ------ code-server/DOCS.md | 39 ++++++++++++++++++++++++--------------- code-server/Dockerfile | 4 ---- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index c8251eb..f7d049f 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,9 @@ Home Assistant configuration. Compared to the original: | `reboot`, `shutdown`, `restart` | – | ✓ | | Custom s6 services and cron | – | ✓ | | Tailscale, 1Password CLI, git-crypt, yq, PHP | – | ✓ | -| Claude Code, pre-installed | – | ✓ | | Extra extensions (Container Tools, GitHub PRs) | – | ✓ | | `packages`, `init_commands`, `config_path` | ✓ | – | -[Claude Code][claude-code] is installed and ready in every terminal: run -`claude` and log in once. Your login and settings are kept in the app's data -folder, so they survive restarts and updates. - **Warning**: this app runs with the Supervisor `admin` role and can access Docker. Used carelessly, it can break your entire system. @@ -52,7 +47,6 @@ below. [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg -[claude-code]: https://github.com/anthropics/claude-code [code-server]: https://github.com/coder/code-server [commits-shield]: https://img.shields.io/github/commit-activity/y/elcajon/app-code-server.svg [commits]: https://github.com/elcajon/app-code-server/commits/main diff --git a/code-server/DOCS.md b/code-server/DOCS.md index 52652c7..4c88487 100644 --- a/code-server/DOCS.md +++ b/code-server/DOCS.md @@ -20,7 +20,6 @@ Added: - [Custom services](#custom-services) and a running cron daemon - Tailscale, 1Password CLI (`op`), git-crypt, yq, PHP, ShellCheck, htop, nano, netcat, yamllint and ESPHome -- [Claude Code](#claude-code), pre-installed - An OpenSSH server, installed but not started. Start it from a [custom service](#custom-services) if you want it. - Extensions: Container Tools, GitHub Pull Requests, Ruff, markdownlint, @@ -76,20 +75,30 @@ If a custom service keeps the app from starting, set `log_level` to ## Claude Code -[Claude Code][claude-code] is installed in the image. Open a terminal in VS -Code, run `claude` and log in once — a Claude subscription or an Anthropic API -key works. +Claude Code is not part of this app, but it runs in it and its login is kept +across restarts. -The login, along with everything else Claude Code stores, is kept in `/data` -instead of the home folder, so it survives restarts and updates of this app. -This works through `CLAUDE_CONFIG_DIR`, which the app exports before starting -the code server; terminals inside VS Code inherit it. A login left over from an -earlier version of this app is copied over on the first start, without -overwriting anything already there. +Install the `Anthropic.claude-code` extension from the Extensions view. It is +published on [Open VSX][open-vsx], the registry code-server uses for extensions +you install yourself, and it brings its own `claude` binary, so nothing else has +to be installed. GitHub Copilot, by contrast, is only published on the Microsoft +Marketplace and cannot be installed here. -Claude Code keeps itself up to date and may replace its own binary at runtime. -Such an update lives in the container's filesystem, not in `/data`, so it is -gone after a restart and is fetched again when needed. +Sign in from a terminal rather than the editor: the browser flow redirects to a +`vscode://` URL, which does not survive Home Assistant's ingress. Open a +terminal in VS Code, run `claude`, then use `/login` and paste back the code it +asks for. + +Everything Claude Code stores is kept in `/data` instead of the home folder, so +your login survives restarts and updates of this app. That works through +`CLAUDE_CONFIG_DIR`, which this app exports before starting the code server; +terminals inside VS Code inherit it. A login left behind by an earlier version +of this app is copied over on the first start, without overwriting anything +already there. + +The `claude` binary lives inside the extension folder under +`/data/code-server/extensions/`. Symlink it into `/usr/local/bin` if you want it +on your `PATH` in every terminal. ## Persistent data @@ -97,7 +106,7 @@ The following survive restarts and updates: - VS Code settings and extensions you install yourself - `~/.ssh`, `~/.gitconfig` and the zsh history -- The Claude Code login and settings +- The Claude Code login and settings, if you install the extension Common folders such as `homeassistant`, `share` and `addon_configs` are linked into the workspace (`/root`). @@ -108,7 +117,7 @@ The app keeps its default settings up to date until you change them. To go back to the defaults, open a terminal in VS Code and run `reset-settings`. -[claude-code]: https://github.com/anthropics/claude-code +[open-vsx]: https://open-vsx.org [hassio-addons]: https://github.com/hassio-addons/app-vscode [ha-addons]: https://github.com/elcajon/ha-repository-edge [my-ha-badge]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg diff --git a/code-server/Dockerfile b/code-server/Dockerfile index b26e103..6904d47 100755 --- a/code-server/Dockerfile +++ b/code-server/Dockerfile @@ -177,10 +177,6 @@ RUN \ --install /usr/bin/python python /usr/bin/python3 10 \ && pip3 install --no-cache-dir --no-compile -r /tmp/requirements.txt \ \ - && curl -fsSL https://claude.ai/install.sh | bash \ - && test -x /root/.local/bin/claude \ - && ln -s /root/.local/bin/claude /usr/local/bin/claude \ - \ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg &> /dev/null \ && echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \