- test-util.ts: find coder_script by run_on_start to pick the
coder-utils install script instead of the shutdown script
- main.test.ts: mock coder CLI for coder exp sync calls
- README.md: rewrite in the style of claude-code and codex READMEs
with feature list, examples, and troubleshooting section
Replace the inline coder_script resource with coder-utils module and
convert scripts/main.sh to scripts/install.sh.tftpl, matching the
pattern used by coder/claude-code and coder-labs/codex.
Changes:
- Replace coder_script.agentapi with module "coder_utils" (v0.0.1)
- Convert scripts/main.sh to scripts/install.sh.tftpl using
templatefile() for variable injection instead of ARG_* env vars
- Keep coder_script.agentapi_shutdown as-is (coder-utils does not
support run_on_stop)
- Keep coder_app resources (web and CLI) unchanged
- Add output "scripts" for downstream sync dependencies
- Simplify agentapi.tftest.hcl assertions (install script content is
now base64-encoded inside the coder-utils wrapper)
- Replace module_dir_name with module_directory in example snippet.
- Remove deleted variables (pre_install_script, post_install_script,
start_script, install_script) from example.
- Update state persistence docs to reference module_directory.
- Replace all stale $module_path references in main.sh with
${MODULE_DIRECTORY}.
- Update main.test.ts: moduleDirName → moduleDirectory, pass
module_directory instead of module_dir_name, fix shutdown test
to use ARG_MODULE_DIRECTORY.
- Remove start_script from agentapi.tftest.hcl (deleted variable).
- Rename module_path → module_directory in testdata/agentapi-start.sh.
The module_directory default contains $HOME which needs shell
expansion at runtime. Single quotes prevented expansion, causing
'No such file or directory' when sourcing the lib script.
- Remove PRE_INSTALL_SCRIPT, INSTALL_SCRIPT, START_SCRIPT,
POST_INSTALL_SCRIPT from main.sh (variables removed from main.tf
but still read under set -o nounset).
- Remove pre-install, install, and post-install execution blocks
from main.sh. Consumer modules are now responsible for placing
the start script at the expected path.
- Remove pre-post-install-scripts test (tests removed functionality).
- Write test start script directly to container instead of passing
via Terraform variable.
- Pass ARG_LIB_SCRIPT_PATH in shutdown tests (required after lib
sourcing was made configurable).
The coder_script resources write scripts to module_directory
(default $HOME/.coder-modules/coder/agentapi) but never create
the directory first, causing 'No such file or directory' in every
test. Also fix stale chmod targets that still referenced /tmp/.
Fixes#835
## Problem
The `data "http"` resource always fires for every selected IDE, even
when the user has pinned versions via `ide_config`. In air-gapped or
caching scenarios, this causes:
- **30-second hangs** when `releases_base_link` is set to a dummy URL
like `https://localhost`
- **Fatal errors** with `https://localhost:1` (connection refused)
- The documented "air-gapped fallback" via `try()` never actually worked
— the `http` data source fails before `try()` can catch anything
## Fix
When `ide_config` is provided, the module now skips all HTTP calls and
uses the pinned build numbers directly.
| Scenario | `ide_config` | HTTP calls | Build source | On API failure |
|---|---|---|---|---|
| User wants latest | `null` (default) | Yes | JetBrains API | Terraform
error (fail loudly) |
| User pins versions | Set | **None** | `ide_config.build` | N/A |
### Changes
- `ide_config` default changed from a full map to `null`
- `name` and `icon` are now `optional(string)` in `ide_config` — falls
back to built-in metadata
- `data.http.jetbrains_ide_versions` `for_each` is empty when
`ide_config` is set
- Static `ide_metadata` local provides name/icon when `ide_config` is
null
- Removed `try()` fallback from `parsed_responses` — API errors are now
explicit instead of silently using stale builds
- Cross-variable validation rejects `major_version`, `channel`, and
`releases_base_link` when `ide_config` is set
- Validation for `ide_config ⊇ default` added (previously only
`ide_config ⊇ options` was checked)
- Version bumped `1.3.1` → `1.4.0`
### Usage
```tf
module "jetbrains" {
source = "registry.coder.com/coder/jetbrains/coder"
version = "1.4.0"
agent_id = coder_agent.main.id
folder = "/home/coder/project"
# Zero HTTP calls — only build is required.
ide_config = {
"GO" = { build = "261.22158.291" }
"PY" = { build = "261.22158.340" }
}
options = ["GO", "PY"]
}
```
> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
## Summary
- Add `auto` as a valid `permission_mode` for the claude-code module,
passing `--enable-auto-mode` to the CLI when selected
- Fix bypass permissions TOS prompt appearing interactively by
pre-seeding `bypassPermissionsModeAccepted` in `~/.claude.json` during
install (workaround for
https://github.com/anthropics/claude-code/issues/25503)
- Bump version `4.8.2` → `4.9.0`
## Test plan
- [x] All 19 terraform tests pass (`terraform test -verbose`)
- [x] Added `test_claude_code_auto_permission_mode` tftest
- [x] Added `claude-auto-permission-mode` TypeScript test verifying both
`--permission-mode auto` and `--enable-auto-mode` are passed
- [ ] Container test with auto mode (requires Linux/Colima)
- [ ] Verify bypass permissions TOS prompt no longer appears on task
startup
🤖 Generated with Claude Code using Claude Opus 4.6
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: DevCats <christofer@coder.com>
The `copilot_model` variable was restricted to a hardcoded enum of three
models (`claude-sonnet-4`, `claude-sonnet-4.5`, `gpt-5`). Models change
fast and this validation was blocking users from using newer models.
## Changes
- Remove `validation` block from `copilot_model` variable in `main.tf`
- Update variable description to indicate any Copilot-supported model
can be used
- Replace enum validation test with a test that verifies arbitrary model
strings are accepted
- Bump module version to `0.4.1` in README examples
Closes#832
> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
Follow-up to #764.
Now that the `agentapi` module `v2.4.0` is published with `web_app`
support, this PR completes the wiring:
## Changes
### `claude-code/main.tf`
- Bump agentapi dependency from `v2.3.0` → `v2.4.0`
- Replace `# TODO: pass web_app = var.web_app once agentapi module is
published with web_app support` with `web_app = var.web_app`
### `claude-code/README.md`
- Bump version references from `4.9.0` → `4.9.1`
## Result
Setting `web_app = false` on the `claude-code` module now correctly
passes through to the `agentapi` module, hiding the web UI app icon from
the Coder dashboard while still running AgentAPI. The task-safe behavior
(auto-enabling for `coder_ai_task`) is handled by the `agentapi` module.
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Adds a `web_app` variable (default: `true`) to both the `claude-code`
and `agentapi` modules. When set to `false`, AgentAPI still runs but the
web UI app icon is not shown in the Coder dashboard.
This mirrors the existing `cli_app` toggle pattern.
## Changes
### `agentapi` module
- New `web_app` variable (bool, default `true`)
- `coder_app.agentapi_web` now has `count = local.web_app ? 1 : 0`
- **Task-safe:** `local.web_app` is computed as `var.web_app ||
local.is_task`, where `is_task = try(data.coder_task.me.enabled,
false)`. This means the web app is always created when the workspace is
a Task, regardless of the `web_app` variable.
- `task_app_id` output returns `""` when `local.web_app` is `false`
### `claude-code` module
- New `web_app` variable (bool, default `true`)
- `TODO` comment to wire `web_app` through to agentapi once published
## Usage (once fully wired)
```hcl
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
...
web_app = false # hides the Claude Code web UI from the dashboard
}
```
Setting `web_app = false` is safe even in templates that use
`coder_ai_task` — the module detects Tasks via
`data.coder_task.me.enabled` and automatically enables the web app.
## Merge strategy
This needs to land in two steps:
1. **Merge this PR** — publishes the agentapi module with `web_app`
support, and adds the `web_app` variable to claude-code (not yet wired
through)
2. **Follow-up PR** — bump the agentapi version in claude-code and
replace the `TODO` with `web_app = var.web_app`
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: DevCats <christofer@coder.com>
## New Template: docker-rstudio
Adds a Docker-based template for R development workspaces.
### What it provides
| Tool | Source | Access |
|------|--------|--------|
| **RStudio Server** | Pre-installed in `rocker/rstudio` image | Browser
via Coder proxy (subdomain) |
| **code-server** | `registry.coder.com/coder/code-server/coder` module
| Browser via Coder proxy |
| **RMarkdown** | Installed on first start, persisted in home-dir R
library | Available in both RStudio and code-server |
### Design decisions
<details>
<summary>Click to expand</summary>
- **`rocker/rstudio` as the base image** instead of
`codercom/enterprise-base:ubuntu` + the `rstudio-server` module. The
module runs RStudio inside a nested Docker container which requires
Docker-in-Docker or socket mounting in the workspace. Using the rocker
image directly avoids that complexity and starts faster since R and
RStudio are already installed.
- **Direct `coder_app` for RStudio** rather than the registry
`rstudio-server` module, because the module is designed for Docker-based
provisioning (it pulls and runs a rocker container). Since the workspace
itself _is_ the rocker container, RStudio Server is started natively via
`rserver`.
- **RMarkdown installed idempotently** — the startup script checks
`require('rmarkdown')` before installing. Since R libraries default to a
subdirectory under `/home/rstudio` (the persistent volume), packages
survive workspace restarts.
- **Persistent volume mounted at `/home/rstudio`** to match the default
user in the rocker image.
- **`--auth-none=1`** disables RStudio authentication since the Coder
proxy handles access control.
</details>
### Files added
- `registry/coder/templates/docker-rstudio/main.tf`
- `registry/coder/templates/docker-rstudio/README.md`
### Validation
- `go run ./cmd/readmevalidation/` — passes (32 templates detected)
- `terraform fmt` — clean
- `bun run fmt` — all files unchanged
---------
Co-authored-by: DevCats <christofer@coder.com>
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Fixes `coder_script "1password"` → `coder_script "onepassword"` since
Terraform resource names cannot start with a digit. Adds a demo
screenshot showing the template variables page and `op whoami` working
in a workspace. Bumps version to 1.0.2.
The 1Password icon was black on transparent, making it invisible on the
registry's dark cards. Replaced with 1Password brand blue (`#0572EC`)
circle + white keyhole.
Adds a 1Password module under the `bpmct` namespace.
## What it does
Installs the [1Password CLI](https://developer.1password.com/docs/cli/)
(`op`) into Coder workspaces at startup. Two auth paths:
- **Service account token** — set `service_account_token` and
`OP_SERVICE_ACCOUNT_TOKEN` is injected automatically. Fully headless.
- **Personal account** — set `account_address`, `account_email`,
`account_secret_key` to pre-register the account. User runs `op signin`
in their terminal.
Optionally installs the [1Password VS Code
extension](https://marketplace.visualstudio.com/items?itemName=1Password.op-vscode)
(`1Password.op-vscode`) for code-server and VS Code with
`install_vscode_extension = true`.
Supports `pre_install_script` and `post_install_script` for custom
orchestration.
## What's included
- `registry/bpmct/` — new namespace (Ben Potter, community)
- `registry/bpmct/modules/1password/` — the module (`main.tf`, `run.sh`,
`README.md`)
- `.icons/1password.svg` — 1Password logo from Simple Icons
## Tested
Spun up a dev Coder instance, pushed the template with a real 1Password
service account token, created a workspace, and confirmed:
- `op` CLI installs and authenticates
- `op vault list` returns vaults
- `1Password.op-vscode` extension installs in code-server
---------
Co-authored-by: DevCats <christofer@coder.com>
## Description
Change `agent-helper` to `coder-utils`
The current tag for agent-helper needs to be deleted before this PR is
merged.
## Type of Change
- [x] New module - kinda..
- [ ] New template
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [x] Other
## Module Information
<!-- Delete this section if not applicable -->
**Path:** `registry/coder/modules/coder-utils`
**New version:** `v1.0.0`
**Breaking change:** [X] Yes [ ] No ( Module name is changing, but this
is not nested in any modules yet )
## Testing & Validation
- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [x] Changes tested locally
## Related
https://github.com/coder/registry/pull/802
PR #822 bumped the jetbrains module version from `1.3.0` to `1.4.0`
(minor), but the change was a bugfix and should have been a patch bump.
This corrects all 7 version references in the README from `1.4.0` to
`1.3.1`.
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
## Problem
The `data "http" "jetbrains_ide_versions"` resource fetches release info
from `data.services.jetbrains.com` for **all configured IDE options** at
plan time, regardless of what the user actually selected. When the API
is unreachable (air-gapped environments, DNS failures, transient
outages), this causes a fatal Terraform error that blocks the workspace
build — even when no JetBrains IDEs were selected.
## Fix
Changed the `for_each` on the HTTP data source (and all dependent
locals) from iterating over `var.options`/`var.default` to
`local.selected_ides` — the user's actual selection.
| Scenario | Before | After |
|---|---|---|
| No IDEs selected (`[]`) | 9 HTTP requests | 0 HTTP requests |
| 1 IDE selected (`["GO"]`) | 9 HTTP requests | 1 HTTP request |
| All IDEs selected | 9 HTTP requests | 9 HTTP requests |
## Validation
- All 17 existing `terraform test` cases pass
- Tested end-to-end on [dev.coder.com](https://dev.coder.com) with
Docker template:
- `jetbrains_ides=[]` — zero HTTP requests, build succeeds
- `jetbrains_ides=["GO"]` — single HTTP request for GoLand only,
`coder_app.jetbrains["GO"]` created
Closes#821
> 🤖 This PR was created with the help of Coder Agents, and needs a human
review. 🧑💻
## Summary
- Fix version pinning bug in the OpenCode install script
(`registry/coder-labs/modules/opencode/scripts/install.sh`, line 42)
**Bug:** The install command was:
```bash
VERSION=$ARG_OPENCODE_VERSION curl -fsSL https://opencode.ai/install | bash
```
`VERSION` was set as an environment variable prefix to `curl` (the left
side of the pipe), so the `bash` process on the right side of the pipe
never received it. In a shell pipeline, each command runs in its own
subprocess, so env var prefixes only apply to the immediately following
command. This caused the installer script to always install the latest
version instead of the pinned version specified by the user.
**Fix:** Move `VERSION` to prefix `bash` instead of `curl`:
```bash
curl -fsSL https://opencode.ai/install | VERSION=$ARG_OPENCODE_VERSION bash
```
Now the `VERSION` variable is correctly available to the install script
executed by `bash`.
## Test plan
- [x] Set `opencode_version` to a specific version (e.g., `0.1.0`) and
verify that version is installed instead of latest
- [x] Set `opencode_version` to `latest` and verify the latest version
is still installed (this code path is unchanged)
- [x] Verify `opencode --version` output matches the requested version
after install
---------
Co-authored-by: 35C4n0r <70096901+35C4n0r@users.noreply.github.com>
## Description
- This lead to a bug where if the folder name is in the form `a.b.c`:
- we check for:
`-home-coder-ai.coder.com/cd32e253-ca16-4fd3-9825-d837e74ae3c2.jsonl`
- But the actual file path for claude-session is:
`-home-coder-ai-coder-com/cd32e253-ca16-4fd3-9825-d837e74ae3c2.jsonl`
- The above bug might also occur in the case of `a_b_c`
- update workdir normalization to handle dot in path
## Type of Change
- [ ] New module
- [ ] New template
- [x] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other
## Module Information
<!-- Delete this section if not applicable -->
**Path:** `registry/coder/modules/claude-code`
**New version:** `v4.8.1`
**Breaking change:** [ ] Yes [x] No
## Testing & Validation
- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [x] Changes tested locally
## Related Issues
<!-- Link related issues or write "None" if not applicable -->
## Summary
- add optional mux auto-restarts with delay, lock cleanup, and
restart-attempt caps
- restart mux after any exit when enabled, including intentional exits
and signals
- require `max_restart_attempts` to be a non-negative whole number and
update docs/tests for the new restart semantics
## Validation
- `bash -n registry/coder/modules/mux/run.sh`
- `cd registry/coder/modules/mux && terraform validate`
- `cd registry/coder/modules/mux && terraform test -verbose`
- `cd registry/coder/modules/mux && bun test main.test.ts`
Generated with OpenAI using Mux
Supersedes #551 (fork branch couldn't be rebased due to GitHub App
permission limitations).
Original author: @willshu
## Description
Adds support for specifying a git branch when cloning dotfiles
repositories.
### Changes
- Introduces `dotfiles_branch` and `default_dotfiles_branch` Terraform
variables
- Adds a `coder_parameter` for `dotfiles_branch` when not explicitly set
(with `order` matching `dotfiles_uri`)
- Conditionally passes the `--branch` flag to `coder dotfiles` only when
branch is non-empty
- Adds validation to prevent empty string for `dotfiles_branch` (use
`null` to prompt the user)
- Default branch is empty string — defers to the repo's default branch
rather than assuming `main`, matching the behavior of `coder dotfiles
--branch` which states: *"If empty, will default to cloning the default
branch or using the existing branch in the cloned repo on disk."*
- Adds test coverage for custom branch setting and parameter creation
### Review feedback addressed (from Copilot on #551)
- Added `order` field to `dotfiles_branch` parameter for UI consistency
with `dotfiles_uri`
- Conditional echo message — only shows branch info when set
- `--branch` flag only passed when `DOTFILES_BRANCH` is non-empty (both
current-user and sudo paths)
- Added validation block on `var.dotfiles_branch` to reject empty
strings
## Type of Change
- [x] Feature/enhancement
## Module Information
**Path:** `registry/coder/modules/dotfiles`
## Testing & Validation
- [ ] Tests pass (`bun test`)
- [ ] Code formatted (`bun fmt`)
- [ ] Changes tested locally
Co-authored-by: William Shu <william.shu@kkr.com>
Co-authored-by: DevCats <christofer@coder.com>
## Description
Adds boundary support to the Codex module by passing boundary
variables through to the agentapi module and using
AGENTAPI_BOUNDARY_PREFIX in the start script.
Depends on #780
## Type of Change
- [x] Feature/enhancement
## Module Information
**Path:** `registry/coder-labs/modules/codex`
**Breaking change:** No
---------
Co-authored-by: Shane White <shane.white@cloudsecure.ltd>
Co-authored-by: 35C4n0r <70096901+35C4n0r@users.noreply.github.com>
## Description
Enable any agent module to run its AI agent inside Coder's Agent
Boundaries.
The agentapi module handles boundary installation, config setup, and
wrapper
script creation, then exports AGENTAPI_BOUNDARY_PREFIX for consuming
modules
to use in their start scripts.
Supports three boundary installation modes:
- coder boundary subcommand (default, Coder v2.30+)
- Standalone binary via install script (use_boundary_directly)
- Compiled from source (compile_boundary_from_source)
Users must provide a boundary config.yaml with their allowlist and
settings when enabling boundary.
Closes#457
## Type of Change
- [x] Feature/enhancement
## Module Information
**Path:** `registry/coder/modules/agentapi`
**Breaking change:** No
## Testing & Validation
- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [x] Changes tested locally
---------
Co-authored-by: Shane White <shane.white@cloudsecure.ltd>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: 35C4n0r <70096901+35C4n0r@users.noreply.github.com>
## Summary
Keep the Mux module's launcher around after startup so it can append
useful diagnostics when `mux server` is killed outside the Node runtime.
## Background
The module previously forked `mux server` and returned immediately,
which meant external kills (for example `SIGKILL` or an OOM kill) could
leave users with only a stopped app and no launcher-side clue about what
happened.
## Implementation
- keep the existing module inputs and startup shape intact
- launch `mux server` under a detached Bash watcher that waits for the
child process to exit
- append signal/exit-code diagnostics to `log_path` when the server dies
unexpectedly
- include a best-effort kernel OOM/SIGKILL hint in the log when the host
exposes it
- add Terraform and Bun tests that cover the new launcher diagnostics
- bump the module examples from `1.3.1` to `1.4.0`
## Validation
- `bun x prettier --check registry/coder/modules/mux/README.md
registry/coder/modules/mux/main.test.ts
registry/coder/modules/mux/mux.tftest.hcl
registry/coder/modules/mux/run.sh`
- `terraform fmt -check -recursive registry/coder/modules/mux`
- `cd registry/coder/modules/mux && terraform validate`
- `cd registry/coder/modules/mux && terraform test -verbose`
- `cd registry/coder/modules/mux && bun test main.test.ts`
- `bun run shellcheck -- registry/coder/modules/mux/run.sh`
---
Generated with mux (exec mode) using openai:gpt-5.4.
This PR adds a new Terraform module that fetches JFrog Xray
vulnerability scanning results for container images stored in
Artifactory.
## Features
- Fetches vulnerability scan results from JFrog Xray
- Outputs vulnerability counts (Critical, High, Medium, Low, Total)
- Supports flexible image path formats
- Works with any workspace type using container images
- Provides secure token handling
## Design Decisions
During testing, we found two issues with the original approach of
defining the `xray` provider and `coder_metadata` inside the module:
1. **`coder_metadata` defined inside modules does not display in the
Coder dashboard** — this is a known limitation
2. **Inline provider blocks prevent using `count`/`for_each` on the
module** — which is needed when attaching metadata to resources like
`docker_container` that use `start_count`
The module now **outputs** vulnerability counts instead, and the caller
creates the `coder_metadata` and configures the `xray` provider in their
root template. This matches the pattern used by other registry modules.
## Usage
```hcl
provider "xray" {
url = "${var.jfrog_url}/xray"
access_token = var.artifactory_access_token
skip_xray_version_check = true
}
module "jfrog_xray" {
source = "registry.coder.com/coder/jfrog-xray/coder"
version = "1.0.0"
xray_url = "${var.jfrog_url}/xray"
xray_token = var.artifactory_access_token
image = "docker-local/codercom/enterprise-base:latest"
}
resource "coder_metadata" "xray_vulnerabilities" {
count = data.coder_workspace.me.start_count
resource_id = docker_container.workspace[0].id
icon = "/icon/shield.svg"
item {
key = "Total Vulnerabilities"
value = module.jfrog_xray.total
}
item {
key = "Critical"
value = module.jfrog_xray.critical
}
item {
key = "High"
value = module.jfrog_xray.high
}
item {
key = "Medium"
value = module.jfrog_xray.medium
}
item {
key = "Low"
value = module.jfrog_xray.low
}
}
```
## Related Issues
- Resolvescoder/coder#12838
- Addresses coder/registry#65
Tested with a JFrog Cloud trial instance using Docker remote repository
and Xray scanning.
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: matifali <10648092+matifali@users.noreply.github.com>
Co-authored-by: DevelopmentCats <christofer@coder.com>
## 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: https://github.com/coder/internal/issues/1187
## Description
Add `aibridge-proxy` module that configures workspaces to use AI Bridge Proxy. Downloads the proxy's CA certificate and exposes `proxy_auth_url` and `cert_path` outputs for tool-specific modules to configure the proxy scoped to their process. The module does not set proxy environment variables globally in the workspace.
## Type of Change
- [x] New module
- [ ] New template
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other
## Module Information
<!-- Delete this section if not applicable -->
**Path:** `registry/coder/modules/aibridge-proxy`
**New version:** `v1.0.0`
**Breaking change:** [ ] Yes [x] No
## Testing & Validation
- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [x] Changes tested locally
## Related Issues
Closes: https://github.com/coder/internal/issues/1187
## Summary
Clarifies that the existing `pre_install_script` variable can be used to
handle dependencies between modules during workspace startup.
## Problem
When using multiple startup modules (e.g., git-clone and claude-code),
there's a race condition where scripts execute in parallel. Module
dependencies need to be managed, such as ensuring git-clone completes
before Claude Code tries to access a workdir.
## Solution
The existing `pre_install_script` variable already provides this
capability. Updated documentation to clarify this use case.
## Example
```hcl
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
workdir = "/path/to/repo"
# Wait for git-clone to complete before starting
pre_install_script = <<-EOT
#!/bin/bash
set -e
while [ ! -f /tmp/.git-clone-complete ]; do
sleep 1
done
EOT
}
```
Resolves issue #609.
Co-authored-by: Jason Barnett <Jason.Barnett@altana.ai>
Co-authored-by: DevCats <christofer@coder.com>
This pull request enhances the VS Code Web module by improving how
machine settings are handled and merged, updating documentation to
clarify the settings behavior, and adding robust automated tests for the
new functionality. The most significant changes are grouped below.
**Machine Settings Handling and Merging:**
* Introduced a new `merge_settings` function in `run.sh` that merges
provided settings with any existing machine settings using `jq` or
`python3` if available, falling back gracefully if neither is present.
Settings are now passed as base64-encoded JSON to avoid quoting issues.
[[1]](diffhunk://#diff-c6d09ac3d801a2417c0e3cf8c2cd0f093ba2cf245bad8c213f70115c75276323R7-R54)
[[2]](diffhunk://#diff-c6d09ac3d801a2417c0e3cf8c2cd0f093ba2cf245bad8c213f70115c75276323L31-R76)
[[3]](diffhunk://#diff-0c7f0791e2c2556eb4ed7666ac44534ea3ff5c7f652e01716e5d7b5c31180d92L180-R184)
[[4]](diffhunk://#diff-0c7f0791e2c2556eb4ed7666ac44534ea3ff5c7f652e01716e5d7b5c31180d92R170-R173)
* Updated the `settings` variable in `main.tf` to clarify that it
applies to VS Code Web's Machine settings and will be merged with any
existing settings on startup.
**Documentation Improvements:**
* Updated the README to clarify that settings are merged with existing
machine settings, not simply overwritten, and added a note about the
requirements (`jq` or `python3`) and limitations regarding persistence
of user settings.
[[1]](diffhunk://#diff-24e2e305e46a08f8a30243bdc916241586e4561d97861b4397b14e871f9f085dL54-R56)
[[2]](diffhunk://#diff-24e2e305e46a08f8a30243bdc916241586e4561d97861b4397b14e871f9f085dR72-R73)
**Automated Testing:**
* Expanded `main.test.ts` to include integration tests that verify
settings file creation and merging behavior inside a container, as well
as improved error handling for invalid configuration combinations.
These changes collectively make machine settings management more robust,
user-friendly, and well-documented.
feat(coder/modules/claude-code): add enable_state_persistence variable
Expose the agentapi module's state persistence toggle so users can
control conversation state persistence across workspace restarts.
Enabled by default, set `enable_state_persistence = false` to disable.
Also bumps agentapi dependency from 2.0.0 to 2.2.0 and claude-code
to 4.8.0.
Refs coder/internal#1258
AgentAPI can now save and restore conversation state across workspace
restarts. The module exports env vars (AGENTAPI_STATE_FILE,
AGENTAPI_SAVE_STATE, AGENTAPI_LOAD_STATE, AGENTAPI_PID_FILE) that the
binary reads directly. No consumer module changes needed.
New variables: enable_state_persistence (default false),
state_file_path, pid_file_path. State and PID files default to
$HOME/<module_dir_name>/.
Requires agentapi >= v0.12.0. A shared version_at_least function in
lib.sh gates the env var exports and SIGUSR1 in the shutdown script.
Old binaries get a warning and graceful skip.
Shutdown script now does SIGUSR1 (state save), log snapshot capture
(existing, now fault-tolerant via subshell), then SIGTERM with wait.
Closes coder/internal#1257
Refs coder/internal#1256
Refs #696
Some Git providers (e.g. on-prem GitLab) disable HTTPS cloning by
default, which causes the dotfiles clone to silently fail during
workspace startup. Users see "Startup scripts are still running" but the
dotfiles folder is never populated.
This PR adds two small documentation touches:
1. **`main.tf` default description** — appends a one-liner suggesting
SSH URLs when HTTPS is restricted. This is what users see in the Coder
UI parameter prompt.
2. **`README.md`** — new "SSH vs HTTPS URLs" section with an example and
a brief explanation of why SSH URLs are more reliable during startup.
No logic changes, no new variables — just documentation.
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: DevCats <christofer@coder.com>
## Summary
Terraform variable names should use underscores (`snake_case`), not
hyphens. Hyphens are technically valid in HCL but are [deprecated and
non-idiomatic](https://developer.hashicorp.com/terraform/language/values/variables).
This PR adds a variable name check into the existing
`terraform_validate.sh` script so it runs as part of the existing "Run
Terraform Validate" CI step — no new scripts or workflow changes needed.
## Changes
### `scripts/terraform_validate.sh` — added `validate_variable_names()`
- Scans `.tf` files in changed modules for `variable` declarations with
hyphens
- Fails with actionable fix suggestions (shows the snake_case
alternative)
- Runs after `terraform validate` in the same CI step
### Fix: `code-server` module — rename `machine-settings` →
`machine_settings`
- Renames the hyphenated variable and its reference in main.tf
- Bumps version `1.4.2` → `1.4.3`
- Updates all README examples
---
Created on behalf of @matifali
---------
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: DevCats <christofer@coder.com>
Terraform variable names should use underscores, not hyphens. Renames
the `add-project` variable to `add_project` in the mux module.
**Changes:**
- `main.tf`: Renamed variable declaration and references
- `README.md`: Updated example usage
Bumped version: 1.3.0 → 1.3.1
---
Generated with [Mux](https://mux.coder.com) using Claude