@@ -96,9 +96,57 @@ 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+
99111locals {
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+ encoded_pre_install_script = var. experiment_pre_install_script != null ? base64encode (var. experiment_pre_install_script ) : " "
113+ encoded_post_install_script = var. experiment_post_install_script != null ? base64encode (var. experiment_post_install_script ) : " "
114+ agentapi_start_command = <<- EOT
115+ #!/bin/bash
116+ set -e
117+
118+ # if the first argument is not empty, start claude with the prompt
119+ if [ -n "$1" ]; then
120+ prompt="$(cat ~/.claude-code-prompt)"
121+ else
122+ prompt=""
123+ fi
124+
125+ # use low width to fit in the tasks UI sidebar. height is adjusted to ~match the default 80k (80x1000) characters
126+ # visible in the terminal screen.
127+ agentapi server --term-width 67 --term-height 1190 -- bash -c "claude --continue --dangerously-skip-permissions \"$prompt\""
128+ EOT
129+ agentapi_wait_for_start_command = <<- EOT
130+ #!/bin/bash
131+ set -o errexit
132+ set -o pipefail
133+
134+ echo "Waiting for agentapi server to start on port 3284..."
135+ for i in $(seq 1 15); do
136+ if lsof -i :3284 | grep -q 'LISTEN'; then
137+ echo "agentapi server started on port 3284."
138+ break
139+ fi
140+ echo "Waiting... ($i/15)"
141+ sleep 1
142+ done
143+ if ! lsof -i :3284 | grep -q 'LISTEN'; then
144+ echo "Error: agentapi server did not start on port 3284 after 15 seconds."
145+ exit 1
146+ fi
147+ EOT
148+ agentapi_start_command_base64 = base64encode (local. agentapi_start_command )
149+ agentapi_wait_for_start_command_base64 = base64encode (local. agentapi_wait_for_start_command )
102150}
103151
104152# Install and Initialize Claude Code
@@ -176,9 +224,38 @@ resource "coder_script" "claude_code" {
176224 npm install -g @anthropic-ai/claude-code@${ var . claude_code_version }
177225 fi
178226
227+ # Install AgentAPI if enabled
228+ if [ "${ var . install_agentapi } " = "true" ]; then
229+ echo "Installing AgentAPI..."
230+ arch=$(uname -m)
231+ if [ "$arch" = "x86_64" ]; then
232+ binary_name="agentapi-linux-amd64"
233+ elif [ "$arch" = "aarch64" ]; then
234+ binary_name="agentapi-linux-arm64"
235+ else
236+ echo "Error: Unsupported architecture: $arch"
237+ exit 1
238+ fi
239+ wget "https://github.com/coder/agentapi/releases/download/${ var . agentapi_version } /$binary_name"
240+ chmod +x "$binary_name"
241+ sudo mv "$binary_name" /usr/local/bin/agentapi
242+ fi
243+ if ! command_exists agentapi; then
244+ echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually."
245+ exit 1
246+ fi
247+
248+ # save the prompt for the agentapi start command
249+ echo -n "$CODER_MCP_CLAUDE_TASK_PROMPT" > ~/.claude-code-prompt
250+
251+ echo -n "${ local . agentapi_start_command_base64 } " | base64 -d > ~/.agentapi-start-command
252+ chmod +x ~/.agentapi-start-command
253+ echo -n "${ local . agentapi_wait_for_start_command_base64 } " | base64 -d > ~/.agentapi-wait-for-start-command
254+ chmod +x ~/.agentapi-wait-for-start-command
255+
179256 if [ "${ var . experiment_report_tasks } " = "true" ]; then
180257 echo "Configuring Claude Code to report tasks via Coder MCP..."
181- coder exp mcp configure claude-code ${ var . folder }
258+ coder exp mcp configure claude-code ${ var . folder } --ai-agentapi-url http://localhost:3284
182259 fi
183260
184261 if [ -n "${ local . encoded_post_install_script } " ]; then
@@ -257,17 +334,16 @@ EOF
257334 export LANG=en_US.UTF-8
258335 export LC_ALL=en_US.UTF-8
259336
337+ tmux new-session -d -s claude-code-agentapi -c ${ var . folder } '~/.agentapi-start-command true; exec bash'
338+ ~/.agentapi-wait-for-start-command
339+
260340 if [ "${ var . experiment_tmux_session_persistence } " = "true" ]; then
261341 sleep 3
342+ fi
262343
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
344+ if ! tmux has-session -t claude-code 2>/dev/null; then
345+ # Only create a new session if one doesn't exist
346+ tmux new-session -d -s claude-code -c ${ var . folder } "agentapi attach; exec bash"
271347 fi
272348 fi
273349
297373 export LANG=en_US.UTF-8
298374 export LC_ALL=en_US.UTF-8
299375
376+ screen -U -dmS claude-code-agentapi bash -c '
377+ cd ${ var . folder }
378+ # setting the first argument will make claude use the prompt
379+ ~/.agentapi-start-command true
380+ exec bash
381+ '
382+ ~/.agentapi-wait-for-start-command
383+
300384 screen -U -dmS claude-code bash -c '
301385 cd ${ var . folder }
302- claude --dangerously-skip-permissions "$CODER_MCP_CLAUDE_TASK_PROMPT" | tee -a "$HOME/.claude-code.log"
386+ agentapi attach
303387 exec bash
304388 '
305389 else
312396 run_on_start = true
313397}
314398
399+ resource "coder_app" "claude_code_web" {
400+ slug = " ccw"
401+ display_name = " Claude Code Web"
402+ agent_id = var. agent_id
403+ url = " http://localhost:3284/"
404+ icon = var. icon
405+ subdomain = true
406+ healthcheck {
407+ url = " http://localhost:3284/status"
408+ interval = 5
409+ threshold = 3
410+ }
411+ }
412+
315413resource "coder_app" "claude_code" {
316414 slug = " claude-code"
317415 display_name = " Claude Code"
@@ -324,31 +422,51 @@ resource "coder_app" "claude_code" {
324422 export LC_ALL=en_US.UTF-8
325423
326424 if [ "${ var . experiment_use_tmux } " = "true" ]; then
425+ if ! tmux has-session -t claude-code-agentapi 2>/dev/null; then
426+ # start agentapi without claude using the prompt (no argument)
427+ tmux new-session -d -s claude-code-agentapi -c ${ var . folder } '~/.agentapi-start-command; exec bash'
428+ ~/.agentapi-wait-for-start-command
429+ fi
430+
327431 if tmux has-session -t claude-code 2>/dev/null; then
328432 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
433+ # If agentapi attach isn't running in the session, start it
434+ if ! tmux list-panes -t claude-code -F '#{pane_current_command}' | grep -q "agentapi "; then
435+ tmux send-keys -t claude-code "cd ${ var . folder } && agentapi attach " C-m
332436 fi
333437 tmux attach-session -t claude-code
334438 else
335439 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"
440+ tmux new-session -s claude-code -c ${ var . folder } "agentapi attach ; exec bash"
337441 fi
338442 elif [ "${ var . experiment_use_screen } " = "true" ]; then
443+ if ! screen -list | grep -q "claude-code-agentapi"; then
444+ screen -S claude-code-agentapi bash -c '
445+ cd ${ var . folder }
446+ # start agentapi without claude using the prompt (no argument)
447+ ~/.agentapi-start-command
448+ exec bash
449+ '
450+ fi
339451 if screen -list | grep -q "claude-code"; then
340452 echo "Attaching to existing Claude Code screen session." | tee -a "$HOME/.claude-code.log"
341453 screen -xRR claude-code
342454 else
343455 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'
456+ screen -S claude-code bash -c 'agentapi attach ; exec bash'
345457 fi
346458 else
347459 cd ${ var . folder }
348- claude
460+ agentapi attach
349461 fi
350462 EOT
351463 icon = var. icon
352464 order = var. order
353465 group = var. group
354466}
467+
468+ resource "coder_ai_task" "claude_code" {
469+ sidebar_app {
470+ id = coder_app. claude_code . id
471+ }
472+ }
0 commit comments