Skip to content

feat(install): add cloud.bash for Claude Code cloud environments - #14

Merged
AnnatarHe merged 2 commits into
mainfrom
claude/tender-curie-xflmdr
Oct 22, 2026
Merged

AnnatarHe merged 2 commits into
mainfrom
claude/tender-curie-xflmdr

Conversation

@AnnatarHe

@AnnatarHe AnnatarHe commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Until now, tracking Claude Code cloud sessions meant pasting a 60-line block into the cloud environment's setup script. This PR moves that block into this repo as cloud.bash, so the setup script needs one line:

curl -fsSL https://raw.githubusercontent.com/shelltime/installation/main/cloud.bash | bash

It does the same three things the block did:

  1. Installs the Linux CLI and daemon (x86_64 or arm64) from the latest release into ~/.shelltime/bin, and links shelltime into ~/.local/bin.
  2. Runs shelltime cc install.
  3. Writes ~/.shelltime/bin/cloud-session-start.sh and registers it as a Claude Code SessionStart hook. Running it again replaces the hook instead of adding a second one. The hook signs in with SHELLTIME_TOKEN and starts the daemon in each session. It prints nothing and does nothing when the variable isn't set.

Why it's safe to pipe into a setup script

  • It only runs in a cloud environment. It installs nothing unless CLAUDE_CODE_REMOTE is true. Cloud environments set it, and it's never true on your own machine (cloud environments docs). The docs only state this for the session's environment. In a cloud VM I confirmed that environment-manager, which runs the setup script (its setup_script step), has CLAUDE_CODE_REMOTE=true but not the environment's own variables, so the setup script inherits it. Run by mistake on a laptop, the script prints how to use the regular installer and changes nothing.
  • It never fails the setup script. If a setup script exits non-zero, sessions don't start. An EXIT trap at the top prints ShellTime setup failed; sessions will not be tracked and exits 0. That covers every case I tried: a failed download, a syntax error from a truncated file, running outside a cloud environment, and an outer script running set -euo pipefail. If curl itself fails, bash gets empty input and also exits 0.
  • A truncated download installs nothing. All the work is in functions, and the only call is main "$@" on the last line.
  • No new allowed domain. raw.githubusercontent.com and the GitHub release hosts are on the cloud environments' default (Trusted) list, which the guide already has users tick.
  • Fixes reach users without them editing anything. The script is downloaded again whenever the environment rebuilds its snapshot (after a settings change, or about once a week).

Also in this PR

  • A new test-cloud CI job on ubuntu-latest and ubuntu-24.04-arm. It first checks that nothing is installed without CLAUDE_CODE_REMOTE. Then it pipes the script into bash twice with CLAUDE_CODE_REMOTE=true and checks the binaries, the OTEL env in settings.json, that exactly one SessionStart hook is registered, and that the hook prints nothing without a token.
  • Notes on the script in README.md and CLAUDE.md.

Merge order

Merge this before shelltime/web#721. The guide links to main/cloud.bash, which only exists once this is merged.

Test plan

  • curl | bash-style run against a throwaway HOME in a Claude Code cloud VM: binaries installed, OTEL settings written, one hook after two runs, hook prints nothing without a token
  • CLAUDE_CODE_REMOTE unset or false: exits 0, nothing created
  • Failure paths: bad release URL, script cut off mid-function, script cut off before the trap, outer set -euo pipefail. All exit 0 with a message
  • shellcheck 0.11 is clean on cloud.bash and on the embedded hook
  • Ran the new CI job's steps locally against a temp HOME
  • test-cloud CI on x86_64 and arm64

🤖 Generated with Claude Code

https://claude.ai/code/session_01CXwVbjYvMsYbyyrcZnyJYV

A cloud environment's setup script can now set ShellTime up with one line:

    curl -fsSL https://raw.githubusercontent.com/shelltime/installation/main/cloud.bash | bash

instead of the long block the guide had people paste. The script does the
same thing: installs the Linux CLI and daemon into ~/.shelltime/bin, runs
`shelltime cc install`, and registers a SessionStart hook that signs in with
SHELLTIME_TOKEN and starts the daemon in every session. Fixes now reach an
environment whenever it rebuilds its snapshot.

A setup script that exits non-zero keeps sessions from starting, so an EXIT
trap reports any failure and exits 0, and nothing runs before the final main
call, so a truncated download installs nothing. raw.githubusercontent.com is
on the cloud environments' default allowlist, so no new domain is needed.

CI runs it twice on x86_64 and arm64 Linux and checks the binaries, the OTEL
settings, a single SessionStart hook and a silent hook without a token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXwVbjYvMsYbyyrcZnyJYV
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-22T11:12:42.102972Z
We couldn’t complete this review. Comment @codex review to try again.
f577dc2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@shelltime

shelltime Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

☁️ Claude Code cloud · Add reusable Claude Cloud initialization script

Added a reusable cloud initialization script for ShellTime and updated CI to test it, using CLAUDE_CODE_REMOTE rather than OS detection.

