Compare commits

...

17 Commits

Author SHA1 Message Date
DevelopmentCats
a3ac438d51 fix(claude-code): check boundary_config_path source exists before creating symlink
Move the source-file existence check before the ln -sf call so we never
create a broken symlink. Previously the check happened after the symlink
was already created, leaving a dangling symlink on disk before exiting.

Generated with OpenClaw using Claude
2026-04-11 20:07:27 +00:00
Claude
388dfee540 fix(claude-code): add explicit existence check for boundary_config_path target
After creating the symlink, explicitly verify that the path specified by
boundary_config_path exists and is a regular file. This gives a clear error
message when the target file is missing, rather than relying solely on the
indirect check via the symlink destination.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 15:08:49 +00:00
DevelopmentCats
132eea2240 fix(claude-code): align version attribute in README code examples
The version-bump script removed alignment spaces from version attributes
in README code block examples. This restores proper terraform fmt-style
alignment so the CI prettier+terraform-formatter check passes.

Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-09 08:06:37 +00:00
DevelopmentCats
3d16037b31 chore(claude-code): bump version to 4.9.3 after addressing review comments
Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-09 03:53:23 +00:00
DevelopmentCats
890f6490ac chore: resolve merge conflict with main, keeping all boundary tests
Merges origin/main into cat/claude-boundary-preconditions, preserving
all boundary config tests (inline, path, auto permission mode, and basic).

Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-08 15:36:52 +00:00
DevelopmentCats
b82fc8cc50 fix(claude-code): remove redundant ARG_BOUNDARY_CONFIG_PATH normalization from elif block
Tilde and \$HOME expansion is already performed at script initialization
(lines 29-30), matching the pattern used for ARG_CLAUDE_BINARY_PATH.
The duplicate expansion inside the elif block was redundant and used a
slightly different pattern (single-slash vs double-slash replace).

Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-08 01:16:51 +00:00
DevelopmentCats
17f9319122 fix: restore version attribute alignment in README tf code blocks
The previous commit accidentally removed proper terraform fmt alignment
from all version attributes in README.md code examples. Restored the
alignment so Prettier + prettier-plugin-terraform-formatter passes CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 08:55:24 +00:00
Coder Agent
aaf149f444 fix: address all Copilot review comments on boundary config validation
- Trim whitespace in enable_boundary validations (trimspace checks)
- Add empty string test cases for boundary_config and boundary_config_path
- Add explicit post-write/link file existence check in start.sh
- Normalize ARG_BOUNDARY_CONFIG_PATH to expand tilde and HOME before symlink

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 18:08:49 +00:00
DevelopmentCats
022fd1c0dd fix(claude-code): expand tilde and \$HOME in boundary config path before symlink
Addresses Copilot review comment on PR #797: when ARG_BOUNDARY_CONFIG_PATH
contains a leading ~ or literal \$HOME (e.g. ~/.config/...), it was passed
as-is to ln -sf without shell expansion, resulting in a broken symlink.
Now expands both forms before creating the symlink.

Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-05 00:40:14 +00:00
DevelopmentCats
8f6853522d fix: address review comments on boundary config validation and safety checks
- Update boundary_config_b64 local and ARG_BOUNDARY_CONFIG_PATH interpolation
  to treat empty/whitespace-only strings as not set using trimspace() checks,
  not just null checks
- Add unified post-fi check in start.sh that BOUNDARY_CONFIG_FILE exists and
  is non-empty after writing/linking, exits with clear error if not

Generated with OpenClaw using Claude
2026-04-04 15:27:22 +00:00
DevelopmentCats
20993b042f fix: align version attribute in tf code blocks for Prettier formatting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 09:51:53 +00:00
DevelopmentCats
738c97246e fix: add boundary config validation and path normalization
Update enable_boundary validation to treat empty/whitespace strings as
unset using trimspace checks, so callers providing "" are handled
consistently with null. Per-variable validations already reject empty
strings. Bump to v4.9.2.

Closes coder/registry#797

