Skip to content

Commit 3fb8d81

Browse files
committed
agentapi and ai task support
1 parent da67cd3 commit 3fb8d81

File tree

1 file changed

+155
-24
lines changed
  • registry/coder/modules/claude-code

1 file changed

+155
-24
lines changed

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

Lines changed: 155 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ terraform {
44
required_providers {
55
coder = {
66
source = "coder/coder"
7-
version = ">= 2.5"
7+
version = ">= 2.7"
88
}
99
}
1010
}
@@ -96,9 +96,69 @@ variable "experiment_tmux_session_save_interval" {
9696
default = "15"
9797
}
9898

99+
variable "install_agentapi" {
100+
type = bool
101+
description = "Whether to install AgentAPI."
102+
default = true
103+
}
104+
105+
variable "agentapi_version" {
106+
type = string
107+
description = "The version of AgentAPI to install."
108+
default = "v0.2.2"
109+
}
110+
99111
locals {
100-
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
101-
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
112+
workdir = trimsuffix(var.folder, "/")
113+
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
114+
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
115+
agentapi_start_command = <<-EOT
116+
#!/bin/bash
117+
set -e
118+
119+
# if the first argument is not empty, start claude with the prompt
120+
if [ -n "$1" ]; then
121+
prompt="$(cat ~/.claude-code-prompt)"
122+
cp ~/.claude-code-prompt /tmp/claude-code-prompt
123+
else
124+
rm -f /tmp/claude-code-prompt
125+
fi
126+
127+
agentapi_started_marker_file=~/.agentapi-started
128+
129+
# if we haven't started agentapi before, then there's no session to continue
130+
# using the --continue flag would fail
131+
continue_flag="--continue"
132+
if [ -f "$agentapi_started_marker_file" ]; then
133+
continue_flag=""
134+
fi
135+
touch "$agentapi_started_marker_file"
136+
137+
# use low width to fit in the tasks UI sidebar. height is adjusted to ~match the default 80k (80x1000) characters
138+
# visible in the terminal screen.
139+
agentapi server --term-width 67 --term-height 1190 -- bash -c "claude $continue_flag --dangerously-skip-permissions \"$(cat /tmp/claude-code-prompt)\""
140+
EOT
141+
agentapi_wait_for_start_command = <<-EOT
142+
#!/bin/bash
143+
set -o errexit
144+
set -o pipefail
145+
146+
echo "Waiting for agentapi server to start on port 3284..."
147+
for i in $(seq 1 15); do
148+
if lsof -i :3284 | grep -q 'LISTEN'; then
149+
echo "agentapi server started on port 3284."
150+
break
151+
fi
152+
echo "Waiting... ($i/15)"
153+
sleep 1
154+
done
155+
if ! lsof -i :3284 | grep -q 'LISTEN'; then
156+
echo "Error: agentapi server did not start on port 3284 after 15 seconds."
157+
exit 1
158+
fi
159+
EOT
160+
agentapi_start_command_base64 = base64encode(local.agentapi_start_command)
161+
agentapi_wait_for_start_command_base64 = base64encode(local.agentapi_wait_for_start_command)
102162
}
103163

104164
# Install and Initialize Claude Code
@@ -132,12 +192,12 @@ resource "coder_script" "claude_code" {
132192
fi
133193
}
134194
135-
if [ ! -d "${var.folder}" ]; then
136-
echo "Warning: The specified folder '${var.folder}' does not exist."
195+
if [ ! -d "${local.workdir}" ]; then
196+
echo "Warning: The specified folder '${local.workdir}' does not exist."
137197
echo "Creating the folder..."
138198
# The folder must exist before tmux is started or else claude will start
139199
# in the home directory.
140-
mkdir -p "${var.folder}"
200+
mkdir -p "${local.workdir}"
141201
echo "Folder created successfully."
142202
fi
143203
if [ -n "${local.encoded_pre_install_script}" ]; then
@@ -176,9 +236,38 @@ resource "coder_script" "claude_code" {
176236
npm install -g @anthropic-ai/claude-code@${var.claude_code_version}
177237
fi
178238
239+
# Install AgentAPI if enabled
240+
if [ "${var.install_agentapi}" = "true" ]; then
241+
echo "Installing AgentAPI..."
242+
arch=$(uname -m)
243+
if [ "$arch" = "x86_64" ]; then
244+
binary_name="agentapi-linux-amd64"
245+
elif [ "$arch" = "aarch64" ]; then
246+
binary_name="agentapi-linux-arm64"
247+
else
248+
echo "Error: Unsupported architecture: $arch"
249+
exit 1
250+
fi
251+
wget "https://github.com/coder/agentapi/releases/download/${var.agentapi_version}/$binary_name"
252+
chmod +x "$binary_name"
253+
sudo mv "$binary_name" /usr/local/bin/agentapi
254+
fi
255+
if ! command_exists agentapi; then
256+
echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually."
257+
exit 1
258+
fi
259+
260+
# save the prompt for the agentapi start command
261+
echo -n "$CODER_MCP_CLAUDE_TASK_PROMPT" > ~/.claude-code-prompt
262+
263+
echo -n "${local.agentapi_start_command_base64}" | base64 -d > ~/.agentapi-start-command
264+
chmod +x ~/.agentapi-start-command
265+
echo -n "${local.agentapi_wait_for_start_command_base64}" | base64 -d > ~/.agentapi-wait-for-start-command
266+
chmod +x ~/.agentapi-wait-for-start-command
267+
179268
if [ "${var.experiment_report_tasks}" = "true" ]; then
180269
echo "Configuring Claude Code to report tasks via Coder MCP..."
181-
coder exp mcp configure claude-code ${var.folder}
270+
coder exp mcp configure claude-code ${local.workdir} --ai-agentapi-url http://localhost:3284
182271
fi
183272
184273
if [ -n "${local.encoded_post_install_script}" ]; then
@@ -257,17 +346,16 @@ EOF
257346
export LANG=en_US.UTF-8
258347
export LC_ALL=en_US.UTF-8
259348
349+
tmux new-session -d -s claude-code-agentapi -c ${local.workdir} '~/.agentapi-start-command true; exec bash'
350+
~/.agentapi-wait-for-start-command
351+
260352
if [ "${var.experiment_tmux_session_persistence}" = "true" ]; then
261353
sleep 3
354+
fi
262355
263-
if ! tmux has-session -t claude-code 2>/dev/null; then
264-
# Only create a new session if one doesn't exist
265-
tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
266-
fi
267-
else
268-
if ! tmux has-session -t claude-code 2>/dev/null; then
269-
tmux new-session -d -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions \"$CODER_MCP_CLAUDE_TASK_PROMPT\""
270-
fi
356+
if ! tmux has-session -t claude-code 2>/dev/null; then
357+
# Only create a new session if one doesn't exist
358+
tmux new-session -d -s claude-code -c ${local.workdir} "agentapi attach; exec bash"
271359
fi
272360
fi
273361
@@ -297,9 +385,17 @@ EOF
297385
export LANG=en_US.UTF-8
298386
export LC_ALL=en_US.UTF-8
299387
388+
screen -U -dmS claude-code-agentapi bash -c '
389+
cd ${local.workdir}
390+
# setting the first argument will make claude use the prompt
391+
~/.agentapi-start-command true
392+
exec bash
393+
'
394+
~/.agentapi-wait-for-start-command
395+
300396
screen -U -dmS claude-code bash -c '
301-
cd ${var.folder}
302-
claude --dangerously-skip-permissions "$CODER_MCP_CLAUDE_TASK_PROMPT" | tee -a "$HOME/.claude-code.log"
397+
cd ${local.workdir}
398+
agentapi attach
303399
exec bash
304400
'
305401
else
@@ -312,6 +408,21 @@ EOF
312408
run_on_start = true
313409
}
314410

