From 4f91ee02f5255e574b1c76fd6da8a339a0028c20 Mon Sep 17 00:00:00 2001 From: Declan Carroll Date: Tue, 11 Aug 2026 12:49:44 +0100 Subject: [PATCH] fix: Make Codespaces secrets reach non-login shells and improve agent sessions (no-changelog) (#36042) Co-authored-by: Claude Fable 5 --- .devcontainer/codespaces/Dockerfile | 5 +++++ .devcontainer/codespaces/README.md | 15 +++++++++++++-- .devcontainer/codespaces/codespaces-secrets.sh | 7 +++++++ .devcontainer/codespaces/tmux.conf | 4 ++++ scripts/cloud-session.mjs | 8 ++++++-- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 .devcontainer/codespaces/tmux.conf diff --git a/.devcontainer/codespaces/Dockerfile b/.devcontainer/codespaces/Dockerfile index 0ac66a1bf08..5f2bdd6b005 100644 --- a/.devcontainer/codespaces/Dockerfile +++ b/.devcontainer/codespaces/Dockerfile @@ -24,6 +24,11 @@ COPY gitcredential-refresh.sh /usr/local/bin/gitcredential-refresh.sh COPY gh-shim.sh /usr/local/bin/gh RUN chmod +x /usr/local/bin/gitcredential-refresh.sh /usr/local/bin/gh +# VS Code terminals are non-login shells: they read bashrc, not profile.d +RUN echo '. /usr/local/lib/codespaces-env.sh' >> /etc/bash.bashrc + +COPY tmux.conf /etc/tmux.conf + RUN echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/node && chmod 0440 /etc/sudoers.d/node RUN mkdir -p /workspaces && chown node:node /workspaces RUN corepack enable diff --git a/.devcontainer/codespaces/README.md b/.devcontainer/codespaces/README.md index d06e68327fa..0470e479943 100644 --- a/.devcontainer/codespaces/README.md +++ b/.devcontainer/codespaces/README.md @@ -32,6 +32,8 @@ pnpm session rm # delete the codespace ``` - **Detach** with `Ctrl-b d` — the agent keeps working without you. +- **Scroll** with the mouse wheel (tmux mouse mode is on). To use the + terminal's own text selection, hold **Shift** and drag. - **Reattach** by running the same `pnpm session ` from any machine. - Each named session gets its own worktree (`/workspaces/wt-`, branch `session/`), so parallel agents never touch each other's tree. Builds @@ -92,8 +94,17 @@ After a stop, `pnpm session ` restarts the codespace (~30–60 s); run `sshd` devcontainer feature, don't remove it. - **User secrets aren't visible in ssh shells by default**: the codespace agent injects them into VS Code sessions only; they're delivered - base64-encoded to `/workspaces/.codespaces/shared/.env-secrets`. The image's - profile shim exports them for ssh/tmux sessions. + base64-encoded to `/workspaces/.codespaces/shared/.env-secrets`. The image + sources `/usr/local/lib/codespaces-env.sh` in login shells (profile.d), in + interactive shells (bashrc), and in the `pnpm session` prelude. If Claude + Code shows `Missing environment variables: FLAKY_MCP_TOKEN`, the shell that + started Claude did not source the file. Run + `. /usr/local/lib/codespaces-env.sh` and start Claude again. +- **You cannot paste images into a remote Claude session.** Image paste reads + the clipboard of the machine where `claude` runs — the codespace, not your + laptop. Drag the file into the VS Code explorer (or + `gh codespace cp shot.png remote:/workspaces/n8n/`) and give Claude the + path. The file stays on disk and survives detach and `--resume`. - **`git push` / `gh` return 401 in tmux and long sessions** — same root cause as the secrets gotcha, plus rotation: Codespaces refreshes the on-disk `GITHUB_TOKEN` every few minutes, so a login-time snapshot goes diff --git a/.devcontainer/codespaces/codespaces-secrets.sh b/.devcontainer/codespaces/codespaces-secrets.sh index e8d46b3da9b..a2f1f03334e 100644 --- a/.devcontainer/codespaces/codespaces-secrets.sh +++ b/.devcontainer/codespaces/codespaces-secrets.sh @@ -2,6 +2,13 @@ # one-time registrations. . /usr/local/lib/codespaces-env.sh +# Claude Code keeps its first-run state in ~/.claude.json. Write the defaults +# so a new codespace does not show the theme and trust prompts. The env +# secrets supply the login. +[ -f "$HOME/.claude.json" ] || printf '%s\n' \ + '{"hasCompletedOnboarding":true,"theme":"dark","projects":{"/workspaces/n8n":{"hasTrustDialogAccepted":true}}}' \ + >"$HOME/.claude.json" + # Register the Flaky MCP server for Claude Code. The config keeps a literal # ${FLAKY_MCP_TOKEN}; Claude Code expands it at connect time, so the token is # not written to disk. Forks have no repo secrets and skip this. diff --git a/.devcontainer/codespaces/tmux.conf b/.devcontainer/codespaces/tmux.conf new file mode 100644 index 00000000000..7d582b610bd --- /dev/null +++ b/.devcontainer/codespaces/tmux.conf @@ -0,0 +1,4 @@ +# Mouse scroll, a deep scrollback, and correct colors for agent sessions. +set -g mouse on +set -g history-limit 50000 +set -g default-terminal "tmux-256color" diff --git a/scripts/cloud-session.mjs b/scripts/cloud-session.mjs index 7e62c606883..1d893bdab0a 100644 --- a/scripts/cloud-session.mjs +++ b/scripts/cloud-session.mjs @@ -56,17 +56,21 @@ function ensureCodespace() { return cs.name; } +// The preludes go inside tmux's '…' argument: do not use single quotes in them. +// tmux commands are not login shells. Source the shared secrets so Claude Code +// finds ${FLAKY_MCP_TOKEN} in its process env. +const SECRETS = '. /usr/local/lib/codespaces-env.sh 2>/dev/null || true'; // Worktrees share the pnpm store but not the turbo cache; a shared TURBO_CACHE_DIR // (seeded from the main checkout) keeps new-worktree builds at cache-hit speed. -// No single quotes allowed here: the whole prelude rides inside tmux's '…' arg. const CACHE = 'export TURBO_CACHE_DIR=/workspaces/.turbo-cache; [ -d "$TURBO_CACHE_DIR" ] || cp -r /workspaces/n8n/.turbo/cache "$TURBO_CACHE_DIR" 2>/dev/null || mkdir -p "$TURBO_CACHE_DIR"'; function remoteCommand(session, extraArgs) { const claude = `claude ${extraArgs}`.trim(); - if (session === 'agent') return `${CACHE}; cd /workspaces/n8n && ${claude}`; + if (session === 'agent') return `${SECRETS}; ${CACHE}; cd /workspaces/n8n && ${claude}`; const wt = `/workspaces/wt-${session}`; const branch = `session/${session}`; return [ + SECRETS, CACHE, `if [ ! -d "${wt}" ]; then echo "Setting up worktree ${wt}…"`, `git -C /workspaces/n8n worktree add "${wt}" -b "${branch}" 2>/dev/null || git -C /workspaces/n8n worktree add "${wt}" "${branch}"`,