From 46ea24df20f560844fe8d5f0286984958d9d9615 Mon Sep 17 00:00:00 2001 From: Serge Gatezh <2880401+gatezh@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:49:51 -0600 Subject: [PATCH 1/3] fix(ralphex-fe): init rtk hook without the /mnt/claude host mount The rtk init block sat inside the `if [ -d /mnt/claude ]` guard, so a standalone container (no host ~/.claude mount) never got the PreToolUse rewrite hook and rtk stayed inert, silently. Hoist `mkdir -p /home/app/.claude` (rtk init -g fails without it), the `chown -R app:app` and the rtk init out of the guard; only the copying from /mnt/claude and the Playwright MCP patch stay behind it. Local additions are fenced with "Local:" banners, and the header no longer claims the file is copied as-is from umputun/ralphex. Fixes #128 --- ralphex-fe/init-docker.sh | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ralphex-fe/init-docker.sh b/ralphex-fe/init-docker.sh index 7b2cbc4..813a655 100755 --- a/ralphex-fe/init-docker.sh +++ b/ralphex-fe/init-docker.sh @@ -3,11 +3,15 @@ # The entrypoint (/init.sh) runs /srv/init.sh if it exists before the main command. # # Source: https://github.com/umputun/ralphex/blob/master/scripts/internal/init-docker.sh -# Copied as-is from umputun/ralphex. Check upstream for updates. +# Copied from umputun/ralphex; local additions are marked "Local:" or cite +# an issue. Check upstream for updates. + +# ── Local: ~/.claude must exist without the host mount (#128) ─────────────── +# rtk init -g below writes into it and fails if it is missing. +mkdir -p /home/app/.claude # copy only essential claude files (not the entire 2GB directory) if [ -d /mnt/claude ]; then - mkdir -p /home/app/.claude # copy config files only (not cache, history, debug, todos, etc.) for f in .credentials.json settings.json settings.local.json CLAUDE.md format.sh; do [ -e "/mnt/claude/$f" ] && cp -L "/mnt/claude/$f" "/home/app/.claude/$f" 2>/dev/null || true @@ -25,21 +29,15 @@ if [ -d /mnt/claude ]; then "$PLAYWRIGHT_MCP_CONFIG" > /tmp/playwright-mcp.json \ && mv /tmp/playwright-mcp.json "$PLAYWRIGHT_MCP_CONFIG" fi +fi - chown -R app:app /home/app/.claude +chown -R app:app /home/app/.claude - # ── RTK: ensure rewrite hook is configured ───────────────────────────── - # The host mount usually brings the hook; re-init idempotently in case the - # mounted ~/.claude carries none. --hook-only avoids workspace artifacts. - # Scope: this sits inside the /mnt/claude guard, so a container started - # without the host mount gets no rtk hook and rtk stays inert. - # RTK_TELEMETRY_DISABLED=1 is the supported opt-out, not a workaround: - # since rtk-ai/rtk#2477 (v0.44.0+) it short-circuits the telemetry consent - # prompt that would otherwise block on stdin here. timeout stays as a - # backstop against a future init-time hang. - if command -v rtk >/dev/null 2>&1; then - RTK_TELEMETRY_DISABLED=1 gosu app timeout 10 rtk init -g --hook-only --auto-patch 2>/dev/null || true - fi +# ── Local: RTK rewrite hook, with or without the host mount (#128) ────────── +# Idempotent; --hook-only avoids workspace artifacts. RTK_TELEMETRY_DISABLED=1 +# skips the consent prompt (rtk-ai/rtk#2477); timeout is a backstop. +if command -v rtk >/dev/null 2>&1; then + RTK_TELEMETRY_DISABLED=1 gosu app timeout 10 rtk init -g --hook-only --auto-patch 2>/dev/null || true fi # copy credentials extracted from macOS keychain (mounted separately) From 25080a0230a70cb241d7edc52012b31a76affc5a Mon Sep 17 00:00:00 2001 From: Serge Gatezh <2880401+gatezh@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:50:13 -0600 Subject: [PATCH 2/3] fix(rtk): close stdin on rtk init at all call sites A devcontainer postCreateCommand runs under a pseudo-TTY, so rtk's is_terminal() check passes and its telemetry consent prompt can block. RTK_TELEMETRY_DISABLED=1 opts out and `timeout 10` kills a hang, but a fired timeout is swallowed by `|| true` and leaves rtk silently unconfigured. Redirecting stdin from /dev/null makes the prompt unreachable instead. Applied to .devcontainer/init-plugins.sh, claude-code's init-plugins.sh and ralphex-fe/init-docker.sh. The env var and timeout stay; the adjacent comments now describe all three defences in three lines. Fixes #130 --- .devcontainer/init-plugins.sh | 10 ++++------ claude-code/.devcontainer/init-plugins.sh | 10 ++++------ ralphex-fe/init-docker.sh | 7 ++++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.devcontainer/init-plugins.sh b/.devcontainer/init-plugins.sh index 2aad0d9..7b27424 100755 --- a/.devcontainer/init-plugins.sh +++ b/.devcontainer/init-plugins.sh @@ -46,13 +46,11 @@ done # Initialize rtk global hook for Claude Code (auto-rewrite mode). # --hook-only: installs only the PreToolUse rewrite hook, no workspace artifacts. -# RTK_TELEMETRY_DISABLED=1 is the supported opt-out, not a workaround: since -# rtk-ai/rtk#2477 (v0.44.0+) it short-circuits the telemetry consent prompt that -# would otherwise block on stdin here. rtk's own TTY check is not enough — a -# devcontainer postCreateCommand gets a pseudo-TTY, so the prompt believes it is -# interactive. timeout stays as a backstop against a future init-time hang. +# Telemetry consent prompt: RTK_TELEMETRY_DISABLED=1 opts out (rtk-ai/rtk#2477), +# closed stdin defeats the pseudo-TTY postCreateCommand hands us, and timeout +# backstops any other init-time hang. echo "Initializing rtk (token optimizer)..." -RTK_TELEMETRY_DISABLED=1 timeout 10 rtk init -g --hook-only --auto-patch || { +RTK_TELEMETRY_DISABLED=1 timeout 10 rtk init -g --hook-only --auto-patch < /dev/null || { echo "Note: rtk init may have already been configured or rtk not available" } diff --git a/claude-code/.devcontainer/init-plugins.sh b/claude-code/.devcontainer/init-plugins.sh index 6f64ac7..c9fe42e 100644 --- a/claude-code/.devcontainer/init-plugins.sh +++ b/claude-code/.devcontainer/init-plugins.sh @@ -76,13 +76,11 @@ done # ── rtk init (token-optimized CLI proxy) ──────────────────────────────────── # Global hook-first mode: installs only the PreToolUse rewrite hook to ~/.claude/, # no workspace artifacts (CLAUDE.md, .rtk/). Safe to run multiple times. -# RTK_TELEMETRY_DISABLED=1 is the supported opt-out, not a workaround: since -# rtk-ai/rtk#2477 (v0.44.0+) it short-circuits the telemetry consent prompt that -# would otherwise block on stdin here. rtk's own TTY check is not enough — a -# devcontainer postCreateCommand gets a pseudo-TTY, so the prompt believes it is -# interactive. timeout stays as a backstop against a future init-time hang. +# Telemetry consent prompt: RTK_TELEMETRY_DISABLED=1 opts out (rtk-ai/rtk#2477), +# closed stdin defeats the pseudo-TTY postCreateCommand hands us, and timeout +# backstops any other init-time hang. if command -v rtk &>/dev/null; then - RTK_TELEMETRY_DISABLED=1 timeout 10 rtk init -g --hook-only --auto-patch 2>/dev/null || true + RTK_TELEMETRY_DISABLED=1 timeout 10 rtk init -g --hook-only --auto-patch < /dev/null 2>/dev/null || true fi # ── agent-browser skill ───────────────────────────────────────────────────── diff --git a/ralphex-fe/init-docker.sh b/ralphex-fe/init-docker.sh index 813a655..e437487 100755 --- a/ralphex-fe/init-docker.sh +++ b/ralphex-fe/init-docker.sh @@ -34,10 +34,11 @@ fi chown -R app:app /home/app/.claude # ── Local: RTK rewrite hook, with or without the host mount (#128) ────────── -# Idempotent; --hook-only avoids workspace artifacts. RTK_TELEMETRY_DISABLED=1 -# skips the consent prompt (rtk-ai/rtk#2477); timeout is a backstop. +# Idempotent; --hook-only avoids workspace artifacts. Telemetry prompt: +# RTK_TELEMETRY_DISABLED=1 opts out (rtk-ai/rtk#2477), closed stdin stops it +# blocking, and timeout backstops any other hang. if command -v rtk >/dev/null 2>&1; then - RTK_TELEMETRY_DISABLED=1 gosu app timeout 10 rtk init -g --hook-only --auto-patch 2>/dev/null || true + RTK_TELEMETRY_DISABLED=1 gosu app timeout 10 rtk init -g --hook-only --auto-patch < /dev/null 2>/dev/null || true fi # copy credentials extracted from macOS keychain (mounted separately) From 056d8d9fcdfb5692055129df4b9aea8de0fb28bb Mon Sep 17 00:00:00 2001 From: Serge Gatezh <2880401+gatezh@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:52:12 -0600 Subject: [PATCH 3/3] fix(ralphex-fe): keep the recursive chown inside the /mnt/claude guard The previous commit made chown -R /home/app/.claude unconditional. That would also rewrite the owner of anything bind-mounted into the directory, e.g. a host .credentials.json, which newer ralphex wrappers mount. Restore chown -R to the guard, where it only touches copies this script made, and chown just the directory itself unconditionally. rtk init runs as app via gosu, so the files it creates are already app-owned. --- ralphex-fe/init-docker.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ralphex-fe/init-docker.sh b/ralphex-fe/init-docker.sh index e437487..dcab520 100755 --- a/ralphex-fe/init-docker.sh +++ b/ralphex-fe/init-docker.sh @@ -7,8 +7,10 @@ # an issue. Check upstream for updates. # ── Local: ~/.claude must exist without the host mount (#128) ─────────────── -# rtk init -g below writes into it and fails if it is missing. +# rtk init -g below writes into it (as app) and fails if it is missing. Not +# recursive: only the copies below are chowned, never anything bind-mounted in. mkdir -p /home/app/.claude +chown app:app /home/app/.claude # copy only essential claude files (not the entire 2GB directory) if [ -d /mnt/claude ]; then @@ -29,9 +31,9 @@ if [ -d /mnt/claude ]; then "$PLAYWRIGHT_MCP_CONFIG" > /tmp/playwright-mcp.json \ && mv /tmp/playwright-mcp.json "$PLAYWRIGHT_MCP_CONFIG" fi -fi -chown -R app:app /home/app/.claude + chown -R app:app /home/app/.claude +fi # ── Local: RTK rewrite hook, with or without the host mount (#128) ────────── # Idempotent; --hook-only avoids workspace artifacts. Telemetry prompt: