diff --git a/.github/workflows/dogfood.yaml b/.github/workflows/dogfood.yaml index cf164e9770..7031f95b3d 100644 --- a/.github/workflows/dogfood.yaml +++ b/.github/workflows/dogfood.yaml @@ -223,6 +223,7 @@ jobs: CODER_SESSION_TOKEN: ${{ secrets.CODER_SESSION_TOKEN }} # Template source & details TF_VAR_CODER_DOGFOOD_ANTHROPIC_API_KEY: ${{ secrets.CODER_DOGFOOD_ANTHROPIC_API_KEY }} + TF_VAR_CODER_DOGFOOD_OPENAI_API_KEY: ${{ secrets.CODER_DOGFOOD_OPENAI_API_KEY }} TF_VAR_CODER_TEMPLATE_NAME: ${{ secrets.CODER_TEMPLATE_NAME }} TF_VAR_CODER_TEMPLATE_VERSION: ${{ steps.vars.outputs.sha_short }} TF_VAR_CODER_TEMPLATE_DIR: ./coder diff --git a/dogfood/coder/main.tf b/dogfood/coder/main.tf index dbc0fb531c..16467795ed 100644 --- a/dogfood/coder/main.tf +++ b/dogfood/coder/main.tf @@ -240,17 +240,17 @@ data "coder_parameter" "devcontainer_autostart" { mutable = true } -data "coder_parameter" "use_ai_bridge" { +data "coder_parameter" "enable_ai_gateway" { type = "bool" - name = "Use AI Bridge" + name = "Use AI Gateway" default = true - description = "If enabled, AI requests will be sent via AI Bridge." + description = "If enabled, AI requests will be sent via AI Gateway." mutable = true } -# Only used if AI Bridge is disabled. +# Only used if AI Gateway is disabled. # dogfood/main.tf injects this value from a GH Actions secret; -# `coderd_template.dogfood` passes the value injected by .github/workflows/dogfood.yaml in `TF_VAR_CODER_DOGFOOD_ANTHROPIC_API_KEY`. +# `coderd_template.dogfood` passes the value injected by .github/workflows/dogfood.yaml in `TF_VAR_CODER_DOGFOOD_ANTHROPIC_API_KEY` and `TF_VAR_CODER_DOGFOOD_OPENAI_API_KEY`. variable "anthropic_api_key" { type = string description = "The API key used to authenticate with the Anthropic API, if AI Bridge is disabled." @@ -258,6 +258,13 @@ variable "anthropic_api_key" { sensitive = true } +variable "openai_api_key" { + type = string + description = "The API key used to authenticate with the OpenAI API, if AI Gateway is disabled." + default = "" + sensitive = true +} + provider "docker" { host = lookup(local.docker_host, data.coder_parameter.region.value) } @@ -503,7 +510,7 @@ resource "coder_agent" "dev" { OIDC_TOKEN : data.coder_workspace_owner.me.oidc_access_token, CODER_AGENT_EXP_MCP_CONFIG_FILES : "~/.mcp.json,.mcp.json", }, - data.coder_parameter.use_ai_bridge.value ? { + data.coder_parameter.enable_ai_gateway.value ? { ANTHROPIC_BASE_URL : "https://dev.coder.com/api/v2/aibridge/anthropic", ANTHROPIC_AUTH_TOKEN : data.coder_workspace_owner.me.session_token, OPENAI_BASE_URL : "https://dev.coder.com/api/v2/aibridge/openai/v1", @@ -891,36 +898,6 @@ resource "coder_metadata" "container_info" { } } -locals { - claude_system_prompt = <<-EOT - -- Framing -- - You are a helpful coding assistant. Aim to autonomously investigate - and solve issues the user gives you and test your work, whenever possible. - - Avoid shortcuts like mocking tests. When you get stuck, you can ask the user - but opt for autonomy. - - -- Tool Selection -- - - playwright: previewing your changes after you made them - to confirm it worked as expected - - Built-in tools - use for everything else: - (file operations, git commands, builds & installs, one-off shell commands) - - -- Workflow -- - When starting new work: - 1. If given a GitHub issue URL, use the `gh` CLI to read the full issue details with `gh issue view `. - 2. Create a feature branch for the work using a descriptive name based on the issue or task. - Example: `git checkout -b fix/issue-123-oauth-error` or `git checkout -b feat/add-dark-mode` - 3. Proceed with implementation following the CLAUDE.md guidelines. - - -- Context -- - There is an existing application in the current directory. - Be sure to read CLAUDE.md before making any changes. - - This is a real-world production application. As such, make sure to think carefully, use TODO lists, and plan carefully before making changes. - EOT -} - resource "coder_script" "boundary_config_setup" { agent_id = coder_agent.dev.id display_name = "Boundary Setup Configuration" @@ -939,76 +916,64 @@ resource "coder_script" "boundary_config_setup" { } module "claude-code" { - count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0 - source = "dev.registry.coder.com/coder/claude-code/coder" - version = "4.9.2" - enable_boundary = true - agent_id = coder_agent.dev.id - workdir = local.repo_dir - claude_code_version = "latest" - model = "opus" - order = 999 - claude_api_key = data.coder_parameter.use_ai_bridge.value ? data.coder_workspace_owner.me.session_token : var.anthropic_api_key - agentapi_version = "latest" + count = data.coder_workspace.me.start_count + source = "dev.registry.coder.com/coder/claude-code/coder" + version = "5.1.0" + enable_ai_gateway = data.coder_parameter.enable_ai_gateway.value + anthropic_api_key = data.coder_parameter.enable_ai_gateway.value ? "" : var.anthropic_api_key + agent_id = coder_agent.dev.id + workdir = local.repo_dir + mcp = <<-EOF + { + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["--", "@playwright/mcp@latest", "--headless", "--isolated", "--no-sandbox"] + } + } + } + EOF +} - system_prompt = local.claude_system_prompt - ai_prompt = data.coder_task.me.prompt - post_install_script = <<-EOT - cd $HOME/coder - claude mcp add playwright npx -- @playwright/mcp@latest --headless --isolated --no-sandbox +resource "coder_app" "claude" { + agent_id = coder_agent.dev.id + slug = "claude" + display_name = "Claude Code" + icon = "/icon/claude.svg" + open_in = "slim-window" + command = <<-EOT + #!/bin/bash + set -e + cd "${local.repo_dir}" + exec tmux new-session -A -s claude claude EOT } -resource "coder_ai_task" "task" { - count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0 - app_id = module.claude-code[count.index].task_app_id -} - -resource "coder_app" "develop_sh" { - count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0 - agent_id = coder_agent.dev.id - slug = "develop-sh" - display_name = "develop.sh" - icon = "${data.coder_workspace.me.access_url}/emojis/1f4bb.png" // 💻 - command = "screen -x develop_sh" - share = "authenticated" - open_in = "tab" - order = 0 -} - -resource "coder_script" "develop_sh" { - count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0 - display_name = "develop.sh" - agent_id = coder_agent.dev.id - run_on_start = true - start_blocks_login = false - icon = "${data.coder_workspace.me.access_url}/emojis/1f4bb.png" // 💻 - script = <<-EOT - #!/usr/bin/env bash - set -eux -o pipefail - - trap 'coder exp sync complete develop-sh' EXIT - coder exp sync want develop-sh install-deps - coder exp sync start develop-sh - - cd "${local.repo_dir}" && screen -dmS develop_sh /bin/sh -c 'while true; do ./scripts/develop.sh --; echo "develop.sh exited with code $? restarting in 30s"; sleep 30; done' +module "codex" { + source = "dev.registry.coder.com/coder-labs/codex/coder" + version = "5.0.0" + agent_id = coder_agent.dev.id + workdir = local.repo_dir + enable_ai_gateway = data.coder_parameter.enable_ai_gateway.value + openai_api_key = data.coder_parameter.enable_ai_gateway.value ? "" : var.openai_api_key + mcp = <<-EOT + [mcp_servers.playwright] + command = "npx" + args = ["--", "@playwright/mcp@latest", "--headless", "--isolated", "--no-sandbox"] + type = "stdio" EOT } -resource "coder_app" "preview" { - count = data.coder_task.me.enabled ? data.coder_workspace.me.start_count : 0 +resource "coder_app" "codex" { agent_id = coder_agent.dev.id - slug = "preview" - display_name = "Preview" - icon = "${data.coder_workspace.me.access_url}/emojis/1f50e.png" // 🔎 - url = "http://localhost:8080" - share = "authenticated" - subdomain = true - open_in = "tab" - order = 1 - healthcheck { - url = "http://localhost:8080/healthz" - interval = 5 - threshold = 15 - } + slug = "codex" + display_name = "Codex" + icon = "/icon/openai-codex.svg" + open_in = "slim-window" + command = <<-EOT + #!/bin/bash + set -e + cd "${local.repo_dir}" + exec tmux new-session -A -s codex codex + EOT } diff --git a/dogfood/main.tf b/dogfood/main.tf index db3e5f6b3f..fc64584bde 100644 --- a/dogfood/main.tf +++ b/dogfood/main.tf @@ -51,6 +51,13 @@ variable "CODER_DOGFOOD_ANTHROPIC_API_KEY" { sensitive = true } +variable "CODER_DOGFOOD_OPENAI_API_KEY" { + type = string + description = "The API key that workspaces will use to authenticate with the OpenAI API." + default = "" + sensitive = true +} + resource "coderd_template" "dogfood" { name = var.CODER_TEMPLATE_NAME display_name = "Write Coder on Coder" @@ -67,6 +74,10 @@ resource "coderd_template" "dogfood" { { name = "anthropic_api_key" value = var.CODER_DOGFOOD_ANTHROPIC_API_KEY + }, + { + name = "openai_api_key" + value = var.CODER_DOGFOOD_OPENAI_API_KEY } ] }