@@ -126,7 +126,7 @@ def start_task():
126126 save_tasks ()
127127
128128 # Start task in background thread
129- thread = threading .Thread (target = run_claude_code_task , args = (task_id ,))
129+ thread = threading .Thread (target = run_ai_code_task , args = (task_id ,))
130130 thread .daemon = True
131131 thread .start ()
132132
@@ -549,33 +549,49 @@ def apply_diff_to_content(original_content, diff_lines, filename):
549549 logger .error (f"❌ Error applying diff to { filename } : { str (e )} " )
550550 return None
551551
552- def run_claude_code_task (task_id ):
553- """Run Claude Code automation in a container"""
552+ def run_ai_code_task (task_id ):
553+ """Run AI Code automation (Claude or Codex) in a container"""
554554 try :
555555 task = tasks [task_id ]
556556 task ['status' ] = TaskStatus .RUNNING
557557
558- logger .info (f"🚀 Starting Claude Code task { task_id } " )
559- logger .info (f"📋 Task details: prompt='{ task ['prompt' ][:50 ]} ...', repo={ task ['repo_url' ]} , branch={ task ['branch' ]} " )
560- logger .info (f"Starting { task .get ('model' , 'claude' ).upper ()} task { task_id } " )
558+ model_name = task .get ('model' , 'claude' ).upper ()
559+ logger .info (f"🚀 Starting { model_name } Code task { task_id } " )
560+ logger .info (f"📋 Task details: prompt='{ task ['prompt' ][:50 ]} ...', repo={ task ['repo_url' ]} , branch={ task ['branch' ]} , model={ model_name } " )
561+ logger .info (f"Starting { model_name } task { task_id } " )
561562
562563 # Escape special characters in prompt for shell safety
563564 escaped_prompt = task ['prompt' ].replace ('"' , '\\ "' ).replace ('$' , '\\ $' ).replace ('`' , '\\ `' )
564565
565566 # Create container environment variables
566567 env_vars = {
567- 'ANTHROPIC_API_KEY' : os .getenv ('ANTHROPIC_API_KEY' ),
568568 'CI' : 'true' , # Indicate we're in CI/non-interactive environment
569569 'TERM' : 'dumb' , # Use dumb terminal to avoid interactive features
570570 'NO_COLOR' : '1' , # Disable colors for cleaner output
571571 'FORCE_COLOR' : '0' , # Disable colors for cleaner output
572572 'NONINTERACTIVE' : '1' , # Common flag for non-interactive mode
573573 'DEBIAN_FRONTEND' : 'noninteractive' , # Non-interactive package installs
574- 'ANTHROPIC_NONINTERACTIVE' : '1' # Custom flag for Anthropic tools
575574 }
576575
577- # Determine which CLI to use
576+ # Add model-specific API keys and environment variables
578577 model_cli = task .get ('model' , 'claude' )
578+ if model_cli == 'claude' :
579+ env_vars .update ({
580+ 'ANTHROPIC_API_KEY' : os .getenv ('ANTHROPIC_API_KEY' ),
581+ 'ANTHROPIC_NONINTERACTIVE' : '1' # Custom flag for Anthropic tools
582+ })
583+ elif model_cli == 'codex' :
584+ env_vars .update ({
585+ 'OPENAI_API_KEY' : os .getenv ('OPENAI_API_KEY' ),
586+ 'OPENAI_NONINTERACTIVE' : '1' , # Custom flag for OpenAI tools
587+ 'CODEX_QUIET_MODE' : '1' # Official Codex non-interactive flag
588+ })
589+
590+ # Use specialized container images based on model
591+ if model_cli == 'codex' :
592+ container_image = 'codex-automation:latest'
593+ else :
594+ container_image = 'claude-code-automation:latest'
579595
580596 # Create the command to run in container
581597 container_command = f'''
@@ -593,13 +609,63 @@ def run_claude_code_task(task_id):
593609# We'll extract the patch instead of pushing directly
594610echo "📋 Will extract changes as patch for later PR creation..."
595611
596- echo "Starting Claude Code with prompt..."
612+ echo "Starting { model_cli . upper () } Code with prompt..."
597613
598614# Create a temporary file with the prompt
599615echo "{ escaped_prompt } " > /tmp/prompt.txt
600616
601- # Try different ways to invoke claude
602- echo "Checking claude installation..."
617+ # Check which CLI tool to use based on model selection
618+ if [ "{ model_cli } " = "codex" ]; then
619+ echo "Using Codex (OpenAI Codex) CLI..."
620+
621+ # Set environment variables for non-interactive mode
622+ export CODEX_QUIET_MODE=1
623+
624+ # Read the prompt from file
625+ PROMPT_TEXT=$(cat /tmp/prompt.txt)
626+
627+ # Check for codex installation
628+ if [ -f /usr/local/bin/codex ]; then
629+ echo "Found codex at /usr/local/bin/codex"
630+ echo "Running Codex in non-interactive mode..."
631+
632+ # Use non-interactive flags for Docker environment
633+ # --dangerously-auto-approve-everything is required when running in Docker
634+ /usr/local/bin/codex --quiet --approval-mode full-auto --dangerously-auto-approve-everything "$PROMPT_TEXT"
635+ CODEX_EXIT_CODE=$?
636+ echo "Codex finished with exit code: $CODEX_EXIT_CODE"
637+
638+ if [ $CODEX_EXIT_CODE -ne 0 ]; then
639+ echo "ERROR: Codex failed with exit code $CODEX_EXIT_CODE"
640+ exit $CODEX_EXIT_CODE
641+ fi
642+
643+ echo "✅ Codex completed successfully"
644+ elif command -v codex >/dev/null 2>&1; then
645+ echo "Using codex from PATH..."
646+ echo "Running Codex in non-interactive mode..."
647+
648+ # Use non-interactive flags for Docker environment
649+ # --dangerously-auto-approve-everything is required when running in Docker
650+ codex --quiet --approval-mode full-auto --dangerously-auto-approve-everything "$PROMPT_TEXT"
651+ CODEX_EXIT_CODE=$?
652+ echo "Codex finished with exit code: $CODEX_EXIT_CODE"
653+ if [ $CODEX_EXIT_CODE -ne 0 ]; then
654+ echo "ERROR: Codex failed with exit code $CODEX_EXIT_CODE"
655+ exit $CODEX_EXIT_CODE
656+ fi
657+ echo "✅ Codex completed successfully"
658+ else
659+ echo "ERROR: codex command not found anywhere"
660+ echo "Please ensure Codex CLI is installed in the container"
661+ exit 1
662+ fi
663+
664+ else
665+ echo "Using Claude CLI..."
666+
667+ # Try different ways to invoke claude
668+ echo "Checking claude installation..."
603669
604670if [ -f /usr/local/bin/claude ]; then
605671 echo "Found claude at /usr/local/bin/claude"
@@ -704,6 +770,8 @@ def run_claude_code_task(task_id):
704770 exit 1
705771fi
706772
773+ fi # End of model selection (claude vs codex)
774+
707775# Check if there are changes
708776if git diff --quiet; then
709777 echo "No changes made"
@@ -740,10 +808,10 @@ def run_claude_code_task(task_id):
740808exit 0
741809'''
742810
743- # Run container with Claude Code
744- logger .info (f"🐳 Creating Docker container for task { task_id } " )
811+ # Run container with unified AI Code tools (supports both Claude and Codex)
812+ logger .info (f"🐳 Creating Docker container for task { task_id } using { container_image } (model: { model_name } ) " )
745813 container = docker_client .containers .run (
746- 'claude-code-automation:latest' ,
814+ container_image ,
747815 command = ['bash' , '-c' , container_command ],
748816 environment = env_vars ,
749817 detach = True ,
@@ -872,20 +940,21 @@ def run_claude_code_task(task_id):
872940 # Save tasks after completion
873941 save_tasks ()
874942
875- logger .info (f"🎉 Task { task_id } completed successfully! Commit: { commit_hash [:8 ] if commit_hash else 'N/A' } , Diff lines: { len (git_diff )} " )
943+ logger .info (f"🎉 { model_name } Task { task_id } completed successfully! Commit: { commit_hash [:8 ] if commit_hash else 'N/A' } , Diff lines: { len (git_diff )} " )
876944
877945 else :
878946 logger .error (f"❌ Container exited with error code { result ['StatusCode' ]} " )
879947 task ['status' ] = TaskStatus .FAILED
880948 task ['error' ] = f"Container exited with code { result ['StatusCode' ]} : { logs } "
881949 save_tasks () # Save failed task
882- logger .error (f"💥 Task { task_id } failed: { task ['error' ][:200 ]} ..." )
950+ logger .error (f"💥 { model_name } Task { task_id } failed: { task ['error' ][:200 ]} ..." )
883951
884952 except Exception as e :
885- logger .error (f"💥 Unexpected exception in task { task_id } : { str (e )} " )
953+ model_name = task .get ('model' , 'claude' ).upper ()
954+ logger .error (f"💥 Unexpected exception in { model_name } task { task_id } : { str (e )} " )
886955 task ['status' ] = TaskStatus .FAILED
887956 task ['error' ] = str (e )
888- logger .error (f"🔄 Task { task_id } failed with exception: { str (e )} " )
957+ logger .error (f"🔄 { model_name } Task { task_id } failed with exception: { str (e )} " )
889958
890959if __name__ == '__main__' :
891960 app .run (debug = True , host = '0.0.0.0' , port = 5000 )
0 commit comments