diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index cd81d2b..c3d689e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "runway-api", "description": "Video generation at scale. Generate videos, images, and audio with Runway's API — batch ad campaigns, product videos, multishot stories, and creative iteration. Supports seedance2, gen4.5, veo3, Nano, Banana Pro, and more.", - "version": "2.0.0", + "version": "2.1.0", "author": { "name": "Runway", "email": "ops@runwayml.com" diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 21b11f0..441e861 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "runway-api", "displayName": "Runway API", - "version": "2.0.0", + "version": "2.1.0", "description": "Video generation at scale. Generate videos, images, and audio with Runway's API — batch ad campaigns, product videos, multishot stories, and creative iteration. Supports seedance2, gen4.5, veo3, Nano, Banana Pro, and more.", "author": { "name": "Runway", diff --git a/CHANGELOG.md b/CHANGELOG.md index 2934994..23fdb49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.1.0 + +- **`use-runway-api`:** Runtime script moved from `scripts/runway-api.mjs` (repo root) to `skills/use-runway-api/scripts/runway-api.mjs` so it ships with the skill when installed via `npx skills add`, Claude plugins, or Cursor plugins. SKILL.md now documents a concrete `` resolution strategy with fallback locations. +- **`use-runway-api`:** Added opinionated **Presenting Generation Output** section — lead with model + cost, embed images as Markdown, proactively offer to download locally, avoid pasting raw signed URLs. +- **`use-runway-api`:** Added `RUNWAY_SKILLS_DIR` env var as a fallback hint for the skill path. +- **`rw-api-reference`:** Added **Request Body Reference (raw JSON)** section with minimal POST bodies for every generation endpoint (text_to_image, text_to_video, image_to_video, video_to_video, text_to_speech, sound_effect, voice_isolation, voice_dubbing, speech_to_speech), plus `avatars`, `documents`, and `realtime_sessions`. +- **`AUTH.md`:** Fixed env var names (`RUNWAY_SKILLS_API_SECRET`, `RUNWAY_SKILLS_API_SECRET_STAGE`, `RUNWAY_SKILLS_BASE_URL`) to match the runtime script. + ## 2.0.0 - **Media generation:** New `rw-generate-video`, `rw-generate-image`, `rw-generate-audio` skills that run Python scripts directly via `uv run` — no SDK setup required diff --git a/skills/rw-api-reference/SKILL.md b/skills/rw-api-reference/SKILL.md index f388998..867a3d1 100644 --- a/skills/rw-api-reference/SKILL.md +++ b/skills/rw-api-reference/SKILL.md @@ -102,6 +102,154 @@ Video duration: **2-15 seconds** (model-dependent). Aspect ratios are pixel-base Each Avatar supports up to **50,000 tokens** of knowledge. Link documents to an Avatar via `client.avatars.update(id, { documentIds: [...] })`. +--- + +## Request Body Reference (raw JSON) + +Use these when calling the API directly (e.g. through `use-runway-api`'s `request` command) rather than via an SDK. Only required + common fields shown — consult `+rw-fetch-api-reference` for the full schema. + +### `POST /v1/text_to_image` + +```json +{ + "model": "gen4_image", + "promptText": "A serene Japanese garden with cherry blossoms", + "ratio": "1920:1080" +} +``` + +- `model`: `gen4_image` | `gen4_image_turbo` | `gemini_2.5_flash` (required) +- `promptText`: string, up to ~1000 chars (required) +- `ratio`: one of `1920:1080`, `1080:1920`, `1024:1024`, `1360:768`, `1080:1080`, `1168:880`, `1440:1080`, `1080:1440`, `1808:768`, `2112:912` (required; 720p or 1080p variants depending on model) +- `referenceImages`: optional `[{ "uri": "https://...", "tag": "MyTag" }]` — reference by `@MyTag` in `promptText` +- `seed`: optional integer for reproducibility + +### `POST /v1/text_to_video` + +```json +{ + "model": "gen4.5", + "promptText": "A golden retriever running through wildflowers at sunset", + "ratio": "1280:720", + "duration": 5 +} +``` + +- `model`: `gen4.5` | `veo3` | `veo3.1` | `veo3.1_fast` | `seedance2` (required) +- `duration`: integer seconds, 2–10 (required; model-specific valid values — e.g. veo3 only accepts 8) +- `ratio`: e.g. `1280:720`, `720:1280`, `1104:832`, `832:1104`, `960:960` (required) + +### `POST /v1/image_to_video` + +```json +{ + "model": "gen4.5", + "promptImage": "https://example.com/cover.jpg", + "promptText": "A slow dolly-in shot", + "ratio": "1280:720", + "duration": 5 +} +``` + +- `model`: `gen4.5` | `gen4_turbo` | `veo3` | `veo3.1` | `veo3.1_fast` | `seedance2` (required) +- `promptImage`: HTTPS URL, data URI, or `runway://` URI (required). Can also be `[{ "uri": "...", "position": "first" | "last" }]` for keyframes. +- `promptText`: optional for most models, required for `gen4_turbo` when no image motion is obvious + +### `POST /v1/video_to_video` + +```json +{ + "model": "gen4_aleph", + "videoUri": "https://example.com/source.mp4", + "promptText": "Change the season to winter with snowfall", + "ratio": "1280:720" +} +``` + +### `POST /v1/text_to_speech` + +```json +{ + "model": "eleven_multilingual_v2", + "text": "Hello, welcome to Runway.", + "voice": { "type": "runway-preset", "presetId": "clara" } +} +``` + +- `voice`: `{ type: "runway-preset", presetId: "clara" | "victoria" | "vincent" | ... }` or a provider-specific voice object +- `languageCode`: optional ISO code (auto-detected by default) + +### `POST /v1/sound_effect` + +```json +{ + "model": "eleven_text_to_sound_v2", + "promptText": "Thunderclap followed by heavy rain", + "duration": 5 +} +``` + +### `POST /v1/voice_isolation` + +```json +{ + "model": "eleven_voice_isolation", + "audioUri": "https://example.com/noisy.mp3" +} +``` + +### `POST /v1/voice_dubbing` + +```json +{ + "model": "eleven_voice_dubbing", + "audioUri": "https://example.com/english.mp3", + "targetLanguage": "es" +} +``` + +### `POST /v1/speech_to_speech` + +```json +{ + "model": "eleven_multilingual_sts_v2", + "audioUri": "https://example.com/source.mp3", + "voice": { "type": "runway-preset", "presetId": "victoria" } +} +``` + +### `POST /v1/avatars` + +```json +{ + "name": "Support Agent", + "referenceImage": "https://example.com/portrait.jpg", + "voice": { "type": "runway-live-preset", "presetId": "clara" }, + "personality": "You are a friendly support agent.", + "documentIds": [] +} +``` + +### `POST /v1/documents` + +```json +{ + "avatarId": "", + "name": "FAQ", + "content": "Q: What is your return policy?\nA: 30 days, no questions asked." +} +``` + +### `POST /v1/realtime_sessions` + +```json +{ + "avatarId": "" +} +``` + +--- + ### Management Endpoints | Method | Endpoint | Description | diff --git a/skills/use-runway-api/AUTH.md b/skills/use-runway-api/AUTH.md index 3fcce5d..5071db0 100644 --- a/skills/use-runway-api/AUTH.md +++ b/skills/use-runway-api/AUTH.md @@ -11,14 +11,14 @@ Use environment variables for direct Runway API actions. The API key must never Current shell before launching the editor: ```bash -export RUNWAYML_API_SECRET=YOUR_KEY_HERE +export RUNWAY_SKILLS_API_SECRET=YOUR_KEY_HERE cursor . ``` Or add it to your shell profile, then restart the editor: ```bash -echo 'export RUNWAYML_API_SECRET=YOUR_KEY_HERE' >> ~/.zshrc +echo 'export RUNWAY_SKILLS_API_SECRET=YOUR_KEY_HERE' >> ~/.zshrc source ~/.zshrc ``` @@ -27,21 +27,26 @@ Replace `YOUR_KEY_HERE` locally in the terminal. Never paste the key into the ch ## Verify ```bash -node /scripts/runway-api.mjs auth status +node /scripts/runway-api.mjs auth status ``` -If `authenticated` is still `false`, restart the editor or launch it from a shell that already has `RUNWAYML_API_SECRET` set. +`` is the absolute directory of the `SKILL.md` you are reading — see the **Runtime Location** section in `SKILL.md` for how to resolve it. + +If `authenticated` is still `false`, restart the editor or launch it from a shell that already has `RUNWAY_SKILLS_API_SECRET` set. ## Non-production environments -Set `RUNWAYML_BASE_URL` alongside the API key: +Set a stage-specific key (preferred) and/or override the base URL: ```bash -export RUNWAYML_BASE_URL=https://api.dev-stage.runwayml.com +export RUNWAY_SKILLS_API_SECRET_STAGE=YOUR_STAGE_KEY +export RUNWAY_SKILLS_BASE_URL=https://api.dev-stage.runwayml.com ``` +With `--stage` on any command, the CLI prefers `RUNWAY_SKILLS_API_SECRET_STAGE` and falls back to `RUNWAY_SKILLS_API_SECRET`. + ## Notes - API keys require prepaid credits to work. -- `RUNWAYML_BASE_URL` defaults to `https://api.dev.runwayml.com`. +- `RUNWAY_SKILLS_BASE_URL` defaults to `https://api.dev.runwayml.com` (production) or `https://api.dev-stage.runwayml.com` when `--stage` is passed. - `auth status` verifies that the current environment can reach the API successfully. diff --git a/skills/use-runway-api/SKILL.md b/skills/use-runway-api/SKILL.md index 39dd87c..566132b 100644 --- a/skills/use-runway-api/SKILL.md +++ b/skills/use-runway-api/SKILL.md @@ -15,13 +15,30 @@ Call the Runway public API directly from the agent to manage resources, trigger > **Skill selection:** Pair this skill with `+api-reference` when you need the canonical API contract. Do not use `+integrate-image`, `+integrate-video`, `+integrate-audio`, `+integrate-characters`, or `+integrate-documents` unless the task is to write or modify application code. +## Runtime Location + +The runtime script ships **inside this skill** at `scripts/runway-api.mjs` (co-located with this `SKILL.md`). It has zero dependencies — Node.js 20+ is required. + +**Resolving the absolute path.** Every shell command below uses the placeholder ``. Replace it with the absolute directory of the `SKILL.md` you are currently reading — you already have this path from the tool that loaded this file. Do **not** guess the path, run `find` from `$HOME`, or search the whole filesystem. + +If `` is not known from context, check these locations in order with `ls` before giving up: + +1. `$RUNWAY_SKILLS_DIR/skills/use-runway-api/scripts/runway-api.mjs` (if the env var is set) +2. `~/.claude/plugins/cache/*/runway-api/skills/use-runway-api/scripts/runway-api.mjs` (Claude Code plugin install) +3. `~/.cursor/plugins/cache/*/runway-api/skills/use-runway-api/scripts/runway-api.mjs` (Cursor plugin install) +4. `~/.claude/skills/use-runway-api/scripts/runway-api.mjs` (`npx skills add` install) +5. `~/.agents/skills/use-runway-api/scripts/runway-api.mjs` (generic agent install) +6. `~/Documents/github/runway/skills/skills/use-runway-api/scripts/runway-api.mjs` (source checkout) + +Pick the first match and use it as `/scripts/runway-api.mjs` for the rest of the session. Do not re-resolve between commands. + ## Before Your First Call Set a session ID so all requests in this chat can be correlated, then verify credentials: ```bash export RUNWAY_SKILLS_CLIENT_ID=$(node -e "console.log(crypto.randomUUID())") -node /scripts/runway-api.mjs auth status +node /scripts/runway-api.mjs auth status ``` - If `authenticated` is `true` → proceed to the API call. Do not re-check. @@ -29,17 +46,13 @@ node /scripts/runway-api.mjs auth status > **Staging caveat:** `auth status` hits `/v1/organization` which may 500 on stage even when data endpoints work fine. If stage `auth status` fails but you have `RUNWAY_SKILLS_API_SECRET_STAGE` set, try a data endpoint like `avatars list --stage` to confirm the key works before giving up. -## Runtime Location - -The runtime script is at `scripts/runway-api.mjs` in this skills repository. It has zero dependencies — only Node.js 20+ is required. - ## Fast Paths For plain list requests, use the compact list commands first instead of the generic `request` command: -- list avatars → `node /scripts/runway-api.mjs avatars list` -- list voices → `node /scripts/runway-api.mjs voices list` -- list documents → `node /scripts/runway-api.mjs documents list [--avatar-id ]` +- list avatars → `node /scripts/runway-api.mjs avatars list` +- list voices → `node /scripts/runway-api.mjs voices list` +- list documents → `node /scripts/runway-api.mjs documents list [--avatar-id ]` These commands return smaller, list-friendly JSON on purpose. After a successful list command, answer once. Do not re-run the command, do not read back the same output, and do not render the same table twice. @@ -59,7 +72,7 @@ After the table, add one short summary line only if something notable stands out Call any public API endpoint: ```bash -node /scripts/runway-api.mjs request [--body ''] [--stdin] [--dry-run] +node /scripts/runway-api.mjs request [--body ''] [--stdin] [--dry-run] ``` All output is JSON. Errors go to stderr with a non-zero exit code and include an `example` field with a correctable invocation. @@ -82,40 +95,40 @@ Do not duplicate or invent request schemas in this skill. For simple GET/DELETE **Get organization info:** ```bash -node /scripts/runway-api.mjs request GET /v1/organization +node /scripts/runway-api.mjs request GET /v1/organization ``` **List avatars:** ```bash -node /scripts/runway-api.mjs avatars list +node /scripts/runway-api.mjs avatars list ``` **Get a specific avatar:** ```bash -node /scripts/runway-api.mjs request GET /v1/avatars/ +node /scripts/runway-api.mjs request GET /v1/avatars/ ``` **Update an avatar:** ```bash -node /scripts/runway-api.mjs request PATCH /v1/avatars/ --body '{ +node /scripts/runway-api.mjs request PATCH /v1/avatars/ --body '{ "personality": "Updated personality text" }' ``` **Delete an avatar (preview first):** ```bash -node /scripts/runway-api.mjs request DELETE /v1/avatars/ --dry-run -node /scripts/runway-api.mjs request DELETE /v1/avatars/ +node /scripts/runway-api.mjs request DELETE /v1/avatars/ --dry-run +node /scripts/runway-api.mjs request DELETE /v1/avatars/ ``` **List knowledge documents for an avatar:** ```bash -node /scripts/runway-api.mjs documents list --avatar-id +node /scripts/runway-api.mjs documents list --avatar-id ``` **Create a knowledge document:** ```bash -node /scripts/runway-api.mjs request POST /v1/documents --body '{ +node /scripts/runway-api.mjs request POST /v1/documents --body '{ "avatarId": "", "name": "FAQ", "content": "Q: What is your return policy?\nA: 30 days, no questions asked." @@ -124,7 +137,7 @@ node /scripts/runway-api.mjs request POST /v1/documents --body '{ **List voices:** ```bash -node /scripts/runway-api.mjs voices list +node /scripts/runway-api.mjs voices list ``` ## Waiting for Tasks @@ -132,26 +145,43 @@ node /scripts/runway-api.mjs voices list Generation endpoints return a task ID. Always run `wait` immediately after a generation call — do not ask the user whether to wait. ```bash -node /scripts/runway-api.mjs wait +node /scripts/runway-api.mjs wait ``` -Return the final output URLs in your response. - ## Generation Requests When the user asks to generate an image, video, or audio: -1. Read `+api-reference` once before the first generation POST. It is the canonical source for model options and exact request fields. +1. Read `+api-reference` once before the first generation POST. It is the canonical source for model options, request body shapes, and valid field values. 2. Choose the model from `+api-reference` and tell the user which one you picked, briefly. 3. Build the request body from `+api-reference`. Do not guess field names. 4. Call the generation endpoint once with that body. 5. Run `wait` automatically. -6. Return the output URL(s) and model used. +6. Present the result following the rules in **Presenting Generation Output** below. For generation requests, never skip the `+api-reference` read. For simple list/get/delete requests, do not load it unless needed. If the user says only "generate an image" but the surrounding context is clearly about Runway account actions or this skill, still use the Runway API rather than a generic built-in image tool. +## Presenting Generation Output + +The `wait` command returns a task with an `output` array of signed URLs. These URLs **expire in 24–48 hours** and are long signed JWT blobs that are awkward to read inline. + +After a successful generation, do all of the following: + +1. **Lead with what was generated** — one short line stating the model and cost, e.g. + `Generated with gen4_image (1080p, 8 credits).` +2. **Embed images inline as Markdown** so the user can see them without clicking: + ```markdown + ![Fox in a snowy forest](https://storage.runway.../signed-url) + ``` + For videos, link them as plain Markdown links (`[fox.mp4](...)`) since inline video does not render in most chat UIs. +3. **Offer to save a local copy** in the same message, proactively. Suggest a predictable path (`./generated/` or `./runway-outputs/`) and infer a filename from the prompt when possible. Example: + > Want me to save it to `./generated/fox.png`? The signed URL expires in ~24–48h. +4. **Do not paste the full signed URL as raw text** unless the user asks for it. The Markdown image/link already contains it. + +If the user confirms a download, fetch with `curl -L -o ''` (quote the URL — it contains `&`). + ## API Reference Use `+api-reference` for the canonical API contract. Use `+fetch-api-reference` only when you specifically need the latest docs content from `docs.dev.runwayml.com`. @@ -161,8 +191,8 @@ Use `+api-reference` for the canonical API contract. Use `+fetch-api-reference` Add `--stage` to any command to target the staging API: ```bash -node /scripts/runway-api.mjs --stage avatars list -node /scripts/runway-api.mjs --stage request GET /v1/avatars +node /scripts/runway-api.mjs --stage avatars list +node /scripts/runway-api.mjs --stage request GET /v1/avatars ``` With `--stage`, the CLI checks `RUNWAY_SKILLS_API_SECRET_STAGE` first, then falls back to `RUNWAY_SKILLS_API_SECRET`. The base URL defaults to `https://api.dev-stage.runwayml.com`. @@ -179,7 +209,7 @@ with open('image.png', 'rb') as f: body = json.dumps({'name': 'My Avatar', 'referenceImage': f'data:image/png;base64,{b64}', ...}) with open('/tmp/body.json', 'w') as f: f.write(body) -" && cat /tmp/body.json | node /scripts/runway-api.mjs request POST /v1/avatars --stdin +" && cat /tmp/body.json | node /scripts/runway-api.mjs request POST /v1/avatars --stdin ``` Alternatively, write the JSON to a file and use `curl -d @file` directly. The `--body` flag is fine for small JSON payloads but will hit `argument list too long` for data URIs of images over ~200KB. @@ -193,6 +223,7 @@ The runtime reads credentials from the process environment: | `RUNWAY_SKILLS_API_SECRET` | Production API key | | `RUNWAY_SKILLS_API_SECRET_STAGE` | Stage API key (used with `--stage`) | | `RUNWAY_SKILLS_BASE_URL` | Override the base URL for any environment | +| `RUNWAY_SKILLS_DIR` | Optional. Absolute path to the source checkout of this skills repo — used by the agent as a fallback when resolving ``. | If the agent cannot see `RUNWAY_SKILLS_API_SECRET`, the editor likely needs to be restarted after the variable is set. diff --git a/scripts/runway-api.mjs b/skills/use-runway-api/scripts/runway-api.mjs similarity index 100% rename from scripts/runway-api.mjs rename to skills/use-runway-api/scripts/runway-api.mjs