Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
db4cbed
feat(coder/modules/claude-code): add support for aibridge
35C4n0r Jan 13, 2026
11203a4
chore: bump version
35C4n0r Jan 13, 2026
2efe178
fix: use precondition instead of check
35C4n0r Jan 14, 2026
4963d31
fix: refactor and bun fmt
35C4n0r Jan 14, 2026
3328805
add validation block
35C4n0r Jan 14, 2026
4aad195
feat: add ARG_ENABLE_AIBRIDGE support to installation script and conf…
35C4n0r Jan 15, 2026
0ed44f4
feat: restore tests
35C4n0r Jan 15, 2026
3dbed9a
Merge branch 'main' into 35C4n0r/feat-claude-aibridge
35C4n0r Jan 15, 2026
3dfd968
bun fmt
35C4n0r Jan 15, 2026
2e76f43
bun fmt
35C4n0r Jan 15, 2026
a59a423
bun fmt
35C4n0r Jan 15, 2026
28fc496
bun fmt
35C4n0r Jan 15, 2026
8a4b5e3
bump version
35C4n0r Jan 15, 2026
f40c16d
feat: update validation error message
35C4n0r Jan 15, 2026
64a790c
chore: update README.md
35C4n0r Jan 16, 2026
a9d3304
Merge branch 'main' into 35C4n0r/feat-claude-aibridge
35C4n0r Jan 16, 2026
d6e77ae
chore: rearrange README.md
35C4n0r Jan 17, 2026
7829d11
Update registry/coder/modules/claude-code/README.md
35C4n0r Jan 19, 2026
85a83f1
chore: test
35C4n0r Jan 19, 2026
40fc90d
chore: test
35C4n0r Jan 19, 2026
2d3302b
Merge remote-tracking branch 'origin/35C4n0r/feat-claude-aibridge' in…
35C4n0r Jan 19, 2026
08bebf1
Apply suggestion from @Copilot
35C4n0r Jan 19, 2026
c5f8ffc
Apply suggestion from @Copilot
35C4n0r Jan 19, 2026
c8c01c8
feat: update Claude Code authentication to support AI Bridge integration
35C4n0r Jan 19, 2026
dce4cff
Update registry/coder/modules/claude-code/main.tf
35C4n0r Jan 19, 2026
495fdd1
Update registry/coder/modules/claude-code/main.tf
35C4n0r Jan 19, 2026
a866b16
feat: add terraform validation tests
35C4n0r Jan 19, 2026
adbc719
Merge remote-tracking branch 'origin/35C4n0r/feat-claude-aibridge' in…
35C4n0r Jan 19, 2026
2471c2f
wip
35C4n0r Jan 19, 2026
a22618a
wip
35C4n0r Jan 19, 2026
d86dca0
wip
35C4n0r Jan 19, 2026
87e04fd
wip
35C4n0r Jan 19, 2026
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
83 changes: 62 additions & 21 deletions registry/coder/modules/claude-code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ display_name: Claude Code
description: Run the Claude Code agent in your workspace.
icon: ../../../../.icons/claude.svg
verified: true
tags: [agent, claude-code, ai, tasks, anthropic]
tags: [agent, claude-code, ai, tasks, anthropic, aibridge]
---

# Claude Code
Expand All @@ -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.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
Expand Down Expand Up @@ -45,33 +45,76 @@ This example shows how to configure the Claude Code module to run the agent behi
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_boundary = true
boundary_version = "v0.5.1"
}
```

### Usage with Tasks and Advanced Configuration
### Usage with AI Bridge

This example shows how to configure the Claude Code module with an AI prompt, API key shared by all users of the template, and other custom settings.
[AI Bridge](https://coder.com/docs/ai-coder/ai-bridge) is a Premium Coder feature that provides centralized LLM proxy management. To use AI Bridge, set `enable_aibridge = true`.

> [!NOTE]
> When a specific `claude_code_version` (other than "latest") is provided, the module will install Claude Code via npm instead of the official installer. This allows for version pinning. The `claude_binary_path` variable can be used to specify where a pre-installed Claude binary is located.
For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage with Tasks](#usage-with-tasks) example below.

#### Standalone usage with AI Bridge

```tf
data "coder_parameter" "ai_prompt" {
type = "string"
name = "AI Prompt"
default = ""
description = "Initial task prompt for Claude Code."
mutable = true
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_aibridge = true
}
```

When `enable_aibridge = true`, the module automatically sets:

- `ANTHROPIC_BASE_URL` to `${data.coder_workspace.me.access_url}/api/v2/aibridge/anthropic`
- `CLAUDE_API_KEY` to the workspace owner's session token

This allows Claude Code to route API requests through Coder's AI Bridge instead of directly to Anthropic's API.
Template build will fail if either `claude_api_key` or `claude_code_oauth_token` is provided alongside `enable_aibridge = true`.

### Usage with Tasks

This example shows how to configure Claude Code with Coder tasks.

```tf
resource "coder_ai_task" "task" {
count = data.coder_workspace.me.start_count
app_id = module.claude-code.task_app_id
}

