Files
codex-crew/scripts/codex-run.sh
T

75 lines
2.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# codex-crew: deterministic wrapper around `codex exec` with stall watchdog.
#
# Usage:
# codex-run.sh <job_dir> exec [codex-flags...] [prompt]
# codex-run.sh <job_dir> resume <thread_id> [codex-flags...] [prompt]
# codex-run.sh <job_dir> review [--base X|--commit Y|--uncommitted] [codex-flags...] [prompt]
#
# If <job_dir>/prompt.md exists and no positional prompt given, it is fed via stdin.
# Review runs against the git repo at the CURRENT WORKING DIRECTORY (no -C support).
#
# Files written to <job_dir>: events.jsonl last.md err.log thread_id status
# status: ok | stalled | error: <reason>. Exit code 0 only on ok.
#
# Env knobs:
# CODEX_STALL_TIMEOUT seconds of event silence before kill (default 600)
# CODEX_WATCH_INTERVAL watchdog poll interval seconds (default 15)
# CODEX_BIN codex binary (default codex; tests use a stub)
set -u
JOB_DIR=$1; MODE=$2; shift 2
mkdir -p "$JOB_DIR"
JOB_DIR=$(cd "$JOB_DIR" && pwd)
EV=$JOB_DIR/events.jsonl LAST=$JOB_DIR/last.md ERR=$JOB_DIR/err.log
STALL=${CODEX_STALL_TIMEOUT:-600}
TICK=${CODEX_WATCH_INTERVAL:-15}
BIN=${CODEX_BIN:-codex}
case $MODE in
exec) CMD=("$BIN" exec --json --skip-git-repo-check -o "$LAST") ;;
resume) TID=$1; shift; printf '%s\n' "$TID" > "$JOB_DIR/thread_id"
CMD=("$BIN" exec resume "$TID" --json --skip-git-repo-check -o "$LAST") ;;
review) CMD=("$BIN" exec review --json -o "$LAST") ;;
*) echo "usage: codex-run.sh <job_dir> exec|resume|review ..." >&2; exit 2 ;;
esac
rm -f "$JOB_DIR/status" "$LAST"; : > "$EV"; : > "$ERR"
if [[ $# -eq 0 && -s $JOB_DIR/prompt.md ]]; then
"${CMD[@]}" - < "$JOB_DIR/prompt.md" > "$EV" 2> "$ERR" &
else
"${CMD[@]}" "$@" < /dev/null > "$EV" 2> "$ERR" &
fi
PID=$!
( while kill -0 "$PID" 2>/dev/null; do
sleep "$TICK"
now=$(date +%s); mt=$(stat -c %Y "$EV" 2>/dev/null || echo "$now")
if (( now - mt > STALL )); then
echo stalled > "$JOB_DIR/status"
kill "$PID" 2>/dev/null; sleep 3; kill -9 "$PID" 2>/dev/null
break
fi
done ) &
WATCH=$!
wait "$PID"; RC=$?
kill "$WATCH" 2>/dev/null; wait "$WATCH" 2>/dev/null
TID_FOUND=$(grep -m1 -o '"thread_id":"[^"]*"' "$EV" | cut -d'"' -f4)
[[ -n $TID_FOUND ]] && printf '%s\n' "$TID_FOUND" > "$JOB_DIR/thread_id"
if [[ -s $JOB_DIR/status ]]; then
: # stalled, written by watchdog
elif [[ $RC -eq 0 && -s $LAST ]]; then
echo ok > "$JOB_DIR/status"
else
echo "error: exit=$RC $(tail -c 300 "$ERR" | tr '\n' ' ')" > "$JOB_DIR/status"
fi
ST=$(head -n1 "$JOB_DIR/status")
echo "status: $ST"
echo "thread_id: $(cat "$JOB_DIR/thread_id" 2>/dev/null || echo none)"
[[ $ST == ok ]]