mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb128fa077 | |||
| 7c5f9b2adc | |||
| 0a92c5c18f | |||
| a443767ef3 | |||
| 316269e437 | |||
| 9af62366b7 | |||
| 46726a903d | |||
| 8be5d5e01c | |||
| fa5fb31454 | |||
| 52603754cd | |||
| e18caa5a46 |
@@ -1,19 +1,19 @@
|
||||
---
|
||||
display_name: Codex CLI
|
||||
icon: ../../../../.icons/openai.svg
|
||||
description: Run Codex CLI in your workspace with AgentAPI integration
|
||||
description: Run Codex CLI in your workspace with optional Tasks integration
|
||||
verified: true
|
||||
tags: [agent, codex, ai, openai, tasks, aibridge]
|
||||
---
|
||||
|
||||
# Codex CLI
|
||||
|
||||
Run Codex CLI in your workspace to access OpenAI's models through the Codex interface, with custom pre/post install scripts. This module integrates with [AgentAPI](https://github.com/coder/agentapi) for Coder Tasks compatibility.
|
||||
Install Codex CLI in your workspace with optional Coder Tasks integration via [AgentAPI](https://github.com/coder/agentapi). The module supports AI Bridge, custom install scripts, and MCP server configuration.
|
||||
|
||||
```tf
|
||||
module "codex" {
|
||||
source = "registry.coder.com/coder-labs/codex/coder"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
openai_api_key = var.openai_api_key
|
||||
workdir = "/home/coder/project"
|
||||
@@ -22,47 +22,51 @@ module "codex" {
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- OpenAI API key for Codex access
|
||||
- OpenAI API key for Codex access (not required when `enable_aibridge = true`)
|
||||
|
||||
## Examples
|
||||
|
||||
### Run standalone
|
||||
### Standalone (no Tasks UI)
|
||||
|
||||
Use `enable_tasks = false` to install Codex without AgentAPI/Tasks. `workdir` is optional in this mode.
|
||||
|
||||
```tf
|
||||
module "codex" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder-labs/codex/coder"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
openai_api_key = "..."
|
||||
workdir = "/home/coder/project"
|
||||
report_tasks = false
|
||||
enable_tasks = false
|
||||
# workdir not required in standalone mode
|
||||
}
|
||||
```
|
||||
|
||||
### Usage with AI Bridge
|
||||
|
||||
[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`. Requires Coder version 2.30+
|
||||
|
||||
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
|
||||
[AI Bridge](https://coder.com/docs/ai-coder/ai-bridge) is a Premium Coder feature that provides centralized LLM proxy management. Set `enable_aibridge = true` to use it (requires Coder 2.30+). When AI Bridge is enabled, authentication uses the workspace owner session token, so `openai_api_key` should be omitted.
|
||||
|
||||
```tf
|
||||
module "codex" {
|
||||
source = "registry.coder.com/coder-labs/codex/coder"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/project"
|
||||
enable_aibridge = true
|
||||
enable_tasks = false # Standalone mode - just CLI, no Tasks UI
|
||||
# workdir not required in standalone mode
|
||||
}
|
||||
```
|
||||
|
||||
For Tasks integration, add `enable_aibridge = true` to the [Usage with Tasks](#usage-with-tasks) example below.
|
||||
|
||||
When `enable_aibridge = true`, the module:
|
||||
|
||||
- Configures Codex to use the AI Bridge profile with `base_url` pointing to `${data.coder_workspace.me.access_url}/api/v2/aibridge/openai/v1` and `env_key` pointing to the workspace owner's session token
|
||||
- Sets `profile = "aibridge"` at the top of `config.toml` so Codex uses AI Bridge by default
|
||||
|
||||
```toml
|
||||
profile = "aibridge"
|
||||
|
||||
[model_providers.aibridge]
|
||||
name = "AI Bridge"
|
||||
base_url = "https://example.coder.com/api/v2/aibridge/openai/v1"
|
||||
@@ -75,9 +79,7 @@ model = "<model>" # as configured in the module input
|
||||
model_reasoning_effort = "<model_reasoning_effort>" # as configured in the module input
|
||||
```
|
||||
|
||||
Codex then runs with `--profile aibridge`
|
||||
|
||||
This allows Codex to route API requests through Coder's AI Bridge instead of directly to OpenAI's API.
|
||||
Codex uses the AI Bridge profile by default, so running `codex` manually does not require `--profile aibridge`.
|
||||
Template build will fail if `openai_api_key` is provided alongside `enable_aibridge = true`.
|
||||
|
||||
### Usage with Tasks
|
||||
@@ -94,7 +96,7 @@ data "coder_task" "me" {}
|
||||
|
||||
module "codex" {
|
||||
source = "registry.coder.com/coder-labs/codex/coder"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
openai_api_key = "..."
|
||||
ai_prompt = data.coder_task.me.prompt
|
||||
@@ -112,7 +114,7 @@ This example shows additional configuration options for custom models, MCP serve
|
||||
```tf
|
||||
module "codex" {
|
||||
source = "registry.coder.com/coder-labs/codex/coder"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
agent_id = coder_agent.example.id
|
||||
openai_api_key = "..."
|
||||
workdir = "/home/coder/project"
|
||||
@@ -142,11 +144,11 @@ module "codex" {
|
||||
|
||||
## How it Works
|
||||
|
||||
- **Install**: The module installs Codex CLI and sets up the environment
|
||||
- **System Prompt**: If `codex_system_prompt` is set, writes the prompt to `AGENTS.md` in the `~/.codex/` directory
|
||||
- **Start**: Launches Codex CLI in the specified directory, wrapped by AgentAPI
|
||||
- **Configuration**: Sets `OPENAI_API_KEY` environment variable and passes `--model` flag to Codex CLI (if variables provided)
|
||||
- **Session Continuity**: When `continue = true` (default), the module automatically tracks task sessions in `~/.codex-module/.codex-task-session`. On workspace restart, it resumes the existing session with full conversation history. Set `continue = false` to always start fresh sessions.
|
||||
- **Install**: Installs Codex CLI and prepares configuration.
|
||||
- **System Prompt**: If `codex_system_prompt` is set, writes it to `~/.codex/AGENTS.md`.
|
||||
- **Start**: When `enable_tasks = true`, launches Codex via AgentAPI in the selected `workdir`. When `enable_tasks = false`, only the install script runs.
|
||||
- **Configuration**: Writes `OPENAI_API_KEY` when provided, and sets the AI Bridge profile when `enable_aibridge = true`.
|
||||
- **Session Continuity**: When `continue = true` (default), task sessions are tracked in `~/.codex-module/.codex-task-session` for resume on restart. Set `continue = false` to always start fresh sessions.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -168,13 +170,14 @@ network_access = true
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Check installation and startup logs in `~/.codex-module/`
|
||||
- Ensure your OpenAI API key has access to the specified model
|
||||
- Tasks mode: check installation/startup logs in `~/.codex-module/`.
|
||||
- Standalone mode: review the workspace script output for the "Install Codex" script.
|
||||
- Ensure your OpenAI API key has access to the specified model (unless using AI Bridge).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker).
|
||||
> The module automatically configures Codex with your API key and model preferences.
|
||||
> workdir is a required variable for the module to function correctly.
|
||||
> `workdir` is required when `enable_tasks = true` (default). For standalone CLI usage, set `enable_tasks = false` and `workdir` becomes optional.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -41,15 +41,25 @@ interface SetupProps {
|
||||
|
||||
const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
||||
const projectDir = "/home/coder/project";
|
||||
|
||||
const moduleVars: Record<string, string> = {
|
||||
install_codex: props?.skipCodexMock ? "true" : "false",
|
||||
codex_model: "gpt-4-turbo",
|
||||
workdir: "/home/coder",
|
||||
...props?.moduleVariables,
|
||||
};
|
||||
|
||||
// For backward compatibility: install_agentapi takes precedence over enable_tasks
|
||||
// Only set install_agentapi when explicitly installing real AgentAPI
|
||||
if (props?.skipAgentAPIMock) {
|
||||
moduleVars.install_agentapi = "true";
|
||||
}
|
||||
// Otherwise, let enable_tasks control whether agentapi module runs
|
||||
// (defaults to true unless explicitly disabled in moduleVariables)
|
||||
|
||||
const { id } = await setupUtil({
|
||||
moduleDir: import.meta.dir,
|
||||
moduleVariables: {
|
||||
install_codex: props?.skipCodexMock ? "true" : "false",
|
||||
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
||||
codex_model: "gpt-4-turbo",
|
||||
workdir: "/home/coder",
|
||||
...props?.moduleVariables,
|
||||
},
|
||||
moduleVariables: moduleVars,
|
||||
registerCleanup,
|
||||
projectDir,
|
||||
skipAgentAPIMock: props?.skipAgentAPIMock,
|
||||
@@ -481,5 +491,28 @@ describe("codex", async () => {
|
||||
expect(configToml).toContain(
|
||||
"[profiles.aibridge]\n" + 'model_provider = "aibridge"',
|
||||
);
|
||||
// Verify profile = "aibridge" is set at the top of the config
|
||||
expect(configToml.startsWith('profile = "aibridge"')).toBe(true);
|
||||
});
|
||||
|
||||
test("codex-standalone-mode", async () => {
|
||||
// Test standalone mode without tasks (enable_tasks = false)
|
||||
// workdir should default to /home/coder when not explicitly provided
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
enable_tasks: "false",
|
||||
enable_aibridge: "true",
|
||||
},
|
||||
});
|
||||
|
||||
await execModuleScript(id);
|
||||
|
||||
const configToml = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.codex/config.toml",
|
||||
);
|
||||
// In standalone mode, config should still have aibridge profile set as default
|
||||
expect(configToml.startsWith('profile = "aibridge"')).toBe(true);
|
||||
expect(configToml).toContain("[profiles.aibridge]");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,19 @@ variable "icon" {
|
||||
|
||||
variable "workdir" {
|
||||
type = string
|
||||
description = "The folder to run Codex in."
|
||||
description = "The folder to run Codex in. Required when enable_tasks is true."
|
||||
default = null
|
||||
|
||||
validation {
|
||||
condition = var.workdir != null || !local.tasks_enabled
|
||||
error_message = "workdir is required when enable_tasks is true. Set workdir or set enable_tasks = false for standalone CLI usage."
|
||||
}
|
||||
}
|
||||
|
||||
variable "enable_tasks" {
|
||||
type = bool
|
||||
description = "Enable Tasks UI for Codex (requires workdir). When false, only installs Codex CLI with config for standalone usage."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "report_tasks" {
|
||||
@@ -122,10 +134,11 @@ variable "openai_api_key" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
# TODO: Remove install_agentapi in next major version (5.0.0)
|
||||
variable "install_agentapi" {
|
||||
type = bool
|
||||
description = "Whether to install AgentAPI."
|
||||
default = true
|
||||
description = "DEPRECATED: Use enable_tasks instead. Whether to install AgentAPI."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "agentapi_version" {
|
||||
@@ -184,7 +197,9 @@ resource "coder_env" "coder_aibridge_session_token" {
|
||||
}
|
||||
|
||||
locals {
|
||||
workdir = trimsuffix(var.workdir, "/")
|
||||
# Use enable_tasks, but fall back to install_agentapi if explicitly set (for backward compat)
|
||||
tasks_enabled = var.install_agentapi != null ? var.install_agentapi : var.enable_tasks
|
||||
workdir = var.workdir != null ? trimsuffix(var.workdir, "/") : "/home/coder"
|
||||
app_slug = "codex"
|
||||
install_script = file("${path.module}/scripts/install.sh")
|
||||
start_script = file("${path.module}/scripts/start.sh")
|
||||
@@ -204,6 +219,7 @@ locals {
|
||||
}
|
||||
|
||||
module "agentapi" {
|
||||
count = local.tasks_enabled ? 1 : 0
|
||||
source = "registry.coder.com/coder/agentapi/coder"
|
||||
version = "2.0.0"
|
||||
|
||||
@@ -218,7 +234,7 @@ module "agentapi" {
|
||||
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
|
||||
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
|
||||
module_dir_name = local.module_dir_name
|
||||
install_agentapi = var.install_agentapi
|
||||
install_agentapi = true
|
||||
agentapi_subdomain = var.subdomain
|
||||
agentapi_version = var.agentapi_version
|
||||
pre_install_script = var.pre_install_script
|
||||
@@ -262,6 +278,37 @@ module "agentapi" {
|
||||
EOT
|
||||
}
|
||||
|
||||
output "task_app_id" {
|
||||
value = module.agentapi.task_app_id
|
||||
|
||||
# Standalone installation (when tasks are disabled)
|
||||
resource "coder_script" "standalone_install" {
|
||||
count = local.tasks_enabled ? 0 : 1
|
||||
agent_id = var.agent_id
|
||||
display_name = "Install Codex"
|
||||
icon = var.icon
|
||||
run_on_start = true
|
||||
start_blocks_login = false
|
||||
script = <<-EOT
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
|
||||
chmod +x /tmp/install.sh
|
||||
ARG_OPENAI_API_KEY='${var.openai_api_key}' \
|
||||
ARG_REPORT_TASKS='false' \
|
||||
ARG_INSTALL='${var.install_codex}' \
|
||||
ARG_CODEX_VERSION='${var.codex_version}' \
|
||||
ARG_BASE_CONFIG_TOML='${base64encode(var.base_config_toml)}' \
|
||||
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
|
||||
ARG_AIBRIDGE_CONFIG='${base64encode(var.enable_aibridge ? local.aibridge_config : "")}' \
|
||||
ARG_ADDITIONAL_MCP_SERVERS='${base64encode(var.additional_mcp_servers)}' \
|
||||
ARG_CODER_MCP_APP_STATUS_SLUG='' \
|
||||
ARG_CODEX_START_DIRECTORY='${local.workdir}' \
|
||||
ARG_CODEX_INSTRUCTION_PROMPT='${base64encode(var.codex_system_prompt)}' \
|
||||
/tmp/install.sh
|
||||
EOT
|
||||
}
|
||||
|
||||
output "task_app_id" {
|
||||
value = local.tasks_enabled ? module.agentapi[0].task_app_id : null
|
||||
}
|
||||
|
||||
@@ -151,6 +151,25 @@ function populate_config_toml() {
|
||||
write_minimal_default_config "$CONFIG_PATH"
|
||||
fi
|
||||
|
||||
# Set aibridge as default profile when AI Bridge is enabled
|
||||
# This allows users to run `codex` without --profile flag
|
||||
if [ "$ARG_ENABLE_AIBRIDGE" = "true" ]; then
|
||||
printf "Setting aibridge as default profile\n"
|
||||
# Remove any existing top-level profile line (before first section header)
|
||||
# This only removes profile = ... at top level, not inside [profiles.*] sections
|
||||
awk '
|
||||
BEGIN { in_top_level = 1 }
|
||||
/^\[/ { in_top_level = 0 }
|
||||
in_top_level && /^profile[ \t]*=/ { next }
|
||||
{ print }
|
||||
' "$CONFIG_PATH" > "${CONFIG_PATH}.tmp"
|
||||
mv "${CONFIG_PATH}.tmp" "$CONFIG_PATH"
|
||||
# Prepend profile = "aibridge" to the config
|
||||
local temp_config
|
||||
temp_config=$(cat "$CONFIG_PATH")
|
||||
echo -e "profile = \"aibridge\"\n\n$temp_config" > "$CONFIG_PATH"
|
||||
fi
|
||||
|
||||
append_mcp_servers_section "$CONFIG_PATH"
|
||||
|
||||
if [ "$ARG_ENABLE_AIBRIDGE" = "true" ]; then
|
||||
|
||||
Reference in New Issue
Block a user