Skip to content

Commit 5ab30fa

Browse files
committed
Query agentapi for status
1 parent c301da7 commit 5ab30fa

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ variable "experiment_report_tasks" {
7272
default = false
7373
}
7474

75+
variable "experiment_report_tasks_interval" {
76+
type = number
77+
description = "How often to poll task status, or zero to disable."
78+
default = 0
79+
}
80+
7581
variable "experiment_pre_install_script" {
7682
type = string
7783
description = "Custom script to run before installing Claude Code."
@@ -247,6 +253,19 @@ resource "coder_app" "claude_code_web" {
247253
subdomain = true
248254
}
249255

256+
resource "coder_script" "task_reporter" {
257+
agent_id = var.agent_id
258+
display_name = "Task reporter"
259+
icon = var.icon
260+
# TODO: Should this be added into `coder exp` instead of being a bash script?
261+
script = templatefile("${path.module}/reporter.bash", {
262+
# Using the `cron` option would be nice, but it does not support sub-minute
263+
# resolutions, so the script will loop itself.
264+
INTERVAL : var.experiment_report_tasks_interval,
265+
})
266+
run_on_start = true
267+
}
268+
250269
resource "coder_app" "claude_code" {
251270
slug = "claude-code"
252271
display_name = "Claude Code"
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
lastmessage=""
5+
laststatus=""
6+
7+
# run queries for the status then relays it to coderd.
8+
# $1: workspace agent url
9+
# $2: workspace token
10+
# $3: llm agent api url
11+
# $4: status slug
12+
function run() {
13+
local agenturl=$1 ; shift
14+
local token=$1 ; shift
15+
local agentapiurl=$1 ; shift
16+
local slug=$1 ; shift
17+
local resp
18+
# TODO: this only returns 'stable' or 'running'.
19+
# 1. We need a message.
20+
# 2. We need 'working', 'complete', or 'failure' for the status.
21+
# 3. Not sure yet what the URI is but maybe we need that.
22+
# Do we add a new endpoint or modify this one?
23+
local status
24+
local message
25+
local uri=""
26+
if ! resp=$(curl --request GET --silent --show-error --location \
27+
"$agentapiurl/status") ; then
28+
message="Failed to fetch status"
29+
status=failure
30+
elif ! status=$(node -e "process.stdout.write(JSON.parse('$resp').status)" 2>&1) ; then
31+
message="Failed to parse status"
32+
status=failure
33+
elif [[ $status == "stable" ]] ; then
34+
message=complete
35+
status=complete
36+
else
37+
message=working
38+
status=working
39+
fi
40+
if [[ $message != $lastmessage ]] || [[ $status != $laststatus ]] ; then
41+
if ! resp=$(curl --request PATCH --silent --show-error --location \
42+
--data "{\"app_slug\":\"$slug\",\"state\":\"$status\",\"message\":\"$message\",\"uri\": \"$uri\"}" \
43+
--header "Content-Type: application/json" \
44+
--header "Coder-Session-Token: $token" \
45+
"$agenturl/api/v2/workspaceagents/me/app-status") ; then
46+
>&2 echo "Failed to update status"
47+
>&2 echo "cURL response: $resp"
48+
else
49+
lastmessage=$message
50+
laststatus=$status
51+
fi
52+
fi
53+
}
54+
55+
# loop runs the status updater every $1 seconds.
56+
# $1: interval in seconds
57+
# $2: workspace agent url
58+
# $3: workspace agent token
59+
# $4: llm agent api url
60+
# $5: status slug
61+
function loop() {
62+
local interval=$1 ; shift
63+
local agenturl=$1 ; shift
64+
local token=$1 ; shift
65+
local agentapiurl=$1 ; shift
66+
local slug=$1 ; shift
67+
while sleep "$interval" ; do
68+
run "$agenturl" "$token" "$agentapiurl" "$slug"
69+
done
70+
}
71+
72+
# configure checks for required vars then starts polling in the background.
73+
# $1: interval in seconds
74+
function configure() {
75+
local interval=$1 ; shift
76+
echo "Configuring task reporting via agent polling..."
77+
# Node will be used to handle JSON. Since the module already requires npm,
78+
# this should not be an extra requirement.
79+
if ! command -v "node" >/dev/null 2>&1 ; then
80+
>&2 echo "Node must be installed"
81+
exit 1
82+
fi
83+
local agenturl=${CODER_AGENT_URL-}
84+
if [[ -z $agenturl ]] ; then
85+
>&2 echo "CODER_AGENT_URL must be set"
86+
exit 1
87+
fi
88+
local token=${CODER_AGENT_TOKEN-}
89+
if [[ -z $token ]] ; then
90+
if [[ -z ${CODER_AGENT_TOKEN_FILE-} ]] ; then
91+
>&2 echo "Either CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set"
92+
exit 1
93+
elif [[ -f $CODER_AGENT_TOKEN_FILE ]] ; then
94+
if ! token=$(<"$CODER_AGENT_TOKEN_FILE") ; then
95+
>&2 echo "$CODER_AGENT_TOKEN_FILE could not be read"
96+
exit 1
97+
fi
98+
else
99+
>&2 echo "$CODER_AGENT_TOKEN_FILE does not exist or could not be accessed"
100+
exit 1
101+
fi
102+
fi
103+
local slug=${CODER_MCP_APP_STATUS_SLUG-}
104+
if [[ -z $slug ]] ; then
105+
>&2 echo "$CODER_MCP_APP_STATUS_SLUG must be set"
106+
exit 1
107+
fi
108+
local agentapiurl=${CODER_AGENT_API_URL-}
109+
if [[ -z $agentapiurl ]] ; then
110+
agentapiurl="http://localhost:3284"
111+
fi
112+
echo "Configured a $interval second poll"
113+
loop "$interval" "$agenturl" "$token" "$agentapiurl" "$slug" &
114+
}
115+
116+
# main configures polling if enabled.
117+
function main() {
118+
local interval=$1 ; shift
119+
if [[ -z "$interval" ]] ; then
120+
echo "Will not poll since interval is undefined or blank"
121+
elif [[ "$interval" == "0" ]] ; then
122+
echo "Will not poll since interval is zero"
123+
else
124+
configure "$interval"
125+
fi
126+
}
127+
128+
main "${INTERVAL-}"

0 commit comments

Comments
 (0)