diff --git a/registry/coder/modules/claude-code/README.md b/registry/coder/modules/claude-code/README.md index c1d4a5d2a..313e43182 100644 --- a/registry/coder/modules/claude-code/README.md +++ b/registry/coder/modules/claude-code/README.md @@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "2.0.7" + version = "2.1.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true @@ -28,7 +28,6 @@ module "claude-code" { ## Prerequisites -- Node.js and npm must be installed in your workspace to install Claude Code - You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template The `codercom/oss-dogfood:latest` container image can be used for testing on container-based workspaces. @@ -84,7 +83,7 @@ resource "coder_agent" "main" { module "claude-code" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/claude-code/coder" - version = "2.0.7" + version = "2.1.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true @@ -102,7 +101,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "2.0.7" + version = "2.1.0" agent_id = coder_agent.example.id folder = "/home/coder" install_claude_code = true diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 7eac02451..7175311ff 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -111,7 +111,7 @@ locals { encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : "" agentapi_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-start.sh")) agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh")) - remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.js")) + remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh")) claude_code_app_slug = "ccw" } @@ -129,6 +129,21 @@ resource "coder_script" "claude_code" { command -v "$1" >/dev/null 2>&1 } + function install_claude_code_cli() { + echo "Installing Claude Code via official installer" + set +e + curl -fsSL claude.ai/install.sh | bash -s -- "${var.claude_code_version}" 2>&1 + CURL_EXIT=$${PIPESTATUS[0]} + set -e + if [ $CURL_EXIT -ne 0 ]; then + echo "Claude Code installer failed with exit code $$CURL_EXIT" + fi + + # Ensure binaries are discoverable. + export PATH="~/.local/bin:$PATH" + echo "Installed Claude Code successfully. Version: $(claude --version || echo 'unknown')" + } + if [ ! -d "${local.workdir}" ]; then echo "Warning: The specified folder '${local.workdir}' does not exist." echo "Creating the folder..." @@ -143,37 +158,7 @@ resource "coder_script" "claude_code" { fi if [ "${var.install_claude_code}" = "true" ]; then - if ! command_exists npm; then - echo "npm not found, checking for Node.js installation..." - if ! command_exists node; then - echo "Node.js not found, installing Node.js via NVM..." - export NVM_DIR="$HOME/.nvm" - if [ ! -d "$NVM_DIR" ]; then - mkdir -p "$NVM_DIR" - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - else - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - fi - - nvm install --lts - nvm use --lts - nvm alias default node - - echo "Node.js installed: $(node --version)" - echo "npm installed: $(npm --version)" - else - echo "Node.js is installed but npm is not available. Please install npm manually." - exit 1 - fi - fi - echo "Installing Claude Code..." - npm install -g @anthropic-ai/claude-code@${var.claude_code_version} - fi - - if ! command_exists node; then - echo "Error: Node.js is not installed. Please install Node.js manually." - exit 1 + install_claude_code_cli fi # Install AgentAPI if enabled @@ -214,7 +199,7 @@ resource "coder_script" "claude_code" { echo -n "${local.agentapi_start_script_b64}" | base64 -d > "$module_path/scripts/agentapi-start.sh" echo -n "${local.agentapi_wait_for_start_script_b64}" | base64 -d > "$module_path/scripts/agentapi-wait-for-start.sh" - echo -n "${local.remove_last_session_id_script_b64}" | base64 -d > "$module_path/scripts/remove-last-session-id.js" + echo -n "${local.remove_last_session_id_script_b64}" | base64 -d > "$module_path/scripts/remove-last-session-id.sh" chmod +x "$module_path/scripts/agentapi-start.sh" chmod +x "$module_path/scripts/agentapi-wait-for-start.sh" @@ -292,4 +277,4 @@ resource "coder_ai_task" "claude_code" { sidebar_app { id = coder_app.claude_code_web.id } -} +} \ No newline at end of file diff --git a/registry/coder/modules/claude-code/scripts/agentapi-start.sh b/registry/coder/modules/claude-code/scripts/agentapi-start.sh index c66b7f359..15aa5d0c8 100644 --- a/registry/coder/modules/claude-code/scripts/agentapi-start.sh +++ b/registry/coder/modules/claude-code/scripts/agentapi-start.sh @@ -19,10 +19,10 @@ if [ -f "$log_file_path" ]; then mv "$log_file_path" "$log_file_path"".$(date +%s)" fi -# see the remove-last-session-id.js script for details +# see the remove-last-session-id.sh script for details # about why we need it # avoid exiting if the script fails -node "$scripts_dir/remove-last-session-id.js" "$(pwd)" || true +bash "$scripts_dir/remove-last-session-id.sh" "$(pwd)" 2>/dev/null || true # we'll be manually handling errors from this point on set +o errexit diff --git a/registry/coder/modules/claude-code/scripts/remove-last-session-id.js b/registry/coder/modules/claude-code/scripts/remove-last-session-id.js deleted file mode 100644 index 0b66edfeb..000000000 --- a/registry/coder/modules/claude-code/scripts/remove-last-session-id.js +++ /dev/null @@ -1,40 +0,0 @@ -// If lastSessionId is present in .claude.json, claude --continue will start a -// conversation starting from that session. The problem is that lastSessionId -// doesn't always point to the last session. The field is updated by claude only -// at the point of normal CLI exit. If Claude exits with an error, or if the user -// restarts the Coder workspace, lastSessionId will be stale, and claude --continue -// will start from an old session. -// -// If lastSessionId is missing, claude seems to accurately figure out where to -// start using the conversation history - even if the CLI previously exited with -// an error. -// -// This script removes the lastSessionId field from .claude.json. -const path = require("path") -const fs = require("fs") - -const workingDirArg = process.argv[2] -if (!workingDirArg) { - console.log("No working directory provided - it must be the first argument") - process.exit(1) -} - -const workingDir = path.resolve(workingDirArg) -console.log("workingDir", workingDir) - - -const claudeJsonPath = path.join(process.env.HOME, ".claude.json") -console.log(".claude.json path", claudeJsonPath) -if (!fs.existsSync(claudeJsonPath)) { - console.log("No .claude.json file found") - process.exit(0) -} - -const claudeJson = JSON.parse(fs.readFileSync(claudeJsonPath, "utf8")) -if ("projects" in claudeJson && workingDir in claudeJson.projects && "lastSessionId" in claudeJson.projects[workingDir]) { - delete claudeJson.projects[workingDir].lastSessionId - fs.writeFileSync(claudeJsonPath, JSON.stringify(claudeJson, null, 2)) - console.log("Removed lastSessionId from .claude.json") -} else { - console.log("No lastSessionId found in .claude.json - nothing to do") -} diff --git a/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh b/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh new file mode 100755 index 000000000..dac86a03c --- /dev/null +++ b/registry/coder/modules/claude-code/scripts/remove-last-session-id.sh @@ -0,0 +1,40 @@ +# If lastSessionId is present in .claude.json, claude --continue will start a +# conversation starting from that session. The problem is that lastSessionId +# doesn't always point to the last session. The field is updated by claude only +# at the point of normal CLI exit. If Claude exits with an error, or if the user +# restarts the Coder workspace, lastSessionId will be stale, and claude --continue +# will start from an old session. +# +# If lastSessionId is missing, claude seems to accurately figure out where to +# start using the conversation history - even if the CLI previously exited with +# an error. +# +# This script removes the lastSessionId field from .claude.json. +if [ $# -eq 0 ]; then + echo "No working directory provided - it must be the first argument" + exit 1 +fi + +# Get absolute path of working directory +working_dir=$(realpath "$1") +echo "workingDir $working_dir" + +# Path to .claude.json +claude_json_path="$HOME/.claude.json" +echo ".claude.json path $claude_json_path" + +# Check if .claude.json exists +if [ ! -f "$claude_json_path" ]; then + echo "No .claude.json file found" + exit 0 +fi + +# Use jq to check if lastSessionId exists for the working directory and remove it + +if jq -e ".projects[\"$working_dir\"].lastSessionId" "$claude_json_path" > /dev/null 2>&1; then + # Remove lastSessionId and update the file + jq "del(.projects[\"$working_dir\"].lastSessionId)" "$claude_json_path" > "${claude_json_path}.tmp" && mv "${claude_json_path}.tmp" "$claude_json_path" + echo "Removed lastSessionId from .claude.json" +else + echo "No lastSessionId found in .claude.json - nothing to do" +fi