CLI
The CLI ships as @odori/cli and installs an odori binary.
pnpm add -D @odori/cli
pnpm odori --helpodori dev
Discovers the project, regenerates .odori/, and starts Studio.
odori dev --port 4300Studio watches videos/ and reloads when a video.tsx, *.preview.tsx, or brand module appears or disappears. The dev server also exposes the endpoints Studio uses to request a still or an export.
Studio opens in the default browser when the shell is interactive. Pass --no-open, set open: false in odori.config.ts, or set ODORI_OPEN=0 to start without it. CI and piped output never open a browser.
odori dev --no-opendocsUrl in odori.config.ts sets where Studio's documentation link points, and defaults to the hosted docs site at https://odori.dev/docs.
odori init
Adds videos/layout.tsx, a first videos/launch/video.tsx, and odori.config.ts to an existing project. Existing files are never overwritten.
odori new <name>
Generates videos/<name>/video.tsx with static metadata and a JSX timeline. It wires the root layout when videos/layout.tsx exists.
odori add component <components...>
Copies registry component source and its preview fixture into videos/components/, resolves registry dependencies, and records provenance in odori.lock.json, which belongs in your repository.
odori add component title-reveal end-card
odori add component terminal --forceA component you have edited locally is kept, with a warning, unless --force is passed.
odori audio import <file> and odori audio generate <prompt>
Import a file unchanged and register its cue, or generate music, sound effects, or speech into the local media library. Add --loudness -20 to explicitly normalize an import or use the legacy audio preparation workflow:
odori audio import ./track.wav --name bed.main
odori audio generate "warm ambient music" --type music --duration 60s --name background
odori audio generate "soft notification chime" --type sfx --duration 2s --name notificationUse --output to set the destination, --loudness to set loudness, or --provider to select a provider. Generation requires a configured API key. See integrations.
odori audio generate --type speech <script>
Generate narration and word timings with ElevenLabs:
odori audio generate --type speech "Introducing our new editor." --output public/audio/openerUse --voice to choose a voice and --name to override the default voice.narration cue. See narration and captions.
odori image and odori video
Generate or import local media. Image operations include generation, editing, and vectorization. Video generation supports text and an optional start image.
odori image generate "A coastal bookshop" --name bookshop --dry-run
odori image edit public/bookshop.png --prompt "Warmer evening light" --name evening
odori image vectorize public/emblem.png --name emblem
odori image import ./artwork.svg --name artwork
odori video generate "A slow push toward the entrance" --duration 6 --name entrance --no-wait
odori video import ./footage.mp4 --name footage| Flag | Purpose |
|---|---|
--provider | Select vercel, quiver, or elevenlabs for a supported operation |
--model | Select a model ID, such as google/gemini-3-pro-image |
--connection | Use a named account |
--name | Name the output using letters, digits, hyphens, or underscores |
--image | Use a start image for video or a reference for image generation |
--references | Comma-separated image reference paths or HTTPS URLs |
--aspect-ratio | Request a ratio, such as 16:9 |
--resolution | Image model size or video dimensions, such as 1K or 1280x720 |
--options | Read model-specific options from a JSON file |
--dry-run | Validate without submitting generation; custom model discovery may contact the provider |
--no-wait | Return a job ID and collect the result in the background |
--json | Return structured output |
Flags depend on the operation and model. Imports accept a file and --name; they do not call a provider. See integrations for credentials, supported routes, uploads, and limitations.
odori models and odori integrations
List curated models or discover those available through an account:
odori models --provider vercel --live --json
odori integrations --json
odori integrations connect work-gateway --provider vercel --key-env WORK_GATEWAY_KEY
odori integrations default --operation image.generate --provider vercel --model google/gemini-3-pro-imageCredentials stay in environment variables or the machine store. Project configuration contains connection references, never keys. Command flags override project defaults, which override personal defaults.
odori catalog <components|templates|audio>
Browse available components, templates, or audio. Add --json for one machine-readable result. odori list lists videos already in your project.
odori catalog templates
odori catalog templates apple-news
odori add template apple-news --output videos/news
odori catalog audio
odori add audio bed-pulseodori diff [components...]
Compares installed component source with the version that was installed and with the version the registry ships today. Each component is reported as up to date, modified locally, update available, or modified locally and updated upstream.
odori diff
odori diff terminal --fullodori update [components...]
Applies upstream changes to components you have not edited. A component that is both edited locally and changed upstream is left alone until you review it with odori diff and pass --force.
odori list
Prints discovered video IDs, formats, durations, source files, and the number of component previews.
odori graph
Compiles the project into .odori/graph.json: every video with its resolved format, duration, brand, tags, audio variants, and the components it uses, beside the component catalog, the audio library, and a structure report. It also refreshes the generated imports and catalog.json, so everything under .odori/ describes the same tree.
The structure report names every place the filesystem contract is almost met: a video.ts that discovery will never see, a fixture named after something other than its directory, a literal /audio/... path with no file behind it, an audio variant that overrides a cue the brand never defines. Errors exit non-zero, so the command doubles as a CI gate; odori doctor prints the same findings as one check, and odori test fails on the errors before a browser starts.
odori graph
odori graph --jsonodori inspect <id>
Resolves the layout, compiles the timeline in a browser, runs prepare.ts, and freezes a manifest.
odori inspect launch
odori inspect launch --json --input '{"headline":"Ship it."}'odori frame <id>
Renders one deterministic frame to a PNG, through the same pipeline an export uses, so the image is the frame the video would show at that moment.
--at is a duration like every other time in Odori: a bare number is seconds, and 120f names frame 120.
odori frame launch --at 4s --output out/hero.png
odori frame launch --at 120fodori test [id]
Validates contracts and representative frames for one video or all of them. It fails when default props do not satisfy the schema, when a declared duration disagrees with the compiled scene total, when a frame is blank, when content sits entirely outside the canvas, or when text is smaller than 20px at a 1080p reference.
The text-size check measures rendered glyphs after scaling.
Use data-odori-chrome to exempt decorative interface text:
<div data-odori-chrome>{/* the app's own sidebar, badges, timestamps */}</div>Apply this only to interface details such as sidebar labels. Keep the check enabled for text the viewer needs to read.
The overflow check reports content entirely outside the canvas. Use data-odori-bleed for intentionally offscreen content:
<div data-odori-bleed>{/* a word set larger than the frame */}</div>odori export <id>
Freezes a manifest, records a job under .odori/builds/, renders every frame through the readiness handshake, mixes the audio track, and encodes an MP4 with FFmpeg. Frames are captured by several browser workers in parallel.
odori export launch --output out/launch.mp4
odori export launch --concurrency 8 --preset slow
odori export launch --no-audio
odori jobs retry job-88e09129a0-msw17lz9--no-audio writes the picture with no audio track, for a silent loop on a landing page or a clip going into an editor that brings its own sound. The choice is frozen with the job, so a retry produces the same file.
--audio-variant <name> exports one of the video's declared audio variants, for a film that exists in more than one voice. The variant is applied when the cues compile, frozen into the manifest like everything else, and the file is named <id>-<name> so two voices cannot overwrite each other. An unknown name fails before anything renders.
A retry replays the frozen manifest, so it never re-resolves inputs or reruns prepare.ts.
odori jobs list
Lists export and generation jobs. Generation IDs start with gen-.
odori jobs get gen-JOB_ID --json
odori jobs collect gen-JOB_ID --json
odori jobs cancel gen-JOB_IDUse the actual ID returned by generation. Collecting resumes an existing job or retries its download. It never silently resubmits an uncertain generation. Cancellation is available only before submission. Check the provider's history if a job reports an uncertain submission.
job-88e09129a0-msw17lz9 launch ready attempts 2 out/launch.mp4Shared flags
| Flag | Purpose |
|---|---|
--input '<json>' | Serializable input validated by the video schema |
--output <path> | Output path for frame and export |
--force | Replace locally modified component source |
--json | One JSON result for list, catalog, audio, graph, inspect, test, docs, and jobs list/show |
--concurrency <n> | Parallel render workers for export |
--preset <name> | x264 preset for export, default medium |
--input-file <path> | Read a JSON input object from a file, instead of --input |
--full | Print the diff body in diff |
Requirements
Rendering needs Chrome or Chromium and FFmpeg. Odori looks for a browser at the usual macOS and Linux locations, at chromePath in odori.config.ts, or at ODORI_CHROME.
odori docs
The documentation ships inside the CLI, so it answers with no network and no browser.
odori docs # every page, grouped by section
odori docs guides/audio # print one page
odori docs audio # the tail of a slug is enough
odori docs search "safe area" # the lines that say it, with page and number
odori docs --json # the same, for a program to readEach CLI release includes a snapshot of this site's docs. Update the CLI to get newer documentation.