Skip to content

Commit 57c900b

Browse files
authored
feat(agentapi): Add support for running under a subdomain (#297)
Updates coder/coder#18779 A separate PR will update dependant modules `goose` and `aider`. ## Description * Adds `subdomain` argument to `agentapi` module * Updates `agentapi` module to set `AGENTAPI_CHAT_BASE_PATH` to an autogenerated path if `var.subdomain = false` * Updates default `agentapi` version to `v0.3.3` to support running without subdomain ## Type of Change - [ ] New module - [ ] Bug fix - [x] Feature/enhancement - [ ] Documentation - [ ] Other ## Testing & Validation - [X] Tests pass (`bun test`) - [X] Code formatted (`bun run fmt`) - [x] Changes tested locally ## Related Issues - coder/coder#18779
1 parent 0ccee61 commit 57c900b

File tree

6 files changed

+144
-5
lines changed

6 files changed

+144
-5
lines changed

registry/coder/modules/agentapi/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ The AgentAPI module is a building block for modules that need to run an AgentAPI
1616
```tf
1717
module "agentapi" {
1818
source = "registry.coder.com/coder/agentapi/coder"
19-
version = "1.0.2"
19+
version = "1.1.0"
2020
2121
agent_id = var.agent_id
2222
web_app_slug = local.app_slug

registry/coder/modules/agentapi/main.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,92 @@ describe("agentapi", async () => {
148148
]);
149149
expect(respAgentAPI.exitCode).toBe(0);
150150
});
151+
152+
test("no-subdomain-base-path", async () => {
153+
const { id } = await setup({
154+
moduleVariables: {
155+
agentapi_subdomain: "false",
156+
},
157+
});
158+
159+
const respModuleScript = await execModuleScript(id);
160+
expect(respModuleScript.exitCode).toBe(0);
161+
162+
await expectAgentAPIStarted(id);
163+
const agentApiStartLog = await readFileContainer(
164+
id,
165+
"/home/coder/test-agentapi-start.log",
166+
);
167+
expect(agentApiStartLog).toContain("Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat");
168+
});
169+
170+
test("validate-agentapi-version", async () => {
171+
const cases = [
172+
{
173+
moduleVariables: {
174+
agentapi_version: "v0.3.2",
175+
},
176+
shouldThrow: "",
177+
},
178+
{
179+
moduleVariables: {
180+
agentapi_version: "v0.3.3",
181+
},
182+
shouldThrow: "",
183+
},
184+
{
185+
moduleVariables: {
186+
agentapi_version: "v0.0.1",
187+
agentapi_subdomain: "false",
188+
},
189+
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.",
190+
},
191+
{
192+
moduleVariables: {
193+
agentapi_version: "v0.3.2",
194+
agentapi_subdomain: "false",
195+
},
196+
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.",
197+
},
198+
{
199+
moduleVariables: {
200+
agentapi_version: "v0.3.3",
201+
agentapi_subdomain: "false",
202+
},
203+
shouldThrow: "",
204+
},
205+
{
206+
moduleVariables: {
207+
agentapi_version: "v0.3.999",
208+
agentapi_subdomain: "false",
209+
},
210+
shouldThrow: "",
211+
},
212+
{
213+
moduleVariables: {
214+
agentapi_version: "v0.999.999",
215+
agentapi_subdomain: "false",
216+
},
217+
},
218+
{
219+
moduleVariables: {
220+
agentapi_version: "v999.999.999",
221+
agentapi_subdomain: "false",
222+
},
223+
},
224+
{
225+
moduleVariables: {
226+
agentapi_version: "arbitrary-string-bypasses-validation",
227+
},
228+
shouldThrow: "",
229+
}
230+
];
231+
for (const { moduleVariables, shouldThrow } of cases) {
232+
if (shouldThrow) {
233+
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).rejects.toThrow(shouldThrow);
234+
} else {
235+
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).resolves.toBeDefined();
236+
}
237+
}
238+
});
151239
});

registry/coder/modules/agentapi/main.tf

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ variable "install_agentapi" {
117117
variable "agentapi_version" {
118118
type = string
119119
description = "The version of AgentAPI to install."
120-
default = "v0.2.3"
120+
default = "v0.3.3"
121121
}
122122

123123
variable "agentapi_port" {
@@ -126,6 +126,31 @@ variable "agentapi_port" {
126126
default = 3284
127127
}
128128

129+
locals {
130+
# agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.3.
131+
# Initial support was added in v0.3.1 but configuration via environment variable
132+
# was added in v0.3.3.
133+
# This is unfortunately a regex because there is no builtin way to compare semantic versions in Terraform.
134+
# See: https://regex101.com/r/oHPyRa/1
135+
agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[3-9]|3.[1-9]\\d+|[4-9]\\.\\d+|[1-9]\\d+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$"
136+
}
137+
138+
variable "agentapi_subdomain" {
139+
type = bool
140+
description = "Whether to use a subdomain for AgentAPI."
141+
default = true
142+
validation {
143+
condition = var.agentapi_subdomain || (
144+
# If version doesn't look like a valid semantic version, just allow it.
145+
# Note that boolean operators do not short-circuit in Terraform.
146+
can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) ?
147+
can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) :
148+
true
149+
)
150+
error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.3."
151+
}
152+
}
153+
129154
variable "module_dir_name" {
130155
type = string
131156
description = "Name of the subdirectory in the home directory for module files."
@@ -140,7 +165,14 @@ locals {
140165
encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : ""
141166
agentapi_start_script_b64 = base64encode(var.start_script)
142167
agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh"))
143-
main_script = file("${path.module}/scripts/main.sh")
168+
// Chat base path is only set if not using a subdomain.
169+
// NOTE:
170+
// - Initial support for --chat-base-path was added in v0.3.1 but configuration
171+
// via environment variable AGENTAPI_CHAT_BASE_PATH was added in v0.3.3.
172+
// - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID
173+
// for backward compatibility.
174+
agentapi_chat_base_path = var.agentapi_subdomain ? "" : "/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}.${var.agent_id}/apps/${var.web_app_slug}/chat"
175+
main_script = file("${path.module}/scripts/main.sh")
144176
}
145177

146178
resource "coder_script" "agentapi" {
@@ -165,6 +197,7 @@ resource "coder_script" "agentapi" {
165197
ARG_WAIT_FOR_START_SCRIPT="$(echo -n '${local.agentapi_wait_for_start_script_b64}' | base64 -d)" \
166198
ARG_POST_INSTALL_SCRIPT="$(echo -n '${local.encoded_post_install_script}' | base64 -d)" \
167199
ARG_AGENTAPI_PORT='${var.agentapi_port}' \
200+
ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \
168201
/tmp/main.sh
169202
EOT
170203
run_on_start = true
@@ -178,7 +211,7 @@ resource "coder_app" "agentapi_web" {
178211
icon = var.web_app_icon
179212
order = var.web_app_order
180213
group = var.web_app_group
181-
subdomain = true
214+
subdomain = var.agentapi_subdomain
182215
healthcheck {
183216
url = "http://localhost:${var.agentapi_port}/status"
184217
interval = 3

registry/coder/modules/agentapi/scripts/main.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ START_SCRIPT="$ARG_START_SCRIPT"
1313
WAIT_FOR_START_SCRIPT="$ARG_WAIT_FOR_START_SCRIPT"
1414
POST_INSTALL_SCRIPT="$ARG_POST_INSTALL_SCRIPT"
1515
AGENTAPI_PORT="$ARG_AGENTAPI_PORT"
16+
AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
1617
set +o nounset
1718

1819
command_exists() {
@@ -92,5 +93,7 @@ export LANG=en_US.UTF-8
9293
export LC_ALL=en_US.UTF-8
9394

9495
cd "${WORKDIR}"
96+
97+
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
9598
nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &>"$module_path/agentapi-start.log" &
9699
"$module_path/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}"

registry/coder/modules/agentapi/test-util.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ export const setupContainer = async ({
2424
});
2525
const coderScript = findResourceInstance(state, "coder_script");
2626
const id = await runContainer(image ?? "codercom/enterprise-node:latest");
27-
return { id, coderScript, cleanup: () => removeContainer(id) };
27+
return {
28+
id, coderScript, cleanup: async () => {
29+
if (process.env["DEBUG"] === "true" || process.env["DEBUG"] === "1" || process.env["DEBUG"] === "yes") {
30+
console.log(`Not removing container ${id} in debug mode`);
31+
console.log(`Run "docker rm -f ${id}" to remove it manually.`);
32+
} else {
33+
await removeContainer(id);
34+
}
35+
}
36+
};
2837
};
2938

3039
export const loadTestFile = async (

registry/coder/modules/agentapi/testdata/agentapi-start.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ log_file_path="$module_path/agentapi.log"
1111
echo "using prompt: $use_prompt" >>/home/coder/test-agentapi-start.log
1212
echo "using port: $port" >>/home/coder/test-agentapi-start.log
1313

14+
AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
15+
if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then
16+
echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >>/home/coder/test-agentapi-start.log
17+
export AGENTAPI_CHAT_BASE_PATH
18+
fi
19+
1420
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
1521
bash -c aiagent \
1622
>"$log_file_path" 2>&1

0 commit comments

Comments
 (0)