# CLI reference (`scripts/sora.py`) This file contains the command catalog for the bundled video generation CLI. Keep `SKILL.md` overview-first; put verbose CLI details here. ## What this CLI does - `create`: create a new video job (async) - `create-and-poll`: create a job, poll until complete, optionally download - `poll`: wait for an existing job to finish - `status`: retrieve job status/details - `download`: download video/thumbnail/spritesheet - `list`: list recent jobs - `delete`: delete a job - `remix`: remix a completed video - `create-batch`: create multiple jobs from a JSONL file Real API calls require **network access** + `OPENAI_API_KEY`. `--dry-run` does not. ## Quick start (works from any repo) Set a stable path to the skill CLI (default `CODEX_HOME` is `~/.codex`): ``` export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}" export SORA_CLI="$CODEX_HOME/skills/sora/scripts/sora.py" ``` If you're in this repo, you can set the path directly: ``` # Use repo root (tests run from output/* so $PWD is not the root) export SORA_CLI="$(git rev-parse --show-toplevel)//scripts/sora.py" ``` If `git` isn't available, set `SORA_CLI` to the absolute path to `/scripts/sora.py`. If uv cache fails with permission errors, set a writable cache: ``` export UV_CACHE_DIR="/tmp/uv-cache" ``` Dry-run (no API call; no network required; does not require the `openai` package): ``` python "$SORA_CLI" create --prompt "Test" --dry-run ``` Preflight a full prompt without running the API: ``` python "$SORA_CLI" create --prompt-file prompt.txt --dry-run --json-out out/request.json ``` Create a job (requires `OPENAI_API_KEY` + network): ``` uv run --with openai python "$SORA_CLI" create \ --model sora-2 \ --prompt "Wide tracking shot of a teal coupe on a desert highway" \ --size 1280x720 \ --seconds 8 ``` Create from a prompt file (avoids shell-escaping issues for multi-line prompts): ``` cat > prompt.txt << 'EOF' Use case: landing page hero Primary request: a matte black camera on a pedestal Action: slow 30-degree orbit over 4 seconds Camera: 85mm, shallow depth of field Lighting/mood: soft key light, subtle rim Constraints: no logos, no text EOF uv run --with openai python "$SORA_CLI" create \ --prompt-file prompt.txt \ --size 1280x720 \ --seconds 4 ``` If your prompt file is already structured (Use case/Scene/Camera/etc), disable tool augmentation: ``` uv run --with openai python "$SORA_CLI" create \ --prompt-file prompt.txt \ --no-augment \ --size 1280x720 \ --seconds 4 ``` Create + poll + download: ``` uv run --with openai python "$SORA_CLI" create-and-poll \ --model sora-2-pro \ --prompt "Close-up of a steaming coffee cup on a wooden table" \ --size 1280x720 \ --seconds 8 \ --download \ --variant video \ --out coffee.mp4 ``` Create + poll + write JSON bundle: ``` uv run --with openai python "$SORA_CLI" create-and-poll \ --prompt "Minimal product teaser of a matte black camera" \ --json-out out/coffee-job.json ``` Remix a completed video: ``` uv run --with openai python "$SORA_CLI" remix \ --id video_abc123 \ --prompt "Same shot, shift palette to teal/sand/rust with warm backlight." ``` Download a thumbnail or spritesheet: ``` uv run --with openai python "$SORA_CLI" download --id video_abc123 --variant thumbnail --out thumb.webp uv run --with openai python "$SORA_CLI" download --id video_abc123 --variant spritesheet --out sheet.jpg ``` ## Guardrails (important) - Use `python "$SORA_CLI" ...` (or equivalent full path) for all video work. - For API calls, prefer `uv run --with openai ...` to avoid missing SDK errors. - Do **not** create one-off runners unless the user explicitly asks. - **Never modify** `scripts/sora.py` unless the user asks. ## Defaults (unless overridden by flags) - Model: `sora-2` - Size: `1280x720` - Seconds: `4` (API expects a string enum: "4", "8", "12") - Variant: `video` - Poll interval: `10` seconds ## JSON output (`--json-out`) - For `create`, `status`, `list`, `delete`, `poll`, and `remix`, `--json-out` writes the JSON response to a file. - For `create-and-poll`, `--json-out` writes a bundle: `{ "create": ..., "final": ... }`. - If the path has no extension, `.json` is added automatically. - In `--dry-run`, `--json-out` writes the request preview instead of a response. ## Input reference images - Must be jpg/png/webp; they should match the target size. - Provide the path with `--input-reference`. ## Optional deps Prefer `uv run --with ...` for an out-of-the-box run without changing the current project env; otherwise install into your active env: ``` uv pip install openai ``` ## JSONL schema for `create-batch` Each line is a JSON object (or a raw prompt string). Required key: `prompt`. Top-level override keys: - `model`, `size`, `seconds` - `input_reference` (path) - `out` (optional output filename for the job JSON) Prompt augmentation keys (top-level or under `fields`): - `use_case`, `scene`, `subject`, `action`, `camera`, `style`, `lighting`, `palette`, `audio`, `dialogue`, `text`, `timing`, `constraints`, `negative` Notes: - `fields` merges into the prompt augmentation inputs. - Top-level keys override CLI defaults. - `seconds` must be one of: "4", "8", "12". ## Common recipes Create with prompt augmentation fields: ``` uv run --with openai python "$SORA_CLI" create \ --prompt "A minimal product teaser shot of a matte black camera" \ --use-case "landing page hero" \ --camera "85mm, slow orbit" \ --lighting "soft key, subtle rim" \ --constraints "no logos, no text" ``` Two-variant workflow (base + remix): ``` # 1) Base clip uv run --with openai python "$SORA_CLI" create-and-poll \ --prompt "Ceramic mug on a sunlit wooden table in a cozy cafe" \ --size 1280x720 --seconds 4 --download --out output.mp4 # 2) Remix with invariant (same shot, change only the drink) uv run --with openai python "$SORA_CLI" remix \ --id video_abc123 \ --prompt "Same shot and framing; replace the mug with an iced americano in a glass, visible ice and condensation." # 3) Poll and download the remix uv run --with openai python "$SORA_CLI" poll \ --id video_def456 --download --out remix.mp4 ``` Poll and download after a job finishes: ``` uv run --with openai python "$SORA_CLI" poll --id video_abc123 --download --variant video --out out.mp4 ``` Write JSON response to a file: ``` uv run --with openai python "$SORA_CLI" status --id video_abc123 --json-out out/status.json ``` Batch create (JSONL input): ``` mkdir -p tmp/sora cat > tmp/sora/prompts.jsonl << 'EOB' {"prompt":"A neon-lit rainy alley, slow dolly-in","seconds":"4"} {"prompt":"A warm sunrise over a misty lake, gentle pan","seconds":"8", "fields":{"camera":"35mm, slow pan","lighting":"soft dawn light"}} EOB uv run --with openai python "$SORA_CLI" create-batch --input tmp/sora/prompts.jsonl --out-dir out --concurrency 3 # Cleanup (recommended) rm -f tmp/sora/prompts.jsonl ``` Notes: - `create-batch` writes one JSON response per job under `--out-dir`. - Output names default to `NNN-.json`. - Use `--concurrency` to control parallelism (default `3`). Higher concurrency can hit rate limits. - Treat the JSONL file as temporary: write it under `tmp/` and delete it after the run (do not commit it). If `rm` is blocked in your sandbox, skip cleanup or truncate the file. ## CLI notes - Supported sizes depend on model (see `references/video-api.md`). - Seconds are limited to 4, 8, or 12. - Download URLs expire after about 1 hour; copy assets to your own storage. - In CI/sandboxes where long-running commands time out, prefer `create` + `poll` (or add `--timeout`). ## See also - API parameter quick reference: `references/video-api.md` - Prompt structure and examples: `references/prompting.md` - Sample prompts: `references/sample-prompts.md` - Troubleshooting: `references/troubleshooting.md`