data "coder_task" "me" {}

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
ai_prompt = data.coder_task.me.prompt

# Optional: route through AI Bridge (Premium feature)
# enable_aibridge = true
}
```

### Advanced Configuration

This example shows additional configuration options for version pinning, custom models, and MCP servers.

> [!NOTE]
> When a specific `claude_code_version` (other than "latest") is provided, the module will install Claude Code via npm instead of the official installer. This allows for version pinning. The `claude_binary_path` variable can be used to specify where a pre-installed Claude binary is located.

```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"

Expand All @@ -83,9 +126,7 @@ module "claude-code" {
claude_binary_path = "/opt/claude/bin" # Path to pre-installed Claude binary
agentapi_version = "0.11.4"

ai_prompt = data.coder_parameter.ai_prompt.value
model = "sonnet"

model = "sonnet"
permission_mode = "plan"

mcp = <<-EOF
Expand All @@ -108,7 +149,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.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
install_claude_code = true
Expand All @@ -130,7 +171,7 @@ variable "claude_code_oauth_token" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token
Expand Down Expand Up @@ -203,7 +244,7 @@ resource "coder_env" "bedrock_api_key" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
Expand Down Expand Up @@ -260,7 +301,7 @@ resource "coder_env" "google_application_credentials" {

module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.4.2"
version = "4.5.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514"
Expand Down
27 changes: 25 additions & 2 deletions registry/coder/modules/claude-code/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ variable "compile_boundary_from_source" {
default = false
}

variable "enable_aibridge" {
type = bool
description = "Use AI Bridge for Claude Code. https://coder.com/docs/ai-coder/ai-bridge"
default = false

validation {
condition = !(var.enable_aibridge && length(var.claude_api_key) > 0)
error_message = "claude_api_key cannot be provided when enable_aibridge is true. AI Bridge automatically authenticates the client using Coder credentials."
}

validation {
condition = !(var.enable_aibridge && length(var.claude_code_oauth_token) > 0)
error_message = "claude_code_oauth_token cannot be provided when enable_aibridge is true. AI Bridge automatically authenticates the client using Coder credentials."
}
}

resource "coder_env" "claude_code_md_path" {
count = var.claude_md_path == "" ? 0 : 1
agent_id = var.agent_id
Expand All @@ -248,10 +264,9 @@ resource "coder_env" "claude_code_oauth_token" {
}

resource "coder_env" "claude_api_key" {
count = length(var.claude_api_key) > 0 ? 1 : 0
agent_id = var.agent_id
name = "CLAUDE_API_KEY"
value = var.claude_api_key
value = var.enable_aibridge ? data.coder_workspace_owner.me.session_token : var.claude_api_key
}

resource "coder_env" "disable_autoupdater" {
Expand Down Expand Up @@ -281,6 +296,13 @@ resource "coder_env" "anthropic_model" {
value = var.model
}

resource "coder_env" "anthropic_base_url" {
count = var.enable_aibridge ? 1 : 0
agent_id = var.agent_id
name = "ANTHROPIC_BASE_URL"
value = "${data.coder_workspace.me.access_url}/api/v2/aibridge/anthropic"
}

locals {
# we have to trim the slash because otherwise coder exp mcp will
# set up an invalid claude config
Expand Down Expand Up @@ -382,6 +404,7 @@ module "agentapi" {
ARG_ALLOWED_TOOLS='${var.allowed_tools}' \
ARG_DISALLOWED_TOOLS='${var.disallowed_tools}' \
ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
/tmp/install.sh
EOT
}
Expand Down
93 changes: 92 additions & 1 deletion registry/coder/modules/claude-code/main.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ run "test_claude_code_with_api_key" {
}

assert {
condition = coder_env.claude_api_key[0].value == "test-api-key-123"
condition = coder_env.claude_api_key.value == "test-api-key-123"
error_message = "Claude API key value should match the input"
}
}
Expand Down Expand Up @@ -288,3 +288,94 @@ run "test_claude_report_tasks_disabled" {
error_message = "System prompt should end with </system>"
}
}

run "test_aibridge_enabled" {
command = plan

variables {
agent_id = "test-agent-aibridge"
workdir = "/home/coder/aibridge"
enable_aibridge = true
}

assert {
condition = var.enable_aibridge == true
error_message = "AI Bridge should be enabled"
}

assert {
condition = coder_env.anthropic_base_url[0].name == "ANTHROPIC_BASE_URL"
error_message = "ANTHROPIC_BASE_URL environment variable should be set"
}

assert {
condition = length(regexall("/api/v2/aibridge/anthropic", coder_env.anthropic_base_url[0].value)) > 0
error_message = "ANTHROPIC_BASE_URL should point to AI Bridge endpoint"
}

assert {
condition = coder_env.claude_api_key.name == "CLAUDE_API_KEY"
error_message = "CLAUDE_API_KEY environment variable should be set"
}

assert {
condition = coder_env.claude_api_key.value == data.coder_workspace_owner.me.session_token
error_message = "CLAUDE_API_KEY should use workspace owner's session token when aibridge is enabled"
}
}

run "test_aibridge_validation_with_api_key" {
command = plan

variables {
agent_id = "test-agent-validation"
workdir = "/home/coder/test"
enable_aibridge = true
claude_api_key = "test-api-key"
}

expect_failures = [
var.enable_aibridge,
]
}

run "test_aibridge_validation_with_oauth_token" {
command = plan

variables {
agent_id = "test-agent-validation"
workdir = "/home/coder/test"
enable_aibridge = true
claude_code_oauth_token = "test-oauth-token"
}

expect_failures = [
var.enable_aibridge,
]
}

run "test_aibridge_disabled_with_api_key" {
command = plan

variables {
agent_id = "test-agent-no-aibridge"
workdir = "/home/coder/test"
enable_aibridge = false
claude_api_key = "test-api-key-xyz"
}

assert {
condition = var.enable_aibridge == false
error_message = "AI Bridge should be disabled"
}

assert {
condition = coder_env.claude_api_key.value == "test-api-key-xyz"
error_message = "CLAUDE_API_KEY should use the provided API key when aibridge is disabled"
}

assert {
condition = length(coder_env.anthropic_base_url) == 0
error_message = "ANTHROPIC_BASE_URL should not be set when aibridge is disabled"
}
}
9 changes: 5 additions & 4 deletions registry/coder/modules/claude-code/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d)
ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}

echo "--------------------------------"

Expand All @@ -31,6 +32,7 @@ printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG"
printf "ARG_MCP: %s\n" "$ARG_MCP"
printf "ARG_ALLOWED_TOOLS: %s\n" "$ARG_ALLOWED_TOOLS"
printf "ARG_DISALLOWED_TOOLS: %s\n" "$ARG_DISALLOWED_TOOLS"
printf "ARG_ENABLE_AIBRIDGE: %s\n" "$ARG_ENABLE_AIBRIDGE"

echo "--------------------------------"

Expand Down Expand Up @@ -133,8 +135,8 @@ function setup_claude_configurations() {
function configure_standalone_mode() {
echo "Configuring Claude Code for standalone mode..."

if [ -z "${CLAUDE_API_KEY:-}" ]; then
echo "Note: CLAUDE_API_KEY not set, skipping authentication setup"
if [ -z "${CLAUDE_API_KEY:-}" ] && [ "$ARG_ENABLE_AIBRIDGE" = "false" ]; then
echo "Note: Neither claude_api_key nor enable_aibridge is set, skipping authentication setup"
return
fi

Expand All @@ -147,8 +149,7 @@ function configure_standalone_mode() {
if [ -f "$claude_config" ]; then
echo "Updating existing Claude configuration at $claude_config"

jq --arg apikey "${CLAUDE_API_KEY:-}" \
--arg workdir "$ARG_WORKDIR" \
jq --arg workdir "$ARG_WORKDIR" --arg apikey "${CLAUDE_API_KEY:-}" \
'.autoUpdaterStatus = "disabled" |
.bypassPermissionsModeAccepted = true |
.hasAcknowledgedCostThreshold = true |
Expand Down