💰 Cost 🔢 Tokens ⏱️ Duration 🧠 Model 💬 Prompts ✏️ Lines
$5.66 10.3M 1h 48m (8m active) claude-opus-5-5 2 +119 / −4

10.3M tokens: 1.1K input · 45.3K output · 9.9M cache read · 344.8K cache write

📝 AI summary

Created /home/user/installation/cloud.bash to centralize the ShellTime setup previously duplicated as a long inline cloud-init command. The script is designed to be fetched from the installation repository and executed via curl | bash, keeping individual Claude Code cloud setup scripts shorter and making future maintenance easier.

Updated the script’s environment guard to check that CLAUDE_CODE_REMOTE is true, following the Claude Code cloud-environments documentation, instead of checking whether the operating system is Linux. Also updated /home/user/installation/.github/workflows/test.yaml to cover the new cloud setup script and its expected remote-environment behavior.

View this session on ShellTime →

Updated 2026-10-23 11:13 UTC by ShellTime · totals for the whole session so far · the session page is visible to its owner

Cloud environments set CLAUDE_CODE_REMOTE=true, and it is never true on a
user's own machine (https://code.claude.com/docs/en/cloud-environments).
That is the check that matters: run by mistake on a laptop, cloud.bash would
swap in Linux binaries and add a SessionStart hook, and a Linux desktop
passed the old uname check. The setup script inherits the variable from
environment-manager, which runs it.

CI now checks that nothing is installed without the variable, and sets it
for the real runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXwVbjYvMsYbyyrcZnyJYV
@AnnatarHe

Copy link
Copy Markdown
Contributor Author

@claude review

@claude

claude Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Claude finished @AnnatarHe's task in 28s —— View job


Review of PR #14: cloud.bash

  • Read cloud.bash, the CI job, README and CLAUDE.md changes
  • Review posted

I read the diff only. I didn't run the script or the CI job, and the test-cloud CI result is still unchecked in your test plan.

Overall: The design holds up. The EXIT trap, the CLAUDE_CODE_REMOTE gate and the single main "$@" call at the end do what the PR says. I found no blocking bugs, only the smaller points below.

Findings

  1. The non-cloud path also prints "setup failed" (cloud.bash:93-96, cloud.bash:28-30).

    • main does return 1 when CLAUDE_CODE_REMOTE isn't true, so the trap treats it as a failure.
    • Someone who runs it on a laptop sees the helpful refusal, then ShellTime setup failed; sessions will not be tracked. That is misleading, because nothing was attempted.
    • Suggestion: return 0 in that branch, or exit 0 from main. The CI step only greps for the refusal text and for exit 0, so it would still pass.
  2. jq is assumed but never checked (cloud.bash:83).

    • If jq is missing from the cloud image, the script fails after the CLI is installed. The hook file exists but isn't registered, and you get the generic failure message.
    • GitHub runners have jq, so CI can't catch this. Add command -v jq >/dev/null || { echo "jq is required" >&2; return 1; } up front so the cause is clear.
    • shelltime cc install may not guarantee that $SETTINGS_PATH exists, which jq also needs. If it can be missing, fall back to echo '{}'.
  3. A leftover settings.json.tmp on failure (cloud.bash:86-87). If jq fails, set -e exits and leaves settings.json.tmp behind. It's harmless, but you could clean it up in finish. settings.json itself is never half-written, which is good.

  4. The token appears on a command line (hook, line 73). shelltime auth --token "$SHELLTIME_TOKEN" makes the token visible in ps for a moment. In a single-user cloud VM this is low risk. If the CLI can read the token from an environment variable or stdin, that would be better; otherwise leave it.

  5. No integrity check on the download (cloud.bash:50-52). The binary is fetched from releases/latest with no checksum or signature check. That's the same trust model as install.bash, so this is a note rather than a request. If the release publishes a checksums.txt, verifying against it would be cheap.

  6. CI coverage gaps (.github/workflows/test.yaml).

    • The job never runs the hook with a token set, so the auth and daemon-start branches are untested. A stub shelltime and shelltime-daemon would cover this without a real token.
    • There is no CLAUDE_CODE_REMOTE=false case. It's covered by the unset case in practice, so this is minor.
    • The hook count check uses .hooks.SessionStart[].hooks[], which is fine given the shape jq writes.
  7. Nits

    • pgrep -fx "$st/bin/shelltime-daemon" relies on an exact command-line match. That works as the daemon is launched, but it breaks if the daemon is ever started with arguments.
    • README and CLAUDE.md are accurate. The CLAUDE.md bullet for cloud.bash is very long, so consider splitting it into sub-bullets, as is done for hooks/.

Verdict

Ready to merge once test-cloud passes on both architectures. I'd fix #1 and #2 first, since they affect what users see.

Fix #1 and #2 →
· branch claude/tender-curie-xflmdr

@AnnatarHe
AnnatarHe merged commit ca04c94 into main Oct 22, 2026
4 of 9 checks passed
@AnnatarHe
AnnatarHe deleted the claude/tender-curie-xflmdr branch October 22, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants