diff --git a/registry/coder-labs/modules/codex/README.md b/registry/coder-labs/modules/codex/README.md index a0cafe267..62c63a328 100644 --- a/registry/coder-labs/modules/codex/README.md +++ b/registry/coder-labs/modules/codex/README.md @@ -13,7 +13,7 @@ Run Codex CLI in your workspace to access OpenAI's models through the Codex inte ```tf module "codex" { source = "registry.coder.com/coder-labs/codex/coder" - version = "1.0.1" + version = "2.0.0" agent_id = coder_agent.example.id openai_api_key = var.openai_api_key folder = "/home/coder/project" @@ -25,29 +25,24 @@ module "codex" { - You must add the [Coder Login](https://registry.coder.com/modules/coder/coder-login) module to your template - OpenAI API key for Codex access -## Usage Example +## Examples -- Simple usage Example: +### Run standalone ```tf module "codex" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder-labs/codex/coder" - version = "1.0.1" - 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:`" + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder-labs/codex/coder" + version = "2.0.0" + agent_id = coder_agent.example.id + openai_api_key = "..." + folder = "/home/coder/project" } ``` -- Example usage with Tasks: +### Tasks integration ```tf -# This data "coder_parameter" "ai_prompt" { type = "string" name = "AI Prompt" @@ -64,79 +59,75 @@ module "coder-login" { } 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 - folder = "/home/coder/project" - approval_policy = "never" # Full auto mode + source = "registry.coder.com/coder-labs/codex/coder" + version = "2.0.0" + agent_id = coder_agent.example.id + openai_api_key = "..." + ai_prompt = data.coder_parameter.ai_prompt.value + folder = "/home/coder/project" + + # Custom configuration for full auto mode + base_config_toml = <<-EOT + approval_policy = "never" + preferred_auth_method = "apikey" + EOT } ``` > [!WARNING] -> **Security Notice**: This module configures Codex with a `workspace-write` sandbox that allows AI tasks to read/write files in the specified folder. While the sandbox provides security boundaries, Codex can still modify files within the workspace. Use this module in trusted environments and be aware of the security implications. +> This module configures Codex with a `workspace-write` sandbox that allows AI tasks to read/write files in the specified folder. While the sandbox provides security boundaries, Codex can still modify files within the workspace. 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` +- **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) -## Sandbox Configuration - -The module automatically configures Codex with a secure sandbox that allows AI tasks to work effectively: - -- **Sandbox Mode**: `workspace-write` - Allows Codex to read/write files in the specified `folder` -- **Approval Policy**: `on-request` - Codex asks for permission before performing potentially risky operations -- **Network Access**: Enabled within the workspace for package installation and API calls - -### Customizing Sandbox Behavior +## Configuration -You can customize the sandbox behavior using dedicated variables: +### Default Configuration -#### **Using Dedicated Variables (Recommended)** +When no custom `base_config_toml` is provided, the module uses these secure defaults: -For most use cases, use the dedicated sandbox variables: +```toml +sandbox_mode = "workspace-write" +approval_policy = "never" +preferred_auth_method = "apikey" -```tf -module "codex" { - source = "registry.coder.com/coder-labs/codex/coder" - # ... other variables ... - - # Containerized environments (fixes Landlock errors) - sandbox_mode = "danger-full-access" - - # Or for read-only mode - # sandbox_mode = "read-only" - - # Or for full auto mode - # approval_policy = "never" - - # Or disable network access - # network_access = false -} +[sandbox_workspace_write] +network_access = true ``` -#### **Using extra_codex_settings_toml (Advanced)** +### Custom Configuration -For advanced configuration or when you need to override multiple settings: +For custom Codex configuration, use `base_config_toml` and/or `additional_mcp_servers`: ```tf module "codex" { - source = "registry.coder.com/coder-labs/codex/coder" + source = "registry.coder.com/coder-labs/codex/coder" + version = "2.0.0" # ... other variables ... - extra_codex_settings_toml = <<-EOT - # Any custom Codex configuration - model = "gpt-4" - disable_response_storage = true + # Override default configuration + base_config_toml = <<-EOT + sandbox_mode = "danger-full-access" + approval_policy = "never" + preferred_auth_method = "apikey" + EOT + + # Add extra MCP servers + additional_mcp_servers = <<-EOT + [mcp_servers.GitHub] + command = "npx" + args = ["-y", "@modelcontextprotocol/server-github"] + type = "stdio" EOT } ``` > [!NOTE] -> The dedicated variables (`sandbox_mode`, `approval_policy`, `network_access`) are the recommended way to configure sandbox behavior. Use `extra_codex_settings_toml` only for advanced configuration that isn't covered by the dedicated variables. +> If no custom configuration is provided, the module uses secure defaults. The Coder MCP server is always included automatically. For containerized workspaces (Docker/Kubernetes), you may need `sandbox_mode = "danger-full-access"` to avoid permission issues. For advanced options, see [Codex config docs](https://github.com/openai/codex/blob/main/codex-rs/config.md). ## Troubleshooting @@ -150,6 +141,6 @@ module "codex" { ## References -- [OpenAI API Documentation](https://platform.openai.com/docs) +- [Codex CLI Documentation](https://github.com/openai/codex) - [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 index ec2d9dbed..55d0b879e 100644 --- a/registry/coder-labs/modules/codex/main.test.ts +++ b/registry/coder-labs/modules/codex/main.test.ts @@ -108,24 +108,25 @@ describe("codex", async () => { await expectAgentAPIStarted(id); }); - 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" + test("base-config-toml", async () => { + const baseConfig = dedent` + sandbox_mode = "danger-full-access" + approval_policy = "never" + preferred_auth_method = "apikey" + + [custom_section] + new_feature = true `.trim(); const { id } = await setup({ moduleVariables: { - extra_codex_settings_toml: settings, + base_config_toml: baseConfig, }, }); await execModuleScript(id); const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); - expect(resp).toContain("[mcp_servers.CustomMCP]"); + expect(resp).toContain("sandbox_mode = \"danger-full-access\""); + expect(resp).toContain("preferred_auth_method = \"apikey\""); + expect(resp).toContain("[custom_section]"); expect(resp).toContain("[mcp_servers.Coder]"); }); @@ -142,7 +143,7 @@ describe("codex", async () => { id, "/home/coder/.codex-module/agentapi-start.log", ); - expect(resp).toContain("openai_api_key provided !"); + expect(resp).toContain("OpenAI API Key: Provided"); }); test("pre-post-install-scripts", async () => { @@ -181,25 +182,106 @@ describe("codex", async () => { expect(resp).toContain(folder); }); - test("additional-extensions", async () => { + test("additional-mcp-servers", 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 + [mcp_servers.GitHub] + command = "npx" + args = ["-y", "@modelcontextprotocol/server-github"] + type = "stdio" + description = "GitHub integration" + + [mcp_servers.FileSystem] + command = "npx" + args = ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"] + type = "stdio" + description = "File system access" + `.trim(); + const { id } = await setup({ + moduleVariables: { + additional_mcp_servers: additional, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); + expect(resp).toContain("[mcp_servers.GitHub]"); + expect(resp).toContain("[mcp_servers.FileSystem]"); + expect(resp).toContain("[mcp_servers.Coder]"); + expect(resp).toContain("GitHub integration"); + }); + + test("full-custom-config", async () => { + const baseConfig = dedent` + sandbox_mode = "read-only" + approval_policy = "untrusted" + preferred_auth_method = "chatgpt" + custom_setting = "test-value" + + [advanced_settings] + timeout = 30000 + debug = true + logging_level = "verbose" + `.trim(); + + const additionalMCP = dedent` + [mcp_servers.CustomTool] + command = "/usr/local/bin/custom-tool" + args = ["--serve", "--port", "8080"] + type = "stdio" + description = "Custom development tool" + + [mcp_servers.DatabaseMCP] + command = "python" + args = ["-m", "database_mcp_server"] type = "stdio" + description = "Database query interface" `.trim(); + const { id } = await setup({ moduleVariables: { - additional_extensions: additional, + base_config_toml: baseConfig, + additional_mcp_servers: additionalMCP, }, }); await execModuleScript(id); const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); - expect(resp).toContain("[mcp_servers.CustomMCP]"); + + // Check base config + expect(resp).toContain("sandbox_mode = \"read-only\""); + expect(resp).toContain("preferred_auth_method = \"chatgpt\""); + expect(resp).toContain("custom_setting = \"test-value\""); + expect(resp).toContain("[advanced_settings]"); + expect(resp).toContain("logging_level = \"verbose\""); + + // Check MCP servers expect(resp).toContain("[mcp_servers.Coder]"); + expect(resp).toContain("[mcp_servers.CustomTool]"); + expect(resp).toContain("[mcp_servers.DatabaseMCP]"); + expect(resp).toContain("Custom development tool"); + expect(resp).toContain("Database query interface"); + }); + + test("minimal-default-config", async () => { + const { id } = await setup({ + moduleVariables: { + // No base_config_toml or additional_mcp_servers - should use defaults + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); + + // Check default base config + expect(resp).toContain("sandbox_mode = \"workspace-write\""); + expect(resp).toContain("approval_policy = \"never\""); + expect(resp).toContain("[sandbox_workspace_write]"); + expect(resp).toContain("network_access = true"); + + // Check only Coder MCP server is present + expect(resp).toContain("[mcp_servers.Coder]"); + expect(resp).toContain("Report ALL tasks and statuses"); + + // Ensure no additional MCP servers + const mcpServerCount = (resp.match(/\[mcp_servers\./g) || []).length; + expect(mcpServerCount).toBe(1); }); test("codex-system-prompt", async () => { @@ -210,7 +292,7 @@ describe("codex", async () => { }, }); await execModuleScript(id); - const resp = await readFileContainer(id, "/home/coder/AGENTS.md"); + const resp = await readFileContainer(id, "/home/coder/.codex/AGENTS.md"); expect(resp).toContain(prompt); }); @@ -223,7 +305,8 @@ describe("codex", async () => { `.trim(); const pre_install_script = dedent` #!/bin/bash - echo -e "${prompt_3}" >> /home/coder/AGENTS.md + mkdir -p /home/coder/.codex + echo -e "${prompt_3}" >> /home/coder/.codex/AGENTS.md `.trim(); const { id } = await setup({ @@ -233,7 +316,7 @@ describe("codex", async () => { }, }); await execModuleScript(id); - const resp = await readFileContainer(id, "/home/coder/AGENTS.md"); + const resp = await readFileContainer(id, "/home/coder/.codex/AGENTS.md"); expect(resp).toContain(prompt_1); expect(resp).toContain(prompt_2); @@ -245,7 +328,7 @@ describe("codex", async () => { }, }); await execModuleScript(id_2); - const resp_2 = await readFileContainer(id_2, "/home/coder/AGENTS.md"); + const resp_2 = await readFileContainer(id_2, "/home/coder/.codex/AGENTS.md"); expect(resp_2).toContain(prompt_1); const count = (resp_2.match(new RegExp(prompt_1, "g")) || []).length; expect(count).toBe(1); @@ -268,12 +351,16 @@ describe("codex", async () => { }); test("start-without-prompt", async () => { - const { id } = await setup(); + const { id } = await setup({ + moduleVariables: { + codex_system_prompt: "", // Explicitly disable system prompt + }, + }); await execModuleScript(id); const prompt = await execContainer(id, [ "ls", "-l", - "/home/coder/AGENTS.md", + "/home/coder/.codex/AGENTS.md", ]); expect(prompt.exitCode).not.toBe(0); expect(prompt.stderr).toContain("No such file or directory"); diff --git a/registry/coder-labs/modules/codex/main.tf b/registry/coder-labs/modules/codex/main.tf index d17d48910..9fdec4011 100644 --- a/registry/coder-labs/modules/codex/main.tf +++ b/registry/coder-labs/modules/codex/main.tf @@ -53,41 +53,21 @@ variable "codex_version" { default = "" # empty string means the latest available version } -variable "extra_codex_settings_toml" { +variable "base_config_toml" { type = string - description = "Settings to append to ~/.codex/config.toml." + description = "Complete base TOML configuration for Codex (without mcp_servers section). If empty, uses minimal default configuration with workspace-write sandbox mode and never approval policy. For advanced options, see https://github.com/openai/codex/blob/main/codex-rs/config.md" default = "" } -variable "sandbox_mode" { +variable "additional_mcp_servers" { type = string - description = "The sandbox mode for Codex. Options: workspace-write, read-only, danger-full-access." - default = "workspace-write" - validation { - condition = contains(["workspace-write", "read-only", "danger-full-access"], var.sandbox_mode) - error_message = "sandbox_mode must be one of: workspace-write, read-only, danger-full-access." - } -} - -variable "approval_policy" { - type = string - description = "The approval policy for Codex. Options: on-request, never, untrusted." - default = "on-request" - validation { - condition = contains(["on-request", "never", "untrusted"], var.approval_policy) - error_message = "approval_policy must be one of: on-request, never, untrusted." - } -} - -variable "network_access" { - type = bool - description = "Whether to allow network access in workspace-write mode." - default = true + description = "Additional MCP servers configuration in TOML format. These will be merged with the required Coder MCP server in the [mcp_servers] section." + default = "" } variable "openai_api_key" { type = string - description = "Codex API Key" + description = "OpenAI API key for Codex CLI" default = "" } @@ -105,7 +85,7 @@ variable "agentapi_version" { variable "codex_model" { type = string - description = "The model for Codex to use (e.g., o4-mini)." + description = "The model for Codex to use. Defaults to gpt-5." default = "" } @@ -123,24 +103,16 @@ variable "post_install_script" { 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." + description = "Initial task prompt for Codex CLI when launched via Tasks" default = "" } variable "codex_system_prompt" { type = string - description = "System prompt for Codex. It will be added to AGENTS.md in the specified folder." - default = "" + description = "System instructions written to AGENTS.md in the ~/.codex directory" + default = "You are a helpful coding assistant. Start every response with `Codex says:`" } - - resource "coder_env" "openai_api_key" { agent_id = var.agent_id name = "OPENAI_API_KEY" @@ -194,14 +166,11 @@ module "agentapi" { 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_BASE_CONFIG_TOML='${base64encode(var.base_config_toml)}' \ + ARG_ADDITIONAL_MCP_SERVERS='${base64encode(var.additional_mcp_servers)}' \ 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)}' \ - ARG_SANDBOX_MODE='${var.sandbox_mode}' \ - ARG_APPROVAL_POLICY='${var.approval_policy}' \ - ARG_NETWORK_ACCESS='${var.network_access}' \ /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 index d06dd4fd8..d725f6108 100644 --- a/registry/coder-labs/modules/codex/scripts/install.sh +++ b/registry/coder-labs/modules/codex/scripts/install.sh @@ -3,7 +3,6 @@ source "$HOME"/.bashrc BOLD='\033[0;1m' -# Function to check if a command exists command_exists() { command -v "$1" > /dev/null 2>&1 } @@ -11,25 +10,23 @@ set -o errexit set -o pipefail 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_BASE_CONFIG_TOML=$(echo -n "$ARG_BASE_CONFIG_TOML" | base64 -d) +ARG_ADDITIONAL_MCP_SERVERS=$(echo -n "$ARG_ADDITIONAL_MCP_SERVERS" | 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 "--------------------------------" +echo "=== Codex Module Configuration ===" +printf "Install Codex: %s\n" "$ARG_INSTALL" +printf "Codex Version: %s\n" "$ARG_CODEX_VERSION" +printf "App Slug: %s\n" "$ARG_CODER_MCP_APP_STATUS_SLUG" +printf "Start Directory: %s\n" "$ARG_CODEX_START_DIRECTORY" +printf "Has Base Config: %s\n" "$([ -n "$ARG_BASE_CONFIG_TOML" ] && echo "Yes" || echo "No")" +printf "Has Additional MCP: %s\n" "$([ -n "$ARG_ADDITIONAL_MCP_SERVERS" ] && echo "Yes" || echo "No")" +printf "Has System Prompt: %s\n" "$([ -n "$ARG_CODEX_INSTRUCTION_PROMPT" ] && echo "Yes" || echo "No")" +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 @@ -58,24 +55,18 @@ function install_node() { 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 @@ -92,79 +83,82 @@ function install_codex() { fi } -function populate_config_toml() { - CONFIG_PATH="$HOME/.codex/config.toml" - mkdir -p "$(dirname "$CONFIG_PATH")" - printf "Custom codex_config is provided !\n" - BASE_SANDBOX_CONFIG=$( - cat << EOF -# Base sandbox configuration for Codex workspace access -# This ensures Codex can read/write files in the specified folder for AI tasks -sandbox_mode = "${ARG_SANDBOX_MODE}" -approval_policy = "${ARG_APPROVAL_POLICY}" - -# Allow network access in workspace-write mode for package installation, API calls, etc. +write_minimal_default_config() { + local config_path="$1" + cat << EOF > "$config_path" +# Minimal Default Codex Configuration +sandbox_mode = "workspace-write" +approval_policy = "never" +preferred_auth_method = "apikey" + [sandbox_workspace_write] -network_access = ${ARG_NETWORK_ACCESS} +network_access = true + EOF - ) - - BASE_EXTENSIONS=$( - cat << EOF +} + +append_mcp_servers_section() { + local config_path="$1" + + cat << EOF >> "$config_path" + +# MCP Servers Configuration [mcp_servers.Coder] command = "coder" args = ["exp", "mcp", "server"] -env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL"= "http://localhost:3284", "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}" } +env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL" = "http://localhost:3284", "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}" } description = "Report ALL tasks and statuses (in progress, done, failed) you are working on." type = "stdio" -EOF - ) - - echo " -${BASE_SANDBOX_CONFIG} - -${ARG_EXTRA_CODEX_CONFIG} -${BASE_EXTENSIONS} +EOF -${ARG_ADDITIONAL_EXTENSIONS} - " > "$HOME/.codex/config.toml" + if [ -n "$ARG_ADDITIONAL_MCP_SERVERS" ]; then + printf "Adding additional MCP servers\n" + echo "$ARG_ADDITIONAL_MCP_SERVERS" >> "$config_path" + fi +} +function populate_config_toml() { + CONFIG_PATH="$HOME/.codex/config.toml" + mkdir -p "$(dirname "$CONFIG_PATH")" + + if [ -n "$ARG_BASE_CONFIG_TOML" ]; then + printf "Using provided base configuration\n" + echo "$ARG_BASE_CONFIG_TOML" > "$CONFIG_PATH" + else + printf "Using minimal default configuration\n" + write_minimal_default_config "$CONFIG_PATH" + fi + + append_mcp_servers_section "$CONFIG_PATH" } 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 - } + AGENTS_PATH="$HOME/.codex/AGENTS.md" + printf "Creating AGENTS.md in .codex directory: %s\\n" "${AGENTS_PATH}" + + mkdir -p "$HOME/.codex" + + if [ -f "${AGENTS_PATH}" ] && grep -Fq "${ARG_CODEX_INSTRUCTION_PROMPT}" "${AGENTS_PATH}"; then + printf "AGENTS.md already contains the instruction prompt. Skipping append.\n" else - printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${ARG_CODEX_START_DIRECTORY}" + printf "Appending instruction prompt to AGENTS.md in .codex directory\n" + echo -e "\n${ARG_CODEX_INSTRUCTION_PROMPT}" >> "${AGENTS_PATH}" + fi + + if [ ! -d "${ARG_CODEX_START_DIRECTORY}" ]; then + printf "Creating start directory '%s'\\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" + printf "AGENTS.md instruction prompt is not set.\n" fi } -# Install Codex install_codex codex --version populate_config_toml diff --git a/registry/coder-labs/modules/codex/scripts/start.sh b/registry/coder-labs/modules/codex/scripts/start.sh index ea3449c18..29a8d741c 100644 --- a/registry/coder-labs/modules/codex/scripts/start.sh +++ b/registry/coder-labs/modules/codex/scripts/start.sh @@ -1,6 +1,5 @@ #!/bin/bash -# Load shell environment source "$HOME"/.bashrc set -o errexit set -o pipefail @@ -18,12 +17,12 @@ printf "Version: %s\n" "$(codex --version)" set -o nounset 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 "--------------------------------" +echo "=== Codex Launch Configuration ===" +printf "OpenAI API Key: %s\n" "$([ -n "$ARG_OPENAI_API_KEY" ] && echo "Provided" || echo "Not provided")" +printf "Codex Model: %s\n" "${ARG_CODEX_MODEL:-"Default"}" +printf "Start Directory: %s\n" "$ARG_CODEX_START_DIRECTORY" +printf "Has Task Prompt: %s\n" "$([ -n "$ARG_CODEX_TASK_PROMPT" ] && echo "Yes" || echo "No")" +echo "======================================" set +o nounset CODEX_ARGS=() @@ -66,13 +65,9 @@ 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, see: https://github.com/openai/codex/issues/1608 -printf "Starting codex with %s\n" "${CODEX_ARGS[@]}" +# Terminal dimensions optimized for Coder Tasks UI sidebar: +# - Width 67: fits comfortably in sidebar +# - Height 1190: adjusted due to Codex terminal height bug +printf "Starting Codex with arguments: %s\n" "${CODEX_ARGS[*]}" agentapi server --term-width 67 --term-height 1190 -- codex "${CODEX_ARGS[@]}"