#!/bin/bash # codex with the `viggle-remake` MCP (examples/codex_config.toml: pool image mode by default). Extra args go to codex, e.g. # scripts/codex_exec.sh interactive # scripts/codex_exec.sh exec "swap the singer in /videos/x.mp4 ..." non-interactive, resumed after "at capacity" # Settings (environment): # REMAKE_HOME sessions, queue, workers (default ~/.viggle-remake); must match the workers' # REMAKE_CODEX_DIR codex's home and workspace (default $REMAKE_HOME/codex). Put it on a local disk: codex's sqlite # logs break on network filesystems # REMAKE_CODEX_AUTH a codex login to copy in (default ~/.codex/auth.json; skipped if missing - then log in once # with CODEX_HOME=$REMAKE_CODEX_DIR/codex_home codex login); pool helpers read the same login ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) REMAKE_HOME=$(realpath -m "${REMAKE_HOME:-$HOME/.viggle-remake}") DIR=${REMAKE_CODEX_DIR:-$REMAKE_HOME/codex} AUTH=${REMAKE_CODEX_AUTH:-$HOME/.codex/auth.json} MCP=$(command -v viggle-remake-mcp) || { echo "codex_exec.sh: viggle-remake-mcp is not on PATH (pip install -e .)" >&2; exit 1; } export CODEX_HOME=$DIR/codex_home WS=$DIR/ws mkdir -p "$CODEX_HOME" "$WS" "$REMAKE_HOME/sessions" [ -f "$AUTH" ] && cp -u "$AUTH" "$CODEX_HOME/auth.json" # -u: pick up a refreshed login sed -e "s#@WS@#$WS#g" -e "s#@CODEX_HOME@#$CODEX_HOME#g" -e "s#@REMAKE_HOME@#$REMAKE_HOME#g" -e "s#@MCP@#$MCP#g" "$ROOT/examples/codex_config.toml" \ > "$CODEX_HOME/config.toml" # AGENTS.md + the short prompt-writing guide (data/short_guide.md) and the two format examples: codex re-reads AGENTS.md after every # context compaction, a tool result does not survive one (the config sets REMAKE_GUIDE_IN_AGENTS, so write_prompts and # cut_windows leave them out, and project_doc_max_bytes, since the whole file is ~60 KB) G=$ROOT/viggle_remake_mcp/data/short_guide.md { cat "$ROOT/viggle_remake_mcp/data/AGENTS.md" printf '\n\n# Prompt-writing guide (write_prompts and cut_windows do not repeat it)\n\n=== GUIDE ===\n' cat "$G" printf '\n\n=== FORMAT EXAMPLE, swap sessions (a different edit of a different clip - copy its structure, tags and level of detail, not its content) ===\n' cat "$ROOT/viggle_remake_mcp/data/example_prompt.txt" printf '\n\n=== FORMAT EXAMPLE, edit sessions (same) ===\n' cat "$ROOT/viggle_remake_mcp/data/example_edit_prompt.txt"; } > "$WS/AGENTS.md" if [ "$1" = exec ]; then # the workspace is no git repo; an open stdin makes exec wait for more prompt shift # "model at capacity" ends the turn: resume the same session, at most 10 times. stdout/stderr still stream through. # Assumes the prompt is the last argument and, for `exec resume ...`, the session id is the one before it. CONT='Your previous turn was cut off by a transient "model at capacity" error; this is not new user feedback. Continue the same task from where it stopped: call `status` first and do not redo finished steps. If your last tool call returned a CODEX IMAGE STEP, call that tool again with the same arguments: it imports the picture if it is in place, otherwise it gives you a fresh brief.' set -o pipefail log=$(mktemp -d -p "$DIR") : > "$CODEX_HOME/run_sids" # the MCP adds every session this run touches (REMAKE_RUN_SIDS in the config) run() { { codex exec --skip-git-repo-check -C "$WS" "$@" &1 1>&3 3>&- | tee "$log/err" >&2; } 3>&1 | tee "$log/out"; } run "$@"; rc=$? args=("$@"); n=$# if [ "$1" = resume ]; then id=${args[n-2]}; opts=("${args[@]:1:n-3}"); else id=; opts=("${args[@]:0:n-1}"); fi sid() { [ -n "$id" ] || id=$(grep -ohE '"thread_id":"[0-9a-f-]{36}"|session id: [0-9a-f-]{36}' "$log/out" "$log/err" | grep -oE '[0-9a-f-]{36}' | head -1); } for i in $(seq 10); do tail -n 5 "$log/out" "$log/err" | grep -q "at capacity" || break sid echo "codex_exec.sh: model at capacity, resuming $id ($i/10)" >&2 sleep 30 run resume "${opts[@]}" "$id" "$CONT"; rc=$? done # codex sometimes ends its turn with the job half done and nothing saved (3 of ~8 runs on a 2-minute film, each after a `!!` warning or a long to-do list). A session it touched without writing a # result.json during this run gets the turn back, at most twice; `save_result` with status failed is the way out. for i in 1 2; do open=$(for s in $(cat "$CODEX_HOME/run_sids"); do [ "$REMAKE_HOME/sessions/$s/result.json" -nt "$log" ] || printf '%s ' $s; done) [ $rc = 0 ] && [ -n "$open" ] && [ ! -f "${REMAKE_STOP:-$REMAKE_HOME/STOP}" ] || break sid echo "codex_exec.sh: ended without a result for ${open% }, resuming $id ($i/2)" >&2 run resume "${opts[@]}" "$id" "Your turn ended, but session(s) ${open% } have no result: save_result was not called. This is not new user feedback. The job is not done and nothing has gone wrong: there is no time or token limit and nobody is waiting - a film of a minute or more takes an hour or more. Call status first and do not redo finished steps, then continue the task: paint every remaining keyframe, render every window, review every strip, do the fix round, finish and save_result. A \`!!\` line from \`windows\` is advice, not a blocker: follow it or accept the weak window, note it and go on. Only if the job truly cannot go on (painting STOPPED, no live workers) call save_result with status failed and say why."; rc=$? done rm -r "$log" exit $rc fi exec codex -C "$WS" "$@"