Skip to content

Commit f638f5a

Browse files
fix: adapt codex command
1 parent 6945abc commit f638f5a

File tree

4 files changed

+137
-23
lines changed

4 files changed

+137
-23
lines changed

Dockerfile.claude-automation

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ RUN apk add --no-cache \
1212
# Install Claude Code globally
1313
RUN npm install -g @anthropic-ai/claude-code
1414

15-
# Install Codex CLI globally
16-
# Based on the GitHub repo, Codex is available as a npm package
17-
RUN npm install -g codex
18-
1915
# Create workspace directory
2016
WORKDIR /workspace
2117

Dockerfile.codex-automation

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
FROM node:22-alpine
2+
3+
# Install system dependencies
4+
RUN apk add --no-cache \
5+
bash \
6+
git \
7+
curl \
8+
openssh-client \
9+
python3 \
10+
py3-pip
11+
12+
# Install Codex CLI globally
13+
# Requires Node.js 22+ according to their documentation
14+
RUN npm install -g @openai/codex
15+
16+
# Create workspace directory
17+
WORKDIR /workspace
18+
19+
# Configure git (default values, will be overridden)
20+
RUN git config --global user.email "[email protected]" && \
21+
git config --global user.name "Codex Code Automation" && \
22+
git config --global init.defaultBranch main
23+
24+
# Set up environment
25+
ENV NODE_ENV=production
26+
27+
# Default command
28+
CMD ["bash"]

async-code-web/app/page.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,27 @@ export default function Home() {
8787
? 'http://localhost:5000'
8888
: '/api';
8989

90+
// Initialize GitHub token from localStorage
91+
useEffect(() => {
92+
if (typeof window !== 'undefined') {
93+
const savedToken = localStorage.getItem('github-token');
94+
if (savedToken) {
95+
setGithubToken(savedToken);
96+
}
97+
}
98+
}, []);
99+
100+
// Save GitHub token to localStorage whenever it changes
101+
useEffect(() => {
102+
if (typeof window !== 'undefined') {
103+
if (githubToken.trim()) {
104+
localStorage.setItem('github-token', githubToken);
105+
} else {
106+
localStorage.removeItem('github-token');
107+
}
108+
}
109+
}, [githubToken]);
110+
90111
// Poll task status
91112
useEffect(() => {
92113
if (currentTask && (currentTask.status === "running" || currentTask.status === "pending")) {

server/main.py

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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
594610
echo "📋 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
599615
echo "{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
604670
if [ -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
705771
fi
706772
773+
fi # End of model selection (claude vs codex)
774+
707775
# Check if there are changes
708776
if git diff --quiet; then
709777
echo "No changes made"
@@ -740,10 +808,10 @@ def run_claude_code_task(task_id):
740808
exit 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

890959
if __name__ == '__main__':
891960
app.run(debug=True, host='0.0.0.0', port=5000)

0 commit comments

Comments
 (0)