fix(registry/coder/modules): write bootstrap scripts to module dir instead of /tmp

Bootstrap scripts used fixed /tmp paths with generic names like
/tmp/main.sh, making them easy to collide with or accidentally
overwrite. Write them into $HOME/<module_dir_name>/scripts/ instead,
following the pattern used by the agent-helper module.

Also fix install.sh hardcoding port 3284 instead of using
ARG_AGENTAPI_PORT.

Refs #736
This commit is contained in:
Mathias Fredriksson 2026-02-27 14:49:35 +00:00
parent 6ec506e9b6
commit 8c45c65835
6 changed files with 44 additions and 25 deletions

View File

@ -16,7 +16,7 @@ The AgentAPI module is a building block for modules that need to run an AgentAPI
```tf ```tf
module "agentapi" { module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder" source = "registry.coder.com/coder/agentapi/coder"
version = "2.1.1" version = "2.1.2"
agent_id = var.agent_id agent_id = var.agent_id
web_app_slug = local.app_slug web_app_slug = local.app_slug

View File

@ -309,16 +309,22 @@ describe("agentapi", async () => {
"../scripts/agentapi-shutdown.sh", "../scripts/agentapi-shutdown.sh",
); );
const shutdownScriptPath = `/home/coder/${moduleDirName}/scripts/agentapi-shutdown.sh`;
await execContainer(containerId, [
"bash",
"-c",
`mkdir -p /home/coder/${moduleDirName}/scripts`,
]);
await writeExecutable({ await writeExecutable({
containerId, containerId,
filePath: "/tmp/shutdown.sh", filePath: shutdownScriptPath,
content: shutdownScript, content: shutdownScript,
}); });
return await execContainer(containerId, [ return await execContainer(containerId, [
"bash", "bash",
"-c", "-c",
`ARG_TASK_ID=${taskId} ARG_AGENTAPI_PORT=3284 CODER_AGENT_URL=http://localhost:18080 CODER_AGENT_TOKEN=test-token /tmp/shutdown.sh`, `ARG_TASK_ID=${taskId} ARG_AGENTAPI_PORT=3284 CODER_AGENT_URL=http://localhost:18080 CODER_AGENT_TOKEN=test-token ${shutdownScriptPath}`,
]); ]);
}; };

View File

@ -193,8 +193,10 @@ resource "coder_script" "agentapi" {
set -o errexit set -o errexit
set -o pipefail set -o pipefail
echo -n '${base64encode(local.main_script)}' | base64 -d > /tmp/main.sh SCRIPT_DIR="$HOME/${var.module_dir_name}/scripts"
chmod +x /tmp/main.sh mkdir -p "$SCRIPT_DIR"
echo -n '${base64encode(local.main_script)}' | base64 -d > "$SCRIPT_DIR/agentapi-main.sh"
chmod +x "$SCRIPT_DIR/agentapi-main.sh"
ARG_MODULE_DIR_NAME='${var.module_dir_name}' \ ARG_MODULE_DIR_NAME='${var.module_dir_name}' \
ARG_WORKDIR="$(echo -n '${base64encode(local.workdir)}' | base64 -d)" \ ARG_WORKDIR="$(echo -n '${base64encode(local.workdir)}' | base64 -d)" \
@ -209,7 +211,7 @@ resource "coder_script" "agentapi" {
ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \ ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \
ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \ ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \
ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \ ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \
/tmp/main.sh "$SCRIPT_DIR/agentapi-main.sh"
EOT EOT
run_on_start = true run_on_start = true
} }
@ -223,13 +225,15 @@ resource "coder_script" "agentapi_shutdown" {
#!/bin/bash #!/bin/bash
set -o pipefail set -o pipefail
echo -n '${base64encode(local.shutdown_script)}' | base64 -d > /tmp/agentapi-shutdown.sh SCRIPT_DIR="$HOME/${var.module_dir_name}/scripts"
chmod +x /tmp/agentapi-shutdown.sh mkdir -p "$SCRIPT_DIR"
echo -n '${base64encode(local.shutdown_script)}' | base64 -d > "$SCRIPT_DIR/agentapi-shutdown.sh"
chmod +x "$SCRIPT_DIR/agentapi-shutdown.sh"
ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \ ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \
ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \ ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \
ARG_AGENTAPI_PORT='${var.agentapi_port}' \ ARG_AGENTAPI_PORT='${var.agentapi_port}' \
/tmp/agentapi-shutdown.sh "$SCRIPT_DIR/agentapi-shutdown.sh"
EOT EOT
} }

View File

@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx" claude_api_key = "xxxx-xxxxx-xxxx"
@ -47,7 +47,7 @@ By default, when `enable_boundary = true`, the module uses `coder boundary` subc
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
enable_boundary = true enable_boundary = true
@ -68,7 +68,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
enable_aibridge = true enable_aibridge = true
@ -97,7 +97,7 @@ data "coder_task" "me" {}
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
ai_prompt = data.coder_task.me.prompt ai_prompt = data.coder_task.me.prompt
@ -120,7 +120,7 @@ This example shows additional configuration options for version pinning, custom
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
@ -176,7 +176,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
install_claude_code = true install_claude_code = true
@ -198,7 +198,7 @@ variable "claude_code_oauth_token" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token claude_code_oauth_token = var.claude_code_oauth_token
@ -271,7 +271,7 @@ resource "coder_env" "bedrock_api_key" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
@ -328,7 +328,7 @@ resource "coder_env" "google_application_credentials" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.7.5" version = "4.7.6"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514" model = "claude-sonnet-4@20250514"

