refactor(agentapi): decouple boundary logic and improve script sourcing
This commit is contained in:
parent
b76b544e78
commit
ad25115a92
@ -88,47 +88,6 @@ module "agentapi" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Boundary (Network Filtering)
|
|
||||||
|
|
||||||
The agentapi module supports optional [Agent Boundaries](https://coder.com/docs/ai-coder/agent-boundaries)
|
|
||||||
for network filtering. When enabled, the module sets up a `AGENTAPI_BOUNDARY_PREFIX` environment
|
|
||||||
variable that points to a wrapper script. Agent modules should use this prefix in their
|
|
||||||
start scripts to run the agent process through boundary.
|
|
||||||
|
|
||||||
Boundary requires a `config.yaml` file with your allowlist, jail type, proxy port, and log
|
|
||||||
level. See the [Agent Boundaries documentation](https://coder.com/docs/ai-coder/agent-boundaries)
|
|
||||||
for configuration details.
|
|
||||||
To enable:
|
|
||||||
|
|
||||||
```tf
|
|
||||||
module "agentapi" {
|
|
||||||
# ... other config
|
|
||||||
enable_boundary = true
|
|
||||||
boundary_config_path = "/home/coder/.config/coder_boundary/config.yaml"
|
|
||||||
|
|
||||||
# Optional: install boundary binary instead of using coder subcommand
|
|
||||||
# use_boundary_directly = true
|
|
||||||
# boundary_version = "0.6.0"
|
|
||||||
# compile_boundary_from_source = false
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Contract for agent modules
|
|
||||||
|
|
||||||
When `enable_boundary = true`, the agentapi module exports `AGENTAPI_BOUNDARY_PREFIX`
|
|
||||||
as an environment variable pointing to a wrapper script. Agent module start scripts
|
|
||||||
should check for this variable and use it to prefix the agent command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if [ -n "${AGENTAPI_BOUNDARY_PREFIX:-}" ]; then
|
|
||||||
agentapi server -- "${AGENTAPI_BOUNDARY_PREFIX}" my-agent "${ARGS[@]}" &
|
|
||||||
else
|
|
||||||
agentapi server -- my-agent "${ARGS[@]}" &
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures only the agent process is sandboxed while agentapi itself runs unrestricted.
|
|
||||||
|
|
||||||
## For module developers
|
## For module developers
|
||||||
|
|
||||||
For a complete example of how to use this module, see the [Goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
|
For a complete example of how to use this module, see the [Goose module](https://github.com/coder/registry/blob/main/registry/coder/modules/goose/main.tf).
|
||||||
|
|||||||
@ -613,109 +613,4 @@ describe("agentapi", async () => {
|
|||||||
expect(result.stdout).toContain("Sending SIGTERM to AgentAPI");
|
expect(result.stdout).toContain("Sending SIGTERM to AgentAPI");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("boundary", async () => {
|
|
||||||
test("boundary-disabled-by-default", async () => {
|
|
||||||
const { id } = await setup();
|
|
||||||
await execModuleScript(id);
|
|
||||||
await expectAgentAPIStarted(id);
|
|
||||||
// Config file should NOT exist when boundary is disabled
|
|
||||||
const configCheck = await execContainer(id, [
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"test -f /home/coder/.config/coder_boundary/config.yaml && echo exists || echo missing",
|
|
||||||
]);
|
|
||||||
expect(configCheck.stdout.trim()).toBe("missing");
|
|
||||||
// AGENTAPI_BOUNDARY_PREFIX should NOT be in the mock log
|
|
||||||
const mockLog = await readFileContainer(
|
|
||||||
id,
|
|
||||||
"/home/coder/agentapi-mock.log",
|
|
||||||
);
|
|
||||||
expect(mockLog).not.toContain("AGENTAPI_BOUNDARY_PREFIX:");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("boundary-enabled", async () => {
|
|
||||||
const { id } = await setup({
|
|
||||||
moduleVariables: {
|
|
||||||
enable_boundary: "true",
|
|
||||||
boundary_config_path: "/tmp/test-boundary.yaml",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// Write boundary config to the path before running the module
|
|
||||||
await execContainer(id, [
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
`cat > /tmp/test-boundary.yaml <<'EOF'
|
|
||||||
jail_type: landjail
|
|
||||||
proxy_port: 8087
|
|
||||||
log_level: warn
|
|
||||||
allowlist:
|
|
||||||
- "domain=api.example.com"
|
|
||||||
EOF`,
|
|
||||||
]);
|
|
||||||
// Add mock coder binary for boundary setup
|
|
||||||
await writeExecutable({
|
|
||||||
containerId: id,
|
|
||||||
filePath: "/usr/bin/coder",
|
|
||||||
content: `#!/bin/bash
|
|
||||||
if [ "$1" = "boundary" ]; then
|
|
||||||
shift; shift; exec "$@"
|
|
||||||
fi
|
|
||||||
echo "mock coder"`,
|
|
||||||
});
|
|
||||||
await execModuleScript(id);
|
|
||||||
await expectAgentAPIStarted(id);
|
|
||||||
// Verify the config file exists at the specified path
|
|
||||||
const config = await readFileContainer(id, "/tmp/test-boundary.yaml");
|
|
||||||
expect(config).toContain("jail_type: landjail");
|
|
||||||
expect(config).toContain("proxy_port: 8087");
|
|
||||||
expect(config).toContain("domain=api.example.com");
|
|
||||||
// AGENTAPI_BOUNDARY_PREFIX should be exported
|
|
||||||
const mockLog = await readFileContainer(
|
|
||||||
id,
|
|
||||||
"/home/coder/agentapi-mock.log",
|
|
||||||
);
|
|
||||||
expect(mockLog).toContain("AGENTAPI_BOUNDARY_PREFIX:");
|
|
||||||
// E2E: start script should have used the wrapper
|
|
||||||
const startLog = await readFileContainer(
|
|
||||||
id,
|
|
||||||
"/home/coder/test-agentapi-start.log",
|
|
||||||
);
|
|
||||||
expect(startLog).toContain("Starting with boundary:");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("boundary-enabled-no-coder-binary", async () => {
|
|
||||||
const { id } = await setup({
|
|
||||||
moduleVariables: {
|
|
||||||
enable_boundary: "true",
|
|
||||||
boundary_config_path: "/tmp/test-boundary.yaml",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// Write boundary config
|
|
||||||
await execContainer(id, [
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
`cat > /tmp/test-boundary.yaml <<'EOF'
|
|
||||||
jail_type: landjail
|
|
||||||
proxy_port: 8087
|
|
||||||
log_level: warn
|
|
||||||
EOF`,
|
|
||||||
]);
|
|
||||||
// Remove coder binary to simulate it not being available
|
|
||||||
await execContainer(
|
|
||||||
id,
|
|
||||||
[
|
|
||||||
"bash",
|
|
||||||
"-c",
|
|
||||||
"rm -f /usr/bin/coder /usr/local/bin/coder 2>/dev/null; hash -r",
|
|
||||||
],
|
|
||||||
["--user", "root"],
|
|
||||||
);
|
|
||||||
const resp = await execModuleScript(id);
|
|
||||||
// Script should fail because coder binary is required
|
|
||||||
expect(resp.exitCode).not.toBe(0);
|
|
||||||
const scriptLog = await readFileContainer(id, "/home/coder/script.log");
|
|
||||||
expect(scriptLog).toContain("Boundary cannot be enabled");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -93,29 +93,6 @@ variable "cli_app_slug" {
|
|||||||
description = "The slug of the CLI workspace app."
|
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" {
|
variable "install_agentapi" {
|
||||||
type = bool
|
type = bool
|
||||||
description = "Whether to install AgentAPI."
|
description = "Whether to install AgentAPI."
|
||||||
@ -170,36 +147,6 @@ variable "module_dir_name" {
|
|||||||
description = "Name of the subdirectory in the home directory for module files."
|
description = "Name of the subdirectory in the home directory for module files."
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "enable_boundary" {
|
|
||||||
type = bool
|
|
||||||
description = "Enable coder boundary for network filtering. Requires boundary_config to be set."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "boundary_config_path" {
|
|
||||||
type = string
|
|
||||||
description = "Path to boundary config.yaml inside the workspace. If provided, exposed as BOUNDARY_CONFIG env var."
|
|
||||||
default = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "boundary_version" {
|
|
||||||
type = string
|
|
||||||
description = "Boundary version. When use_boundary_directly is true, a release version should be provided or 'latest' for the latest release. When compile_boundary_from_source is true, a valid git reference should be provided (tag, commit, branch)."
|
|
||||||
default = "latest"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "compile_boundary_from_source" {
|
|
||||||
type = bool
|
|
||||||
description = "Whether to compile boundary from source instead of using the official install script."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "use_boundary_directly" {
|
|
||||||
type = bool
|
|
||||||
description = "Whether to use boundary binary directly instead of coder boundary subcommand. When false (default), uses coder boundary subcommand. When true, installs and uses boundary binary from release."
|
|
||||||
default = false
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "enable_state_persistence" {
|
variable "enable_state_persistence" {
|
||||||
type = bool
|
type = bool
|
||||||
description = "Enable AgentAPI conversation state persistence across restarts."
|
description = "Enable AgentAPI conversation state persistence across restarts."
|
||||||
@ -218,11 +165,10 @@ variable "pid_file_path" {
|
|||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_env" "boundary_config" {
|
variable "module_directory" {
|
||||||
count = var.enable_boundary && var.boundary_config_path != "" ? 1 : 0
|
type = string
|
||||||
agent_id = var.agent_id
|
description = ""
|
||||||
name = "BOUNDARY_CONFIG"
|
default = "$HOME/.coder-modules/coder/agentapi"
|
||||||
value = var.boundary_config_path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
@ -233,10 +179,6 @@ locals {
|
|||||||
|
|
||||||
# we always trim the slash for consistency
|
# we always trim the slash for consistency
|
||||||
workdir = trimsuffix(var.folder, "/")
|
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"))
|
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.
|
// Chat base path is only set if not using a subdomain.
|
||||||
// NOTE:
|
// NOTE:
|
||||||
@ -248,7 +190,10 @@ locals {
|
|||||||
main_script = file("${path.module}/scripts/main.sh")
|
main_script = file("${path.module}/scripts/main.sh")
|
||||||
shutdown_script = file("${path.module}/scripts/agentapi-shutdown.sh")
|
shutdown_script = file("${path.module}/scripts/agentapi-shutdown.sh")
|
||||||
lib_script = file("${path.module}/scripts/lib.sh")
|
lib_script = file("${path.module}/scripts/lib.sh")
|
||||||
boundary_script = file("${path.module}/scripts/boundary.sh")
|
|
||||||
|
main_script_destination = "${var.module_directory}/main.sh"
|
||||||
|
lib_script_destination = "${var.module_directory}/agentapi-lib.sh"
|
||||||
|
shutdown_script_destination = "${var.module_directory}/agentapi-shutdown.sh"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_script" "agentapi" {
|
resource "coder_script" "agentapi" {
|
||||||
@ -260,34 +205,24 @@ 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
|
echo -n '${base64encode(local.main_script)}' | base64 -d > "${local.main_script_destination}"
|
||||||
chmod +x /tmp/main.sh
|
chmod +x /tmp/main.sh
|
||||||
echo -n '${base64encode(local.lib_script)}' | base64 -d > /tmp/agentapi-lib.sh
|
echo -n '${base64encode(local.lib_script)}' | base64 -d > "${local.lib_script_destination}"
|
||||||
|
|
||||||
echo -n '${base64encode(local.boundary_script)}' | base64 -d > /tmp/agentapi-boundary.sh
|
|
||||||
chmod +x /tmp/agentapi-boundary.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)" \
|
||||||
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_INSTALL_AGENTAPI='${var.install_agentapi}' \
|
||||||
ARG_AGENTAPI_VERSION='${var.agentapi_version}' \
|
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_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_PORT='${var.agentapi_port}' \
|
||||||
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}' \
|
||||||
ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \
|
|
||||||
ARG_BOUNDARY_VERSION='${var.boundary_version}' \
|
|
||||||
ARG_COMPILE_BOUNDARY_FROM_SOURCE='${var.compile_boundary_from_source}' \
|
|
||||||
ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \
|
|
||||||
ARG_ENABLE_STATE_PERSISTENCE='${var.enable_state_persistence}' \
|
ARG_ENABLE_STATE_PERSISTENCE='${var.enable_state_persistence}' \
|
||||||
ARG_STATE_FILE_PATH='${var.state_file_path}' \
|
ARG_STATE_FILE_PATH='${var.state_file_path}' \
|
||||||
ARG_PID_FILE_PATH='${var.pid_file_path}' \
|
ARG_PID_FILE_PATH='${var.pid_file_path}' \
|
||||||
/tmp/main.sh
|
ARG_LIB_SCRIPT_PATH='${local.lib_script_destination}' \
|
||||||
|
"${local.main_script_destination}"
|
||||||
EOT
|
EOT
|
||||||
run_on_start = true
|
run_on_start = true
|
||||||
}
|
}
|
||||||
@ -301,9 +236,9 @@ 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
|
echo -n '${base64encode(local.shutdown_script)}' | base64 -d > "${local.shutdown_script_destination}"
|
||||||
chmod +x /tmp/agentapi-shutdown.sh
|
chmod +x /tmp/agentapi-shutdown.sh
|
||||||
echo -n '${base64encode(local.lib_script)}' | base64 -d > /tmp/agentapi-lib.sh
|
echo -n '${base64encode(local.lib_script)}' | base64 -d > "${local.lib_script_destination}"
|
||||||
|
|
||||||
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}' \
|
||||||
@ -311,7 +246,8 @@ resource "coder_script" "agentapi_shutdown" {
|
|||||||
ARG_ENABLE_STATE_PERSISTENCE='${var.enable_state_persistence}' \
|
ARG_ENABLE_STATE_PERSISTENCE='${var.enable_state_persistence}' \
|
||||||
ARG_MODULE_DIR_NAME='${var.module_dir_name}' \
|
ARG_MODULE_DIR_NAME='${var.module_dir_name}' \
|
||||||
ARG_PID_FILE_PATH='${var.pid_file_path}' \
|
ARG_PID_FILE_PATH='${var.pid_file_path}' \
|
||||||
/tmp/agentapi-shutdown.sh
|
ARG_LIB_SCRIPT_PATH='${local.lib_script_destination}' \
|
||||||
|
"${local.shutdown_script_destination}"
|
||||||
EOT
|
EOT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,11 @@ readonly AGENTAPI_PORT="${ARG_AGENTAPI_PORT:-3284}"
|
|||||||
readonly ENABLE_STATE_PERSISTENCE="${ARG_ENABLE_STATE_PERSISTENCE:-false}"
|
readonly ENABLE_STATE_PERSISTENCE="${ARG_ENABLE_STATE_PERSISTENCE:-false}"
|
||||||
readonly MODULE_DIR_NAME="${ARG_MODULE_DIR_NAME:-}"
|
readonly MODULE_DIR_NAME="${ARG_MODULE_DIR_NAME:-}"
|
||||||
readonly PID_FILE_PATH="${ARG_PID_FILE_PATH:-${MODULE_DIR_NAME:+$HOME/$MODULE_DIR_NAME/agentapi.pid}}"
|
readonly PID_FILE_PATH="${ARG_PID_FILE_PATH:-${MODULE_DIR_NAME:+$HOME/$MODULE_DIR_NAME/agentapi.pid}}"
|
||||||
|
readonly LIB_SCRIPT_PATH="${ARG_LIB_SCRIPT_PATH}"
|
||||||
|
|
||||||
# Source shared utilities (written by the coder_script wrapper).
|
# Source shared utilities (written by the coder_script wrapper).
|
||||||
# shellcheck source=lib.sh
|
# shellcheck source=lib.sh
|
||||||
source /tmp/agentapi-lib.sh
|
source "${LIB_SCRIPT_PATH}"
|
||||||
|
|
||||||
# Runtime environment variables.
|
# Runtime environment variables.
|
||||||
readonly CODER_AGENT_URL="${CODER_AGENT_URL:-}"
|
readonly CODER_AGENT_URL="${CODER_AGENT_URL:-}"
|
||||||
|
|||||||
@ -1,95 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# boundary.sh - Boundary installation and setup for agentapi module.
|
|
||||||
# Sourced by main.sh when ENABLE_BOUNDARY=true.
|
|
||||||
# Exports AGENTAPI_BOUNDARY_PREFIX for use by module start scripts.
|
|
||||||
|
|
||||||
validate_boundary_subcommand() {
|
|
||||||
if command_exists coder; then
|
|
||||||
if coder boundary --help > /dev/null 2>&1; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "Error: 'coder' command found but does not support 'boundary' subcommand. Please enable install_boundary."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Error: ENABLE_BOUNDARY=true, but 'coder' command not found. Boundary cannot be enabled." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install boundary binary if needed.
|
|
||||||
# Uses one of three strategies:
|
|
||||||
# 1. Compile from source (compile_boundary_from_source=true)
|
|
||||||
# 2. Install from release (use_boundary_directly=true)
|
|
||||||
# 3. Use coder boundary subcommand (default, no installation needed)
|
|
||||||
install_boundary() {
|
|
||||||
if [ "${COMPILE_BOUNDARY_FROM_SOURCE}" = "true" ]; then
|
|
||||||
echo "Compiling boundary from source (version: ${BOUNDARY_VERSION})"
|
|
||||||
|
|
||||||
# Remove existing boundary directory to allow re-running safely
|
|
||||||
if [ -d boundary ]; then
|
|
||||||
rm -rf boundary
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Cloning boundary repository"
|
|
||||||
git clone https://github.com/coder/boundary.git
|
|
||||||
cd boundary || exit 1
|
|
||||||
git checkout "${BOUNDARY_VERSION}"
|
|
||||||
|
|
||||||
make build
|
|
||||||
|
|
||||||
sudo cp boundary /usr/local/bin/
|
|
||||||
sudo chmod +x /usr/local/bin/boundary
|
|
||||||
cd - || exit 1
|
|
||||||
elif [ "${USE_BOUNDARY_DIRECTLY}" = "true" ]; then
|
|
||||||
echo "Installing boundary using official install script (version: ${BOUNDARY_VERSION})"
|
|
||||||
curl -fsSL https://raw.githubusercontent.com/coder/boundary/main/install.sh | bash -s -- --version "${BOUNDARY_VERSION}"
|
|
||||||
else
|
|
||||||
validate_boundary_subcommand
|
|
||||||
echo "Using coder boundary subcommand (provided by Coder)"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set up boundary: install, write config, create wrapper script.
|
|
||||||
# Exports AGENTAPI_BOUNDARY_PREFIX pointing to the wrapper script.
|
|
||||||
setup_boundary() {
|
|
||||||
local module_path="$1"
|
|
||||||
|
|
||||||
echo "Setting up coder boundary..."
|
|
||||||
|
|
||||||
# Install boundary binary if needed
|
|
||||||
install_boundary
|
|
||||||
|
|
||||||
# Determine which boundary command to use and create wrapper script
|
|
||||||
BOUNDARY_WRAPPER_SCRIPT="$module_path/boundary-wrapper.sh"
|
|
||||||
|
|
||||||
if [ "${COMPILE_BOUNDARY_FROM_SOURCE}" = "true" ] || [ "${USE_BOUNDARY_DIRECTLY}" = "true" ]; then
|
|
||||||
# Use boundary binary directly (from compilation or release installation)
|
|
||||||
cat > "${BOUNDARY_WRAPPER_SCRIPT}" << 'WRAPPER_EOF'
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
exec boundary -- "$@"
|
|
||||||
WRAPPER_EOF
|
|
||||||
else
|
|
||||||
# Use coder boundary subcommand (default)
|
|
||||||
# Copy coder binary to strip CAP_NET_ADMIN capabilities.
|
|
||||||
# This is necessary because boundary doesn't work with privileged binaries
|
|
||||||
# (you can't launch privileged binaries inside network namespaces unless
|
|
||||||
# you have sys_admin).
|
|
||||||
CODER_NO_CAPS="$module_path/coder-no-caps"
|
|
||||||
if ! cp "$(which coder)" "$CODER_NO_CAPS"; then
|
|
||||||
echo "Error: Failed to copy coder binary to ${CODER_NO_CAPS}. Boundary cannot be enabled." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cat > "${BOUNDARY_WRAPPER_SCRIPT}" << 'WRAPPER_EOF'
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
exec "${SCRIPT_DIR}/coder-no-caps" boundary -- "$@"
|
|
||||||
WRAPPER_EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x "${BOUNDARY_WRAPPER_SCRIPT}"
|
|
||||||
export AGENTAPI_BOUNDARY_PREFIX="${BOUNDARY_WRAPPER_SCRIPT}"
|
|
||||||
echo "Boundary wrapper configured: ${AGENTAPI_BOUNDARY_PREFIX}"
|
|
||||||
}
|
|
||||||
@ -16,17 +16,14 @@ AGENTAPI_PORT="$ARG_AGENTAPI_PORT"
|
|||||||
AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
|
AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
|
||||||
TASK_ID="${ARG_TASK_ID:-}"
|
TASK_ID="${ARG_TASK_ID:-}"
|
||||||
TASK_LOG_SNAPSHOT="${ARG_TASK_LOG_SNAPSHOT:-true}"
|
TASK_LOG_SNAPSHOT="${ARG_TASK_LOG_SNAPSHOT:-true}"
|
||||||
ENABLE_BOUNDARY="${ARG_ENABLE_BOUNDARY:-false}"
|
|
||||||
BOUNDARY_VERSION="${ARG_BOUNDARY_VERSION:-latest}"
|
|
||||||
COMPILE_BOUNDARY_FROM_SOURCE="${ARG_COMPILE_BOUNDARY_FROM_SOURCE:-false}"
|
|
||||||
USE_BOUNDARY_DIRECTLY="${ARG_USE_BOUNDARY_DIRECTLY:-false}"
|
|
||||||
ENABLE_STATE_PERSISTENCE="${ARG_ENABLE_STATE_PERSISTENCE:-false}"
|
ENABLE_STATE_PERSISTENCE="${ARG_ENABLE_STATE_PERSISTENCE:-false}"
|
||||||
STATE_FILE_PATH="${ARG_STATE_FILE_PATH:-}"
|
STATE_FILE_PATH="${ARG_STATE_FILE_PATH:-}"
|
||||||
PID_FILE_PATH="${ARG_PID_FILE_PATH:-}"
|
PID_FILE_PATH="${ARG_PID_FILE_PATH:-}"
|
||||||
|
LIB_SCRIPT_PATH="$ARG_LIB_SCRIPT_PATH"
|
||||||
set +o nounset
|
set +o nounset
|
||||||
|
|
||||||
# shellcheck source=lib.sh
|
# shellcheck source=lib.sh
|
||||||
source /tmp/agentapi-lib.sh
|
source "${LIB_SCRIPT_PATH}"
|
||||||
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" > /dev/null 2>&1
|
command -v "$1" > /dev/null 2>&1
|
||||||
@ -113,14 +110,6 @@ export LC_ALL=en_US.UTF-8
|
|||||||
|
|
||||||
cd "${WORKDIR}"
|
cd "${WORKDIR}"
|
||||||
|
|
||||||
# Set up boundary if enabled
|
|
||||||
export AGENTAPI_BOUNDARY_PREFIX=""
|
|
||||||
if [ "${ENABLE_BOUNDARY}" = "true" ]; then
|
|
||||||
# shellcheck source=boundary.sh
|
|
||||||
source /tmp/agentapi-boundary.sh
|
|
||||||
setup_boundary "$module_path"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
|
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
|
||||||
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
|
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
|
||||||
export AGENTAPI_ALLOWED_HOSTS="*"
|
export AGENTAPI_ALLOWED_HOSTS="*"
|
||||||
|
|||||||
@ -31,16 +31,6 @@ for (const v of [
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Log boundary env vars.
|
|
||||||
for (const v of ["AGENTAPI_BOUNDARY_PREFIX"]) {
|
|
||||||
if (process.env[v]) {
|
|
||||||
fs.appendFileSync(
|
|
||||||
"/home/coder/agentapi-mock.log",
|
|
||||||
`\n${v}: ${process.env[v]}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write PID file for shutdown script.
|
// Write PID file for shutdown script.
|
||||||
if (process.env.AGENTAPI_PID_FILE) {
|
if (process.env.AGENTAPI_PID_FILE) {
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|||||||
@ -17,16 +17,6 @@ if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then
|
|||||||
export AGENTAPI_CHAT_BASE_PATH
|
export AGENTAPI_CHAT_BASE_PATH
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use boundary wrapper if configured by agentapi module.
|
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
|
||||||
# AGENTAPI_BOUNDARY_PREFIX is set by the agentapi module's main.sh
|
bash -c aiagent \
|
||||||
# and points to a wrapper script that runs the command through coder boundary.
|
> "$log_file_path" 2>&1
|
||||||
if [ -n "${AGENTAPI_BOUNDARY_PREFIX:-}" ]; then
|
|
||||||
echo "Starting with boundary: ${AGENTAPI_BOUNDARY_PREFIX}" >> /home/coder/test-agentapi-start.log
|
|
||||||
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
|
|
||||||
"${AGENTAPI_BOUNDARY_PREFIX}" bash -c aiagent \
|
|
||||||
> "$log_file_path" 2>&1
|
|
||||||
else
|
|
||||||
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
|
|
||||||
bash -c aiagent \
|
|
||||||
> "$log_file_path" 2>&1
|
|
||||||
fi
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user