Skip to content

Commit d4686e0

Browse files
committed
Query agentapi for status
1 parent c301da7 commit d4686e0

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-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 in seconds, 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: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
lastmessage=""
5+
laststatus=""
6+
7+
# json-stringify returns a json string for updating the status.
8+
# $1: status slug
9+
# $2: status
10+
# $3: message
11+
# $4: uri
12+
function json-stringify() {
13+
node -e "process.stdout.write(JSON.stringify({
14+
app_slug: process.argv[1],
15+
state: process.argv[2],
16+
message: process.argv[3].substring(0, 160),
17+
uri: process.argv[4],
18+
}))" "$@"
19+
}
20+
21+
# json-parse returns the specified key from the provided json.
22+
# $1: the key
23+
# $2: the json
24+
function json-parse() {
25+
node -e "process.stdout.write(JSON.parse(process.argv[2])[process.argv[1]])" "$@"
26+
}
27+
28+
# run queries for the status then relays it to coderd.
29+
# $1: workspace agent uri
30+
# $2: workspace token
31+
# $3: llm agent api uri
32+
# $4: status slug
33+
function run() {
34+
local agenturi=$1 ; shift
35+
local token=$1 ; shift
36+
local agentapiuri=$1 ; shift
37+
local slug=$1 ; shift
38+
local resp
39+
# TODO: this only returns 'stable' or 'running'.
40+
# 1. We need a message.
41+
# 2. We need 'working', 'complete', or 'failure' for the status.
42+
# 3. Not sure yet what the URI is but maybe we need that.
43+
# Do we add a new endpoint or modify this one?
44+
local status
45+
local message
46+
local uri=""
47+
if ! resp=$(curl --fail-with-body --silent --show-error --location \
48+
"$agentapiuri/status" 2>&1) ; then
49+
message=$(echo "$resp" | xargs)
50+
status=failure
51+
elif ! status=$(json-parse "status" "$resp" 2>&1) ; then
52+
message=$(echo "$status" | xargs)
53+
status=failure
54+
elif [[ $status == stable ]] ; then
55+
message=complete
56+
status=complete
57+
else
58+
message=working
59+
status=working
60+
fi
61+
if [[ $message != $lastmessage ]] || [[ $status != $laststatus ]] ; then
62+
local data
63+
if ! data=$(json-stringify "$slug" "$status" "$message" "$uri" 2>&1) ; then
64+
>&2 echo "Failed to update status"
65+
>&2 echo "$data"
66+
elif ! resp=$(curl --request PATCH \
67+
--fail-with-body --silent --show-error --location \
68+
--data "$data" \
69+
--header "Content-Type: application/json" \
70+
--header "Coder-Session-Token: $token" \
71+
"$agenturi/api/v2/workspaceagents/me/app-status" 2>&1) ; then
72+
>&2 echo "Failed to update status"
73+
>&2 echo "$resp"
74+
>&2 echo "message: $message"
75+
>&2 echo "status: $status"
76+
else
77+
lastmessage=$message
78+
laststatus=$status
79+
fi
80+
fi
81+
}
82+
83+
# loop runs the status updater every $1 seconds.
84+
# $1: interval in seconds
85+
# $2: workspace agent uri
86+
# $3: workspace agent token
87+
# $4: llm agent api uri
88+
# $5: status slug
89+
function loop() {
90+
local interval=$1 ; shift
91+
local agenturi=$1 ; shift
92+
local token=$1 ; shift
93+
local agentapiuri=$1 ; shift
94+
local slug=$1 ; shift
95+
while sleep "$interval" ; do
96+
run "$agenturi" "$token" "$agentapiuri" "$slug"
97+
done
98+
}
99+
100+
# configure checks for required vars then starts polling in the background.
101+
# $1: interval in seconds
102+
function configure() {
103+
local interval=$1 ; shift
104+
echo "Configuring task reporting via agent polling..."
105+
# Node will be used to handle JSON. Since the module already requires npm,
106+
# this should not be an extra requirement.
107+
if ! command -v "node" >/dev/null 2>&1 ; then
108+
>&2 echo "Node must be installed"
109+
exit 1
110+
fi
111+
local agenturi=${CODER_AGENT_URL-}
112+
if [[ -z $agenturi ]] ; then
113+
>&2 echo "CODER_AGENT_URL must be set"
114+
exit 1
115+
fi
116+
local token=${CODER_AGENT_TOKEN-}
117+
if [[ -z $token ]] ; then
118+
if [[ -z ${CODER_AGENT_TOKEN_FILE-} ]] ; then
119+
>&2 echo "Either CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set"
120+
exit 1
121+
elif [[ -f $CODER_AGENT_TOKEN_FILE ]] ; then
122+
if ! token=$(<"$CODER_AGENT_TOKEN_FILE") ; then
123+
>&2 echo "$CODER_AGENT_TOKEN_FILE could not be read"
124+
exit 1
125+
fi
126+
else
127+
>&2 echo "$CODER_AGENT_TOKEN_FILE does not exist or could not be accessed"
128+
exit 1
129+
fi
130+
fi
131+
local slug=${CODER_MCP_APP_STATUS_SLUG-}
132+
if [[ -z $slug ]] ; then
133+
>&2 echo "$CODER_MCP_APP_STATUS_SLUG must be set"
134+
exit 1
135+
fi
136+
local agentapiuri=${CODER_AGENT_API_URL-}
137+
if [[ -z $agentapiuri ]] ; then
138+
agentapiuri="http://localhost:3284"
139+
fi
140+
echo "Configured a $interval second poll"
141+
loop "$interval" "$agenturi" "$token" "$agentapiuri" "$slug" &
142+
}
143+
144+
# main configures polling if enabled.
145+
# $1: interval in seconds
146+
function main() {
147+
local interval=$1 ; shift
148+
if [[ -z "$interval" ]] ; then
149+
echo "Will not poll since interval is undefined or blank"
150+
elif [[ "$interval" == "0" ]] ; then
151+
echo "Will not poll since interval is zero"
152+
else
153+
configure "$interval"
154+
fi
155+
}
156+
157+
main "${INTERVAL}"

0 commit comments

Comments
 (0)