411+
resource "coder_app" "claude_code_web" {
412+
# use a short slug to mitigate https://github.com/coder/coder/issues/15178
413+
slug = "ccw"
414+
display_name = "Claude Code Web"
415+
agent_id = var.agent_id
416+
url = "http://localhost:3284/"
417+
icon = var.icon
418+
subdomain = true
419+
healthcheck {
420+
url = "http://localhost:3284/status"
421+
interval = 5
422+
threshold = 3
423+
}
424+
}
425+
315426
resource "coder_app" "claude_code" {
316427
slug = "claude-code"
317428
display_name = "Claude Code"
@@ -324,31 +435,51 @@ resource "coder_app" "claude_code" {
324435
export LC_ALL=en_US.UTF-8
325436
326437
if [ "${var.experiment_use_tmux}" = "true" ]; then
438+
if ! tmux has-session -t claude-code-agentapi 2>/dev/null; then
439+
# start agentapi without claude using the prompt (no argument)
440+
tmux new-session -d -s claude-code-agentapi -c ${local.workdir} '~/.agentapi-start-command; exec bash'
441+
~/.agentapi-wait-for-start-command
442+
fi
443+
327444
if tmux has-session -t claude-code 2>/dev/null; then
328445
echo "Attaching to existing Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
329-
# If Claude isn't running in the session, start it without the prompt
330-
if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "claude"; then
331-
tmux send-keys -t claude-code "cd ${var.folder} && claude -c --dangerously-skip-permissions" C-m
446+
# If agentapi attach isn't running in the session, start it
447+
if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "agentapi"; then
448+
tmux send-keys -t claude-code "cd ${local.workdir} && agentapi attach" C-m
332449
fi
333450
tmux attach-session -t claude-code
334451
else
335452
echo "Starting a new Claude Code tmux session." | tee -a "$HOME/.claude-code.log"
336-
tmux new-session -s claude-code -c ${var.folder} "claude --dangerously-skip-permissions | tee -a \"$HOME/.claude-code.log\"; exec bash"
453+
tmux new-session -s claude-code -c ${local.workdir} "agentapi attach; exec bash"
337454
fi
338455
elif [ "${var.experiment_use_screen}" = "true" ]; then
456+
if ! screen -list | grep -q "claude-code-agentapi"; then
457+
screen -S claude-code-agentapi bash -c '
458+
cd ${local.workdir}
459+
# start agentapi without claude using the prompt (no argument)
460+
~/.agentapi-start-command
461+
exec bash
462+
'
463+
fi
339464
if screen -list | grep -q "claude-code"; then
340465
echo "Attaching to existing Claude Code screen session." | tee -a "$HOME/.claude-code.log"
341466
screen -xRR claude-code
342467
else
343468
echo "Starting a new Claude Code screen session." | tee -a "$HOME/.claude-code.log"
344-
screen -S claude-code bash -c 'claude --dangerously-skip-permissions | tee -a "$HOME/.claude-code.log"; exec bash'
469+
screen -S claude-code bash -c 'agentapi attach; exec bash'
345470
fi
346471
else
347-
cd ${var.folder}
348-
claude
472+
cd ${local.workdir}
473+
agentapi attach
349474
fi
350475
EOT
351476
icon = var.icon
352477
order = var.order
353478
group = var.group
354479
}
480+
481+
resource "coder_ai_task" "claude_code" {
482+
sidebar_app {
483+
id = coder_app.claude_code.id
484+
}
485+
}

0 commit comments

Comments
 (0)