Skip to content

Commit 265a88f

Browse files
committed
Query agentapi for status
1 parent c301da7 commit 265a88f

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-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: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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" 2>&1) ; then
28+
local error=$(echo "$resp" | head -n 1)
29+
message=${error:-Failed to fetch status}
30+
status=failure
31+
elif ! status=$(node -e "process.stdout.write(JSON.parse('$resp').status)" 2>&1) ; then
32+
local error=$(echo "$status" | head -n 1)
33+
message=${error:-Failed to parse status}
34+
status=failure
35+
elif [[ $status == "stable" ]] ; then
36+
message=complete
37+
status=complete
38+
else
39+
message=working
40+
status=working
41+
fi
42+
if [[ $message != $lastmessage ]] || [[ $status != $laststatus ]] ; then
43+
if ! resp=$(curl --request PATCH --silent --show-error --location \
44+
--data "{\"app_slug\":\"$slug\",\"state\":\"$status\",\"message\":\"$message\",\"uri\": \"$uri\"}" \
45+
--header "Content-Type: application/json" \
46+
--header "Coder-Session-Token: $token" \
47+
"$agenturl/api/v2/workspaceagents/me/app-status" 2>&1) ; then
48+
>&2 echo "Failed to update status"
49+
>&2 echo "cURL response: $resp"
50+
else
51+
lastmessage=$message
52+
laststatus=$status
53+
fi
54+
fi
55+
}
56+
57+
# loop runs the status updater every $1 seconds.
58+
# $1: interval in seconds
59+
# $2: workspace agent url
60+
# $3: workspace agent token
61+
# $4: llm agent api url
62+
# $5: status slug
63+
function loop() {
64+
local interval=$1 ; shift
65+
local agenturl=$1 ; shift
66+
local token=$1 ; shift
67+
local agentapiurl=$1 ; shift
68+
local slug=$1 ; shift
69+
while sleep "$interval" ; do
70+
run "$agenturl" "$token" "$agentapiurl" "$slug"
71+
done
72+
}
73+
74+
# configure checks for required vars then starts polling in the background.
75+
# $1: interval in seconds
76+
function configure() {
77+
local interval=$1 ; shift
78+
echo "Configuring task reporting via agent polling..."
79+
# Node will be used to handle JSON. Since the module already requires npm,
80+
# this should not be an extra requirement.
81+
if ! command -v "node" >/dev/null 2>&1 ; then
82+
>&2 echo "Node must be installed"
83+
exit 1
84+
fi
85+
local agenturl=${CODER_AGENT_URL-}
86+
if [[ -z $agenturl ]] ; then
87+
>&2 echo "CODER_AGENT_URL must be set"
88+
exit 1
89+
fi
90+
local token=${CODER_AGENT_TOKEN-}
91+
if [[ -z $token ]] ; then
92+
if [[ -z ${CODER_AGENT_TOKEN_FILE-} ]] ; then
93+
>&2 echo "Either CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set"
94+
exit 1
95+
elif [[ -f $CODER_AGENT_TOKEN_FILE ]] ; then
96+
if ! token=$(<"$CODER_AGENT_TOKEN_FILE") ; then
97+
>&2 echo "$CODER_AGENT_TOKEN_FILE could not be read"
98+
exit 1
99+
fi
100+
else
101+
>&2 echo "$CODER_AGENT_TOKEN_FILE does not exist or could not be accessed"
102+
exit 1
103+
fi
104+
fi
105+
local slug=${CODER_MCP_APP_STATUS_SLUG-}
106+
if [[ -z $slug ]] ; then
107+
>&2 echo "$CODER_MCP_APP_STATUS_SLUG must be set"
108+
exit 1
109+
fi
110+
local agentapiurl=${CODER_AGENT_API_URL-}
111+
if [[ -z $agentapiurl ]] ; then
112+
agentapiurl="http://localhost:3284"
113+
fi
114+
echo "Configured a $interval second poll"
115+
loop "$interval" "$agenturl" "$token" "$agentapiurl" "$slug" &
116+
}
117+
118+
# main configures polling if enabled.
119+
function main() {
120+
local interval=$1 ; shift
121+
if [[ -z "$interval" ]] ; then
122+
echo "Will not poll since interval is undefined or blank"
123+
elif [[ "$interval" == "0" ]] ; then
124+
echo "Will not poll since interval is zero"
125+
else
126+
configure "$interval"
127+
fi
128+
}
129+
130+
main "${INTERVAL-}"

0 commit comments

Comments
 (0)