Skip to content

Commit 4dcaea7

Browse files
blink-so[bot]kylecarbs35C4n0r
authored
feat(claude-code): support binary distribution without Node.js dependency (#332)
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com> Co-authored-by: kyle <[email protected]> Co-authored-by: 35C4n0r <[email protected]>
1 parent c2bc5cd commit 4dcaea7

File tree

5 files changed

+64
-80
lines changed

5 files changed

+64
-80
lines changed

registry/coder/modules/claude-code/README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
1313
```tf
1414
module "claude-code" {
1515
source = "registry.coder.com/coder/claude-code/coder"
16-
version = "2.0.7"
16+
version = "2.1.0"
1717
agent_id = coder_agent.example.id
1818
folder = "/home/coder"
1919
install_claude_code = true
@@ -28,7 +28,6 @@ module "claude-code" {
2828
2929
## Prerequisites
3030

31-
- Node.js and npm must be installed in your workspace to install Claude Code
3231
- You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template
3332

3433
The `codercom/oss-dogfood:latest` container image can be used for testing on container-based workspaces.
@@ -84,7 +83,7 @@ resource "coder_agent" "main" {
8483
module "claude-code" {
8584
count = data.coder_workspace.me.start_count
8685
source = "registry.coder.com/coder/claude-code/coder"
87-
version = "2.0.7"
86+
version = "2.1.0"
8887
agent_id = coder_agent.example.id
8988
folder = "/home/coder"
9089
install_claude_code = true
@@ -102,7 +101,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
102101
```tf
103102
module "claude-code" {
104103
source = "registry.coder.com/coder/claude-code/coder"
105-
version = "2.0.7"
104+
version = "2.1.0"
106105
agent_id = coder_agent.example.id
107106
folder = "/home/coder"
108107
install_claude_code = true

registry/coder/modules/claude-code/main.tf

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ locals {
111111
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
112112
agentapi_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-start.sh"))
113113
agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh"))
114-
remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.js"))
114+
remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh"))
115115
claude_code_app_slug = "ccw"
116116
}
117117

@@ -129,6 +129,21 @@ resource "coder_script" "claude_code" {
129129
command -v "$1" >/dev/null 2>&1
130130
}
131131
132+
function install_claude_code_cli() {
133+
echo "Installing Claude Code via official installer"
134+
set +e
135+
curl -fsSL claude.ai/install.sh | bash -s -- "${var.claude_code_version}" 2>&1
136+
CURL_EXIT=$${PIPESTATUS[0]}
137+
set -e
138+
if [ $CURL_EXIT -ne 0 ]; then
139+
echo "Claude Code installer failed with exit code $$CURL_EXIT"
140+
fi
141+
142+
# Ensure binaries are discoverable.
143+
export PATH="~/.local/bin:$PATH"
144+
echo "Installed Claude Code successfully. Version: $(claude --version || echo 'unknown')"
145+
}
146+
132147
if [ ! -d "${local.workdir}" ]; then
133148
echo "Warning: The specified folder '${local.workdir}' does not exist."
134149
echo "Creating the folder..."
@@ -143,37 +158,7 @@ resource "coder_script" "claude_code" {
143158
fi
144159
145160
if [ "${var.install_claude_code}" = "true" ]; then
146-
if ! command_exists npm; then
147-
echo "npm not found, checking for Node.js installation..."
148-
if ! command_exists node; then
149-
echo "Node.js not found, installing Node.js via NVM..."
150-
export NVM_DIR="$HOME/.nvm"
151-
if [ ! -d "$NVM_DIR" ]; then
152-
mkdir -p "$NVM_DIR"
153-
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
154-
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
155-
else
156-
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
157-
fi
158-
159-
nvm install --lts
160-
nvm use --lts
161-
nvm alias default node
162-
163-
echo "Node.js installed: $(node --version)"
164-
echo "npm installed: $(npm --version)"
165-
else
166-
echo "Node.js is installed but npm is not available. Please install npm manually."
167-
exit 1
168-
fi
169-
fi
170-
echo "Installing Claude Code..."
171-
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
172-
fi
173-
174-
if ! command_exists node; then
175-
echo "Error: Node.js is not installed. Please install Node.js manually."
176-
exit 1
161+
install_claude_code_cli
177162
fi
178163
179164
# Install AgentAPI if enabled
@@ -214,7 +199,7 @@ resource "coder_script" "claude_code" {
214199
215200
echo -n "${local.agentapi_start_script_b64}" | base64 -d > "$module_path/scripts/agentapi-start.sh"
216201
echo -n "${local.agentapi_wait_for_start_script_b64}" | base64 -d > "$module_path/scripts/agentapi-wait-for-start.sh"
217-
echo -n "${local.remove_last_session_id_script_b64}" | base64 -d > "$module_path/scripts/remove-last-session-id.js"
202+
echo -n "${local.remove_last_session_id_script_b64}" | base64 -d > "$module_path/scripts/remove-last-session-id.sh"
218203
chmod +x "$module_path/scripts/agentapi-start.sh"
219204
chmod +x "$module_path/scripts/agentapi-wait-for-start.sh"
220205
@@ -292,4 +277,4 @@ resource "coder_ai_task" "claude_code" {
292277
sidebar_app {
293278
id = coder_app.claude_code_web.id
294279
}
295-
}
280+
}

registry/coder/modules/claude-code/scripts/agentapi-start.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ if [ -f "$log_file_path" ]; then
1919
mv "$log_file_path" "$log_file_path"".$(date +%s)"
2020
fi
2121

22-
# see the remove-last-session-id.js script for details
22+
# see the remove-last-session-id.sh script for details
2323
# about why we need it
2424
# avoid exiting if the script fails
25-
node "$scripts_dir/remove-last-session-id.js" "$(pwd)" || true
25+
bash "$scripts_dir/remove-last-session-id.sh" "$(pwd)" 2>/dev/null || true
2626

2727
# we'll be manually handling errors from this point on
2828
set +o errexit

registry/coder/modules/claude-code/scripts/remove-last-session-id.js

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# If lastSessionId is present in .claude.json, claude --continue will start a
2+
# conversation starting from that session. The problem is that lastSessionId
3+
# doesn't always point to the last session. The field is updated by claude only
4+
# at the point of normal CLI exit. If Claude exits with an error, or if the user
5+
# restarts the Coder workspace, lastSessionId will be stale, and claude --continue
6+
# will start from an old session.
7+
#
8+
# If lastSessionId is missing, claude seems to accurately figure out where to
9+
# start using the conversation history - even if the CLI previously exited with
10+
# an error.
11+
#
12+
# This script removes the lastSessionId field from .claude.json.
13+
if [ $# -eq 0 ]; then
14+
echo "No working directory provided - it must be the first argument"
15+
exit 1
16+
fi
17+
18+
# Get absolute path of working directory
19+
working_dir=$(realpath "$1")
20+
echo "workingDir $working_dir"
21+
22+
# Path to .claude.json
23+
claude_json_path="$HOME/.claude.json"
24+
echo ".claude.json path $claude_json_path"
25+
26+
# Check if .claude.json exists
27+
if [ ! -f "$claude_json_path" ]; then
28+
echo "No .claude.json file found"
29+
exit 0
30+
fi
31+
32+
# Use jq to check if lastSessionId exists for the working directory and remove it
33+
34+
if jq -e ".projects[\"$working_dir\"].lastSessionId" "$claude_json_path" > /dev/null 2>&1; then
35+
# Remove lastSessionId and update the file
36+
jq "del(.projects[\"$working_dir\"].lastSessionId)" "$claude_json_path" > "${claude_json_path}.tmp" && mv "${claude_json_path}.tmp" "$claude_json_path"
37+
echo "Removed lastSessionId from .claude.json"
38+
else
39+
echo "No lastSessionId found in .claude.json - nothing to do"
40+
fi

0 commit comments

Comments
 (0)