From 59ad4b5c60aaa01c27cee34475e9a9ca6dae6cd1 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 16:33:45 +0100 Subject: [PATCH 01/17] feat: agentapi: automatically set --chat-base-path if var.subdomain = false --- registry/coder/modules/agentapi/main.test.ts | 19 +++++++++++++++++++ registry/coder/modules/agentapi/main.tf | 15 +++++++++++++-- .../coder/modules/agentapi/scripts/main.sh | 3 +++ .../agentapi/testdata/agentapi-start.sh | 15 ++++++++++++--- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index fab169679..ad63df127 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -148,4 +148,23 @@ describe("agentapi", async () => { ]); expect(respAgentAPI.exitCode).toBe(0); }); + + test("no-subdomain-base-path", async () => { + const { id } = await setup({ + moduleVariables: { + agentapi_subdomain: "false", + }, + }); + + const respModuleScript = await execModuleScript(id); + expect(respModuleScript.exitCode).toBe(0); + + const agentApiProcessOutput = await execContainer(id, [ + "bash", + "-c", + "ps -eo command | grep [a]gentapi", + ]); + expect(agentApiProcessOutput.exitCode).toBe(0); + expect(agentApiProcessOutput.stdout).toContain(`--chat-base-path /@default/default.foo/apps/agentapi-web/chat`); + }); }); diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index 2e2c8669f..cfba0a5d5 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -126,6 +126,12 @@ variable "agentapi_port" { default = 3284 } +variable "agentapi_subdomain" { + type = bool + description = "Whether to use a subdomain for AgentAPI." + default = true +} + variable "module_dir_name" { type = string description = "Name of the subdirectory in the home directory for module files." @@ -140,7 +146,11 @@ locals { encoded_post_install_script = var.post_install_script != null ? base64encode(var.post_install_script) : "" agentapi_start_script_b64 = base64encode(var.start_script) agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh")) - main_script = file("${path.module}/scripts/main.sh") + // Chat base path is only set if not using a subdomain. + // NOTE: CODER_WORKSPACE_AGENT_NAME is a recent addition, so using agent ID + // for backward compatibility. + 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" + main_script = file("${path.module}/scripts/main.sh") } resource "coder_script" "agentapi" { @@ -165,6 +175,7 @@ resource "coder_script" "agentapi" { ARG_WAIT_FOR_START_SCRIPT="$(echo -n '${local.agentapi_wait_for_start_script_b64}' | base64 -d)" \ ARG_POST_INSTALL_SCRIPT="$(echo -n '${local.encoded_post_install_script}' | base64 -d)" \ ARG_AGENTAPI_PORT='${var.agentapi_port}' \ + ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \ /tmp/main.sh EOT run_on_start = true @@ -178,7 +189,7 @@ resource "coder_app" "agentapi_web" { icon = var.web_app_icon order = var.web_app_order group = var.web_app_group - subdomain = true + subdomain = var.agentapi_subdomain healthcheck { url = "http://localhost:${var.agentapi_port}/status" interval = 3 diff --git a/registry/coder/modules/agentapi/scripts/main.sh b/registry/coder/modules/agentapi/scripts/main.sh index f7a5caab7..9cf7264be 100644 --- a/registry/coder/modules/agentapi/scripts/main.sh +++ b/registry/coder/modules/agentapi/scripts/main.sh @@ -13,6 +13,7 @@ START_SCRIPT="$ARG_START_SCRIPT" WAIT_FOR_START_SCRIPT="$ARG_WAIT_FOR_START_SCRIPT" POST_INSTALL_SCRIPT="$ARG_POST_INSTALL_SCRIPT" AGENTAPI_PORT="$ARG_AGENTAPI_PORT" +AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}" set +o nounset command_exists() { @@ -92,5 +93,7 @@ export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 cd "${WORKDIR}" + +export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}" nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &>"$module_path/agentapi-start.log" & "$module_path/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}" diff --git a/registry/coder/modules/agentapi/testdata/agentapi-start.sh b/registry/coder/modules/agentapi/testdata/agentapi-start.sh index 1564fe032..ed763c509 100644 --- a/registry/coder/modules/agentapi/testdata/agentapi-start.sh +++ b/registry/coder/modules/agentapi/testdata/agentapi-start.sh @@ -11,6 +11,15 @@ log_file_path="$module_path/agentapi.log" echo "using prompt: $use_prompt" >>/home/coder/test-agentapi-start.log echo "using port: $port" >>/home/coder/test-agentapi-start.log -agentapi server --port "$port" --term-width 67 --term-height 1190 -- \ - bash -c aiagent \ - >"$log_file_path" 2>&1 +AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}" +if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then + echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >>/home/coder/test-agentapi-start.log +fi + +cmd=(agentapi server) +if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then + cmd+=(--chat-base-path "$AGENTAPI_CHAT_BASE_PATH") +fi +cmd+=(--port "$port" --term-width 67 --term-height 1190 -- bash -c aiagent) + +"${cmd[@]}" >"$log_file_path" 2>&1 From 5ce1cecc9a415349e07c42474bfd09a3d01874a3 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 16:35:17 +0100 Subject: [PATCH 02/17] chore: agentapi: allow setting DEBUG=1 to skip container removal for debugging --- registry/coder/modules/agentapi/test-util.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/agentapi/test-util.ts b/registry/coder/modules/agentapi/test-util.ts index 66860def5..ca2c1a273 100644 --- a/registry/coder/modules/agentapi/test-util.ts +++ b/registry/coder/modules/agentapi/test-util.ts @@ -24,7 +24,17 @@ export const setupContainer = async ({ }); const coderScript = findResourceInstance(state, "coder_script"); const id = await runContainer(image ?? "codercom/enterprise-node:latest"); - return { id, coderScript, cleanup: () => removeContainer(id) }; + return { + id, coderScript, cleanup: () => { + if (process.env["DEBUG"] === "true" || process.env["DEBUG"] === "1" || process.env["DEBUG"] === "yes") { + console.log(`Not removing container ${id} in debug mode`); + console.log(`Run "docker rm -f ${id}" to remove it manually.`); + } else { + removeContainer(id) + } + return Promise.resolve(); + } + }; }; export const loadTestFile = async ( From 204aa7c60cc1514447d33e0b9d9e39bedfdd5a16 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:00:38 +0100 Subject: [PATCH 03/17] feat: agentapi: validate var.subdomain based on agentapi_version --- registry/coder/modules/agentapi/main.test.ts | 50 +++++++++++++++++++- registry/coder/modules/agentapi/main.tf | 15 +++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index ad63df127..01269f384 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -165,6 +165,54 @@ describe("agentapi", async () => { "ps -eo command | grep [a]gentapi", ]); expect(agentApiProcessOutput.exitCode).toBe(0); - expect(agentApiProcessOutput.stdout).toContain(`--chat-base-path /@default/default.foo/apps/agentapi-web/chat`); + expect(agentApiProcessOutput.stdout).toContain( + `--chat-base-path /@default/default.foo/apps/agentapi-web/chat`, + ); + }); + + test("validate-agentapi-version", async () => { + const cases = [ + { + moduleVariables: { + agentapi_version: "v0.3.2", + agentapi_subdomain: "false", + }, + shouldThrow: "", + }, + { + moduleVariables: { + agentapi_version: "v0.3.2", + agentapi_subdomain: "true", + }, + shouldThrow: "", + }, + { + moduleVariables: { + agentapi_version: "v0.3.1", + agentapi_subdomain: "false", + }, + shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.2.", + }, + { + moduleVariables: { + agentapi_version: "v0.3.1", + agentapi_subdomain: "true", + }, + shouldThrow: "", + }, + { + moduleVariables: { + agentapi_version: "arbitrary-string-bypasses-validation", + }, + shouldThrow: "", + } + ]; + for (const { moduleVariables, shouldThrow } of cases) { + if (shouldThrow) { + return expect(setup({ moduleVariables: moduleVariables as Record })).rejects.toThrow(shouldThrow); + } else { + return expect(setup({ moduleVariables: moduleVariables as Record })).resolves.toBeDefined(); + } + } }); }); diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index cfba0a5d5..9488ba736 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -130,6 +130,15 @@ variable "agentapi_subdomain" { type = bool description = "Whether to use a subdomain for AgentAPI." default = true + validation { + condition = var.agentapi_subdomain || ( + # If version doesn't look like a valid semantic version, just allow it. + !can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) + || + can(regex("^v(0\\.(3\\.[2-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$", var.agentapi_version)) + ) + error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.2." + } } variable "module_dir_name" { @@ -147,8 +156,10 @@ locals { agentapi_start_script_b64 = base64encode(var.start_script) agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh")) // Chat base path is only set if not using a subdomain. - // NOTE: CODER_WORKSPACE_AGENT_NAME is a recent addition, so using agent ID - // for backward compatibility. + // NOTE: + // - This requires agentapi version >= v0.3.2. + // - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID + // for backward compatibility. 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" main_script = file("${path.module}/scripts/main.sh") } From 8429e04f1ddc8102fed3a00393de440b7c7b045e Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:02:03 +0100 Subject: [PATCH 04/17] feat: agentapi: set default version to v0.3.2 --- registry/coder/modules/agentapi/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index 9488ba736..5c43b4bad 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -117,7 +117,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.2.3" + default = "v0.3.2" } variable "agentapi_port" { From f7449060ad407047f0ef6e06ede0109facbb7239 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:21:26 +0100 Subject: [PATCH 05/17] fixup! feat: agentapi: validate var.subdomain based on agentapi_version --- registry/coder/modules/agentapi/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index 01269f384..92e52dac3 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -209,9 +209,9 @@ describe("agentapi", async () => { ]; for (const { moduleVariables, shouldThrow } of cases) { if (shouldThrow) { - return expect(setup({ moduleVariables: moduleVariables as Record })).rejects.toThrow(shouldThrow); + expect(setup({ moduleVariables: moduleVariables as Record })).rejects.toThrow(shouldThrow); } else { - return expect(setup({ moduleVariables: moduleVariables as Record })).resolves.toBeDefined(); + expect(setup({ moduleVariables: moduleVariables as Record })).resolves.toBeDefined(); } } }); From 1a3118167f2d048bf798be116ac39132bdb4ebf0 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:22:05 +0100 Subject: [PATCH 06/17] fixup! chore: agentapi: allow setting DEBUG=1 to skip container removal for debugging --- registry/coder/modules/agentapi/test-util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/agentapi/test-util.ts b/registry/coder/modules/agentapi/test-util.ts index ca2c1a273..dfdbf65e7 100644 --- a/registry/coder/modules/agentapi/test-util.ts +++ b/registry/coder/modules/agentapi/test-util.ts @@ -30,7 +30,7 @@ export const setupContainer = async ({ console.log(`Not removing container ${id} in debug mode`); console.log(`Run "docker rm -f ${id}" to remove it manually.`); } else { - removeContainer(id) + removeContainer(id); } return Promise.resolve(); } From ada586f3f57a5ec1071a593888ab840966555eaf Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:38:12 +0100 Subject: [PATCH 07/17] fixup! feat: agentapi: validate var.subdomain based on agentapi_version --- registry/coder/modules/agentapi/main.tf | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index 5c43b4bad..9ec491a3b 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -126,6 +126,11 @@ variable "agentapi_port" { default = 3284 } +locals { + # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.2. + agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[2-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" +} + variable "agentapi_subdomain" { type = bool description = "Whether to use a subdomain for AgentAPI." @@ -134,8 +139,7 @@ variable "agentapi_subdomain" { condition = var.agentapi_subdomain || ( # If version doesn't look like a valid semantic version, just allow it. !can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) - || - can(regex("^v(0\\.(3\\.[2-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$", var.agentapi_version)) + || can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) ) error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.2." } From 2defdcf7b1b8f8d10c6e6e95d4f5de053232680c Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Mon, 4 Aug 2025 21:49:05 +0100 Subject: [PATCH 08/17] fix: agentapi: fix subpath version off-by-one error --- registry/coder/modules/agentapi/main.test.ts | 10 +++++----- registry/coder/modules/agentapi/main.tf | 16 +++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index 92e52dac3..b5cc88091 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -174,28 +174,28 @@ describe("agentapi", async () => { const cases = [ { moduleVariables: { - agentapi_version: "v0.3.2", + agentapi_version: "v0.3.1", agentapi_subdomain: "false", }, shouldThrow: "", }, { moduleVariables: { - agentapi_version: "v0.3.2", + agentapi_version: "v0.3.1", agentapi_subdomain: "true", }, shouldThrow: "", }, { moduleVariables: { - agentapi_version: "v0.3.1", + agentapi_version: "v0.3.0", agentapi_subdomain: "false", }, - shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.2.", + shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.1.", }, { moduleVariables: { - agentapi_version: "v0.3.1", + agentapi_version: "v0.3.0", agentapi_subdomain: "true", }, shouldThrow: "", diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index 9ec491a3b..e7ce460c1 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -117,7 +117,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.3.2" + default = "v0.3.1" } variable "agentapi_port" { @@ -127,8 +127,8 @@ variable "agentapi_port" { } locals { - # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.2. - agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[2-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" + # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.1. + agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[1-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" } variable "agentapi_subdomain" { @@ -138,10 +138,12 @@ variable "agentapi_subdomain" { validation { condition = var.agentapi_subdomain || ( # If version doesn't look like a valid semantic version, just allow it. - !can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) - || can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) + # Note that boolean operators do not short-circuit in Terraform. + can(regex("^v\\d+\\.\\d+\\.\\d+$", var.agentapi_version)) ? + can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) : + true ) - error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.2." + error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.1." } } @@ -161,7 +163,7 @@ locals { agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh")) // Chat base path is only set if not using a subdomain. // NOTE: - // - This requires agentapi version >= v0.3.2. + // - This requires agentapi version >= v0.3.1. // - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID // for backward compatibility. 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" From 6f1fa345dc91a68956b0d00c8bb3961f222188b7 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 5 Aug 2025 17:32:06 +0100 Subject: [PATCH 09/17] chore: agentapi: bump module version (minor) --- registry/coder/modules/agentapi/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/agentapi/README.md b/registry/coder/modules/agentapi/README.md index 1514bb260..98f2a9ef5 100644 --- a/registry/coder/modules/agentapi/README.md +++ b/registry/coder/modules/agentapi/README.md @@ -16,7 +16,7 @@ The AgentAPI module is a building block for modules that need to run an AgentAPI ```tf module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.0.2" + version = "1.1.0" agent_id = var.agent_id web_app_slug = local.app_slug From bab4557ed57a0379be7d8942c329ac588c613477 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 5 Aug 2025 18:32:04 +0100 Subject: [PATCH 10/17] set AGENTAPI_CHAT_BASE_PATH using env instead --- registry/coder/modules/agentapi/main.test.ts | 13 +++++-------- .../modules/agentapi/testdata/agentapi-start.sh | 11 ++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index b5cc88091..508bb421f 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -159,15 +159,12 @@ describe("agentapi", async () => { const respModuleScript = await execModuleScript(id); expect(respModuleScript.exitCode).toBe(0); - const agentApiProcessOutput = await execContainer(id, [ - "bash", - "-c", - "ps -eo command | grep [a]gentapi", - ]); - expect(agentApiProcessOutput.exitCode).toBe(0); - expect(agentApiProcessOutput.stdout).toContain( - `--chat-base-path /@default/default.foo/apps/agentapi-web/chat`, + await expectAgentAPIStarted(id); + const agentApiStartLog = await readFileContainer( + id, + "/home/coder/test-agentapi-start.log", ); + expect(agentApiStartLog).toContain("Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat"); }); test("validate-agentapi-version", async () => { diff --git a/registry/coder/modules/agentapi/testdata/agentapi-start.sh b/registry/coder/modules/agentapi/testdata/agentapi-start.sh index ed763c509..2b4362115 100644 --- a/registry/coder/modules/agentapi/testdata/agentapi-start.sh +++ b/registry/coder/modules/agentapi/testdata/agentapi-start.sh @@ -14,12 +14,9 @@ echo "using port: $port" >>/home/coder/test-agentapi-start.log AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}" if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >>/home/coder/test-agentapi-start.log + export AGENTAPI_CHAT_BASE_PATH fi -cmd=(agentapi server) -if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then - cmd+=(--chat-base-path "$AGENTAPI_CHAT_BASE_PATH") -fi -cmd+=(--port "$port" --term-width 67 --term-height 1190 -- bash -c aiagent) - -"${cmd[@]}" >"$log_file_path" 2>&1 +agentapi server --port "$port" --term-width 67 --term-height 1190 -- \ + bash -c aiagent \ + >"$log_file_path" 2>&1 \ No newline at end of file From f54bd0a352284e90c7eff368df46468dc2638903 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 5 Aug 2025 18:33:27 +0100 Subject: [PATCH 11/17] fixup! set AGENTAPI_CHAT_BASE_PATH using env instead --- registry/coder/modules/agentapi/testdata/agentapi-start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/agentapi/testdata/agentapi-start.sh b/registry/coder/modules/agentapi/testdata/agentapi-start.sh index 2b4362115..739cb27d5 100644 --- a/registry/coder/modules/agentapi/testdata/agentapi-start.sh +++ b/registry/coder/modules/agentapi/testdata/agentapi-start.sh @@ -19,4 +19,4 @@ fi agentapi server --port "$port" --term-width 67 --term-height 1190 -- \ bash -c aiagent \ - >"$log_file_path" 2>&1 \ No newline at end of file + >"$log_file_path" 2>&1 From 772f2990967bd59f6a0ce0ace22c19e390d931fa Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 5 Aug 2025 19:49:04 +0100 Subject: [PATCH 12/17] bump min agentapi_version to v0.3.3 assuming this is when support for AGENTAPI_CHAT_BASE_PATH will land --- registry/coder/modules/agentapi/main.test.ts | 10 +++++----- registry/coder/modules/agentapi/main.tf | 13 ++++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index 508bb421f..be471699d 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -171,28 +171,28 @@ describe("agentapi", async () => { const cases = [ { moduleVariables: { - agentapi_version: "v0.3.1", + agentapi_version: "v0.3.3", agentapi_subdomain: "false", }, shouldThrow: "", }, { moduleVariables: { - agentapi_version: "v0.3.1", + agentapi_version: "v0.3.3", agentapi_subdomain: "true", }, shouldThrow: "", }, { moduleVariables: { - agentapi_version: "v0.3.0", + agentapi_version: "v0.3.2", agentapi_subdomain: "false", }, - shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.1.", + shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.", }, { moduleVariables: { - agentapi_version: "v0.3.0", + agentapi_version: "v0.3.3", agentapi_subdomain: "true", }, shouldThrow: "", diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index e7ce460c1..8eadb18f4 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -117,7 +117,7 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.3.1" + default = "v0.3.3" } variable "agentapi_port" { @@ -127,8 +127,10 @@ variable "agentapi_port" { } locals { - # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.1. - agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[1-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" + # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.3. + # Initial support was added in v0.3.1 but configuration via environment variable + # was added in v0.3.3. + agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[3-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" } variable "agentapi_subdomain" { @@ -143,7 +145,7 @@ variable "agentapi_subdomain" { can(regex(local.agentapi_subdomain_false_min_version_expr, var.agentapi_version)) : true ) - error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.1." + error_message = "Running with subdomain = false is only supported by agentapi >= v0.3.3." } } @@ -163,7 +165,8 @@ locals { agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh")) // Chat base path is only set if not using a subdomain. // NOTE: - // - This requires agentapi version >= v0.3.1. + // - Initial support for --chat-base-path was added in v0.3.1 but configuration + // via environment variable AGENTAPI_CHAT_BASE_PATH was added in v0.3.3. // - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID // for backward compatibility. 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" From 4145d9c93870fa8ac27cabfc53efdc857ad43c14 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 6 Aug 2025 12:17:28 +0100 Subject: [PATCH 13/17] chore: agentapi: fix cleanup async func --- registry/coder/modules/agentapi/test-util.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/registry/coder/modules/agentapi/test-util.ts b/registry/coder/modules/agentapi/test-util.ts index dfdbf65e7..a9062031e 100644 --- a/registry/coder/modules/agentapi/test-util.ts +++ b/registry/coder/modules/agentapi/test-util.ts @@ -25,14 +25,13 @@ export const setupContainer = async ({ const coderScript = findResourceInstance(state, "coder_script"); const id = await runContainer(image ?? "codercom/enterprise-node:latest"); return { - id, coderScript, cleanup: () => { + id, coderScript, cleanup: async () => { if (process.env["DEBUG"] === "true" || process.env["DEBUG"] === "1" || process.env["DEBUG"] === "yes") { console.log(`Not removing container ${id} in debug mode`); console.log(`Run "docker rm -f ${id}" to remove it manually.`); } else { - removeContainer(id); + await removeContainer(id); } - return Promise.resolve(); } }; }; From 85d238eb4bab3b76611e4418cce2193ebeddef44 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 6 Aug 2025 12:17:45 +0100 Subject: [PATCH 14/17] chore: agentapi: improve agentapi_subdomain_false_min_version_expr --- registry/coder/modules/agentapi/main.test.ts | 32 +++++++++++++++++--- registry/coder/modules/agentapi/main.tf | 4 ++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/agentapi/main.test.ts b/registry/coder/modules/agentapi/main.test.ts index be471699d..773205cc1 100644 --- a/registry/coder/modules/agentapi/main.test.ts +++ b/registry/coder/modules/agentapi/main.test.ts @@ -171,18 +171,23 @@ describe("agentapi", async () => { const cases = [ { moduleVariables: { - agentapi_version: "v0.3.3", - agentapi_subdomain: "false", + agentapi_version: "v0.3.2", }, shouldThrow: "", }, { moduleVariables: { agentapi_version: "v0.3.3", - agentapi_subdomain: "true", }, shouldThrow: "", }, + { + moduleVariables: { + agentapi_version: "v0.0.1", + agentapi_subdomain: "false", + }, + shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.", + }, { moduleVariables: { agentapi_version: "v0.3.2", @@ -193,10 +198,29 @@ describe("agentapi", async () => { { moduleVariables: { agentapi_version: "v0.3.3", - agentapi_subdomain: "true", + agentapi_subdomain: "false", + }, + shouldThrow: "", + }, + { + moduleVariables: { + agentapi_version: "v0.3.999", + agentapi_subdomain: "false", }, shouldThrow: "", }, + { + moduleVariables: { + agentapi_version: "v0.999.999", + agentapi_subdomain: "false", + }, + }, + { + moduleVariables: { + agentapi_version: "v999.999.999", + agentapi_subdomain: "false", + }, + }, { moduleVariables: { agentapi_version: "arbitrary-string-bypasses-validation", diff --git a/registry/coder/modules/agentapi/main.tf b/registry/coder/modules/agentapi/main.tf index 8eadb18f4..225a992d3 100644 --- a/registry/coder/modules/agentapi/main.tf +++ b/registry/coder/modules/agentapi/main.tf @@ -130,7 +130,9 @@ locals { # agentapi_subdomain_false_min_version_expr matches a semantic version >= v0.3.3. # Initial support was added in v0.3.1 but configuration via environment variable # was added in v0.3.3. - agentapi_subdomain_false_min_version_expr = "^v(0\\.(3\\.[3-9]+|[4-9]+\\.\\d+)|[1-9]\\d*\\.\\d+\\.\\d+)$" + # This is unfortunately a regex because there is no builtin way to compare semantic versions in Terraform. + # See: https://regex101.com/r/oHPyRa/1 + 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+)$" } variable "agentapi_subdomain" { From ea35534806eb216bbbfd81e9b4bafc876141e615 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 5 Aug 2025 15:57:39 +0100 Subject: [PATCH 15/17] feat: goose: add support for subdomain=false --- registry/coder/modules/goose/main.test.ts | 17 +++++++++++++++++ registry/coder/modules/goose/main.tf | 11 +++++++++-- registry/coder/modules/goose/scripts/start.sh | 5 +++++ .../goose/testdata/agentapi-mock-print-args.js | 1 + 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/goose/main.test.ts b/registry/coder/modules/goose/main.test.ts index bbf8b262e..779767fbd 100644 --- a/registry/coder/modules/goose/main.test.ts +++ b/registry/coder/modules/goose/main.test.ts @@ -251,4 +251,21 @@ describe("goose", async () => { expect(prompt.exitCode).not.toBe(0); expect(prompt.stderr).toContain("No such file or directory"); }); + + test("subdomain-false", async () => { + const { id } = await setup({ + agentapiMockScript: await loadTestFile( + import.meta.dir, + "agentapi-mock-print-args.js", + ), + moduleVariables: { + subdomain: "false", + }, + }); + + await execModuleScript(id); + + const agentapiMockOutput = await readFileContainer(id, agentapiStartLog); + expect(agentapiMockOutput).toContain("AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/goose/chat"); + }); }); diff --git a/registry/coder/modules/goose/main.tf b/registry/coder/modules/goose/main.tf index 93c2e15bc..47a49522c 100644 --- a/registry/coder/modules/goose/main.tf +++ b/registry/coder/modules/goose/main.tf @@ -63,7 +63,13 @@ variable "install_agentapi" { variable "agentapi_version" { type = string description = "The version of AgentAPI to install." - default = "v0.2.3" + default = "v0.3.3" +} + +variable "subdomain" { + type = bool + description = "Whether to use a subdomain for AgentAPI." + default = true } variable "goose_provider" { @@ -133,7 +139,7 @@ EOT module "agentapi" { source = "registry.coder.com/coder/agentapi/coder" - version = "1.0.0" + version = "1.1.0" agent_id = var.agent_id web_app_slug = local.app_slug @@ -146,6 +152,7 @@ module "agentapi" { module_dir_name = local.module_dir_name install_agentapi = var.install_agentapi agentapi_version = var.agentapi_version + agentapi_subdomain = var.subdomain pre_install_script = var.pre_install_script post_install_script = var.post_install_script start_script = local.start_script diff --git a/registry/coder/modules/goose/scripts/start.sh b/registry/coder/modules/goose/scripts/start.sh index 314a41d0f..bbea56cc1 100644 --- a/registry/coder/modules/goose/scripts/start.sh +++ b/registry/coder/modules/goose/scripts/start.sh @@ -31,5 +31,10 @@ else GOOSE_ARGS=() fi +if [ -n "${AGENTAPI_CHAT_BASE_PATH:-}" ]; then + echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" + export AGENTAPI_CHAT_BASE_PATH +fi + agentapi server --term-width 67 --term-height 1190 -- \ bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")" diff --git a/registry/coder/modules/goose/testdata/agentapi-mock-print-args.js b/registry/coder/modules/goose/testdata/agentapi-mock-print-args.js index fd859c819..0205331b2 100644 --- a/registry/coder/modules/goose/testdata/agentapi-mock-print-args.js +++ b/registry/coder/modules/goose/testdata/agentapi-mock-print-args.js @@ -3,6 +3,7 @@ const http = require("http"); const args = process.argv.slice(2); console.log(args); +console.log(`AGENTAPI_CHAT_BASE_PATH=${process.env["AGENTAPI_CHAT_BASE_PATH"]}`); const port = 3284; console.log(`starting server on port ${port}`); From d94b356cc2f299549866f15faf4321923973133e Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 6 Aug 2025 11:35:52 +0100 Subject: [PATCH 16/17] chore: goose: bump module version (minor) --- registry/coder/modules/goose/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/goose/README.md b/registry/coder/modules/goose/README.md index 7d957d2fc..b3ce49b89 100644 --- a/registry/coder/modules/goose/README.md +++ b/registry/coder/modules/goose/README.md @@ -13,7 +13,7 @@ Run the [Goose](https://block.github.io/goose/) agent in your workspace to gener ```tf module "goose" { source = "registry.coder.com/coder/goose/coder" - version = "2.0.1" + version = "2.1.0" agent_id = coder_agent.example.id folder = "/home/coder" install_goose = true @@ -79,7 +79,7 @@ resource "coder_agent" "main" { module "goose" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/goose/coder" - version = "2.0.1" + version = "2.1.0" agent_id = coder_agent.example.id folder = "/home/coder" install_goose = true From f0a675ec285c4ab1e14064f3627d35854b230321 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 6 Aug 2025 12:34:31 +0100 Subject: [PATCH 17/17] chore: goose: we can just use AGENTAPI_CHAT_BASE_PATH exported from agentapi --- registry/coder/modules/goose/scripts/start.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/registry/coder/modules/goose/scripts/start.sh b/registry/coder/modules/goose/scripts/start.sh index bbea56cc1..314a41d0f 100644 --- a/registry/coder/modules/goose/scripts/start.sh +++ b/registry/coder/modules/goose/scripts/start.sh @@ -31,10 +31,5 @@ else GOOSE_ARGS=() fi -if [ -n "${AGENTAPI_CHAT_BASE_PATH:-}" ]; then - echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" - export AGENTAPI_CHAT_BASE_PATH -fi - agentapi server --term-width 67 --term-height 1190 -- \ bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")"