diff --git a/.icons/openai.svg b/.icons/openai.svg
new file mode 100644
index 00000000..3b4eff96
--- /dev/null
+++ b/.icons/openai.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/registry/coder-labs/modules/codex/README.md b/registry/coder-labs/modules/codex/README.md
new file mode 100644
index 00000000..c5548b0e
--- /dev/null
+++ b/registry/coder-labs/modules/codex/README.md
@@ -0,0 +1,101 @@
+---
+display_name: Codex CLI
+icon: ../../../../.icons/openai.svg
+description: Run Codex CLI in your workspace with AgentAPI integration
+verified: true
+tags: [agent, codex, ai, openai, tasks]
+---
+
+# 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.
+
+```tf
+module "codex" {
+ source = "registry.coder.com/coder-labs/codex/coder"
+ version = "1.0.0"
+ agent_id = coder_agent.example.id
+ openai_api_key = var.openai_api_key
+ agentapi_version = "v0.3.2"
+}
+```
+
+## Prerequisites
+
+- You must add the [Coder Login](https://registry.coder.com/modules/coder-login/coder) module to your template
+- OpenAI API key for Codex access
+
+## Usage Example
+
+- Simple usage Example:
+
+```tf
+module "codex" {
+ count = data.coder_workspace.me.start_count
+ source = "registry.coder.com/coder-labs/codex/coder"
+ version = "1.0.0"
+ agent_id = coder_agent.example.id
+ openai_api_key = "..."
+ codex_model = "o4-mini"
+ install_codex = true
+ codex_version = "latest"
+ folder = "/home/coder/project"
+ codex_system_prompt = "You are a helpful coding assistant. Start every response with `Codex says:`"
+ agentapi_version = "v0.3.2"
+}
+```
+
+- Example usage with Tasks:
+
+```tf
+# This
+data "coder_parameter" "ai_prompt" {
+ type = "string"
+ name = "AI Prompt"
+ default = ""
+ description = "Initial prompt for the Codex CLI"
+ mutable = true
+}
+
+module "coder-login" {
+ count = data.coder_workspace.me.start_count
+ source = "registry.coder.com/coder/coder-login/coder"
+ version = "1.0.31"
+ agent_id = coder_agent.example.id
+}
+
+module "codex" {
+ source = "registry.coder.com/coder-labs/codex/coder"
+ agent_id = coder_agent.example.id
+ openai_api_key = "..."
+ ai_prompt = data.coder_parameter.ai_prompt.value
+ agentapi_version = "v0.3.2"
+}
+```
+
+> **Security Notice**: This module uses the `--dangerously-bypass-approvals-and-sandbox` flag when running Codex CLI. This flag
+> bypasses standard permission checks and allows Codex CLI broader access to your system than normally permitted. While
+> this enables more functionality, it also means Codex CLI can potentially execute commands with the same privileges as
+> the user running it. Use this module _only_ in trusted environments and be aware of the security implications.
+
+## How it Works
+
+- **Install**: The module installs Codex CLI and sets up the environment
+- **System Prompt**: If `codex_system_prompt` and `folder` are set, creates the directory (if needed) and writes the prompt to `AGENTS.md`
+- **Start**: Launches Codex CLI in the specified directory, wrapped by AgentAPI
+- **Environment**: Sets `OPENAI_API_KEY` and `CODEX_MODEL` for the CLI (if variables provided)
+
+## Troubleshooting
+
+- Check installation and startup logs in `~/.codex-module/`
+- Ensure your OpenAI API key has access to the specified model
+
+> [!IMPORTANT]
+> To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set, and **you create an `ai_prompt` `coder_parameter` and pass it's value to codex `ai_prompt` variable**. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker).
+> The module automatically configures Codex with your API key and model preferences.
+
+## References
+
+- [OpenAI API Documentation](https://platform.openai.com/docs)
+- [AgentAPI Documentation](https://github.com/coder/agentapi)
+- [Coder AI Agents Guide](https://coder.com/docs/tutorials/ai-agents)
diff --git a/registry/coder-labs/modules/codex/main.test.ts b/registry/coder-labs/modules/codex/main.test.ts
new file mode 100644
index 00000000..c2b482d9
--- /dev/null
+++ b/registry/coder-labs/modules/codex/main.test.ts
@@ -0,0 +1,215 @@
+import {
+ test,
+ afterEach,
+ describe,
+ setDefaultTimeout,
+ beforeAll,
+ expect,
+} from "bun:test";
+import { execContainer, readFileContainer, runTerraformInit } from "~test";
+import {
+ loadTestFile,
+ writeExecutable,
+ setup as setupUtil,
+ execModuleScript,
+ expectAgentAPIStarted,
+} from "../../../coder/modules/agentapi/test-util";
+import dedent from "dedent";
+
+let cleanupFunctions: (() => Promise)[] = [];
+const registerCleanup = (cleanup: () => Promise) => {
+ cleanupFunctions.push(cleanup);
+};
+afterEach(async () => {
+ const cleanupFnsCopy = cleanupFunctions.slice().reverse();
+ cleanupFunctions = [];
+ for (const cleanup of cleanupFnsCopy) {
+ try {
+ await cleanup();
+ } catch (error) {
+ console.error("Error during cleanup:", error);
+ }
+ }
+});
+
+interface SetupProps {
+ skipAgentAPIMock?: boolean;
+ skipCodexMock?: boolean;
+ moduleVariables?: Record;
+ agentapiMockScript?: string;
+}
+
+const setup = async (props?: SetupProps): Promise<{ id: string }> => {
+ const projectDir = "/home/coder/project";
+ 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",
+ ...props?.moduleVariables,
+ },
+ registerCleanup,
+ projectDir,
+ skipAgentAPIMock: props?.skipAgentAPIMock,
+ agentapiMockScript: props?.agentapiMockScript,
+ });
+ if (!props?.skipCodexMock) {
+ await writeExecutable({
+ containerId: id,
+ filePath: "/usr/bin/codex",
+ content: await loadTestFile(import.meta.dir, "codex-mock.sh"),
+ });
+ }
+ return { id };
+};
+
+setDefaultTimeout(60 * 1000);
+
+describe("codex", async () => {
+ beforeAll(async () => {
+ await runTerraformInit(import.meta.dir);
+ });
+
+ test("happy-path", async () => {
+ const { id } = await setup();
+ await execModuleScript(id);
+ await expectAgentAPIStarted(id);
+ });
+
+ test("install-codex-version", async () => {
+ const version_to_install = "0.10.0";
+ const { id } = await setup({
+ skipCodexMock: true,
+ moduleVariables: {
+ install_codex: "true",
+ codex_version: version_to_install,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await execContainer(id, [
+ "bash",
+ "-c",
+ `cat /home/coder/.codex-module/install.log`,
+ ]);
+ expect(resp.stdout).toContain(version_to_install);
+ });
+
+ test("codex-config-toml", async () => {
+ const settings = dedent`
+ [mcp_servers.CustomMCP]
+ command = "/Users/jkmr/Documents/work/coder/coder_darwin_arm64"
+ args = ["exp", "mcp", "server", "app-status-slug=codex"]
+ env = { "CODER_MCP_APP_STATUS_SLUG" = "codex", "CODER_MCP_AI_AGENTAPI_URL"= "http://localhost:3284" }
+ description = "Report ALL tasks and statuses (in progress, done, failed) you are working on."
+ enabled = true
+ type = "stdio"
+ `.trim();
+ const { id } = await setup({
+ moduleVariables: {
+ extra_codex_settings_toml: settings,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
+ expect(resp).toContain("[mcp_servers.CustomMCP]");
+ expect(resp).toContain("[mcp_servers.Coder]");
+ });
+
+ test("codex-api-key", async () => {
+ const apiKey = "test-api-key-123";
+ const { id } = await setup({
+ moduleVariables: {
+ openai_api_key: apiKey,
+ },
+ });
+ await execModuleScript(id);
+
+ const resp = await readFileContainer(id, "/home/coder/.codex-module/agentapi-start.log");
+ expect(resp).toContain("openai_api_key provided !");
+ });
+
+ test("pre-post-install-scripts", async () => {
+ const { id } = await setup({
+ moduleVariables: {
+ pre_install_script: "#!/bin/bash\necho 'pre-install-script'",
+ post_install_script: "#!/bin/bash\necho 'post-install-script'",
+ },
+ });
+ await execModuleScript(id);
+ const preInstallLog = await readFileContainer(id, "/home/coder/.codex-module/pre_install.log");
+ expect(preInstallLog).toContain("pre-install-script");
+ const postInstallLog = await readFileContainer(id, "/home/coder/.codex-module/post_install.log");
+ expect(postInstallLog).toContain("post-install-script");
+ });
+
+ test("folder-variable", async () => {
+ const folder = "/tmp/codex-test-folder";
+ const { id } = await setup({
+ skipCodexMock: false,
+ moduleVariables: {
+ folder,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await readFileContainer(id, "/home/coder/.codex-module/install.log");
+ expect(resp).toContain(folder);
+ });
+
+ test("additional-extensions", async () => {
+ const additional = dedent`
+ [mcp_servers.CustomMCP]
+ command = "/Users/jkmr/Documents/work/coder/coder_darwin_arm64"
+ args = ["exp", "mcp", "server", "app-status-slug=codex"]
+ env = { "CODER_MCP_APP_STATUS_SLUG" = "codex", "CODER_MCP_AI_AGENTAPI_URL"= "http://localhost:3284" }
+ description = "Report ALL tasks and statuses (in progress, done, failed) you are working on."
+ enabled = true
+ type = "stdio"
+ `.trim();
+ const { id } = await setup({
+ moduleVariables: {
+ additional_extensions: additional,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
+ expect(resp).toContain("[mcp_servers.CustomMCP]");
+ expect(resp).toContain("[mcp_servers.Coder]");
+ });
+
+ test("codex-system-prompt", async () => {
+ const prompt = "This is a system prompt for Codex.";
+ const { id } = await setup({
+ moduleVariables: {
+ codex_system_prompt: prompt,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await readFileContainer(id, "/home/coder/AGENTS.md");
+ expect(resp).toContain(prompt);
+ });
+
+ test("codex-ai-task-prompt", async () => {
+ const prompt = "This is a system prompt for Codex.";
+ const { id } = await setup({
+ moduleVariables: {
+ ai_prompt: prompt,
+ },
+ });
+ await execModuleScript(id);
+ const resp = await execContainer(id, [
+ "bash",
+ "-c",
+ `cat /home/coder/.codex-module/agentapi-start.log`,
+ ]);
+ expect(resp.stdout).toContain(`Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: ${prompt}`);
+ });
+
+ test("start-without-prompt", async () => {
+ const { id } = await setup();
+ await execModuleScript(id);
+ const prompt = await execContainer(id, ["ls", "-l", "/home/coder/AGENTS.md"]);
+ expect(prompt.exitCode).not.toBe(0);
+ expect(prompt.stderr).toContain("No such file or directory");
+ });
+});
\ No newline at end of file
diff --git a/registry/coder-labs/modules/codex/main.tf b/registry/coder-labs/modules/codex/main.tf
new file mode 100644
index 00000000..0b9d1f33
--- /dev/null
+++ b/registry/coder-labs/modules/codex/main.tf
@@ -0,0 +1,177 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ coder = {
+ source = "coder/coder"
+ version = ">= 2.7"
+ }
+ }
+}
+
+variable "agent_id" {
+ type = string
+ description = "The ID of a Coder agent."
+}
+
+data "coder_workspace" "me" {}
+
+data "coder_workspace_owner" "me" {}
+
+variable "order" {
+ type = number
+ description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
+ default = null
+}
+
+variable "group" {
+ type = string
+ description = "The name of a group that this app belongs to."
+ default = null
+}
+
+variable "icon" {
+ type = string
+ description = "The icon to use for the app."
+ default = "/icon/code.svg"
+}
+
+variable "folder" {
+ type = string
+ description = "The folder to run Codex in."
+ default = "/home/coder"
+}
+
+variable "install_codex" {
+ type = bool
+ description = "Whether to install Codex."
+ default = true
+}
+
+variable "codex_version" {
+ type = string
+ description = "The version of Codex to install."
+ default = ""
+}
+
+variable "extra_codex_settings_toml" {
+ type = string
+ description = "Settings to append to ~/.codex/config.toml."
+ default = ""
+}
+
+variable "openai_api_key" {
+ type = string
+ description = "Codex API Key"
+ default = ""
+}
+
+variable "install_agentapi" {
+ type = bool
+ description = "Whether to install AgentAPI."
+ default = true
+}
+
+variable "agentapi_version" {
+ type = string
+ description = "The version of AgentAPI to install."
+ default = "v0.3.2"
+}
+
+variable "codex_model" {
+ type = string
+ description = "The model to use for Codex (e.g., o4-mini)."
+ default = ""
+}
+
+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 "ai_prompt" {
+ type = string
+ description = "Task prompt for the Codex CLI"
+ default = ""
+}
+
+variable "additional_extensions" {
+ type = string
+ description = "Additional extensions configuration in json format to append to the config."
+ default = ""
+}
+
+variable "codex_system_prompt" {
+ type = string
+ description = "System prompt for Codex. It will be added to AGENTS.md in the specified folder."
+ default = ""
+}
+
+resource "coder_env" "openai_api_key" {
+ agent_id = var.agent_id
+ name = "OPENAI_API_KEY"
+ value = var.openai_api_key
+}
+
+locals {
+ app_slug = "codex"
+ install_script = file("${path.module}/scripts/install.sh")
+ start_script = file("${path.module}/scripts/start.sh")
+ module_dir_name = ".codex-module"
+}
+
+module "agentapi" {
+ source = "registry.coder.com/coder/agentapi/coder"
+ version = "1.0.1"
+
+ agent_id = var.agent_id
+ web_app_slug = local.app_slug
+ web_app_order = var.order
+ web_app_group = var.group
+ web_app_icon = var.icon
+ web_app_display_name = "Codex"
+ cli_app_slug = "${local.app_slug}-cli"
+ cli_app_display_name = "Codex CLI"
+ module_dir_name = local.module_dir_name
+ install_agentapi = var.install_agentapi
+ agentapi_version = var.agentapi_version
+ pre_install_script = var.pre_install_script
+ post_install_script = var.post_install_script
+ start_script = <<-EOT
+ #!/bin/bash
+ set -o errexit
+ set -o pipefail
+
+ echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
+ chmod +x /tmp/start.sh
+ ARG_OPENAI_API_KEY='${var.openai_api_key}' \
+ ARG_CODEX_MODEL='${var.codex_model}' \
+ ARG_CODEX_START_DIRECTORY='${var.folder}' \
+ ARG_CODEX_TASK_PROMPT='${base64encode(var.ai_prompt)}' \
+ /tmp/start.sh
+ EOT
+
+ install_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_INSTALL='${var.install_codex}' \
+ ARG_CODEX_VERSION='${var.codex_version}' \
+ ARG_EXTRA_CODEX_CONFIG='${base64encode(var.extra_codex_settings_toml)}' \
+ ARG_CODER_MCP_APP_STATUS_SLUG='${local.app_slug}' \
+ ARG_ADDITIONAL_EXTENSIONS='${base64encode(var.additional_extensions)}' \
+ ARG_CODEX_START_DIRECTORY='${var.folder}' \
+ ARG_CODEX_INSTRUCTION_PROMPT='${base64encode(var.codex_system_prompt)}' \
+ /tmp/install.sh
+ EOT
+}
\ No newline at end of file
diff --git a/registry/coder-labs/modules/codex/scripts/install.sh b/registry/coder-labs/modules/codex/scripts/install.sh
new file mode 100644
index 00000000..e9a54adc
--- /dev/null
+++ b/registry/coder-labs/modules/codex/scripts/install.sh
@@ -0,0 +1,154 @@
+#!/bin/bash
+
+BOLD='\033[0;1m'
+
+# Function to check if a command exists
+command_exists() {
+ command -v "$1" >/dev/null 2>&1
+}
+
+set -o nounset
+
+ARG_EXTRA_CODEX_CONFIG=$(echo -n "$ARG_EXTRA_CODEX_CONFIG" | base64 -d)
+ARG_ADDITIONAL_EXTENSIONS=$(echo -n "$ARG_ADDITIONAL_EXTENSIONS" | base64 -d)
+ARG_CODEX_INSTRUCTION_PROMPT=$(echo -n "$ARG_CODEX_INSTRUCTION_PROMPT" | base64 -d)
+
+echo "--------------------------------"
+printf "install: %s\n" "$ARG_INSTALL"
+printf "codex_version: %s\n" "$ARG_CODEX_VERSION"
+printf "codex_config: %s\n" "$ARG_EXTRA_CODEX_CONFIG"
+printf "app_slug: %s\n" "$ARG_CODER_MCP_APP_STATUS_SLUG"
+printf "additional_extensions: %s\n" "$ARG_ADDITIONAL_EXTENSIONS"
+printf "start_directory: %s\n" "$ARG_CODEX_START_DIRECTORY"
+printf "instruction_prompt: %s\n" "$ARG_CODEX_INSTRUCTION_PROMPT"
+
+echo "--------------------------------"
+
+set +o nounset
+
+function install_node() {
+ # borrowed from claude-code module
+ if ! command_exists npm; then
+ printf "npm not found, checking for Node.js installation...\n"
+ if ! command_exists node; then
+ printf "Node.js not found, installing Node.js via NVM...\n"
+ export NVM_DIR="$HOME/.nvm"
+ if [ ! -d "$NVM_DIR" ]; then
+ mkdir -p "$NVM_DIR"
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
+ else
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
+ fi
+
+ nvm install --lts
+ nvm use --lts
+ nvm alias default node
+
+ printf "Node.js installed: %s\n" "$(node --version)"
+ printf "npm installed: %s\n" "$(npm --version)"
+ else
+ printf "Node.js is installed but npm is not available. Please install npm manually.\n"
+ exit 1
+ fi
+ fi
+}
+
+function install_codex() {
+ if [ "${ARG_INSTALL}" = "true" ]; then
+ # we need node to install and run codex-cli
+ install_node
+
+ # If nvm does not exist, we will create a global npm directory (this os to prevent the possibility of EACCESS issues on npm -g)
+ if ! command_exists nvm; then
+ printf "which node: %s\n" "$(which node)"
+ printf "which npm: %s\n" "$(which npm)"
+
+ # Create a directory for global packages
+ mkdir -p "$HOME"/.npm-global
+
+ # Configure npm to use it
+ npm config set prefix "$HOME/.npm-global"
+
+ # Add to PATH for current session
+ export PATH="$HOME/.npm-global/bin:$PATH"
+
+ # Add to shell profile for future sessions
+ if ! grep -q "export PATH=$HOME/.npm-global/bin:\$PATH" ~/.bashrc; then
+ echo "export PATH=$HOME/.npm-global/bin:\$PATH" >> ~/.bashrc
+ fi
+ fi
+
+ printf "%s Installing Codex CLI\n" "${BOLD}"
+
+ if [ -n "$ARG_CODEX_VERSION" ]; then
+ npm install -g "@openai/codex@$ARG_CODEX_VERSION"
+ else
+ npm install -g "@openai/codex"
+ fi
+ printf "%s Successfully installed Codex CLI. Version: %s\n" "${BOLD}" "$(codex --version)"
+ fi
+}
+
+function populate_config_toml() {
+ CONFIG_PATH="$HOME/.codex/config.toml"
+ mkdir -p "$(dirname "$CONFIG_PATH")"
+ printf "Custom codex_config is provided !\n"
+ BASE_EXTENSIONS=$(cat < "$HOME/.codex/config.toml"
+
+}
+
+function add_instruction_prompt_if_exists() {
+ if [ -n "${ARG_CODEX_INSTRUCTION_PROMPT:-}" ]; then
+ if [ -d "${ARG_CODEX_START_DIRECTORY}" ]; then
+ printf "Directory '%s' exists. Changing to it.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ cd "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not change to directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+ else
+ printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ mkdir -p "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not create directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+ cd "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not change to directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+ fi
+
+ # Check if AGENTS.md contains the instruction prompt already
+ if [ -f AGENTS.md ] && grep -Fxq "${ARG_CODEX_INSTRUCTION_PROMPT}" AGENTS.md; then
+ printf "AGENTS.md already contains the instruction prompt. Skipping append.\n"
+ else
+ printf "Appending instruction prompt to AGENTS.md\n"
+ echo -e "\n${ARG_CODEX_INSTRUCTION_PROMPT}" >> AGENTS.md
+ fi
+ else
+ printf "AGENTS.md is not set.\n"
+ fi
+}
+
+
+# Install Codex
+install_codex
+codex --version
+populate_config_toml
+add_instruction_prompt_if_exists
\ No newline at end of file
diff --git a/registry/coder-labs/modules/codex/scripts/start.sh b/registry/coder-labs/modules/codex/scripts/start.sh
new file mode 100644
index 00000000..833a26dd
--- /dev/null
+++ b/registry/coder-labs/modules/codex/scripts/start.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# Load shell environment
+source "$HOME"/.bashrc
+
+command_exists() {
+ command -v "$1" >/dev/null 2>&1
+}
+
+if [ -f "$HOME/.nvm/nvm.sh" ]; then
+ source "$HOME"/.nvm/nvm.sh
+else
+ export PATH="$HOME/.npm-global/bin:$PATH"
+fi
+
+printf "Version: %s\n" "$(codex --version)"
+
+ARG_CODEX_TASK_PROMPT=$(echo -n "$ARG_CODEX_TASK_PROMPT" | base64 -d)
+
+echo "--------------------------------"
+printf "openai_api_key: %s\n" "$ARG_OPENAI_API_KEY"
+printf "codex_model: %s\n" "$ARG_CODEX_MODEL"
+printf "start_directory: %s\n" "$ARG_CODEX_START_DIRECTORY"
+printf "task_prompt: %s\n" "$ARG_CODEX_TASK_PROMPT"
+echo "--------------------------------"
+
+CODEX_ARGS=("--skip-git-repo-check")
+
+if command_exists codex; then
+ printf "Codex is installed\n"
+else
+ printf "Error: Codex is not installed. Please enable install_codex or install it manually\n"
+ exit 1
+fi
+
+if [ -d "${ARG_CODEX_START_DIRECTORY}" ]; then
+ printf "Directory '%s' exists. Changing to it.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ cd "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not change to directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+else
+ printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ mkdir -p "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not create directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+ cd "${ARG_CODEX_START_DIRECTORY}" || {
+ printf "Error: Could not change to directory '%s'.\\n" "${ARG_CODEX_START_DIRECTORY}"
+ exit 1
+ }
+fi
+
+if [ -n "$ARG_CODEX_MODEL" ]; then
+ CODEX_ARGS+=("--model" "$ARG_CODEX_MODEL")
+fi
+
+if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then
+ printf "Running the task prompt %s\n" "$ARG_CODEX_TASK_PROMPT"
+ PROMPT="Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT"
+ CODEX_ARGS+=("--dangerously-bypass-approvals-and-sandbox" "$PROMPT")
+else
+ printf "No task prompt given.\n"
+fi
+
+if [ -n "$ARG_OPENAI_API_KEY" ]; then
+ printf "openai_api_key provided !\n"
+else
+ printf "openai_api_key not provided\n"
+fi
+
+# use low width to fit in the tasks UI sidebar
+# we adjust the height to 930 due to a bug in codex module, see: https://github.com/openai/codex/issues/1608
+printf "Starting codex with %s\n" "${CODEX_ARGS[@]}"
+agentapi server --term-width 63 --term-height 930 -- codex "${CODEX_ARGS[@]}"
\ No newline at end of file
diff --git a/registry/coder-labs/modules/codex/testdata/codex-mock.sh b/registry/coder-labs/modules/codex/testdata/codex-mock.sh
new file mode 100644
index 00000000..989abd1e
--- /dev/null
+++ b/registry/coder-labs/modules/codex/testdata/codex-mock.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [[ "$1" == "--version" ]]; then
+ echo "HELLO: $(bash -c env)"
+ echo "codex version v1.0.0"
+ exit 0
+fi
+
+set -e
+
+while true; do
+ echo "$(date) - codex-mock"
+ sleep 15
+done
\ No newline at end of file