Generated with OpenClaw using Claude
2026-04-04 08:09:47 +00:00
DevelopmentCats
5f83d8188a fix: improve boundary configuration validation in start script
- Added a check to ensure the boundary configuration file is not empty after writing inline config.
- Enhanced error messages for better clarity on configuration issues.
2026-04-04 08:09:31 +00:00
DevelopmentCats
352b7752fb feat: address comments from Copilot and tighten up config behaviour and validation 2026-04-04 08:09:31 +00:00
DevelopmentCats
fe3cf30ac2 refactor: button up boundary related testing 2026-04-04 08:09:31 +00:00
DevelopmentCats
9578f81982 chore: update Claude Code module version to 4.9.0 in README 2026-04-04 08:09:31 +00:00
DevelopmentCats
eb2da40cf7 feat: enhance boundary configuration options in Claude Code module
- Added new variables `boundary_config` and `boundary_config_path` for inline YAML and file path configurations, respectively.
- Implemented validation rules to ensure proper configuration when `enable_boundary` is true.
- Updated README with usage examples for both configuration methods.
- Enhanced test cases to cover various boundary configuration scenarios, including validation failures.
2026-04-04 08:09:08 +00:00
4 changed files with 283 additions and 15 deletions

View File

@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
@ -55,15 +55,43 @@ module "claude-code" {
This example shows how to configure the Claude Code module to run the agent behind a process-level boundary that restricts its network access.
By default, when `enable_boundary = true`, the module uses `coder boundary` subcommand (provided by Coder) without requiring any installation.
When `enable_boundary = true`, you must provide network filtering rules via one of two options:
- `boundary_config` — inline YAML string (config lives in the template)
- `boundary_config_path` — path to a config file already on disk
The module writes the config to `~/.config/coder_boundary/config.yaml` automatically.
#### Inline boundary config
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_boundary = true
boundary_config = <<-EOT
allow:
- "*.anthropic.com"
- "*.github.com"
EOT
}
```
#### Boundary config from file path
Use this when the config file is provisioned separately or managed outside the template:
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_boundary = true
boundary_config_path = "/home/coder/.config/coder_boundary/config.yaml"
}
```
@ -81,7 +109,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_aibridge = true
@ -110,7 +138,7 @@ data "coder_task" "me" {}
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
ai_prompt = data.coder_task.me.prompt
@ -133,7 +161,7 @@ This example shows additional configuration options for version pinning, custom
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
@ -189,7 +217,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
install_claude_code = true
@ -211,7 +239,7 @@ variable "claude_code_oauth_token" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token
@ -284,7 +312,7 @@ resource "coder_env" "bedrock_api_key" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
@ -341,7 +369,7 @@ resource "coder_env" "google_application_credentials" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.9.2"
version = "4.9.3"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514"

View File

