Skip to content

Commit 7e75d5d

Browse files
authored
feat: add AI Bridge Proxy support to copilot module (#725)
## Description Add AI Bridge Proxy support to the copilot module. When enabled, the module configures proxy environment variables (`HTTPS_PROXY`, `NODE_EXTRA_CA_CERTS`) scoped to the copilot process tree (agentapi and copilot), routing Copilot traffic through AI Bridge Proxy without affecting other workspace traffic. GitHub authentication is still required, the proxy authenticates with AI Bridge using the Coder session token but does not replace GitHub authentication. Note: Uses [coder exp sync](https://coder.com/docs/admin/templates/startup-coordination) for startup coordination, ensuring the copilot module waits for the `aibridge-proxy` setup to complete before starting. ## Type of Change - [ ] New module - [ ] New template - [ ] Bug fix - [x] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information **Path:** `registry/coder-labs/modules/copilot` **New version:** `v0.4.0` **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun fmt`) - [x] Changes tested locally ## Related Issues Depends on: #721 Related to: coder/internal#1187
1 parent b6c2998 commit 7e75d5d

4 files changed

Lines changed: 231 additions & 7 deletions

File tree

registry/coder-labs/modules/copilot/README.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ display_name: Copilot CLI
33
description: GitHub Copilot CLI agent for AI-powered terminal assistance
44
icon: ../../../../.icons/github.svg
55
verified: false
6-
tags: [agent, copilot, ai, github, tasks]
6+
tags: [agent, copilot, ai, github, tasks, aibridge]
77
---
88

99
# Copilot
@@ -13,7 +13,7 @@ Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-c
1313
```tf
1414
module "copilot" {
1515
source = "registry.coder.com/coder-labs/copilot/coder"
16-
version = "0.3.0"
16+
version = "0.4.0"
1717
agent_id = coder_agent.example.id
1818
workdir = "/home/coder/projects"
1919
}
@@ -51,7 +51,7 @@ data "coder_parameter" "ai_prompt" {
5151
5252
module "copilot" {
5353
source = "registry.coder.com/coder-labs/copilot/coder"
54-
version = "0.3.0"
54+
version = "0.4.0"
5555
agent_id = coder_agent.example.id
5656
workdir = "/home/coder/projects"
5757
@@ -71,7 +71,7 @@ Customize tool permissions, MCP servers, and Copilot settings:
7171
```tf
7272
module "copilot" {
7373
source = "registry.coder.com/coder-labs/copilot/coder"
74-
version = "0.3.0"
74+
version = "0.4.0"
7575
agent_id = coder_agent.example.id
7676
workdir = "/home/coder/projects"
7777
@@ -142,7 +142,7 @@ variable "github_token" {
142142
143143
module "copilot" {
144144
source = "registry.coder.com/coder-labs/copilot/coder"
145-
version = "0.3.0"
145+
version = "0.4.0"
146146
agent_id = coder_agent.example.id
147147
workdir = "/home/coder/projects"
148148
github_token = var.github_token
@@ -156,14 +156,47 @@ Run Copilot as a command-line tool without task reporting or web interface. This
156156
```tf
157157
module "copilot" {
158158
source = "registry.coder.com/coder-labs/copilot/coder"
159-
version = "0.3.0"
159+
version = "0.4.0"
160160
agent_id = coder_agent.example.id
161161
workdir = "/home/coder"
162162
report_tasks = false
163163
cli_app = true
164164
}
165165
```
166166

167+
### Usage with AI Bridge Proxy
168+
169+
[AI Bridge Proxy](https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy) routes Copilot traffic through [AI Bridge](https://coder.com/docs/ai-coder/ai-bridge) for centralized LLM management and governance.
170+
The proxy environment variables are scoped to the Copilot process only and do not affect other workspace traffic.
171+
172+
```tf
173+
module "aibridge-proxy" {
174+
source = "registry.coder.com/coder/aibridge-proxy/coder"
175+
version = "1.0.0"
176+
agent_id = coder_agent.main.id
177+
proxy_url = "https://aiproxy.example.com"
178+
}
179+
180+
module "copilot" {
181+
source = "registry.coder.com/coder-labs/copilot/coder"
182+
version = "0.4.0"
183+
agent_id = coder_agent.main.id
184+
workdir = "/home/coder/projects"
185+
enable_aibridge_proxy = true
186+
aibridge_proxy_auth_url = module.aibridge-proxy.proxy_auth_url
187+
aibridge_proxy_cert_path = module.aibridge-proxy.cert_path
188+
}
189+
```
190+
191+
> [!NOTE]
192+
> AI Bridge Proxy is a Premium Coder feature that requires [AI Governance Add-On](https://coder.com/docs/ai-coder/ai-governance).
193+
> See the [AI Bridge Proxy setup guide](https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy/setup) for details on configuring the proxy on your Coder deployment.
194+
> GitHub authentication is still required for Copilot as the proxy authenticates with AI Bridge using the Coder session token, but does not replace GitHub authentication.
195+
196+
> [!IMPORTANT]
197+
> When using AI Bridge Proxy, enable [startup coordination](https://coder.com/docs/admin/templates/startup-coordination) by setting `CODER_AGENT_SOCKET_SERVER_ENABLED=true` in the workspace container environment.
198+
> This ensures the Copilot module waits for the `aibridge-proxy` module to complete before starting. Without it, the Copilot start script may fail if the AI Bridge Proxy setup has not completed in time.
199+
167200
## Authentication
168201

169202
The module supports multiple authentication methods (in priority order):

registry/coder-labs/modules/copilot/copilot.tftest.hcl

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,116 @@ run "app_slug_is_consistent" {
234234
error_message = "module_dir_name should be '.copilot-module'"
235235
}
236236
}
237+
238+
run "aibridge_proxy_defaults" {
239+
command = plan
240+
241+
variables {
242+
agent_id = "test-agent"
243+
workdir = "/home/coder"
244+
}
245+
246+
assert {
247+
condition = var.enable_aibridge_proxy == false
248+
error_message = "enable_aibridge_proxy should default to false"
249+
}
250+
251+
assert {
252+
condition = var.aibridge_proxy_auth_url == null
253+
error_message = "aibridge_proxy_auth_url should default to null"
254+
}
255+
256+
assert {
257+
condition = var.aibridge_proxy_cert_path == null
258+
error_message = "aibridge_proxy_cert_path should default to null"
259+
}
260+
}
261+
262+
run "aibridge_proxy_enabled" {
263+
command = plan
264+
265+
variables {
266+
agent_id = "test-agent-aibridge-proxy"
267+
workdir = "/home/coder"
268+
enable_aibridge_proxy = true
269+
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
270+
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
271+
}
272+
273+
assert {
274+
condition = var.enable_aibridge_proxy == true
275+
error_message = "AI Bridge Proxy should be enabled"
276+
}
277+
278+
assert {
279+
condition = var.aibridge_proxy_auth_url == "https://coder:mock-token@aiproxy.example.com"
280+
error_message = "AI Bridge Proxy auth URL should match the input variable"
281+
}
282+
283+
assert {
284+
condition = var.aibridge_proxy_cert_path == "/tmp/aibridge-proxy/ca-cert.pem"
285+
error_message = "AI Bridge Proxy cert path should match the input variable"
286+
}
287+
}
288+
289+
run "aibridge_proxy_validation_missing_proxy_auth_url" {
290+
command = plan
291+
292+
variables {
293+
agent_id = "test-agent-validation"
294+
workdir = "/home/coder"
295+
enable_aibridge_proxy = true
296+
aibridge_proxy_auth_url = ""
297+
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
298+
}
299+
300+
expect_failures = [
301+
var.enable_aibridge_proxy,
302+
]
303+
}
304+
305+
run "aibridge_proxy_validation_missing_cert_path" {
306+
command = plan
307+
308+
variables {
309+
agent_id = "test-agent-validation"
310+
workdir = "/home/coder"
311+
enable_aibridge_proxy = true
312+
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
313+
aibridge_proxy_cert_path = ""
314+
}
315+
316+
expect_failures = [
317+
var.enable_aibridge_proxy,
318+
]
319+
}
320+
321+
run "aibridge_proxy_with_copilot_config" {
322+
command = plan
323+
324+
variables {
325+
agent_id = "test-agent"
326+
workdir = "/home/coder"
327+
copilot_model = "gpt-5"
328+
github_token = "ghp_test123"
329+
allow_all_tools = true
330+
enable_aibridge_proxy = true
331+
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
332+
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
333+
}
334+
335+
assert {
336+
condition = var.enable_aibridge_proxy == true
337+
error_message = "AI Bridge Proxy should be enabled"
338+
}
339+
340+
assert {
341+
condition = length(resource.coder_env.github_token) == 1
342+
error_message = "github_token environment variable should be set alongside proxy"
343+
}
344+
345+
assert {
346+
condition = length(resource.coder_env.copilot_model) == 1
347+
error_message = "copilot_model environment variable should be set alongside proxy"
348+
}
349+
}

registry/coder-labs/modules/copilot/main.tf

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
terraform {
2-
required_version = ">= 1.0"
2+
required_version = ">= 1.9"
33
required_providers {
44
coder = {
55
source = "coder/coder"
@@ -173,6 +173,35 @@ variable "post_install_script" {
173173
default = null
174174
}
175175

176+
variable "enable_aibridge_proxy" {
177+
type = bool
178+
description = "Route Copilot traffic through AI Bridge Proxy. See https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy"
179+
default = false
180+
181+
validation {
182+
condition = !var.enable_aibridge_proxy || (var.aibridge_proxy_auth_url != null && length(var.aibridge_proxy_auth_url) > 0)
183+
error_message = "aibridge_proxy_auth_url is required when enable_aibridge_proxy is true."
184+
}
185+
186+
validation {
187+
condition = !var.enable_aibridge_proxy || (var.aibridge_proxy_cert_path != null && length(var.aibridge_proxy_cert_path) > 0)
188+
error_message = "aibridge_proxy_cert_path is required when enable_aibridge_proxy is true."
189+
}
190+
}
191+
192+
variable "aibridge_proxy_auth_url" {
193+
type = string
194+
description = "AI Bridge Proxy URL with authentication. Use the proxy_auth_url output from the aibridge-proxy module."
195+
default = null
196+
sensitive = true
197+
}
198+
199+
variable "aibridge_proxy_cert_path" {
200+
type = string
201+
description = "Path to the AI Bridge Proxy CA certificate. Use the cert_path output from the aibridge-proxy module."
202+
default = null
203+
}
204+
176205
data "coder_workspace" "me" {}
177206
data "coder_workspace_owner" "me" {}
178207

@@ -279,6 +308,9 @@ module "agentapi" {
279308
ARG_TRUSTED_DIRECTORIES='${join(",", var.trusted_directories)}' \
280309
ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \
281310
ARG_RESUME_SESSION='${var.resume_session}' \
311+
ARG_ENABLE_AIBRIDGE_PROXY='${var.enable_aibridge_proxy}' \
312+
ARG_AIBRIDGE_PROXY_AUTH_URL='${var.aibridge_proxy_auth_url != null ? var.aibridge_proxy_auth_url : ""}' \
313+
ARG_AIBRIDGE_PROXY_CERT_PATH='${var.aibridge_proxy_cert_path != null ? var.aibridge_proxy_cert_path : ""}' \
282314
/tmp/start.sh
283315
EOT
284316

registry/coder-labs/modules/copilot/scripts/start.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ ARG_DENY_TOOLS=${ARG_DENY_TOOLS:-}
2222
ARG_TRUSTED_DIRECTORIES=${ARG_TRUSTED_DIRECTORIES:-}
2323
ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github}
2424
ARG_RESUME_SESSION=${ARG_RESUME_SESSION:-true}
25+
ARG_ENABLE_AIBRIDGE_PROXY=${ARG_ENABLE_AIBRIDGE_PROXY:-false}
26+
ARG_AIBRIDGE_PROXY_AUTH_URL=${ARG_AIBRIDGE_PROXY_AUTH_URL:-}
27+
ARG_AIBRIDGE_PROXY_CERT_PATH=${ARG_AIBRIDGE_PROXY_CERT_PATH:-}
2528

2629
validate_copilot_installation() {
2730
if ! command_exists copilot; then
@@ -118,6 +121,48 @@ setup_github_authentication() {
118121
return 0
119122
}
120123

124+
setup_aibridge_proxy() {
125+
if [ "$ARG_ENABLE_AIBRIDGE_PROXY" != "true" ]; then
126+
return 0
127+
fi
128+
129+
echo "Setting up AI Bridge Proxy..."
130+
131+
# Wait for the aibridge-proxy module to finish.
132+
# Uses startup coordination to block until aibridge-proxy-setup signals completion.
133+
if command -v coder > /dev/null 2>&1; then
134+
coder exp sync want "copilot-aibridge" "aibridge-proxy-setup" > /dev/null 2>&1 || true
135+
coder exp sync start "copilot-aibridge" > /dev/null 2>&1 || true
136+
trap 'coder exp sync complete "copilot-aibridge" > /dev/null 2>&1 || true' EXIT
137+
fi
138+
139+
if [ -z "$ARG_AIBRIDGE_PROXY_AUTH_URL" ]; then
140+
echo "ERROR: AI Bridge Proxy is enabled but no proxy auth URL provided."
141+
exit 1
142+
fi
143+
144+
if [ -z "$ARG_AIBRIDGE_PROXY_CERT_PATH" ]; then
145+
echo "ERROR: AI Bridge Proxy is enabled but no certificate path provided."
146+
exit 1
147+
fi
148+
149+
if [ ! -f "$ARG_AIBRIDGE_PROXY_CERT_PATH" ]; then
150+
echo "ERROR: AI Bridge Proxy certificate not found at $ARG_AIBRIDGE_PROXY_CERT_PATH."
151+
echo " Ensure the aibridge-proxy module has successfully completed setup."
152+
exit 1
153+
fi
154+
155+
# Set proxy environment variables scoped to this process tree only.
156+
# These are inherited by the agentapi/copilot process below,
157+
# but do not affect other workspace processes, avoiding routing
158+
# unnecessary traffic through the proxy.
159+
export HTTPS_PROXY="$ARG_AIBRIDGE_PROXY_AUTH_URL"
160+
export NODE_EXTRA_CA_CERTS="$ARG_AIBRIDGE_PROXY_CERT_PATH"
161+
162+
echo "✓ AI Bridge Proxy configured"
163+
echo " CA certificate: $ARG_AIBRIDGE_PROXY_CERT_PATH"
164+
}
165+
121166
start_agentapi() {
122167
echo "Starting in directory: $ARG_WORKDIR"
123168
cd "$ARG_WORKDIR"
@@ -157,5 +202,6 @@ start_agentapi() {
157202
}
158203

159204
setup_github_authentication
205+
setup_aibridge_proxy
160206
validate_copilot_installation
161207
start_agentapi

0 commit comments

Comments
 (0)