Captures the last 10 messages from AgentAPI when task workspaces stop, allowing users to view conversation history while the task is paused. The shutdown script fetches messages, builds a payload with last 10 messages, truncates to 64KB if needed (removes old messages first, then truncates content of the last message), and posts to the log snapshot endpoint. Gracefully handles non-task workspaces (skips), older Coder versions without the endpoint (logs and continues), and empty message sets. Enabled by default via task_log_snapshot variable. Task ID is automatically resolved from data.coder_task when available. Updates coder/internal#1257
275 lines
8.2 KiB
HCL
275 lines
8.2 KiB
HCL
terraform {
|
|
required_version = ">= 1.0"
|
|
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
version = ">= 2.13"
|
|
}
|
|
}
|
|
}
|
|
|
|
variable "agent_id" {
|
|
type = string
|
|
description = "The ID of a Coder agent."
|
|
}
|
|
|
|
data "coder_workspace" "me" {}
|
|
|
|
data "coder_workspace_owner" "me" {}
|
|
|
|
data "coder_task" "me" {}
|
|
|
|
variable "web_app_order" {
|
|
type = number
|
|
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
|
|
default = null
|
|
}
|
|
|
|
variable "web_app_group" {
|
|
type = string
|
|
description = "The name of a group that this app belongs to."
|
|
default = null
|
|
}
|
|
|
|
variable "web_app_icon" {
|
|
type = string
|
|
description = "The icon to use for the app."
|
|
}
|
|
|
|
variable "web_app_display_name" {
|
|
type = string
|
|
description = "The display name of the web app."
|
|
}
|
|
|
|
variable "web_app_slug" {
|
|
type = string
|
|
description = "The slug of the web app."
|
|
}
|
|
|
|
variable "folder" {
|
|
type = string
|
|
description = "The folder to run AgentAPI in."
|
|
default = "/home/coder"
|
|
}
|
|
|
|
variable "cli_app" {
|
|
type = bool
|
|
description = "Whether to create the CLI workspace app."
|
|
default = false
|
|
}
|
|
|
|
variable "cli_app_order" {
|
|
type = number
|
|
description = "The order of the CLI workspace app."
|
|
default = null
|
|
}
|
|
|
|
variable "cli_app_group" {
|
|
type = string
|
|
description = "The group of the CLI workspace app."
|
|
default = null
|
|
}
|
|
|
|
variable "cli_app_icon" {
|
|
type = string
|
|
description = "The icon to use for the app."
|
|
default = "/icon/claude.svg"
|
|
}
|
|
|
|
variable "cli_app_display_name" {
|
|
type = string
|
|
description = "The display name of the CLI workspace app."
|
|
}
|
|
|
|
variable "cli_app_slug" {
|
|
type = string
|
|
description = "The slug of the CLI workspace app."
|
|
}
|
|
|
|
variable "pre_install_script" {
|
|
type = string
|
|
description = "Custom script to run before installing the agent used by AgentAPI."
|
|
default = null
|
|
}
|
|
|
|
variable "install_script" {
|
|
type = string
|
|
description = "Script to install the agent used by AgentAPI."
|
|
default = ""
|
|
}
|
|
|
|
variable "post_install_script" {
|
|
type = string
|
|
description = "Custom script to run after installing the agent used by AgentAPI."
|
|
default = null
|
|
}
|
|
|
|
variable "start_script" {
|
|
type = string
|
|
description = "Script that starts AgentAPI."
|
|
}
|
|
|
|
variable "install_agentapi" {
|
|
type = bool
|
|
description = "Whether to install AgentAPI."
|
|
default = true
|
|
}
|
|
|
|
variable "agentapi_version" {
|
|
type = string
|
|
description = "The version of AgentAPI to install."
|
|
default = "v0.10.0"
|
|
}
|
|
|
|
variable "agentapi_port" {
|
|
type = number
|
|
description = "The port used by AgentAPI."
|
|
default = 3284
|
|
}
|
|
|
|
variable "task_log_snapshot" {
|
|
type = bool
|
|
description = "Capture last 10 messages when workspace stops for offline viewing while task is paused."
|
|
default = true
|
|
}
|
|
|
|
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.
|
|
# 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" {
|
|
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.3."
|
|
}
|
|
}
|
|
|
|
variable "module_dir_name" {
|
|
type = string
|
|
description = "Name of the subdirectory in the home directory for module files."
|
|
}
|
|
|
|
|
|
locals {
|
|
# we always trim the slash for consistency
|
|
workdir = trimsuffix(var.folder, "/")
|
|
encoded_pre_install_script = var.pre_install_script != null ? base64encode(var.pre_install_script) : ""
|
|
encoded_install_script = var.install_script != null ? base64encode(var.install_script) : ""
|
|
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"))
|
|
// Chat base path is only set if not using a subdomain.
|
|
// NOTE:
|
|
// - 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"
|
|
main_script = file("${path.module}/scripts/main.sh")
|
|
shutdown_script = file("${path.module}/scripts/agentapi-shutdown.sh")
|
|
}
|
|
|
|
resource "coder_script" "agentapi" {
|
|
agent_id = var.agent_id
|
|
display_name = "Install and start AgentAPI"
|
|
icon = var.web_app_icon
|
|
script = <<-EOT
|
|
#!/bin/bash
|
|
set -o errexit
|
|
set -o pipefail
|
|
|
|
echo -n '${base64encode(local.main_script)}' | base64 -d > /tmp/main.sh
|
|
chmod +x /tmp/main.sh
|
|
|
|
ARG_MODULE_DIR_NAME='${var.module_dir_name}' \
|
|
ARG_WORKDIR="$(echo -n '${base64encode(local.workdir)}' | base64 -d)" \
|
|
ARG_PRE_INSTALL_SCRIPT="$(echo -n '${local.encoded_pre_install_script}' | base64 -d)" \
|
|
ARG_INSTALL_SCRIPT="$(echo -n '${local.encoded_install_script}' | base64 -d)" \
|
|
ARG_INSTALL_AGENTAPI='${var.install_agentapi}' \
|
|
ARG_AGENTAPI_VERSION='${var.agentapi_version}' \
|
|
ARG_START_SCRIPT="$(echo -n '${local.agentapi_start_script_b64}' | base64 -d)" \
|
|
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}' \
|
|
ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \
|
|
ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \
|
|
/tmp/main.sh
|
|
EOT
|
|
run_on_start = true
|
|
}
|
|
|
|
resource "coder_script" "agentapi_shutdown" {
|
|
agent_id = var.agent_id
|
|
display_name = "AgentAPI Shutdown"
|
|
icon = var.web_app_icon
|
|
run_on_stop = true
|
|
script = <<-EOT
|
|
#!/bin/bash
|
|
set -o pipefail
|
|
|
|
echo -n '${base64encode(local.shutdown_script)}' | base64 -d > /tmp/agentapi-shutdown.sh
|
|
chmod +x /tmp/agentapi-shutdown.sh
|
|
|
|
ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \
|
|
ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \
|
|
ARG_AGENTAPI_PORT='${var.agentapi_port}' \
|
|
/tmp/agentapi-shutdown.sh
|
|
EOT
|
|
}
|
|
|
|
resource "coder_app" "agentapi_web" {
|
|
slug = var.web_app_slug
|
|
display_name = var.web_app_display_name
|
|
agent_id = var.agent_id
|
|
url = "http://localhost:${var.agentapi_port}/"
|
|
icon = var.web_app_icon
|
|
order = var.web_app_order
|
|
group = var.web_app_group
|
|
subdomain = var.agentapi_subdomain
|
|
healthcheck {
|
|
url = "http://localhost:${var.agentapi_port}/status"
|
|
interval = 3
|
|
threshold = 20
|
|
}
|
|
}
|
|
|
|
resource "coder_app" "agentapi_cli" {
|
|
count = var.cli_app ? 1 : 0
|
|
|
|
slug = var.cli_app_slug
|
|
display_name = var.cli_app_display_name
|
|
agent_id = var.agent_id
|
|
command = <<-EOT
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
export LANG=en_US.UTF-8
|
|
export LC_ALL=en_US.UTF-8
|
|
|
|
agentapi attach
|
|
EOT
|
|
icon = var.cli_app_icon
|
|
order = var.cli_app_order
|
|
group = var.cli_app_group
|
|
}
|
|
|
|
output "task_app_id" {
|
|
value = coder_app.agentapi_web.id
|
|
}
|