@ -231,6 +231,38 @@ variable "enable_boundary" {
type = bool
description = "Whether to enable coder boundary for network filtering"
default = false
validation {
condition = !var.enable_boundary || (var.boundary_config == null || trimspace(var.boundary_config) == "") || (var.boundary_config_path == null || trimspace(var.boundary_config_path) == "")
error_message = "Only one of boundary_config or boundary_config_path can be provided, not both."
}
validation {
condition = ((var.boundary_config == null || trimspace(var.boundary_config) == "") && (var.boundary_config_path == null || trimspace(var.boundary_config_path) == "")) || var.enable_boundary
error_message = "boundary_config and boundary_config_path can only be set when enable_boundary is true."
}
}
variable "boundary_config" {
type = string
description = "Inline YAML config for coder boundary network filtering rules. Written to ~/.config/coder_boundary/config.yaml before boundary starts. Mutually exclusive with boundary_config_path."
default = null
validation {
condition = var.boundary_config == null || trimspace(var.boundary_config) != ""
error_message = "boundary_config must not be empty or whitespace-only when provided."
}
}
variable "boundary_config_path" {
type = string
description = "Path to an existing boundary config file on disk. Symlinked to ~/.config/coder_boundary/config.yaml before boundary starts. Mutually exclusive with boundary_config."
default = null
validation {
condition = var.boundary_config_path == null || trimspace(var.boundary_config_path) != ""
error_message = "boundary_config_path must not be empty or whitespace-only when provided."
}
}
variable "boundary_version" {
@ -332,6 +364,7 @@ locals {
module_dir_name = ".claude-module"
# Extract hostname from access_url for boundary --allow flag
coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "")
boundary_config_b64 = var.boundary_config != null && trimspace(var.boundary_config) != "" ? base64encode(var.boundary_config) : ""
claude_api_key = var.enable_aibridge ? data.coder_workspace_owner.me.session_token : var.claude_api_key
# Required prompts for the module to properly report task status to Coder
@ -407,6 +440,8 @@ module "agentapi" {
ARG_COMPILE_FROM_SOURCE='${var.compile_boundary_from_source}' \
ARG_USE_BOUNDARY_DIRECTLY='${var.use_boundary_directly}' \
ARG_CODER_HOST='${local.coder_host}' \
ARG_BOUNDARY_CONFIG='${local.boundary_config_b64}' \
ARG_BOUNDARY_CONFIG_PATH='${var.boundary_config_path != null && trimspace(var.boundary_config_path) != "" ? trimspace(var.boundary_config_path) : ""}' \
ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}' \
/tmp/start.sh
EOT

View File

@ -188,6 +188,74 @@ run "test_claude_code_permission_mode_validation" {
}
}
run "test_claude_code_with_boundary_inline_config" {
command = plan
variables {
agent_id = "test-agent-boundary"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config = <<-EOT
allow:
- "*.anthropic.com"
- "*.github.com"
EOT
}
override_data {
target = data.coder_workspace.me
values = {
access_url = "https://coder.example.com"
}
}
assert {
condition = var.enable_boundary == true
error_message = "Boundary should be enabled"
}
assert {
condition = var.boundary_config != null
error_message = "Boundary config should be set"
}
assert {
condition = local.coder_host == "coder.example.com"
error_message = "Coder host should be 'coder.example.com' after stripping https:// from access URL"
}
assert {
condition = local.boundary_config_b64 != ""
error_message = "Boundary config should be base64-encoded for the start script"
}
assert {
condition = base64decode(local.boundary_config_b64) == var.boundary_config
error_message = "Base64-encoded boundary config should decode back to the original config"
}
}
run "test_claude_code_with_boundary_config_path" {
command = plan
variables {
agent_id = "test-agent-boundary-path"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config_path = "/home/coder/.config/coder_boundary/config.yaml"
}
assert {
condition = var.enable_boundary == true
error_message = "Boundary should be enabled"
}
assert {
condition = var.boundary_config_path == "/home/coder/.config/coder_boundary/config.yaml"
error_message = "Boundary config path should be set correctly"
}
}
run "test_claude_code_auto_permission_mode" {
command = plan
@ -216,11 +284,112 @@ run "test_claude_code_with_boundary" {
condition = var.enable_boundary == true
error_message = "Boundary should be enabled"
}
assert {
condition = local.coder_host != ""
error_message = "Coder host should be extracted from access URL"
}
run "test_boundary_both_configs_fails" {
command = plan
variables {
agent_id = "test-agent-boundary-both"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config = "allow:\n - '*.example.com'"
boundary_config_path = "/home/coder/.config/coder_boundary/config.yaml"
}
expect_failures = [
var.enable_boundary,
]
}
run "test_boundary_config_without_boundary_fails" {
command = plan
variables {
agent_id = "test-agent-no-boundary"
workdir = "/home/coder/boundary-test"
enable_boundary = false
boundary_config = "allow:\n - '*.example.com'"
}
expect_failures = [
var.enable_boundary,
]
}
run "test_boundary_config_path_without_boundary_fails" {
command = plan
variables {
agent_id = "test-agent-no-boundary-path"
workdir = "/home/coder/boundary-test"
enable_boundary = false
boundary_config_path = "/home/coder/.config/coder_boundary/config.yaml"
}
expect_failures = [
var.enable_boundary,
]
}
run "test_boundary_empty_config_fails" {
command = plan
variables {
agent_id = "test-agent-empty-config"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config = ""
}
expect_failures = [
var.boundary_config,
]
}
run "test_boundary_empty_config_path_fails" {
command = plan
variables {
agent_id = "test-agent-empty-config-path"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config_path = ""
}
expect_failures = [
var.boundary_config_path,
]
}
run "test_boundary_whitespace_config_fails" {
command = plan
variables {
agent_id = "test-agent-whitespace-config"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config = " "
}
expect_failures = [
var.boundary_config,
]
}
run "test_boundary_whitespace_config_path_fails" {
command = plan
variables {
agent_id = "test-agent-whitespace-config-path"
workdir = "/home/coder/boundary-test"
enable_boundary = true
boundary_config_path = " "
}
expect_failures = [
var.boundary_config_path,
]
}
run "test_claude_code_system_prompt" {

View File

@ -24,6 +24,10 @@ ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"latest"}
ARG_COMPILE_FROM_SOURCE=${ARG_COMPILE_FROM_SOURCE:-false}
ARG_USE_BOUNDARY_DIRECTLY=${ARG_USE_BOUNDARY_DIRECTLY:-false}
ARG_CODER_HOST=${ARG_CODER_HOST:-}
ARG_BOUNDARY_CONFIG=${ARG_BOUNDARY_CONFIG:-}
ARG_BOUNDARY_CONFIG_PATH=${ARG_BOUNDARY_CONFIG_PATH:-}
ARG_BOUNDARY_CONFIG_PATH="${ARG_BOUNDARY_CONFIG_PATH/#\~/$HOME}"
ARG_BOUNDARY_CONFIG_PATH="${ARG_BOUNDARY_CONFIG_PATH//\$HOME/$HOME}"
echo "--------------------------------"
@ -223,6 +227,38 @@ function start_agentapi() {
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
if [ "$ARG_ENABLE_BOUNDARY" = "true" ]; then
BOUNDARY_CONFIG_DIR="$HOME/.config/coder_boundary"
BOUNDARY_CONFIG_FILE="$BOUNDARY_CONFIG_DIR/config.yaml"
if [ -n "$ARG_BOUNDARY_CONFIG" ]; then
printf "Writing inline boundary config to %s\n" "$BOUNDARY_CONFIG_FILE"
mkdir -p "$BOUNDARY_CONFIG_DIR"
echo -n "$ARG_BOUNDARY_CONFIG" | base64 -d > "$BOUNDARY_CONFIG_FILE"
if [ ! -s "$BOUNDARY_CONFIG_FILE" ]; then
printf "Error: boundary configuration file '%s' does not exist or is empty after writing inline config.\n" "$BOUNDARY_CONFIG_FILE" >&2
exit 1
fi
elif [ -n "$ARG_BOUNDARY_CONFIG_PATH" ]; then
printf "Linking boundary config from %s to %s\n" "$ARG_BOUNDARY_CONFIG_PATH" "$BOUNDARY_CONFIG_FILE"
if [ ! -f "$ARG_BOUNDARY_CONFIG_PATH" ]; then
printf "Error: boundary_config_path '%s' does not exist or is not a regular file. Ensure the file exists at this path before starting.\n" "$ARG_BOUNDARY_CONFIG_PATH" >&2
exit 1
fi
if [ "$ARG_BOUNDARY_CONFIG_PATH" != "$BOUNDARY_CONFIG_FILE" ]; then
mkdir -p "$BOUNDARY_CONFIG_DIR"
ln -sf "$ARG_BOUNDARY_CONFIG_PATH" "$BOUNDARY_CONFIG_FILE"
fi
if [ ! -s "$BOUNDARY_CONFIG_FILE" ]; then
printf "Error: boundary configuration file '%s' does not exist or is empty. Check that '%s' exists and is not empty.\n" "$BOUNDARY_CONFIG_FILE" "$ARG_BOUNDARY_CONFIG_PATH" >&2
exit 1
fi
fi
if [ ! -s "$BOUNDARY_CONFIG_FILE" ]; then
printf "Error: boundary configuration file '%s' does not exist or is empty. Check ARG_BOUNDARY_CONFIG/ARG_BOUNDARY_CONFIG_PATH.\n" "$BOUNDARY_CONFIG_FILE" >&2
exit 1
fi
install_boundary
printf "Starting with coder boundary enabled\n"