View File

@ -318,6 +318,7 @@ locals {
install_script = file("${path.module}/scripts/install.sh") install_script = file("${path.module}/scripts/install.sh")
start_script = file("${path.module}/scripts/start.sh") start_script = file("${path.module}/scripts/start.sh")
module_dir_name = ".claude-module" module_dir_name = ".claude-module"
agentapi_port = 3284
# Extract hostname from access_url for boundary --allow flag # Extract hostname from access_url for boundary --allow flag
coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "") coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "")
claude_api_key = var.enable_aibridge ? data.coder_workspace_owner.me.session_token : var.claude_api_key claude_api_key = var.enable_aibridge ? data.coder_workspace_owner.me.session_token : var.claude_api_key
@ -378,8 +379,11 @@ module "agentapi" {
#!/bin/bash #!/bin/bash
set -o errexit set -o errexit
set -o pipefail set -o pipefail
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
chmod +x /tmp/start.sh SCRIPT_DIR="$HOME/${local.module_dir_name}/scripts"
mkdir -p "$SCRIPT_DIR"
echo -n '${base64encode(local.start_script)}' | base64 -d > "$SCRIPT_DIR/claude-code-start.sh"
chmod +x "$SCRIPT_DIR/claude-code-start.sh"
ARG_RESUME_SESSION_ID='${var.resume_session_id}' \ ARG_RESUME_SESSION_ID='${var.resume_session_id}' \
ARG_CONTINUE='${var.continue}' \ ARG_CONTINUE='${var.continue}' \
@ -394,7 +398,7 @@ module "agentapi" {
ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \ ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \
ARG_CODER_HOST='${local.coder_host}' \ ARG_CODER_HOST='${local.coder_host}' \
ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}' \ ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}' \
/tmp/start.sh "$SCRIPT_DIR/claude-code-start.sh"
EOT EOT
install_script = <<-EOT install_script = <<-EOT
@ -402,8 +406,10 @@ module "agentapi" {
set -o errexit set -o errexit
set -o pipefail set -o pipefail
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh SCRIPT_DIR="$HOME/${local.module_dir_name}/scripts"
chmod +x /tmp/install.sh mkdir -p "$SCRIPT_DIR"
echo -n '${base64encode(local.install_script)}' | base64 -d > "$SCRIPT_DIR/claude-code-install.sh"
chmod +x "$SCRIPT_DIR/claude-code-install.sh"
ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}' \ ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}' \
ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \ ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \
ARG_INSTALL_CLAUDE_CODE='${var.install_claude_code}' \ ARG_INSTALL_CLAUDE_CODE='${var.install_claude_code}' \
@ -416,7 +422,8 @@ module "agentapi" {
ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \ ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \
ARG_MCP_CONFIG_REMOTE_PATH='${base64encode(jsonencode(var.mcp_config_remote_path))}' \ ARG_MCP_CONFIG_REMOTE_PATH='${base64encode(jsonencode(var.mcp_config_remote_path))}' \
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \ ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
/tmp/install.sh ARG_AGENTAPI_PORT='${local.agentapi_port}' \
"$SCRIPT_DIR/claude-code-install.sh"
EOT EOT
} }

View File

@ -17,6 +17,7 @@ ARG_CLAUDE_BINARY_PATH="${ARG_CLAUDE_BINARY_PATH//\$HOME/$HOME}"
ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false} ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true} ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
ARG_AGENTAPI_PORT=${ARG_AGENTAPI_PORT:-3284}
ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d) ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d)
ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n "${ARG_MCP_CONFIG_REMOTE_PATH:-}" | base64 -d) ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n "${ARG_MCP_CONFIG_REMOTE_PATH:-}" | base64 -d)
ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-} ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
@ -34,6 +35,7 @@ printf "ARG_CLAUDE_BINARY_PATH: %s\n" "$ARG_CLAUDE_BINARY_PATH"
printf "ARG_INSTALL_VIA_NPM: %s\n" "$ARG_INSTALL_VIA_NPM" printf "ARG_INSTALL_VIA_NPM: %s\n" "$ARG_INSTALL_VIA_NPM"
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS" printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG" printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG"
printf "ARG_AGENTAPI_PORT: %s\n" "$ARG_AGENTAPI_PORT"
printf "ARG_MCP: %s\n" "$ARG_MCP" printf "ARG_MCP: %s\n" "$ARG_MCP"
printf "ARG_MCP_CONFIG_REMOTE_PATH: %s\n" "$ARG_MCP_CONFIG_REMOTE_PATH" printf "ARG_MCP_CONFIG_REMOTE_PATH: %s\n" "$ARG_MCP_CONFIG_REMOTE_PATH"
printf "ARG_ALLOWED_TOOLS: %s\n" "$ARG_ALLOWED_TOOLS" printf "ARG_ALLOWED_TOOLS: %s\n" "$ARG_ALLOWED_TOOLS"
@ -228,7 +230,7 @@ function report_tasks() {
if [ "$ARG_REPORT_TASKS" = "true" ]; then if [ "$ARG_REPORT_TASKS" = "true" ]; then
echo "Configuring Claude Code to report tasks via Coder MCP..." echo "Configuring Claude Code to report tasks via Coder MCP..."
export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG" export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG"
export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284" export CODER_MCP_AI_AGENTAPI_URL="http://localhost:${ARG_AGENTAPI_PORT:-3284}"
coder exp mcp configure claude-code "$ARG_WORKDIR" coder exp mcp configure claude-code "$ARG_WORKDIR"
else else
configure_standalone_mode configure_standalone_mode