Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion registry/coder/modules/agentapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
67 changes: 67 additions & 0 deletions registry/coder/modules/agentapi/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,71 @@ 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`,
);
});

test("validate-agentapi-version", async () => {
const cases = [
{
moduleVariables: {
agentapi_version: "v0.3.1",
agentapi_subdomain: "false",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.3.1",
agentapi_subdomain: "true",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "v0.3.0",
agentapi_subdomain: "false",
},
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.1.",
},
{
moduleVariables: {
agentapi_version: "v0.3.0",
agentapi_subdomain: "true",
},
shouldThrow: "",
},
{
moduleVariables: {
agentapi_version: "arbitrary-string-bypasses-validation",
},
shouldThrow: "",
}
];
for (const { moduleVariables, shouldThrow } of cases) {
if (shouldThrow) {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).rejects.toThrow(shouldThrow);
} else {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).resolves.toBeDefined();
}
}
});
});
34 changes: 31 additions & 3 deletions registry/coder/modules/agentapi/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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.1"
}

variable "agentapi_port" {
Expand All @@ -126,6 +126,27 @@ variable "agentapi_port" {
default = 3284
}

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+)$"
}

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.
# 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.1."
}
}

variable "module_dir_name" {
type = string
description = "Name of the subdirectory in the home directory for module files."
Expand All @@ -140,7 +161,13 @@ 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:
// - 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"
main_script = file("${path.module}/scripts/main.sh")
}

resource "coder_script" "agentapi" {
Expand All @@ -165,6 +192,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
Expand All @@ -178,7 +206,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
Expand Down
3 changes: 3 additions & 0 deletions registry/coder/modules/agentapi/scripts/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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}"
12 changes: 11 additions & 1 deletion registry/coder/modules/agentapi/test-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
15 changes: 12 additions & 3 deletions registry/coder/modules/agentapi/testdata/agentapi-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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