feat: codex qol updates (#348)

## Description

- Removed variables for hardcoded configuration options, and replaced
with variables for base config, and additional mcp servers.
- Set module defaults so that this will run with minimal module
configuration for tasks, while allowing further configuration if needed
by the user for codex through the base configuration.
- Updated tests for expected responses and new configuration options.
- Move all codex related files outside of project folder.
<!-- Briefly describe what this PR does and why -->

## Type of Change

- [ ] New module
- [ ] Bug fix
- [X] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

<!-- Delete this section if not applicable -->

**Path:** `registry/coder-labs/modules/codex`  
**New version:** `v1.1.0`  
**Breaking change:** [X] Yes [ ] No

## Testing & Validation

- [X] Tests pass (`bun test`)
- [X] Code formatted (`bun run fmt`)
- [X] Changes tested locally

## Related Issues

<!-- Link related issues or write "None" if not applicable -->

---------

Co-authored-by: Atif Ali <atif@coder.com>
This commit is contained in:
DevCats 2025-08-21 10:56:09 -05:00 committed by GitHub
parent eca289be3a
commit d6ae51fad0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 250 additions and 214 deletions

View File

@ -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
## Configuration
The module automatically configures Codex with a secure sandbox that allows AI tasks to work effectively:
### Default Configuration
- **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
When no custom `base_config_toml` is provided, the module uses these secure defaults:
### Customizing Sandbox Behavior
```toml
sandbox_mode = "workspace-write"
approval_policy = "never"
preferred_auth_method = "apikey"
You can customize the sandbox behavior using dedicated variables:
#### **Using Dedicated Variables (Recommended)**
For most use cases, use the dedicated sandbox variables:
```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)

View File

@ -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_extensions: additional,
additional_mcp_servers: 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.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: {
base_config_toml: baseConfig,
additional_mcp_servers: additionalMCP,
},
});
await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
// 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");

View File

@ -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
}

View File

@ -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}"
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"
# Allow network access in workspace-write mode for package installation, API calls, etc.
[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}
${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

View File

@ -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[@]}"