Closes #878 ## What Major refactor of the `coder-labs/codex` module to mirror the `coder/claude-code` v5 changes from #861. ## Changes ### Structural - Replace `module "agentapi"` with `module "coder_utils"` (`registry.coder.com/coder/coder-utils/coder v0.0.1`) - Replace `scripts/install.sh` with `scripts/install.sh.tftpl` (Terraform templatefile) - Delete `scripts/start.sh` - Module dir changed from `.codex-module` to `.coder-modules/coder-labs/codex` - Output changed from `task_app_id` to `scripts` (ordered list of coder exp sync names) - Extracted shared test helpers (`collectScripts`, `runScripts`) into `agentapi/coder-utils-test-helpers.ts` ### Removed variables All AgentAPI pass-throughs, boundary, and start-script-only variables: `order`, `group`, `report_tasks`, `subdomain`, `cli_app`, `web_app_display_name`, `cli_app_display_name`, `install_agentapi`, `agentapi_version`, `ai_prompt`, `continue`, `enable_state_persistence`, `codex_system_prompt`, `enable_boundary`, `boundary_config_path`, `boundary_version`, `compile_boundary_from_source`, `use_boundary_directly`, `codex_model` ### Retained `install_codex` (toggle for skipping npm install when CLI is pre-installed) ### Renamed - `enable_aibridge` -> `enable_ai_gateway` ### Changed - `workdir`: now optional (`default = null`) - `openai_api_key`: conditional env var with `count`, marked `sensitive = true` - `base_config_toml`: heredoc description documenting generated defaults; notes that `model_reasoning_effort` and workdir trust are only applied in default config - Default `config.toml`: stripped `sandbox_mode`, `approval_policy`, `sandbox_workspace_write`, `notice.model_migrations` - Install script: removed Node.js/NVM bootstrap (assumes npm pre-installed), sources NVM if present, fails with actionable error if npm missing - `ARG_CODEX_VERSION` and `ARG_WORKDIR` base64-encoded to prevent shell/TOML injection - Duplicate `[model_providers.aibridge]` guarded with grep before appending - Debug header uses user-facing variable names ### Tests - Terraform: 11 pass - Bun: 15 pass (rewritten to shared `collectScripts`/`runScripts` pattern) - Added: `model-reasoning-effort-standalone`, `ai-gateway-with-custom-base-config`, `ai-gateway-custom-config-no-duplicate-provider`, `install-codex-latest`, `workdir-trusted-project`, `no-workdir-no-project-section` - Negative assertions on `minimal-default-config` ### Docs - Migration guide (v4 to v5) in README - Quoted path in coder_app example - AI Gateway note about custom `base_config_toml` requiring manual `model_provider` > [!WARNING] > Breaking change. Drops support for Coder Tasks and Boundary. Keep using v4.x.x if you depend on them. --- *This PR was authored by Coder Agents.* --------- Co-authored-by: Jay Kumar <jay.kumar@coder.com> Co-authored-by: DevCats <christofer@coder.com>
169 lines
5.7 KiB
HCL
169 lines
5.7 KiB
HCL
terraform {
|
|
required_version = ">= 1.9"
|
|
|
|
required_providers {
|
|
coder = {
|
|
source = "coder/coder"
|
|
version = ">= 2.12"
|
|
}
|
|
}
|
|
}
|
|
|
|
variable "agent_id" {
|
|
type = string
|
|
description = "The ID of a Coder agent."
|
|
}
|
|
|
|
data "coder_workspace" "me" {}
|
|
|
|
data "coder_workspace_owner" "me" {}
|
|
|
|
variable "icon" {
|
|
type = string
|
|
description = "The icon to use for the app."
|
|
default = "/icon/openai.svg"
|
|
}
|
|
|
|
variable "workdir" {
|
|
type = string
|
|
description = "Optional project directory. When set, the module pre-creates it if missing and adds it as a trusted project in Codex config.toml."
|
|
default = null
|
|
}
|
|
|
|
variable "pre_install_script" {
|
|
type = string
|
|
description = "Custom script to run before installing Codex."
|
|
default = null
|
|
}
|
|
|
|
variable "post_install_script" {
|
|
type = string
|
|
description = "Custom script to run after installing Codex."
|
|
default = null
|
|
}
|
|
|
|
variable "install_codex" {
|
|
type = bool
|
|
description = "Whether to install Codex."
|
|
default = true
|
|
}
|
|
|
|
variable "codex_version" {
|
|
type = string
|
|
description = "The version of Codex to install. Empty string installs the latest available version."
|
|
default = ""
|
|
}
|
|
|
|
variable "openai_api_key" {
|
|
type = string
|
|
description = "OpenAI API key for Codex CLI."
|
|
sensitive = true
|
|
default = ""
|
|
}
|
|
|
|
variable "base_config_toml" {
|
|
type = string
|
|
description = <<-EOT
|
|
Complete base TOML configuration for Codex (without mcp_servers section).
|
|
When empty, the module generates a minimal default:
|
|
|
|
preferred_auth_method = "apikey"
|
|
# model_provider = "aigateway" (sets the default profile, when enable_ai_gateway = true)
|
|
# model_reasoning_effort = "<value>" (sets the reasoning effort, when model_reasoning_effort is set)
|
|
|
|
[projects."<workdir>"] (when workdir is set)
|
|
trust_level = "trusted"
|
|
|
|
When non-empty, the value is written verbatim as the base of config.toml;
|
|
additional_mcp_servers and AI Gateway sections are still appended after it.
|
|
Note: model_reasoning_effort and workdir trust are only applied in the
|
|
default config. Include them in your custom config if needed.
|
|
EOT
|
|
default = ""
|
|
}
|
|
|
|
variable "additional_mcp_servers" {
|
|
type = string
|
|
description = "Additional MCP servers configuration in TOML format."
|
|
default = ""
|
|
}
|
|
|
|
variable "model_reasoning_effort" {
|
|
type = string
|
|
description = "The reasoning effort for the model. One of: none, minimal, low, medium, high, xhigh. See https://platform.openai.com/docs/guides/latest-model#lower-reasoning-effort"
|
|
default = ""
|
|
validation {
|
|
condition = contains(["", "none", "minimal", "low", "medium", "high", "xhigh"], var.model_reasoning_effort)
|
|
error_message = "model_reasoning_effort must be one of: none, minimal, low, medium, high, xhigh."
|
|
}
|
|
}
|
|
|
|
variable "enable_ai_gateway" {
|
|
type = bool
|
|
description = "Use AI Gateway for Codex. https://coder.com/docs/ai-coder/ai-gateway"
|
|
default = false
|
|
|
|
validation {
|
|
condition = !(var.enable_ai_gateway && length(var.openai_api_key) > 0)
|
|
error_message = "openai_api_key cannot be provided when enable_ai_gateway is true. AI Gateway automatically authenticates the client using Coder credentials."
|
|
}
|
|
}
|
|
|
|
resource "coder_env" "openai_api_key" {
|
|
count = var.openai_api_key != "" ? 1 : 0
|
|
agent_id = var.agent_id
|
|
name = "OPENAI_API_KEY"
|
|
value = var.openai_api_key
|
|
}
|
|
|
|
# Authenticates the client against Coder's AI Gateway using the workspace
|
|
# owner's session token. Referenced by config.toml model_providers.aigateway.
|
|
resource "coder_env" "ai_gateway_session_token" {
|
|
count = var.enable_ai_gateway ? 1 : 0
|
|
agent_id = var.agent_id
|
|
name = "CODER_AIBRIDGE_SESSION_TOKEN"
|
|
value = data.coder_workspace_owner.me.session_token
|
|
}
|
|
|
|
locals {
|
|
workdir = var.workdir != null ? trimsuffix(var.workdir, "/") : ""
|
|
aibridge_config = <<-EOF
|
|
[model_providers.aigateway]
|
|
name = "AI Gateway"
|
|
base_url = "${data.coder_workspace.me.access_url}/api/v2/aibridge/openai/v1"
|
|
env_key = "CODER_AIBRIDGE_SESSION_TOKEN"
|
|
wire_api = "responses"
|
|
|
|
EOF
|
|
install_script = templatefile("${path.module}/scripts/install.sh.tftpl", {
|
|
ARG_INSTALL = tostring(var.install_codex)
|
|
ARG_CODEX_VERSION = var.codex_version != "" ? base64encode(var.codex_version) : ""
|
|
ARG_WORKDIR = local.workdir != "" ? base64encode(local.workdir) : ""
|
|
ARG_BASE_CONFIG_TOML = var.base_config_toml != "" ? base64encode(var.base_config_toml) : ""
|
|
ARG_ADDITIONAL_MCP_SERVERS = var.additional_mcp_servers != "" ? base64encode(var.additional_mcp_servers) : ""
|
|
ARG_ENABLE_AI_GATEWAY = tostring(var.enable_ai_gateway)
|
|
ARG_AIBRIDGE_CONFIG = var.enable_ai_gateway ? base64encode(local.aibridge_config) : ""
|
|
ARG_MODEL_REASONING_EFFORT = var.model_reasoning_effort
|
|
ARG_OPENAI_API_KEY = var.openai_api_key != "" ? base64encode(var.openai_api_key) : ""
|
|
})
|
|
module_dir_name = ".coder-modules/coder-labs/codex"
|
|
}
|
|
|
|
module "coder_utils" {
|
|
source = "registry.coder.com/coder/coder-utils/coder"
|
|
version = "0.0.1"
|
|
|
|
agent_id = var.agent_id
|
|
module_directory = "$HOME/${local.module_dir_name}"
|
|
display_name_prefix = "Codex"
|
|
icon = var.icon
|
|
pre_install_script = var.pre_install_script
|
|
post_install_script = var.post_install_script
|
|
install_script = local.install_script
|
|
}
|
|
|
|
output "scripts" {
|
|
description = "Ordered list of coder exp sync names for the coder_script resources this module creates, in run order (pre_install, install, post_install). Scripts that were not configured are absent from the list."
|
|
value = module.coder_utils.scripts
|
|
}
|